renderer: added parent scene query api

added 2 more APIs for user convenience to allow backtracking tree traversal.

API Additions:
- const Paint* Paint::scene() const
- const Tvg_Paint* tvg_paint_get_scene(const Tvg_Paint* paint)
This commit is contained in:
Hermet Park 2025-03-18 16:53:42 +09:00
parent 0f20a4a1a8
commit 563f8b2f78
11 changed files with 115 additions and 57 deletions

View file

@ -309,6 +309,21 @@ class TVG_API Paint
public:
virtual ~Paint();
/**
* @brief Retrieves the parent paint object.
*
* This function returns a pointer to the parent object if the current paint
* belongs to one. Otherwise, it returns @c nullptr.
*
* @return A pointer to the parent object if available, otherwise @c nullptr.
*
* @see Scene::push()
* @see Canvas::push()
*
* @since 1.0
*/
const Paint* parent() const noexcept;
/**
* @brief Sets the angle by which the object is rotated.
*
@ -381,6 +396,8 @@ public:
*
* @param[in] target The paint of the target object.
* @param[in] method The method used to mask the source object with the target.
*
* @retval Result::InsufficientCondition if the target has already belonged to another paint.
*/
Result mask(Paint* target, MaskMethod method) noexcept;
@ -392,6 +409,7 @@ public:
* @param[in] clipper The shape object as the clipper.
*
* @retval Result::NonSupport If the @p clipper type is not Shape.
* @retval Result::InsufficientCondition if the target has already belonged to another paint.
*
* @note @p clipper only supports the Shape type.
* @since 1.0

View file

