Commit 553c5a93b45de183aa34471c1f6b823b77585583

Authored by Jojo-1000
Committed by Moritz Wirger
1 parent aec7baeb

Add color conversion from RGB to XY.

Bounds to closest representable color of the gamut.
include/hueplusplus/Units.h
... ... @@ -27,7 +27,6 @@
27 27  
28 28 namespace hueplusplus
29 29 {
30   -
31 30 struct HueSaturation
32 31 {
33 32 int hue;
... ... @@ -40,11 +39,30 @@ struct XY
40 39 float y;
41 40 };
42 41  
  42 +struct XYBrightness
  43 +{
  44 + XY xy;
  45 + float brightness;
  46 +};
  47 +
  48 +struct ColorGamut
  49 +{
  50 + XY redCorner;
  51 + XY greenCorner;
  52 + XY blueCorner;
  53 +
  54 + XY corrected(const XY& xy) const;
  55 +};
  56 +
43 57 struct RGB
44 58 {
45 59 uint8_t r;
46 60 uint8_t g;
47 61 uint8_t b;
  62 +
  63 + XYBrightness toXY() const;
  64 + XYBrightness toXY(const ColorGamut& gamut) const;
  65 + static RGB fromXY(const XYBrightness& xy);
48 66 };
49 67 } // namespace hueplusplus
50 68  
... ...
src/CMakeLists.txt
... ... @@ -16,8 +16,9 @@ set(hueplusplus_SOURCES
16 16 SimpleColorTemperatureStrategy.cpp
17 17 StateTransaction.cpp
18 18 TimePattern.cpp
  19 + Units.cpp
19 20 UPnP.cpp
20   - Utils.cpp)
  21 + Utils.cpp )
