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.

502 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. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. namespace ColourHelpers
  21. {
  22. uint8 floatAlphaToInt (const float alpha) noexcept
  23. {
  24. return (uint8) jlimit (0, 0xff, roundToInt (alpha * 255.0f));
  25. }
  26. // This is an adjusted brightness value, based on the way the human eye responds to
  27. // different colour channels..
  28. float getPerceivedBrightness (float r, float g, float b) noexcept
  29. {
  30. return std::sqrt (r * r * 0.241f
  31. + g * g * 0.691f
  32. + b * b * 0.068f);
  33. }
  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 void convertHSBtoRGB (float h, float s, float v,
  80. uint8& r, uint8& g, uint8& b) noexcept
  81. {
  82. v = jlimit (0.0f, 1.0f, v);
  83. v *= 255.0f;
  84. const uint8 intV = (uint8) roundToInt (v);
  85. if (s <= 0)
  86. {
  87. r = intV;
  88. g = intV;
  89. b = intV;
  90. }
  91. else
  92. {
  93. s = jmin (1.0f, s);
  94. h = (h - std::floor (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors
  95. const float f = h - std::floor (h);
  96. const uint8 x = (uint8) roundToInt (v * (1.0f - s));
  97. if (h < 1.0f)
  98. {
  99. r = intV;
  100. g = (uint8) roundToInt (v * (1.0f - (s * (1.0f - f))));
  101. b = x;
  102. }
  103. else if (h < 2.0f)
  104. {
  105. r = (uint8) roundToInt (v * (1.0f - s * f));
  106. g = intV;
  107. b = x;
  108. }
  109. else if (h < 3.0f)
  110. {
  111. r = x;
  112. g = intV;
  113. b = (uint8) roundToInt (v * (1.0f - (s * (1.0f - f))));
  114. }
  115. else if (h < 4.0f)
  116. {
  117. r = x;
  118. g = (uint8) roundToInt (v * (1.0f - s * f));
  119. b = intV;
  120. }
  121. else if (h < 5.0f)
  122. {
  123. r = (uint8) roundToInt (v * (1.0f - (s * (1.0f - f))));
  124. g = x;
  125. b = intV;
  126. }
  127. else
  128. {
  129. r = intV;
  130. g = x;
  131. b = (uint8) roundToInt (v * (1.0f - s * f));
  132. }
  133. }
  134. }
  135. float hue, saturation, brightness;
  136. };
  137. //==============================================================================
  138. Colour::Colour() noexcept
  139. : argb (0)
  140. {
  141. }
  142. Colour::Colour (const Colour& other) noexcept
  143. : argb (other.argb)
  144. {
  145. }
  146. Colour& Colour::operator= (const Colour& other) noexcept
  147. {
  148. argb = other.argb;
  149. return *this;
  150. }
  151. bool Colour::operator== (const Colour& other) const noexcept
  152. {
  153. return argb.getARGB() == other.argb.getARGB();
  154. }
  155. bool Colour::operator!= (const Colour& other) const noexcept
  156. {
  157. return argb.getARGB() != other.argb.getARGB();
  158. }
  159. //==============================================================================
  160. Colour::Colour (const uint32 argb_) noexcept
  161. : argb (argb_)
  162. {
  163. }
  164. Colour::Colour (const uint8 red,
  165. const uint8 green,
  166. const uint8 blue) noexcept
  167. {
  168. argb.setARGB (0xff, red, green, blue);
  169. }
  170. Colour Colour::fromRGB (const uint8 red,
  171. const uint8 green,
  172. const uint8 blue) noexcept
  173. {
  174. return Colour (red, green, blue);
  175. }
  176. Colour::Colour (const uint8 red,
  177. const uint8 green,
  178. const uint8 blue,
  179. const uint8 alpha) noexcept
  180. {
  181. argb.setARGB (alpha, red, green, blue);
  182. }
  183. Colour Colour::fromRGBA (const uint8 red,
  184. const uint8 green,
  185. const uint8 blue,
  186. const uint8 alpha) noexcept
  187. {
  188. return Colour (red, green, blue, alpha);
  189. }
  190. Colour::Colour (const uint8 red,
  191. const uint8 green,
  192. const uint8 blue,
  193. const float alpha) noexcept
  194. {
  195. argb.setARGB (ColourHelpers::floatAlphaToInt (alpha), red, green, blue);
  196. }
  197. Colour Colour::fromRGBAFloat (const uint8 red,
  198. const uint8 green,
  199. const uint8 blue,
  200. const float alpha) noexcept
  201. {
  202. return Colour (red, green, blue, alpha);
  203. }
  204. Colour::Colour (const float hue,
  205. const float saturation,
  206. const float brightness,
  207. const float alpha) noexcept
  208. {
  209. uint8 r, g, b;
  210. HSB::convertHSBtoRGB (hue, saturation, brightness, r, g, b);
  211. argb.setARGB (ColourHelpers::floatAlphaToInt (alpha), r, g, b);
  212. }
  213. Colour Colour::fromHSV (const float hue,
  214. const float saturation,
  215. const float brightness,
  216. const float alpha) noexcept
  217. {
  218. return Colour (hue, saturation, brightness, alpha);
  219. }
  220. Colour::Colour (const float hue,
  221. const float saturation,
  222. const float brightness,
  223. const uint8 alpha) noexcept
  224. {
  225. uint8 r, g, b;
  226. HSB::convertHSBtoRGB (hue, saturation, brightness, r, g, b);
  227. argb.setARGB (alpha, r, g, b);
  228. }
  229. Colour::~Colour() noexcept
  230. {
  231. }
  232. //==============================================================================
  233. const PixelARGB Colour::getPixelARGB() const noexcept
  234. {
  235. PixelARGB p (argb);
  236. p.premultiply();
  237. return p;
  238. }
  239. uint32 Colour::getARGB() const noexcept
  240. {
  241. return argb.getARGB();
  242. }
  243. //==============================================================================
  244. bool Colour::isTransparent() const noexcept
  245. {
  246. return getAlpha() == 0;
  247. }
  248. bool Colour::isOpaque() const noexcept
  249. {
  250. return getAlpha() == 0xff;
  251. }
  252. Colour Colour::withAlpha (const uint8 newAlpha) const noexcept
  253. {
  254. PixelARGB newCol (argb);
  255. newCol.setAlpha (newAlpha);
  256. return Colour (newCol.getARGB());
  257. }
  258. Colour Colour::withAlpha (const float newAlpha) const noexcept
  259. {
  260. jassert (newAlpha >= 0 && newAlpha <= 1.0f);
  261. PixelARGB newCol (argb);
  262. newCol.setAlpha (ColourHelpers::floatAlphaToInt (newAlpha));
  263. return Colour (newCol.getARGB());
  264. }
  265. Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept
  266. {
  267. jassert (alphaMultiplier >= 0);
  268. PixelARGB newCol (argb);
  269. newCol.setAlpha ((uint8) jmin (0xff, roundToInt (alphaMultiplier * newCol.getAlpha())));
  270. return Colour (newCol.getARGB());
  271. }
  272. //==============================================================================
  273. Colour Colour::overlaidWith (const Colour& src) const noexcept
  274. {
  275. const int destAlpha = getAlpha();
  276. if (destAlpha > 0)
  277. {
  278. const int invA = 0xff - (int) src.getAlpha();
  279. const int resA = 0xff - (((0xff - destAlpha) * invA) >> 8);
  280. if (resA > 0)
  281. {
  282. const int da = (invA * destAlpha) / resA;
  283. return Colour ((uint8) (src.getRed() + ((((int) getRed() - src.getRed()) * da) >> 8)),
  284. (uint8) (src.getGreen() + ((((int) getGreen() - src.getGreen()) * da) >> 8)),
  285. (uint8) (src.getBlue() + ((((int) getBlue() - src.getBlue()) * da) >> 8)),
  286. (uint8) resA);
  287. }
  288. return *this;
  289. }
  290. else
  291. {
  292. return src;
  293. }
  294. }
  295. Colour Colour::interpolatedWith (const Colour& other, float proportionOfOther) const noexcept
  296. {
  297. if (proportionOfOther <= 0)
  298. return *this;
  299. if (proportionOfOther >= 1.0f)
  300. return other;
  301. PixelARGB c1 (getPixelARGB());
  302. const PixelARGB c2 (other.getPixelARGB());
  303. c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f));
  304. c1.unpremultiply();
  305. return Colour (c1.getARGB());
  306. }
  307. //==============================================================================
  308. float Colour::getFloatRed() const noexcept { return getRed() / 255.0f; }
  309. float Colour::getFloatGreen() const noexcept { return getGreen() / 255.0f; }
  310. float Colour::getFloatBlue() const noexcept { return getBlue() / 255.0f; }
  311. float Colour::getFloatAlpha() const noexcept { return getAlpha() / 255.0f; }
  312. //==============================================================================
  313. void Colour::getHSB (float& h, float& s, float& v) const noexcept
  314. {
  315. const HSB hsb (*this);
  316. h = hsb.hue;
  317. s = hsb.saturation;
  318. v = hsb.brightness;
  319. }
  320. //==============================================================================
  321. float Colour::getHue() const noexcept { return HSB (*this).hue; }
  322. float Colour::getSaturation() const noexcept { return HSB (*this).saturation; }
  323. float Colour::getBrightness() const noexcept { return HSB (*this).brightness; }
  324. Colour Colour::withHue (float h) const noexcept { HSB hsb (*this); hsb.hue = h; return hsb.toColour (*this); }
  325. Colour Colour::withSaturation (float s) const noexcept { HSB hsb (*this); hsb.saturation = s; return hsb.toColour (*this); }
  326. Colour Colour::withBrightness (float v) const noexcept { HSB hsb (*this); hsb.brightness = v; return hsb.toColour (*this); }
  327. //==============================================================================
  328. Colour Colour::withRotatedHue (const float amountToRotate) const noexcept
  329. {
  330. HSB hsb (*this);
  331. hsb.hue += amountToRotate;
  332. return hsb.toColour (*this);
  333. }
  334. //==============================================================================
  335. Colour Colour::withMultipliedSaturation (const float amount) const noexcept
  336. {
  337. HSB hsb (*this);
  338. hsb.saturation = jmin (1.0f, hsb.saturation * amount);
  339. return hsb.toColour (*this);
  340. }
  341. //==============================================================================
  342. Colour Colour::withMultipliedBrightness (const float amount) const noexcept
  343. {
  344. HSB hsb (*this);
  345. hsb.brightness *= amount;
  346. if (hsb.brightness > 1.0f)
  347. hsb.brightness = 1.0f;
  348. return hsb.toColour (*this);
  349. }
  350. //==============================================================================
  351. Colour Colour::brighter (float amount) const noexcept
  352. {
  353. amount = 1.0f / (1.0f + amount);
  354. return Colour ((uint8) (255 - (amount * (255 - getRed()))),
  355. (uint8) (255 - (amount * (255 - getGreen()))),
  356. (uint8) (255 - (amount * (255 - getBlue()))),
  357. getAlpha());
  358. }
  359. Colour Colour::darker (float amount) const noexcept
  360. {
  361. amount = 1.0f / (1.0f + amount);
  362. return Colour ((uint8) (amount * getRed()),
  363. (uint8) (amount * getGreen()),
  364. (uint8) (amount * getBlue()),
  365. getAlpha());
  366. }
  367. //==============================================================================
  368. Colour Colour::greyLevel (const float brightness) noexcept
  369. {
  370. const uint8 level
  371. = (uint8) jlimit (0x00, 0xff, roundToInt (brightness * 255.0f));
  372. return Colour (level, level, level);
  373. }
  374. //==============================================================================
  375. Colour Colour::contrasting (const float amount) const noexcept
  376. {
  377. return overlaidWith ((ColourHelpers::getPerceivedBrightness (getFloatRed(), getFloatGreen(), getFloatBlue()) >= 0.5f
  378. ? Colours::black
  379. : Colours::white).withAlpha (amount));
  380. }
  381. Colour Colour::contrasting (const Colour& colour1,
  382. const Colour& colour2) noexcept
  383. {
  384. const float b1 = colour1.getBrightness();
  385. const float b2 = colour2.getBrightness();
  386. float best = 0.0f;
  387. float bestDist = 0.0f;
  388. for (float i = 0.0f; i < 1.0f; i += 0.02f)
  389. {
  390. const float d1 = std::abs (i - b1);
  391. const float d2 = std::abs (i - b2);
  392. const float dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2);
  393. if (dist > bestDist)
  394. {
  395. best = i;
  396. bestDist = dist;
  397. }
  398. }
  399. return colour1.overlaidWith (colour2.withMultipliedAlpha (0.5f))
  400. .withBrightness (best);
  401. }
  402. //==============================================================================
  403. String Colour::toString() const
  404. {
  405. return String::toHexString ((int) argb.getARGB());
  406. }
  407. Colour Colour::fromString (const String& encodedColourString)
  408. {
  409. return Colour ((uint32) encodedColourString.getHexValue32());
  410. }
  411. String Colour::toDisplayString (const bool includeAlphaValue) const
  412. {
  413. return String::toHexString ((int) (argb.getARGB() & (includeAlphaValue ? 0xffffffff : 0xffffff)))
  414. .paddedLeft ('0', includeAlphaValue ? 8 : 6)
  415. .toUpperCase();
  416. }
  417. END_JUCE_NAMESPACE