HSBSupport.m 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. //==============================================================================
  2. //
  3. // HSBSupport
  4. // HSBSupport
  5. //
  6. // Created by Troy Gaul on 7 Aug 2010.
  7. //
  8. // Copyright (c) 2011-2013 InfinitApps LLC: http://infinitapps.com
  9. // Some rights reserved: http://opensource.org/licenses/MIT
  10. //
  11. //==============================================================================
  12. #import "HSBSupport.h"
  13. //------------------------------------------------------------------------------
  14. float pin(float minValue, float value, float maxValue)
  15. {
  16. if (minValue > value)
  17. return minValue;
  18. else if (maxValue < value)
  19. return maxValue;
  20. else
  21. return value;
  22. }
  23. //------------------------------------------------------------------------------
  24. #pragma mark Floating point conversion
  25. //------------------------------------------------------------------------------
  26. static void hueToComponentFactors(float h, float* r, float* g, float* b)
  27. {
  28. float h_prime = h / 60.0f;
  29. float x = 1.0f - fabsf(fmodf(h_prime, 2.0f) - 1.0f);
  30. if (h_prime < 1.0f) {
  31. *r = 1;
  32. *g = x;
  33. *b = 0;
  34. }
  35. else if (h_prime < 2.0f) {
  36. *r = x;
  37. *g = 1;
  38. *b = 0;
  39. }
  40. else if (h_prime < 3.0f) {
  41. *r = 0;
  42. *g = 1;
  43. *b = x;
  44. }
  45. else if (h_prime < 4.0f) {
  46. *r = 0;
  47. *g = x;
  48. *b = 1;
  49. }
  50. else if (h_prime < 5.0f) {
  51. *r = x;
  52. *g = 0;
  53. *b = 1;
  54. }
  55. else {
  56. *r = 1;
  57. *g = 0;
  58. *b = x;
  59. }
  60. }
  61. //------------------------------------------------------------------------------
  62. void HSVtoRGB(float h, float s, float v, float* r, float* g, float* b)
  63. {
  64. hueToComponentFactors(h, r, g, b);
  65. float c = v * s;
  66. float m = v - c;
  67. *r = *r * c + m;
  68. *g = *g * c + m;
  69. *b = *b * c + m;
  70. }
  71. //------------------------------------------------------------------------------
  72. void RGBToHSV(float r, float g, float b, float* h, float* s, float* v, BOOL preserveHS)
  73. {
  74. float max = r;
  75. if (max < g)
  76. max = g;
  77. if (max < b)
  78. max = b;
  79. float min = r;
  80. if (min > g)
  81. min = g;
  82. if (min > b)
  83. min = b;
  84. // Brightness (aka Value)
  85. *v = max;
  86. // Saturation
  87. float sat;
  88. if (max != 0.0f) {
  89. sat = (max - min) / max;
  90. *s = sat;
  91. }
  92. else {
  93. sat = 0.0f;
  94. if (!preserveHS)
  95. *s = 0.0f; // Black, so sat is undefined, use 0
  96. }
  97. // Hue
  98. float delta;
  99. if (sat == 0.0f) {
  100. if (!preserveHS)
  101. *h = 0.0f; // No color, so hue is undefined, use 0
  102. }
  103. else {
  104. delta = max - min;
  105. float hue;
  106. if (r == max)
  107. hue = (g - b) / delta;
  108. else if (g == max)
  109. hue = 2 + (b - r) / delta;
  110. else
  111. hue = 4 + (r - g) / delta;
  112. hue /= 6.0f;
  113. if (hue < 0.0f)
  114. hue += 1.0f;
  115. if (!preserveHS || fabsf(hue - *h) != 1.0f)
  116. *h = hue; // 0.0 and 1.0 hues are actually both the same (red)
  117. }
  118. }
  119. //------------------------------------------------------------------------------
  120. #pragma mark Square/Bar image creation
  121. //------------------------------------------------------------------------------
  122. static UInt8 blend(UInt8 value, UInt8 percentIn255)
  123. {
  124. return (UInt8) ((int) value * percentIn255 / 255);
  125. }
  126. //------------------------------------------------------------------------------
  127. static CGContextRef createBGRxImageContext(int w, int h, void* data)
  128. {
  129. CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  130. CGBitmapInfo kBGRxBitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst;
  131. // BGRA is the most efficient on the iPhone.
  132. CGContextRef context = CGBitmapContextCreate(data, w, h, 8, w * 4, colorSpace, kBGRxBitmapInfo);
  133. CGColorSpaceRelease(colorSpace);
  134. return context;
  135. }
  136. //------------------------------------------------------------------------------
  137. CGImageRef createSaturationBrightnessSquareContentImageWithHue(float hue)
  138. {
  139. void* data = malloc(256 * 256 * 4);
  140. if (data == nil)
  141. return nil;
  142. CGContextRef context = createBGRxImageContext(256, 256, data);
  143. if (context == nil) {
  144. free(data);
  145. return nil;
  146. }
  147. UInt8* dataPtr = data;
  148. size_t rowBytes = CGBitmapContextGetBytesPerRow(context);
  149. float r, g, b;
  150. hueToComponentFactors(hue, &r, &g, &b);
  151. UInt8 r_s = (UInt8) ((1.0f - r) * 255);
  152. UInt8 g_s = (UInt8) ((1.0f - g) * 255);
  153. UInt8 b_s = (UInt8) ((1.0f - b) * 255);
  154. for (int s = 0; s < 256; ++s) {
  155. register UInt8* ptr = dataPtr;
  156. register unsigned int r_hs = 255 - blend(s, r_s);
  157. register unsigned int g_hs = 255 - blend(s, g_s);
  158. register unsigned int b_hs = 255 - blend(s, b_s);
  159. for (register int v = 255; v >= 0; --v) {
  160. ptr[0] = (UInt8) (v * b_hs >> 8);
  161. ptr[1] = (UInt8) (v * g_hs >> 8);
  162. ptr[2] = (UInt8) (v * r_hs >> 8);
  163. // Really, these should all be of the form used in blend(),
  164. // which does a divide by 255. However, integer divide is
  165. // implemented in software on ARM, so a divide by 256
  166. // (done as a bit shift) will be *nearly* the same value,
  167. // and is faster. The more-accurate versions would look like:
  168. // ptr[0] = blend(v, b_hs);
  169. ptr += rowBytes;
  170. }
  171. dataPtr += 4;
  172. }
  173. // Return an image of the context's content:
  174. CGImageRef image = CGBitmapContextCreateImage(context);
  175. CGContextRelease(context);
  176. free(data);
  177. return image;
  178. }
  179. //------------------------------------------------------------------------------
  180. CGImageRef createHSVBarContentImage(InfComponentIndex barComponentIndex, float hsv[3])
  181. {
  182. UInt8 data[256 * 4];
  183. // Set up the bitmap context for filling with color:
  184. CGContextRef context = createBGRxImageContext(256, 1, data);
  185. if (context == nil)
  186. return nil;
  187. // Draw into context here:
  188. UInt8* ptr = CGBitmapContextGetData(context);
  189. if (ptr == nil) {
  190. CGContextRelease(context);
  191. return nil;
  192. }
  193. float r, g, b;
  194. for (int x = 0; x < 256; ++x) {
  195. hsv[barComponentIndex] = (float) x / 255.0f;
  196. HSVtoRGB(hsv[0] * 360.0f, hsv[1], hsv[2], &r, &g, &b);
  197. ptr[0] = (UInt8) (b * 255.0f);
  198. ptr[1] = (UInt8) (g * 255.0f);
  199. ptr[2] = (UInt8) (r * 255.0f);
  200. ptr += 4;
  201. }
  202. // Return an image of the context's content:
  203. CGImageRef image = CGBitmapContextCreateImage(context);
  204. CGContextRelease(context);
  205. return image;
  206. }
  207. //------------------------------------------------------------------------------