21 22  
22 23 # on windows we want to compile the WinHttpHandler
23 24 if(WIN32)
... ...
src/SimpleColorHueStrategy.cpp
... ... @@ -287,14 +287,5 @@ std::pair<float, float> SimpleColorHueStrategy::getColorXY(const HueLight& light
287 287 {
288 288 return std::make_pair(light.state.getValue()["state"]["xy"][0].get<float>(), light.state.getValue()["state"]["xy"][1].get<float>());
289 289 }
290   -/*bool SimpleColorHueStrategy::pointInTriangle(float pointx, float pointy, float
291   -x0, float y0, float x1, float y1, float x2, float y2)
292   -{
293   -float A = (-y1 * x2 + y0*(-x1 + x2) + x0*(y1 - y2) + x1 * y1);
294   -int8_t sign = A < 0 ? -1 : 1;
295   -float s = (y0 * x2 - x0 * y2 + (y2 - y0) * pointx + (x0 - x2) * pointy) * sign;
296   -float t = (x0 * y1 - y0 * x1 + (y0 - y1) * pointx + (x1 - x0) * pointy) * sign;
297 290  
298   -return s > 0 && t > 0 && (s + t) < A * sign;
299   -}*/
300 291 } // namespace hueplusplus
... ...
src/Units.cpp 0 → 100644
  1 +#include <cmath>
  2 +
  3 +#include <hueplusplus/Units.h>
  4 +
  5 +namespace hueplusplus
  6 +{
  7 +namespace
  8 +{
  9 +float sign(const XY& p0, const XY& p1, const XY& p2)
  10 +{
  11 + return (p0.x - p2.x) * (p1.y - p2.y) - (p1.x - p2.x) * (p0.y - p2.y);
  12 +}
  13 +
  14 +bool isInTriangle(const XY& xy, const ColorGamut& triangle)
  15 +{
  16 + const float d1 = sign(xy, triangle.redCorner, triangle.greenCorner);
  17 + const float d2 = sign(xy, triangle.greenCorner, triangle.blueCorner);
  18 + const float d3 = sign(xy, triangle.blueCorner, triangle.redCorner);
  19 +
  20 + const bool hasNeg = (d1 < 0) || (d2 < 0) || (d3 < 0);
  21 + const bool hasPos = (d1 > 0) || (d2 > 0) || (d3 > 0);
  22 + return !(hasNeg && hasPos);
  23 +}
  24 +
  25 +bool isRightOf(const XY& xy, const XY& p1, const XY& p2)
  26 +{
  27 + return sign(xy, p1, p2) < 0;
  28 +}
  29 +
  30 +XY projectOntoLine(const XY& xy, const XY& p1, const XY& p2)
  31 +{
  32 + // Using dot product to project onto line
  33 + // Vector AB = B - A
  34 + // Vector AX = X - A
  35 + // Projected length l = (AX dot AB) / len(AB)
  36 + // Result: E = A + l*AB/len(AB) = A + AB * (AX dot AB) / (len(AB))^2
  37 +
  38 + const float abX = p2.x - p1.x;
  39 + const float abY = p2.y - p1.y;
  40 + const float lenABSquared = abX * abX + abY * abY;
  41 +
  42 + const float dot = (xy.x - p1.x) * abX + (xy.y - p1.y) * abY;
  43 + const float eX = p1.x + abX * dot / lenABSquared;
  44 + const float eY = p1.y + abY * dot / lenABSquared;
  45 + return XY {eX, eY};
  46 +}
  47 +} // namespace
  48 +
  49 +XY ColorGamut::corrected(const XY& xy) const
  50 +{
  51 + // red, green and blue are in counterclockwise orientation
  52 + if (isRightOf(xy, redCorner, greenCorner))
  53 + {
  54 + // Outside of triangle, check whether to use nearest corner or point on line
  55 + if (isRightOf(xy, greenCorner, blueCorner))
  56 + {
  57 + // Point is outside of red-green line, closest to green corner
  58 + return greenCorner;
  59 + }
  60 + else if (isRightOf(xy, blueCorner, redCorner))
  61 + {
  62 + // Point is outside of red-green line, closest to red corner
  63 + return redCorner;
  64 + }
  65 + else
  66 + {
  67 + // Point is closest to line, project onto it
  68 + return projectOntoLine(xy, redCorner, greenCorner);
  69 + }
  70 + }
  71 + else if (isRightOf(xy, greenCorner, blueCorner))
  72 + {
  73 + // Green corner already checked above
  74 + if (isRightOf(xy, blueCorner, redCorner))
  75 + {
  76 + // Point is outside of green-blue line, closest to blue corner
  77 + return blueCorner;
  78 + }
  79 + else
  80 + {
  81 + return projectOntoLine(xy, greenCorner, blueCorner);
  82 + }
  83 + }
  84 + else if (isRightOf(xy, blueCorner, redCorner))
  85 + {
  86 + // All corners already checked
  87 + return projectOntoLine(xy, blueCorner, redCorner);
  88 + }
  89 + return xy;
  90 +}
  91 +
  92 +XYBrightness RGB::toXY() const
  93 +{
  94 + const float red = r / 255.f;
  95 + const float green = g / 255.f;
  96 + const float blue = b / 255.f;
  97 +
  98 + const float redCorrected = (red > 0.04045f) ? pow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f);
  99 + const float greenCorrected = (green > 0.04045f) ? pow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f);
  100 + const float blueCorrected = (blue > 0.04045f) ? pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f);
  101 +
  102 + const float X = redCorrected * 0.664511f + greenCorrected * 0.154324f + blueCorrected * 0.162028f;
  103 + const float Y = redCorrected * 0.283881f + greenCorrected * 0.668433f + blueCorrected * 0.047685f;
  104 + const float Z = redCorrected * 0.000088f + greenCorrected * 0.072310f + blueCorrected * 0.986039f;
  105 +
  106 + const float x = X / (X + Y + Z);
  107 + const float y = Y / (X + Y + Z);
  108 + return XYBrightness {XY {x, y}, Y};
  109 +}
  110 +
  111 +XYBrightness RGB::toXY(const ColorGamut& gamut) const
  112 +{
  113 + XYBrightness xy = toXY();
  114 + xy.xy = gamut.corrected(xy.xy);
  115 +
  116 + return xy;
  117 +}
  118 +
  119 +RGB RGB::fromXY(const XYBrightness& xy) {}
  120 +
  121 +} // namespace hueplusplus
0 122 \ No newline at end of file
... ...