From 5abccb2091436e1ef4ecf7fb99d051821a90cd4d Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 15 Jul 2025 20:19:10 +0900 Subject: [PATCH] sw_engine: improved the dropshadow effect support - shadow rendering was skipped when the blur value was 0. it now correctly renders shadows without blur. - this also fixes the distance offset scalability. issue: https://github.com/thorvg/thorvg/issues/3602 --- src/renderer/sw_engine/tvgSwPostEffect.cpp | 101 ++++++++++++++++----- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/src/renderer/sw_engine/tvgSwPostEffect.cpp b/src/renderer/sw_engine/tvgSwPostEffect.cpp index f2adecf2..7e6909c6 100644 --- a/src/renderer/sw_engine/tvgSwPostEffect.cpp +++ b/src/renderer/sw_engine/tvgSwPostEffect.cpp @@ -265,26 +265,78 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i } } - -static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, SwBBox& region, SwPoint& offset, uint8_t opacity) +static void _shift(uint32_t** dst, uint32_t** src, int dstride, int sstride, int wmax, int hmax, const SwBBox& bbox, const SwPoint& offset, SwSize& size) { - src += (region.min.y * sstride + region.min.x); - dst += (region.min.y * dstride + region.min.x); + size.w = bbox.max.x - bbox.min.x; + size.h = bbox.max.y - bbox.min.y; - auto w = region.max.x - region.min.x; - auto h = region.max.y - region.min.y; - auto translucent = opacity < 255; + //shift + if (bbox.min.x + offset.x < 0) *src -= offset.x; + else *dst += offset.x; - //shift offset - if (region.min.x + offset.x < 0) src -= offset.x; - else dst += offset.x; + if (bbox.min.y + offset.y < 0) *src -= (offset.y * sstride); + else *dst += (offset.y * dstride); - if (region.min.y + offset.y < 0) src -= (offset.y * sstride); - else dst += (offset.y * dstride); + if (size.w + bbox.min.x + offset.x > wmax) size.w -= (size.w + bbox.min.x + offset.x - wmax); + if (size.h + bbox.min.y + offset.y > hmax) size.h -= (size.h + bbox.min.y + offset.y - hmax); +} - for (auto y = 0; y < h; ++y) { - if (translucent) rasterTranslucentPixel32(dst, src, w, opacity); - else rasterPixel32(dst, src, w, opacity); + +static void _dropShadowNoFilter(SwImage* dimg, SwImage* simg, const SwBBox& bbox, const SwPoint& offset, uint32_t color) +{ + int dstride = dimg->stride; + int sstride = simg->stride; + + auto src = simg->buf32 + (bbox.min.y * sstride + bbox.min.x); + auto dst = dimg->buf32 + (bbox.min.y * dstride + bbox.min.x); + + //for shadow + auto src2 = src; + auto dst2 = dst; + + SwSize size; + _shift(&dst2, &src2, dimg->stride, simg->stride, dimg->w, dimg->h, bbox, offset, size); + + //shadow + for (auto y = 0; y < size.h; ++y) { + auto s2 = src2; + auto d2 = dst2; + for (int x = 0; x < size.w; ++x, ++d2, ++s2) { + *d2 = ALPHA_BLEND(color, A(*s2)); + } + src2 += sstride; + dst2 += dstride; + } + + //original + for (auto y = 0; y < (bbox.max.y - bbox.min.y); ++y) { + auto s = src; + auto d = dst; + for (int x = 0; x < (bbox.max.x - bbox.min.x); ++x, ++d, ++s) { + *d = *s + ALPHA_BLEND(*d, IA(*s)); + } + src += sstride; + dst += dstride; + } +} + + +static void _dropShadowShift(SwImage* dimg, SwImage* simg, SwBBox& bbox, const SwPoint& offset, uint8_t opacity) +{ + int dstride = dimg->stride; + int sstride = simg->stride; + + auto src = simg->buf32 + (bbox.min.y * sstride + bbox.min.x); + auto dst = dimg->buf32 + (bbox.min.y * dstride + bbox.min.x); + + auto translucent = (opacity < 255); + + SwSize size; + _shift(&dst, &src, dimg->stride, simg->stride, dimg->w, dimg->h, bbox, offset, size); + + for (auto y = 0; y < size.h; ++y) { + if (translucent) rasterTranslucentPixel32(dst, src, size.w, opacity); + else rasterPixel32(dst, src, size.w, opacity); src += sstride; dst += dstride; } @@ -322,7 +374,7 @@ void effectDropShadowUpdate(RenderEffectDropShadow* params, const Matrix& transf rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality); //invalid - if (rd->extends == 0 || params->color[3] == 0) { + if (params->color[3] == 0) { params->valid = false; return; } @@ -330,7 +382,7 @@ void effectDropShadowUpdate(RenderEffectDropShadow* params, const Matrix& transf //offset if (params->distance > 0.0f) { auto radian = tvg::deg2rad(90.0f - params->angle); - rd->offset = {(SwCoord)(params->distance * cosf(radian)), (SwCoord)(-1.0f * params->distance * sinf(radian))}; + rd->offset = {(int32_t)((params->distance * scale) * cosf(radian)), (int32_t)(-1.0f * (params->distance * scale) * sinf(radian))}; } else { rd->offset = {0, 0}; } @@ -362,6 +414,12 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level); + //no filter required + if (params->sigma == 0.0f) { + _dropShadowNoFilter(buffer[1], &cmp->image, bbox, data->offset, color); + std::swap(cmp->image.buf32, buffer[1]->buf32); + return true; + } //saving the original image in order to overlay it into the filtered image. _dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false); std::swap(front, buffer[0]->buf32); @@ -386,7 +444,7 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe std::swap(cmp->image.buf32, back); //draw to the intermediate surface rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h); - _dropShadowShift(buffer[1]->buf32, cmp->image.buf32, stride, stride, bbox, data->offset, params->color[3]); + _dropShadowShift(buffer[1], &cmp->image, bbox, data->offset, params->color[3]); std::swap(cmp->image.buf32, buffer[1]->buf32); //compositing shadow and body @@ -475,8 +533,6 @@ bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct) TVGLOG("SW_ENGINE", "Tint region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->black[0], params->black[1], params->black[2], params->white[0], params->white[1], params->white[2], params->intensity); - /* Tint Formula: (1 - L) * Black + L * White, where the L is Luminance. */ - if (direct) { auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x); auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); @@ -515,11 +571,6 @@ bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct) static uint32_t _trintone(uint32_t s, uint32_t m, uint32_t h, int l) { - /* Tritone Formula: - if (L < 0.5) { (1 - 2L) * Shadow + 2L * Midtone } - else { (1 - 2(L - 0.5)) * Midtone + (2(L - 0.5)) * Highlight } - Where the L is Luminance. */ - if (l < 128) { auto a = std::min(l * 2, 255); return ALPHA_BLEND(s, 255 - a) + ALPHA_BLEND(m, a);