From 730b0d23f02a42501a9c8e025651431cf91a6883 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 30 Jul 2024 10:54:53 +0900 Subject: [PATCH] renderer/paint: revise the Paint::bounds() behavior The current Paint::bounds(transform=true) returns the coordinates of the paint in its local coordinates after transformation. However, it did not convert the origin to the world coordinate. This is problematic when the user wants to determine the paint's position and size with the origin being the canvas. Specifically, this matters that when the paint is belonged to a certain scene. Now, the bounds() method returns the coordinates of the paint's bounding box with the corrected world space. User can figure out the actual boundary within the painted result. Remark that, this may break the functional behavior compatibility. --- inc/thorvg.h | 7 ++++--- src/bindings/capi/thorvg_capi.h | 5 ++++- src/renderer/tvgPaint.cpp | 13 +++++++------ src/renderer/tvgPaint.h | 8 +++++--- src/renderer/tvgRender.h | 1 - test/capi/capiPaint.cpp | 10 +++++++++- test/testPaint.cpp | 14 ++++++++++++-- 7 files changed, 41 insertions(+), 17 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index 5cee59fb..f9f78474 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -395,15 +395,16 @@ public: /** * @brief Gets the axis-aligned bounding box of the paint object. * - * In case @p transform is @c true, all object's transformations are applied first, and then the bounding box is established. Otherwise, the bounding box is determined before any transformations. - * * @param[out] x The x-coordinate of the upper-left corner of the object. * @param[out] y The y-coordinate of the upper-left corner of the object. * @param[out] w The width of the object. * @param[out] h The height of the object. - * @param[in] transformed If @c true, the paint's transformations are taken into account, otherwise they aren't. + * @param[in] transformed If @c true, the paint's transformations are taken into account in the scene it belongs to. Otherwise they aren't. * + * @note This is useful when you need to figure out the bounding box of the paint in the canvas space. * @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object. + * @note If @p transformed is @c true, the paint needs to be pushed into a canvas and updated before this api is called. + * @see Canvas::update() */ Result bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept; diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index 8d9bf787..770d5c66 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -954,12 +954,15 @@ TVG_API Tvg_Paint* tvg_paint_duplicate(Tvg_Paint* paint); * \param[out] y The y-coordinate of the upper-left corner of the object. * \param[out] w The width of the object. * \param[out] h The height of the object. -* \param[in] transformed If @c true, the transformation of the paint is taken into account, otherwise it isn't. +* \param[in] transformed If @c true, the paint's transformations are taken into account in the scene it belongs to. Otherwise they aren't. * * \return Tvg_Result enumeration. * \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer. * +* \note This is useful when you need to figure out the bounding box of the paint in the canvas space. * \note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object. +* \note If @p transformed is @c true, the paint needs to be pushed into a canvas and updated before this api is called. +* \see tvg_canvas_update_paint() */ TVG_API Tvg_Result tvg_paint_get_bounds(const Tvg_Paint* paint, float* x, float* y, float* w, float* h, bool transformed); diff --git a/src/renderer/tvgPaint.cpp b/src/renderer/tvgPaint.cpp index d8cc230b..87e2d3d3 100644 --- a/src/renderer/tvgPaint.cpp +++ b/src/renderer/tvgPaint.cpp @@ -292,8 +292,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arrayopacity); RenderData rd = nullptr; - auto m = pm * tr.m; - PAINT_METHOD(rd, update(renderer, m, clips, opacity, newFlag, clipper)); + + tr.cm = pm * tr.m; + PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper)); /* 3. Composition Post Processing */ if (compFastTrack == Result::Success) renderer->viewport(viewport); @@ -303,10 +304,10 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Arraytransform(); + const auto& m = this->transform(origin); //Case: No transformed, quick return! if (!transformed || identity(&m)) { @@ -405,9 +406,9 @@ TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) cons } -Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept +Result Paint::bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept { - if (pImpl->bounds(x, y, w, h, transform, true)) return Result::Success; + if (pImpl->bounds(x, y, w, h, transformed, true, true)) return Result::Success; return Result::InsufficientCondition; } diff --git a/src/renderer/tvgPaint.h b/src/renderer/tvgPaint.h index 0d0882f4..15308c7a 100644 --- a/src/renderer/tvgPaint.h +++ b/src/renderer/tvgPaint.h @@ -52,7 +52,8 @@ namespace tvg RenderMethod* renderer = nullptr; BlendMethod blendMethod = BlendMethod::Normal; //uint8_t struct { - Matrix m; + Matrix m; //input matrix + Matrix cm; //multipled parents matrix float degree = 0.0f; //rotation degree float scale = 1.0f; //scale factor bool overriding = false; //user transform? @@ -111,10 +112,11 @@ namespace tvg return true; } - Matrix& transform() + Matrix& transform(bool origin = false) { //update transform if (renderFlag & RenderUpdateFlag::Transform) tr.update(); + if (origin) return tr.cm; return tr.m; } @@ -150,7 +152,7 @@ namespace tvg bool rotate(float degree); bool scale(float factor); bool translate(float x, float y); - bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking); + bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin = false); RenderData update(RenderMethod* renderer, const Matrix& pm, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); bool render(RenderMethod* renderer); Paint* duplicate(Paint* ret = nullptr); diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index 0a64f050..d1675fca 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -111,7 +111,6 @@ struct RenderRegion } }; - struct RenderStroke { float width = 0.0f; diff --git a/test/capi/capiPaint.cpp b/test/capi/capiPaint.cpp index da9c036d..67705a8f 100644 --- a/test/capi/capiPaint.cpp +++ b/test/capi/capiPaint.cpp @@ -137,11 +137,17 @@ TEST_CASE("Paint Opacity", "[capiPaint]") TEST_CASE("Paint Bounds", "[capiPaint]") { + tvg_engine_init(TVG_ENGINE_SW, 0); + Tvg_Canvas* canvas = tvg_swcanvas_create(); + Tvg_Paint* paint = tvg_shape_new(); REQUIRE(paint); + REQUIRE(tvg_canvas_push(canvas, paint) == TVG_RESULT_SUCCESS); + float x, y, w, h; + REQUIRE(tvg_canvas_update_paint(canvas, paint) == TVG_RESULT_SUCCESS); REQUIRE(tvg_shape_append_rect(paint, 0, 10, 20, 100, 0, 0) == TVG_RESULT_SUCCESS); REQUIRE(tvg_paint_get_bounds(paint, &x, &y, &w, &h, true) == TVG_RESULT_SUCCESS); @@ -164,6 +170,7 @@ TEST_CASE("Paint Bounds", "[capiPaint]") REQUIRE(w == 20); REQUIRE(h == 100); + REQUIRE(tvg_canvas_update_paint(canvas, paint) == TVG_RESULT_SUCCESS); REQUIRE(tvg_paint_get_bounds(paint, &x, &y, &w, &h, true) == TVG_RESULT_SUCCESS); REQUIRE(x == Approx(100.0f).margin(0.000001)); @@ -171,7 +178,8 @@ TEST_CASE("Paint Bounds", "[capiPaint]") REQUIRE(w == Approx(40.0f).margin(0.000001)); REQUIRE(h == Approx(200.0f).margin(0.000001)); - REQUIRE(tvg_paint_del(paint) == TVG_RESULT_SUCCESS); + tvg_canvas_destroy(canvas); + tvg_engine_term(TVG_ENGINE_SW); } TEST_CASE("Paint Duplication", "[capiPaint]") diff --git a/test/testPaint.cpp b/test/testPaint.cpp index 8d5fdf2e..a7942a86 100644 --- a/test/testPaint.cpp +++ b/test/testPaint.cpp @@ -118,8 +118,12 @@ TEST_CASE("Opacity", "[tvgPaint]") TEST_CASE("Bounding Box", "[tvgPaint]") { - auto shape = Shape::gen(); - REQUIRE(shape); + Initializer::init(tvg::CanvasEngine::Sw, 0); + + auto canvas = SwCanvas::gen(); + auto shape = Shape::gen().release(); + canvas->push(tvg::cast(shape)); + canvas->sync(); //Negative float x = 0, y = 0, w = 0, h = 0; @@ -133,6 +137,8 @@ TEST_CASE("Bounding Box", "[tvgPaint]") REQUIRE(y == 10.0f); REQUIRE(w == 20.0f); REQUIRE(h == 100.0f); + + REQUIRE(canvas->update(shape) == Result::Success); REQUIRE(shape->bounds(&x, &y, &w, &h, true) == Result::Success); REQUIRE(x == 100.0f); REQUIRE(y == 121.0f); @@ -150,11 +156,15 @@ TEST_CASE("Bounding Box", "[tvgPaint]") REQUIRE(y == 10.0f); REQUIRE(w == 20.0f); REQUIRE(h == 200.0f); + + REQUIRE(canvas->update(shape) == Result::Success); REQUIRE(shape->bounds(&x, &y, &w, &h, true) == Result::Success); REQUIRE(x == 0.0f); REQUIRE(y == 10.0f); REQUIRE(w == 20.0f); REQUIRE(h == 200.0f); + + Initializer::term(tvg::CanvasEngine::Sw); } TEST_CASE("Duplication", "[tvgPaint]")