@ -715,7 +715,6 @@ TVG_API Tvg_Result tvg_canvas_set_viewport(Tvg_Canvas* canvas, int32_t x, int32_
/** \} */ // end defgroup ThorVGCapi_Canvas
/**
* @defgroup ThorVGCapi_Paint Paint
* @brief A module for managing graphical elements. It enables duplication, transformation and composition.
@ -964,6 +963,8 @@ TVG_API Tvg_Result tvg_paint_get_obb(const Tvg_Paint* paint, Tvg_Point* pt4);
* @param[in] target The target object of the masking.
* @param[in] method The method used to mask the source object with the target.
*
* @retval TVG_RESULT_INSUFFICIENT_CONDITION if the target has already belonged to another paint.
*
* @return Tvg_Result enumeration.
*/
@ -993,6 +994,7 @@ TVG_API Tvg_Result tvg_paint_get_mask_method(const Tvg_Paint* paint, const Tvg_P
*
* @return Tvg_Result enumeration.
* @retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument.
* @retval TVG_RESULT_INSUFFICIENT_CONDITION if the target has already belonged to another paint.
* @retval TVG_RESULT_NOT_SUPPORTED If the @p clipper type is not Shape.
*
* @since 1.0
@ -1000,6 +1002,24 @@ TVG_API Tvg_Result tvg_paint_get_mask_method(const Tvg_Paint* paint, const Tvg_P
TVG_API Tvg_Result tvg_paint_clip(Tvg_Paint* paint, Tvg_Paint* clipper);
/**
* @brief Retrieves the parent paint object.
*
* This function returns a pointer to the parent object if the current paint
* belongs to one. Otherwise, it returns @c nullptr.
*
* @param[in] paint The Tvg_Paint object of which to get the scene.
*
* @return A pointer to the parent object if available, otherwise @c nullptr.
*
* @see tvg_scene_push()
* @see tvg_canvas_push()
*
* @since 1.0
*/
TVG_API const Tvg_Paint* tvg_paint_get_parent(const Tvg_Paint* paint);
/**
* @brief Gets the unique value of the paint instance indicating the instance type.
*

View file

@ -169,6 +169,12 @@ TVG_API Tvg_Result tvg_canvas_set_viewport(Tvg_Canvas* canvas, int32_t x, int32_
/* Paint API */
/************************************************************************/
TVG_API const Tvg_Paint* tvg_paint_get_parent(const Tvg_Paint* paint)
{
return (const Tvg_Paint*) reinterpret_cast<const Paint*>(paint)->parent();
}
TVG_API Tvg_Result tvg_paint_del(Tvg_Paint* paint)
{
if (!paint) return TVG_RESULT_INVALID_ARGUMENT;

View file

@ -51,10 +51,7 @@ struct Canvas::Impl
Result push(Paint* target, Paint* at)
{
//You cannot push paints during rendering.
if (status == Status::Drawing) {
TVG_DELETE(target);
return Result::InsufficientCondition;
}
if (status == Status::Drawing) return Result::InsufficientCondition;
auto ret = scene->push(target, at);
if (ret != Result::Success) return ret;

View file

@ -392,19 +392,15 @@ Result Paint::clip(Paint* clipper) noexcept
{
if (clipper && clipper->type() != Type::Shape) {
TVGERR("RENDERER", "Clipping only supports the Shape!");
TVG_DELETE(clipper);
return Result::NonSupport;
}
pImpl->clip(clipper);
return Result::Success;
return pImpl->clip(clipper);
}
Result Paint::mask(Paint* target, MaskMethod method) noexcept
{
if (pImpl->mask(target, method)) return Result::Success;
if (target) TVG_DELETE(target);
return Result::InvalidArguments;
return pImpl->mask(target, method);
}
@ -448,11 +444,17 @@ uint8_t Paint::ref() noexcept
uint8_t Paint::unref(bool free) noexcept
{
return pImpl->unref(free);
return pImpl->unrefx(free);
}
uint8_t Paint::refCnt() const noexcept
{
return pImpl->refCnt;
}
const Paint* Paint::parent() const noexcept
{
return pImpl->parent;
}

View file

@ -51,6 +51,7 @@ namespace tvg
struct Paint::Impl
{
Paint* paint = nullptr;
Paint* parent = nullptr;
Mask* maskData = nullptr;
Paint* clipper = nullptr;
RenderMethod* renderer = nullptr;
@ -91,11 +92,11 @@ namespace tvg
virtual ~Impl()
{
if (maskData) {
maskData->target->unref();
PAINT(maskData->target)->unref();
tvg::free(maskData);
}
if (clipper) clipper->unref();
if (clipper) PAINT(clipper)->unref();
if (renderer) {
if (rd) renderer->dispose(rd);
@ -110,13 +111,18 @@ namespace tvg
return refCnt;
}
uint8_t unref(bool free)
uint8_t unref(bool free = true)
{
parent = nullptr;
return unrefx(free);
}
uint8_t unrefx(bool free)
{
if (refCnt > 0) --refCnt;
else TVGERR("RENDERER", "Corrupted Reference Count!");
if (free && refCnt == 0) {
//TODO: use the global dismiss function?
delete(paint);
return 0;
}
@ -146,37 +152,37 @@ namespace tvg
return tr.m;
}
void clip(Paint* clp)
Result clip(Paint* clp)
{
if (clipper) clipper->unref(clipper != clp);
if (PAINT(clp)->parent) return Result::InsufficientCondition;
if (clipper) PAINT(clipper)->unref(clipper != clp);
clipper = clp;
if (!clp) return;
clipper->ref();
if (clp) {
clp->ref();
PAINT(clp)->parent = parent;
}
return Result::Success;
}
bool mask(Paint* target, MaskMethod method)
Result mask(Paint* target, MaskMethod method)
{
//Invalid case
if ((!target && method != MaskMethod::None) || (target && method == MaskMethod::None)) return false;
if (target && PAINT(target)->parent) return Result::InsufficientCondition;
if (maskData) {
maskData->target->unref(maskData->target != target);
//Reset scenario
if (!target && method == MaskMethod::None) {
tvg::free(maskData);
maskData = nullptr;
return true;
}
} else {
if (!target && method == MaskMethod::None) return true;
maskData = tvg::malloc<Mask*>(sizeof(Mask));
PAINT(maskData->target)->unref(maskData->target != target);
tvg::free(maskData);
maskData = nullptr;
}
if (!target && method == MaskMethod::None) return Result::Success;
maskData = tvg::malloc<Mask*>(sizeof(Mask));
target->ref();
maskData->target = target;
PAINT(target)->parent = parent;
maskData->source = paint;
maskData->method = method;
return true;
return Result::Success;
}
MaskMethod mask(const Paint** target) const
@ -193,12 +199,12 @@ namespace tvg
void reset()
{
if (clipper) {
clipper->unref();
PAINT(clipper)->unref();
clipper = nullptr;
}
if (maskData) {
maskData->target->unref();
PAINT(maskData->target)->unref();
tvg::free(maskData);
maskData = nullptr;
}
@ -208,6 +214,7 @@ namespace tvg
tr.scale = 1.0f;
tr.overriding = false;
parent = nullptr;
blendMethod = BlendMethod::Normal;
renderFlag = RenderUpdateFlag::None;
ctxFlag = ContextFlag::Default;

View file

@ -165,7 +165,10 @@ struct Picture::Impl : Paint::Impl
auto picture = Picture::gen();
auto dup = PICTURE(picture);
if (vector) dup->vector = vector->duplicate();
if (vector) {
dup->vector = vector->duplicate();
PAINT(dup->vector)->parent = picture;
}
if (loader) {
dup->loader = loader;
@ -211,6 +214,7 @@ struct Picture::Impl : Paint::Impl
} else {
vector = loader->paint();
if (vector) {
PAINT(vector)->parent = paint;
if (w != loader->w || h != loader->h) {
if (!resizing) {
w = loader->w;

View file

@ -229,6 +229,7 @@ struct Scene::Impl : Paint::Impl
for (auto paint : paints) {
auto cdup = paint->duplicate();
PAINT(cdup)->parent = scene;
cdup->ref();
dup->paints.push_back(cdup);
}
@ -242,7 +243,7 @@ struct Scene::Impl : Paint::Impl
{
auto itr = paints.begin();
while (itr != paints.end()) {
(*itr)->unref();
PAINT((*itr))->unref();
paints.erase(itr++);
}
return Result::Success;
@ -250,31 +251,24 @@ struct Scene::Impl : Paint::Impl
Result remove(Paint* paint)
{
owned(paint);
paint->unref();
if (PAINT(paint)->parent != this->paint) return Result::InsufficientCondition;
PAINT(paint)->unref();
paints.remove(paint);
return Result::Success;
}
void owned(Paint* paint)
{
#ifdef THORVG_LOG_ENABLED
for (auto p : paints) {
if (p == paint) return;
}
TVGERR("RENDERER", "The paint(%p) is not existed from the scene(%p)", paint, this->paint);
#endif
}
Result insert(Paint* target, Paint* at)
{
if (!target) return Result::InvalidArguments;
auto timpl = PAINT(target);
if (timpl->parent) return Result::InsufficientCondition;
target->ref();
//Relocated the paint to the current scene space
PAINT(target)->renderFlag |= RenderUpdateFlag::Transform;
timpl->renderFlag |= RenderUpdateFlag::Transform;
if (at == nullptr) {
if (!at) {
paints.push_back(target);
} else {
//OPTIMIZE: Remove searching?
@ -282,6 +276,9 @@ struct Scene::Impl : Paint::Impl
if (itr == paints.end()) return Result::InvalidArguments;
paints.insert(itr, target);
}
timpl->parent = paint;
if (timpl->clipper) PAINT(timpl->clipper)->parent = paint;
if (timpl->maskData) PAINT(timpl->maskData->target)->parent = paint;
return Result::Success;
}

View file

@ -41,6 +41,7 @@ struct Text::Impl : Paint::Impl
Impl(Text* p) : Paint::Impl(p), shape(Shape::gen())
{
PAINT(shape)->parent = p;
}
~Impl()
@ -149,6 +150,8 @@ struct Text::Impl : Paint::Impl
auto text = Text::gen();
auto dup = TEXT(text);
dup->parent = text;
SHAPE(shape)->duplicate(dup->shape);
if (loader) {

View file

@ -218,11 +218,8 @@ TEST_CASE("Composition", "[tvgPaint]")
//Negative
REQUIRE(shape->mask(nullptr) == MaskMethod::None);
auto comp = Shape::gen();
REQUIRE(shape->mask(comp, MaskMethod::None) == Result::InvalidArguments);
//Clipping
comp = Shape::gen();
auto comp = Shape::gen();
REQUIRE(shape->clip(comp) == Result::Success);
//AlphaMask

View file

@ -39,18 +39,25 @@ TEST_CASE("Pushing Paints Into Scene", "[tvgScene]")
{
auto scene = unique_ptr<Scene>(Scene::gen());
REQUIRE(scene);
REQUIRE(scene->parent() == nullptr);
Paint* paints[3];
//Pushing Paints
paints[0] = Shape::gen();
REQUIRE(paints[0]->parent() == nullptr);
REQUIRE(scene->push(paints[0]) == Result::Success);
REQUIRE(paints[0]->parent() == scene.get());
paints[1] = Picture::gen();
REQUIRE(paints[1]->parent() == nullptr);
REQUIRE(scene->push(paints[1]) == Result::Success);
REQUIRE(paints[1]->parent() == scene.get());
paints[2] = Picture::gen();
REQUIRE(paints[2]->parent() == nullptr);
REQUIRE(scene->push(paints[2]) == Result::Success);
REQUIRE(paints[2]->parent() == scene.get());
//Pushing Null Pointer
REQUIRE(scene->push(nullptr) == Result::InvalidArguments);