* @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.
+ * @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.
/* Internal Class Implementation */
/************************************************************************/
-
-
static inline bool FLT_SAME(float a, float b)
{
return (fabsf(a - b) < FLT_EPSILON);
}
+
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. */
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())
{
}
-/************************************************************************/
-/* External Class Implementation */
-/************************************************************************/
-
Result Paint::rotate(float degree) noexcept
{
if (pImpl->rotate(degree)) return Result::Success;
return Result::FailedAllocation;
}
+
Matrix Paint::transform() noexcept
{
auto pTransform = pImpl->transform();
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
+{
+ 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)) return Result::Success;
+ if (pImpl->bounds(x, y, w, h, transform)) return Result::Success;
return Result::InsufficientCondition;
}
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 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 Paint* duplicate() = 0;
virtual Iterator* iterator() = 0;
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
{
return smethod->bounds(renderer);
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);
void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag);
bool render(RenderMethod& renderer);
Paint* duplicate();
PaintMethod(T* _inst) : inst(_inst) {}
~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 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 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 (y) *y = 0;
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;
auto w = 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
if (x < x1) x1 = x;
if (style->fill.paint.none) {
//Do nothing
} 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) {
auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, vx, vy, vw, vh, style->fill.opacity);
if (style->stroke.paint.none) {
//Do nothing
} 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) {
auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, vx, vy, vw, vh, style->stroke.opacity);
void TvgSaver::run(unsigned tid)
{
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;
}
this->path = strdup(path.c_str());
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) {
TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint);