/* * * Copyright (c) 2021 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ColorFormat.h" #include // define a clamp macro to substitute the std::clamp macro which is available from C++17 onwards #define clamp(a, min, max) ((a) < (min) ? (min) : ((a) > (max) ? (max) : (a))) RgbColor_t HsvToRgb(HsvColor_t hsv) { RgbColor_t rgb; uint8_t region, p, q, t; uint32_t h, s, v, remainder; if (hsv.s == 0) { rgb.r = rgb.g = rgb.b = hsv.v; } else { h = hsv.h; s = hsv.s; v = hsv.v; region = h / 43; remainder = (h - (region * 43)) * 6; p = (v * (255 - s)) >> 8; q = (v * (255 - ((s * remainder) >> 8))) >> 8; t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; switch (region) { case 0: rgb.r = v, rgb.g = t, rgb.b = p; break; case 1: rgb.r = q, rgb.g = v, rgb.b = p; break; case 2: rgb.r = p, rgb.g = v, rgb.b = t; break; case 3: rgb.r = p, rgb.g = q, rgb.b = v; break; case 4: rgb.r = t, rgb.g = p, rgb.b = v; break; case 5: default: rgb.r = v, rgb.g = p, rgb.b = q; break; } } return rgb; } RgbColor_t XYToRgb(uint8_t Level, uint16_t currentX, uint16_t currentY) { // convert xyY color space to RGB // https://www.easyrgb.com/en/math.php // https://en.wikipedia.org/wiki/SRGB // refer https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space // The currentX/currentY attribute contains the current value of the normalized chromaticity value of x/y. // The value of x/y shall be related to the currentX/currentY attribute by the relationship // x = currentX/65536 // y = currentY/65536 // z = 1-x-y RgbColor_t rgb; float x, y, z; float X, Y, Z; float r, g, b; x = (static_cast(currentX)) / 65535.0f; y = (static_cast(currentY)) / 65535.0f; z = 1.0f - x - y; // Calculate XYZ values // Y - given brightness in 0 - 1 range Y = (static_cast(Level)) / 254.0f; X = (Y / y) * x; Z = (Y / y) * z; // X, Y and Z input refer to a D65/2° standard illuminant. // sR, sG and sB (standard RGB) output range = 0 ÷ 255 // convert XYZ to RGB - CIE XYZ to sRGB r = (X * 3.2410f) - (Y * 1.5374f) - (Z * 0.4986f); g = -(X * 0.9692f) + (Y * 1.8760f) + (Z * 0.0416f); b = (X * 0.0556f) - (Y * 0.2040f) + (Z * 1.0570f); // apply gamma 2.2 correction r = (r <= 0.00304f ? 12.92f * r : (1.055f) * pow(r, (1.0f / 2.4f)) - 0.055f); g = (g <= 0.00304f ? 12.92f * g : (1.055f) * pow(g, (1.0f / 2.4f)) - 0.055f); b = (b <= 0.00304f ? 12.92f * b : (1.055f) * pow(b, (1.0f / 2.4f)) - 0.055f); // Round off r = clamp(r, 0, 1); g = clamp(g, 0, 1); b = clamp(b, 0, 1); // these rgb values are in the range of 0 to 1, convert to limit of HW specific LED rgb.r = (uint8_t) (r * 255); rgb.g = (uint8_t) (g * 255); rgb.b = (uint8_t) (b * 255); return rgb; } RgbColor_t CTToRgb(CtColor_t ct) { RgbColor_t rgb; float r, g, b; // Algorithm credits to Tanner Helland: https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html // Convert Mireds to centiKelvins. k = 1,000,000/mired float ctCentiKelvin = 10000 / static_cast(ct.ctMireds); // Red if (ctCentiKelvin <= 66) { r = 255; } else { r = 329.698727446f * pow(ctCentiKelvin - 60, -0.1332047592f); } // Green if (ctCentiKelvin <= 66) { g = 99.4708025861f * log(ctCentiKelvin) - 161.1195681661f; } else { g = 288.1221695283f * pow(ctCentiKelvin - 60, -0.0755148492f); } // Blue if (ctCentiKelvin >= 66) { b = 255; } else { if (ctCentiKelvin <= 19) { b = 0; } else { b = 138.5177312231f * log(ctCentiKelvin - 10) - 305.0447927307f; } } rgb.r = (uint8_t) clamp(r, 0, 255); rgb.g = (uint8_t) clamp(g, 0, 255); rgb.b = (uint8_t) clamp(b, 0, 255); return rgb; }