The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

451 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. namespace ColourHelpers
  19. {
  20. static uint8 floatToUInt8 (const float n) noexcept
  21. {
  22. return n <= 0.0f ? 0 : (n >= 1.0f ? 255 : (uint8) (n * 255.0f));
  23. }
  24. // This is an adjusted brightness value, based on the way the human
  25. // eye responds to different colour channels..
  26. static float getPerceivedBrightness (const Colour& c) noexcept
  27. {
  28. const float r = c.getFloatRed();
  29. const float g = c.getFloatGreen();
  30. const float b = c.getFloatBlue();
  31. return std::sqrt (r * r * 0.241f
  32. + g * g * 0.691f
  33. + b * b * 0.068f);
  34. }
  35. //==============================================================================
  36. struct HSB
  37. {
  38. HSB (const Colour& col) noexcept
  39. {
  40. const int r = col.getRed();
  41. const int g = col.getGreen();
  42. const int b = col.getBlue();
  43. const int hi = jmax (r, g, b);
  44. const int lo = jmin (r, g, b);
  45. if (hi != 0)
  46. {
  47. saturation = (hi - lo) / (float) hi;
  48. if (saturation > 0)
  49. {
  50. const float invDiff = 1.0f / (hi - lo);
  51. const float red = (hi - r) * invDiff;
  52. const float green = (hi - g) * invDiff;
  53. const float blue = (hi - b) * invDiff;
  54. if (r == hi)
  55. hue = blue - green;
  56. else if (g == hi)
  57. hue = 2.0f + red - blue;
  58. else
  59. hue = 4.0f + green - red;
  60. hue *= 1.0f / 6.0f;
  61. if (hue < 0)
  62. ++hue;
  63. }
  64. else
  65. {
  66. hue = 0;
  67. }
  68. }
  69. else
  70. {
  71. saturation = hue = 0;
  72. }
  73. brightness = hi / 255.0f;
  74. }
  75. Colour toColour (const Colour& original) const noexcept
  76. {
  77. return Colour (hue, saturation, brightness, original.getAlpha());
  78. }
  79. static PixelARGB toRGB (float h, float s, float v, const uint8 alpha) noexcept
  80. {
  81. v = jlimit (0.0f, 255.0f, v * 255.0f);
  82. const uint8 intV = (uint8) roundToInt (v);
  83. if (s <= 0)
  84. return PixelARGB (alpha, intV, intV, intV);
  85. s = jmin (1.0f, s);
  86. h = (h - std::floor (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors
  87. const float f = h - std::floor (h);
  88. const uint8 x = (uint8) roundToInt (v * (1.0f - s));
  89. if (h < 1.0f) return PixelARGB (alpha, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x);
  90. if (h < 2.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - s * f)), intV, x);
  91. if (h < 3.0f) return PixelARGB (alpha, x, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))));
  92. if (h < 4.0f) return PixelARGB (alpha, x, (uint8) roundToInt (v * (1.0f - s * f)), intV);
  93. if (h < 5.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x, intV);
  94. return PixelARGB (alpha, intV, x, (uint8) roundToInt (v * (1.0f - s * f)));
  95. }
  96. float hue, saturation, brightness;
  97. };
  98. //==============================================================================
  99. struct YIQ
  100. {
  101. YIQ (const Colour& c) noexcept
  102. {
  103. const float r = c.getFloatRed();
  104. const float g = c.getFloatGreen();
  105. const float b = c.getFloatBlue();
  106. y = 0.2999f * r + 0.5870f * g + 0.1140f * b;
  107. i = 0.5957f * r - 0.2744f * g - 0.3212f * b;
  108. q = 0.2114f * r - 0.5225f * g - 0.3113f * b;
  109. alpha = c.getFloatAlpha();
  110. }
  111. Colour toColour() const noexcept
  112. {
  113. return Colour::fromFloatRGBA (y + 0.9563f * i + 0.6210f * q,
  114. y - 0.2721f * i - 0.6474f * q,
  115. y - 1.1070f * i + 1.7046f * q,
  116. alpha);
  117. }
  118. float y, i, q, alpha;
  119. };
  120. }
  121. //==============================================================================
  122. Colour::Colour() noexcept
  123. : argb (0)
  124. {
  125. }
  126. Colour::Colour (const Colour& other) noexcept
  127. : argb (other.argb)
  128. {
  129. }
  130. Colour& Colour::operator= (const Colour& other) noexcept
  131. {
  132. argb = other.argb;
  133. return *this;
  134. }
  135. bool Colour::operator== (const Colour& other) const noexcept { return argb.getARGB() == other.argb.getARGB(); }
  136. bool Colour::operator!= (const Colour& other) const noexcept { return argb.getARGB() != other.argb.getARGB(); }
  137. //==============================================================================
  138. Colour::Colour (const uint32 argb_) noexcept
  139. : argb (argb_)
  140. {
  141. }
  142. Colour::Colour (const uint8 red, const uint8 green, const uint8 blue) noexcept
  143. {
  144. argb.setARGB (0xff, red, green, blue);
  145. }
  146. Colour Colour::fromRGB (const uint8 red, const uint8 green, const uint8 blue) noexcept
  147. {
  148. return Colour (red, green, blue);
  149. }
  150. Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
  151. {
  152. argb.setARGB (alpha, red, green, blue);
  153. }
  154. Colour Colour::fromRGBA (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
  155. {
  156. return Colour (red, green, blue, alpha);
  157. }
  158. Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const float alpha) noexcept
  159. {
  160. argb.setARGB (ColourHelpers::floatToUInt8 (alpha), red, green, blue);
  161. }
  162. Colour Colour::fromFloatRGBA (const float red, const float green, const float blue, const float alpha) noexcept
  163. {
  164. return Colour (ColourHelpers::floatToUInt8 (red), ColourHelpers::floatToUInt8 (green), ColourHelpers::floatToUInt8 (blue), alpha);
  165. }
  166. Colour::Colour (const float hue, const float saturation, const float brightness, const float alpha) noexcept
  167. : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, ColourHelpers::floatToUInt8 (alpha)))
  168. {
  169. }
  170. Colour Colour::fromHSV (const float hue, const float saturation, const float brightness, const float alpha) noexcept
  171. {
  172. return Colour (hue, saturation, brightness, alpha);
  173. }
  174. Colour::Colour (const float hue, const float saturation, const float brightness, const uint8 alpha) noexcept
  175. : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, alpha))
  176. {
  177. }
  178. Colour::~Colour() noexcept
  179. {
  180. }
  181. //==============================================================================
  182. const PixelARGB Colour::getPixelARGB() const noexcept
  183. {
  184. PixelARGB p (argb);
  185. p.premultiply();
  186. return p;
  187. }
  188. uint32 Colour::getARGB() const noexcept
  189. {
  190. return argb.getARGB();
  191. }
  192. //==============================================================================
  193. bool Colour::isTransparent() const noexcept
  194. {
  195. return getAlpha() == 0;
  196. }
  197. bool Colour::isOpaque() const noexcept
  198. {
  199. return getAlpha() == 0xff;
  200. }
  201. Colour Colour::withAlpha (const uint8 newAlpha) const noexcept
  202. {
  203. PixelARGB newCol (argb);
  204. newCol.setAlpha (newAlpha);
  205. return Colour (newCol.getARGB());
  206. }
  207. Colour Colour::withAlpha (const float newAlpha) const noexcept
  208. {
  209. jassert (newAlpha >= 0 && newAlpha <= 1.0f);
  210. PixelARGB newCol (argb);
  211. newCol.setAlpha (ColourHelpers::floatToUInt8 (newAlpha));
  212. return Colour (newCol.getARGB());
  213. }
  214. Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept
  215. {
  216. jassert (alphaMultiplier >= 0);
  217. PixelARGB newCol (argb);
  218. newCol.setAlpha ((uint8) jmin (0xff, roundToInt (alphaMultiplier * newCol.getAlpha())));
  219. return Colour (newCol.getARGB());
  220. }
  221. //==============================================================================
  222. Colour Colour::overlaidWith (const Colour& src) const noexcept
  223. {
  224. const int destAlpha = getAlpha();
  225. if (destAlpha <= 0)
  226. return src;
  227. const int invA = 0xff - (int) src.getAlpha();
  228. const int resA = 0xff - (((0xff - destAlpha) * invA) >> 8);
  229. if (resA <= 0)
  230. return *this;
  231. const int da = (invA * destAlpha) / resA;
  232. return Colour ((uint8) (src.getRed() + ((((int) getRed() - src.getRed()) * da) >> 8)),
  233. (uint8) (src.getGreen() + ((((int) getGreen() - src.getGreen()) * da) >> 8)),
  234. (uint8) (src.getBlue() + ((((int) getBlue() - src.getBlue()) * da) >> 8)),
  235. (uint8) resA);
  236. }
  237. Colour Colour::interpolatedWith (const Colour& other, float proportionOfOther) const noexcept
  238. {
  239. if (proportionOfOther <= 0)
  240. return *this;
  241. if (proportionOfOther >= 1.0f)
  242. return other;
  243. PixelARGB c1 (getPixelARGB());
  244. const PixelARGB c2 (other.getPixelARGB());
  245. c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f));
  246. c1.unpremultiply();
  247. return Colour (c1.getARGB());
  248. }
  249. //==============================================================================
  250. float Colour::getFloatRed() const noexcept { return getRed() / 255.0f; }
  251. float Colour::getFloatGreen() const noexcept { return getGreen() / 255.0f; }
  252. float Colour::getFloatBlue() const noexcept { return getBlue() / 255.0f; }
  253. float Colour::getFloatAlpha() const noexcept { return getAlpha() / 255.0f; }
  254. //==============================================================================
  255. void Colour::getHSB (float& h, float& s, float& v) const noexcept
  256. {
  257. const ColourHelpers::HSB hsb (*this);
  258. h = hsb.hue;
  259. s = hsb.saturation;
  260. v = hsb.brightness;
  261. }
  262. float Colour::getHue() const noexcept { return ColourHelpers::HSB (*this).hue; }
  263. float Colour::getSaturation() const noexcept { return ColourHelpers::HSB (*this).saturation; }
  264. float Colour::getBrightness() const noexcept { return ColourHelpers::HSB (*this).brightness; }
  265. Colour Colour::withHue (float h) const noexcept { ColourHelpers::HSB hsb (*this); hsb.hue = h; return hsb.toColour (*this); }
  266. Colour Colour::withSaturation (float s) const noexcept { ColourHelpers::HSB hsb (*this); hsb.saturation = s; return hsb.toColour (*this); }
  267. Colour Colour::withBrightness (float v) const noexcept { ColourHelpers::HSB hsb (*this); hsb.brightness = v; return hsb.toColour (*this); }
  268. //==============================================================================
  269. Colour Colour::withRotatedHue (const float amountToRotate) const noexcept
  270. {
  271. ColourHelpers::HSB hsb (*this);
  272. hsb.hue += amountToRotate;
  273. return hsb.toColour (*this);
  274. }
  275. Colour Colour::withMultipliedSaturation (const float amount) const noexcept
  276. {
  277. ColourHelpers::HSB hsb (*this);
  278. hsb.saturation = jmin (1.0f, hsb.saturation * amount);
  279. return hsb.toColour (*this);
  280. }
  281. Colour Colour::withMultipliedBrightness (const float amount) const noexcept
  282. {
  283. ColourHelpers::HSB hsb (*this);
  284. hsb.brightness = jmin (1.0f, hsb.brightness * amount);
  285. return hsb.toColour (*this);
  286. }
  287. //==============================================================================
  288. Colour Colour::brighter (float amount) const noexcept
  289. {
  290. amount = 1.0f / (1.0f + amount);
  291. return Colour ((uint8) (255 - (amount * (255 - getRed()))),
  292. (uint8) (255 - (amount * (255 - getGreen()))),
  293. (uint8) (255 - (amount * (255 - getBlue()))),
  294. getAlpha());
  295. }
  296. Colour Colour::darker (float amount) const noexcept
  297. {
  298. amount = 1.0f / (1.0f + amount);
  299. return Colour ((uint8) (amount * getRed()),
  300. (uint8) (amount * getGreen()),
  301. (uint8) (amount * getBlue()),
  302. getAlpha());
  303. }
  304. //==============================================================================
  305. Colour Colour::greyLevel (const float brightness) noexcept
  306. {
  307. const uint8 level = ColourHelpers::floatToUInt8 (brightness);
  308. return Colour (level, level, level);
  309. }
  310. //==============================================================================
  311. Colour Colour::contrasting (const float amount) const noexcept
  312. {
  313. return overlaidWith ((ColourHelpers::getPerceivedBrightness (*this) >= 0.5f
  314. ? Colours::black
  315. : Colours::white).withAlpha (amount));
  316. }
  317. Colour Colour::contrasting (const Colour& target, float minContrast) const noexcept
  318. {
  319. const ColourHelpers::YIQ bg (*this);
  320. ColourHelpers::YIQ fg (target);
  321. if (fabs (bg.y - fg.y) >= minContrast)
  322. return target;
  323. const float y1 = jmax (0.0f, bg.y - minContrast);
  324. const float y2 = jmin (1.0f, bg.y + minContrast);
  325. fg.y = (fabs (y1 - bg.y) > fabs (y2 - bg.y)) ? y1 : y2;
  326. return fg.toColour();
  327. }
  328. Colour Colour::contrasting (const Colour& colour1,
  329. const Colour& colour2) noexcept
  330. {
  331. const float b1 = ColourHelpers::getPerceivedBrightness (colour1);
  332. const float b2 = ColourHelpers::getPerceivedBrightness (colour2);
  333. float best = 0.0f;
  334. float bestDist = 0.0f;
  335. for (float i = 0.0f; i < 1.0f; i += 0.02f)
  336. {
  337. const float d1 = std::abs (i - b1);
  338. const float d2 = std::abs (i - b2);
  339. const float dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2);
  340. if (dist > bestDist)
  341. {
  342. best = i;
  343. bestDist = dist;
  344. }
  345. }
  346. return colour1.overlaidWith (colour2.withMultipliedAlpha (0.5f))
  347. .withBrightness (best);
  348. }
  349. //==============================================================================
  350. String Colour::toString() const
  351. {
  352. return String::toHexString ((int) argb.getARGB());
  353. }
  354. Colour Colour::fromString (const String& encodedColourString)
  355. {
  356. return Colour ((uint32) encodedColourString.getHexValue32());
  357. }
  358. String Colour::toDisplayString (const bool includeAlphaValue) const
  359. {
  360. return String::toHexString ((int) (argb.getARGB() & (includeAlphaValue ? 0xffffffff : 0xffffff)))
  361. .paddedLeft ('0', includeAlphaValue ? 8 : 6)
  362. .toUpperCase();
  363. }