mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
common: improved instance memory handling
- prevent dangling instances in failure scenarios by utilizing a reference counter. - update the test suite to treat null pointer arguments as invalid argument failures.
This commit is contained in:
parent
df8fc1949c
commit
0b4f2a49fe
17 changed files with 60 additions and 35 deletions
|
@ -2116,7 +2116,7 @@ public:
|
||||||
*
|
*
|
||||||
* @note Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
Result set(const Picture* picture, std::function<bool(const Paint* paint, void* data)> func, void* data) noexcept;
|
Result set(Picture* picture, std::function<bool(const Paint* paint, void* data)> func, void* data) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate a unique ID (hash key) from a given name.
|
* @brief Generate a unique ID (hash key) from a given name.
|
||||||
|
|
|
@ -50,20 +50,28 @@ static bool accessChildren(Iterator* it, function<bool(const Paint* paint, void*
|
||||||
/* External Class Implementation */
|
/* External Class Implementation */
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
Result Accessor::set(const Picture* picture, function<bool(const Paint* paint, void* data)> func, void* data) noexcept
|
Result Accessor::set(Picture* picture, function<bool(const Paint* paint, void* data)> func, void* data) noexcept
|
||||||
{
|
{
|
||||||
if (!picture || !func) return Result::InvalidArguments;
|
if (!picture || !func) return Result::InvalidArguments;
|
||||||
|
|
||||||
//Use the Preorder Tree-Search
|
//Use the Preorder Tree-Search
|
||||||
|
|
||||||
|
picture->ref();
|
||||||
|
|
||||||
//Root
|
//Root
|
||||||
if (!func(picture, data)) return Result::Success;
|
if (!func(picture, data)) {
|
||||||
|
picture->unref();
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
//Children
|
//Children
|
||||||
if (auto it = IteratorAccessor::iterator(picture)) {
|
if (auto it = IteratorAccessor::iterator(picture)) {
|
||||||
accessChildren(it, func, data);
|
accessChildren(it, func, data);
|
||||||
delete(it);
|
delete(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
picture->unref(false);
|
||||||
|
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,8 @@ Result Canvas::draw() noexcept
|
||||||
Result Canvas::update(Paint* paint) noexcept
|
Result Canvas::update(Paint* paint) noexcept
|
||||||
{
|
{
|
||||||
TVGLOG("RENDERER", "Update S. ------------------------------ Canvas(%p)", this);
|
TVGLOG("RENDERER", "Update S. ------------------------------ Canvas(%p)", this);
|
||||||
|
|
||||||
|
if (pImpl->paints.empty() || pImpl->status == Status::Drawing) return Result::InsufficientCondition;
|
||||||
auto ret = pImpl->update(paint, false);
|
auto ret = pImpl->update(paint, false);
|
||||||
TVGLOG("RENDERER", "Update E. ------------------------------ Canvas(%p)", this);
|
TVGLOG("RENDERER", "Update E. ------------------------------ Canvas(%p)", this);
|
||||||
|
|
||||||
|
|
|
@ -60,10 +60,14 @@ struct Canvas::Impl
|
||||||
|
|
||||||
Result push(Paint* paint)
|
Result push(Paint* paint)
|
||||||
{
|
{
|
||||||
//You cannot push paints during rendering.
|
if (!paint) return Result::InvalidArguments;
|
||||||
if (status == Status::Drawing) return Result::InsufficientCondition;
|
|
||||||
|
//You cannot push paints during rendering.
|
||||||
|
if (status == Status::Drawing) {
|
||||||
|
TVG_DELETE(paint);
|
||||||
|
return Result::InsufficientCondition;
|
||||||
|
}
|
||||||
|
|
||||||
if (!paint) return Result::MemoryCorruption;
|
|
||||||
paint->ref();
|
paint->ref();
|
||||||
paints.push_back(paint);
|
paints.push_back(paint);
|
||||||
|
|
||||||
|
@ -88,8 +92,6 @@ struct Canvas::Impl
|
||||||
|
|
||||||
Result update(Paint* paint, bool force)
|
Result update(Paint* paint, bool force)
|
||||||
{
|
{
|
||||||
if (paints.empty() || status == Status::Drawing) return Result::InsufficientCondition;
|
|
||||||
|
|
||||||
Array<RenderData> clips;
|
Array<RenderData> clips;
|
||||||
auto flag = RenderUpdateFlag::None;
|
auto flag = RenderUpdateFlag::None;
|
||||||
if (status == Status::Damaged || force) flag = RenderUpdateFlag::All;
|
if (status == Status::Damaged || force) flag = RenderUpdateFlag::All;
|
||||||
|
@ -109,8 +111,11 @@ struct Canvas::Impl
|
||||||
|
|
||||||
Result draw()
|
Result draw()
|
||||||
{
|
{
|
||||||
|
if (status == Status::Drawing || paints.empty()) return Result::InsufficientCondition;
|
||||||
|
|
||||||
if (status == Status::Damaged) update(nullptr, false);
|
if (status == Status::Damaged) update(nullptr, false);
|
||||||
if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition;
|
|
||||||
|
if (!renderer->preRender()) return Result::InsufficientCondition;
|
||||||
|
|
||||||
bool rendered = false;
|
bool rendered = false;
|
||||||
for (auto paint : paints) {
|
for (auto paint : paints) {
|
||||||
|
|
|
@ -80,6 +80,9 @@ uint16_t THORVG_VERSION_NUMBER();
|
||||||
#define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl.
|
#define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl.
|
||||||
|
|
||||||
|
|
||||||
|
#define TVG_DELETE(PAINT) \
|
||||||
|
if (PAINT->refCnt() == 0) delete(PAINT)
|
||||||
|
|
||||||
//for debugging
|
//for debugging
|
||||||
#if 0
|
#if 0
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
|
@ -163,8 +163,8 @@ Paint* Paint::Impl::duplicate(Paint* ret)
|
||||||
|
|
||||||
ret->pImpl->opacity = opacity;
|
ret->pImpl->opacity = opacity;
|
||||||
|
|
||||||
if (maskData) ret->pImpl->mask(ret, maskData->target->duplicate(), maskData->method);
|
if (maskData) ret->mask(maskData->target->duplicate(), maskData->method);
|
||||||
if (clipper) ret->pImpl->clip(clipper->duplicate());
|
if (clipper) ret->clip(clipper->duplicate());
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -357,7 +357,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
|
||||||
void Paint::Impl::reset()
|
void Paint::Impl::reset()
|
||||||
{
|
{
|
||||||
if (clipper) {
|
if (clipper) {
|
||||||
delete(clipper);
|
clipper->unref();
|
||||||
clipper = nullptr;
|
clipper = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,6 +446,7 @@ Result Paint::clip(Paint* clipper) noexcept
|
||||||
{
|
{
|
||||||
if (clipper && clipper->type() != Type::Shape) {
|
if (clipper && clipper->type() != Type::Shape) {
|
||||||
TVGERR("RENDERER", "Clipping only supports the Shape!");
|
TVGERR("RENDERER", "Clipping only supports the Shape!");
|
||||||
|
TVG_DELETE(clipper);
|
||||||
return Result::NonSupport;
|
return Result::NonSupport;
|
||||||
}
|
}
|
||||||
pImpl->clip(clipper);
|
pImpl->clip(clipper);
|
||||||
|
@ -455,9 +456,8 @@ Result Paint::clip(Paint* clipper) noexcept
|
||||||
|
|
||||||
Result Paint::mask(Paint* target, MaskMethod method) noexcept
|
Result Paint::mask(Paint* target, MaskMethod method) noexcept
|
||||||
{
|
{
|
||||||
if (pImpl->mask(this, target, method)) return Result::Success;
|
if (pImpl->mask(target, method)) return Result::Success;
|
||||||
|
if (target) TVG_DELETE(target);
|
||||||
delete(target);
|
|
||||||
return Result::InvalidArguments;
|
return Result::InvalidArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ namespace tvg
|
||||||
clipper->ref();
|
clipper->ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mask(Paint* source, Paint* target, MaskMethod method)
|
bool mask(Paint* target, MaskMethod method)
|
||||||
{
|
{
|
||||||
//Invalid case
|
//Invalid case
|
||||||
if ((!target && method != MaskMethod::None) || (target && method == MaskMethod::None)) return false;
|
if ((!target && method != MaskMethod::None) || (target && method == MaskMethod::None)) return false;
|
||||||
|
@ -138,7 +138,7 @@ namespace tvg
|
||||||
}
|
}
|
||||||
target->ref();
|
target->ref();
|
||||||
maskData->target = target;
|
maskData->target = target;
|
||||||
maskData->source = source;
|
maskData->source = paint;
|
||||||
maskData->method = method;
|
maskData->method = method;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ struct Saver::Impl
|
||||||
~Impl()
|
~Impl()
|
||||||
{
|
{
|
||||||
delete(saveModule);
|
delete(saveModule);
|
||||||
delete(bg);
|
if (bg) bg->unref();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,11 +102,11 @@ Saver::~Saver()
|
||||||
|
|
||||||
Result Saver::save(Paint* paint, const char* filename, uint32_t quality) noexcept
|
Result Saver::save(Paint* paint, const char* filename, uint32_t quality) noexcept
|
||||||
{
|
{
|
||||||
if (!paint) return Result::MemoryCorruption;
|
if (!paint) return Result::InvalidArguments;
|
||||||
|
|
||||||
//Already on saving another resource.
|
//Already on saving another resource.
|
||||||
if (pImpl->saveModule) {
|
if (pImpl->saveModule) {
|
||||||
if (paint->refCnt() == 0) delete(paint);
|
TVG_DELETE(paint);
|
||||||
return Result::InsufficientCondition;
|
return Result::InsufficientCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,19 +115,22 @@ Result Saver::save(Paint* paint, const char* filename, uint32_t quality) noexcep
|
||||||
pImpl->saveModule = saveModule;
|
pImpl->saveModule = saveModule;
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
} else {
|
} else {
|
||||||
if (paint->refCnt() == 0) delete(paint);
|
TVG_DELETE(paint);
|
||||||
delete(saveModule);
|
delete(saveModule);
|
||||||
return Result::Unknown;
|
return Result::Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (paint->refCnt() == 0) delete(paint);
|
TVG_DELETE(paint);
|
||||||
return Result::NonSupport;
|
return Result::NonSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Result Saver::background(Paint* paint) noexcept
|
Result Saver::background(Paint* paint) noexcept
|
||||||
{
|
{
|
||||||
delete(pImpl->bg);
|
if (!paint) return Result::InvalidArguments;
|
||||||
|
|
||||||
|
if (pImpl->bg) TVG_DELETE(pImpl->bg);
|
||||||
|
paint->ref();
|
||||||
pImpl->bg = paint;
|
pImpl->bg = paint;
|
||||||
|
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
|
@ -136,7 +139,7 @@ Result Saver::background(Paint* paint) noexcept
|
||||||
|
|
||||||
Result Saver::save(Animation* animation, const char* filename, uint32_t quality, uint32_t fps) noexcept
|
Result Saver::save(Animation* animation, const char* filename, uint32_t quality, uint32_t fps) noexcept
|
||||||
{
|
{
|
||||||
if (!animation) return Result::MemoryCorruption;
|
if (!animation) return Result::InvalidArguments;
|
||||||
|
|
||||||
//animation holds the picture, it must be 1 at the bottom.
|
//animation holds the picture, it must be 1 at the bottom.
|
||||||
auto remove = animation->picture()->refCnt() <= 1 ? true : false;
|
auto remove = animation->picture()->refCnt() <= 1 ? true : false;
|
||||||
|
|
|
@ -69,7 +69,7 @@ Type Scene::type() const noexcept
|
||||||
|
|
||||||
Result Scene::push(Paint* paint) noexcept
|
Result Scene::push(Paint* paint) noexcept
|
||||||
{
|
{
|
||||||
if (!paint) return Result::MemoryCorruption;
|
if (!paint) return Result::InvalidArguments;
|
||||||
paint->ref();
|
paint->ref();
|
||||||
pImpl->paints.push_back(paint);
|
pImpl->paints.push_back(paint);
|
||||||
|
|
||||||
|
|
|
@ -212,7 +212,7 @@ Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
||||||
|
|
||||||
Result Shape::fill(Fill* f) noexcept
|
Result Shape::fill(Fill* f) noexcept
|
||||||
{
|
{
|
||||||
if (!f) return Result::MemoryCorruption;
|
if (!f) return Result::InvalidArguments;
|
||||||
|
|
||||||
if (pImpl->rs.fill && pImpl->rs.fill != f) delete(pImpl->rs.fill);
|
if (pImpl->rs.fill && pImpl->rs.fill != f) delete(pImpl->rs.fill);
|
||||||
pImpl->rs.fill = f;
|
pImpl->rs.fill = f;
|
||||||
|
|
|
@ -280,7 +280,7 @@ struct Shape::Impl
|
||||||
|
|
||||||
Result strokeFill(Fill* f)
|
Result strokeFill(Fill* f)
|
||||||
{
|
{
|
||||||
if (!f) return Result::MemoryCorruption;
|
if (!f) return Result::InvalidArguments;
|
||||||
|
|
||||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||||
if (rs.stroke->fill && rs.stroke->fill != f) delete(rs.stroke->fill);
|
if (rs.stroke->fill && rs.stroke->fill != f) delete(rs.stroke->fill);
|
||||||
|
|
|
@ -96,7 +96,7 @@ bool GifSaver::close()
|
||||||
{
|
{
|
||||||
this->done();
|
this->done();
|
||||||
|
|
||||||
delete(bg);
|
if (bg) bg->unref();
|
||||||
bg = nullptr;
|
bg = nullptr;
|
||||||
|
|
||||||
//animation holds the picture, it must be 1 at the bottom.
|
//animation holds the picture, it must be 1 at the bottom.
|
||||||
|
@ -143,7 +143,10 @@ bool GifSaver::save(Animation* animation, Paint* bg, const char* filename, TVG_U
|
||||||
|
|
||||||
this->animation = animation;
|
this->animation = animation;
|
||||||
|
|
||||||
if (bg) this->bg = bg->duplicate();
|
if (bg) {
|
||||||
|
bg->ref();
|
||||||
|
this->bg = bg;
|
||||||
|
}
|
||||||
this->fps = static_cast<float>(fps);
|
this->fps = static_cast<float>(fps);
|
||||||
|
|
||||||
TaskScheduler::request(this);
|
TaskScheduler::request(this);
|
||||||
|
|
|
@ -71,7 +71,7 @@ TEST_CASE("Linear Gradient in shape", "[capiLinearGradient]")
|
||||||
REQUIRE(tvg_shape_get_gradient(shape, &gradient_ret) == TVG_RESULT_SUCCESS);
|
REQUIRE(tvg_shape_get_gradient(shape, &gradient_ret) == TVG_RESULT_SUCCESS);
|
||||||
REQUIRE(gradient_ret);
|
REQUIRE(gradient_ret);
|
||||||
|
|
||||||
REQUIRE(tvg_shape_set_gradient(shape, NULL) == TVG_RESULT_MEMORY_CORRUPTION);
|
REQUIRE(tvg_shape_set_gradient(shape, NULL) == TVG_RESULT_INVALID_ARGUMENT);
|
||||||
REQUIRE(tvg_paint_del(shape) == TVG_RESULT_SUCCESS);
|
REQUIRE(tvg_paint_del(shape) == TVG_RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ TEST_CASE("Set gradient in shape", "[capiRadialGradient]")
|
||||||
REQUIRE(tvg_shape_get_gradient(shape, &gradient_ret) == TVG_RESULT_SUCCESS);
|
REQUIRE(tvg_shape_get_gradient(shape, &gradient_ret) == TVG_RESULT_SUCCESS);
|
||||||
REQUIRE(gradient_ret);
|
REQUIRE(gradient_ret);
|
||||||
|
|
||||||
REQUIRE(tvg_shape_set_gradient(shape, NULL) == TVG_RESULT_MEMORY_CORRUPTION);
|
REQUIRE(tvg_shape_set_gradient(shape, NULL) == TVG_RESULT_INVALID_ARGUMENT);
|
||||||
REQUIRE(tvg_paint_del(shape) == TVG_RESULT_SUCCESS);
|
REQUIRE(tvg_paint_del(shape) == TVG_RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@ TEST_CASE("Canvas draw", "[capiSwCanvas]")
|
||||||
REQUIRE(tvg_swcanvas_set_target(canvas, buffer, 200, 200, 200, TVG_COLORSPACE_ARGB8888) == TVG_RESULT_SUCCESS);
|
REQUIRE(tvg_swcanvas_set_target(canvas, buffer, 200, 200, 200, TVG_COLORSPACE_ARGB8888) == TVG_RESULT_SUCCESS);
|
||||||
|
|
||||||
REQUIRE(tvg_canvas_draw(canvas) == TVG_RESULT_INSUFFICIENT_CONDITION);
|
REQUIRE(tvg_canvas_draw(canvas) == TVG_RESULT_INSUFFICIENT_CONDITION);
|
||||||
|
|
||||||
REQUIRE(tvg_canvas_sync(canvas) == TVG_RESULT_INSUFFICIENT_CONDITION);
|
REQUIRE(tvg_canvas_sync(canvas) == TVG_RESULT_INSUFFICIENT_CONDITION);
|
||||||
|
|
||||||
Tvg_Paint* paint = tvg_shape_new();
|
Tvg_Paint* paint = tvg_shape_new();
|
||||||
|
|
|
@ -153,11 +153,11 @@ TEST_CASE("Set gradient text fill", "[capiText]")
|
||||||
|
|
||||||
REQUIRE(tvg_font_load(TEST_DIR"/Arial.ttf") == TVG_RESULT_SUCCESS);
|
REQUIRE(tvg_font_load(TEST_DIR"/Arial.ttf") == TVG_RESULT_SUCCESS);
|
||||||
|
|
||||||
REQUIRE(tvg_text_set_gradient(text, NULL) == TVG_RESULT_MEMORY_CORRUPTION);
|
REQUIRE(tvg_text_set_gradient(text, NULL) == TVG_RESULT_INVALID_ARGUMENT);
|
||||||
|
|
||||||
REQUIRE(tvg_text_set_font(text, "Arial", 10.0f, "") == TVG_RESULT_SUCCESS);
|
REQUIRE(tvg_text_set_font(text, "Arial", 10.0f, "") == TVG_RESULT_SUCCESS);
|
||||||
|
|
||||||
REQUIRE(tvg_text_set_gradient(text, NULL) == TVG_RESULT_MEMORY_CORRUPTION);
|
REQUIRE(tvg_text_set_gradient(text, NULL) == TVG_RESULT_INVALID_ARGUMENT);
|
||||||
REQUIRE(tvg_text_set_gradient(NULL, NULL) == TVG_RESULT_INVALID_ARGUMENT);
|
REQUIRE(tvg_text_set_gradient(NULL, NULL) == TVG_RESULT_INVALID_ARGUMENT);
|
||||||
REQUIRE(tvg_text_set_gradient(text, gradientLin) == TVG_RESULT_SUCCESS);
|
REQUIRE(tvg_text_set_gradient(text, gradientLin) == TVG_RESULT_SUCCESS);
|
||||||
REQUIRE(tvg_text_set_gradient(text, gradientRad) == TVG_RESULT_SUCCESS);
|
REQUIRE(tvg_text_set_gradient(text, gradientRad) == TVG_RESULT_SUCCESS);
|
||||||
|
|
|
@ -53,10 +53,10 @@ TEST_CASE("Pushing Paints Into Scene", "[tvgScene]")
|
||||||
REQUIRE(scene->push(paints[2]) == Result::Success);
|
REQUIRE(scene->push(paints[2]) == Result::Success);
|
||||||
|
|
||||||
//Pushing Null Pointer
|
//Pushing Null Pointer
|
||||||
REQUIRE(scene->push(nullptr) == Result::MemoryCorruption);
|
REQUIRE(scene->push(nullptr) == Result::InvalidArguments);
|
||||||
|
|
||||||
//Pushing Invalid Paint
|
//Pushing Invalid Paint
|
||||||
REQUIRE(scene->push(nullptr) == Result::MemoryCorruption);
|
REQUIRE(scene->push(nullptr) == Result::InvalidArguments);
|
||||||
|
|
||||||
//Check list of paints
|
//Check list of paints
|
||||||
auto list = scene->paints();
|
auto list = scene->paints();
|
||||||
|
|
Loading…
Add table
Reference in a new issue