renderer: fixed a clippging update isssue

ThorVG did not consider updates to the clipping path
when a paint object was modified after being added to the scene.

This fix ensures that any updates to clipping are correctly
reflected during rendering.

issue: https://github.com/thorvg/thorvg/issues/3403
This commit is contained in:
Hermet Park 2025-04-16 12:43:55 +09:00
parent 8b2024a8f4
commit 1b37743b2c
5 changed files with 35 additions and 40 deletions

View file

@ -118,53 +118,40 @@ struct SwShapeTask : SwTask
auto strokeWidth = validStrokeWidth(); auto strokeWidth = validStrokeWidth();
SwBBox renderRegion{}; SwBBox renderRegion{};
auto visibleFill = false; auto updateShape = (RenderUpdateFlag::Path | RenderUpdateFlag::Transform | RenderUpdateFlag::Clip);
auto updateFill = false;
//This checks also for the case, if the invisible shape turned to visible by alpha.
auto prepareShape = !shapePrepared(&shape) && flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient);
//Shape //Shape
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) { if (updateShape || flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient)) {
uint8_t alpha = 0; uint8_t alpha = 0;
rshape->fillColor(nullptr, nullptr, nullptr, &alpha); rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
alpha = MULTIPLY(alpha, opacity); updateFill = (MULTIPLY(alpha, opacity) || rshape->fill);
visibleFill = (alpha > 0 || rshape->fill); if (updateShape) shapeReset(&shape);
shapeReset(&shape); if (updateFill || clipper) {
if (visibleFill || clipper) { if (shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) {
if (!shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) { if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
visibleFill = false; } else {
updateFill = false;
renderRegion.reset(); renderRegion.reset();
} }
} }
} }
//Fill //Fill
if (flags & (RenderUpdateFlag::Path |RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) { if (updateFill) {
if (visibleFill || clipper) {
if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
}
if (auto fill = rshape->fill) { if (auto fill = rshape->fill) {
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false; auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
if (ctable) shapeResetFill(&shape); if (ctable) shapeResetFill(&shape);
if (!shapeGenFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err; if (!shapeGenFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
} else {
shapeDelFill(&shape);
} }
} }
//Stroke //Stroke
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { if ((updateShape || flags & RenderUpdateFlag::Stroke) && (strokeWidth > 0.0f)) {
if (strokeWidth > 0.0f) { shapeResetStroke(&shape, rshape, transform);
shapeResetStroke(&shape, rshape, transform); if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err;
if (auto fill = rshape->strokeFill()) {
if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err; auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
if (auto fill = rshape->strokeFill()) { if (ctable) shapeResetStrokeFill(&shape);
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
if (ctable) shapeResetStrokeFill(&shape);
if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
} else {
shapeDelStrokeFill(&shape);
}
} else {
shapeDelStroke(&shape);
} }
} }

View file

@ -276,16 +276,19 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
/* 2. Clipping */ /* 2. Clipping */
if (this->clipper) { if (this->clipper) {
P(this->clipper)->ctxFlag &= ~ContextFlag::FastTrack; //reset auto pclip = P(this->clipper);
if (pclip->renderFlag | static_cast<Shape*>(this->clipper)->pImpl->rFlag) renderFlag |= RenderUpdateFlag::Clip;
pclip->ctxFlag &= ~ContextFlag::FastTrack; //reset
viewport = renderer->viewport(); viewport = renderer->viewport();
/* TODO: Intersect the clipper's clipper, if both are FastTrack. /* TODO: Intersect the clipper's clipper, if both are FastTrack.
Update the subsequent clipper first and check its ctxFlag. */ Update the subsequent clipper first and check its ctxFlag. */
if (!P(this->clipper)->clipper && (compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) { if (!pclip->clipper && _compFastTrack(renderer, this->clipper, pm, viewport) == Result::Success) {
P(this->clipper)->ctxFlag |= ContextFlag::FastTrack; pclip->ctxFlag |= ContextFlag::FastTrack;
} compFastTrack = Result::Success;
if (compFastTrack == Result::InsufficientCondition) { } else {
trd = P(this->clipper)->update(renderer, pm, clips, 255, pFlag, true); trd = pclip->update(renderer, pm, clips, 255, pFlag, true);
clips.push(trd); clips.push(trd);
compFastTrack = Result::InsufficientCondition;
} }
} }

View file

@ -72,8 +72,8 @@ namespace tvg
tvg::rotate(&m, degree); tvg::rotate(&m, degree);
} }
} tr; } tr;
RenderUpdateFlag renderFlag = RenderUpdateFlag::None;
BlendMethod blendMethod; BlendMethod blendMethod;
uint8_t renderFlag;
uint8_t ctxFlag; uint8_t ctxFlag;
uint8_t opacity; uint8_t opacity;
uint8_t refCnt = 0; //reference count uint8_t refCnt = 0; //reference count

View file

@ -37,9 +37,14 @@ using pixel_t = uint32_t;
#define DASH_PATTERN_THRESHOLD 0.001f #define DASH_PATTERN_THRESHOLD 0.001f
enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255}; enum RenderUpdateFlag : uint16_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, Clip = 256, All = 0xffff};
enum CompositionFlag : uint8_t {Invalid = 0, Opacity = 1, Blending = 2, Masking = 4, PostProcessing = 8}; //Composition Purpose enum CompositionFlag : uint8_t {Invalid = 0, Opacity = 1, Blending = 2, Masking = 4, PostProcessing = 8}; //Composition Purpose
static inline void operator|=(RenderUpdateFlag& a, const RenderUpdateFlag b)
{
a = RenderUpdateFlag(uint16_t(a) | uint16_t(b));
}
//TODO: Move this in public header unifying with SwCanvas::Colorspace //TODO: Move this in public header unifying with SwCanvas::Colorspace
enum ColorSpace : uint8_t enum ColorSpace : uint8_t
{ {

View file

@ -33,7 +33,7 @@ struct Shape::Impl
RenderShape rs; //shape data RenderShape rs; //shape data
RenderData rd = nullptr; //engine data RenderData rd = nullptr; //engine data
Shape* shape; Shape* shape;
uint8_t rFlag = RenderUpdateFlag::None; RenderUpdateFlag rFlag = RenderUpdateFlag::None;
uint8_t cFlag = CompositionFlag::Invalid; uint8_t cFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition uint8_t opacity; //for composition