sw_engine: support Scene ClipPath behavior.

Introduced SceneTask, it implements scene clippath behavior
by merging RLE of the scene children render data on any running thread.

Co-authored-by: Michal Szczecinski <m.szczecinsk@partner.samsung.com>

@Issue: https://github.com/thorvg/thorvg/issues/524
This commit is contained in:
Hermet Park 2023-04-13 16:35:42 +09:00
parent a45275b779
commit b26672a1f6
8 changed files with 227 additions and 18 deletions

View file

@ -201,6 +201,13 @@ RenderData GlRenderer::prepare(TVG_UNUSED Surface* image, TVG_UNUSED Polygon* tr
}
RenderData GlRenderer::prepare(TVG_UNUSED const Array<RenderData>& scene, TVG_UNUSED RenderData data, TVG_UNUSED const RenderTransform* transform, TVG_UNUSED uint32_t opacity, TVG_UNUSED Array<RenderData>& clips, TVG_UNUSED RenderUpdateFlag flags)
{
//TODO:
return nullptr;
}
RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, TVG_UNUSED uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, TVG_UNUSED bool clipper)
{
//prepare shape data

View file

@ -31,6 +31,7 @@ public:
Surface surface = {nullptr, 0, 0, 0};
RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) override;
RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
bool preRender() override;
bool renderShape(RenderData data) override;

View file

@ -335,10 +335,12 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x,
void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
SwRleData* rleRender(const SwBBox* bbox);
void rleFree(SwRleData* rle);
void rleReset(SwRleData* rle);
void rleClipPath(SwRleData *rle, const SwRleData *clip);
void rleClipRect(SwRleData *rle, const SwBBox* clip);
void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2);
void rleClipPath(SwRleData* rle, const SwRleData* clip);
void rleClipRect(SwRleData* rle, const SwBBox* clip);
SwMpool* mpoolInit(uint32_t threads);
bool mpoolTerm(SwMpool* mpool);

View file

