common paint: refine the bounds() api to return the values after applying transformation.

Current paint::bounds() returns the coordinates under the raw status,
the values are not quite useful if the paint object has the transformed children.

Thus, we extends the feature and give an additional parameter "transformed"
to return the coordinates values after transformation by user demands.

This is also necessary for tvg format, since we need the exact view size of the scene information.

The previous api is deprecated and we introduce a new api to replace it.

@APIs:
+ Result Paint::bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept;
- Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept;

@Issues: https://github.com/Samsung/thorvg/issues/746
This commit is contained in:
Hermet Park 2021-09-16 22:03:17 +09:00 committed by Hermet Park
parent 000d3ec2f0
commit 74954db56d
31 changed files with 123 additions and 24 deletions

View file

@ -289,8 +289,28 @@ public:
* @return Result::Success when succeed, Result::InsufficientCondition otherwise. * @return Result::Success when succeed, Result::InsufficientCondition otherwise.
* *
* @note The bounding box doesn't indicate the final rendered region. It's the smallest rectangle that encloses the object. * @note The bounding box doesn't indicate the final rendered region. It's the smallest rectangle that encloses the object.
* @see Paint::bounds(float* x, float* y, float* w, float* h, bool transformed);
*/ */
Result bounds(float* x, float* y, float* w, float* h) const noexcept; TVG_DEPRECATED Result bounds(float* x, float* y, float* w, float* h) const noexcept;
/**
* @brief Gets the axis-aligned bounding box of the paint 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] h The height of the object.
* @param[in] transformed if @c true, apply the transformation of the paint.
*
* @return Result::Success when succeed, Result::InsufficientCondition otherwise.
*
* @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object.
*
* @BETA_API
*/
Result bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept;
/** /**
* @brief Duplicates the object. * @brief Duplicates the object.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -27,13 +27,12 @@
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
static inline bool FLT_SAME(float a, float b) static inline bool FLT_SAME(float a, float b)
{ {
return (fabsf(a - b) < FLT_EPSILON); return (fabsf(a - b) < FLT_EPSILON);
} }
static bool _clipPathFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport) static bool _clipPathFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport)
{ {
/* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */ /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */
@ -243,6 +242,60 @@ void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransf
return edata; return edata;
} }
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed)
{
Matrix* m = nullptr;
//Case: No transformed, quick return!
if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h);
//Case: Transformed
auto tx = 0.0f;
auto ty = 0.0f;
auto tw = 0.0f;
auto th = 0.0f;
auto ret = smethod->bounds(&tx, &ty, &tw, &th);
//Get vertices
Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
//New bounding box
auto x1 = FLT_MAX;
auto y1 = FLT_MAX;
auto x2 = -FLT_MAX;
auto y2 = -FLT_MAX;
//function = Point * Matrix
auto multiply = [&](Point* pt, const Matrix* transform) {
auto tx = pt->x * transform->e11 + pt->y * transform->e12 + transform->e13;
auto ty = pt->x * transform->e21 + pt->y * transform->e22 + transform->e23;
pt->x = tx;
pt->y = ty;
};
//Compute the AABB after transformation
for (int i = 0; i < 4; i++) {
multiply(&pt[i], m);
if (pt[i].x < x1) x1 = pt[i].x;
if (pt[i].x > x2) x2 = pt[i].x;
if (pt[i].y < y1) y1 = pt[i].y;
if (pt[i].y > y2) y2 = pt[i].y;
}
if (x) *x = x1;
if (y) *y = y1;
if (w) *w = x2 - x1;
if (h) *h = y2 - y1;
return ret;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Paint :: Paint() : pImpl(new Impl()) Paint :: Paint() : pImpl(new Impl())
{ {
@ -255,10 +308,6 @@ Paint :: ~Paint()
} }
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Result Paint::rotate(float degree) noexcept Result Paint::rotate(float degree) noexcept
{ {
if (pImpl->rotate(degree)) return Result::Success; if (pImpl->rotate(degree)) return Result::Success;
@ -286,6 +335,7 @@ Result Paint::transform(const Matrix& m) noexcept
return Result::FailedAllocation; return Result::FailedAllocation;
} }
Matrix Paint::transform() noexcept Matrix Paint::transform() noexcept
{ {
auto pTransform = pImpl->transform(); auto pTransform = pImpl->transform();
@ -293,9 +343,16 @@ Matrix Paint::transform() noexcept
return {1, 0, 0, 0, 1, 0, 0, 0, 1}; return {1, 0, 0, 0, 1, 0, 0, 0, 1};
} }
Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
{ {
if (pImpl->bounds(x, y, w, h)) return Result::Success; return this->bounds(x, y, w, h, false);
}
Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept
{
if (pImpl->bounds(x, y, w, h, transform)) return Result::Success;
return Result::InsufficientCondition; return Result::InsufficientCondition;
} }

View file

