common paint: refine the bounds() api to return the values after applying transformation.
authorHermet Park <chuneon.park@samsung.com>
Thu, 16 Sep 2021 13:03:17 +0000 (22:03 +0900)
committerHermet Park <chuneon.park@samsung.com>
Mon, 27 Sep 2021 02:21:29 +0000 (11:21 +0900)
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

31 files changed:
inc/thorvg.h
src/examples/images/1528966158.tvg
src/examples/images/1528971751.tvg
src/examples/images/1630446379Bahamas-patriotic-flag-symbol.tvg
src/examples/images/bojo.tvg
src/examples/images/bzrfeed.tvg
src/examples/images/cartman.tvg
src/examples/images/dst.tvg
src/examples/images/duke.tvg
src/examples/images/gradient_stroke.tvg
src/examples/images/heliocentric.tvg
src/examples/images/ietf.tvg [new file with mode: 0644]
src/examples/images/image-embeded.tvg
src/examples/images/intertwingly.tvg
src/examples/images/logo.tvg
src/examples/images/open-clipart.tvg
src/examples/images/opensearch.tvg
src/examples/images/penrose-staircase.tvg
src/examples/images/penrose-tiling.tvg
src/examples/images/ranze-penguin.tvg
src/examples/images/rg1024_eggs.tvg
src/examples/images/rotlines.tvg [new file with mode: 0644]
src/examples/images/test.tvg
src/examples/images/yinyang.tvg
src/lib/tvgPaint.cpp
src/lib/tvgPaint.h
src/lib/tvgPicture.cpp
src/lib/tvgPictureImpl.h
src/lib/tvgSceneImpl.h
src/loaders/svg/tvgSvgSceneBuilder.cpp
src/savers/tvg/tvgTvgSaver.cpp

index e56fd3d..01c8e28 100644 (file)
@@ -289,8 +289,28 @@ public:
      * @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.
index 5c5b91a..87a0614 100644 (file)
Binary files a/src/examples/images/1528966158.tvg and b/src/examples/images/1528966158.tvg differ
index 8610041..ff11380 100644 (file)
Binary files a/src/examples/images/1528971751.tvg and b/src/examples/images/1528971751.tvg differ
index 16171ca..e9f1cc9 100644 (file)
Binary files a/src/examples/images/1630446379Bahamas-patriotic-flag-symbol.tvg and b/src/examples/images/1630446379Bahamas-patriotic-flag-symbol.tvg differ
index e95477d..c0bf371 100644 (file)
Binary files a/src/examples/images/bojo.tvg and b/src/examples/images/bojo.tvg differ
index 03a4b7c..e1f788d 100644 (file)
Binary files a/src/examples/images/bzrfeed.tvg and b/src/examples/images/bzrfeed.tvg differ
index edc1cd2..c1ff9d4 100644 (file)
Binary files a/src/examples/images/cartman.tvg and b/src/examples/images/cartman.tvg differ
index 78f718c..3883f55 100644 (file)
Binary files a/src/examples/images/dst.tvg and b/src/examples/images/dst.tvg differ
index f80b355..fcc8dfe 100644 (file)
Binary files a/src/examples/images/duke.tvg and b/src/examples/images/duke.tvg differ
index 9dd45d8..34bc8c6 100644 (file)
Binary files a/src/examples/images/gradient_stroke.tvg and b/src/examples/images/gradient_stroke.tvg differ
index 4f4dcee..ab1c673 100644 (file)
Binary files a/src/examples/images/heliocentric.tvg and b/src/examples/images/heliocentric.tvg differ
diff --git a/src/examples/images/ietf.tvg b/src/examples/images/ietf.tvg
new file mode 100644 (file)
index 0000000..de404df
Binary files /dev/null and b/src/examples/images/ietf.tvg differ
index 3fc37db..0b90c06 100644 (file)
Binary files a/src/examples/images/image-embeded.tvg and b/src/examples/images/image-embeded.tvg differ
index dd77983..b543fc0 100644 (file)
Binary files a/src/examples/images/intertwingly.tvg and b/src/examples/images/intertwingly.tvg differ
index 16d99c8..c07c437 100644 (file)
Binary files a/src/examples/images/logo.tvg and b/src/examples/images/logo.tvg differ
index d5ca6e1..f0f71e2 100644 (file)
Binary files a/src/examples/images/open-clipart.tvg and b/src/examples/images/open-clipart.tvg differ
index 9d1c9b5..9e67668 100644 (file)
Binary files a/src/examples/images/opensearch.tvg and b/src/examples/images/opensearch.tvg differ
index 0d2043c..9cf79e2 100644 (file)
Binary files a/src/examples/images/penrose-staircase.tvg and b/src/examples/images/penrose-staircase.tvg differ
index 277b8cc..29bb08d 100644 (file)
Binary files a/src/examples/images/penrose-tiling.tvg and b/src/examples/images/penrose-tiling.tvg differ
index 6454608..c163afe 100644 (file)
Binary files a/src/examples/images/ranze-penguin.tvg and b/src/examples/images/ranze-penguin.tvg differ
index d07ae86..0c36768 100644 (file)
Binary files a/src/examples/images/rg1024_eggs.tvg and b/src/examples/images/rg1024_eggs.tvg differ
diff --git a/src/examples/images/rotlines.tvg b/src/examples/images/rotlines.tvg
new file mode 100644 (file)
index 0000000..d26bccb
Binary files /dev/null and b/src/examples/images/rotlines.tvg differ
index d822a90..f354a5b 100644 (file)
Binary files a/src/examples/images/test.tvg and b/src/examples/images/test.tvg differ
index 9c4151e..93f2224 100644 (file)
Binary files a/src/examples/images/yinyang.tvg and b/src/examples/images/yinyang.tvg differ
index 73e71dc..7b2f008 100644 (file)
 /* 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. */
