Browse Source

Added HSL support to Colour and use Colour::fromHSL() when parsing SVG HSL colours

tags/2021-05-28
ed 5 years ago
parent
commit
7553b8dc1b
4 changed files with 396 additions and 89 deletions
  1. +1
    -0
      modules/juce_core/unit_tests/juce_UnitTestCategories.h
  2. +348
    -75
      modules/juce_graphics/colour/juce_Colour.cpp
  3. +43
    -10
      modules/juce_graphics/colour/juce_Colour.h
  4. +4
    -4
      modules/juce_gui_basics/drawables/juce_SVGParser.cpp

+ 1
- 0
modules/juce_core/unit_tests/juce_UnitTestCategories.h View File

@@ -35,6 +35,7 @@ namespace UnitTestCategories
static const String dsp { "DSP" }; static const String dsp { "DSP" };
static const String files { "Files" }; static const String files { "Files" };
static const String function { "Function" }; static const String function { "Function" };
static const String graphics { "Graphics" };
static const String gui { "GUI" }; static const String gui { "GUI" };
static const String json { "JSON" }; static const String json { "JSON" };
static const String maths { "Maths" }; static const String maths { "Maths" };


+ 348
- 75
modules/juce_graphics/colour/juce_Colour.cpp View File

@@ -21,58 +21,121 @@ namespace juce
namespace ColourHelpers namespace ColourHelpers
{ {
static uint8 floatToUInt8 (const float n) noexcept
static uint8 floatToUInt8 (float n) noexcept
{ {
return n <= 0.0f ? 0 : (n >= 1.0f ? 255 : static_cast<uint8> (n * 255.996f));
return n <= 0.0f ? 0 : (n >= 1.0f ? 255 : (uint8) roundToInt (n * 255.0f));
} }
static float getHue (Colour col)
{
auto r = (int) col.getRed();
auto g = (int) col.getGreen();
auto b = (int) col.getBlue();
auto hi = jmax (r, g, b);
auto lo = jmin (r, g, b);
float hue = 0.0f;
if (hi > 0)
{
auto invDiff = 1.0f / (hi - lo);
auto red = (hi - r) * invDiff;
auto green = (hi - g) * invDiff;
auto blue = (hi - b) * invDiff;
if (r == hi) hue = blue - green;
else if (g == hi) hue = 2.0f + red - blue;
else hue = 4.0f + green - red;
hue *= 1.0f / 6.0f;
if (hue < 0.0f)
hue += 1.0f;
}
return hue;
}
//==============================================================================
struct HSL
{
HSL (Colour col) noexcept
{
auto r = (int) col.getRed();
auto g = (int) col.getGreen();
auto b = (int) col.getBlue();
auto hi = jmax (r, g, b);
auto lo = jmin (r, g, b);
if (hi > 0)
{
lightness = ((hi + lo) / 2.0f) / 255.0f;
if (lightness > 0.0f)
hue = getHue (col);
saturation = (hi - lo) / (1.0f - std::abs ((2.0f * lightness) - 1.0f));
}
}
Colour toColour (Colour original) const noexcept
{
return Colour::fromHSL (hue, saturation, lightness, original.getAlpha());
}
static PixelARGB toRGB (float h, float s, float l, uint8 alpha) noexcept
{
auto v = l < 0.5f ? l * (1.0f + s) : l + s - (l * s);
if (approximatelyEqual (v, 0.0f))
return PixelARGB (alpha, 0, 0, 0);
auto min = (2.0f * l) - v;
auto sv = (v - min) / v;
h = jlimit (0.0f, 360.0f, h * 360.0f) / 60.0f;
auto f = h - std::floor (h);
auto vsf = v * sv * f;
auto mid1 = min + vsf;
auto mid2 = v - vsf;
if (h < 1.0f) return PixelARGB (alpha, floatToUInt8 (v), floatToUInt8 (mid1), floatToUInt8 (min));
else if (h < 2.0f) return PixelARGB (alpha, floatToUInt8 (mid2), floatToUInt8 (v), floatToUInt8 (min));
else if (h < 3.0f) return PixelARGB (alpha, floatToUInt8 (min), floatToUInt8 (v), floatToUInt8 (mid1));
else if (h < 4.0f) return PixelARGB (alpha, floatToUInt8 (min), floatToUInt8 (mid2), floatToUInt8 (v));
else if (h < 5.0f) return PixelARGB (alpha, floatToUInt8 (mid1), floatToUInt8 (min), floatToUInt8 (v));
else if (h < 6.0f) return PixelARGB (alpha, floatToUInt8 (v), floatToUInt8 (min), floatToUInt8 (mid2));
return PixelARGB (alpha, 0, 0, 0);
}
float hue = 0.0f, saturation = 0.0f, lightness = 0.0f;
};
//============================================================================== //==============================================================================
struct HSB struct HSB
{ {
HSB (Colour col) noexcept HSB (Colour col) noexcept
{ {
const int r = col.getRed();
const int g = col.getGreen();
const int b = col.getBlue();
auto r = (int) col.getRed();
auto g = (int) col.getGreen();
auto b = (int) col.getBlue();
const int hi = jmax (r, g, b);
const int lo = jmin (r, g, b);
auto hi = jmax (r, g, b);
auto lo = jmin (r, g, b);
if (hi != 0)
if (hi > 0)
{ {
saturation = (hi - lo) / (float) hi; saturation = (hi - lo) / (float) hi;
if (saturation > 0)
{
const float invDiff = 1.0f / (hi - lo);
const float red = (hi - r) * invDiff;
const float green = (hi - g) * invDiff;
const float blue = (hi - b) * invDiff;
if (r == hi)
hue = blue - green;
else if (g == hi)
hue = 2.0f + red - blue;
else
hue = 4.0f + green - red;
hue *= 1.0f / 6.0f;
if (hue < 0)
++hue;
}
else
{
hue = 0;
}
}
else
{
saturation = hue = 0;
}
if (saturation > 0.0f)
hue = getHue (col);
brightness = hi / 255.0f;
brightness = hi / 255.0f;
}
} }
Colour toColour (Colour original) const noexcept Colour toColour (Colour original) const noexcept
@@ -80,18 +143,18 @@ namespace ColourHelpers
return Colour (hue, saturation, brightness, original.getAlpha()); return Colour (hue, saturation, brightness, original.getAlpha());
} }
static PixelARGB toRGB (float h, float s, float v, const uint8 alpha) noexcept
static PixelARGB toRGB (float h, float s, float v, uint8 alpha) noexcept
{ {
v = jlimit (0.0f, 255.0f, v * 255.0f); v = jlimit (0.0f, 255.0f, v * 255.0f);
const uint8 intV = (uint8) roundToInt (v);
auto intV = (uint8) roundToInt (v);
if (s <= 0) if (s <= 0)
return PixelARGB (alpha, intV, intV, intV); return PixelARGB (alpha, intV, intV, intV);
s = jmin (1.0f, s); s = jmin (1.0f, s);
h = (h - std::floor (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors
const float f = h - std::floor (h);
const uint8 x = (uint8) roundToInt (v * (1.0f - s));
h = jlimit (0.0f, 360.0f, h * 360.0f) / 60.0f;
auto f = h - std::floor (h);
auto x = (uint8) roundToInt (v * (1.0f - s));
if (h < 1.0f) return PixelARGB (alpha, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x); if (h < 1.0f) return PixelARGB (alpha, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x);
if (h < 2.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - s * f)), intV, x); if (h < 2.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - s * f)), intV, x);
@@ -101,7 +164,7 @@ namespace ColourHelpers
return PixelARGB (alpha, intV, x, (uint8) roundToInt (v * (1.0f - s * f))); return PixelARGB (alpha, intV, x, (uint8) roundToInt (v * (1.0f - s * f)));
} }
float hue, saturation, brightness;
float hue = 0.0f, saturation = 0.0f, brightness = 0.0f;
}; };
//============================================================================== //==============================================================================
@@ -109,9 +172,9 @@ namespace ColourHelpers
{ {
YIQ (Colour c) noexcept YIQ (Colour c) noexcept
{ {
const float r = c.getFloatRed();
const float g = c.getFloatGreen();
const float b = c.getFloatBlue();
auto r = c.getFloatRed();
auto g = c.getFloatGreen();
auto b = c.getFloatBlue();
y = 0.2999f * r + 0.5870f * g + 0.1140f * b; y = 0.2999f * r + 0.5870f * g + 0.1140f * b;
i = 0.5957f * r - 0.2744f * g - 0.3212f * b; i = 0.5957f * r - 0.2744f * g - 0.3212f * b;
@@ -127,7 +190,7 @@ namespace ColourHelpers
alpha); alpha);
} }
float y, i, q, alpha;
float y = 0.0f, i = 0.0f, q = 0.0f, alpha = 0.0f;
}; };
} }
@@ -136,7 +199,7 @@ bool Colour::operator== (const Colour& other) const noexcept { return argb.ge
bool Colour::operator!= (const Colour& other) const noexcept { return argb.getNativeARGB() != other.argb.getNativeARGB(); } bool Colour::operator!= (const Colour& other) const noexcept { return argb.getNativeARGB() != other.argb.getNativeARGB(); }
//============================================================================== //==============================================================================
Colour::Colour (const uint32 col) noexcept
Colour::Colour (uint32 col) noexcept
: argb (static_cast<uint8> ((col >> 24) & 0xff), : argb (static_cast<uint8> ((col >> 24) & 0xff),
static_cast<uint8> ((col >> 16) & 0xff), static_cast<uint8> ((col >> 16) & 0xff),
static_cast<uint8> ((col >> 8) & 0xff), static_cast<uint8> ((col >> 8) & 0xff),
@@ -144,49 +207,57 @@ Colour::Colour (const uint32 col) noexcept
{ {
} }
Colour::Colour (const uint8 red, const uint8 green, const uint8 blue) noexcept
Colour::Colour (uint8 red, uint8 green, uint8 blue) noexcept
{ {
argb.setARGB (0xff, red, green, blue); argb.setARGB (0xff, red, green, blue);
} }
Colour Colour::fromRGB (const uint8 red, const uint8 green, const uint8 blue) noexcept
Colour Colour::fromRGB (uint8 red, uint8 green, uint8 blue) noexcept
{ {
return Colour (red, green, blue); return Colour (red, green, blue);
} }
Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
Colour::Colour (uint8 red, uint8 green, uint8 blue, uint8 alpha) noexcept
{ {
argb.setARGB (alpha, red, green, blue); argb.setARGB (alpha, red, green, blue);
} }
Colour Colour::fromRGBA (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
Colour Colour::fromRGBA (uint8 red, uint8 green, uint8 blue, uint8 alpha) noexcept
{ {
return Colour (red, green, blue, alpha); return Colour (red, green, blue, alpha);
} }
Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const float alpha) noexcept
Colour::Colour (uint8 red, uint8 green, uint8 blue, float alpha) noexcept
{ {
argb.setARGB (ColourHelpers::floatToUInt8 (alpha), red, green, blue); argb.setARGB (ColourHelpers::floatToUInt8 (alpha), red, green, blue);
} }
Colour Colour::fromFloatRGBA (const float red, const float green, const float blue, const float alpha) noexcept
Colour Colour::fromFloatRGBA (float red, float green, float blue, float alpha) noexcept
{ {
return Colour (ColourHelpers::floatToUInt8 (red), return Colour (ColourHelpers::floatToUInt8 (red),
ColourHelpers::floatToUInt8 (green), ColourHelpers::floatToUInt8 (green),
ColourHelpers::floatToUInt8 (blue), alpha); ColourHelpers::floatToUInt8 (blue), alpha);
} }
Colour::Colour (const float hue, const float saturation, const float brightness, const float alpha) noexcept
Colour::Colour (float hue, float saturation, float brightness, float alpha) noexcept
: argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, ColourHelpers::floatToUInt8 (alpha))) : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, ColourHelpers::floatToUInt8 (alpha)))
{ {
} }
Colour Colour::fromHSV (const float hue, const float saturation, const float brightness, const float alpha) noexcept
Colour Colour::fromHSV (float hue, float saturation, float brightness, float alpha) noexcept
{ {
return Colour (hue, saturation, brightness, alpha); return Colour (hue, saturation, brightness, alpha);
} }
Colour::Colour (const float hue, const float saturation, const float brightness, const uint8 alpha) noexcept
Colour Colour::fromHSL (float hue, float saturation, float lightness, float alpha) noexcept
{
Colour hslColour;
hslColour.argb = ColourHelpers::HSL::toRGB (hue, saturation, lightness, ColourHelpers::floatToUInt8 (alpha));
return hslColour;
}
Colour::Colour (float hue, float saturation, float brightness, uint8 alpha) noexcept
: argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, alpha)) : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, alpha))
{ {
} }
@@ -258,18 +329,18 @@ Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept
//============================================================================== //==============================================================================
Colour Colour::overlaidWith (Colour src) const noexcept Colour Colour::overlaidWith (Colour src) const noexcept
{ {
const int destAlpha = getAlpha();
auto destAlpha = getAlpha();
if (destAlpha <= 0) if (destAlpha <= 0)
return src; return src;
const int invA = 0xff - (int) src.getAlpha();
const int resA = 0xff - (((0xff - destAlpha) * invA) >> 8);
auto invA = 0xff - (int) src.getAlpha();
auto resA = 0xff - (((0xff - destAlpha) * invA) >> 8);
if (resA <= 0) if (resA <= 0)
return *this; return *this;
const int da = (invA * destAlpha) / resA;
auto da = (invA * destAlpha) / resA;
return Colour ((uint8) (src.getRed() + ((((int) getRed() - src.getRed()) * da) >> 8)), return Colour ((uint8) (src.getRed() + ((((int) getRed() - src.getRed()) * da) >> 8)),
(uint8) (src.getGreen() + ((((int) getGreen() - src.getGreen()) * da) >> 8)), (uint8) (src.getGreen() + ((((int) getGreen() - src.getGreen()) * da) >> 8)),
@@ -286,7 +357,7 @@ Colour Colour::interpolatedWith (Colour other, float proportionOfOther) const no
return other; return other;
PixelARGB c1 (getPixelARGB()); PixelARGB c1 (getPixelARGB());
const PixelARGB c2 (other.getPixelARGB());
PixelARGB c2 (other.getPixelARGB());
c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f)); c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f));
c1.unpremultiply(); c1.unpremultiply();
@@ -302,20 +373,32 @@ float Colour::getFloatAlpha() const noexcept { return getAlpha() / 255.0f; }
//============================================================================== //==============================================================================
void Colour::getHSB (float& h, float& s, float& v) const noexcept void Colour::getHSB (float& h, float& s, float& v) const noexcept
{ {
const ColourHelpers::HSB hsb (*this);
ColourHelpers::HSB hsb (*this);
h = hsb.hue; h = hsb.hue;
s = hsb.saturation; s = hsb.saturation;
v = hsb.brightness; v = hsb.brightness;
} }
void Colour::getHSL (float& h, float& s, float& l) const noexcept
{
ColourHelpers::HSL hsl (*this);
h = hsl.hue;
s = hsl.saturation;
l = hsl.lightness;
}
float Colour::getHue() const noexcept { return ColourHelpers::HSB (*this).hue; } float Colour::getHue() const noexcept { return ColourHelpers::HSB (*this).hue; }
float Colour::getSaturation() const noexcept { return ColourHelpers::HSB (*this).saturation; } float Colour::getSaturation() const noexcept { return ColourHelpers::HSB (*this).saturation; }
float Colour::getBrightness() const noexcept { return ColourHelpers::HSB (*this).brightness; } float Colour::getBrightness() const noexcept { return ColourHelpers::HSB (*this).brightness; }
float Colour::getLightness() const noexcept { return ColourHelpers::HSL (*this).lightness; }
Colour Colour::withHue (float h) const noexcept { ColourHelpers::HSB hsb (*this); hsb.hue = h; return hsb.toColour (*this); } Colour Colour::withHue (float h) const noexcept { ColourHelpers::HSB hsb (*this); hsb.hue = h; return hsb.toColour (*this); }
Colour Colour::withSaturation (float s) const noexcept { ColourHelpers::HSB hsb (*this); hsb.saturation = s; return hsb.toColour (*this); } Colour Colour::withSaturation (float s) const noexcept { ColourHelpers::HSB hsb (*this); hsb.saturation = s; return hsb.toColour (*this); }
Colour Colour::withBrightness (float v) const noexcept { ColourHelpers::HSB hsb (*this); hsb.brightness = v; return hsb.toColour (*this); } Colour Colour::withBrightness (float v) const noexcept { ColourHelpers::HSB hsb (*this); hsb.brightness = v; return hsb.toColour (*this); }
Colour Colour::withLightness (float l) const noexcept { ColourHelpers::HSL hsl (*this); hsl.lightness = l; return hsl.toColour (*this); }
float Colour::getPerceivedBrightness() const noexcept float Colour::getPerceivedBrightness() const noexcept
{ {
return std::sqrt (0.241f * square (getFloatRed()) return std::sqrt (0.241f * square (getFloatRed())
@@ -345,6 +428,13 @@ Colour Colour::withMultipliedBrightness (const float amount) const noexcept
return hsb.toColour (*this); return hsb.toColour (*this);
} }
Colour Colour::withMultipliedLightness (const float amount) const noexcept
{
ColourHelpers::HSL hsl (*this);
hsl.lightness = jmin (1.0f, hsl.lightness * amount);
return hsl.toColour (*this);
}
//============================================================================== //==============================================================================
Colour Colour::brighter (float amount) const noexcept Colour Colour::brighter (float amount) const noexcept
{ {
@@ -369,7 +459,7 @@ Colour Colour::darker (float amount) const noexcept
//============================================================================== //==============================================================================
Colour Colour::greyLevel (const float brightness) noexcept Colour Colour::greyLevel (const float brightness) noexcept
{ {
const uint8 level = ColourHelpers::floatToUInt8 (brightness);
auto level = ColourHelpers::floatToUInt8 (brightness);
return Colour (level, level, level); return Colour (level, level, level);
} }
@@ -383,14 +473,14 @@ Colour Colour::contrasting (const float amount) const noexcept
Colour Colour::contrasting (Colour target, float minContrast) const noexcept Colour Colour::contrasting (Colour target, float minContrast) const noexcept
{ {
const ColourHelpers::YIQ bg (*this);
ColourHelpers::YIQ bg (*this);
ColourHelpers::YIQ fg (target); ColourHelpers::YIQ fg (target);
if (std::abs (bg.y - fg.y) >= minContrast) if (std::abs (bg.y - fg.y) >= minContrast)
return target; return target;
const float y1 = jmax (0.0f, bg.y - minContrast);
const float y2 = jmin (1.0f, bg.y + minContrast);
auto y1 = jmax (0.0f, bg.y - minContrast);
auto y2 = jmin (1.0f, bg.y + minContrast);
fg.y = (std::abs (y1 - bg.y) > std::abs (y2 - bg.y)) ? y1 : y2; fg.y = (std::abs (y1 - bg.y) > std::abs (y2 - bg.y)) ? y1 : y2;
return fg.toColour(); return fg.toColour();
@@ -399,16 +489,15 @@ Colour Colour::contrasting (Colour target, float minContrast) const noexcept
Colour Colour::contrasting (Colour colour1, Colour Colour::contrasting (Colour colour1,
Colour colour2) noexcept Colour colour2) noexcept
{ {
const float b1 = colour1.getPerceivedBrightness();
const float b2 = colour2.getPerceivedBrightness();
float best = 0.0f;
float bestDist = 0.0f;
auto b1 = colour1.getPerceivedBrightness();
auto b2 = colour2.getPerceivedBrightness();
float best = 0.0f, bestDist = 0.0f;
for (float i = 0.0f; i < 1.0f; i += 0.02f) for (float i = 0.0f; i < 1.0f; i += 0.02f)
{ {
const float d1 = std::abs (i - b1);
const float d2 = std::abs (i - b2);
const float dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2);
auto d1 = std::abs (i - b1);
auto d2 = std::abs (i - b2);
auto dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2);
if (dist > bestDist) if (dist > bestDist)
{ {
@@ -439,4 +528,188 @@ String Colour::toDisplayString (const bool includeAlphaValue) const
.toUpperCase(); .toUpperCase();
} }
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class ColourTests : public UnitTest
{
public:
ColourTests()
: UnitTest ("Colour", UnitTestCategories::graphics)
{}
void runTest() override
{
beginTest ("Constructors");
{
Colour c1;
expectEquals (c1.getRed(), (uint8) 0);
expectEquals (c1.getGreen(), (uint8) 0);
expectEquals (c1.getBlue(), (uint8) 0);
expectEquals (c1.getAlpha(), (uint8) 0);
expectEquals (c1.getFloatAlpha(), 0.0f);
Colour c2 ((uint32) 0);
expectEquals (c2.getRed(), (uint8) 0);
expectEquals (c2.getGreen(), (uint8) 0);
expectEquals (c2.getBlue(), (uint8) 0);
expectEquals (c2.getAlpha(), (uint8) 0);
expectEquals (c2.getFloatAlpha(), 0.0f);
Colour c3 ((uint32) 0xffffffff);
expectEquals (c3.getRed(), (uint8) 255);
expectEquals (c3.getGreen(), (uint8) 255);
expectEquals (c3.getBlue(), (uint8) 255);
expectEquals (c3.getAlpha(), (uint8) 255);
expectEquals (c3.getFloatAlpha(), 1.0f);
Colour c4 (0, 0, 0);
expectEquals (c4.getRed(), (uint8) 0);
expectEquals (c4.getGreen(), (uint8) 0);
expectEquals (c4.getBlue(), (uint8) 0);
expectEquals (c4.getAlpha(), (uint8) 255);
expectEquals (c4.getFloatAlpha(), 1.0f);
Colour c5 (255, 255, 255);
expectEquals (c5.getRed(), (uint8) 255);
expectEquals (c5.getGreen(), (uint8) 255);
expectEquals (c5.getBlue(), (uint8) 255);
expectEquals (c5.getAlpha(), (uint8) 255);
expectEquals (c5.getFloatAlpha(), 1.0f);
Colour c6 ((uint8) 0, (uint8) 0, (uint8) 0, (uint8) 0);
expectEquals (c6.getRed(), (uint8) 0);
expectEquals (c6.getGreen(), (uint8) 0);
expectEquals (c6.getBlue(), (uint8) 0);
expectEquals (c6.getAlpha(), (uint8) 0);
expectEquals (c6.getFloatAlpha(), 0.0f);
Colour c7 ((uint8) 255, (uint8) 255, (uint8) 255, (uint8) 255);
expectEquals (c7.getRed(), (uint8) 255);
expectEquals (c7.getGreen(), (uint8) 255);
expectEquals (c7.getBlue(), (uint8) 255);
expectEquals (c7.getAlpha(), (uint8) 255);
expectEquals (c7.getFloatAlpha(), 1.0f);
Colour c8 ((uint8) 0, (uint8) 0, (uint8) 0, 0.0f);
expectEquals (c8.getRed(), (uint8) 0);
expectEquals (c8.getGreen(), (uint8) 0);
expectEquals (c8.getBlue(), (uint8) 0);
expectEquals (c8.getAlpha(), (uint8) 0);
expectEquals (c8.getFloatAlpha(), 0.0f);
Colour c9 ((uint8) 255, (uint8) 255, (uint8) 255, 1.0f);
expectEquals (c9.getRed(), (uint8) 255);
expectEquals (c9.getGreen(), (uint8) 255);
expectEquals (c9.getBlue(), (uint8) 255);
expectEquals (c9.getAlpha(), (uint8) 255);
expectEquals (c9.getFloatAlpha(), 1.0f);
}
beginTest ("HSV");
{
auto testHSV = [this] (int hueDegrees, int saturationPercentage, int brightnessPercentage,
uint8 expectedRed, uint8 expectedGreen, uint8 expectedBlue)
{
auto testColour = Colour::fromHSV (hueDegrees / 360.0f,
saturationPercentage / 100.0f,
brightnessPercentage / 100.0f,
1.0f);
expectEquals (testColour.getRed(), expectedRed);
expectEquals (testColour.getGreen(), expectedGreen);
expectEquals (testColour.getBlue(), expectedBlue);
};
// black
testHSV (0, 0, 0, 0, 0, 0);
// white
testHSV (0, 0, 100, 255, 255, 255);
// red
testHSV (0, 100, 100, 255, 0, 0);
// lime
testHSV (120, 100, 100, 0, 255, 0);
// blue
testHSV (240, 100, 100, 0, 0, 255);
// yellow
testHSV (60, 100, 100, 255, 255, 0);
// cyan
testHSV (180, 100, 100, 0, 255, 255);
// magenta
testHSV (300, 100, 100, 255, 0, 255);
// silver
testHSV (0, 0, 75, 191, 191, 191);
// grey
testHSV (0, 0, 50, 128, 128, 128);
// maroon
testHSV (0, 100, 50, 128, 0, 0);
// olive
testHSV (60, 100, 50, 128, 128, 0);
// green
testHSV (120, 100, 50, 0, 128, 0);
// purple
testHSV (300, 100, 50, 128, 0, 128);
// teal
testHSV (180, 100, 50, 0, 128, 128);
// navy
testHSV (240, 100, 50, 0, 0, 128);
}
beginTest ("HSL");
{
auto testHSL = [this] (int hueDegrees, int saturationPercentage, int lightnessPercentage,
uint8 expectedRed, uint8 expectedGreen, uint8 expectedBlue)
{
auto testColour = Colour::fromHSL (hueDegrees / 360.0f,
saturationPercentage / 100.0f,
lightnessPercentage / 100.0f,
1.0f);
expectEquals (testColour.getRed(), expectedRed);
expectEquals (testColour.getGreen(), expectedGreen);
expectEquals (testColour.getBlue(), expectedBlue);
};
// black
testHSL (0, 0, 0, 0, 0, 0);
// white
testHSL (0, 0, 100, 255, 255, 255);
// red
testHSL (0, 100, 50, 255, 0, 0);
// lime
testHSL (120, 100, 50, 0, 255, 0);
// blue
testHSL (240, 100, 50, 0, 0, 255);
// yellow
testHSL (60, 100, 50, 255, 255, 0);
// cyan
testHSL (180, 100, 50, 0, 255, 255);
// magenta
testHSL (300, 100, 50, 255, 0, 255);
// silver
testHSL (0, 0, 75, 191, 191, 191);
// grey
testHSL (0, 0, 50, 128, 128, 128);
// maroon
testHSL (0, 100, 25, 128, 0, 0);
// olive
testHSL (60, 100, 25, 128, 128, 0);
// green
testHSL (120, 100, 25, 0, 128, 0);
// purple
testHSL (300, 100, 25, 128, 0, 128);
// teal
testHSL (180, 100, 25, 0, 128, 128);
// navy
testHSL (240, 100, 25, 0, 0, 128);
}
}
};
static ColourTests colourTests;
#endif
} // namespace juce } // namespace juce

+ 43
- 10
modules/juce_graphics/colour/juce_Colour.h View File

@@ -110,6 +110,26 @@ public:
float brightness, float brightness,
float alpha) noexcept; float alpha) noexcept;
/** Creates a colour using floating point hue, saturation, brightness and alpha values.
All values must be between 0.0 and 1.0.
Numbers outside the valid range will be clipped.
*/
static Colour fromHSV (float hue,
float saturation,
float brightness,
float alpha) noexcept;
/** Creates a colour using floating point hue, saturation, lightness and alpha values.
All values must be between 0.0 and 1.0.
Numbers outside the valid range will be clipped.
*/
static Colour fromHSL (float hue,
float saturation,
float lightness,
float alpha) noexcept;
/** Creates a colour using a PixelARGB object. This function assumes that the argb pixel is /** Creates a colour using a PixelARGB object. This function assumes that the argb pixel is
not premultiplied. not premultiplied.
*/ */
@@ -123,16 +143,6 @@ public:
*/ */
Colour (PixelAlpha alpha) noexcept; Colour (PixelAlpha alpha) noexcept;
/** Creates a colour using floating point hue, saturation, brightness and alpha values.
All values must be between 0.0 and 1.0.
Numbers outside the valid range will be clipped.
*/
static Colour fromHSV (float hue,
float saturation,
float brightness,
float alpha) noexcept;
/** Destructor. */ /** Destructor. */
~Colour() = default; ~Colour() = default;
@@ -250,6 +260,11 @@ public:
*/ */
float getBrightness() const noexcept; float getBrightness() const noexcept;
/** Returns the colour's lightness component.
The value returned is in the range 0.0 to 1.0
*/
float getLightness() const noexcept;
/** Returns a skewed brightness value, adjusted to better reflect the way the human /** Returns a skewed brightness value, adjusted to better reflect the way the human
eye responds to different colour channels. This makes it better than getBrightness() eye responds to different colour channels. This makes it better than getBrightness()
for comparing differences in brightness. for comparing differences in brightness.
@@ -263,6 +278,13 @@ public:
float& saturation, float& saturation,
float& brightness) const noexcept; float& brightness) const noexcept;
/** Returns the colour's hue, saturation and lightness components all at once.
The values returned are in the range 0.0 to 1.0
*/
void getHSL (float& hue,
float& saturation,
float& lightness) const noexcept;
//============================================================================== //==============================================================================
/** Returns a copy of this colour with a different hue. */ /** Returns a copy of this colour with a different hue. */
Colour withHue (float newHue) const noexcept; Colour withHue (float newHue) const noexcept;
@@ -275,6 +297,11 @@ public:
*/ */
Colour withBrightness (float newBrightness) const noexcept; Colour withBrightness (float newBrightness) const noexcept;
/** Returns a copy of this colour with a different lightness.
@see lighter, darker, withMultipliedLightness
*/
Colour withLightness (float newLightness) const noexcept;
/** Returns a copy of this colour with its hue rotated. /** Returns a copy of this colour with its hue rotated.
The new colour's hue is ((this->getHue() + amountToRotate) % 1.0) The new colour's hue is ((this->getHue() + amountToRotate) % 1.0)
@see brighter, darker, withMultipliedBrightness @see brighter, darker, withMultipliedBrightness
@@ -293,6 +320,12 @@ public:
*/ */
Colour withMultipliedBrightness (float amount) const noexcept; Colour withMultipliedBrightness (float amount) const noexcept;
/** Returns a copy of this colour with its lightness multiplied by the given value.
The new colour's lightness is (this->lightness() * multiplier)
(the result is clipped to legal limits).
*/
Colour withMultipliedLightness (float amount) const noexcept;
//============================================================================== //==============================================================================
/** Returns a brighter version of this colour. /** Returns a brighter version of this colour.
@param amountBrighter how much brighter to make it - a value from 0 to 1.0 where 0 is @param amountBrighter how much brighter to make it - a value from 0 to 1.0 where 0 is


+ 4
- 4
modules/juce_gui_basics/drawables/juce_SVGParser.cpp View File

@@ -1573,10 +1573,10 @@ private:
}(); }();
if (text.startsWith ("hsl")) if (text.startsWith ("hsl"))
return Colour ((float) (tokens[0].getDoubleValue() / 360.0),
(float) (tokens[1].getDoubleValue() / 100.0),
(float) (tokens[2].getDoubleValue() / 100.0),
alpha);
return Colour::fromHSL ((float) (tokens[0].getDoubleValue() / 360.0),
(float) (tokens[1].getDoubleValue() / 100.0),
(float) (tokens[2].getDoubleValue() / 100.0),
alpha);
if (tokens[0].containsChar ('%')) if (tokens[0].containsChar ('%'))
return Colour ((uint8) roundToInt (2.55 * tokens[0].getDoubleValue()), return Colour ((uint8) roundToInt (2.55 * tokens[0].getDoubleValue()),


Loading…
Cancel
Save