mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
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.
This commit is contained in:
parent
7ec8d2f392
commit
6ef1986fe1
7 changed files with 42 additions and 18 deletions
11
inc/thorvg.h
11
inc/thorvg.h
|
@ -373,15 +373,16 @@ public:
|
||||||
/**
|
/**
|
||||||
* @brief Gets the axis-aligned bounding box of the paint object.
|
* @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] 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] w The width of the object.
|
||||||
* @param[out] h The height 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 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;
|
Result bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept;
|
||||||
|
|
||||||
|
|
|
@ -934,12 +934,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] y The y coordinate of the upper left corner of the object.
|
||||||
* \param[out] w The width of the object.
|
* \param[out] w The width of the object.
|
||||||
* \param[out] h The height 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.
|
* \return Tvg_Result enumeration.
|
||||||
* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Paint pointer.
|
* \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 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);
|
TVG_API Tvg_Result tvg_paint_get_bounds(const Tvg_Paint* paint, float* x, float* y, float* w, float* h, bool transformed);
|
||||||
|
|
||||||
|
|
|
@ -292,8 +292,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
|
||||||
opacity = MULTIPLY(opacity, this->opacity);
|
opacity = MULTIPLY(opacity, this->opacity);
|
||||||
|
|
||||||
RenderData rd = nullptr;
|
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 */
|
/* 3. Composition Post Processing */
|
||||||
if (compFastTrack == Result::Success) renderer->viewport(viewport);
|
if (compFastTrack == Result::Success) renderer->viewport(viewport);
|
||||||
|
@ -303,10 +304,10 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
|
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin)
|
||||||
{
|
{
|
||||||
bool ret;
|
bool ret;
|
||||||
const auto& m = this->transform();
|
const auto& m = this->transform(origin);
|
||||||
|
|
||||||
//Case: No transformed, quick return!
|
//Case: No transformed, quick return!
|
||||||
if (!transformed || mathIdentity(&m)) {
|
if (!transformed || mathIdentity(&m)) {
|
||||||
|
@ -407,7 +408,7 @@ 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 transform) const noexcept
|
||||||
{
|
{
|
||||||
if (pImpl->bounds(x, y, w, h, transform, true)) return Result::Success;
|
if (pImpl->bounds(x, y, w, h, transform, true, true)) return Result::Success;
|
||||||
return Result::InsufficientCondition;
|
return Result::InsufficientCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,8 @@ namespace tvg
|
||||||
RenderMethod* renderer = nullptr;
|
RenderMethod* renderer = nullptr;
|
||||||
BlendMethod blendMethod = BlendMethod::Normal; //uint8_t
|
BlendMethod blendMethod = BlendMethod::Normal; //uint8_t
|
||||||
struct {
|
struct {
|
||||||
Matrix m;
|
Matrix m; //input matrix
|
||||||
|
Matrix cm; //multipled parents matrix
|
||||||
float degree = 0.0f; //rotation degree
|
float degree = 0.0f; //rotation degree
|
||||||
float scale = 1.0f; //scale factor
|
float scale = 1.0f; //scale factor
|
||||||
bool overriding = false; //user transform?
|
bool overriding = false; //user transform?
|
||||||
|
@ -112,10 +113,11 @@ namespace tvg
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix& transform()
|
Matrix& transform(bool origin = false)
|
||||||
{
|
{
|
||||||
//update transform
|
//update transform
|
||||||
if (renderFlag & RenderUpdateFlag::Transform) tr.update();
|
if (renderFlag & RenderUpdateFlag::Transform) tr.update();
|
||||||
|
if (origin) return tr.cm;
|
||||||
return tr.m;
|
return tr.m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +153,7 @@ namespace tvg
|
||||||
bool rotate(float degree);
|
bool rotate(float degree);
|
||||||
bool scale(float factor);
|
bool scale(float factor);
|
||||||
bool translate(float x, float y);
|
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<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
RenderData update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
||||||
bool render(RenderMethod* renderer);
|
bool render(RenderMethod* renderer);
|
||||||
Paint* duplicate(Paint* ret = nullptr);
|
Paint* duplicate(Paint* ret = nullptr);
|
||||||
|
|
|
@ -111,7 +111,6 @@ struct RenderRegion
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct RenderStroke
|
struct RenderStroke
|
||||||
{
|
{
|
||||||
float width = 0.0f;
|
float width = 0.0f;
|
||||||
|
|
|
@ -137,11 +137,17 @@ TEST_CASE("Paint Opacity", "[capiPaint]")
|
||||||
|
|
||||||
TEST_CASE("Paint Bounds", "[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();
|
Tvg_Paint* paint = tvg_shape_new();
|
||||||
REQUIRE(paint);
|
REQUIRE(paint);
|
||||||
|
|
||||||
|
REQUIRE(tvg_canvas_push(canvas, paint) == TVG_RESULT_SUCCESS);
|
||||||
|
|
||||||
float x, y, w, h;
|
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_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);
|
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(w == 20);
|
||||||
REQUIRE(h == 100);
|
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(tvg_paint_get_bounds(paint, &x, &y, &w, &h, true) == TVG_RESULT_SUCCESS);
|
||||||
|
|
||||||
REQUIRE(x == Approx(100.0f).margin(0.000001));
|
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(w == Approx(40.0f).margin(0.000001));
|
||||||
REQUIRE(h == Approx(200.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 Dupliction", "[capiPaint]")
|
TEST_CASE("Paint Dupliction", "[capiPaint]")
|
||||||
|
|
|
@ -118,8 +118,12 @@ TEST_CASE("Opacity", "[tvgPaint]")
|
||||||
|
|
||||||
TEST_CASE("Bounding Box", "[tvgPaint]")
|
TEST_CASE("Bounding Box", "[tvgPaint]")
|
||||||
{
|
{
|
||||||
auto shape = Shape::gen();
|
Initializer::init(tvg::CanvasEngine::Sw, 0);
|
||||||
REQUIRE(shape);
|
|
||||||
|
auto canvas = SwCanvas::gen();
|
||||||
|
auto shape = Shape::gen().release();
|
||||||
|
canvas->push(tvg::cast(shape));
|
||||||
|
canvas->sync();
|
||||||
|
|
||||||
//Negative
|
//Negative
|
||||||
float x = 0, y = 0, w = 0, h = 0;
|
float x = 0, y = 0, w = 0, h = 0;
|
||||||
|
@ -133,6 +137,8 @@ TEST_CASE("Bounding Box", "[tvgPaint]")
|
||||||
REQUIRE(y == 10.0f);
|
REQUIRE(y == 10.0f);
|
||||||
REQUIRE(w == 20.0f);
|
REQUIRE(w == 20.0f);
|
||||||
REQUIRE(h == 100.0f);
|
REQUIRE(h == 100.0f);
|
||||||
|
|
||||||
|
REQUIRE(canvas->update(shape) == Result::Success);
|
||||||
REQUIRE(shape->bounds(&x, &y, &w, &h, true) == Result::Success);
|
REQUIRE(shape->bounds(&x, &y, &w, &h, true) == Result::Success);
|
||||||
REQUIRE(x == 100.0f);
|
REQUIRE(x == 100.0f);
|
||||||
REQUIRE(y == 121.0f);
|
REQUIRE(y == 121.0f);
|
||||||
|
@ -150,11 +156,15 @@ TEST_CASE("Bounding Box", "[tvgPaint]")
|
||||||
REQUIRE(y == 10.0f);
|
REQUIRE(y == 10.0f);
|
||||||
REQUIRE(w == 20.0f);
|
REQUIRE(w == 20.0f);
|
||||||
REQUIRE(h == 200.0f);
|
REQUIRE(h == 200.0f);
|
||||||
|
|
||||||
|
REQUIRE(canvas->update(shape) == Result::Success);
|
||||||
REQUIRE(shape->bounds(&x, &y, &w, &h, true) == Result::Success);
|
REQUIRE(shape->bounds(&x, &y, &w, &h, true) == Result::Success);
|
||||||
REQUIRE(x == 0.0f);
|
REQUIRE(x == 0.0f);
|
||||||
REQUIRE(y == 10.0f);
|
REQUIRE(y == 10.0f);
|
||||||
REQUIRE(w == 20.0f);
|
REQUIRE(w == 20.0f);
|
||||||
REQUIRE(h == 200.0f);
|
REQUIRE(h == 200.0f);
|
||||||
|
|
||||||
|
Initializer::term(tvg::CanvasEngine::Sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Duplication", "[tvgPaint]")
|
TEST_CASE("Duplication", "[tvgPaint]")
|
||||||
|
|
Loading…
Add table
Reference in a new issue