@@ -243,6 +242,60 @@ void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransf
     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())
 {
@@ -255,10 +308,6 @@ Paint :: ~Paint()
 }
 
 
-/************************************************************************/
-/* External Class Implementation                                        */
-/************************************************************************/
-
 Result Paint::rotate(float degree) noexcept
 {
     if (pImpl->rotate(degree)) return Result::Success;
@@ -286,6 +335,7 @@ Result Paint::transform(const Matrix& m) noexcept
     return Result::FailedAllocation;
 }
 
+
 Matrix Paint::transform() noexcept
 {
     auto pTransform = pImpl->transform();
@@ -293,9 +343,16 @@ Matrix Paint::transform() noexcept
     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;
 }
 
index 4700413..f69499c 100644 (file)
@@ -42,7 +42,7 @@ namespace tvg
         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;
@@ -89,11 +89,6 @@ namespace tvg
             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);
@@ -122,6 +117,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);
         void* update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag);
         bool render(RenderMethod& renderer);
         Paint* duplicate();
@@ -136,7 +132,7 @@ namespace tvg
         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);
         }
index a00cde6..a1d6f5d 100644 (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);
 }
 
-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);
 }
index 02dc4b1..8a56f10 100644 (file)
@@ -167,7 +167,7 @@ struct Picture::Impl
         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;
index f935487..022d7af 100644 (file)
@@ -149,7 +149,7 @@ struct Scene::Impl
         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;
 
@@ -164,7 +164,7 @@ struct Scene::Impl
             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;
index 6f8aa9d..0170ad3 100644 (file)
@@ -246,7 +246,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float v
     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);
@@ -285,7 +285,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float v
     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);
index d0e8d51..7d77998 100644 (file)
@@ -697,7 +697,27 @@ TvgBinCounter TvgSaver::serialize(const Paint* paint, const Matrix* pTransform,
 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;
 }
 
@@ -736,7 +756,12 @@ bool TvgSaver::save(Paint* paint, const string& path, bool compress)
     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);