This commit is contained in:
Hermet Park 2025-07-22 15:37:30 +00:00 committed by GitHub
commit fd0c84b18d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 143 additions and 22 deletions

View file

@ -162,12 +162,12 @@ struct UserExample : tvgexam::Example
blender(canvas, "SoftLight", tvg::BlendMethod::SoftLight, 900.0f, 0.0f, data);
blender(canvas, "Difference", tvg::BlendMethod::Difference, 900.0f, 150.0f, data);
blender(canvas, "Exclusion", tvg::BlendMethod::Exclusion, 900.0f, 300.0f, data);
blender(canvas, "Hue (Not Supported)", tvg::BlendMethod::Hue, 900.0f, 450.0f, data);
blender(canvas, "Saturation (Not Supported)", tvg::BlendMethod::Saturation, 900.0f, 600.0f, data);
blender(canvas, "Color (Not Supported)", tvg::BlendMethod::Color, 900.0f, 750.0f, data);
blender(canvas, "Luminosity (Not Supported)", tvg::BlendMethod::Luminosity, 900.0f, 900.0f, data);
blender(canvas, "Hue", tvg::BlendMethod::Hue, 900.0f, 450.0f, data);
blender(canvas, "Saturation", tvg::BlendMethod::Saturation, 900.0f, 600.0f, data);
blender(canvas, "Color", tvg::BlendMethod::Color, 900.0f, 750.0f, data);
blender(canvas, "Luminosity", tvg::BlendMethod::Luminosity, 900.0f, 900.0f, data);
blender(canvas, "Add", tvg::BlendMethod::Add, 900.0f, 1050.0f, data);
blender(canvas, "HardMix (Not Supported)", tvg::BlendMethod::HardMix, 900.0f, 1200.0f, data);
blender(canvas, "HardMix", tvg::BlendMethod::HardMix, 900.0f, 1200.0f, data);
free(data);

View file

@ -209,12 +209,12 @@ enum class BlendMethod : uint8_t
SoftLight, ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (255 - 2 * S) * (D * D) + (2 * S * D)
Difference, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S)
Exclusion, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. S + D - (2 * S * D)
Hue, ///< Reserved. Not supported.
Saturation, ///< Reserved. Not supported.
Color, ///< Reserved. Not supported.
Luminosity, ///< Reserved. Not supported.
Hue, ///< Combine with HSL(Sh + Ds + Dl) then convert it to RGB.
Saturation, ///< Combine with HSL(Dh + Ss + Dl) then convert it to RGB.
Color, ///< Combine with HSL(Sh + Ss + Dl) then convert it to RGB.
Luminosity, ///< Combine with HSL(Dh + Ds + Sl) then convert it to RGB.
Add, ///< Simply adds pixel values of one layer with the other. (S + D)
HardMix, ///< Reserved. Not supported.
HardMix, ///< Adds S and D; result is 255 if the sum is greater than or equal to 255, otherwise 0.
Composition = 255 ///< Used for intermediate composition. @since 1.0
};

View file

@ -27,11 +27,6 @@ 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;
@ -53,6 +48,7 @@ void hsl2rgb(float h, float s, float l, uint8_t& r, uint8_t& g, uint8_t& b)
auto vsf = v * sv * f;
auto t = p + vsf;
auto q = v - vsf;
float tr, tg, tb;
switch (i) {
case 0: tr = v; tg = t; tb = p; break;
@ -61,7 +57,7 @@ void hsl2rgb(float h, float s, float l, uint8_t& r, uint8_t& g, uint8_t& b)
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;
default: tr = tg = tb = 0.0f; break;
}
r = (uint8_t)nearbyint(tr * 255.0f);
g = (uint8_t)nearbyint(tg * 255.0f);

View file

@ -645,7 +645,7 @@ static bool _toColor(const char* str, uint8_t& r, uint8_t&g, uint8_t& b, char**
hsl.l /= 100.0f;
brightness = _skipSpace(brightness + 1, nullptr);
if (brightness && brightness[0] == ')' && brightness[1] == '\0') {
hsl2rgb(hsl.h, hsl.s, hsl.l, r, g, b);
hsl2rgb(hsl.h, tvg::clamp(hsl.s, 0.0f, 1.0f), tvg::clamp(hsl.l, 0.0f, 1.0f), r, g, b);
return true;
}
}

View file

