diff --git a/inc/thorvg.h b/inc/thorvg.h index b5d62240..7a9ed248 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -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 diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index d2a02ff0..40348576 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -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. * diff --git a/src/bindings/capi/tvgCapi.cpp b/src/bindings/capi/tvgCapi.cpp index 7a9fcec1..b8dd1891 100644 --- a/src/bindings/capi/tvgCapi.cpp +++ b/src/bindings/capi/tvgCapi.cpp @@ -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(paint)->parent(); +} + + TVG_API Tvg_Result tvg_paint_del(Tvg_Paint* paint) { if (!paint) return TVG_RESULT_INVALID_ARGUMENT; diff --git a/src/renderer/tvgCanvas.h b/src/renderer/tvgCanvas.h index 9c71416b..6ab2f2b4 100644 --- a/src/renderer/tvgCanvas.h +++ b/src/renderer/tvgCanvas.h @@ -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; diff --git a/src/renderer/tvgPaint.cpp b/src/renderer/tvgPaint.cpp index a07c3ff6..2dd0336c 100644 --- a/src/renderer/tvgPaint.cpp +++ b/src/renderer/tvgPaint.cpp @@ -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; } \ No newline at end of file diff --git a/src/renderer/tvgPaint.h b/src/renderer/tvgPaint.h index 290b9511..88d8f404 100644 --- a/src/renderer/tvgPaint.h +++ b/src/renderer/tvgPaint.h @@ -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(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(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; diff --git a/src/renderer/tvgPicture.h b/src/renderer/tvgPicture.h index 12eb7e25..7e0a1105 100644 --- a/src/renderer/tvgPicture.h +++ b/src/renderer/tvgPicture.h @@ -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; diff --git a/src/renderer/tvgScene.h b/src/renderer/tvgScene.h index 70a2ea0d..92ea35af 100644 --- a/src/renderer/tvgScene.h +++ b/src/renderer/tvgScene.h @@ -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; } diff --git a/src/renderer/tvgText.h b/src/renderer/tvgText.h index e08966cd..5a6dcf5c 100644 --- a/src/renderer/tvgText.h +++ b/src/renderer/tvgText.h @@ -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) { diff --git a/test/testPaint.cpp b/test/testPaint.cpp index 92d27f5a..853a8fc5 100644 --- a/test/testPaint.cpp +++ b/test/testPaint.cpp @@ -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 diff --git a/test/testScene.cpp b/test/testScene.cpp index c193774a..7320e3f8 100644 --- a/test/testScene.cpp +++ b/test/testScene.cpp @@ -39,18 +39,25 @@ TEST_CASE("Pushing Paints Into Scene", "[tvgScene]") { auto scene = unique_ptr(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);