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
This commit is contained in:
Hermet Park 2025-07-15 20:19:10 +09:00 committed by Mira Grudzinska
parent 8c9c2888cc
commit 5abccb2091

View file

@ -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);