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.

464 lines
15KB

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