mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 21:53:41 +00:00
api: Introduced Paint::clip() API
Separate clip function from the Composite() clipping and composition can be used together. This helps avoid the introduction of nested scenes when composition and clipping overlap. Deprecated: - enum class CompositeMethod::ClipPath - enum Tvg_Composite_Method::TVG_COMPOSITE_METHOD_CLIP_PATH Experimental API: - Result Paint::clip(std::unique_ptr<Paint> clipper) - Tvg_Result tvg_paint_set_clip(Tvg_Paint* paint, Tvg_Paint* clipper) Issue: https://github.com/thorvg/thorvg/issues/1496
This commit is contained in:
parent
82d4725c3e
commit
7a0c12ed8b
10 changed files with 127 additions and 85 deletions
18
inc/thorvg.h
18
inc/thorvg.h
|
@ -157,7 +157,7 @@ enum class FillRule
|
|||
enum class CompositeMethod
|
||||
{
|
||||
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
|
||||
|
@ -338,7 +338,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;
|
||||
|
||||
|
@ -350,6 +349,20 @@ public:
|
|||
*/
|
||||
Result composite(std::unique_ptr<Paint> 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<Paint> clipper) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Sets the blending method for the paint object.
|
||||
*
|
||||
|
@ -1053,7 +1066,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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -993,6 +993,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.
|
||||
*
|
||||
|
|
|
@ -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*>(paint)->clip(unique_ptr<Paint>((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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite)
|
||||
{
|
||||
unique_ptr<Scene> finalScene;
|
||||
auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1, isMaskWhite);
|
||||
|
||||
// mUseTransform = mUseTransform * mTranslate
|
||||
|
@ -751,9 +749,7 @@ static unique_ptr<Scene> _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<Scene> _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();
|
||||
|
|
|
@ -546,8 +546,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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<SwSpan*>(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!");
|
||||
}
|
|
@ -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, Array<R
|
|||
RenderData trd = nullptr; //composite target render data
|
||||
RenderRegion viewport;
|
||||
Result compFastTrack = Result::InsufficientCondition;
|
||||
bool childClipper = false;
|
||||
|
||||
if (compData) {
|
||||
auto target = compData->target;
|
||||
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<Shape*>(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<Shape*>(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<RenderUpdateFlag>(pFlag | renderFlag);
|
||||
renderFlag = RenderUpdateFlag::None;
|
||||
opacity = MULTIPLY(opacity, this->opacity);
|
||||
|
@ -294,9 +297,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
|
|||
tr.cm = pm * tr.m;
|
||||
PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
|
||||
|
||||
/* 3. Composition Post Processing */
|
||||
/* 4. Composition Post Processing */
|
||||
if (compFastTrack == Result::Success) renderer->viewport(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);
|
||||
|
@ -437,15 +445,27 @@ Paint* Paint::duplicate() const noexcept
|
|||
}
|
||||
|
||||
|
||||
Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
|
||||
Result Paint::clip(std::unique_ptr<Paint> 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<Paint> 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;
|
||||
}
|
||||
|
@ -457,6 +477,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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue