From 553c5a93b45de183aa34471c1f6b823b77585583 Mon Sep 17 00:00:00 2001 From: Jojo-1000 <33495614+Jojo-1000@users.noreply.github.com> Date: Mon, 18 May 2020 21:04:51 +0200 Subject: [PATCH] Add color conversion from RGB to XY. Bounds to closest representable color of the gamut. --- include/hueplusplus/Units.h | 20 +++++++++++++++++++- src/CMakeLists.txt | 3 ++- src/SimpleColorHueStrategy.cpp | 9 --------- src/Units.cpp | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 src/Units.cpp diff --git a/include/hueplusplus/Units.h b/include/hueplusplus/Units.h index 5d62352..35f133e 100644 --- a/include/hueplusplus/Units.h +++ b/include/hueplusplus/Units.h @@ -27,7 +27,6 @@ namespace hueplusplus { - struct HueSaturation { int hue; @@ -40,11 +39,30 @@ struct XY float y; }; +struct XYBrightness +{ + XY xy; + float brightness; +}; + +struct ColorGamut +{ + XY redCorner; + XY greenCorner; + XY blueCorner; + + XY corrected(const XY& xy) const; +}; + struct RGB { uint8_t r; uint8_t g; uint8_t b; + + XYBrightness toXY() const; + XYBrightness toXY(const ColorGamut& gamut) const; + static RGB fromXY(const XYBrightness& xy); }; } // namespace hueplusplus diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 47ff103..63b7ac5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,8 +16,9 @@ set(hueplusplus_SOURCES SimpleColorTemperatureStrategy.cpp StateTransaction.cpp TimePattern.cpp + Units.cpp UPnP.cpp - Utils.cpp) + Utils.cpp ) # on windows we want to compile the WinHttpHandler if(WIN32) diff --git a/src/SimpleColorHueStrategy.cpp b/src/SimpleColorHueStrategy.cpp index b1dbd35..0f4b6f2 100644 --- a/src/SimpleColorHueStrategy.cpp +++ b/src/SimpleColorHueStrategy.cpp @@ -287,14 +287,5 @@ std::pair SimpleColorHueStrategy::getColorXY(const HueLight& light { return std::make_pair(light.state.getValue()["state"]["xy"][0].get(), light.state.getValue()["state"]["xy"][1].get()); } -/*bool SimpleColorHueStrategy::pointInTriangle(float pointx, float pointy, float -x0, float y0, float x1, float y1, float x2, float y2) -{ -float A = (-y1 * x2 + y0*(-x1 + x2) + x0*(y1 - y2) + x1 * y1); -int8_t sign = A < 0 ? -1 : 1; -float s = (y0 * x2 - x0 * y2 + (y2 - y0) * pointx + (x0 - x2) * pointy) * sign; -float t = (x0 * y1 - y0 * x1 + (y0 - y1) * pointx + (x1 - x0) * pointy) * sign; -return s > 0 && t > 0 && (s + t) < A * sign; -}*/ } // namespace hueplusplus diff --git a/src/Units.cpp b/src/Units.cpp new file mode 100644 index 0000000..a97b472 --- /dev/null +++ b/src/Units.cpp @@ -0,0 +1,121 @@ +#include + +#include + +namespace hueplusplus +{ +namespace +{ +float sign(const XY& p0, const XY& p1, const XY& p2) +{ + return (p0.x - p2.x) * (p1.y - p2.y) - (p1.x - p2.x) * (p0.y - p2.y); +} + +bool isInTriangle(const XY& xy, const ColorGamut& triangle) +{ + const float d1 = sign(xy, triangle.redCorner, triangle.greenCorner); + const float d2 = sign(xy, triangle.greenCorner, triangle.blueCorner); + const float d3 = sign(xy, triangle.blueCorner, triangle.redCorner); + + const bool hasNeg = (d1 < 0) || (d2 < 0) || (d3 < 0); + const bool hasPos = (d1 > 0) || (d2 > 0) || (d3 > 0); + return !(hasNeg && hasPos); +} + +bool isRightOf(const XY& xy, const XY& p1, const XY& p2) +{ + return sign(xy, p1, p2) < 0; +} + +XY projectOntoLine(const XY& xy, const XY& p1, const XY& p2) +{ + // Using dot product to project onto line + // Vector AB = B - A + // Vector AX = X - A + // Projected length l = (AX dot AB) / len(AB) + // Result: E = A + l*AB/len(AB) = A + AB * (AX dot AB) / (len(AB))^2 + + const float abX = p2.x - p1.x; + const float abY = p2.y - p1.y; + const float lenABSquared = abX * abX + abY * abY; + + const float dot = (xy.x - p1.x) * abX + (xy.y - p1.y) * abY; + const float eX = p1.x + abX * dot / lenABSquared; + const float eY = p1.y + abY * dot / lenABSquared; + return XY {eX, eY}; +} +} // namespace + +XY ColorGamut::corrected(const XY& xy) const +{ + // red, green and blue are in counterclockwise orientation + if (isRightOf(xy, redCorner, greenCorner)) + { + // Outside of triangle, check whether to use nearest corner or point on line + if (isRightOf(xy, greenCorner, blueCorner)) + { + // Point is outside of red-green line, closest to green corner + return greenCorner; + } + else if (isRightOf(xy, blueCorner, redCorner)) + { + // Point is outside of red-green line, closest to red corner + return redCorner; + } + else + { + // Point is closest to line, project onto it + return projectOntoLine(xy, redCorner, greenCorner); + } + } + else if (isRightOf(xy, greenCorner, blueCorner)) + { + // Green corner already checked above + if (isRightOf(xy, blueCorner, redCorner)) + { + // Point is outside of green-blue line, closest to blue corner + return blueCorner; + } + else + { + return projectOntoLine(xy, greenCorner, blueCorner); + } + } + else if (isRightOf(xy, blueCorner, redCorner)) + { + // All corners already checked + return projectOntoLine(xy, blueCorner, redCorner); + } + return xy; +} + +XYBrightness RGB::toXY() const +{ + const float red = r / 255.f; + const float green = g / 255.f; + const float blue = b / 255.f; + + const float redCorrected = (red > 0.04045f) ? pow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f); + const float greenCorrected = (green > 0.04045f) ? pow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f); + const float blueCorrected = (blue > 0.04045f) ? pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); + + const float X = redCorrected * 0.664511f + greenCorrected * 0.154324f + blueCorrected * 0.162028f; + const float Y = redCorrected * 0.283881f + greenCorrected * 0.668433f + blueCorrected * 0.047685f; + const float Z = redCorrected * 0.000088f + greenCorrected * 0.072310f + blueCorrected * 0.986039f; + + const float x = X / (X + Y + Z); + const float y = Y / (X + Y + Z); + return XYBrightness {XY {x, y}, Y}; +} + +XYBrightness RGB::toXY(const ColorGamut& gamut) const +{ + XYBrightness xy = toXY(); + xy.xy = gamut.corrected(xy.xy); + + return xy; +} + +RGB RGB::fromXY(const XYBrightness& xy) {} + +} // namespace hueplusplus \ No newline at end of file -- libgit2 0.21.4