@ -62,6 +62,7 @@ struct SwTask : Task
virtual bool dispose() = 0;
virtual bool clip(SwRleData* target) = 0;
virtual SwRleData* rle() = 0;
virtual ~SwTask()
{
@ -86,6 +87,14 @@ struct SwShapeTask : SwTask
return true;
}
SwRleData* rle() override
{
if (!shape.rle && shape.fastTrack) {
shape.rle = rleRender(&shape.bbox);
}
return shape.rle;
}
void run(unsigned tid) override
{
if (opacity == 0 && !clipper) return; //Invisible
@ -184,6 +193,68 @@ struct SwShapeTask : SwTask
};
struct SwSceneTask : SwTask
{
Array<RenderData> scene; //list of paints render data (SwTask)
SwRleData* sceneRle = nullptr;
bool clip(SwRleData* target) override
{
//Only one shape
if (scene.count == 1) {
return static_cast<SwTask*>(*scene.data)->clip(target);
}
//More than one shapes
if (sceneRle) rleClipPath(target, sceneRle);
else TVGLOG("SW_ENGINE", "No clippers in a scene?");
return true;
}
SwRleData* rle() override
{
return sceneRle;
}
void run(unsigned tid) override
{
//TODO: Skip the run if the scene hans't changed.
if (!sceneRle) sceneRle = static_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
else rleReset(sceneRle);
//Only one shape
if (scene.count == 1) {
auto clipper = static_cast<SwTask*>(*scene.data);
clipper->done(tid);
//Merge shapes if it has more than one shapes
} else {
//Merge first two clippers
auto clipper1 = static_cast<SwTask*>(*scene.data);
clipper1->done(tid);
auto clipper2 = static_cast<SwTask*>(*(scene.data + 1));
clipper2->done(tid);
rleMerge(sceneRle, clipper1->rle(), clipper2->rle());
//Unify the remained clippers
for (auto rd = scene.data + 2; rd < (scene.data + scene.count); ++rd) {
auto clipper = static_cast<SwTask*>(*rd);
clipper->done(tid);
rleMerge(sceneRle, sceneRle, clipper->rle());
}
}
}
bool dispose() override
{
rleFree(sceneRle);
return true;
}
};
struct SwImageTask : SwTask
{
SwImage image;
@ -196,6 +267,12 @@ struct SwImageTask : SwTask
return true;
}
SwRleData* rle() override
{
TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?");
return nullptr;
}
void run(unsigned tid) override
{
auto clipRegion = bbox;
@ -600,7 +677,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
//Finish previous task if it has duplicated request.
task->done();
if (clips.count > 0) task->clips = clips;
task->clips = clips;
if (transform) {
if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
@ -647,6 +724,17 @@ RenderData SwRenderer::prepare(Surface* image, Polygon* triangles, uint32_t tria
}
RenderData SwRenderer::prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
{
//prepare task
auto task = static_cast<SwSceneTask*>(data);
if (!task) task = new SwSceneTask;
task->scene = scene;
return prepareCommon(task, transform, opacity, clips, flags);
}
RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper)
{
//prepare task
@ -654,8 +742,9 @@ RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const
if (!task) {
task = new SwShapeTask;
task->rshape = &rshape;
task->clipper = clipper;
}
task->clipper = clipper;
return prepareCommon(task, transform, opacity, clips, flags);
}

View file

@ -37,6 +37,7 @@ class SwRenderer : public RenderMethod
{
public:
RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) override;
RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
bool preRender() override;
bool renderShape(RenderData data) override;

View file

@ -773,7 +773,7 @@ static int _genRle(RleWorker& rw)
}
SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
{
auto out = outSpans;
auto spans = targetRle->spans;
@ -826,7 +826,7 @@ SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle,
}
SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
{
auto out = outSpans;
auto spans = targetRle->spans;
@ -865,6 +865,46 @@ SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSp
}
static SwSpan* _mergeSpansRegion(const SwRleData *clip1, const SwRleData *clip2, SwSpan *outSpans)
{
auto out = outSpans;
auto spans1 = clip1->spans;
auto end1 = clip1->spans + clip1->size;
auto spans2 = clip2->spans;
auto end2 = clip2->spans + clip2->size;
//list two spans up in y order
//TODO: Remove duplicated regions?
while (spans1 < end1 && spans2 < end2) {
while (spans1 < end1 && spans1->y <= spans2->y) {
*out = *spans1;
++spans1;
++out;
}
if (spans1 >= end1) break;
while (spans2 < end2 && spans2->y <= spans1->y) {
*out = *spans2;
++spans2;
++out;
}
}
//Leftovers
while (spans1 < end1) {
*out = *spans1;
++spans1;
++out;
}
while (spans2 < end2) {
*out = *spans2;
++spans2;
++out;
}
return out;
}
void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
{
free(rle->spans);
@ -1001,6 +1041,28 @@ error:
}
SwRleData* rleRender(const SwBBox* bbox)
{
auto width = static_cast<uint16_t>(bbox->max.x - bbox->min.x);
auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y);
auto rle = static_cast<SwRleData*>(malloc(sizeof(SwRleData)));
rle->spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * height));
rle->size = height;
rle->alloc = height;
auto span = rle->spans;
for (uint16_t i = 0; i < height; ++i, ++span) {
span->x = bbox->min.x;
span->y = bbox->min.y + i;
span->len = width;
span->coverage = 255;
}
return rle;
}
void rleReset(SwRleData* rle)
{
if (!rle) return;
@ -1016,12 +1078,50 @@ void rleFree(SwRleData* rle)
}
void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2)
{
if (!rle || (!clip1 && !clip2)) return;
if (clip1 && clip1->size == 0 && clip2 && clip2->size == 0) return;
TVGLOG("SW_ENGINE", "Unifying Rle!");
//clip1 is empty, just copy clip2
if (!clip1 || clip1->size == 0) {
if (clip2) {
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip2->size)));
memcpy(spans, clip2->spans, clip2->size);
_replaceClipSpan(rle, spans, clip2->size);
} else {
_replaceClipSpan(rle, nullptr, 0);
}
return;
}
//clip2 is empty, just copy clip1
if (!clip2 || clip2->size == 0) {
if (clip1) {
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip1->size)));
memcpy(spans, clip1->spans, clip1->size);
_replaceClipSpan(rle, spans, clip1->size);
} else {
_replaceClipSpan(rle, nullptr, 0);
}
return;
}
auto spanCnt = clip1->size + clip2->size;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * spanCnt));
auto spansEnd = _mergeSpansRegion(clip1, clip2, spans);
_replaceClipSpan(rle, spans, spansEnd - spans);
}
void rleClipPath(SwRleData *rle, const SwRleData *clip)
{
if (rle->size == 0 || clip->size == 0) return;
auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
if (!spans) return;
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
_replaceClipSpan(rle, spans, spansEnd - spans);
@ -1034,10 +1134,9 @@ void rleClipRect(SwRleData *rle, const SwBBox* clip)
{
if (rle->size == 0) return;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
if (!spans) return;
auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
_replaceClipSpan(rle, spans, spansEnd - spans);
TVGLOG("SW_ENGINE", "Using ClipRect!");
}
}

View file

@ -186,6 +186,7 @@ class RenderMethod
public:
virtual ~RenderMethod() {}
virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags, bool clipper) = 0;
virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
virtual RenderData prepare(Surface* image, Polygon* triangles, uint32_t triangleCnt, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
virtual bool preRender() = 0;
virtual bool renderShape(RenderData data) = 0;

View file

@ -61,6 +61,7 @@ struct Scene::Impl
Array<Paint*> paints;
uint8_t opacity; //for composition
RenderMethod* renderer = nullptr; //keep it for explicit clear
RenderData rd = nullptr;
Scene* scene = nullptr;
Impl(Scene* s) : scene(s)
@ -80,9 +81,11 @@ struct Scene::Impl
(*paint)->pImpl->dispose(renderer);
}
auto ret = renderer.dispose(rd);
this->renderer = nullptr;
this->rd = nullptr;
return true;
return ret;
}
bool needComposition(uint32_t opacity)
@ -109,16 +112,22 @@ struct Scene::Impl
this->opacity = static_cast<uint8_t>(opacity);
if (needComposition(opacity)) opacity = 255;
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
(*paint)->pImpl->update(renderer, transform, opacity, clips, static_cast<uint32_t>(flag), clipper);
}
/* FXIME: it requires to return list of children engine data
This is necessary for scene composition */
this->renderer = &renderer;
return nullptr;
if (clipper) {
Array<RenderData> rds;
rds.reserve(paints.count);
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
rds.push((*paint)->pImpl->update(renderer, transform, opacity, clips, flag, true));
}
rd = renderer.prepare(rds, rd, transform, opacity, clips, flag);
return rd;
} else {
for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
(*paint)->pImpl->update(renderer, transform, opacity, clips, flag, false);
}
return nullptr;
}
}
bool render(RenderMethod& renderer)