From 39b0f87cb35e9bef7f6e3e1dd1a9c8e057a67b5e Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 15 Jun 2025 14:35:06 +0900 Subject: [PATCH] api: revise the canvas update() API Removed the paint parameter previously used to forcibly update a single paint. This behavior was unsafe in ThorVG, as a single paint object may be connected to others through the scene graph. Now, the canvas engine is responsible for properly updating all damaged paints as needed, allowing the user to simply call update() in any case. API Modifications: C++ API: * Result Canvas::update(Paint* paint) -> Result Canvas::update() CAPI: - tvg_canvas_update_paint() Issue: https://github.com/thorvg/thorvg/issues/3116 --- examples/ImageScaling.cpp | 2 +- inc/thorvg.h | 12 ++++-------- src/bindings/capi/thorvg_capi.h | 17 ----------------- src/bindings/capi/tvgCapi.cpp | 9 +-------- src/renderer/tvgCanvas.cpp | 4 ++-- src/renderer/tvgCanvas.h | 16 +++++----------- src/renderer/tvgPicture.h | 2 +- src/renderer/tvgScene.h | 2 +- test/testPaint.cpp | 4 ++-- 9 files changed, 17 insertions(+), 51 deletions(-) diff --git a/examples/ImageScaling.cpp b/examples/ImageScaling.cpp index 635daea9..05761c0c 100644 --- a/examples/ImageScaling.cpp +++ b/examples/ImageScaling.cpp @@ -54,7 +54,7 @@ struct UserExample : tvgexam::Example picture->scale((1.0f - progress) * 1.5f); - canvas->update(picture); + canvas->update(); return true; } diff --git a/inc/thorvg.h b/inc/thorvg.h index 1ba5641c..84236a74 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -733,16 +733,12 @@ public: Result remove(Paint* paint = nullptr) noexcept; /** - * @brief Request the canvas to update the paint objects. + * @brief Requests the canvas to update the paint for up-to-date render preparation. * - * If a @c nullptr is passed all paint objects retained by the Canvas are updated, - * otherwise only the paint to which the given @p paint points. - * - * @param[in] paint A pointer to the Paint object or @c nullptr. - * - * @note The Update behavior can be asynchronous if the assigned thread number is greater than zero. + * @note Only modified paint instances will undergo the internal update process. + * @note The update operation may be asynchronous if the assigned thread count is greater than zero. */ - Result update(Paint* paint = nullptr) noexcept; + Result update() noexcept; /** * @brief Requests the canvas to render Paint objects. diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index 9f56972a..83f97e80 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -630,23 +630,6 @@ TVG_API Tvg_Result tvg_canvas_remove(Tvg_Canvas* canvas, Tvg_Paint* paint); TVG_API Tvg_Result tvg_canvas_update(Tvg_Canvas* canvas); -/*! -* @brief Updates the given Tvg_Paint object from the canvas before the rendering. -* -* If a client application using the TVG library does not update the entire canvas with tvg_canvas_update() in the frame -* rendering process, Tvg_Paint objects previously added to the canvas should be updated manually with this function. -* -* @param[in] canvas The Tvg_Canvas object to which the @p paint belongs. -* @param[in] paint The Tvg_Paint object to be updated. -* -* @return Tvg_Result enumeration. -* @retval TVG_RESULT_INVALID_ARGUMENT In case a @c nullptr is passed as the argument. -* -* @see tvg_canvas_update() -*/ -TVG_API Tvg_Result tvg_canvas_update_paint(Tvg_Canvas* canvas, Tvg_Paint* paint); - - /*! * @brief Requests the canvas to draw the Tvg_Paint objects. * diff --git a/src/bindings/capi/tvgCapi.cpp b/src/bindings/capi/tvgCapi.cpp index b5092576..b650a671 100644 --- a/src/bindings/capi/tvgCapi.cpp +++ b/src/bindings/capi/tvgCapi.cpp @@ -134,14 +134,7 @@ TVG_API Tvg_Result tvg_canvas_remove(Tvg_Canvas* canvas, Tvg_Paint* paint) TVG_API Tvg_Result tvg_canvas_update(Tvg_Canvas* canvas) { - if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->update(nullptr); - return TVG_RESULT_INVALID_ARGUMENT; -} - - -TVG_API Tvg_Result tvg_canvas_update_paint(Tvg_Canvas* canvas, Tvg_Paint* paint) -{ - if (canvas && paint) return (Tvg_Result) reinterpret_cast(canvas)->update((Paint*) paint); + if (canvas) return (Tvg_Result) reinterpret_cast(canvas)->update(); return TVG_RESULT_INVALID_ARGUMENT; } diff --git a/src/renderer/tvgCanvas.cpp b/src/renderer/tvgCanvas.cpp index ebbb188c..ac580b20 100644 --- a/src/renderer/tvgCanvas.cpp +++ b/src/renderer/tvgCanvas.cpp @@ -55,11 +55,11 @@ Result Canvas::draw(bool clear) noexcept } -Result Canvas::update(Paint* paint) noexcept +Result Canvas::update() noexcept { TVGLOG("RENDERER", "Update S. ------------------------------ Canvas(%p)", this); if (pImpl->scene->paints().empty() || pImpl->status == Status::Drawing) return Result::InsufficientCondition; - auto ret = pImpl->update(paint, false); + auto ret = pImpl->update(false); TVGLOG("RENDERER", "Update E. ------------------------------ Canvas(%p)", this); return ret; diff --git a/src/renderer/tvgCanvas.h b/src/renderer/tvgCanvas.h index ba5cc5ed..323fad5c 100644 --- a/src/renderer/tvgCanvas.h +++ b/src/renderer/tvgCanvas.h @@ -56,7 +56,7 @@ struct Canvas::Impl auto ret = scene->push(target, at); if (ret != Result::Success) return ret; - return update(target, true); + return update(true); } Result remove(Paint* paint) @@ -65,18 +65,16 @@ struct Canvas::Impl return scene->remove(paint); } - Result update(Paint* paint, bool force) + Result update(bool force) { Array clips; auto flag = RenderUpdateFlag::None; if (status == Status::Damaged || force) flag = RenderUpdateFlag::All; - auto m = tvg::identity(); - if (!renderer->preUpdate()) return Result::InsufficientCondition; - if (paint) PAINT(paint)->update(renderer, m, clips, 255, flag); - else PAINT(scene)->update(renderer, m, clips, 255, flag); + auto m = tvg::identity(); + PAINT(scene)->update(renderer, m, clips, 255, flag); if (!renderer->postUpdate()) return Result::InsufficientCondition; @@ -87,13 +85,9 @@ struct Canvas::Impl Result draw(bool clear) { if (status == Status::Drawing) return Result::InsufficientCondition; - if (clear && !renderer->clear()) return Result::InsufficientCondition; - if (scene->paints().empty()) return Result::InsufficientCondition; - - if (status == Status::Damaged) update(nullptr, false); - + if (status == Status::Damaged) update(false); if (!renderer->preRender()) return Result::InsufficientCondition; if (!PAINT(scene)->render(renderer) || !renderer->postRender()) return Result::InsufficientCondition; diff --git a/src/renderer/tvgPicture.h b/src/renderer/tvgPicture.h index d44bbfdd..553dd714 100644 --- a/src/renderer/tvgPicture.h +++ b/src/renderer/tvgPicture.h @@ -116,7 +116,7 @@ struct PictureImpl : Picture return Result::Success; } - Result bounds(Point* pt4, Matrix& m, TVG_UNUSED bool obb, TVG_UNUSED bool stroking) + Result bounds(Point* pt4, Matrix& m, TVG_UNUSED bool obb, TVG_UNUSED bool stroking) const { pt4[0] = Point{0.0f, 0.0f} * m; pt4[1] = Point{w, 0.0f} * m; diff --git a/src/renderer/tvgScene.h b/src/renderer/tvgScene.h index 16e7f1a2..884ebdae 100644 --- a/src/renderer/tvgScene.h +++ b/src/renderer/tvgScene.h @@ -116,7 +116,7 @@ struct SceneImpl : Scene opacity = 255; } for (auto paint : paints) { - paint->pImpl->update(renderer, transform, clips, opacity, flag, false); + PAINT(paint)->update(renderer, transform, clips, opacity, flag, false); } if (effects) { diff --git a/test/testPaint.cpp b/test/testPaint.cpp index e91e26d4..874121f6 100644 --- a/test/testPaint.cpp +++ b/test/testPaint.cpp @@ -141,7 +141,7 @@ TEST_CASE("Bounding Box", "[tvgPaint]") REQUIRE(w == 20.0f); REQUIRE(h == 100.0f); - REQUIRE(canvas->update(shape) == Result::Success); + REQUIRE(canvas->update() == Result::Success); Point pts[4]; REQUIRE(shape->bounds(pts) == Result::Success); REQUIRE(pts[0].x == 100.0f); @@ -165,7 +165,7 @@ TEST_CASE("Bounding Box", "[tvgPaint]") REQUIRE(w == 20.0f); REQUIRE(h == 200.0f); - REQUIRE(canvas->update(shape) == Result::Success); + REQUIRE(canvas->update() == Result::Success); REQUIRE(shape->bounds(pts) == Result::Success); REQUIRE(pts[0].x == 0.0f); REQUIRE(pts[3].x == 0.0f);