sw_engine: cleaned up the blending operations.

Corrected the alpha interpolation order during blending.
This also corrected the hard mix blending result in the guitar sample.

issue: https://github.com/thorvg/thorvg/issues/2704
This commit is contained in:
Hermet Park 2024-09-06 15:31:00 +09:00
parent 5849431850
commit 403710ffbc
12 changed files with 29 additions and 47 deletions

View file

@ -912,10 +912,8 @@ const Surface* GlRenderer::mainSurface()
}
bool GlRenderer::blend(TVG_UNUSED BlendMethod method, TVG_UNUSED bool direct)
bool GlRenderer::blend(TVG_UNUSED BlendMethod method)
{
if (method != BlendMethod::Normal) return true;
//TODO:
return false;
}

View file

@ -63,7 +63,7 @@ public:
RenderRegion region(RenderData data) override;
RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override;
bool blend(BlendMethod method, bool direct) override;
bool blend(BlendMethod method) override;
ColorSpace colorSpace() override;
const Surface* mainSurface() override;

View file

@ -367,7 +367,7 @@ static inline uint32_t opBlendSrcOver(uint32_t s, TVG_UNUSED uint32_t d, TVG_UNU
}
//TODO: BlendMethod could remove the alpha parameter.
static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, uint8_t a)
static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
//if (s > d) => s - d
//else => d - s
@ -404,7 +404,7 @@ static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opBlendDirectMultiply(uint32_t s, uint32_t d, uint8_t a)
static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
// s * d
auto c1 = MULTIPLY(C1(s), C1(d));
@ -413,11 +413,6 @@ static inline uint32_t opBlendDirectMultiply(uint32_t s, uint32_t d, uint8_t a)
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, uint8_t a)
{
return opBlendDirectMultiply(s, d, a) + ALPHA_BLEND(d, IA(s));
}
static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
// if (2 * d < da) => 2 * s * d,
@ -570,7 +565,7 @@ bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);
void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
void rasterUnpremultiply(Surface* surface);

View file

