diff --git a/inc/thorvg.h b/inc/thorvg.h index bd7154bc..0d316608 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. Note that ClipPath only supports the Shape type. + 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. @deprecated Use Paint::clip() instead. 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 @@ -339,7 +339,6 @@ public: * @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * * @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible. - * @note ClipPath won't use the opacity value. (see: enum class CompositeMethod::ClipPath) */ Result opacity(uint8_t o) noexcept; @@ -351,6 +350,20 @@ public: */ Result composite(std::unique_ptr target, CompositeMethod method) noexcept; + /** + * @brief Clip the drawing region of the paint object. + * + * This function restricts the drawing area of the paint object to the specified shape's paths. + * + * @param[in] clipper The shape object as the clipper. + * + * @retval Result::NonSupport If the @p clipper type is not Shape. + * + * @note @p clipper only supports the Shape type. + * @note Experimental API + */ + Result clip(std::unique_ptr clipper) noexcept; + /** * @brief Sets the blending method for the paint object. * @@ -1050,7 +1063,6 @@ public: * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. - * @note ClipPath won't use the fill values. (see: enum class CompositeMethod::ClipPath) */ Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index 962673ab..ab2bd01a 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. Note that ClipPath only supports the Shape type. + 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. @deprecated Use Paint::clip() instead. 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 @@ -994,6 +994,23 @@ TVG_API Tvg_Result tvg_paint_set_composite_method(Tvg_Paint* paint, Tvg_Paint* t TVG_API Tvg_Result tvg_paint_get_composite_method(const Tvg_Paint* paint, const Tvg_Paint** target, Tvg_Composite_Method* method); +/*! +* \brief Clip the drawing region of the paint object. +* +* This function restricts the drawing area of the paint object to the specified shape's paths. +* +* \param[in] paint The target object of the clipping. +* \param[in] clipper The shape object as the clipper. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. +* \retval TVG_RESULT_NOT_SUPPORTED If the @p clipper type is not Shape. +* +* \note Experimental API +*/ +TVG_API Tvg_Result tvg_paint_set_clip(Tvg_Paint* paint, Tvg_Paint* clipper); + + /** * \brief Gets the unique value of the paint instance indicating the instance type. * diff --git a/src/bindings/capi/tvgCapi.cpp b/src/bindings/capi/tvgCapi.cpp index 619a9993..ed348543 100644 --- a/src/bindings/capi/tvgCapi.cpp +++ b/src/bindings/capi/tvgCapi.cpp @@ -260,6 +260,13 @@ TVG_API Tvg_Result tvg_paint_get_type(const Tvg_Paint* paint, Tvg_Type* type) } +TVG_API Tvg_Result tvg_paint_set_clip(Tvg_Paint* paint, Tvg_Paint* clipper) +{ + if (!paint) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(paint)->clip(unique_ptr((Paint*)(clipper))); +} + + TVG_DEPRECATED TVG_API Tvg_Result tvg_paint_get_identifier(const Tvg_Paint* paint, Tvg_Identifier* identifier) { return tvg_paint_get_type(paint, (Tvg_Type*) identifier); diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 1c5bf795..1b09af6f 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -961,17 +961,10 @@ void LottieBuilder::updatePrecomp(LottieComposition* comp, LottieLayer* precomp, if (!child->matteSrc) updateLayer(comp, precomp->scene, child, frameNo); } - //TODO: remove the intermediate scene.... - if (precomp->scene->composite(nullptr) != CompositeMethod::None) { - auto cscene = Scene::gen().release(); - cscene->push(cast(precomp->scene)); - precomp->scene = cscene; - } - //clip the layer viewport auto clipper = precomp->statical.pooling(true); clipper->transform(precomp->cache.matrix); - precomp->scene->composite(cast(clipper), CompositeMethod::ClipPath); + precomp->scene->clip(cast(clipper)); } @@ -1396,5 +1389,5 @@ void LottieBuilder::build(LottieComposition* comp) //viewport clip auto clip = Shape::gen(); clip->appendRect(0, 0, comp->w, comp->h); - comp->root->scene->composite(std::move(clip), CompositeMethod::ClipPath); + comp->root->scene->clip(std::move(clip)); } diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index e95090cf..ee505564 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -272,8 +272,7 @@ static void _applyComposition(SvgLoaderData& loaderData, Paint* paint, const Svg if (valid) { Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath); comp->transform(finalTransform); - - paint->composite(std::move(comp), CompositeMethod::ClipPath); + paint->clip(std::move(comp)); } node->style->clipPath.applying = false; @@ -714,7 +713,6 @@ static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMee static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite) { - unique_ptr finalScene; auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1, isMaskWhite); // mUseTransform = mUseTransform * mTranslate @@ -751,9 +749,7 @@ static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNod mSceneTransform = mUseTransform * mSceneTransform; scene->transform(mSceneTransform); - if (node->node.use.symbol->node.symbol.overflowVisible) { - finalScene = std::move(scene); - } else { + if (!node->node.use.symbol->node.symbol.overflowVisible) { auto viewBoxClip = Shape::gen(); viewBoxClip->appendRect(0, 0, width, height, 0, 0); @@ -764,21 +760,13 @@ static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNod } viewBoxClip->transform(mClipTransform); - auto compositeLayer = Scene::gen(); - compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath); - compositeLayer->push(std::move(scene)); - - auto root = Scene::gen(); - root->push(std::move(compositeLayer)); - - finalScene = std::move(root); + scene->clip(std::move(viewBoxClip)); } } else { scene->transform(mUseTransform); - finalScene = std::move(scene); } - return finalScene; + return scene; } @@ -937,7 +925,7 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe viewBoxClip->appendRect(0, 0, w, h); auto compositeLayer = Scene::gen(); - compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath); + compositeLayer->clip(std::move(viewBoxClip)); compositeLayer->push(std::move(docNode)); auto root = Scene::gen(); diff --git a/src/renderer/sw_engine/tvgSwCommon.h b/src/renderer/sw_engine/tvgSwCommon.h index d04f92ac..b5fbc59e 100644 --- a/src/renderer/sw_engine/tvgSwCommon.h +++ b/src/renderer/sw_engine/tvgSwCommon.h @@ -542,8 +542,8 @@ SwRle* rleRender(const SwBBox* bbox); void rleFree(SwRle* rle); void rleReset(SwRle* rle); void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2); -void rleClipPath(SwRle* rle, const SwRle* clip); -void rleClipRect(SwRle* rle, const SwBBox* clip); +void rleClip(SwRle* rle, const SwRle* clip); +void rleClip(SwRle* rle, const SwBBox* clip); SwMpool* mpoolInit(uint32_t threads); bool mpoolTerm(SwMpool* mpool); diff --git a/src/renderer/sw_engine/tvgSwRenderer.cpp b/src/renderer/sw_engine/tvgSwRenderer.cpp index 5492503c..8b668acd 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -66,8 +66,6 @@ struct SwTask : Task virtual void dispose() = 0; virtual bool clip(SwRle* target) = 0; - virtual SwRle* rle() = 0; - virtual ~SwTask() {} }; @@ -102,21 +100,13 @@ struct SwShapeTask : SwTask bool clip(SwRle* target) override { - if (shape.fastTrack) rleClipRect(target, &bbox); - else if (shape.rle) rleClipPath(target, shape.rle); + if (shape.fastTrack) rleClip(target, &bbox); + else if (shape.rle) rleClip(target, shape.rle); else return false; return true; } - SwRle* 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 @@ -209,12 +199,6 @@ struct SwImageTask : SwTask return true; } - SwRle* rle() override - { - TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?"); - return nullptr; - } - void run(unsigned tid) override { auto clipRegion = bbox; diff --git a/src/renderer/sw_engine/tvgSwRle.cpp b/src/renderer/sw_engine/tvgSwRle.cpp index cb73e599..27b7840b 100644 --- a/src/renderer/sw_engine/tvgSwRle.cpp +++ b/src/renderer/sw_engine/tvgSwRle.cpp @@ -990,7 +990,7 @@ void rleFree(SwRle* rle) } -void rleClipPath(SwRle *rle, const SwRle *clip) +void rleClip(SwRle *rle, const SwRle *clip) { if (rle->size == 0 || clip->size == 0) return; auto spanCnt = rle->size > clip->size ? rle->size : clip->size; @@ -999,11 +999,11 @@ void rleClipPath(SwRle *rle, const SwRle *clip) _replaceClipSpan(rle, spans, spansEnd - spans); - TVGLOG("SW_ENGINE", "Using ClipPath!"); + TVGLOG("SW_ENGINE", "Using Path Clipping!"); } -void rleClipRect(SwRle *rle, const SwBBox* clip) +void rleClip(SwRle *rle, const SwBBox* clip) { if (rle->size == 0) return; auto spans = static_cast(malloc(sizeof(SwSpan) * (rle->size))); @@ -1011,5 +1011,5 @@ void rleClipRect(SwRle *rle, const SwBBox* clip) _replaceClipSpan(rle, spans, spansEnd - spans); - TVGLOG("SW_ENGINE", "Using ClipRect!"); + TVGLOG("SW_ENGINE", "Using Box Clipping!"); } \ No newline at end of file diff --git a/src/renderer/tvgPaint.cpp b/src/renderer/tvgPaint.cpp index 5a4d008b..317c2c70 100644 --- a/src/renderer/tvgPaint.cpp +++ b/src/renderer/tvgPaint.cpp @@ -164,6 +164,7 @@ Paint* Paint::Impl::duplicate(Paint* ret) ret->pImpl->opacity = opacity; if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method); + if (clipper) ret->pImpl->clip(clipper->duplicate()); return ret; } @@ -209,9 +210,7 @@ bool Paint::Impl::render(RenderMethod* renderer) Compositor* cmp = nullptr; - /* Note: only ClipPath is processed in update() step. - Create a composition image. */ - if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) { + if (compData && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) { RenderRegion region; PAINT_METHOD(region, bounds(renderer)); @@ -248,43 +247,47 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arraytarget; auto method = compData->method; P(target)->ctxFlag &= ~ContextFlag::FastTrack; //reset - /* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle, - we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */ - auto tryFastTrack = false; + /* If the transformation has no rotational factors and the Alpha(InvAlpha)Masking involves a simple rectangle, + we can optimize by using the viewport instead of the regular AlphaMasking sequence for improved performance. */ if (target->type() == Type::Shape) { - if (method == CompositeMethod::ClipPath) tryFastTrack = true; - else { - auto shape = static_cast(target); - uint8_t a; - shape->fillColor(nullptr, nullptr, nullptr, &a); - //no gradient fill & no compositions of the composition target. - if (!shape->fill() && !(PP(shape)->compData)) { - if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true; - else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true; - } - } - if (tryFastTrack) { - viewport = renderer->viewport(); - if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) { - P(target)->ctxFlag |= ContextFlag::FastTrack; + auto shape = static_cast(target); + uint8_t a; + shape->fillColor(nullptr, nullptr, nullptr, &a); + //no gradient fill & no compositions of the composition target. + if (!shape->fill() && !(PP(shape)->compData)) { + if ((method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) || (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0))) { + viewport = renderer->viewport(); + if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) { + P(target)->ctxFlag |= ContextFlag::FastTrack; + } } } } if (compFastTrack == Result::InsufficientCondition) { - childClipper = compData->method == CompositeMethod::ClipPath ? true : false; - trd = P(target)->update(renderer, pm, clips, 255, pFlag, childClipper); - if (childClipper) clips.push(trd); + trd = P(target)->update(renderer, pm, clips, 255, pFlag, false); } } - /* 2. Main Update */ + /* 2. Clipping */ + if (this->clipper) { + P(this->clipper)->ctxFlag &= ~ContextFlag::FastTrack; //reset + viewport = renderer->viewport(); + if ((compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) { + P(this->clipper)->ctxFlag |= ContextFlag::FastTrack; + } + if (compFastTrack == Result::InsufficientCondition) { + trd = P(this->clipper)->update(renderer, pm, clips, 255, pFlag, true); + clips.push(trd); + } + } + + /* 3. Main Update */ auto newFlag = static_cast(pFlag | renderFlag); renderFlag = RenderUpdateFlag::None; opacity = MULTIPLY(opacity, this->opacity); @@ -294,9 +297,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arrayviewport(viewport); - else if (childClipper) clips.pop(); + else if (this->clipper) clips.pop(); return rd; } @@ -351,6 +354,11 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme void Paint::Impl::reset() { + if (clipper) { + delete(clipper); + clipper = nullptr; + } + if (compData) { if (P(compData->target)->unref() == 0) delete(compData->target); free(compData); @@ -431,15 +439,27 @@ Paint* Paint::duplicate() const noexcept } -Result Paint::composite(std::unique_ptr target, CompositeMethod method) noexcept +Result Paint::clip(std::unique_ptr clipper) noexcept { - if (method == CompositeMethod::ClipPath && target && target->type() != Type::Shape) { - TVGERR("RENDERER", "ClipPath only allows the Shape!"); + auto p = clipper.release(); + + if (p && p->type() != Type::Shape) { + TVGERR("RENDERER", "Clipping only supports the Shape!"); return Result::NonSupport; } + pImpl->clip(p); + return Result::Success; +} + + +Result Paint::composite(std::unique_ptr target, CompositeMethod method) noexcept +{ + //TODO: remove. Keep this for the backward compatibility + if (method == CompositeMethod::ClipPath) return clip(std::move(target)); auto p = target.release(); if (pImpl->composite(this, p, method)) return Result::Success; + delete(p); return Result::InvalidArguments; } @@ -451,6 +471,11 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept if (target) *target = pImpl->compData->target; return pImpl->compData->method; } else { + //TODO: remove. Keep this for the backward compatibility + if (pImpl->clipper) { + if (target) *target = pImpl->clipper; + return CompositeMethod::ClipPath; + } if (target) *target = nullptr; return CompositeMethod::None; } diff --git a/src/renderer/tvgPaint.h b/src/renderer/tvgPaint.h index 6c3ec6f7..552d4689 100644 --- a/src/renderer/tvgPaint.h +++ b/src/renderer/tvgPaint.h @@ -49,6 +49,7 @@ namespace tvg { Paint* paint = nullptr; Composite* compData = nullptr; + Paint* clipper = nullptr; RenderMethod* renderer = nullptr; struct { Matrix m; //input matrix @@ -88,6 +89,7 @@ namespace tvg if (P(compData->target)->unref() == 0) delete(compData->target); free(compData); } + if (clipper && P(clipper)->unref() == 0) delete(clipper); if (renderer && (renderer->unref() == 0)) delete(renderer); } @@ -120,6 +122,20 @@ namespace tvg return tr.m; } + void clip(Paint* clipper) + { + if (this->clipper) { + P(this->clipper)->unref(); + if (this->clipper != clipper && P(this->clipper)->refCnt == 0) { + delete(this->clipper); + } + } + this->clipper = clipper; + if (!clipper) return; + + P(clipper)->ref(); + } + bool composite(Paint* source, Paint* target, CompositeMethod method) { //Invalid case