mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
renderer: introduced paint reference counting
The reference count of the Paint object allows easy & safe shared ownership and control over its lifetime among users and the engine. New APIs: - uint8_t Paint::ref() - uint8_t Paint::unref(bool free = true) - uint8_t Paint::refCnt() const issue: https://github.com/thorvg/thorvg/issues/1372 https://github.com/thorvg/thorvg/issues/2598
This commit is contained in:
parent
511495f6d8
commit
22d94ea629
12 changed files with 113 additions and 61 deletions
47
inc/thorvg.h
47
inc/thorvg.h
|
@ -447,6 +447,53 @@ public:
|
|||
*/
|
||||
MaskMethod mask(const Paint** target) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Increment the reference count for the Paint instance.
|
||||
*
|
||||
* This method increases the reference count of the Paint object, allowing shared ownership and control over its lifetime.
|
||||
*
|
||||
* @return The updated reference count after the increment by 1.
|
||||
*
|
||||
* @warning Please ensure that each call to ref() is paired with a corresponding call to unref() to prevent a dangling instance.
|
||||
*
|
||||
* @see Paint::unref()
|
||||
* @see Paint::refCnt()
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
uint8_t ref() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Decrement the reference count for the Paint instance.
|
||||
*
|
||||
* This method decreases the reference count of the Paint object by 1.
|
||||
* If the reference count reaches zero and the @p free flag is set to true, the Paint instance is automatically deleted.
|
||||
*
|
||||
* @param[in] free Flag indicating whether to delete the Paint instance when the reference count reaches zero.
|
||||
*
|
||||
* @return The updated reference count after the decrement.
|
||||
*
|
||||
* @see Paint::ref()
|
||||
* @see Paint::refCnt()
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
uint8_t unref(bool free = true) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the current reference count of the Paint instance.
|
||||
*
|
||||
* This method provides the current reference count, allowing the user to check the shared ownership state of the Paint object.
|
||||
*
|
||||
* @return The current reference count of the Paint instance.
|
||||
*
|
||||
* @see Paint::ref()
|
||||
* @see Paint::unref()
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
uint8_t refCnt() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Returns the ID value of this class.
|
||||
*
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgInlist.h"
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgShape.h"
|
||||
#include "tvgLottieExpressions.h"
|
||||
#include "tvgLottieModifier.h"
|
||||
|
@ -64,13 +63,13 @@ struct RenderContext
|
|||
RenderContext(Shape* propagator)
|
||||
{
|
||||
P(propagator)->reset();
|
||||
PP(propagator)->ref();
|
||||
propagator->ref();
|
||||
this->propagator = propagator;
|
||||
}
|
||||
|
||||
~RenderContext()
|
||||
{
|
||||
PP(propagator)->unref();
|
||||
propagator->unref(false);
|
||||
free(transform);
|
||||
delete(roundness);
|
||||
delete(offsetPath);
|
||||
|
@ -79,7 +78,7 @@ struct RenderContext
|
|||
RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false)
|
||||
{
|
||||
if (mergeable) merging = rhs.merging;
|
||||
PP(propagator)->ref();
|
||||
propagator->ref();
|
||||
this->propagator = propagator;
|
||||
this->repeaters = rhs.repeaters;
|
||||
if (rhs.roundness) this->roundness = new LottieRoundnessModifier(rhs.roundness->r);
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
*/
|
||||
|
||||
#include "tvgMath.h"
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgFill.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgLottieModel.h"
|
||||
|
||||
|
@ -170,7 +168,7 @@ void LottieImage::prepare()
|
|||
TaskScheduler::async(true);
|
||||
|
||||
picture->size(data.width, data.height);
|
||||
PP(picture)->ref();
|
||||
picture->ref();
|
||||
|
||||
pooler.push(picture);
|
||||
}
|
||||
|
@ -472,14 +470,14 @@ void LottieLayer::prepare(RGB24* color)
|
|||
if (type == LottieLayer::Precomp) {
|
||||
auto clipper = Shape::gen();
|
||||
clipper->appendRect(0.0f, 0.0f, w, h);
|
||||
PP(clipper)->ref();
|
||||
clipper->ref();
|
||||
statical.pooler.push(clipper);
|
||||
//prepare solid fill in advance if it is a layer type.
|
||||
} else if (color && type == LottieLayer::Solid) {
|
||||
auto solidFill = Shape::gen();
|
||||
solidFill->appendRect(0, 0, static_cast<float>(w), static_cast<float>(h));
|
||||
solidFill->fill(color->rgb[0], color->rgb[1], color->rgb[2]);
|
||||
PP(solidFill)->ref();
|
||||
solidFill->ref();
|
||||
statical.pooler.push(solidFill);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ struct LottieRenderPooler
|
|||
~LottieRenderPooler()
|
||||
{
|
||||
for (auto p = pooler.begin(); p < pooler.end(); ++p) {
|
||||
if (PP(*p)->unref() == 0) delete(*p);
|
||||
(*p)->unref();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,12 +44,12 @@ struct LottieRenderPooler
|
|||
{
|
||||
//return available one.
|
||||
for (auto p = pooler.begin(); p < pooler.end(); ++p) {
|
||||
if (PP(*p)->refCnt == 1) return *p;
|
||||
if ((*p)->refCnt() == 1) return *p;
|
||||
}
|
||||
|
||||
//no empty, generate a new one.
|
||||
auto p = copy ? static_cast<T*>(pooler[0]->duplicate()) : T::gen();
|
||||
PP(p)->ref();
|
||||
p->ref();
|
||||
pooler.push(p);
|
||||
return p;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#define _TVG_ANIMATION_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgPicture.h"
|
||||
|
||||
struct Animation::Impl
|
||||
|
@ -34,14 +33,12 @@ struct Animation::Impl
|
|||
Impl()
|
||||
{
|
||||
picture = Picture::gen();
|
||||
PP(picture)->ref();
|
||||
picture->ref();
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (PP(picture)->unref() == 0) {
|
||||
delete(picture);
|
||||
}
|
||||
picture->unref();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ struct Canvas::Impl
|
|||
void clearPaints()
|
||||
{
|
||||
for (auto paint : paints) {
|
||||
if (P(paint)->unref() == 0) delete(paint);
|
||||
paint->unref();
|
||||
}
|
||||
paints.clear();
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ struct Canvas::Impl
|
|||
if (status == Status::Drawing) return Result::InsufficientCondition;
|
||||
|
||||
if (!paint) return Result::MemoryCorruption;
|
||||
PP(paint)->ref();
|
||||
paint->ref();
|
||||
paints.push_back(paint);
|
||||
|
||||
return update(paint, true);
|
||||
|
@ -72,16 +72,18 @@ struct Canvas::Impl
|
|||
|
||||
Result clear(bool paints, bool buffer)
|
||||
{
|
||||
auto ret = Result::Success;
|
||||
|
||||
if (status == Status::Drawing) return Result::InsufficientCondition;
|
||||
|
||||
//Clear render target
|
||||
if (buffer) {
|
||||
if (!renderer->clear()) return Result::InsufficientCondition;
|
||||
if (buffer && !renderer->clear()) {
|
||||
ret = Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
if (paints) clearPaints();
|
||||
|
||||
return Result::Success;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result update(Paint* paint, bool force)
|
||||
|
|
|
@ -362,7 +362,7 @@ void Paint::Impl::reset()
|
|||
}
|
||||
|
||||
if (maskData) {
|
||||
if (P(maskData->target)->unref() == 0) delete(maskData->target);
|
||||
maskData->target->unref();
|
||||
free(maskData);
|
||||
maskData = nullptr;
|
||||
}
|
||||
|
@ -503,3 +503,33 @@ Result Paint::blend(BlendMethod method) noexcept
|
|||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint8_t Paint::ref() noexcept
|
||||
{
|
||||
if (pImpl->refCnt == UINT8_MAX) TVGERR("RENDERER", "Reference Count Overflow!");
|
||||
else ++pImpl->refCnt;
|
||||
|
||||
return pImpl->refCnt;
|
||||
}
|
||||
|
||||
|
||||
uint8_t Paint::unref(bool free) noexcept
|
||||
{
|
||||
if (pImpl->refCnt > 0) --pImpl->refCnt;
|
||||
else TVGERR("RENDERER", "Corrupted Reference Count!");
|
||||
|
||||
if (free && pImpl->refCnt == 0) {
|
||||
//TODO: use the global dismiss function?
|
||||
delete(this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pImpl->refCnt;
|
||||
}
|
||||
|
||||
|
||||
uint8_t Paint::refCnt() const noexcept
|
||||
{
|
||||
return pImpl->refCnt;
|
||||
}
|
|
@ -86,25 +86,13 @@ namespace tvg
|
|||
~Impl()
|
||||
{
|
||||
if (maskData) {
|
||||
if (P(maskData->target)->unref() == 0) delete(maskData->target);
|
||||
maskData->target->unref();
|
||||
free(maskData);
|
||||
}
|
||||
if (clipper && P(clipper)->unref() == 0) delete(clipper);
|
||||
if (clipper) clipper->unref();
|
||||
if (renderer && (renderer->unref() == 0)) delete(renderer);
|
||||
}
|
||||
|
||||
uint8_t ref()
|
||||
{
|
||||
if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
|
||||
return ++refCnt;
|
||||
}
|
||||
|
||||
uint8_t unref()
|
||||
{
|
||||
if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!");
|
||||
return --refCnt;
|
||||
}
|
||||
|
||||
bool transform(const Matrix& m)
|
||||
{
|
||||
if (&tr.m != &m) tr.m = m;
|
||||
|
@ -124,16 +112,11 @@ namespace tvg
|
|||
|
||||
void clip(Paint* clp)
|
||||
{
|
||||
if (this->clipper) {
|
||||
P(this->clipper)->unref();
|
||||
if (this->clipper != clp && P(this->clipper)->refCnt == 0) {
|
||||
delete(this->clipper);
|
||||
}
|
||||
}
|
||||
this->clipper = clp;
|
||||
if (clipper) clipper->unref(clipper != clp);
|
||||
clipper = clp;
|
||||
if (!clp) return;
|
||||
|
||||
P(clipper)->ref();
|
||||
clipper->ref();
|
||||
}
|
||||
|
||||
bool mask(Paint* source, Paint* target, MaskMethod method)
|
||||
|
@ -142,10 +125,7 @@ namespace tvg
|
|||
if ((!target && method != MaskMethod::None) || (target && method == MaskMethod::None)) return false;
|
||||
|
||||
if (maskData) {
|
||||
P(maskData->target)->unref();
|
||||
if ((maskData->target != target) && P(maskData->target)->refCnt == 0) {
|
||||
delete(maskData->target);
|
||||
}
|
||||
maskData->target->unref(maskData->target != target);
|
||||
//Reset scenario
|
||||
if (!target && method == MaskMethod::None) {
|
||||
free(maskData);
|
||||
|
@ -156,7 +136,7 @@ namespace tvg
|
|||
if (!target && method == MaskMethod::None) return true;
|
||||
maskData = static_cast<Mask*>(malloc(sizeof(Mask)));
|
||||
}
|
||||
P(target)->ref();
|
||||
target->ref();
|
||||
maskData->target = target;
|
||||
maskData->source = source;
|
||||
maskData->method = method;
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "tvgCommon.h"
|
||||
#include "tvgStr.h"
|
||||
#include "tvgSaveModule.h"
|
||||
#include "tvgPaint.h"
|
||||
|
||||
#ifdef THORVG_GIF_SAVER_SUPPORT
|
||||
#include "tvgGifSaver.h"
|
||||
|
@ -107,7 +106,7 @@ Result Saver::save(Paint* paint, const char* filename, uint32_t quality) noexcep
|
|||
|
||||
//Already on saving another resource.
|
||||
if (pImpl->saveModule) {
|
||||
if (P(paint)->refCnt == 0) delete(paint);
|
||||
if (paint->refCnt() == 0) delete(paint);
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
@ -116,12 +115,12 @@ Result Saver::save(Paint* paint, const char* filename, uint32_t quality) noexcep
|
|||
pImpl->saveModule = saveModule;
|
||||
return Result::Success;
|
||||
} else {
|
||||
if (P(paint)->refCnt == 0) delete(paint);
|
||||
if (paint->refCnt() == 0) delete(paint);
|
||||
delete(saveModule);
|
||||
return Result::Unknown;
|
||||
}
|
||||
}
|
||||
if (P(paint)->refCnt == 0) delete(paint);
|
||||
if (paint->refCnt() == 0) delete(paint);
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
@ -140,7 +139,7 @@ Result Saver::save(Animation* animation, const char* filename, uint32_t quality,
|
|||
if (!animation) return Result::MemoryCorruption;
|
||||
|
||||
//animation holds the picture, it must be 1 at the bottom.
|
||||
auto remove = PP(animation->picture())->refCnt <= 1 ? true : false;
|
||||
auto remove = animation->picture()->refCnt() <= 1 ? true : false;
|
||||
|
||||
if (tvg::zero(animation->totalFrame())) {
|
||||
if (remove) delete(animation);
|
||||
|
|
|
@ -70,7 +70,7 @@ Type Scene::type() const noexcept
|
|||
Result Scene::push(Paint* paint) noexcept
|
||||
{
|
||||
if (!paint) return Result::MemoryCorruption;
|
||||
PP(paint)->ref();
|
||||
paint->ref();
|
||||
pImpl->paints.push_back(paint);
|
||||
|
||||
return Result::Success;
|
||||
|
|
|
@ -74,7 +74,7 @@ struct Scene::Impl
|
|||
resetEffects();
|
||||
|
||||
for (auto paint : paints) {
|
||||
if (P(paint)->unref() == 0) delete(paint);
|
||||
paint->unref();
|
||||
}
|
||||
|
||||
if (auto renderer = PP(scene)->renderer) {
|
||||
|
@ -230,7 +230,7 @@ struct Scene::Impl
|
|||
|
||||
for (auto paint : paints) {
|
||||
auto cdup = paint->duplicate();
|
||||
P(cdup)->ref();
|
||||
cdup->ref();
|
||||
dup->paints.push_back(cdup);
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,7 @@ struct Scene::Impl
|
|||
void clear(bool free)
|
||||
{
|
||||
for (auto paint : paints) {
|
||||
if (P(paint)->unref() == 0 && free) delete(paint);
|
||||
paint->unref(free);
|
||||
}
|
||||
paints.clear();
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ bool GifSaver::close()
|
|||
bg = nullptr;
|
||||
|
||||
//animation holds the picture, it must be 1 at the bottom.
|
||||
if (animation && PP(animation->picture())->refCnt <= 1) delete(animation);
|
||||
if (animation && animation->picture()->refCnt() <= 1) delete(animation);
|
||||
animation = nullptr;
|
||||
|
||||
free(path);
|
||||
|
|
Loading…
Add table
Reference in a new issue