From afd355ba929c63e30dabfbd865e4c9cab4b3e4f4 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 21 Jul 2025 15:24:02 +0900 Subject: [PATCH] common: consolidate color-related functions - Introduced RGB, RGBA, and HSL structures - Migrated hsl2Rgb() from SVG loader to common module This is a refactoring for upcoming hsl color functionlaities. --- src/common/meson.build | 2 + src/common/tvgColor.cpp | 71 +++++++++++++++ src/common/tvgColor.h | 49 ++++++++++ src/loaders/svg/tvgSvgLoader.cpp | 128 ++++++--------------------- src/loaders/svg/tvgSvgLoaderCommon.h | 8 +- src/renderer/tvgRender.h | 7 +- 6 files changed, 153 insertions(+), 112 deletions(-) create mode 100644 src/common/tvgColor.cpp create mode 100644 src/common/tvgColor.h diff --git a/src/common/meson.build b/src/common/meson.build index 2d83a775..9797e028 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -1,10 +1,12 @@ source_file = [ 'tvgArray.h', + 'tvgColor.h', 'tvgCompressor.h', 'tvgInlist.h', 'tvgLock.h', 'tvgMath.h', 'tvgStr.h', + 'tvgColor.cpp', 'tvgCompressor.cpp', 'tvgMath.cpp', 'tvgStr.cpp' diff --git a/src/common/tvgColor.cpp b/src/common/tvgColor.cpp new file mode 100644 index 00000000..7ef104a4 --- /dev/null +++ b/src/common/tvgColor.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025 the ThorVG project. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tvgMath.h" +#include "tvgColor.h" +namespace tvg +{ + +void hsl2rgb(float h, float s, float l, uint8_t& r, uint8_t& g, uint8_t& b) +{ + s = tvg::clamp(s, 0.0f, 1.0f); + l = tvg::clamp(l, 0.0f, 1.0f); + + auto tr = 0.0f, tg = 0.0f, tb = 0.0f; + + if (tvg::zero(s)) { + r = g = b = (uint8_t)nearbyint(l * 255.0f); + return; + } + + if (tvg::equal(h, 360.0f)) { + h = 0.0f; + } else { + h = fmod(h, 360.0f); + if (h < 0.0f) h += 360.0f; + h /= 60.0f; + } + + auto v = (l <= 0.5f) ? (l * (1.0f + s)) : (l + s - (l * s)); + auto p = l + l - v; + auto sv = tvg::zero(v) ? 0.0f : (v - p) / v; + auto i = static_cast(h); + auto f = h - i; + auto vsf = v * sv * f; + auto t = p + vsf; + auto q = v - vsf; + + switch (i) { + case 0: tr = v; tg = t; tb = p; break; + case 1: tr = q; tg = v; tb = p; break; + case 2: tr = p; tg = v; tb = t; break; + case 3: tr = p; tg = q; tb = v; break; + case 4: tr = t; tg = p; tb = v; break; + case 5: tr = v; tg = p; tb = q; break; + default: break; + } + r = (uint8_t)nearbyint(tr * 255.0f); + g = (uint8_t)nearbyint(tg * 255.0f); + b = (uint8_t)nearbyint(tb * 255.0f); +} + +} \ No newline at end of file diff --git a/src/common/tvgColor.h b/src/common/tvgColor.h new file mode 100644 index 00000000..22a13be0 --- /dev/null +++ b/src/common/tvgColor.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 the ThorVG project. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _TVG_COLOR_H_ +#define _TVG_COLOR_H_ + + +#include "tvgCommon.h" + +namespace tvg +{ + struct RGB + { + uint8_t r, g, b; + }; + + struct RGBA + { + uint8_t r, g, b, a; + }; + + struct HSL + { + float h, s, l; + }; + + void hsl2rgb(float h, float s, float l, uint8_t& r, uint8_t& g, uint8_t& b); +} + +#endif //_TVG_COLOR_H_ diff --git a/src/loaders/svg/tvgSvgLoader.cpp b/src/loaders/svg/tvgSvgLoader.cpp index bb922f59..b101889c 100644 --- a/src/loaders/svg/tvgSvgLoader.cpp +++ b/src/loaders/svg/tvgSvgLoader.cpp @@ -23,6 +23,7 @@ #include #include "tvgStr.h" #include "tvgMath.h" +#include "tvgColor.h" #include "tvgLoader.h" #include "tvgXmlParser.h" #include "tvgSvgLoader.h" @@ -573,85 +574,7 @@ static constexpr struct }; -static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* red, uint8_t* green, uint8_t* blue) -{ - auto r = 0.0f, g = 0.0f, b = 0.0f; - auto i = 0; - - while (hue < 0) hue += 360.0f; - hue = fmod(hue, 360.0f); - saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f; - brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f; - - if (tvg::zero(saturation)) r = g = b = brightness; - else { - if (tvg::equal(hue, 360.0)) hue = 0.0f; - hue /= 60.0f; - - auto v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation)); - auto p = brightness + brightness - v; - - float sv; - if (!tvg::zero(v)) sv = (v - p) / v; - else sv = 0.0f; - - i = static_cast(hue); - auto f = hue - i; - - auto vsf = v * sv * f; - - auto t = p + vsf; - auto q = v - vsf; - - switch (i) { - case 0: { - r = v; - g = t; - b = p; - break; - } - case 1: { - r = q; - g = v; - b = p; - break; - } - case 2: { - r = p; - g = v; - b = t; - break; - } - case 3: { - r = p; - g = q; - b = v; - break; - } - case 4: { - r = t; - g = p; - b = v; - break; - } - case 5: { - r = v; - g = p; - b = q; - break; - } - } - } - - *red = (uint8_t)nearbyint(r * 255.0f); - *green = (uint8_t)nearbyint(g * 255.0f); - *blue = (uint8_t)nearbyint(b * 255.0f); - - return true; -} - - -static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref) +static bool _toColor(const char* str, uint8_t& r, uint8_t&g, uint8_t& b, char** ref) { auto len = strlen(str); @@ -661,13 +584,13 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** char tmp[3] = { '\0', '\0', '\0' }; tmp[0] = str[1]; tmp[1] = str[1]; - *r = strtol(tmp, nullptr, 16); + r = strtol(tmp, nullptr, 16); tmp[0] = str[2]; tmp[1] = str[2]; - *g = strtol(tmp, nullptr, 16); + g = strtol(tmp, nullptr, 16); tmp[0] = str[3]; tmp[1] = str[3]; - *b = strtol(tmp, nullptr, 16); + b = strtol(tmp, nullptr, 16); } return true; } else if (len == 7 && str[0] == '#') { @@ -675,13 +598,13 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** char tmp[3] = { '\0', '\0', '\0' }; tmp[0] = str[1]; tmp[1] = str[2]; - *r = strtol(tmp, nullptr, 16); + r = strtol(tmp, nullptr, 16); tmp[0] = str[3]; tmp[1] = str[4]; - *g = strtol(tmp, nullptr, 16); + g = strtol(tmp, nullptr, 16); tmp[0] = str[5]; tmp[1] = str[6]; - *b = strtol(tmp, nullptr, 16); + b = strtol(tmp, nullptr, 16); } return true; } else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') { @@ -692,9 +615,9 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** if (green && *green == ',') { auto tb = _parseColor(green + 1, &blue); if (blue && blue[0] == ')' && blue[1] == '\0') { - *r = tr; - *g = tg; - *b = tb; + r = tr; + g = tg; + b = tb; } } } @@ -704,25 +627,26 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** *ref = _idFromUrl((const char*)(str + 3)); return true; } else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') { - float th, ts, tb; + tvg::HSL hsl; const char* content = _skipSpace(str + 4, nullptr); const char* hue = nullptr; - if (_parseNumber(&content, &hue, &th) && hue) { + if (_parseNumber(&content, &hue, &hsl.h) && hue) { const char* saturation = nullptr; hue = _skipSpace(hue, nullptr); hue = (char*)_skipComma(hue); hue = _skipSpace(hue, nullptr); - if (_parseNumber(&hue, &saturation, &ts) && saturation && *saturation == '%') { + if (_parseNumber(&hue, &saturation, &hsl.s) && saturation && *saturation == '%') { const char* brightness = nullptr; - ts /= 100.0f; + hsl.s /= 100.0f; saturation = _skipSpace(saturation + 1, nullptr); saturation = (char*)_skipComma(saturation); saturation = _skipSpace(saturation, nullptr); - if (_parseNumber(&saturation, &brightness, &tb) && brightness && *brightness == '%') { - tb /= 100.0f; + if (_parseNumber(&saturation, &brightness, &hsl.l) && brightness && *brightness == '%') { + hsl.l /= 100.0f; brightness = _skipSpace(brightness + 1, nullptr); if (brightness && brightness[0] == ')' && brightness[1] == '\0') { - return _hslToRgb(th, ts, tb, r, g, b); + hsl2rgb(hsl.h, hsl.s, hsl.l, r, g, b); + return true; } } } @@ -731,9 +655,9 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** //Handle named color for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) { if (!strcasecmp(colors[i].name, str)) { - *r = (((uint8_t*)(&(colors[i].value)))[2]); - *g = (((uint8_t*)(&(colors[i].value)))[1]); - *b = (((uint8_t*)(&(colors[i].value)))[0]); + r = ((uint8_t*)(&(colors[i].value)))[2]; + g = ((uint8_t*)(&(colors[i].value)))[1]; + b = ((uint8_t*)(&(colors[i].value)))[0]; return true; } } @@ -964,14 +888,14 @@ static void _handlePaintAttr(SvgPaint* paint, const char* value) paint->none = false; return; } - if (_toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url)) paint->none = false; + if (_toColor(value, paint->color.r, paint->color.g, paint->color.b, &paint->url)) paint->none = false; } static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { SvgStyleProperty* style = node->style; - if (_toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr)) { + if (_toColor(value, style->color.r, style->color.g, style->color.b, nullptr)) { style->curColorSet = true; } } @@ -2710,7 +2634,7 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value) stop->g = latestColor->g; stop->b = latestColor->b; } - } else if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) { + } else if (_toColor(value, stop->r, stop->g, stop->b, nullptr)) { loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor); } } else { @@ -2740,7 +2664,7 @@ static bool _attrParseStops(void* data, const char* key, const char* value) stop->b = latestColor->b; } } else if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) { - _toColor(value, &stop->r, &stop->g, &stop->b, nullptr); + _toColor(value, stop->r, stop->g, stop->b, nullptr); } } else if (STR_AS(key, "style")) { xmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data); diff --git a/src/loaders/svg/tvgSvgLoaderCommon.h b/src/loaders/svg/tvgSvgLoaderCommon.h index e439a16e..ec5b358f 100644 --- a/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/src/loaders/svg/tvgSvgLoaderCommon.h @@ -26,6 +26,9 @@ #include "tvgCommon.h" #include "tvgArray.h" #include "tvgInlist.h" +#include "tvgColor.h" + +using SvgColor = tvg::RGB; struct Box { @@ -428,11 +431,6 @@ struct SvgComposite bool applying; //flag for checking circular dependency. }; -struct SvgColor -{ - uint8_t r, g, b; -}; - struct SvgPaint { SvgStyleGradient* gradient; diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index 737dda07..5651bbbb 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -28,11 +28,13 @@ #include "tvgCommon.h" #include "tvgArray.h" #include "tvgLock.h" +#include "tvgColor.h" namespace tvg { using RenderData = void*; +using RenderColor = tvg::RGBA; using pixel_t = uint32_t; #define DASH_PATTERN_THRESHOLD 0.001f @@ -81,11 +83,6 @@ struct RenderSurface } }; -struct RenderColor -{ - uint8_t r, g, b, a; -}; - struct RenderCompositor { MaskMethod method;