@ -42,7 +42,7 @@ namespace tvg
virtual bool dispose(RenderMethod& renderer) = 0; virtual bool dispose(RenderMethod& renderer) = 0;
virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag) = 0; //Return engine data if it has. virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag) = 0; //Return engine data if it has.
virtual bool render(RenderMethod& renderer) = 0; virtual bool render(RenderMethod& renderer) = 0;
virtual bool bounds(float* x, float* y, float* w, float* h) const = 0; virtual bool bounds(float* x, float* y, float* w, float* h) = 0;
virtual RenderRegion bounds(RenderMethod& renderer) const = 0; virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
virtual Paint* duplicate() = 0; virtual Paint* duplicate() = 0;
virtual Iterator* iterator() = 0; virtual Iterator* iterator() = 0;
@ -89,11 +89,6 @@ namespace tvg
return nullptr; return nullptr;
} }
bool bounds(float* x, float* y, float* w, float* h) const
{
return smethod->bounds(x, y, w, h);
}
RenderRegion bounds(RenderMethod& renderer) const RenderRegion bounds(RenderMethod& renderer) const
{ {
return smethod->bounds(renderer); return smethod->bounds(renderer);
@ -122,6 +117,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);
void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag); void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag);
bool render(RenderMethod& renderer); bool render(RenderMethod& renderer);
Paint* duplicate(); Paint* duplicate();
@ -136,7 +132,7 @@ namespace tvg
PaintMethod(T* _inst) : inst(_inst) {} PaintMethod(T* _inst) : inst(_inst) {}
~PaintMethod() {} ~PaintMethod() {}
bool bounds(float* x, float* y, float* w, float* h) const override bool bounds(float* x, float* y, float* w, float* h) override
{ {
return inst->bounds(x, y, w, h); return inst->bounds(x, y, w, h);
} }

View file

@ -60,7 +60,8 @@ Result Picture::load(const char* data, uint32_t size, const string& mimeType, bo
return pImpl->load(data, size, mimeType, copy); return pImpl->load(data, size, mimeType, copy);
} }
Result Picture::load(const char* data, uint32_t size, bool copy) noexcept
TVG_DEPRECATED Result Picture::load(const char* data, uint32_t size, bool copy) noexcept
{ {
return load(data, size, "", copy); return load(data, size, "", copy);
} }

View file

@ -167,7 +167,7 @@ struct Picture::Impl
return true; return true;
} }
bool bounds(float* x, float* y, float* w, float* h) const bool bounds(float* x, float* y, float* w, float* h)
{ {
if (x) *x = 0; if (x) *x = 0;
if (y) *y = 0; if (y) *y = 0;

View file

@ -149,7 +149,7 @@ struct Scene::Impl
return {x1, y1, (x2 - x1), (y2 - y1)}; return {x1, y1, (x2 - x1), (y2 - y1)};
} }
bool bounds(float* px, float* py, float* pw, float* ph) const bool bounds(float* px, float* py, float* pw, float* ph)
{ {
if (paints.count == 0) return false; if (paints.count == 0) return false;
@ -164,7 +164,7 @@ struct Scene::Impl
auto w = 0.0f; auto w = 0.0f;
auto h = 0.0f; auto h = 0.0f;
if (!(*paint)->pImpl->bounds(&x, &y, &w, &h)) continue; if ((*paint)->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
//Merge regions //Merge regions
if (x < x1) x1 = x; if (x < x1) x1 = x;

View file

@ -246,7 +246,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float v
if (style->fill.paint.none) { if (style->fill.paint.none) {
//Do nothing //Do nothing
} else if (style->fill.paint.gradient) { } else if (style->fill.paint.gradient) {
if (!style->fill.paint.gradient->userSpace) vg->bounds(&vx, &vy, &vw, &vh); if (!style->fill.paint.gradient->userSpace) vg->bounds(&vx, &vy, &vw, &vh, false);
if (style->fill.paint.gradient->type == SvgGradientType::Linear) { if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, vx, vy, vw, vh, style->fill.opacity); auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, vx, vy, vw, vh, style->fill.opacity);
@ -285,7 +285,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float v
if (style->stroke.paint.none) { if (style->stroke.paint.none) {
//Do nothing //Do nothing
} else if (style->stroke.paint.gradient) { } else if (style->stroke.paint.gradient) {
if (!style->stroke.paint.gradient->userSpace) vg->bounds(&vx, &vy, &vw, &vh); if (!style->stroke.paint.gradient->userSpace) vg->bounds(&vx, &vy, &vw, &vh, false);
if (style->stroke.paint.gradient->type == SvgGradientType::Linear) { if (style->stroke.paint.gradient->type == SvgGradientType::Linear) {
auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, vx, vy, vw, vh, style->stroke.opacity); auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, vx, vy, vw, vh, style->stroke.opacity);

View file

@ -697,7 +697,27 @@ TvgBinCounter TvgSaver::serialize(const Paint* paint, const Matrix* pTransform,
void TvgSaver::run(unsigned tid) void TvgSaver::run(unsigned tid)
{ {
if (!writeHeader()) return; if (!writeHeader()) return;
if (serialize(paint, nullptr) == 0) return;
//Serialize Root Paint, without its transform.
Matrix transform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if (paint->opacity() > 0) {
switch (paint->id()) {
case TVG_CLASS_ID_SHAPE: {
serializeShape(static_cast<const Shape*>(paint), nullptr, &transform);
break;
}
case TVG_CLASS_ID_SCENE: {
serializeScene(static_cast<const Scene*>(paint), nullptr, &transform);
break;
}
case TVG_CLASS_ID_PICTURE: {
serializePicture(static_cast<const Picture*>(paint), nullptr, &transform);
break;
}
}
}
if (!saveEncoding(path)) return; if (!saveEncoding(path)) return;
} }
@ -736,7 +756,12 @@ bool TvgSaver::save(Paint* paint, const string& path, bool compress)
this->path = strdup(path.c_str()); this->path = strdup(path.c_str());
if (!this->path) return false; if (!this->path) return false;
paint->bounds(nullptr, nullptr, &vsize[0], &vsize[1]); float x, y;
paint->bounds(&x, &y, &vsize[0], &vsize[1], false);
//cut off the negative space
if (x < 0) vsize[0] += x;
if (y < 0) vsize[1] += y;
if (vsize[0] <= FLT_EPSILON || vsize[1] <= FLT_EPSILON) { if (vsize[0] <= FLT_EPSILON || vsize[1] <= FLT_EPSILON) {
TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint); TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint);