common shape: fixing stroking composition issue.

When combining Shape, Stroke, and AlphaMasking, there is a missing
composition step which results in an incorrect output as expected by the user.

This problem is resolved by introducing shape fill and stroking composition.

@Issue: https://github.com/thorvg/thorvg/issues/209
This commit is contained in:
Hermet Park 2023-05-17 14:56:23 +09:00 committed by Hermet Park
parent 4938c3253c
commit 6994925b9d
4 changed files with 42 additions and 4 deletions

View file

@ -70,6 +70,7 @@ protected: \
#define _TVG_DECLARE_ACCESSOR() \
friend Canvas; \
friend Scene; \
friend Shape; \
friend Picture; \
friend Accessor; \
friend IteratorAccessor
@ -81,6 +82,7 @@ namespace tvg
class RenderMethod;
class IteratorAccessor;
class Scene;
class Shape;
class Picture;
class Canvas;
class Accessor;

View file

@ -142,8 +142,9 @@ struct Picture::Impl
const Paint* target;
auto method = picture->composite(&target);
if (!target || method == tvg::CompositeMethod::ClipPath) return false;
if (target->pImpl->opacity < 255 && target->pImpl->opacity > 0) return true;
return false;
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
return true;
}
RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper)

View file

@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f;
/* External Class Implementation */
/************************************************************************/
Shape :: Shape() : pImpl(new Impl())
Shape :: Shape() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));

View file

@ -35,6 +35,12 @@ struct Shape::Impl
RenderShape rs; //shape data
RenderData rd = nullptr; //engine data
uint32_t flag = RenderUpdateFlag::None;
Shape* shape;
bool needComp; //for composition
Impl(Shape* s) : shape(s)
{
}
bool dispose(RenderMethod& renderer)
{
@ -45,11 +51,40 @@ struct Shape::Impl
bool render(RenderMethod& renderer)
{
return renderer.renderShape(rd);
Compositor* cmp = nullptr;
bool ret;
if (needComp) {
cmp = renderer.target(bounds(renderer), renderer.colorSpace());
renderer.beginComposite(cmp, CompositeMethod::None, 255);
}
ret = renderer.renderShape(rd);
if (cmp) renderer.endComposite(cmp);
return ret;
}
bool needComposition(uint32_t opacity)
{
//In this case, paint(shape) would try composition by backends. (See: SwShapeTask::run())
//TODO: that composition should be moved here?
if (opacity < 255) return false;
//Shape composition is only necessary when stroking is valid.
if (!rs.stroke || rs.stroke->width < FLT_EPSILON || rs.stroke->color[3] == 0) return false;
//Composition test
const Paint* target;
auto method = shape->composite(&target);
if (!target || method == tvg::CompositeMethod::ClipPath) return false;
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
return true;
}
RenderData update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag, bool clipper)
{
needComp = needComposition(opacity);
rd = renderer.prepare(rs, rd, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
flag = RenderUpdateFlag::None;
return rd;