diff --git a/examples/SceneClipper.cpp b/examples/SceneClipper.cpp deleted file mode 100644 index 8425314b..00000000 --- a/examples/SceneClipper.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved. - - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "Example.h" - -/************************************************************************/ -/* ThorVG Drawing Contents */ -/************************************************************************/ - -struct UserExample : tvgexam::Example -{ - bool content(tvg::Canvas* canvas, uint32_t w, uint32_t h) override - { - if (!canvas) return false; - - /* Using a single shape is sufficient to clip multiple regions. - The disabled code below performs the same function. Please try it. */ - #if 1 - //A scene clipper - auto clipper = tvg::Scene::gen(); - - auto shape1 = tvg::Shape::gen(); - shape1->appendCircle(200, 200, 100, 100); - clipper->push(std::move(shape1)); - - auto shape2 = tvg::Shape::gen(); - shape2->appendCircle(400, 400, 150, 150); - clipper->push(std::move(shape2)); - - auto shape3 = tvg::Shape::gen(); - shape3->appendCircle(150, 300, 60, 60); - clipper->push(std::move(shape3)); - - auto shape4 = tvg::Shape::gen(); - shape4->appendCircle(400, 100, 125, 125); - clipper->push(std::move(shape4)); - - auto shape5 = tvg::Shape::gen(); - shape5->appendCircle(150, 500, 100, 100); - clipper->push(std::move(shape5)); - #else - //A single clipper with multiple regions - auto clipper = tvg::Shape::gen(); - clipper->appendCircle(200, 200, 100, 100); - clipper->appendCircle(400, 400, 150, 150); - clipper->appendCircle(150, 300, 60, 60); - clipper->appendCircle(400, 100, 125, 125); - clipper->appendCircle(150, 500, 100, 100); - #endif - - //Source - auto shape = tvg::Shape::gen(); - shape->appendRect(100, 100, 400, 400, 50, 50); - shape->strokeFill(0, 0, 255); - shape->strokeWidth(10); - shape->fill(255, 255, 255); - shape->composite(std::move(clipper), tvg::CompositeMethod::ClipPath); - canvas->push(std::move(shape)); - - return true; - } -}; - - -/************************************************************************/ -/* Entry Point */ -/************************************************************************/ - -int main(int argc, char **argv) -{ - return tvgexam::main(new UserExample, argc, argv); -} \ No newline at end of file diff --git a/examples/meson.build b/examples/meson.build index 56a97d3e..8dd902c2 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -59,7 +59,6 @@ source_file = [ 'Retaining.cpp', 'Scene.cpp', 'SceneBlending.cpp', - 'SceneClipper.cpp', 'SceneTransform.cpp', 'Shape.cpp', 'Stroke.cpp', diff --git a/inc/thorvg.h b/inc/thorvg.h index f6d4c312..c23eafed 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -157,7 +157,7 @@ enum class FillRule : uint8_t enum class CompositeMethod : uint8_t { None = 0, ///< No composition is applied. - ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. + ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. Note that ClipPath only supports the Shape type. AlphaMask, ///< Alpha Masking using the compositing target's pixels as an alpha value. InvAlphaMask, ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value. LumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9 diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index bcf8d6ea..a9d69561 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -138,7 +138,7 @@ typedef enum { */ typedef enum { TVG_COMPOSITE_METHOD_NONE = 0, ///< No composition is applied. - TVG_COMPOSITE_METHOD_CLIP_PATH, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. + TVG_COMPOSITE_METHOD_CLIP_PATH, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. Note that ClipPath only supports the Shape type. TVG_COMPOSITE_METHOD_ALPHA_MASK, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which intersects with the target is visible. TVG_COMPOSITE_METHOD_INVERSE_ALPHA_MASK, ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which is not covered by the target is visible. TVG_COMPOSITE_METHOD_LUMA_MASK, ///< The source pixels are converted to grayscale (luma value) and alpha blended with the target. As a result, only the part of the source which intersects with the target is visible. \since 0.9 diff --git a/src/renderer/gl_engine/tvgGlRenderer.cpp b/src/renderer/gl_engine/tvgGlRenderer.cpp index 8db1ba84..5e5fdea4 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.cpp +++ b/src/renderer/gl_engine/tvgGlRenderer.cpp @@ -1131,13 +1131,6 @@ RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderDat } -RenderData GlRenderer::prepare(TVG_UNUSED const Array& scene, TVG_UNUSED RenderData data, TVG_UNUSED const Matrix& transform, TVG_UNUSED Array& clips, TVG_UNUSED uint8_t opacity, TVG_UNUSED RenderUpdateFlag flags) -{ - //TODO: - return nullptr; -} - - RenderData GlRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) { // If prepare for clip, only path is meaningful. diff --git a/src/renderer/gl_engine/tvgGlRenderer.h b/src/renderer/gl_engine/tvgGlRenderer.h index b1e65a6b..212ac51e 100644 --- a/src/renderer/gl_engine/tvgGlRenderer.h +++ b/src/renderer/gl_engine/tvgGlRenderer.h @@ -54,7 +54,6 @@ public: }; RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; - RenderData prepare(const Array& scene, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; diff --git a/src/renderer/sw_engine/tvgSwRenderer.cpp b/src/renderer/sw_engine/tvgSwRenderer.cpp index 2e707a88..e93bd166 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -199,59 +199,6 @@ struct SwShapeTask : SwTask }; -struct SwSceneTask : SwTask -{ - Array scene; //list of paints render data (SwTask) - SwRle* sceneRle = nullptr; - - bool clip(SwRle* target) override - { - //Only one shape - if (scene.count == 1) { - return static_cast(*scene.data)->clip(target); - } - - //More than one shapes - if (sceneRle) rleClipPath(target, sceneRle); - else TVGLOG("SW_ENGINE", "No clippers in a scene?"); - - return true; - } - - SwRle* rle() override - { - return sceneRle; - } - - void run(unsigned tid) override - { - //TODO: Skip the run if the scene hasn't changed. - if (!sceneRle) sceneRle = static_cast(calloc(1, sizeof(SwRle))); - else rleReset(sceneRle); - - //Merge shapes if it has more than one shapes - if (scene.count > 1) { - //Merge first two clippers - auto clipper1 = static_cast(*scene.data); - auto clipper2 = static_cast(*(scene.data + 1)); - - rleMerge(sceneRle, clipper1->rle(), clipper2->rle()); - - //Unify the remained clippers - for (auto rd = scene.begin() + 2; rd < scene.end(); ++rd) { - auto clipper = static_cast(*rd); - rleMerge(sceneRle, sceneRle, clipper->rle()); - } - } - } - - void dispose() override - { - rleFree(sceneRle); - } -}; - - struct SwImageTask : SwTask { SwImage image; @@ -759,26 +706,6 @@ RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD } -RenderData SwRenderer::prepare(const Array& scene, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) -{ - //prepare task - auto task = static_cast(data); - if (!task) task = new SwSceneTask; - else task->done(); - - task->scene = scene; - - //TODO: Failed threading them. It would be better if it's possible. - //See: https://github.com/thorvg/thorvg/issues/1409 - //Guarantee composition targets get ready. - for (auto task = scene.begin(); task < scene.end(); ++task) { - static_cast(*task)->done(); - } - - return prepareCommon(task, transform, clips, opacity, flags); -} - - RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) { //prepare task diff --git a/src/renderer/sw_engine/tvgSwRenderer.h b/src/renderer/sw_engine/tvgSwRenderer.h index 2ee46208..ee4d9be1 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.h +++ b/src/renderer/sw_engine/tvgSwRenderer.h @@ -37,7 +37,6 @@ class SwRenderer : public RenderMethod { public: RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; - RenderData prepare(const Array& scene, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; diff --git a/src/renderer/sw_engine/tvgSwRle.cpp b/src/renderer/sw_engine/tvgSwRle.cpp index f7b105f5..cb73e599 100644 --- a/src/renderer/sw_engine/tvgSwRle.cpp +++ b/src/renderer/sw_engine/tvgSwRle.cpp @@ -822,46 +822,6 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRle *targetRle, S } -static SwSpan* _mergeSpansRegion(const SwRle *clip1, const SwRle *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(SwRle *rle, SwSpan* clippedSpans, uint32_t size) { free(rle->spans); @@ -1030,45 +990,6 @@ void rleFree(SwRle* rle) } -void rleMerge(SwRle* rle, SwRle* clip1, SwRle* 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(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(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(malloc(sizeof(SwSpan) * spanCnt)); - auto spansEnd = _mergeSpansRegion(clip1, clip2, spans); - - _replaceClipSpan(rle, spans, spansEnd - spans); -} - - void rleClipPath(SwRle *rle, const SwRle *clip) { if (rle->size == 0 || clip->size == 0) return; diff --git a/src/renderer/tvgPaint.cpp b/src/renderer/tvgPaint.cpp index 8f09ba5a..c8402bcf 100644 --- a/src/renderer/tvgPaint.cpp +++ b/src/renderer/tvgPaint.cpp @@ -435,6 +435,11 @@ Paint* Paint::duplicate() const noexcept Result Paint::composite(std::unique_ptr target, CompositeMethod method) noexcept { + if (method == CompositeMethod::ClipPath && target && target->type() != Type::Shape) { + TVGERR("RENDERER", "ClipPath only allows the Shape!"); + return Result::NonSupport; + } + auto p = target.release(); if (pImpl->composite(this, p, method)) return Result::Success; delete(p); diff --git a/src/renderer/tvgPicture.h b/src/renderer/tvgPicture.h index 1b03843b..b81b2e31 100644 --- a/src/renderer/tvgPicture.h +++ b/src/renderer/tvgPicture.h @@ -89,7 +89,7 @@ struct Picture::Impl delete(paint); } - RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper) { auto flag = static_cast(pFlag | load()); @@ -109,7 +109,7 @@ struct Picture::Impl resizing = false; } needComp = needComposition(opacity) ? true : false; - rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, clipper); + rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, false); } return rd; } diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index bd078dad..1537366e 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -285,7 +285,6 @@ public: virtual ~RenderMethod() {} virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0; - virtual RenderData prepare(const Array& scene, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; virtual bool preRender() = 0; virtual bool renderShape(RenderData data) = 0; diff --git a/src/renderer/tvgScene.h b/src/renderer/tvgScene.h index 7965bd5b..c9a6c8e1 100644 --- a/src/renderer/tvgScene.h +++ b/src/renderer/tvgScene.h @@ -101,7 +101,7 @@ struct Scene::Impl return true; } - RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper) + RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) { if ((needComp = needComposition(opacity))) { /* Overriding opacity value. If this scene is half-translucent, @@ -109,20 +109,10 @@ struct Scene::Impl this->opacity = opacity; opacity = 255; } - - if (clipper) { - Array rds(paints.size()); - for (auto paint : paints) { - rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true)); - } - rd = renderer->prepare(rds, rd, transform, clips, opacity, flag); - return rd; - } else { - for (auto paint : paints) { - paint->pImpl->update(renderer, transform, clips, opacity, flag, false); - } - return nullptr; + for (auto paint : paints) { + paint->pImpl->update(renderer, transform, clips, opacity, flag, false); } + return nullptr; } bool render(RenderMethod* renderer) diff --git a/src/renderer/tvgText.h b/src/renderer/tvgText.h index 6194f0eb..8c554db4 100644 --- a/src/renderer/tvgText.h +++ b/src/renderer/tvgText.h @@ -120,7 +120,7 @@ struct Text::Impl return false; } - RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + RenderData update(RenderMethod* renderer, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper) { if (!load()) return nullptr; @@ -142,7 +142,7 @@ struct Text::Impl P(static_cast(fill))->fr *= scale; } } - return PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper); + return PP(paint)->update(renderer, transform, clips, opacity, pFlag, false); } bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking) diff --git a/src/renderer/wg_engine/tvgWgRenderer.cpp b/src/renderer/wg_engine/tvgWgRenderer.cpp index 70b049f1..9ca23b88 100755 --- a/src/renderer/wg_engine/tvgWgRenderer.cpp +++ b/src/renderer/wg_engine/tvgWgRenderer.cpp @@ -111,12 +111,6 @@ RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const } -RenderData WgRenderer::prepare(TVG_UNUSED const Array& scene, TVG_UNUSED RenderData data, TVG_UNUSED const Matrix& transform, TVG_UNUSED Array& clips, TVG_UNUSED uint8_t opacity, TVG_UNUSED RenderUpdateFlag flags) -{ - return nullptr; -} - - RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) { // get or create render data shape diff --git a/src/renderer/wg_engine/tvgWgRenderer.h b/src/renderer/wg_engine/tvgWgRenderer.h index 9d71805f..c5e8f67e 100755 --- a/src/renderer/wg_engine/tvgWgRenderer.h +++ b/src/renderer/wg_engine/tvgWgRenderer.h @@ -29,7 +29,6 @@ class WgRenderer : public RenderMethod { public: RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; - RenderData prepare(const Array& scene, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override;