@ -26,6 +26,7 @@
#include <algorithm>
#include "tvgCommon.h"
#include "tvgMath.h"
#include "tvgColor.h"
#include "tvgRender.h"
#define SW_CURVE_TYPE_POINT 0
@ -564,6 +565,80 @@ static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d)
return BLEND_PRE(JOIN(255, f(C1(s), o.r), f(C2(s), o.g), f(C3(s), o.b)), s, o.a);
}
void rasterRGB2HSL(uint8_t r, uint8_t g, uint8_t b, float* h, float* s, float* l);
static inline uint32_t opBlendHue(uint32_t s, uint32_t d)
{
RenderColor o;
if (!BLEND_UPRE(d, o)) return s;
float sh, ds, dl;
rasterRGB2HSL(C1(s), C2(s), C3(s), &sh, 0, 0);
rasterRGB2HSL(o.r, o.g, o.b, 0, &ds, &dl);
uint8_t r, g, b;
hsl2rgb(sh, ds, dl, r, g, b);
return BLEND_PRE(JOIN(255, r, g, b), s, o.a);
}
static inline uint32_t opBlendSaturation(uint32_t s, uint32_t d)
{
RenderColor o;
if (!BLEND_UPRE(d, o)) return s;
float dh, ss, dl;
rasterRGB2HSL(C1(s), C2(s), C3(s), 0, &ss, 0);
rasterRGB2HSL(o.r, o.g, o.b, &dh, 0, &dl);
uint8_t r, g, b;
hsl2rgb(dh, ss, dl, r, g, b);
return BLEND_PRE(JOIN(255, r, g, b), s, o.a);
}
static inline uint32_t opBlendColor(uint32_t s, uint32_t d)
{
RenderColor o;
if (!BLEND_UPRE(d, o)) return s;
float sh, ss, dl;
rasterRGB2HSL(C1(s), C2(s), C3(s), &sh, &ss, 0);
rasterRGB2HSL(o.r, o.g, o.b, 0, 0, &dl);
uint8_t r, g, b;
hsl2rgb(sh, ss, dl, r, g, b);
return BLEND_PRE(JOIN(255, r, g, b), s, o.a);
}
static inline uint32_t opBlendLuminosity(uint32_t s, uint32_t d)
{
RenderColor o;
if (!BLEND_UPRE(d, o)) return s;
float dh, ds, sl;
rasterRGB2HSL(C1(s), C2(s), C3(s), 0, 0, &sl);
rasterRGB2HSL(o.r, o.g, o.b, &dh, &ds, 0);
uint8_t r, g, b;
hsl2rgb(dh, ds, sl, r, g, b);
return BLEND_PRE(JOIN(255, r, g, b), s, o.a);
}
static inline uint32_t opBlendHardMix(uint32_t s, uint32_t d)
{
RenderColor o;
if (!BLEND_UPRE(d, o)) return s;
auto f = [](uint8_t s, uint8_t d) {
return (s + d >= 255) ? 255 : 0;
};
return BLEND_PRE(JOIN(255, f(C1(s), o.r), f(C2(s), o.g), f(C3(s), o.b)), s, o.a);
}
int64_t mathMultiply(int64_t a, int64_t b);
int64_t mathDivide(int64_t a, int64_t b);
@ -679,4 +754,6 @@ bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct);
void effectTritoneUpdate(RenderEffectTritone* effect);
bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct);
#endif /* _TVG_SW_COMMON_H_ */

View file

@ -1769,4 +1769,40 @@ void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32
}
}
}
}
}
//TODO: can be moved in tvgColor
void rasterRGB2HSL(uint8_t r, uint8_t g, uint8_t b, float* h, float* s, float* l)
{
auto rf = r / 255.0f;
auto gf = g / 255.0f;
auto bf = b / 255.0f;
auto maxVal = std::max(std::max(rf, gf), bf);
auto minVal = std::min(std::min(rf, gf), bf);
auto delta = maxVal - minVal;
//lightness
float t;
if (l || s) {
t = (maxVal + minVal) * 0.5f;
if (l) *l = t;
}
if (tvg::zero(delta)) {
if (h) *h = 0.0f;
if (s) *s = 0.0f;
} else {
//saturation
if (s) {
*s = (t < 0.5f) ? (delta / (maxVal + minVal)) : (delta / (2.0f - maxVal - minVal));
}
//hue
if (h) {
if (maxVal == rf) *h = (gf - bf) / delta + (gf < bf ? 6.0f : 0.0f);
else if (maxVal == gf) *h = (bf - rf) / delta + 2.0f;
else *h = (rf - gf) / delta + 4.0f;
*h *= 60.0f; //directly convert to degrees
}
}
}

View file

@ -564,9 +564,24 @@ bool SwRenderer::blend(BlendMethod method)
case BlendMethod::Exclusion:
surface->blender = opBlendExclusion;
break;
case BlendMethod::Hue:
surface->blender = opBlendHue;
break;
case BlendMethod::Saturation:
surface->blender = opBlendSaturation;
break;
case BlendMethod::Color:
surface->blender = opBlendColor;
break;
case BlendMethod::Luminosity:
surface->blender = opBlendLuminosity;
break;
case BlendMethod::Add:
surface->blender = opBlendAdd;
break;
case BlendMethod::HardMix:
surface->blender = opBlendHardMix;
break;
default:
TVGLOG("SW_ENGINE", "Non supported blending option = %d", (int) method);
surface->blender = nullptr;

View file

@ -429,10 +429,7 @@ uint8_t Paint::opacity() const noexcept
Result Paint::blend(BlendMethod method) noexcept
{
//TODO: Remove later
if (method == BlendMethod::Hue || method == BlendMethod::Saturation || method == BlendMethod::Color || method == BlendMethod::Luminosity || method == BlendMethod::HardMix) return Result::NonSupport;
if (method == BlendMethod::Composition || method <= BlendMethod::HardMix) {
if (method <= BlendMethod::HardMix || method == BlendMethod::Composition) {
pImpl->blend(method);
return Result::Success;
}