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.

406 lines
13KB

  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 (float r, float g, float b) noexcept
  27. {
  28. return std::sqrt (r * r * 0.241f
  29. + g * g * 0.691f
  30. + b * b * 0.068f);
  31. }
  32. }
  33. //==============================================================================
  34. struct HSB
  35. {
  36. HSB (const Colour& col) noexcept
  37. {
  38. const int r = col.getRed();
  39. const int g = col.getGreen();
  40. const int b = col.getBlue();
  41. const int hi = jmax (r, g, b);
  42. const int lo = jmin (r, g, b);
  43. if (hi != 0)
  44. {
  45. saturation = (hi - lo) / (float) hi;
  46. if (saturation > 0)
  47. {
  48. const float invDiff = 1.0f / (hi - lo);
  49. const float red = (hi - r) * invDiff;
  50. const float green = (hi - g) * invDiff;
  51. const float blue = (hi - b) * invDiff;
  52. if (r == hi)
  53. hue = blue - green;
  54. else if (g == hi)
  55. hue = 2.0f + red - blue;
  56. else
  57. hue = 4.0f + green - red;
  58. hue *= 1.0f / 6.0f;
  59. if (hue < 0)
  60. ++hue;
  61. }
  62. else
  63. {
  64. hue = 0;
  65. }
  66. }
  67. else
  68. {
  69. saturation = hue = 0;
  70. }
  71. brightness = hi / 255.0f;
  72. }
  73. Colour toColour (const Colour& original) const noexcept
  74. {
  75. return Colour (hue, saturation, brightness, original.getAlpha());
  76. }
  77. static PixelARGB toRGB (float h, float s, float v, const uint8 alpha) noexcept
  78. {
  79. v = jlimit (0.0f, 255.0f, v * 255.0f);
  80. const uint8 intV = (uint8) roundToInt (v);
  81. if (s <= 0)
  82. return PixelARGB (alpha, intV, intV, intV);
  83. s = jmin (1.0f, s);
  84. h = (h - std::floor (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors
  85. const float f = h - std::floor (h);
  86. const uint8 x = (uint8) roundToInt (v * (1.0f - s));
  87. if (h < 1.0f) return PixelARGB (alpha, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x);
  88. if (h < 2.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - s * f)), intV, x);
  89. if (h < 3.0f) return PixelARGB (alpha, x, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))));
  90. if (h < 4.0f) return PixelARGB (alpha, x, (uint8) roundToInt (v * (1.0f - s * f)), intV);
  91. if (h < 5.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x, intV);
  92. else return PixelARGB (alpha, intV, x, (uint8) roundToInt (v * (1.0f - s * f)));
  93. }
  94. float hue, saturation, brightness;
  95. };
  96. //==============================================================================
  97. Colour::Colour() noexcept
  98. : argb (0)
  99. {
  100. }
  101. Colour::Colour (const Colour& other) noexcept
  102. : argb (other.argb)
  103. {
  104. }
  105. Colour& Colour::operator= (const Colour& other) noexcept
  106. {
  107. argb = other.argb;
  108. return *this;
  109. }
  110. bool Colour::operator== (const Colour& other) const noexcept { return argb.getARGB() == other.argb.getARGB(); }
  111. bool Colour::operator!= (const Colour& other) const noexcept { return argb.getARGB() != other.argb.getARGB(); }
  112. //==============================================================================
  113. Colour::Colour (const uint32 argb_) noexcept
  114. : argb (argb_)
  115. {
  116. }
  117. Colour::Colour (const uint8 red, const uint8 green, const uint8 blue) noexcept
  118. {
  119. argb.setARGB (0xff, red, green, blue);
  120. }
  121. Colour Colour::fromRGB (const uint8 red, const uint8 green, const uint8 blue) noexcept
  122. {
  123. return Colour (red, green, blue);
  124. }
  125. Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
  126. {
  127. argb.setARGB (alpha, red, green, blue);
  128. }
  129. Colour Colour::fromRGBA (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
  130. {
  131. return Colour (red, green, blue, alpha);
  132. }
  133. Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const float alpha) noexcept
  134. {
  135. argb.setARGB (ColourHelpers::floatToUInt8 (alpha), red, green, blue);
  136. }
  137. Colour Colour::fromFloatRGBA (const float red, const float green, const float blue, const float alpha) noexcept
  138. {
  139. return Colour (ColourHelpers::floatToUInt8 (red), ColourHelpers::floatToUInt8 (green), ColourHelpers::floatToUInt8 (blue), alpha);
  140. }
  141. Colour::Colour (const float hue, const float saturation, const float brightness, const float alpha) noexcept
  142. : argb (HSB::toRGB (hue, saturation, brightness, ColourHelpers::floatToUInt8 (alpha)))
  143. {
  144. }
  145. Colour Colour::fromHSV (const float hue, const float saturation, const float brightness, const float alpha) noexcept
  146. {
  147. return Colour (hue, saturation, brightness, alpha);
  148. }
  149. Colour::Colour (const float hue, const float saturation, const float brightness, const uint8 alpha) noexcept
  150. : argb (HSB::toRGB (hue, saturation, brightness, alpha))
  151. {
  152. }
  153. Colour::~Colour() noexcept
  154. {
  155. }
  156. //==============================================================================
  157. const PixelARGB Colour::getPixelARGB() const noexcept
  158. {
  159. PixelARGB p (argb);
  160. p.premultiply();
  161. return p;
  162. }
  163. uint32 Colour::getARGB() const noexcept
  164. {
  165. return argb.getARGB();
  166. }
  167. //==============================================================================
  168. bool Colour::isTransparent() const noexcept
  169. {
  170. return getAlpha() == 0;
  171. }
  172. bool Colour::isOpaque() const noexcept
  173. {
  174. return getAlpha() == 0xff;
  175. }
  176. Colour Colour::withAlpha (const uint8 newAlpha) const noexcept
  177. {
  178. PixelARGB newCol (argb);
  179. newCol.setAlpha (newAlpha);
  180. return Colour (newCol.getARGB());
  181. }
  182. Colour Colour::withAlpha (const float newAlpha) const noexcept
  183. {
  184. jassert (newAlpha >= 0 && newAlpha <= 1.0f);
  185. PixelARGB newCol (argb);
  186. newCol.setAlpha (ColourHelpers::floatToUInt8 (newAlpha));
  187. return Colour (newCol.getARGB());
  188. }
  189. Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept
  190. {
  191. jassert (alphaMultiplier >= 0);
  192. PixelARGB newCol (argb);
  193. newCol.setAlpha ((uint8) jmin (0xff, roundToInt (alphaMultiplier * newCol.getAlpha())));
  194. return Colour (newCol.getARGB());
  195. }
  196. //==============================================================================
  197. Colour Colour::overlaidWith (const Colour& src) const noexcept
  198. {
  199. const int destAlpha = getAlpha();
  200. if (destAlpha <= 0)
  201. return src;
  202. const int invA = 0xff - (int) src.getAlpha();
  203. const int resA = 0xff - (((0xff - destAlpha) * invA) >> 8);
  204. if (resA <= 0)
  205. return *this;
  206. const int da = (invA * destAlpha) / resA;
  207. return Colour ((uint8) (src.getRed() + ((((int) getRed() - src.getRed()) * da) >> 8)),
  208. (uint8) (src.getGreen() + ((((int) getGreen() - src.getGreen()) * da) >> 8)),
  209. (uint8) (src.getBlue() + ((((int) getBlue() - src.getBlue()) * da) >> 8)),
  210. (uint8) resA);
  211. }
  212. Colour Colour::interpolatedWith (const Colour& other, float proportionOfOther) const noexcept
  213. {
  214. if (proportionOfOther <= 0)
  215. return *this;
  216. if (proportionOfOther >= 1.0f)
  217. return other;
  218. PixelARGB c1 (getPixelARGB());
  219. const PixelARGB c2 (other.getPixelARGB());
  220. c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f));
  221. c1.unpremultiply();
  222. return Colour (c1.getARGB());
  223. }
  224. //==============================================================================
  225. float Colour::getFloatRed() const noexcept { return getRed() / 255.0f; }
  226. float Colour::getFloatGreen() const noexcept { return getGreen() / 255.0f; }
  227. float Colour::getFloatBlue() const noexcept { return getBlue() / 255.0f; }
  228. float Colour::getFloatAlpha() const noexcept { return getAlpha() / 255.0f; }
  229. //==============================================================================
  230. void Colour::getHSB (float& h, float& s, float& v) const noexcept
  231. {
  232. const HSB hsb (*this);
  233. h = hsb.hue;
  234. s = hsb.saturation;
  235. v = hsb.brightness;
  236. }
  237. float Colour::getHue() const noexcept { return HSB (*this).hue; }
  238. float Colour::getSaturation() const noexcept { return HSB (*this).saturation; }
  239. float Colour::getBrightness() const noexcept { return HSB (*this).brightness; }
  240. Colour Colour::withHue (float h) const noexcept { HSB hsb (*this); hsb.hue = h; return hsb.toColour (*this); }
  241. Colour Colour::withSaturation (float s) const noexcept { HSB hsb (*this); hsb.saturation = s; return hsb.toColour (*this); }
  242. Colour Colour::withBrightness (float v) const noexcept { HSB hsb (*this); hsb.brightness = v; return hsb.toColour (*this); }
  243. //==============================================================================
  244. Colour Colour::withRotatedHue (const float amountToRotate) const noexcept
  245. {
  246. HSB hsb (*this);
  247. hsb.hue += amountToRotate;
  248. return hsb.toColour (*this);
  249. }
  250. Colour Colour::withMultipliedSaturation (const float amount) const noexcept
  251. {
  252. HSB hsb (*this);
  253. hsb.saturation = jmin (1.0f, hsb.saturation * amount);
  254. return hsb.toColour (*this);
  255. }
  256. Colour Colour::withMultipliedBrightness (const float amount) const noexcept
  257. {
  258. HSB hsb (*this);
  259. hsb.brightness = jmin (1.0f, hsb.brightness * amount);
  260. return hsb.toColour (*this);
  261. }
  262. //==============================================================================
  263. Colour Colour::brighter (float amount) const noexcept
  264. {
  265. amount = 1.0f / (1.0f + amount);
  266. return Colour ((uint8) (255 - (amount * (255 - getRed()))),
  267. (uint8) (255 - (amount * (255 - getGreen()))),
  268. (uint8) (255 - (amount * (255 - getBlue()))),
  269. getAlpha());
  270. }
  271. Colour Colour::darker (float amount) const noexcept
  272. {
  273. amount = 1.0f / (1.0f + amount);
  274. return Colour ((uint8) (amount * getRed()),
  275. (uint8) (amount * getGreen()),
  276. (uint8) (amount * getBlue()),
  277. getAlpha());
  278. }
  279. //==============================================================================
  280. Colour Colour::greyLevel (const float brightness) noexcept
  281. {
  282. const uint8 level = ColourHelpers::floatToUInt8 (brightness);
  283. return Colour (level, level, level);
  284. }
  285. //==============================================================================
  286. Colour Colour::contrasting (const float amount) const noexcept
  287. {
  288. return overlaidWith ((ColourHelpers::getPerceivedBrightness (getFloatRed(), getFloatGreen(), getFloatBlue()) >= 0.5f
  289. ? Colours::black
  290. : Colours::white).withAlpha (amount));
  291. }
  292. Colour Colour::contrasting (const Colour& colour1,
  293. const Colour& colour2) noexcept
  294. {
  295. const float b1 = colour1.getBrightness();
  296. const float b2 = colour2.getBrightness();
  297. float best = 0.0f;
  298. float bestDist = 0.0f;
  299. for (float i = 0.0f; i < 1.0f; i += 0.02f)
  300. {
  301. const float d1 = std::abs (i - b1);
  302. const float d2 = std::abs (i - b2);
  303. const float dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2);
  304. if (dist > bestDist)
  305. {
  306. best = i;
  307. bestDist = dist;
  308. }
  309. }
  310. return colour1.overlaidWith (colour2.withMultipliedAlpha (0.5f))
  311. .withBrightness (best);
  312. }
  313. //==============================================================================
  314. String Colour::toString() const
  315. {
  316. return String::toHexString ((int) argb.getARGB());
  317. }
  318. Colour Colour::fromString (const String& encodedColourString)
  319. {
  320. return Colour ((uint32) encodedColourString.getHexValue32());
  321. }
  322. String Colour::toDisplayString (const bool includeAlphaValue) const
  323. {
  324. return String::toHexString ((int) (argb.getARGB() & (includeAlphaValue ? 0xffffffff : 0xffffff)))
  325. .paddedLeft ('0', includeAlphaValue ? 8 : 6)
  326. .toUpperCase();
  327. }