Audio plugin host https://kx.studio/carla
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.

462 lines
15KB

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