@ -423,12 +423,11 @@ static bool _rasterBlendingRect(SwSurface* surface, const SwBBox& region, uint8_
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto color = surface->join(r, g, b, a);
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto ialpha = 255 - a;
for (uint32_t y = 0; y < h; ++y) {
auto dst = &buffer[y * surface->stride];
for (uint32_t x = 0; x < w; ++x, ++dst) {
*dst = surface->blender(color, *dst, ialpha);
*dst = surface->blender(color, *dst, 255);
}
}
return true;
@ -595,17 +594,16 @@ static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, uint8_t r,
auto span = rle->spans;
auto color = surface->join(r, g, b, a);
auto ialpha = 255 - a;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst) {
*dst = surface->blender(color, *dst, ialpha);
*dst = surface->blender(color, *dst, 255);
}
} else {
for (uint32_t x = 0; x < span->len; ++x, ++dst) {
auto tmp = surface->blender(color, *dst, ialpha);
auto tmp = surface->blender(color, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, span->coverage);
}
}
@ -813,9 +811,8 @@ static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* ima
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) src = ALPHA_BLEND(src, opacity);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src)));
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(src)));
}
}
}
@ -981,18 +978,12 @@ static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* ima
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
*dst = surface->blender(*img, *dst, IA(*img));
}
} else if (opacity == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
auto tmp = surface->blender(*img, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(*img)));
*dst = surface->blender(*img, *dst, 255);
}
} else {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
auto src = ALPHA_BLEND(*img, opacity);
auto tmp = surface->blender(src, *dst, IA(src));
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src)));
auto tmp = surface->blender(*img, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(*img)));
}
}
}
@ -1164,9 +1155,8 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) ALPHA_BLEND(src, opacity);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, A(src));
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(src)));
}
}
return true;
@ -1384,9 +1374,8 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
}
} else {
for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
auto tmp = ALPHA_BLEND(*src, opacity);
auto tmp2 = surface->blender(tmp, *dst, 255);
*dst = INTERPOLATE(tmp2, *dst, A(tmp));
auto tmp = surface->blender(*src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(*src)));
}
}
dbuffer += surface->stride;
@ -1843,7 +1832,7 @@ bool rasterCompositor(SwSurface* surface)
}
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val)
{
if (!surface || !surface->buf32 || surface->stride == 0 || surface->w == 0 || surface->h == 0) return false;
@ -1851,11 +1840,11 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_
if (surface->channelSize == sizeof(uint32_t)) {
//full clear
if (w == surface->stride) {
rasterPixel32(surface->buf32, 0x00000000, surface->stride * y, w * h);
rasterPixel32(surface->buf32, val, surface->stride * y, w * h);
//partial clear
} else {
for (uint32_t i = 0; i < h; i++) {
rasterPixel32(surface->buf32, 0x00000000, (surface->stride * y + x) + (surface->stride * i), w);
rasterPixel32(surface->buf32, val, (surface->stride * y + x) + (surface->stride * i), w);
}
}
//8 bits

View file

@ -446,7 +446,7 @@ bool SwRenderer::renderShape(RenderData data)
}
bool SwRenderer::blend(BlendMethod method, bool direct)
bool SwRenderer::blend(BlendMethod method)
{
if (surface->blendMethod == method) return true;
surface->blendMethod = method;
@ -459,7 +459,7 @@ bool SwRenderer::blend(BlendMethod method, bool direct)
surface->blender = opBlendScreen;
break;
case BlendMethod::Multiply:
surface->blender = direct ? opBlendDirectMultiply : opBlendMultiply;
surface->blender = opBlendMultiply;
break;
case BlendMethod::Overlay:
surface->blender = opBlendOverlay;
@ -606,7 +606,7 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
cmp->w = cmp->compositor->image.w;
cmp->h = cmp->compositor->image.h;
rasterClear(cmp, x, y, w, h);
rasterClear(cmp, x, y, w, h, (surface->blendMethod == BlendMethod::Normal) ? 0x00000000 : 0x00ffffff);
//Switch render target
surface = cmp;

View file

@ -46,7 +46,7 @@ public:
RenderRegion region(RenderData data) override;
RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override;
bool blend(BlendMethod method, bool direct) override;
bool blend(BlendMethod method) override;
ColorSpace colorSpace() override;
const Surface* mainSurface() override;

View file

@ -73,7 +73,7 @@ bool Picture::Impl::needComposition(uint8_t opacity)
bool Picture::Impl::render(RenderMethod* renderer)
{
bool ret = false;
renderer->blend(picture->blend(), true);
renderer->blend(picture->blend());
if (surface) return renderer->renderImage(rd);
else if (paint) {

View file

@ -296,7 +296,7 @@ public:
virtual RenderRegion region(RenderData data) = 0;
virtual RenderRegion viewport() = 0;
virtual bool viewport(const RenderRegion& vp) = 0;
virtual bool blend(BlendMethod method, bool direct = false) = 0;
virtual bool blend(BlendMethod method) = 0;
virtual ColorSpace colorSpace() = 0;
virtual const Surface* mainSurface() = 0;

View file

@ -54,7 +54,7 @@ struct Shape::Impl
Compositor* cmp = nullptr;
bool ret;
renderer->blend(shape->blend(), !needComp);
renderer->blend(shape->blend());
if (needComp) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace());

View file

@ -89,7 +89,7 @@ struct Text::Impl
bool render(RenderMethod* renderer)
{
renderer->blend(paint->blend(), true);
renderer->blend(paint->blend());
return PP(shape)->render(renderer);
}

View file

@ -225,10 +225,10 @@ bool WgRenderer::viewport(const RenderRegion& vp)
}
bool WgRenderer::blend(BlendMethod method, TVG_UNUSED bool direct)
bool WgRenderer::blend(BlendMethod method)
{
mBlendMethod = method;
return false;
return true;
}

View file

@ -38,7 +38,7 @@ public:
RenderRegion region(RenderData data) override;
RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override;
bool blend(BlendMethod method, bool direct) override;
bool blend(BlendMethod method) override;
ColorSpace colorSpace() override;
const Surface* mainSurface() override;