summaryrefslogtreecommitdiff
path: root/thirdparty/thorvg/src/lib
diff options
context:
space:
mode:
authorK. S. Ernest (iFire) Lee <ernest.lee@chibifire.com>2022-01-13 13:54:19 +0100
committerRémi Verschelde <rverschelde@gmail.com>2022-01-14 15:49:39 +0100
commit8d02759c720c3a91663e56979273feabad1dc051 (patch)
treeb939f28feb3224d4c4d2e39d12ef191157e2664b /thirdparty/thorvg/src/lib
parent9b3535a33a1dda541a3a39e7786b8428fadbff6c (diff)
Use ThorVG instead of NanoSVG for importing SVGs
ThorVG is a platform-independent portable library for drawing vector-based scene and animation. Co-authored-by: Rémi Verschelde <rverschelde@gmail.com>
Diffstat (limited to 'thirdparty/thorvg/src/lib')
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h359
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp333
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp128
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp502
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp156
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp1497
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h179
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h64
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h126
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h602
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h163
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp685
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h81
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp1043
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp664
-rw-r--r--thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp932
-rw-r--r--thirdparty/thorvg/src/lib/tvgAccessor.cpp84
-rw-r--r--thirdparty/thorvg/src/lib/tvgArray.h109
-rw-r--r--thirdparty/thorvg/src/lib/tvgBezier.cpp150
-rw-r--r--thirdparty/thorvg/src/lib/tvgBezier.h48
-rw-r--r--thirdparty/thorvg/src/lib/tvgBinaryDesc.h96
-rw-r--r--thirdparty/thorvg/src/lib/tvgCanvas.cpp73
-rw-r--r--thirdparty/thorvg/src/lib/tvgCanvasImpl.h142
-rw-r--r--thirdparty/thorvg/src/lib/tvgCommon.h76
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.cpp115
-rw-r--r--thirdparty/thorvg/src/lib/tvgFill.h88
-rw-r--r--thirdparty/thorvg/src/lib/tvgGlCanvas.cpp87
-rw-r--r--thirdparty/thorvg/src/lib/tvgInitializer.cpp152
-rw-r--r--thirdparty/thorvg/src/lib/tvgIteratorAccessor.h42
-rw-r--r--thirdparty/thorvg/src/lib/tvgLinearGradient.cpp99
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoadModule.h58
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoader.cpp213
-rw-r--r--thirdparty/thorvg/src/lib/tvgLoader.h36
-rw-r--r--thirdparty/thorvg/src/lib/tvgLzw.cpp428
-rw-r--r--thirdparty/thorvg/src/lib/tvgLzw.h31
-rw-r--r--thirdparty/thorvg/src/lib/tvgMath.h157
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.cpp407
-rw-r--r--thirdparty/thorvg/src/lib/tvgPaint.h200
-rw-r--r--thirdparty/thorvg/src/lib/tvgPicture.cpp121
-rw-r--r--thirdparty/thorvg/src/lib/tvgPictureImpl.h251
-rw-r--r--thirdparty/thorvg/src/lib/tvgRadialGradient.cpp97
-rw-r--r--thirdparty/thorvg/src/lib/tvgRender.cpp73
-rw-r--r--thirdparty/thorvg/src/lib/tvgRender.h113
-rw-r--r--thirdparty/thorvg/src/lib/tvgSaveModule.h41
-rw-r--r--thirdparty/thorvg/src/lib/tvgSaver.cpp140
-rw-r--r--thirdparty/thorvg/src/lib/tvgScene.cpp76
-rw-r--r--thirdparty/thorvg/src/lib/tvgSceneImpl.h217
-rw-r--r--thirdparty/thorvg/src/lib/tvgShape.cpp427
-rw-r--r--thirdparty/thorvg/src/lib/tvgShapeImpl.h392
-rw-r--r--thirdparty/thorvg/src/lib/tvgSwCanvas.cpp105
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp192
-rw-r--r--thirdparty/thorvg/src/lib/tvgTaskScheduler.h88
52 files changed, 12738 insertions, 0 deletions
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
new file mode 100644
index 0000000000..e0ffc1fb97
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SW_COMMON_H_
+#define _TVG_SW_COMMON_H_
+
+#include "tvgCommon.h"
+#include "tvgRender.h"
+
+#if 0
+#include <sys/time.h>
+static double timeStamp()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec + tv.tv_usec / 1000000.0);
+}
+#endif
+
+#define SW_CURVE_TYPE_POINT 0
+#define SW_CURVE_TYPE_CUBIC 1
+#define SW_ANGLE_PI (180L << 16)
+#define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
+#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
+#define SW_ANGLE_PI4 (SW_ANGLE_PI >> 2)
+
+using SwCoord = signed long;
+using SwFixed = signed long long;
+
+struct SwPoint
+{
+ SwCoord x, y;
+
+ SwPoint& operator+=(const SwPoint& rhs)
+ {
+ x += rhs.x;
+ y += rhs.y;
+ return *this;
+ }
+
+ SwPoint operator+(const SwPoint& rhs) const
+ {
+ return {x + rhs.x, y + rhs.y};
+ }
+
+ SwPoint operator-(const SwPoint& rhs) const
+ {
+ return {x - rhs.x, y - rhs.y};
+ }
+
+ bool operator==(const SwPoint& rhs) const
+ {
+ return (x == rhs.x && y == rhs.y);
+ }
+
+ bool operator!=(const SwPoint& rhs) const
+ {
+ return (x != rhs.x || y != rhs.y);
+ }
+
+ bool zero() const
+ {
+ if (x == 0 && y == 0) return true;
+ else return false;
+ }
+
+ bool small() const
+ {
+ //2 is epsilon...
+ if (abs(x) < 2 && abs(y) < 2) return true;
+ else return false;
+ }
+
+};
+
+struct SwSize
+{
+ SwCoord w, h;
+};
+
+struct SwOutline
+{
+ SwPoint* pts; //the outline's points
+ uint16_t ptsCnt; //number of points in the glyph
+ uint16_t reservedPtsCnt;
+ uint16_t* cntrs; //the contour end points
+ uint16_t cntrsCnt; //number of contours in glyph
+ uint16_t reservedCntrsCnt;
+ uint8_t* types; //curve type
+ bool* closed; //opened or closed path?
+ FillRule fillRule;
+};
+
+struct SwSpan
+{
+ uint16_t x, y;
+ uint16_t len;
+ uint8_t coverage;
+};
+
+struct SwRleData
+{
+ SwSpan *spans;
+ uint32_t alloc;
+ uint32_t size;
+};
+
+struct SwBBox
+{
+ SwPoint min, max;
+
+ void reset()
+ {
+ min.x = min.y = max.x = max.y = 0;
+ }
+};
+
+struct SwFill
+{
+ struct SwLinear {
+ float dx, dy;
+ float len;
+ float offset;
+ };
+
+ struct SwRadial {
+ float a11, a12, shiftX;
+ float a21, a22, shiftY;
+ float detSecDeriv;
+ float a;
+ };
+
+ union {
+ SwLinear linear;
+ SwRadial radial;
+ };
+
+ uint32_t* ctable;
+ FillSpread spread;
+
+ bool translucent;
+};
+
+struct SwStrokeBorder
+{
+ uint32_t ptsCnt;
+ uint32_t maxPts;
+ SwPoint* pts;
+ uint8_t* tags;
+ int32_t start; //index of current sub-path start point
+ bool movable; //true: for ends of lineto borders
+};
+
+struct SwStroke
+{
+ SwFixed angleIn;
+ SwFixed angleOut;
+ SwPoint center;
+ SwFixed lineLength;
+ SwFixed subPathAngle;
+ SwPoint ptStartSubPath;
+ SwFixed subPathLineLength;
+ SwFixed width;
+
+ StrokeCap cap;
+ StrokeJoin join;
+ StrokeJoin joinSaved;
+ SwFill* fill = nullptr;
+
+ SwStrokeBorder borders[2];
+
+ float sx, sy;
+
+ bool firstPt;
+ bool closedSubPath;
+ bool handleWideStrokes;
+};
+
+struct SwDashStroke
+{
+ SwOutline* outline;
+ float curLen;
+ int32_t curIdx;
+ Point ptStart;
+ Point ptCur;
+ float* pattern;
+ uint32_t cnt;
+ bool curOpGap;
+};
+
+struct SwShape
+{
+ SwOutline* outline = nullptr;
+ SwStroke* stroke = nullptr;
+ SwFill* fill = nullptr;
+ SwRleData* rle = nullptr;
+ SwRleData* strokeRle = nullptr;
+ SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling.
+
+ bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips?
+};
+
+struct SwImage
+{
+ SwOutline* outline = nullptr;
+ SwRleData* rle = nullptr;
+ uint32_t* data = nullptr;
+ uint32_t w, h, stride;
+ int32_t ox = 0; //offset x
+ int32_t oy = 0; //offset y
+ float scale;
+
+ bool direct = false; //draw image directly (with offset)
+ bool scaled = false; //draw scaled image
+};
+
+struct SwBlender
+{
+ uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+};
+
+struct SwCompositor;
+
+struct SwSurface : Surface
+{
+ SwBlender blender; //mandatory
+ SwCompositor* compositor = nullptr; //compositor (optional)
+};
+
+struct SwCompositor : Compositor
+{
+ SwSurface* recoverSfc; //Recover surface when composition is started
+ SwCompositor* recoverCmp; //Recover compositor when composition is done
+ SwImage image;
+ SwBBox bbox;
+ bool valid;
+};
+
+struct SwMpool
+{
+ SwOutline* outline;
+ SwOutline* strokeOutline;
+ unsigned allocSize;
+};
+
+static inline SwCoord TO_SWCOORD(float val)
+{
+ return SwCoord(val * 64.0f);
+}
+
+static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
+{
+ return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) +
+ ((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff));
+}
+
+static inline uint32_t INTERPOLATE(uint32_t a, uint32_t c0, uint32_t c1)
+{
+ return (((((((c0 >> 8) & 0xff00ff) - ((c1 >> 8) & 0xff00ff)) * a) + (c1 & 0xff00ff00)) & 0xff00ff00) + ((((((c0 & 0xff00ff) - (c1 & 0xff00ff)) * a) >> 8) + (c1 & 0xff00ff)) & 0xff00ff));
+}
+
+static inline SwCoord HALF_STROKE(float width)
+{
+ return TO_SWCOORD(width * 0.5f);
+}
+
+int64_t mathMultiply(int64_t a, int64_t b);
+int64_t mathDivide(int64_t a, int64_t b);
+int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
+void mathRotate(SwPoint& pt, SwFixed angle);
+SwFixed mathTan(SwFixed angle);
+SwFixed mathAtan(const SwPoint& pt);
+SwFixed mathCos(SwFixed angle);
+SwFixed mathSin(SwFixed angle);
+void mathSplitCubic(SwPoint* base);
+SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
+SwFixed mathLength(const SwPoint& pt);
+bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
+SwFixed mathMean(SwFixed angle1, SwFixed angle2);
+SwPoint mathTransform(const Point* to, const Matrix* transform);
+bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
+bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
+
+void shapeReset(SwShape* shape);
+bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
+bool shapePrepared(const SwShape* shape);
+bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias);
+void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
+void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
+bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
+void shapeFree(SwShape* shape);
+void shapeDelStroke(SwShape* shape);
+bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
+void shapeResetFill(SwShape* shape);
+void shapeResetStrokeFill(SwShape* shape);
+void shapeDelFill(SwShape* shape);
+void shapeDelStrokeFill(SwShape* shape);
+
+void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
+bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
+SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
+void strokeFree(SwStroke* stroke);
+
+bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
+bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
+void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
+void imageReset(SwImage* image);
+void imageFree(SwImage* image);
+
+bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
+void fillReset(SwFill* fill);
+void fillFree(SwFill* fill);
+void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
+void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
+
+SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
+void rleFree(SwRleData* rle);
+void rleReset(SwRleData* rle);
+void rleClipPath(SwRleData *rle, const SwRleData *clip);
+void rleClipRect(SwRleData *rle, const SwBBox* clip);
+
+SwMpool* mpoolInit(uint32_t threads);
+bool mpoolTerm(SwMpool* mpool);
+bool mpoolClear(SwMpool* mpool);
+SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx);
+void mpoolRetOutline(SwMpool* mpool, unsigned idx);
+SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx);
+void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
+
+bool rasterCompositor(SwSurface* surface);
+bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
+bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity);
+bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
+bool rasterClear(SwSurface* surface);
+void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
+void rasterUnpremultiply(SwSurface* surface);
+
+#endif /* _TVG_SW_COMMON_H_ */
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
new file mode 100644
index 0000000000..0bf051c17f
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgSwCommon.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+#define GRADIENT_STOP_SIZE 1024
+#define FIXPT_BITS 8
+#define FIXPT_SIZE (1<<FIXPT_BITS)
+
+
+static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint32_t opacity)
+{
+ if (!fill->ctable) {
+ fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
+ if (!fill->ctable) return false;
+ }
+
+ const Fill::ColorStop* colors;
+ auto cnt = fdata->colorStops(&colors);
+ if (cnt == 0 || !colors) return false;
+
+ auto pColors = colors;
+
+ auto a = (pColors->a * opacity) / 255;
+ if (a < 255) fill->translucent = true;
+
+ auto r = pColors->r;
+ auto g = pColors->g;
+ auto b = pColors->b;
+ auto rgba = surface->blender.join(r, g, b, a);
+
+ auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
+ auto pos = 1.5f * inc;
+ uint32_t i = 0;
+
+ fill->ctable[i++] = ALPHA_BLEND(rgba | 0xff000000, a);
+
+ while (pos <= pColors->offset) {
+ fill->ctable[i] = fill->ctable[i - 1];
+ ++i;
+ pos += inc;
+ }
+
+ for (uint32_t j = 0; j < cnt - 1; ++j) {
+ auto curr = colors + j;
+ auto next = curr + 1;
+ auto delta = 1.0f / (next->offset - curr->offset);
+ auto a2 = (next->a * opacity) / 255;
+ if (!fill->translucent && a2 < 255) fill->translucent = true;
+
+ auto rgba2 = surface->blender.join(next->r, next->g, next->b, a2);
+
+ while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
+ auto t = (pos - curr->offset) * delta;
+ auto dist = static_cast<int32_t>(255 * t);
+ auto dist2 = 255 - dist;
+
+ auto color = INTERPOLATE(dist2, rgba, rgba2);
+ fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
+
+ ++i;
+ pos += inc;
+ }
+ rgba = rgba2;
+ a = a2;
+ }
+ rgba = ALPHA_BLEND((rgba | 0xff000000), a);
+
+ for (; i < GRADIENT_STOP_SIZE; ++i)
+ fill->ctable[i] = rgba;
+
+ //Make sure the last color stop is represented at the end of the table
+ fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba;
+
+ return true;
+}
+
+
+bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* transform)
+{
+ float x1, x2, y1, y2;
+ if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
+
+ fill->linear.dx = x2 - x1;
+ fill->linear.dy = y2 - y1;
+ fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
+
+ if (fill->linear.len < FLT_EPSILON) return true;
+
+ fill->linear.dx /= fill->linear.len;
+ fill->linear.dy /= fill->linear.len;
+ fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
+
+ auto gradTransform = linear->transform();
+ bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
+
+ if (isTransformation) {
+ if (transform) gradTransform = mathMultiply(transform, &gradTransform);
+ } else if (transform) {
+ gradTransform = *transform;
+ isTransformation = true;
+ }
+
+ if (isTransformation) {
+ Matrix invTransform;
+ if (!mathInverse(&gradTransform, &invTransform)) return false;
+
+ fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23;
+
+ auto dx = fill->linear.dx;
+ fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
+ fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22;
+
+ fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
+ if (fill->linear.len < FLT_EPSILON) return true;
+ }
+
+ return true;
+}
+
+
+bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
+{
+ float radius, cx, cy;
+ if (radial->radial(&cx, &cy, &radius) != Result::Success) return false;
+ if (radius < FLT_EPSILON) return true;
+
+ float invR = 1.0f / radius;
+ fill->radial.shiftX = -cx;
+ fill->radial.shiftY = -cy;
+ fill->radial.a = radius;
+
+ auto gradTransform = radial->transform();
+ bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
+
+ if (isTransformation) {
+ if (transform) gradTransform = mathMultiply(transform, &gradTransform);
+ } else if (transform) {
+ gradTransform = *transform;
+ isTransformation = true;
+ }
+
+ if (isTransformation) {
+ Matrix invTransform;
+ if (!mathInverse(&gradTransform, &invTransform)) return false;
+
+ fill->radial.a11 = invTransform.e11 * invR;
+ fill->radial.a12 = invTransform.e12 * invR;
+ fill->radial.shiftX += invTransform.e13;
+ fill->radial.a21 = invTransform.e21 * invR;
+ fill->radial.a22 = invTransform.e22 * invR;
+ fill->radial.shiftY += invTransform.e23;
+ fill->radial.detSecDeriv = 2.0f * fill->radial.a11 * fill->radial.a11 + 2 * fill->radial.a21 * fill->radial.a21;
+
+ fill->radial.a *= sqrt(pow(invTransform.e11, 2) + pow(invTransform.e21, 2));
+ } else {
+ fill->radial.a11 = fill->radial.a22 = invR;
+ fill->radial.a12 = fill->radial.a21 = 0.0f;
+ fill->radial.detSecDeriv = 2.0f * invR * invR;
+ }
+ fill->radial.shiftX *= invR;
+ fill->radial.shiftY *= invR;
+
+ return true;
+}
+
+
+static inline uint32_t _clamp(const SwFill* fill, int32_t pos)
+{
+ switch (fill->spread) {
+ case FillSpread::Pad: {
+ if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1;
+ else if (pos < 0) pos = 0;
+ break;
+ }
+ case FillSpread::Repeat: {
+ pos = pos % GRADIENT_STOP_SIZE;
+ if (pos < 0) pos = GRADIENT_STOP_SIZE + pos;
+ break;
+ }
+ case FillSpread::Reflect: {
+ auto limit = GRADIENT_STOP_SIZE * 2;
+ pos = pos % limit;
+ if (pos < 0) pos = limit + pos;
+ if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1);
+ break;
+ }
+ }
+ return pos;
+}
+
+
+static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos)
+{
+ int32_t i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
+ return fill->ctable[_clamp(fill, i)];
+}
+
+
+static inline uint32_t _pixel(const SwFill* fill, float pos)
+{
+ auto i = static_cast<int32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f);
+ return fill->ctable[_clamp(fill, i)];
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
+{
+ auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
+ auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
+
+ // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
+ auto detSecondDerivative = fill->radial.detSecDeriv;
+ // detFirstDerivative = d(det)/dx
+ auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
+ auto det = rx * rx + ry * ry;
+
+ for (uint32_t i = 0 ; i < len ; ++i) {
+ *dst = _pixel(fill, sqrtf(det));
+ ++dst;
+ det += detFirstDerivative;
+ detFirstDerivative += detSecondDerivative;
+ }
+}
+
+
+void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
+{
+ //Rotation
+ float rx = x + 0.5f;
+ float ry = y + 0.5f;
+ float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
+ float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
+
+ if (mathZero(inc)) {
+ auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
+ rasterRGBA32(dst, color, 0, len);
+ return;
+ }
+
+ auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
+ auto vMin = -vMax;
+ auto v = t + (inc * len);
+
+ //we can use fixed point math
+ if (v < vMax && v > vMin) {
+ auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
+ auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
+ for (uint32_t j = 0; j < len; ++j) {
+ *dst = _fixedPixel(fill, t2);
+ ++dst;
+ t2 += inc2;
+ }
+ //we have to fallback to float math
+ } else {
+ uint32_t counter = 0;
+ while (counter++ < len) {
+ *dst = _pixel(fill, t / GRADIENT_STOP_SIZE);
+ ++dst;
+ t += inc;
+ }
+ }
+}
+
+
+bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+{
+ if (!fill) return false;
+
+ fill->spread = fdata->spread();
+
+ if (ctable) {
+ if (!_updateColorTable(fill, fdata, surface, opacity)) return false;
+ }
+
+ if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
+ return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform);
+ } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
+ return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform);
+ }
+
+ //LOG: What type of gradient?!
+
+ return false;
+}
+
+
+void fillReset(SwFill* fill)
+{
+ if (fill->ctable) {
+ free(fill->ctable);
+ fill->ctable = nullptr;
+ }
+ fill->translucent = false;
+}
+
+
+void fillFree(SwFill* fill)
+{
+ if (!fill) return;
+
+ if (fill->ctable) free(fill->ctable);
+
+ free(fill);
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
new file mode 100644
index 0000000000..fe22fce017
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgSwCommon.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static inline bool _onlyShifted(const Matrix* m)
+{
+ if (mathEqual(m->e11, 1.0f) && mathEqual(m->e22, 1.0f) && mathZero(m->e12) && mathZero(m->e21)) return true;
+ return false;
+}
+
+
+static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, unsigned tid)
+{
+ image->outline = mpoolReqOutline(mpool, tid);
+ auto outline = image->outline;
+
+ if (outline->reservedPtsCnt < 5) {
+ outline->reservedPtsCnt = 5;
+ outline->pts = static_cast<SwPoint*>(realloc(outline->pts, outline->reservedPtsCnt * sizeof(SwPoint)));
+ outline->types = static_cast<uint8_t*>(realloc(outline->types, outline->reservedPtsCnt * sizeof(uint8_t)));
+ }
+
+ if (outline->reservedCntrsCnt < 1) {
+ outline->reservedCntrsCnt = 1;
+ outline->cntrs = static_cast<uint16_t*>(realloc(outline->cntrs, outline->reservedCntrsCnt * sizeof(uint16_t)));
+ outline->closed = static_cast<bool*>(realloc(outline->closed, outline->reservedCntrsCnt * sizeof(bool)));
+ outline->closed[0] = true;
+ }
+
+ auto w = static_cast<float>(image->w);
+ auto h = static_cast<float>(image->h);
+
+ Point to[4] = {{0 ,0}, {w, 0}, {w, h}, {0, h}};
+ for (int i = 0; i < 4; i++) {
+ outline->pts[outline->ptsCnt] = mathTransform(&to[i], transform);
+ outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline->ptsCnt;
+ }
+
+ outline->pts[outline->ptsCnt] = outline->pts[0];
+ outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline->ptsCnt;
+
+ outline->cntrs[outline->cntrsCnt] = outline->ptsCnt - 1;
+ ++outline->cntrsCnt;
+
+ image->outline = outline;
+
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
+{
+ image->direct = _onlyShifted(transform);
+
+ //Fast track: Non-transformed image but just shifted.
+ if (image->direct) {
+ image->ox = -static_cast<uint32_t>(round(transform->e13));
+ image->oy = -static_cast<uint32_t>(round(transform->e23));
+ //Figure out the scale factor by transform matrix
+ } else {
+ auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21));
+ auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12));
+ image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
+
+ if (mathZero(transform->e12) && mathZero(transform->e21)) image->scaled = true;
+ else image->scaled = false;
+ }
+
+ if (!_genOutline(image, transform, mpool, tid)) return false;
+ return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
+}
+
+
+bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias)
+{
+ if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
+
+ return false;
+}
+
+
+void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid)
+{
+ mpoolRetOutline(mpool, tid);
+ image->outline = nullptr;
+}
+
+
+void imageReset(SwImage* image)
+{
+ rleReset(image->rle);
+}
+
+
+void imageFree(SwImage* image)
+{
+ rleFree(image->rle);
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp
new file mode 100644
index 0000000000..7a3529bd69
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <math.h>
+#include "tvgSwCommon.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+//clz: count leading zero’s
+#if defined(_MSC_VER) && !defined(__clang__)
+ #include <intrin.h>
+ static uint32_t __inline _clz(uint32_t value)
+ {
+ unsigned long leadingZero = 0;
+ if (_BitScanReverse(&leadingZero, value)) return 31 - leadingZero;
+ else return 32;
+ }
+#else
+ #define _clz(x) __builtin_clz((x))
+#endif
+
+
+constexpr SwFixed CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
+
+//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
+constexpr static auto ATAN_MAX = 23;
+constexpr static SwFixed ATAN_TBL[] = {
+ 1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
+ 14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
+ 57L, 29L, 14L, 7L, 4L, 2L, 1L};
+
+static inline SwCoord SATURATE(const SwCoord x)
+{
+ return (x >> (sizeof(SwCoord) * 8 - 1));
+}
+
+
+static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
+{
+ return (((x) + ((n)/2)) & ~((n)-1));
+}
+
+
+static SwCoord _downscale(SwFixed x)
+{
+ //multiply a give value by the CORDIC shrink factor
+ auto s = abs(x);
+ int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
+ s = static_cast<SwFixed>(t >> 32);
+ if (x < 0) s = -s;
+ return s;
+}
+
+
+static int32_t _normalize(SwPoint& pt)
+{
+ /* the highest bit in overflow-safe vector components
+ MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
+ constexpr auto SAFE_MSB = 29;
+
+ auto v = pt;
+
+ //High order bit(MSB)
+ int32_t shift = 31 - _clz(abs(v.x) | abs(v.y));
+
+ if (shift <= SAFE_MSB) {
+ shift = SAFE_MSB - shift;
+ pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
+ pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
+ } else {
+ shift -= SAFE_MSB;
+ pt.x = v.x >> shift;
+ pt.y = v.y >> shift;
+ shift = -shift;
+ }
+ return shift;
+}
+
+
+static void _polarize(SwPoint& pt)
+{
+ auto v = pt;
+ SwFixed theta;
+
+ //Get the vector into [-PI/4, PI/4] sector
+ if (v.y > v.x) {
+ if (v.y > -v.x) {
+ auto tmp = v.y;
+ v.y = -v.x;
+ v.x = tmp;
+ theta = SW_ANGLE_PI2;
+ } else {
+ theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
+ v.x = -v.x;
+ v.y = -v.y;
+ }
+ } else {
+ if (v.y < -v.x) {
+ theta = -SW_ANGLE_PI2;
+ auto tmp = -v.y;
+ v.y = v.x;
+ v.x = tmp;
+ } else {
+ theta = 0;
+ }
+ }
+
+ auto atan = ATAN_TBL;
+ uint32_t i;
+ SwFixed j;
+
+ //Pseudorotations. with right shifts
+ for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
+ if (v.y > 0) {
+ auto tmp = v.x + ((v.y + j) >> i);
+ v.y = v.y - ((v.x + j) >> i);
+ v.x = tmp;
+ theta += *atan++;
+ } else {
+ auto tmp = v.x - ((v.y + j) >> i);
+ v.y = v.y + ((v.x + j) >> i);
+ v.x = tmp;
+ theta -= *atan++;
+ }
+ }
+
+ //round theta
+ if (theta >= 0) theta = PAD_ROUND(theta, 32);
+ else theta = -PAD_ROUND(-theta, 32);
+
+ pt.x = v.x;
+ pt.y = theta;
+}
+
+
+static void _rotate(SwPoint& pt, SwFixed theta)
+{
+ SwFixed x = pt.x;
+ SwFixed y = pt.y;
+
+ //Rotate inside [-PI/4, PI/4] sector
+ while (theta < -SW_ANGLE_PI4) {
+ auto tmp = y;
+ y = -x;
+ x = tmp;
+ theta += SW_ANGLE_PI2;
+ }
+
+ while (theta > SW_ANGLE_PI4) {
+ auto tmp = -y;
+ y = x;
+ x = tmp;
+ theta -= SW_ANGLE_PI2;
+ }
+
+ auto atan = ATAN_TBL;
+ uint32_t i;
+ SwFixed j;
+
+ for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
+ if (theta < 0) {
+ auto tmp = x + ((y + j) >> i);
+ y = y - ((x + j) >> i);
+ x = tmp;
+ theta += *atan++;
+ } else {
+ auto tmp = x - ((y + j) >> i);
+ y = y + ((x + j) >> i);
+ x = tmp;
+ theta -= *atan++;
+ }
+ }
+
+ pt.x = static_cast<SwCoord>(x);
+ pt.y = static_cast<SwCoord>(y);
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SwFixed mathMean(SwFixed angle1, SwFixed angle2)
+{
+ return angle1 + mathDiff(angle1, angle2) / 2;
+}
+
+
+bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
+{
+ auto d1 = base[2] - base[3];
+ auto d2 = base[1] - base[2];
+ auto d3 = base[0] - base[1];
+
+ if (d1.small()) {
+ if (d2.small()) {
+ if (d3.small()) {
+ //basically a point.
+ //do nothing to retain original direction
+ } else {
+ angleIn = angleMid = angleOut = mathAtan(d3);
+ }
+ } else {
+ if (d3.small()) {
+ angleIn = angleMid = angleOut = mathAtan(d2);
+ } else {
+ angleIn = angleMid = mathAtan(d2);
+ angleOut = mathAtan(d3);
+ }
+ }
+ } else {
+ if (d2.small()) {
+ if (d3.small()) {
+ angleIn = angleMid = angleOut = mathAtan(d1);
+ } else {
+ angleIn = mathAtan(d1);
+ angleOut = mathAtan(d3);
+ angleMid = mathMean(angleIn, angleOut);
+ }
+ } else {
+ if (d3.small()) {
+ angleIn = mathAtan(d1);
+ angleMid = angleOut = mathAtan(d2);
+ } else {
+ angleIn = mathAtan(d1);
+ angleMid = mathAtan(d2);
+ angleOut = mathAtan(d3);
+ }
+ }
+ }
+
+ auto theta1 = abs(mathDiff(angleIn, angleMid));
+ auto theta2 = abs(mathDiff(angleMid, angleOut));
+
+ if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true;
+ return false;
+}
+
+
+int64_t mathMultiply(int64_t a, int64_t b)
+{
+ int32_t s = 1;
+
+ //move sign
+ if (a < 0) {
+ a = -a;
+ s = -s;
+ }
+ if (b < 0) {
+ b = -b;
+ s = -s;
+ }
+ int64_t c = (a * b + 0x8000L) >> 16;
+ return (s > 0) ? c : -c;
+}
+
+
+int64_t mathDivide(int64_t a, int64_t b)
+{
+ int32_t s = 1;
+
+ //move sign
+ if (a < 0) {
+ a = -a;
+ s = -s;
+ }
+ if (b < 0) {
+ b = -b;
+ s = -s;
+ }
+ int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL;
+ return (s < 0 ? -q : q);
+}
+
+
+int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
+{
+ int32_t s = 1;
+
+ //move sign
+ if (a < 0) {
+ a = -a;
+ s = -s;
+ }
+ if (b < 0) {
+ b = -b;
+ s = -s;
+ }
+ if (c < 0) {
+ c = -c;
+ s = -s;
+ }
+ int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL;
+
+ return (s > 0 ? d : -d);
+}
+
+
+void mathRotate(SwPoint& pt, SwFixed angle)
+{
+ if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
+
+ auto v = pt;
+ auto shift = _normalize(v);
+
+ auto theta = angle;
+ _rotate(v, theta);
+
+ v.x = _downscale(v.x);
+ v.y = _downscale(v.y);
+
+ if (shift > 0) {
+ auto half = static_cast<int32_t>(1L << (shift - 1));
+ pt.x = (v.x + half + SATURATE(v.x)) >> shift;
+ pt.y = (v.y + half + SATURATE(v.y)) >> shift;
+ } else {
+ shift = -shift;
+ pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
+ pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
+ }
+}
+
+SwFixed mathTan(SwFixed angle)
+{
+ SwPoint v = {CORDIC_FACTOR >> 8, 0};
+ _rotate(v, angle);
+ return mathDivide(v.y, v.x);
+}
+
+
+SwFixed mathAtan(const SwPoint& pt)
+{
+ if (pt.x == 0 && pt.y == 0) return 0;
+
+ auto v = pt;
+ _normalize(v);
+ _polarize(v);
+
+ return v.y;
+}
+
+
+SwFixed mathSin(SwFixed angle)
+{
+ return mathCos(SW_ANGLE_PI2 - angle);
+}
+
+
+SwFixed mathCos(SwFixed angle)
+{
+ SwPoint v = {CORDIC_FACTOR >> 8, 0};
+ _rotate(v, angle);
+ return (v.x + 0x80L) >> 8;
+}
+
+
+SwFixed mathLength(const SwPoint& pt)
+{
+ auto v = pt;
+
+ //trivial case
+ if (v.x == 0) return abs(v.y);
+ if (v.y == 0) return abs(v.x);
+
+ //general case
+ auto shift = _normalize(v);
+ _polarize(v);
+ v.x = _downscale(v.x);
+
+ if (shift > 0) return (v.x + (static_cast<SwFixed>(1) << (shift -1))) >> shift;
+ return static_cast<SwFixed>((uint32_t)v.x << -shift);
+}
+
+
+void mathSplitCubic(SwPoint* base)
+{
+ SwCoord a, b, c, d;
+
+ base[6].x = base[3].x;
+ c = base[1].x;
+ d = base[2].x;
+ base[1].x = a = (base[0].x + c) / 2;
+ base[5].x = b = (base[3].x + d) / 2;
+ c = (c + d) / 2;
+ base[2].x = a = (a + c) / 2;
+ base[4].x = b = (b + c) / 2;
+ base[3].x = (a + b) / 2;
+
+ base[6].y = base[3].y;
+ c = base[1].y;
+ d = base[2].y;
+ base[1].y = a = (base[0].y + c) / 2;
+ base[5].y = b = (base[3].y + d) / 2;
+ c = (c + d) / 2;
+ base[2].y = a = (a + c) / 2;
+ base[4].y = b = (b + c) / 2;
+ base[3].y = (a + b) / 2;
+}
+
+
+SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
+{
+ auto delta = angle2 - angle1;
+
+ delta %= SW_ANGLE_2PI;
+ if (delta < 0) delta += SW_ANGLE_2PI;
+ if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI;
+
+ return delta;
+}
+
+
+SwPoint mathTransform(const Point* to, const Matrix* transform)
+{
+ if (!transform) return {TO_SWCOORD(to->x), TO_SWCOORD(to->y)};
+
+ auto tx = to->x * transform->e11 + to->y * transform->e12 + transform->e13;
+ auto ty = to->x * transform->e21 + to->y * transform->e22 + transform->e23;
+
+ return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
+}
+
+
+bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee)
+{
+ clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x;
+ clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y;
+ clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x;
+ clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y;
+
+ //Check valid region
+ if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false;
+
+ //Check boundary
+ if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y ||
+ clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false;
+
+ return true;
+}
+
+
+bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack)
+{
+ if (!outline) return false;
+
+ auto pt = outline->pts;
+
+ if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) {
+ renderRegion.reset();
+ return false;
+ }
+
+ auto xMin = pt->x;
+ auto xMax = pt->x;
+ auto yMin = pt->y;
+ auto yMax = pt->y;
+
+ ++pt;
+
+ for (uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) {
+ if (xMin > pt->x) xMin = pt->x;
+ if (xMax < pt->x) xMax = pt->x;
+ if (yMin > pt->y) yMin = pt->y;
+ if (yMax < pt->y) yMax = pt->y;
+ }
+ //Since no antialiasing is applied in the Fast Track case,
+ //the rasterization region has to be rearranged.
+ //https://github.com/Samsung/thorvg/issues/916
+ if (fastTrack) {
+ renderRegion.min.x = static_cast<SwCoord>(round(xMin / 64.0f));
+ renderRegion.max.x = static_cast<SwCoord>(round(xMax / 64.0f));
+ renderRegion.min.y = static_cast<SwCoord>(round(yMin / 64.0f));
+ renderRegion.max.y = static_cast<SwCoord>(round(yMax / 64.0f));
+ } else {
+ renderRegion.min.x = xMin >> 6;
+ renderRegion.max.x = (xMax + 63) >> 6;
+ renderRegion.min.y = yMin >> 6;
+ renderRegion.max.y = (yMax + 63) >> 6;
+ }
+ return mathClipBBox(clipRegion, renderRegion);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp
new file mode 100644
index 0000000000..a44be85bbb
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgSwCommon.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx)
+{
+ return &mpool->outline[idx];
+}
+
+
+void mpoolRetOutline(SwMpool* mpool, unsigned idx)
+{
+ mpool->outline[idx].cntrsCnt = 0;
+ mpool->outline[idx].ptsCnt = 0;
+}
+
+
+SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx)
+{
+ return &mpool->strokeOutline[idx];
+}
+
+
+void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx)
+{
+ mpool->strokeOutline[idx].cntrsCnt = 0;
+ mpool->strokeOutline[idx].ptsCnt = 0;
+}
+
+
+SwMpool* mpoolInit(unsigned threads)
+{
+ if (threads == 0) threads = 1;
+
+ auto mpool = static_cast<SwMpool*>(calloc(sizeof(SwMpool), 1));
+ mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
+ if (!mpool->outline) goto err;
+
+ mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
+ if (!mpool->strokeOutline) goto err;
+
+ mpool->allocSize = threads;
+
+ return mpool;
+
+err:
+ if (mpool->outline) {
+ free(mpool->outline);
+ mpool->outline = nullptr;
+ }
+
+ if (mpool->strokeOutline) {
+ free(mpool->strokeOutline);
+ mpool->strokeOutline = nullptr;
+ }
+ free(mpool);
+ return nullptr;
+}
+
+
+bool mpoolClear(SwMpool* mpool)
+{
+ SwOutline* p;
+
+ for (unsigned i = 0; i < mpool->allocSize; ++i) {
+
+ //Outline
+ p = &mpool->outline[i];
+
+ free(p->cntrs);
+ p->cntrs = nullptr;
+
+ free(p->pts);
+ p->pts = nullptr;
+
+ free(p->types);
+ p->types = nullptr;
+
+ free(p->closed);
+ p->closed = nullptr;
+
+ p->cntrsCnt = p->reservedCntrsCnt = 0;
+ p->ptsCnt = p->reservedPtsCnt = 0;
+
+ //StrokeOutline
+ p = &mpool->strokeOutline[i];
+
+ free(p->cntrs);
+ p->cntrs = nullptr;
+
+ free(p->pts);
+ p->pts = nullptr;
+
+ free(p->types);
+ p->types = nullptr;
+
+ free(p->closed);
+ p->closed = nullptr;
+
+ p->cntrsCnt = p->reservedCntrsCnt = 0;
+ p->ptsCnt = p->reservedPtsCnt = 0;
+ }
+
+ return true;
+}
+
+
+bool mpoolTerm(SwMpool* mpool)
+{
+ if (!mpool) return false;
+
+ mpoolClear(mpool);
+
+ if (mpool->outline) {
+ free(mpool->outline);
+ mpool->outline = nullptr;
+ }
+
+ if (mpool->strokeOutline) {
+ free(mpool->strokeOutline);
+ mpool->strokeOutline = nullptr;
+ }
+
+ free(mpool);
+
+ return true;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
new file mode 100644
index 0000000000..deebed16ee
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp
@@ -0,0 +1,1497 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgRender.h"
+#include "tvgSwCommon.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+constexpr auto DOWN_SCALE_TOLERANCE = 0.5f;
+
+
+static inline uint32_t _multiplyAlpha(uint32_t c, uint32_t a)
+{
+ return ((c * a + 0xff) >> 8);
+}
+
+
+static inline uint32_t _alpha(uint32_t c)
+{
+ return (c >> 24);
+}
+
+
+static inline uint32_t _ialpha(uint32_t c)
+{
+ return (~c >> 24);
+}
+
+
+static inline uint32_t _abgrJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ return (a << 24 | b << 16 | g << 8 | r);
+}
+
+
+static inline uint32_t _argbJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ return (a << 24 | r << 16 | g << 8 | b);
+}
+
+
+#include "tvgSwRasterTexmap.h"
+#include "tvgSwRasterC.h"
+#include "tvgSwRasterAvx.h"
+#include "tvgSwRasterNeon.h"
+
+
+static inline bool _compositing(const SwSurface* surface)
+{
+ if (!surface->compositor || surface->compositor->method == CompositeMethod::None) return false;
+ return true;
+}
+
+
+static inline uint32_t _halfScale(float scale)
+{
+ auto halfScale = static_cast<uint32_t>(0.5f / scale);
+ if (halfScale == 0) halfScale = 1;
+ return halfScale;
+}
+
+//Bilinear Interpolation
+static uint32_t _interpUpScaler(const uint32_t *img, uint32_t w, uint32_t h, float sx, float sy)
+{
+ auto rx = (uint32_t)(sx);
+ auto ry = (uint32_t)(sy);
+ auto rx2 = rx + 1;
+ if (rx2 >= w) rx2 = w - 1;
+ auto ry2 = ry + 1;
+ if (ry2 >= h) ry2 = h - 1;
+
+ auto dx = static_cast<uint32_t>((sx - rx) * 255.0f);
+ auto dy = static_cast<uint32_t>((sy - ry) * 255.0f);
+
+ auto c1 = img[rx + ry * w];
+ auto c2 = img[rx2 + ry * w];
+ auto c3 = img[rx2 + ry2 * w];
+ auto c4 = img[rx + ry2 * w];
+
+ return INTERPOLATE(dy, INTERPOLATE(dx, c3, c4), INTERPOLATE(dx, c2, c1));
+}
+
+
+//2n x 2n Mean Kernel
+static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, uint32_t rx, uint32_t ry, uint32_t n)
+{
+ uint32_t c[4] = {0, 0, 0, 0};
+ auto n2 = n * n;
+ auto src = img + rx - n + (ry - n) * stride;
+
+ for (auto y = ry - n; y < ry + n; ++y) {
+ if (y >= h) continue;
+ auto p = src;
+ for (auto x = rx - n; x < rx + n; ++x, ++p) {
+ if (x >= w) continue;
+ c[0] += *p >> 24;
+ c[1] += (*p >> 16) & 0xff;
+ c[2] += (*p >> 8) & 0xff;
+ c[3] += *p & 0xff;
+ }
+ src += stride;
+ }
+ for (auto i = 0; i < 4; ++i) {
+ c[i] = (c[i] >> 2) / n2;
+ }
+ return (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
+}
+
+
+/************************************************************************/
+/* Rect */
+/************************************************************************/
+
+static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint32_t color, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Masked Rect");
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ auto cmp = &cbuffer[y * surface->stride];
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp) {
+ auto tmp = ALPHA_BLEND(color, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+{
+ auto buffer = surface->buffer + (region.min.y * surface->stride);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ rasterRGBA32(buffer + y * surface->stride, color, region.min.x, w);
+ }
+ return true;
+}
+
+
+static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint32_t color, uint8_t opacity)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterMaskedRect(surface, region, color, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterMaskedRect(surface, region, color, _ialpha);
+ }
+ } else {
+ if (opacity == 255) {
+ return _rasterSolidRect(surface, region, color);
+ } else {
+#if defined(THORVG_AVX_VECTOR_SUPPORT)
+ return avxRasterTranslucentRect(surface, region, color);
+#elif defined(THORVG_NEON_VECTOR_SUPPORT)
+ return neonRasterTranslucentRect(surface, region, color);
+#else
+ return cRasterTranslucentRect(surface, region, color);
+#endif
+ }
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Rle */
+/************************************************************************/
+
+static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Masked Rle");
+
+ auto span = rle->spans;
+ uint32_t src;
+ auto cbuffer = surface->compositor->image.data;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ if (span->coverage == 255) src = color;
+ else src = ALPHA_BLEND(color, span->coverage);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp) {
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+{
+ auto span = rle->spans;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ if (span->coverage == 255) {
+ rasterRGBA32(surface->buffer + span->y * surface->stride, color, span->x, span->len);
+ } else {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto src = ALPHA_BLEND(color, span->coverage);
+ auto ialpha = 255 - span->coverage;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = src + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint8_t opacity)
+{
+ if (!rle) return false;
+
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterMaskedRle(surface, rle, color, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterMaskedRle(surface, rle, color, _ialpha);
+ }
+ } else {
+ if (opacity == 255) {
+ return _rasterSolidRle(surface, rle, color);
+ } else {
+#if defined(THORVG_AVX_VECTOR_SUPPORT)
+ return avxRasterTranslucentRle(surface, rle, color);
+#elif defined(THORVG_NEON_VECTOR_SUPPORT)
+ return neonRasterTranslucentRle(surface, rle, color);
+#else
+ return cRasterTranslucentRle(surface, rle, color);
+#endif
+ }
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* RLE Transformed RGBA Image */
+/************************************************************************/
+
+static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, uint32_t opacity)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _ialpha);
+ }
+ } else {
+ return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, nullptr);
+ }
+ return false;
+}
+
+/************************************************************************/
+/* RLE Scaled RGBA Image */
+/************************************************************************/
+
+static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Scaled Masked Translucent Rle Image");
+
+ auto span = image->rle->spans;
+
+ //Center (Down-Scaled)
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ //Center (Up-Scaled)
+ } else {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha);
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Scaled Masked Rle Image");
+
+ auto span = image->rle->spans;
+
+ //Center (Down-Scaled)
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ if (span->coverage == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto tmp = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage);
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ //Center (Up-Scaled)
+ } else {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &surface->compositor->image.data[span->y * surface->compositor->image.stride + span->x];
+ if (span->coverage == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto tmp = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage);
+ auto tmp = ALPHA_BLEND(src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale)
+{
+ auto span = image->rle->spans;
+
+ //Center (Down-Scaled)
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ //Center (Up-Scaled)
+ } else {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale)
+{
+ auto span = image->rle->spans;
+
+ //Center (Down-Scaled)
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ if (span->coverage == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ //Center (Up-Scaled)
+ } else {
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto sy = span->y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ if (span->coverage == 255) {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ } else {
+ for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), span->coverage);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
+{
+ Matrix itransform;
+ if (transform && !mathInverse(transform, &itransform)) return false;
+
+ auto halfScale = _halfScale(image->scale);
+
+ if (_compositing(surface)) {
+ if (opacity == 255) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _ialpha);
+ }
+ } else {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha);
+ }
+ }
+ } else {
+ if (opacity == 255) return _rasterScaledRleRGBAImage(surface, image, &itransform, region, opacity, halfScale);
+ else return _rasterScaledTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* RLE Direct RGBA Image */
+/************************************************************************/
+
+static bool _rasterDirectMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Direct Masked Rle Image");
+
+ auto span = image->rle->spans;
+ auto cbuffer = surface->compositor->image.data;
+
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ if (alpha == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
+ auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
+ auto tmp = ALPHA_BLEND(*img, _multiplyAlpha(alpha, blendMethod(*cmp)));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterDirectMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Direct Masked Rle Image");
+
+ auto span = image->rle->spans;
+ auto cbuffer = surface->compositor->image.data;
+
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
+ auto tmp = ALPHA_BLEND(*img, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++img) {
+ auto tmp = ALPHA_BLEND(*img, _multiplyAlpha(span->coverage, blendMethod(*cmp)));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterDirectTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity)
+{
+ auto span = image->rle->spans;
+
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ auto alpha = _multiplyAlpha(span->coverage, opacity);
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
+ auto src = ALPHA_BLEND(*img, alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterDirectRleRGBAImage(SwSurface* surface, const SwImage* image)
+{
+ auto span = image->rle->spans;
+
+ for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto img = image->data + (span->y + image->oy) * image->stride + (span->x + image->ox);
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
+ *dst = *img + ALPHA_BLEND(*dst, _ialpha(*img));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
+ auto src = ALPHA_BLEND(*img, span->coverage);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity)
+{
+ if (_compositing(surface)) {
+ if (opacity == 255) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterDirectMaskedRleRGBAImage(surface, image, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterDirectMaskedRleRGBAImage(surface, image, _ialpha);
+ }
+ } else {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _ialpha);
+ }
+ }
+ } else {
+ if (opacity == 255) return _rasterDirectRleRGBAImage(surface, image);
+ else return _rasterDirectTranslucentRleRGBAImage(surface, image, opacity);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Transformed RGBA Image */
+/************************************************************************/
+
+static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterTexmapPolygon(surface, image, transform, &region, opacity, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterTexmapPolygon(surface, image, transform, &region, opacity, _ialpha);
+ }
+ } else {
+ return _rasterTexmapPolygon(surface, image, transform, &region, opacity, nullptr);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/*Scaled RGBA Image */
+/************************************************************************/
+
+
+static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Scaled Masked Image");
+
+ auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride + region.min.x);
+
+ // Down-Scaled
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = (uint32_t)(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ auto cmp = cbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ dbuffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ }
+ // Up-Scaled
+ } else {
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = dbuffer;
+ auto cmp = cbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto alpha = _multiplyAlpha(opacity, blendMethod(*cmp));
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), alpha);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ dbuffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Scaled Masked Image");
+
+ auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride + region.min.x);
+
+ // Down-Scaled
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = (uint32_t)(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ auto cmp = cbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), blendMethod(*cmp));
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ dbuffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ }
+ // Up-Scaled
+ } else {
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto sy = y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = dbuffer;
+ auto cmp = cbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++cmp) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), blendMethod(*cmp));
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ dbuffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale)
+{
+ auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+
+ // Down-Scaled
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
+ auto sy = (uint32_t)(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale), opacity);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ // Up-Scaled
+ } else {
+ for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
+ auto sy = fabsf(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = ALPHA_BLEND(_interpUpScaler(image->data, image->w, image->h, sx, sy), opacity);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale)
+{
+ auto dbuffer = surface->buffer + (region.min.y * surface->stride + region.min.x);
+
+ // Down-Scaled
+ if (image->scale < DOWN_SCALE_TOLERANCE) {
+ for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
+ auto sy = (uint32_t)(y * itransform->e22 + itransform->e23);
+ if (sy >= image->h) continue;
+ auto dst = dbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+ auto sx = (uint32_t)(x * itransform->e11 + itransform->e13);
+ if (sx >= image->w) continue;
+ auto src = _interpDownScaler(image->data, image->stride, image->w, image->h, sx, sy, halfScale);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ // Up-Scaled
+ } else {
+ for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
+ auto sy = y * itransform->e22 + itransform->e23;
+ if ((uint32_t)sy >= image->h) continue;
+ auto dst = dbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
+ auto sx = x * itransform->e11 + itransform->e13;
+ if ((uint32_t)sx >= image->w) continue;
+ auto src = _interpUpScaler(image->data, image->w, image->h, sx, sy);
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
+{
+ Matrix itransform;
+ if (transform && !mathInverse(transform, &itransform)) return false;
+
+ auto halfScale = _halfScale(image->scale);
+
+ if (_compositing(surface)) {
+ if (opacity == 255) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _ialpha);
+ }
+ } else {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha);
+ }
+ }
+ } else {
+ if (opacity == 255) return _rasterScaledRGBAImage(surface, image, &itransform, region, halfScale);
+ else return _rasterScaledTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Direct RGBA Image */
+/************************************************************************/
+
+static bool _rasterDirectMaskedRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Direct Masked Image");
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h2 = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w2 = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer
+
+ for (uint32_t y = 0; y < h2; ++y) {
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ buffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ sbuffer += image->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+{
+ TVGLOG("SW_ENGINE", "Direct Masked Translucent Image");
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h2 = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w2 = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer
+
+ for (uint32_t y = 0; y < h2; ++y) {
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, ++cmp) {
+ auto tmp = ALPHA_BLEND(*src, _multiplyAlpha(opacity, blendMethod(*cmp)));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ buffer += surface->stride;
+ cbuffer += surface->compositor->image.stride;
+ sbuffer += image->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterDirectTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity)
+{
+ auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
+ auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto dst = dbuffer;
+ auto src = sbuffer;
+ for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
+ auto tmp = ALPHA_BLEND(*src, opacity);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ dbuffer += surface->stride;
+ sbuffer += image->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region)
+{
+ auto dbuffer = &surface->buffer[region.min.y * surface->stride + region.min.x];
+ auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
+
+ for (auto y = region.min.y; y < region.max.y; ++y) {
+ auto dst = dbuffer;
+ auto src = sbuffer;
+ for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
+ *dst = *src + ALPHA_BLEND(*dst, _ialpha(*src));
+ }
+ dbuffer += surface->stride;
+ sbuffer += image->stride;
+ }
+ return true;
+}
+
+
+//Blenders for the following scenarios: [Composition / Non-Composition] * [Opaque / Translucent]
+static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity)
+{
+ if (_compositing(surface)) {
+ if (opacity == 255) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterDirectMaskedRGBAImage(surface, image, region, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterDirectMaskedRGBAImage(surface, image, region, _ialpha);
+ }
+ } else {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _ialpha);
+ }
+ }
+ } else {
+ if (opacity == 255) return _rasterDirectRGBAImage(surface, image, region);
+ else return _rasterDirectTranslucentRGBAImage(surface, image, region, opacity);
+ }
+ return false;
+}
+
+
+//Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed]
+static bool _rasterRGBAImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity)
+{
+ //RLE Image
+ if (image->rle) {
+ if (image->direct) return _directRleRGBAImage(surface, image, opacity);
+ else if (image->scaled) return _scaledRleRGBAImage(surface, image, transform, region, opacity);
+ else return _transformedRleRGBAImage(surface, image, transform, opacity);
+ //Whole Image
+ } else {
+ if (image->direct) return _directRGBAImage(surface, image, region, opacity);
+ else if (image->scaled) return _scaledRGBAImage(surface, image, transform, region, opacity);
+ else return _transformedRGBAImage(surface, image, transform, region, opacity);
+ }
+}
+
+
+/************************************************************************/
+/* Rect Linear Gradient */
+/************************************************************************/
+
+static bool _rasterLinearGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x;
+
+ auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
+ if (!sbuffer) return false;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w);
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ buffer += surface->stride;
+ cbuffer += surface->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterTranslucentLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
+ if (!sbuffer) return false;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = buffer;
+ fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w);
+ for (uint32_t x = 0; x < w; ++x, ++dst) {
+ *dst = sbuffer[x] + ALPHA_BLEND(*dst, _ialpha(sbuffer[x]));
+ }
+ buffer += surface->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterSolidLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ fillFetchLinear(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w);
+ }
+ return true;
+}
+
+
+static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterLinearGradientMaskedRect(surface, region, fill, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterLinearGradientMaskedRect(surface, region, fill, _ialpha);
+ }
+ } else {
+ if (fill->translucent) return _rasterTranslucentLinearGradientRect(surface, region, fill);
+ else _rasterSolidLinearGradientRect(surface, region, fill);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Rle Linear Gradient */
+/************************************************************************/
+
+static bool _rasterLinearGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto span = rle->spans;
+ auto cbuffer = surface->compositor->image.data;
+ auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buffer) return false;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ fillFetchLinear(fill, buffer, span->y, span->x, span->len);
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto src = buffer;
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ auto ialpha = 255 - span->coverage;
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ tmp = ALPHA_BLEND(tmp, span->coverage) + ALPHA_BLEND(*dst, ialpha);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterTranslucentLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto span = rle->spans;
+ auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buffer) return false;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ fillFetchLinear(fill, buffer, span->y, span->x, span->len);
+ if (span->coverage == 255) {
+ for (uint32_t i = 0; i < span->len; ++i, ++dst) {
+ *dst = buffer[i] + ALPHA_BLEND(*dst, _ialpha(buffer[i]));
+ }
+ } else {
+ for (uint32_t i = 0; i < span->len; ++i, ++dst) {
+ auto tmp = ALPHA_BLEND(buffer[i], span->coverage);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterSolidLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (fill->linear.len < FLT_EPSILON) return false;
+
+ auto buf = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buf) return false;
+
+ auto span = rle->spans;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ if (span->coverage == 255) {
+ fillFetchLinear(fill, surface->buffer + span->y * surface->stride + span->x, span->y, span->x, span->len);
+ } else {
+ fillFetchLinear(fill, buf, span->y, span->x, span->len);
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ for (uint32_t i = 0; i < span->len; ++i) {
+ dst[i] = INTERPOLATE(span->coverage, buf[i], dst[i]);
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (!rle) return false;
+
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterLinearGradientMaskedRle(surface, rle, fill, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterLinearGradientMaskedRle(surface, rle, fill, _ialpha);
+ }
+ } else {
+ if (fill->translucent) return _rasterTranslucentLinearGradientRle(surface, rle, fill);
+ else return _rasterSolidLinearGradientRle(surface, rle, fill);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* Rect Radial Gradient */
+/************************************************************************/
+
+static bool _rasterRadialGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x;
+
+ auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
+ if (!sbuffer) return false;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w);
+ auto dst = buffer;
+ auto cmp = cbuffer;
+ auto src = sbuffer;
+ for (uint32_t x = 0; x < w; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ buffer += surface->stride;
+ cbuffer += surface->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterTranslucentRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto sbuffer = static_cast<uint32_t*>(alloca(w * sizeof(uint32_t)));
+ if (!sbuffer) return false;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = buffer;
+ fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w);
+ for (uint32_t x = 0; x < w; ++x, ++dst) {
+ *dst = sbuffer[x] + ALPHA_BLEND(*dst, _ialpha(sbuffer[x]));
+ }
+ buffer += surface->stride;
+ }
+ return true;
+}
+
+
+static bool _rasterSolidRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ fillFetchRadial(fill, dst, region.min.y + y, region.min.x, w);
+ }
+ return true;
+}
+
+
+static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
+{
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterRadialGradientMaskedRect(surface, region, fill, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterRadialGradientMaskedRect(surface, region, fill, _ialpha);
+ }
+ } else {
+ if (fill->translucent) return _rasterTranslucentRadialGradientRect(surface, region, fill);
+ else return _rasterSolidRadialGradientRect(surface, region, fill);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* RLE Radial Gradient */
+/************************************************************************/
+
+static bool _rasterRadialGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint32_t (*blendMethod)(uint32_t))
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto span = rle->spans;
+ auto cbuffer = surface->compositor->image.data;
+ auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buffer) return false;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ fillFetchRadial(fill, buffer, span->y, span->x, span->len);
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto cmp = &cbuffer[span->y * surface->compositor->image.stride + span->x];
+ auto src = buffer;
+ if (span->coverage == 255) {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = ALPHA_BLEND(*src, blendMethod(*cmp));
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ } else {
+ for (uint32_t x = 0; x < span->len; ++x, ++dst, ++cmp, ++src) {
+ auto tmp = INTERPOLATE(span->coverage, ALPHA_BLEND(*src, blendMethod(*cmp)), *dst);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterTranslucentRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto span = rle->spans;
+ auto buffer = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buffer) return false;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ fillFetchRadial(fill, buffer, span->y, span->x, span->len);
+ if (span->coverage == 255) {
+ for (uint32_t i = 0; i < span->len; ++i, ++dst) {
+ *dst = buffer[i] + ALPHA_BLEND(*dst, _ialpha(buffer[i]));
+ }
+ } else {
+ for (uint32_t i = 0; i < span->len; ++i, ++dst) {
+ auto tmp = ALPHA_BLEND(buffer[i], span->coverage);
+ *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp));
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterSolidRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (fill->radial.a < FLT_EPSILON) return false;
+
+ auto buf = static_cast<uint32_t*>(alloca(surface->w * sizeof(uint32_t)));
+ if (!buf) return false;
+
+ auto span = rle->spans;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ if (span->coverage == 255) {
+ fillFetchRadial(fill, dst, span->y, span->x, span->len);
+ } else {
+ fillFetchRadial(fill, buf, span->y, span->x, span->len);
+ auto ialpha = 255 - span->coverage;
+ for (uint32_t i = 0; i < span->len; ++i, ++dst) {
+ *dst = ALPHA_BLEND(buf[i], span->coverage) + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+ }
+ return true;
+}
+
+
+static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
+{
+ if (!rle) return false;
+
+ if (_compositing(surface)) {
+ if (surface->compositor->method == CompositeMethod::AlphaMask) {
+ return _rasterRadialGradientMaskedRle(surface, rle, fill, _alpha);
+ } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) {
+ return _rasterRadialGradientMaskedRle(surface, rle, fill, _ialpha);
+ }
+ } else {
+ if (fill->translucent) _rasterTranslucentRadialGradientRle(surface, rle, fill);
+ else return _rasterSolidRadialGradientRle(surface, rle, fill);
+ }
+ return false;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
+#if defined(THORVG_AVX_VECTOR_SUPPORT)
+ avxRasterRGBA32(dst, val, offset, len);
+#elif defined(THORVG_NEON_VECTOR_SUPPORT)
+ neonRasterRGBA32(dst, val, offset, len);
+#else
+ cRasterRGBA32(dst, val, offset, len);
+#endif
+}
+
+
+bool rasterCompositor(SwSurface* surface)
+{
+ if (surface->cs == SwCanvas::ABGR8888 || surface->cs == SwCanvas::ABGR8888_STRAIGHT) {
+ surface->blender.join = _abgrJoin;
+ } else if (surface->cs == SwCanvas::ARGB8888 || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
+ surface->blender.join = _argbJoin;
+ } else {
+ //What Color Space ???
+ return false;
+ }
+ return true;
+}
+
+
+bool rasterClear(SwSurface* surface)
+{
+ if (!surface || !surface->buffer || surface->stride <= 0 || surface->w <= 0 || surface->h <= 0) return false;
+
+ if (surface->w == surface->stride) {
+ rasterRGBA32(surface->buffer, 0x00000000, 0, surface->w * surface->h);
+ } else {
+ for (uint32_t i = 0; i < surface->h; i++) {
+ rasterRGBA32(surface->buffer + surface->stride * i, 0x00000000, 0, surface->w);
+ }
+ }
+ return true;
+}
+
+
+void rasterUnpremultiply(SwSurface* surface)
+{
+ //OPTIMIZE_ME: +SIMD
+ for (uint32_t y = 0; y < surface->h; y++) {
+ auto buffer = surface->buffer + surface->stride * y;
+ for (uint32_t x = 0; x < surface->w; ++x) {
+ uint8_t a = buffer[x] >> 24;
+ if (a == 255) {
+ continue;
+ } else if (a == 0) {
+ buffer[x] = 0x00ffffff;
+ } else {
+ uint16_t r = ((buffer[x] >> 8) & 0xff00) / a;
+ uint16_t g = ((buffer[x]) & 0xff00) / a;
+ uint16_t b = ((buffer[x] << 8) & 0xff00) / a;
+ if (r > 0xff) r = 0xff;
+ if (g > 0xff) g = 0xff;
+ if (b > 0xff) b = 0xff;
+ buffer[x] = (a << 24) | (r << 16) | (g << 8) | (b);
+ }
+ }
+ }
+}
+
+
+bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
+{
+ if (!shape->fill) return false;
+
+ if (shape->fastTrack) {
+ if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
+ else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
+ } else {
+ if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->rle, shape->fill);
+ else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->rle, shape->fill);
+ }
+ return false;
+}
+
+
+bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
+{
+ if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false;
+
+ if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
+ else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
+
+ return false;
+}
+
+
+bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ if (a < 255) {
+ r = _multiplyAlpha(r, a);
+ g = _multiplyAlpha(g, a);
+ b = _multiplyAlpha(b, a);
+ }
+
+ auto color = surface->blender.join(r, g, b, a);
+
+ if (shape->fastTrack) return _rasterRect(surface, shape->bbox, color, a);
+ else return _rasterRle(surface, shape->rle, color, a);
+}
+
+
+bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ if (a < 255) {
+ r = _multiplyAlpha(r, a);
+ g = _multiplyAlpha(g, a);
+ b = _multiplyAlpha(b, a);
+ }
+
+ auto color = surface->blender.join(r, g, b, a);
+
+ return _rasterRle(surface, shape->strokeRle, color, a);
+}
+
+
+bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity)
+{
+ //Verify Boundary
+ if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= surface->w || bbox.min.y >= surface->h) return false;
+
+ //TOOD: switch (image->format)
+ //TODO: case: _rasterRGBImage()
+ //TODO: case: _rasterGrayscaleImage()
+ //TODO: case: _rasterAlphaImage()
+ return _rasterRGBAImage(surface, image, transform, bbox, opacity);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h
new file mode 100644
index 0000000000..1d6552f3e9
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifdef THORVG_AVX_VECTOR_SUPPORT
+
+#include <immintrin.h>
+
+#define N_32BITS_IN_128REG 4
+#define N_32BITS_IN_256REG 8
+
+static inline __m128i ALPHA_BLEND(__m128i c, __m128i a)
+{
+ //1. set the masks for the A/G and R/B channels
+ auto AG = _mm_set1_epi32(0xff00ff00);
+ auto RB = _mm_set1_epi32(0x00ff00ff);
+
+ //2. mask the alpha vector - originally quartet [a, a, a, a]
+ auto aAG = _mm_and_si128(a, AG);
+ auto aRB = _mm_and_si128(a, RB);
+
+ //3. calculate the alpha blending of the 2nd and 4th channel
+ //- mask the color vector
+ //- multiply it by the masked alpha vector
+ //- add the correction to compensate bit shifting used instead of dividing by 255
+ //- shift bits - corresponding to division by 256
+ auto even = _mm_and_si128(c, RB);
+ even = _mm_mullo_epi16(even, aRB);
+ even =_mm_add_epi16(even, RB);
+ even = _mm_srli_epi16(even, 8);
+
+ //4. calculate the alpha blending of the 1st and 3rd channel:
+ //- mask the color vector
+ //- multiply it by the corresponding masked alpha vector and store the high bits of the result
+ //- add the correction to compensate division by 256 instead of by 255 (next step)
+ //- remove the low 8 bits to mimic the division by 256
+ auto odd = _mm_and_si128(c, AG);
+ odd = _mm_mulhi_epu16(odd, aAG);
+ odd = _mm_add_epi16(odd, RB);
+ odd = _mm_and_si128(odd, AG);
+
+ //5. the final result
+ return _mm_or_si128(odd, even);
+}
+
+
+static void avxRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
+ //1. calculate how many iterations we need to cover the length
+ uint32_t iterations = len / N_32BITS_IN_256REG;
+ uint32_t avxFilled = iterations * N_32BITS_IN_256REG;
+
+ //2. set the beginning of the array
+ dst += offset;
+
+ //3. fill the octets
+ for (uint32_t i = 0; i < iterations; ++i, dst += N_32BITS_IN_256REG) {
+ _mm256_storeu_si256((__m256i*)dst, _mm256_set1_epi32(val));
+ }
+
+ //4. fill leftovers (in the first step we have to set the pointer to the place where the avx job is done)
+ int32_t leftovers = len - avxFilled;
+ while (leftovers--) *dst++ = val;
+}
+
+
+static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+{
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+
+ auto ialpha = 255 - static_cast<uint8_t>(_alpha(color));
+
+ auto avxColor = _mm_set1_epi32(color);
+ auto avxIalpha = _mm_set1_epi8(ialpha);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+
+ //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
+ auto notAligned = ((uintptr_t)dst & 0xf) / 4;
+ if (notAligned) {
+ notAligned = (N_32BITS_IN_128REG - notAligned > w ? w : N_32BITS_IN_128REG - notAligned);
+ for (uint32_t x = 0; x < notAligned; ++x, ++dst) {
+ *dst = color + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+
+ //2. fill the aligned memory - N_32BITS_IN_128REG pixels processed at once
+ uint32_t iterations = (w - notAligned) / N_32BITS_IN_128REG;
+ uint32_t avxFilled = iterations * N_32BITS_IN_128REG;
+ auto avxDst = (__m128i*)dst;
+ for (uint32_t x = 0; x < iterations; ++x, ++avxDst) {
+ *avxDst = _mm_add_epi32(avxColor, ALPHA_BLEND(*avxDst, avxIalpha));
+ }
+
+ //3. fill the remaining pixels
+ int32_t leftovers = w - notAligned - avxFilled;
+ dst += avxFilled;
+ while (leftovers--) {
+ *dst = color + ALPHA_BLEND(*dst, ialpha);
+ dst++;
+ }
+ }
+ return true;
+}
+
+
+static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+{
+ auto span = rle->spans;
+ uint32_t src;
+
+ for (uint32_t i = 0; i < rle->size; ++i) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+
+ if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
+ else src = color;
+
+ auto ialpha = 255 - static_cast<uint8_t>(_alpha(src));
+
+ //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
+ auto notAligned = ((uintptr_t)dst & 0xf) / 4;
+ if (notAligned) {
+ notAligned = (N_32BITS_IN_128REG - notAligned > span->len ? span->len : N_32BITS_IN_128REG - notAligned);
+ for (uint32_t x = 0; x < notAligned; ++x, ++dst) {
+ *dst = src + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+
+ //2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once
+ //In order to avoid unneccessary avx variables declarations a check is made whether there are any iterations at all
+ uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG;
+ uint32_t avxFilled = 0;
+ if (iterations > 0) {
+ auto avxSrc = _mm_set1_epi32(src);
+ auto avxIalpha = _mm_set1_epi8(ialpha);
+
+ avxFilled = iterations * N_32BITS_IN_128REG;
+ auto avxDst = (__m128i*)dst;
+ for (uint32_t x = 0; x < iterations; ++x, ++avxDst) {
+ *avxDst = _mm_add_epi32(avxSrc, ALPHA_BLEND(*avxDst, avxIalpha));
+ }
+ }
+
+ //3. fill the remaining pixels
+ int32_t leftovers = span->len - notAligned - avxFilled;
+ dst += avxFilled;
+ while (leftovers--) {
+ *dst = src + ALPHA_BLEND(*dst, ialpha);
+ dst++;
+ }
+
+ ++span;
+ }
+ return true;
+}
+
+
+#endif
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h
new file mode 100644
index 0000000000..6d60957eb9
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+static void inline cRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
+ dst += offset;
+ while (len--) *dst++ = val;
+}
+
+
+static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+{
+ auto span = rle->spans;
+ uint32_t src;
+
+ for (uint32_t i = 0; i < rle->size; ++i, ++span) {
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+
+ if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
+ else src = color;
+
+ for (uint32_t x = 0; x < span->len; ++x, ++dst) {
+ *dst = src + ALPHA_BLEND(*dst, _ialpha(src));
+ }
+ }
+ return true;
+}
+
+
+static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+{
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto ialpha = _ialpha(color);
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+ for (uint32_t x = 0; x < w; ++x, ++dst) {
+ *dst = color + ALPHA_BLEND(*dst, ialpha);
+ }
+ }
+ return true;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h
new file mode 100644
index 0000000000..c74a6b309c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifdef THORVG_NEON_VECTOR_SUPPORT
+
+#include <arm_neon.h>
+
+static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
+{
+ uint16x8_t t = vmull_u8(c, a);
+ return vshrn_n_u16(t, 8);
+}
+
+
+static void neonRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
+{
+ uint32_t iterations = len / 4;
+ uint32_t neonFilled = iterations * 4;
+
+ dst += offset;
+ uint32x4_t vectorVal = {val, val, val, val};
+
+ for (uint32_t i = 0; i < iterations; ++i) {
+ vst1q_u32(dst, vectorVal);
+ dst += 4;
+ }
+
+ int32_t leftovers = len - neonFilled;
+ while (leftovers--) *dst++ = val;
+}
+
+
+static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint32_t color)
+{
+ auto span = rle->spans;
+ uint32_t src;
+ uint8x8_t *vDst = nullptr;
+ uint16_t align;
+
+ for (uint32_t i = 0; i < rle->size; ++i) {
+ if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
+ else src = color;
+
+ auto dst = &surface->buffer[span->y * surface->stride + span->x];
+ auto ialpha = 255 - _alpha(src);
+
+ if ((((uint32_t) dst) & 0x7) != 0) {
+ //fill not aligned byte
+ *dst = src + ALPHA_BLEND(*dst, ialpha);
+ vDst = (uint8x8_t*)(dst + 1);
+ align = 1;
+ } else {
+ vDst = (uint8x8_t*) dst;
+ align = 0;
+ }
+
+ uint8x8_t vSrc = (uint8x8_t) vdup_n_u32(src);
+ uint8x8_t vIalpha = vdup_n_u8((uint8_t) ialpha);
+
+ for (uint32_t x = 0; x < (span->len - align) / 2; ++x)
+ vDst[x] = vadd_u8(vSrc, ALPHA_BLEND(vDst[x], vIalpha));
+
+ auto leftovers = (span->len - align) % 2;
+ if (leftovers > 0) dst[span->len - 1] = src + ALPHA_BLEND(dst[span->len - 1], ialpha);
+
+ ++span;
+ }
+ return true;
+}
+
+
+static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint32_t color)
+{
+ auto buffer = surface->buffer + (region.min.y * surface->stride) + region.min.x;
+ auto h = static_cast<uint32_t>(region.max.y - region.min.y);
+ auto w = static_cast<uint32_t>(region.max.x - region.min.x);
+ auto ialpha = 255 - _alpha(color);
+
+ auto vColor = vdup_n_u32(color);
+ auto vIalpha = vdup_n_u8((uint8_t) ialpha);
+
+ uint8x8_t* vDst = nullptr;
+ uint32_t align;
+
+ for (uint32_t y = 0; y < h; ++y) {
+ auto dst = &buffer[y * surface->stride];
+
+ if ((((uint32_t) dst) & 0x7) != 0) {
+ //fill not aligned byte
+ *dst = color + ALPHA_BLEND(*dst, ialpha);
+ vDst = (uint8x8_t*) (dst + 1);
+ align = 1;
+ } else {
+ vDst = (uint8x8_t*) dst;
+ align = 0;
+ }
+
+ for (uint32_t x = 0; x < (w - align) / 2; ++x)
+ vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND(vDst[x], vIalpha));
+
+ auto leftovers = (w - align) % 2;
+ if (leftovers > 0) dst[w - 1] = color + ALPHA_BLEND(dst[w - 1], ialpha);
+ }
+ return true;
+}
+
+#endif
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
new file mode 100644
index 0000000000..2cf9fb4e93
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h
@@ -0,0 +1,602 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+struct Vertex
+{
+ Point pt;
+ Point uv;
+};
+
+struct Polygon
+{
+ Vertex vertex[3];
+};
+
+struct AALine
+{
+ int32_t x[2];
+ int32_t coverage[2];
+ int32_t length[2];
+};
+
+struct AASpans
+{
+ AALine *lines;
+ int32_t yStart;
+ int32_t yEnd;
+};
+
+static inline void _swap(float& a, float& b, float& tmp)
+{
+ tmp = a;
+ a = b;
+ b = tmp;
+}
+
+//Careful! Shared resource, No support threading
+static float dudx, dvdx;
+static float dxdya, dxdyb, dudya, dvdya;
+static float xa, xb, ua, va;
+
+
+//Y Range exception handling
+static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
+{
+ int32_t regionTop, regionBottom;
+
+ if (region) {
+ regionTop = region->min.y;
+ regionBottom = region->max.y;
+ } else {
+ regionTop = image->rle->spans->y;
+ regionBottom = image->rle->spans[image->rle->size - 1].y;
+ }
+
+ if (yStart >= regionBottom) return false;
+
+ if (yStart < regionTop) yStart = regionTop;
+ if (yEnd > regionBottom) yEnd = regionBottom;
+
+ return true;
+}
+
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
+{
+#define TEXMAP_TRANSLUCENT
+#define TEXMAP_MASKING
+ #include "tvgSwRasterTexmapInternal.h"
+#undef TEXMAP_MASKING
+#undef TEXMAP_TRANSLUCENT
+}
+
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
+{
+#define TEXMAP_MASKING
+ #include "tvgSwRasterTexmapInternal.h"
+#undef TEXMAP_MASKING
+}
+
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, AASpans* aaSpans)
+{
+#define TEXMAP_TRANSLUCENT
+ #include "tvgSwRasterTexmapInternal.h"
+#undef TEXMAP_TRANSLUCENT
+}
+
+
+static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans)
+{
+ #include "tvgSwRasterTexmapInternal.h"
+}
+
+
+/* This mapping algorithm is based on Mikael Kalms's. */
+static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint32_t (*blendMethod)(uint32_t), AASpans* aaSpans)
+{
+ float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
+ float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
+ float u[3] = {polygon.vertex[0].uv.x, polygon.vertex[1].uv.x, polygon.vertex[2].uv.x};
+ float v[3] = {polygon.vertex[0].uv.y, polygon.vertex[1].uv.y, polygon.vertex[2].uv.y};
+
+ float off_y;
+ float dxdy[3] = {0.0f, 0.0f, 0.0f};
+ float tmp;
+
+ auto upper = false;
+
+ //Sort the vertices in ascending Y order
+ if (y[0] > y[1]) {
+ _swap(x[0], x[1], tmp);
+ _swap(y[0], y[1], tmp);
+ _swap(u[0], u[1], tmp);
+ _swap(v[0], v[1], tmp);
+ }
+ if (y[0] > y[2]) {
+ _swap(x[0], x[2], tmp);
+ _swap(y[0], y[2], tmp);
+ _swap(u[0], u[2], tmp);
+ _swap(v[0], v[2], tmp);
+ }
+ if (y[1] > y[2]) {
+ _swap(x[1], x[2], tmp);
+ _swap(y[1], y[2], tmp);
+ _swap(u[1], u[2], tmp);
+ _swap(v[1], v[2], tmp);
+ }
+
+ //Y indexes
+ int yi[3] = {(int)y[0], (int)y[1], (int)y[2]};
+
+ //Skip drawing if it's too thin to cover any pixels at all.
+ if ((yi[0] == yi[1] && yi[0] == yi[2]) || ((int) x[0] == (int) x[1] && (int) x[0] == (int) x[2])) return;
+
+ //Calculate horizontal and vertical increments for UV axes (these calcs are certainly not optimal, although they're stable (handles any dy being 0)
+ auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
+
+ //Skip poly if it's an infinitely thin line
+ if (mathZero(denom)) return;
+
+ denom = 1 / denom; //Reciprocal for speeding up
+ dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
+ dvdx = ((v[2] - v[0]) * (y[1] - y[0]) - (v[1] - v[0]) * (y[2] - y[0])) * denom;
+ auto dudy = ((u[1] - u[0]) * (x[2] - x[0]) - (u[2] - u[0]) * (x[1] - x[0])) * denom;
+ auto dvdy = ((v[1] - v[0]) * (x[2] - x[0]) - (v[2] - v[0]) * (x[1] - x[0])) * denom;
+
+ //Calculate X-slopes along the edges
+ if (y[1] > y[0]) dxdy[0] = (x[1] - x[0]) / (y[1] - y[0]);
+ if (y[2] > y[0]) dxdy[1] = (x[2] - x[0]) / (y[2] - y[0]);
+ if (y[2] > y[1]) dxdy[2] = (x[2] - x[1]) / (y[2] - y[1]);
+
+ //Determine which side of the polygon the longer edge is on
+ auto side = (dxdy[1] > dxdy[0]) ? true : false;
+
+ if (mathEqual(y[0], y[1])) side = x[0] > x[1];
+ if (mathEqual(y[1], y[2])) side = x[2] > x[1];
+
+ auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
+
+ //Longer edge is on the left side
+ if (!side) {
+ //Calculate slopes along left edge
+ dxdya = dxdy[1];
+ dudya = dxdya * dudx + dudy;
+ dvdya = dxdya * dvdx + dvdy;
+
+ //Perform subpixel pre-stepping along left edge
+ auto dy = 1.0f - (y[0] - yi[0]);
+ xa = x[0] + dy * dxdya;
+ ua = u[0] + dy * dudya;
+ va = v[0] + dy * dvdya;
+
+ //Draw upper segment if possibly visible
+ if (yi[0] < yi[1]) {
+ off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
+ xa += (off_y * dxdya);
+ ua += (off_y * dudya);
+ va += (off_y * dvdya);
+
+ // Set right edge X-slope and perform subpixel pre-stepping
+ dxdyb = dxdy[0];
+ xb = x[0] + dy * dxdyb + (off_y * dxdyb);
+
+ if (blendMethod) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
+ } else {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
+ }
+
+ upper = true;
+ }
+ //Draw lower segment if possibly visible
+ if (yi[1] < yi[2]) {
+ off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
+ if (!upper) {
+ xa += (off_y * dxdya);
+ ua += (off_y * dudya);
+ va += (off_y * dvdya);
+ }
+ // Set right edge X-slope and perform subpixel pre-stepping
+ dxdyb = dxdy[2];
+ xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
+ if (blendMethod) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
+ } else {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
+ }
+ }
+ //Longer edge is on the right side
+ } else {
+ //Set right edge X-slope and perform subpixel pre-stepping
+ dxdyb = dxdy[1];
+ auto dy = 1.0f - (y[0] - yi[0]);
+ xb = x[0] + dy * dxdyb;
+
+ //Draw upper segment if possibly visible
+ if (yi[0] < yi[1]) {
+ off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
+ xb += (off_y *dxdyb);
+
+ // Set slopes along left edge and perform subpixel pre-stepping
+ dxdya = dxdy[0];
+ dudya = dxdya * dudx + dudy;
+ dvdya = dxdya * dvdx + dvdy;
+
+ xa = x[0] + dy * dxdya + (off_y * dxdya);
+ ua = u[0] + dy * dudya + (off_y * dudya);
+ va = v[0] + dy * dvdya + (off_y * dvdya);
+
+ if (blendMethod) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blendMethod, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blendMethod, aaSpans);
+ } else {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans);
+ }
+
+ upper = true;
+ }
+ //Draw lower segment if possibly visible
+ if (yi[1] < yi[2]) {
+ off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
+ if (!upper) xb += (off_y *dxdyb);
+
+ // Set slopes along left edge and perform subpixel pre-stepping
+ dxdya = dxdy[2];
+ dudya = dxdya * dudx + dudy;
+ dvdya = dxdya * dvdx + dvdy;
+ dy = 1 - (y[1] - yi[1]);
+ xa = x[1] + dy * dxdya + (off_y * dxdya);
+ ua = u[1] + dy * dudya + (off_y * dudya);
+ va = v[1] + dy * dvdya + (off_y * dvdya);
+
+ if (blendMethod) {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blendMethod, aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blendMethod, aaSpans);
+ } else {
+ if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans);
+ else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans);
+ }
+ }
+ }
+}
+
+
+static AASpans* _AASpans(const Vertex* vertices, const SwImage* image, const SwBBox* region)
+{
+ //Initialize Y range
+ float ys = FLT_MAX, ye = -1.0f;
+
+ for (int i = 0; i < 4; i++) {
+ if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
+ if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
+ }
+
+ auto yStart = static_cast<int32_t>(ys);
+ auto yEnd = static_cast<int32_t>(ye);
+
+ if (!_arrange(image, region, yStart, yEnd)) return nullptr;
+
+ auto aaSpans = static_cast<AASpans*>(malloc(sizeof(AASpans)));
+ aaSpans->yStart = yStart;
+ aaSpans->yEnd = yEnd;
+
+ //Initialize X range
+ auto height = yEnd - yStart;
+
+ aaSpans->lines = static_cast<AALine*>(calloc(height, sizeof(AALine)));
+
+ for (int32_t i = 0; i < height; i++) {
+ aaSpans->lines[i].x[0] = INT32_MAX;
+ aaSpans->lines[i].x[1] = INT32_MIN;
+ }
+ return aaSpans;
+}
+
+
+static void _calcIrregularCoverage(AALine* lines, int32_t eidx, int32_t y, int32_t diagonal, int32_t edgeDist, bool reverse)
+{
+ if (eidx == 1) reverse = !reverse;
+ int32_t coverage = (255 / (diagonal + 2));
+ int32_t tmp;
+ for (int32_t ry = 0; ry < (diagonal + 2); ry++) {
+ tmp = y - ry - edgeDist;
+ if (tmp < 0) return;
+ lines[tmp].length[eidx] = 1;
+ if (reverse) lines[tmp].coverage[eidx] = 255 - (coverage * ry);
+ else lines[tmp].coverage[eidx] = (coverage * ry);
+ }
+}
+
+
+static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t rewind, bool reverse)
+{
+ if (eidx == 1) reverse = !reverse;
+ int32_t coverage = (255 / (rewind + 1));
+ int32_t tmp;
+ for (int ry = 1; ry < (rewind + 1); ry++) {
+ tmp = y - ry;
+ if (tmp < 0) return;
+ lines[tmp].length[eidx] = 1;
+ if (reverse) lines[tmp].coverage[eidx] = (255 - (coverage * ry));
+ else lines[tmp].coverage[eidx] = (coverage * ry);
+ }
+}
+
+
+static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
+{
+ if (lines[y].length[eidx] < abs(x - x2)) {
+ lines[y].length[eidx] = abs(x - x2);
+ lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
+ }
+}
+
+
+/*
+ * This Anti-Aliasing mechanism is originated from Hermet Park's idea.
+ * To understand this AA logic, you can refer this page:
+ * www.hermet.pe.kr/122 (hermetpark@gmail.com)
+*/
+static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
+{
+//Previous edge direction:
+#define DirOutHor 0x0011
+#define DirOutVer 0x0001
+#define DirInHor 0x0010
+#define DirInVer 0x0000
+#define DirNone 0x1000
+
+#define PUSH_VERTEX() \
+ do { \
+ pEdge.x = lines[y].x[eidx]; \
+ pEdge.y = y; \
+ ptx[0] = tx[0]; \
+ ptx[1] = tx[1]; \
+ } while (0)
+
+ int32_t y = 0;
+ SwPoint pEdge = {-1, -1}; //previous edge point
+ SwPoint edgeDiff = {0, 0}; //temporary used for point distance
+
+ /* store bigger to tx[0] between prev and current edge's x positions. */
+ int32_t tx[2] = {0, 0};
+ /* back up prev tx values */
+ int32_t ptx[2] = {0, 0};
+ int32_t diagonal = 0; //straight diagonal pixels count
+
+ auto yStart = aaSpans->yStart;
+ auto yEnd = aaSpans->yEnd;
+ auto lines = aaSpans->lines;
+
+ int32_t prevDir = DirNone;
+ int32_t curDir = DirNone;
+
+ yEnd -= yStart;
+
+ //Start Edge
+ if (y < yEnd) {
+ pEdge.x = lines[y].x[eidx];
+ pEdge.y = y;
+ }
+
+ //Calculates AA Edges
+ for (y++; y < yEnd; y++) {
+ //Ready tx
+ if (eidx == 0) {
+ tx[0] = pEdge.x;
+ tx[1] = lines[y].x[0];
+ } else {
+ tx[0] = lines[y].x[1];
+ tx[1] = pEdge.x;
+ }
+ edgeDiff.x = (tx[0] - tx[1]);
+ edgeDiff.y = (y - pEdge.y);
+
+ //Confirm current edge direction
+ if (edgeDiff.x > 0) {
+ if (edgeDiff.y == 1) curDir = DirOutHor;
+ else curDir = DirOutVer;
+ } else if (edgeDiff.x < 0) {
+ if (edgeDiff.y == 1) curDir = DirInHor;
+ else curDir = DirInVer;
+ } else curDir = DirNone;
+
+ //straight diagonal increase
+ if ((curDir == prevDir) && (y < yEnd)) {
+ if ((abs(edgeDiff.x) == 1) && (edgeDiff.y == 1)) {
+ ++diagonal;
+ PUSH_VERTEX();
+ continue;
+ }
+ }
+
+ switch (curDir) {
+ case DirOutHor: {
+ _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
+ if (diagonal > 0) {
+ _calcIrregularCoverage(lines, eidx, y, diagonal, 0, true);
+ diagonal = 0;
+ }
+ /* Increment direction is changed: Outside Vertical -> Outside Horizontal */
+ if (prevDir == DirOutVer) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
+
+ //Trick, but fine-tunning!
+ if (y == 1) _calcHorizCoverage(lines, eidx, pEdge.y, tx[0], tx[1]);
+ PUSH_VERTEX();
+ }
+ break;
+ case DirOutVer: {
+ _calcVertCoverage(lines, eidx, y, edgeDiff.y, true);
+ if (diagonal > 0) {
+ _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, false);
+ diagonal = 0;
+ }
+ /* Increment direction is changed: Outside Horizontal -> Outside Vertical */
+ if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
+ PUSH_VERTEX();
+ }
+ break;
+ case DirInHor: {
+ _calcHorizCoverage(lines, eidx, (y - 1), tx[0], tx[1]);
+ if (diagonal > 0) {
+ _calcIrregularCoverage(lines, eidx, y, diagonal, 0, false);
+ diagonal = 0;
+ }
+ /* Increment direction is changed: Outside Horizontal -> Inside Horizontal */
+ if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
+ PUSH_VERTEX();
+ }
+ break;
+ case DirInVer: {
+ _calcVertCoverage(lines, eidx, y, edgeDiff.y, false);
+ if (prevDir == DirOutHor) edgeDiff.y -= 1; //Weird, fine tuning?????????????????????
+ if (diagonal > 0) {
+ _calcIrregularCoverage(lines, eidx, y, diagonal, edgeDiff.y, true);
+ diagonal = 0;
+ }
+ /* Increment direction is changed: Outside Horizontal -> Inside Vertical */
+ if (prevDir == DirOutHor) _calcHorizCoverage(lines, eidx, pEdge.y, ptx[0], ptx[1]);
+ PUSH_VERTEX();
+ }
+ break;
+ }
+ if (curDir != DirNone) prevDir = curDir;
+ }
+
+ //leftovers...?
+ if ((edgeDiff.y == 1) && (edgeDiff.x != 0)) {
+ if (y >= yEnd) y = (yEnd - 1);
+ _calcHorizCoverage(lines, eidx, y - 1, ptx[0], ptx[1]);
+ _calcHorizCoverage(lines, eidx, y, tx[0], tx[1]);
+ } else {
+ ++y;
+ if (y > yEnd) y = yEnd;
+ _calcVertCoverage(lines, eidx, y, (edgeDiff.y + 1), (prevDir & 0x00000001));
+ }
+}
+
+
+static bool _apply(SwSurface* surface, AASpans* aaSpans)
+{
+ auto y = aaSpans->yStart;
+ uint32_t pixel;
+ uint32_t* dst;
+ int32_t pos;
+
+ //left side
+ _calcAAEdge(aaSpans, 0);
+ //right side
+ _calcAAEdge(aaSpans, 1);
+
+ while (y < aaSpans->yEnd) {
+ auto line = &aaSpans->lines[y - aaSpans->yStart];
+ auto width = line->x[1] - line->x[0];
+ if (width > 0) {
+ auto offset = y * surface->stride;
+
+ //Left edge
+ dst = surface->buffer + (offset + line->x[0]);
+ if (line->x[0] > 1) pixel = *(dst - 1);
+ else pixel = *dst;
+
+ pos = 1;
+ while (pos <= line->length[0]) {
+ *dst = INTERPOLATE((line->coverage[0] * pos), *dst, pixel);
+ ++dst;
+ ++pos;
+ }
+
+ //Right edge
+ dst = surface->buffer + (offset + line->x[1] - 1);
+ if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
+ else pixel = *dst;
+
+ pos = width;
+ while ((int32_t)(width - line->length[1]) < pos) {
+ *dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel);
+ --dst;
+ --pos;
+ }
+ }
+ y++;
+ }
+
+ free(aaSpans->lines);
+ free(aaSpans);
+
+ return true;
+}
+
+
+/*
+ 2 triangles constructs 1 mesh.
+ below figure illustrates vert[4] index info.
+ If you need better quality, please divide a mesh by more number of triangles.
+
+ 0 -- 1
+ | / |
+ | / |
+ 3 -- 2
+*/
+static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint32_t (*blendMethod)(uint32_t))
+{
+ //Exceptions: No dedicated drawing area?
+ if (!region && image->rle->size == 0) return false;
+
+ /* Prepare vertices.
+ shift XY coordinates to match the sub-pixeling technique. */
+ Vertex vertices[4];
+ vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
+ vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}};
+ vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}};
+ vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}};
+
+ for (int i = 0; i < 4; i++) mathMultiply(&vertices[i].pt, transform);
+
+ auto aaSpans = _AASpans(vertices, image, region);
+ if (!aaSpans) return true;
+
+ Polygon polygon;
+
+ //Draw the first polygon
+ polygon.vertex[0] = vertices[0];
+ polygon.vertex[1] = vertices[1];
+ polygon.vertex[2] = vertices[3];
+
+ _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
+
+ //Draw the second polygon
+ polygon.vertex[0] = vertices[1];
+ polygon.vertex[1] = vertices[2];
+ polygon.vertex[2] = vertices[3];
+
+ _rasterPolygonImage(surface, image, region, opacity, polygon, blendMethod, aaSpans);
+
+ return _apply(surface, aaSpans);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
new file mode 100644
index 0000000000..4e8d342137
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+{
+ float _dudx = dudx, _dvdx = dvdx;
+ float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
+ float _xa = xa, _xb = xb, _ua = ua, _va = va;
+ auto sbuf = image->data;
+ auto dbuf = surface->buffer;
+ int32_t sw = static_cast<int32_t>(image->stride);
+ int32_t sh = image->h;
+ int32_t dw = surface->stride;
+ int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
+ int32_t vv = 0, uu = 0;
+ int32_t minx, maxx;
+ float dx, u, v, iptr;
+ uint32_t* buf;
+ SwSpan* span = nullptr; //used only when rle based.
+
+#ifdef TEXMAP_MASKING
+ uint32_t* cmp;
+#endif
+
+ if (!_arrange(image, region, yStart, yEnd)) return;
+
+ //Loop through all lines in the segment
+ uint32_t spanIdx = 0;
+
+ if (region) {
+ minx = region->min.x;
+ maxx = region->max.x;
+ } else {
+ span = image->rle->spans;
+ while (span->y < yStart) {
+ ++span;
+ ++spanIdx;
+ }
+ }
+
+ y = yStart;
+
+ while (y < yEnd) {
+ x1 = _xa;
+ x2 = _xb;
+
+ if (!region) {
+ minx = INT32_MAX;
+ maxx = INT32_MIN;
+ //one single row, could be consisted of multiple spans.
+ while (span->y == y && spanIdx < image->rle->size) {
+ if (minx > span->x) minx = span->x;
+ if (maxx < span->x + span->len) maxx = span->x + span->len;
+ ++span;
+ ++spanIdx;
+ }
+ }
+ if (x1 < minx) x1 = minx;
+ if (x2 > maxx) x2 = maxx;
+
+ //Anti-Aliasing frames
+ ay = y - aaSpans->yStart;
+ if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
+ if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
+
+ //Range exception
+ if ((x2 - x1) < 1 || (x1 >= maxx) || (x2 <= minx)) goto next;
+
+ //Perform subtexel pre-stepping on UV
+ dx = 1 - (_xa - x1);
+ u = _ua + dx * _dudx;
+ v = _va + dx * _dvdx;
+
+ buf = dbuf + ((y * dw) + x1);
+
+ x = x1;
+
+#ifdef TEXMAP_MASKING
+ cmp = &surface->compositor->image.data[y * surface->compositor->image.stride + x1];
+#endif
+ //Draw horizontal line
+ while (x++ < x2) {
+ uu = (int) u;
+ vv = (int) v;
+
+ ar = (int)(255 * (1 - modff(u, &iptr)));
+ ab = (int)(255 * (1 - modff(v, &iptr)));
+ iru = uu + 1;
+ irv = vv + 1;
+ px = *(sbuf + (vv * sw) + uu);
+
+ /* horizontal interpolate */
+ if (iru < sw) {
+ /* right pixel */
+ int px2 = *(sbuf + (vv * sw) + iru);
+ px = INTERPOLATE(ar, px, px2);
+ }
+ /* vertical interpolate */
+ if (irv < sh) {
+ /* bottom pixel */
+ int px2 = *(sbuf + (irv * sw) + uu);
+
+ /* horizontal interpolate */
+ if (iru < sw) {
+ /* bottom right pixel */
+ int px3 = *(sbuf + (irv * sw) + iru);
+ px2 = INTERPOLATE(ar, px2, px3);
+ }
+ px = INTERPOLATE(ab, px, px2);
+ }
+#if defined(TEXMAP_MASKING) && defined(TEXMAP_TRANSLUCENT)
+ auto src = ALPHA_BLEND(px, _multiplyAlpha(opacity, blendMethod(*cmp)));
+#elif defined(TEXMAP_MASKING)
+ auto src = ALPHA_BLEND(px, blendMethod(*cmp));
+#elif defined(TEXMAP_TRANSLUCENT)
+ auto src = ALPHA_BLEND(px, opacity);
+#else
+ auto src = px;
+#endif
+ *buf = src + ALPHA_BLEND(*buf, _ialpha(src));
+ ++buf;
+#ifdef TEXMAP_MASKING
+ ++cmp;
+#endif
+ //Step UV horizontally
+ u += _dudx;
+ v += _dvdx;
+ //range over?
+ if ((uint32_t)v >= image->h) break;
+ }
+next:
+ //Step along both edges
+ _xa += _dxdya;
+ _xb += _dxdyb;
+ _ua += _dudya;
+ _va += _dvdya;
+
+ if (!region && spanIdx >= image->rle->size) break;
+
+ ++y;
+ }
+ xa = _xa;
+ xb = _xb;
+ ua = _ua;
+ va = _va;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
new file mode 100644
index 0000000000..78537e7726
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <math.h>
+#include "tvgSwCommon.h"
+#include "tvgTaskScheduler.h"
+#include "tvgSwRenderer.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+static int32_t initEngineCnt = false;
+static int32_t rendererCnt = 0;
+static SwMpool* globalMpool = nullptr;
+static uint32_t threadsCnt = 0;
+
+struct SwTask : Task
+{
+ Matrix* transform = nullptr;
+ SwSurface* surface = nullptr;
+ SwMpool* mpool = nullptr;
+ RenderUpdateFlag flags = RenderUpdateFlag::None;
+ Array<RenderData> clips;
+ uint32_t opacity;
+ SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
+ bool pushed = false; //Pushed into task list?
+ bool disposed = false; //Disposed task?
+
+ RenderRegion bounds() const
+ {
+ RenderRegion region;
+
+ //Range over?
+ region.x = bbox.min.x > 0 ? bbox.min.x : 0;
+ region.y = bbox.min.y > 0 ? bbox.min.y : 0;
+ region.w = bbox.max.x - region.x;
+ region.h = bbox.max.y - region.y;
+ if (region.w < 0) region.w = 0;
+ if (region.h < 0) region.h = 0;
+
+ return region;
+ }
+
+ virtual bool dispose() = 0;
+
+ virtual ~SwTask()
+ {
+ free(transform);
+ }
+};
+
+
+struct SwShapeTask : SwTask
+{
+ SwShape shape;
+ const Shape* sdata = nullptr;
+ bool cmpStroking = false;
+
+ void run(unsigned tid) override
+ {
+ if (opacity == 0) return; //Invisible
+
+ uint8_t strokeAlpha = 0;
+ auto visibleStroke = false;
+ bool visibleFill = false;
+ auto clipRegion = bbox;
+
+ if (HALF_STROKE(sdata->strokeWidth()) > 0) {
+ sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
+ visibleStroke = sdata->strokeFill() || (static_cast<uint32_t>(strokeAlpha * opacity / 255) > 0);
+ }
+
+ //This checks also for the case, if the invisible shape turned to visible by alpha.
+ auto prepareShape = false;
+ if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
+
+ //Shape
+ if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
+ uint8_t alpha = 0;
+ sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
+ alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
+ visibleFill = (alpha > 0 || sdata->fill());
+ if (visibleFill || visibleStroke) {
+ shapeReset(&shape);
+ if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
+ }
+ }
+
+ //Decide Stroking Composition
+ if (visibleStroke && visibleFill && opacity < 255) cmpStroking = true;
+ else cmpStroking = false;
+
+ //Fill
+ if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
+ if (visibleFill) {
+ /* We assume that if stroke width is bigger than 2,
+ shape outline below stroke could be full covered by stroke drawing.
+ Thus it turns off antialising in that condition.
+ Also, it shouldn't be dash style. */
+ auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true;
+
+ if (!shapeGenRle(&shape, sdata, antiAlias)) goto err;
+ }
+ if (auto fill = sdata->fill()) {
+ auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
+ if (ctable) shapeResetFill(&shape);
+ if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
+ } else {
+ shapeDelFill(&shape);
+ }
+ }
+
+ //Stroke
+ if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
+ if (visibleStroke) {
+ shapeResetStroke(&shape, sdata, transform);
+ if (!shapeGenStrokeRle(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err;
+
+ if (auto fill = sdata->strokeFill()) {
+ auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
+ if (ctable) shapeResetStrokeFill(&shape);
+ if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err;
+ } else {
+ shapeDelStrokeFill(&shape);
+ }
+ } else {
+ shapeDelStroke(&shape);
+ }
+ }
+
+ //Clip Path
+ for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+ auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
+ //Clip shape rle
+ if (shape.rle) {
+ if (clipper->fastTrack) rleClipRect(shape.rle, &clipper->bbox);
+ else if (clipper->rle) rleClipPath(shape.rle, clipper->rle);
+ else goto err;
+ }
+ //Clip stroke rle
+ if (shape.strokeRle) {
+ if (clipper->fastTrack) rleClipRect(shape.strokeRle, &clipper->bbox);
+ else if (clipper->rle) rleClipPath(shape.strokeRle, clipper->rle);
+ else goto err;
+ }
+ }
+ goto end;
+
+ err:
+ shapeReset(&shape);
+ end:
+ shapeDelOutline(&shape, mpool, tid);
+ }
+
+ bool dispose() override
+ {
+ shapeFree(&shape);
+ return true;
+ }
+};
+
+
+struct SwImageTask : SwTask
+{
+ SwImage image;
+
+ void run(unsigned tid) override
+ {
+ auto clipRegion = bbox;
+
+ //Invisible shape turned to visible by alpha.
+ if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) {
+ imageReset(&image);
+ if (!image.data || image.w == 0 || image.h == 0) goto end;
+
+ if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
+
+ if (clips.count > 0) {
+ if (!imageGenRle(&image, bbox, false)) goto end;
+ if (image.rle) {
+ for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+ auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
+ if (clipper->fastTrack) rleClipRect(image.rle, &clipper->bbox);
+ else if (clipper->rle) rleClipPath(image.rle, clipper->rle);
+ else goto err;
+ }
+ }
+ }
+ }
+ goto end;
+
+ err:
+ rleReset(image.rle);
+ end:
+ imageDelOutline(&image, mpool, tid);
+ }
+
+ bool dispose() override
+ {
+ imageFree(&image);
+ return true;
+ }
+};
+
+
+static void _termEngine()
+{
+ if (rendererCnt > 0) return;
+
+ mpoolTerm(globalMpool);
+ globalMpool = nullptr;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SwRenderer::~SwRenderer()
+{
+ clearCompositors();
+
+ if (surface) delete(surface);
+
+ if (!sharedMpool) mpoolTerm(mpool);
+
+ --rendererCnt;
+
+ if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
+}
+
+
+bool SwRenderer::clear()
+{
+ for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
+ if ((*task)->disposed) {
+ delete(*task);
+ } else {
+ (*task)->done();
+ (*task)->pushed = false;
+ }
+ }
+ tasks.clear();
+
+ if (!sharedMpool) mpoolClear(mpool);
+
+ if (surface) {
+ vport.x = vport.y = 0;
+ vport.w = surface->w;
+ vport.h = surface->h;
+ }
+
+ return true;
+}
+
+
+bool SwRenderer::sync()
+{
+ return true;
+}
+
+
+RenderRegion SwRenderer::viewport()
+{
+ return vport;
+}
+
+
+bool SwRenderer::viewport(const RenderRegion& vp)
+{
+ vport = vp;
+ return true;
+}
+
+
+bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs)
+{
+ if (!buffer || stride == 0 || w == 0 || h == 0 || w > stride) return false;
+
+ if (!surface) surface = new SwSurface;
+
+ surface->buffer = buffer;
+ surface->stride = stride;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = cs;
+
+ vport.x = vport.y = 0;
+ vport.w = surface->w;
+ vport.h = surface->h;
+
+ return rasterCompositor(surface);
+}
+
+
+bool SwRenderer::preRender()
+{
+ return rasterClear(surface);
+}
+
+void SwRenderer::clearCompositors()
+{
+ //Free Composite Caches
+ for (auto comp = compositors.data; comp < (compositors.data + compositors.count); ++comp) {
+ free((*comp)->compositor->image.data);
+ delete((*comp)->compositor);
+ delete(*comp);
+ }
+ compositors.reset();
+}
+
+
+bool SwRenderer::postRender()
+{
+ //Unmultiply alpha if needed
+ if (surface->cs == SwCanvas::ABGR8888_STRAIGHT || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
+ rasterUnpremultiply(surface);
+ }
+
+ for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) {
+ (*task)->pushed = false;
+ }
+ tasks.clear();
+
+ clearCompositors();
+ return true;
+}
+
+
+bool SwRenderer::renderImage(RenderData data)
+{
+ auto task = static_cast<SwImageTask*>(data);
+ task->done();
+
+ if (task->opacity == 0) return true;
+
+ return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
+}
+
+
+bool SwRenderer::renderShape(RenderData data)
+{
+ auto task = static_cast<SwShapeTask*>(data);
+ if (!task) return false;
+
+ task->done();
+
+ if (task->opacity == 0) return true;
+
+ uint32_t opacity;
+ Compositor* cmp = nullptr;
+
+ //Do Stroking Composition
+ if (task->cmpStroking) {
+ opacity = 255;
+ cmp = target(task->bounds());
+ beginComposite(cmp, CompositeMethod::None, task->opacity);
+ //No Stroking Composition
+ } else {
+ opacity = task->opacity;
+ }
+
+ //Main raster stage
+ uint8_t r, g, b, a;
+
+ if (auto fill = task->sdata->fill()) {
+ rasterGradientShape(surface, &task->shape, fill->identifier());
+ } else {
+ task->sdata->fillColor(&r, &g, &b, &a);
+ a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
+ if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
+ }
+
+ if (auto strokeFill = task->sdata->strokeFill()) {
+ rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
+ } else {
+ if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) {
+ a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
+ if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
+ }
+ }
+
+ if (task->cmpStroking) endComposite(cmp);
+
+ return true;
+}
+
+
+RenderRegion SwRenderer::region(RenderData data)
+{
+ return static_cast<SwTask*>(data)->bounds();
+}
+
+
+bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity)
+{
+ if (!cmp) return false;
+ auto p = static_cast<SwCompositor*>(cmp);
+
+ p->method = method;
+ p->opacity = opacity;
+
+ //Current Context?
+ if (p->method != CompositeMethod::None) {
+ surface = p->recoverSfc;
+ surface->compositor = p;
+ }
+
+ return true;
+}
+
+
+bool SwRenderer::mempool(bool shared)
+{
+ if (shared == sharedMpool) return true;
+
+ if (shared) {
+ if (!sharedMpool) {
+ if (!mpoolTerm(mpool)) return false;
+ mpool = globalMpool;
+ }
+ } else {
+ if (sharedMpool) mpool = mpoolInit(threadsCnt);
+ }
+
+ sharedMpool = shared;
+
+ if (mpool) return true;
+ return false;
+}
+
+
+Compositor* SwRenderer::target(const RenderRegion& region)
+{
+ auto x = region.x;
+ auto y = region.y;
+ auto w = region.w;
+ auto h = region.h;
+ auto sw = static_cast<int32_t>(surface->w);
+ auto sh = static_cast<int32_t>(surface->h);
+
+ //Out of boundary
+ if (x > sw || y > sh) return nullptr;
+
+ SwSurface* cmp = nullptr;
+
+ //Use cached data
+ for (auto p = compositors.data; p < (compositors.data + compositors.count); ++p) {
+ if ((*p)->compositor->valid) {
+ cmp = *p;
+ break;
+ }
+ }
+
+ //New Composition
+ if (!cmp) {
+ cmp = new SwSurface;
+ if (!cmp) goto err;
+
+ //Inherits attributes from main surface
+ *cmp = *surface;
+
+ cmp->compositor = new SwCompositor;
+ if (!cmp->compositor) goto err;
+
+ //SwImage, Optimize Me: Surface size from MainSurface(WxH) to Parameter W x H
+ cmp->compositor->image.data = (uint32_t*) malloc(sizeof(uint32_t) * surface->stride * surface->h);
+ if (!cmp->compositor->image.data) goto err;
+ compositors.push(cmp);
+ }
+
+ //Boundary Check
+ if (x + w > sw) w = (sw - x);
+ if (y + h > sh) h = (sh - y);
+
+ TVGLOG("SW_ENGINE", "Using intermediate composition [Region: %d %d %d %d]", x, y, w, h);
+
+ cmp->compositor->recoverSfc = surface;
+ cmp->compositor->recoverCmp = surface->compositor;
+ cmp->compositor->valid = false;
+ cmp->compositor->bbox.min.x = x;
+ cmp->compositor->bbox.min.y = y;
+ cmp->compositor->bbox.max.x = x + w;
+ cmp->compositor->bbox.max.y = y + h;
+ cmp->compositor->image.stride = surface->stride;
+ cmp->compositor->image.w = surface->w;
+ cmp->compositor->image.h = surface->h;
+ cmp->compositor->image.direct = true;
+
+ //We know partial clear region
+ cmp->buffer = cmp->compositor->image.data + (cmp->stride * y + x);
+ cmp->w = w;
+ cmp->h = h;
+
+ rasterClear(cmp);
+
+ //Recover context
+ cmp->buffer = cmp->compositor->image.data;
+ cmp->w = cmp->compositor->image.w;
+ cmp->h = cmp->compositor->image.h;
+
+ //Switch render target
+ surface = cmp;
+
+ return cmp->compositor;
+
+err:
+ if (cmp) {
+ if (cmp->compositor) delete(cmp->compositor);
+ delete(cmp);
+ }
+
+ return nullptr;
+}
+
+
+bool SwRenderer::endComposite(Compositor* cmp)
+{
+ if (!cmp) return false;
+
+ auto p = static_cast<SwCompositor*>(cmp);
+ p->valid = true;
+
+ //Recover Context
+ surface = p->recoverSfc;
+ surface->compositor = p->recoverCmp;
+
+ //Default is alpha blending
+ if (p->method == CompositeMethod::None) {
+ return rasterImage(surface, &p->image, nullptr, p->bbox, p->opacity);
+ }
+
+ return true;
+}
+
+
+bool SwRenderer::dispose(RenderData data)
+{
+ auto task = static_cast<SwTask*>(data);
+ if (!task) return true;
+ task->done();
+ task->dispose();
+
+ if (task->pushed) task->disposed = true;
+ else delete(task);
+
+ return true;
+}
+
+
+void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags)
+{
+ if (!surface) return task;
+ if (flags == RenderUpdateFlag::None) return task;
+
+ //Finish previous task if it has duplicated request.
+ task->done();
+
+ if (clips.count > 0) {
+ //Guarantee composition targets get ready.
+ for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
+ static_cast<SwShapeTask*>(*clip)->done();
+ }
+ task->clips = clips;
+ }
+
+ if (transform) {
+ if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
+ *task->transform = transform->m;
+ } else {
+ if (task->transform) free(task->transform);
+ task->transform = nullptr;
+ }
+
+ task->opacity = opacity;
+ task->surface = surface;
+ task->mpool = mpool;
+ task->flags = flags;
+ task->bbox.min.x = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
+ task->bbox.min.y = max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
+ task->bbox.max.x = min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
+ task->bbox.max.y = min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
+
+ if (!task->pushed) {
+ task->pushed = true;
+ tasks.push(task);
+ }
+
+ TaskScheduler::request(task);
+
+ return task;
+}
+
+
+RenderData SwRenderer::prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
+{
+ //prepare task
+ auto task = static_cast<SwImageTask*>(data);
+ if (!task) {
+ task = new SwImageTask;
+ if (flags & RenderUpdateFlag::Image) {
+ task->image.data = image->buffer;
+ task->image.w = image->w;
+ task->image.h = image->h;
+ task->image.stride = image->stride;
+ }
+ }
+ return prepareCommon(task, transform, opacity, clips, flags);
+}
+
+
+RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
+{
+ //prepare task
+ auto task = static_cast<SwShapeTask*>(data);
+ if (!task) {
+ task = new SwShapeTask;
+ task->sdata = &sdata;
+ }
+ return prepareCommon(task, transform, opacity, clips, flags);
+}
+
+
+SwRenderer::SwRenderer():mpool(globalMpool)
+{
+}
+
+
+bool SwRenderer::init(uint32_t threads)
+{
+ if ((initEngineCnt++) > 0) return true;
+
+ threadsCnt = threads;
+
+ //Share the memory pool among the renderer
+ globalMpool = mpoolInit(threads);
+ if (!globalMpool) {
+ --initEngineCnt;
+ return false;
+ }
+
+ return true;
+}
+
+
+int32_t SwRenderer::init()
+{
+ return initEngineCnt;
+}
+
+
+bool SwRenderer::term()
+{
+ if ((--initEngineCnt) > 0) return true;
+
+ initEngineCnt = 0;
+
+ _termEngine();
+
+ return true;
+}
+
+SwRenderer* SwRenderer::gen()
+{
+ ++rendererCnt;
+ return new SwRenderer();
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
new file mode 100644
index 0000000000..3f883ac409
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SW_RENDERER_H_
+#define _TVG_SW_RENDERER_H_
+
+#include "tvgRender.h"
+
+struct SwSurface;
+struct SwTask;
+struct SwCompositor;
+struct SwMpool;
+
+namespace tvg
+{
+
+class SwRenderer : public RenderMethod
+{
+public:
+ RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
+ RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
+ bool preRender() override;
+ bool renderShape(RenderData data) override;
+ bool renderImage(RenderData data) override;
+ bool postRender() override;
+ bool dispose(RenderData data) override;
+ RenderRegion region(RenderData data) override;
+ RenderRegion viewport() override;
+ bool viewport(const RenderRegion& vp) override;
+
+ bool clear() override;
+ bool sync() override;
+ bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs);
+ bool mempool(bool shared);
+
+ Compositor* target(const RenderRegion& region) override;
+ bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override;
+ bool endComposite(Compositor* cmp) override;
+ void clearCompositors();
+
+ static SwRenderer* gen();
+ static bool init(uint32_t threads);
+ static int32_t init();
+ static bool term();
+
+private:
+ SwSurface* surface = nullptr; //active surface
+ Array<SwTask*> tasks; //async task list
+ Array<SwSurface*> compositors; //render targets cache list
+ SwMpool* mpool; //private memory pool
+ RenderRegion vport; //viewport
+
+ bool sharedMpool = true; //memory-pool behavior policy
+
+ SwRenderer();
+ ~SwRenderer();
+
+ RenderData prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array<RenderData>& clips, RenderUpdateFlag flags);
+};
+
+}
+
+#endif /* _TVG_SW_RENDERER_H_ */
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
new file mode 100644
index 0000000000..b41e48b7ca
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp
@@ -0,0 +1,1043 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * The FreeType Project LICENSE
+ * ----------------------------
+
+ * 2006-Jan-27
+
+ * Copyright 1996-2002, 2006 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg
+
+
+
+ * Introduction
+ * ============
+
+ * The FreeType Project is distributed in several archive packages;
+ * some of them may contain, in addition to the FreeType font engine,
+ * various tools and contributions which rely on, or relate to, the
+ * FreeType Project.
+
+ * This license applies to all files found in such packages, and
+ * which do not fall under their own explicit license. The license
+ * affects thus the FreeType font engine, the test programs,
+ * documentation and makefiles, at the very least.
+
+ * This license was inspired by the BSD, Artistic, and IJG
+ * (Independent JPEG Group) licenses, which all encourage inclusion
+ * and use of free software in commercial and freeware products
+ * alike. As a consequence, its main points are that:
+
+ * o We don't promise that this software works. However, we will be
+ * interested in any kind of bug reports. (`as is' distribution)
+
+ * o You can use this software for whatever you want, in parts or
+ * full form, without having to pay us. (`royalty-free' usage)
+
+ * o You may not pretend that you wrote this software. If you use
+ * it, or only parts of it, in a program, you must acknowledge
+ * somewhere in your documentation that you have used the
+ * FreeType code. (`credits')
+
+ * We specifically permit and encourage the inclusion of this
+ * software, with or without modifications, in commercial products.
+ * We disclaim all warranties covering The FreeType Project and
+ * assume no liability related to The FreeType Project.
+
+
+ * Finally, many people asked us for a preferred form for a
+ * credit/disclaimer to use in compliance with this license. We thus
+ * encourage you to use the following text:
+
+ * """
+ * Portions of this software are copyright � <year> The FreeType
+ * Project (www.freetype.org). All rights reserved.
+ * """
+
+ * Please replace <year> with the value from the FreeType version you
+ * actually use.
+
+* Legal Terms
+* ===========
+
+* 0. Definitions
+* --------------
+
+* Throughout this license, the terms `package', `FreeType Project',
+* and `FreeType archive' refer to the set of files originally
+* distributed by the authors (David Turner, Robert Wilhelm, and
+* Werner Lemberg) as the `FreeType Project', be they named as alpha,
+* beta or final release.
+
+* `You' refers to the licensee, or person using the project, where
+* `using' is a generic term including compiling the project's source
+* code as well as linking it to form a `program' or `executable'.
+* This program is referred to as `a program using the FreeType
+* engine'.
+
+* This license applies to all files distributed in the original
+* FreeType Project, including all source code, binaries and
+* documentation, unless otherwise stated in the file in its
+* original, unmodified form as distributed in the original archive.
+* If you are unsure whether or not a particular file is covered by
+* this license, you must contact us to verify this.
+
+* The FreeType Project is copyright (C) 1996-2000 by David Turner,
+* Robert Wilhelm, and Werner Lemberg. All rights reserved except as
+* specified below.
+
+* 1. No Warranty
+* --------------
+
+* THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
+* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
+* USE, OF THE FREETYPE PROJECT.
+
+* 2. Redistribution
+* -----------------
+
+* This license grants a worldwide, royalty-free, perpetual and
+* irrevocable right and license to use, execute, perform, compile,
+* display, copy, create derivative works of, distribute and
+* sublicense the FreeType Project (in both source and object code
+* forms) and derivative works thereof for any purpose; and to
+* authorize others to exercise some or all of the rights granted
+* herein, subject to the following conditions:
+
+* o Redistribution of source code must retain this license file
+* (`FTL.TXT') unaltered; any additions, deletions or changes to
+* the original files must be clearly indicated in accompanying
+* documentation. The copyright notices of the unaltered,
+* original files must be preserved in all copies of source
+* files.
+
+* o Redistribution in binary form must provide a disclaimer that
+* states that the software is based in part of the work of the
+* FreeType Team, in the distribution documentation. We also
+* encourage you to put an URL to the FreeType web page in your
+* documentation, though this isn't mandatory.
+
+* These conditions apply to any software derived from or based on
+* the FreeType Project, not just the unmodified files. If you use
+* our work, you must acknowledge us. However, no fee need be paid
+* to us.
+
+* 3. Advertising
+* --------------
+
+* Neither the FreeType authors and contributors nor you shall use
+* the name of the other for commercial, advertising, or promotional
+* purposes without specific prior written permission.
+
+* We suggest, but do not require, that you use one or more of the
+* following phrases to refer to this software in your documentation
+* or advertising materials: `FreeType Project', `FreeType Engine',
+* `FreeType library', or `FreeType Distribution'.
+
+* As you have not signed this license, you are not required to
+* accept it. However, as the FreeType Project is copyrighted
+* material, only this license, or another one contracted with the
+* authors, grants you the right to use, distribute, and modify it.
+* Therefore, by using, distributing, or modifying the FreeType
+* Project, you indicate that you understand and accept all the terms
+* of this license.
+
+* 4. Contacts
+* -----------
+
+* There are two mailing lists related to FreeType:
+
+* o freetype@nongnu.org
+
+* Discusses general use and applications of FreeType, as well as
+* future and wanted additions to the library and distribution.
+* If you are looking for support, start in this list if you
+* haven't found anything to help you in the documentation.
+
+* o freetype-devel@nongnu.org
+
+* Discusses bugs, as well as engine internals, design issues,
+* specific licenses, porting, etc.
+
+* Our home page can be found at
+
+* http://www.freetype.org
+*/
+
+#include <setjmp.h>
+#include <limits.h>
+#include <memory.h>
+#include "tvgSwCommon.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+constexpr auto MAX_SPANS = 256;
+constexpr auto PIXEL_BITS = 8; //must be at least 6 bits!
+constexpr auto ONE_PIXEL = (1L << PIXEL_BITS);
+
+using Area = long;
+
+struct Band
+{
+ SwCoord min, max;
+};
+
+struct Cell
+{
+ SwCoord x;
+ SwCoord cover;
+ Area area;
+ Cell *next;
+};
+
+struct RleWorker
+{
+ SwRleData* rle;
+
+ SwPoint cellPos;
+ SwPoint cellMin;
+ SwPoint cellMax;
+ SwCoord cellXCnt;
+ SwCoord cellYCnt;
+
+ Area area;
+ SwCoord cover;
+
+ Cell* cells;
+ ptrdiff_t maxCells;
+ ptrdiff_t cellsCnt;
+
+ SwPoint pos;
+
+ SwPoint bezStack[32 * 3 + 1];
+ int levStack[32];
+
+ SwOutline* outline;
+
+ SwSpan spans[MAX_SPANS];
+ int spansCnt;
+ int ySpan;
+
+ int bandSize;
+ int bandShoot;
+
+ jmp_buf jmpBuf;
+
+ void* buffer;
+ long bufferSize;
+
+ Cell** yCells;
+ SwCoord yCnt;
+
+ bool invalid;
+ bool antiAlias;
+};
+
+
+static inline SwPoint UPSCALE(const SwPoint& pt)
+{
+ return {SwCoord(((unsigned long) pt.x) << (PIXEL_BITS - 6)), SwCoord(((unsigned long) pt.y) << (PIXEL_BITS - 6))};
+}
+
+
+static inline SwPoint TRUNC(const SwPoint& pt)
+{
+ return {pt.x >> PIXEL_BITS, pt.y >> PIXEL_BITS};
+}
+
+
+static inline SwCoord TRUNC(const SwCoord x)
+{
+ return x >> PIXEL_BITS;
+}
+
+
+static inline SwPoint SUBPIXELS(const SwPoint& pt)
+{
+ return {SwCoord(((unsigned long) pt.x) << PIXEL_BITS), SwCoord(((unsigned long) pt.y) << PIXEL_BITS)};
+}
+
+
+static inline SwCoord SUBPIXELS(const SwCoord x)
+{
+ return SwCoord(((unsigned long) x) << PIXEL_BITS);
+}
+
+/*
+ * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
+ * algorithm. We use alpha = 1, beta = 3/8, giving us results with a
+ * largest error less than 7% compared to the exact value.
+ */
+static inline SwCoord HYPOT(SwPoint pt)
+{
+ if (pt.x < 0) pt.x = -pt.x;
+ if (pt.y < 0) pt.y = -pt.y;
+ return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3)));
+}
+
+static void _genSpan(SwRleData* rle, const SwSpan* spans, uint32_t count)
+{
+ auto newSize = rle->size + count;
+
+ /* allocate enough memory for new spans */
+ /* alloc is required to prevent free and reallocation */
+ /* when the rle needs to be regenerated because of attribute change. */
+ if (rle->alloc < newSize) {
+ rle->alloc = (newSize * 2);
+ //OPTIMIZE: use mempool!
+ rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
+ }
+
+ //copy the new spans to the allocated memory
+ SwSpan* lastSpan = rle->spans + rle->size;
+ memcpy(lastSpan, spans, count * sizeof(SwSpan));
+
+ rle->size = newSize;
+}
+
+
+static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount)
+{
+ x += rw.cellMin.x;
+ y += rw.cellMin.y;
+
+ //Clip Y range
+ if (y < rw.cellMin.y || y >= rw.cellMax.y) return;
+
+ /* compute the coverage line's coverage, depending on the outline fill rule */
+ /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
+ auto coverage = static_cast<int>(area >> (PIXEL_BITS * 2 + 1 - 8)); //range 0 - 255
+
+ if (coverage < 0) coverage = -coverage;
+
+ if (rw.outline->fillRule == FillRule::EvenOdd) {
+ coverage &= 511;
+ if (coverage > 255) coverage = 511 - coverage;
+ } else {
+ //normal non-zero winding rule
+ if (coverage > 255) coverage = 255;
+ }
+
+ //span has ushort coordinates. check limit overflow
+ if (x >= SHRT_MAX) {
+ TVGERR("SW_ENGINE", "X-coordiante overflow!");
+ x = SHRT_MAX;
+ }
+ if (y >= SHRT_MAX) {
+ TVGERR("SW_ENGINE", "Y Coordiante overflow!");
+ y = SHRT_MAX;
+ }
+
+ if (coverage > 0) {
+ if (!rw.antiAlias) coverage = 255;
+ auto count = rw.spansCnt;
+ auto span = rw.spans + count - 1;
+
+ //see whether we can add this span to the current list
+ if ((count > 0) && (rw.ySpan == y) &&
+ (span->x + span->len == x) && (span->coverage == coverage)) {
+
+ //Clip x range
+ SwCoord xOver = 0;
+ if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
+ if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
+
+ //span->len += (acount + xOver) - 1;
+ span->len += (acount + xOver);
+ return;
+ }
+
+ if (count >= MAX_SPANS) {
+ _genSpan(rw.rle, rw.spans, count);
+ rw.spansCnt = 0;
+ rw.ySpan = 0;
+ span = rw.spans;
+ } else {
+ ++span;
+ }
+
+ //Clip x range
+ SwCoord xOver = 0;
+ if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
+ if (x < rw.cellMin.x) {
+ xOver -= (rw.cellMin.x - x);
+ x = rw.cellMin.x;
+ }
+
+ //Nothing to draw
+ if (acount + xOver <= 0) return;
+
+ //add a span to the current list
+ span->x = x;
+ span->y = y;
+ span->len = (acount + xOver);
+ span->coverage = coverage;
+ ++rw.spansCnt;
+ rw.ySpan = y;
+ }
+}
+
+
+static void _sweep(RleWorker& rw)
+{
+ if (rw.cellsCnt == 0) return;
+
+ rw.spansCnt = 0;
+ rw.ySpan = 0;
+
+ for (int y = 0; y < rw.yCnt; ++y) {
+ auto cover = 0;
+ auto x = 0;
+ auto cell = rw.yCells[y];
+
+ while (cell) {
+ if (cell->x > x && cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), cell->x - x);
+ cover += cell->cover;
+ auto area = cover * (ONE_PIXEL * 2) - cell->area;
+ if (area != 0 && cell->x >= 0) _horizLine(rw, cell->x, y, area, 1);
+ x = cell->x + 1;
+ cell = cell->next;
+ }
+
+ if (cover != 0) _horizLine(rw, x, y, cover * (ONE_PIXEL * 2), rw.cellXCnt - x);
+ }
+
+ if (rw.spansCnt > 0) _genSpan(rw.rle, rw.spans, rw.spansCnt);
+}
+
+
+static Cell* _findCell(RleWorker& rw)
+{
+ auto x = rw.cellPos.x;
+ if (x > rw.cellXCnt) x = rw.cellXCnt;
+
+ auto pcell = &rw.yCells[rw.cellPos.y];
+
+ while(true) {
+ Cell* cell = *pcell;
+ if (!cell || cell->x > x) break;
+ if (cell->x == x) return cell;
+ pcell = &cell->next;
+ }
+
+ if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1);
+
+ auto cell = rw.cells + rw.cellsCnt++;
+ cell->x = x;
+ cell->area = 0;
+ cell->cover = 0;
+ cell->next = *pcell;
+ *pcell = cell;
+
+ return cell;
+}
+
+
+static void _recordCell(RleWorker& rw)
+{
+ if (rw.area | rw.cover) {
+ auto cell = _findCell(rw);
+ cell->area += rw.area;
+ cell->cover += rw.cover;
+ }
+}
+
+
+static void _setCell(RleWorker& rw, SwPoint pos)
+{
+ /* Move the cell pointer to a new position. We set the `invalid' */
+ /* flag to indicate that the cell isn't part of those we're interested */
+ /* in during the render phase. This means that: */
+ /* */
+ /* . the new vertical position must be within min_ey..max_ey-1. */
+ /* . the new horizontal position must be strictly less than max_ex */
+ /* */
+ /* Note that if a cell is to the left of the clipping region, it is */
+ /* actually set to the (min_ex-1) horizontal position. */
+
+ /* All cells that are on the left of the clipping region go to the
+ min_ex - 1 horizontal position. */
+ pos.x -= rw.cellMin.x;
+ pos.y -= rw.cellMin.y;
+
+ if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
+
+ //Are we moving to a different cell?
+ if (pos != rw.cellPos) {
+ //Record the current one if it is valid
+ if (!rw.invalid) _recordCell(rw);
+ }
+
+ rw.area = 0;
+ rw.cover = 0;
+ rw.cellPos = pos;
+ rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt);
+}
+
+
+static void _startCell(RleWorker& rw, SwPoint pos)
+{
+ if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
+ if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x;
+
+ rw.area = 0;
+ rw.cover = 0;
+ rw.cellPos = pos - rw.cellMin;
+ rw.invalid = false;
+
+ _setCell(rw, pos);
+}
+
+
+static void _moveTo(RleWorker& rw, const SwPoint& to)
+{
+ //record current cell, if any */
+ if (!rw.invalid) _recordCell(rw);
+
+ //start to a new position
+ _startCell(rw, TRUNC(to));
+
+ rw.pos = to;
+}
+
+
+static void _lineTo(RleWorker& rw, const SwPoint& to)
+{
+#define SW_UDIV(a, b) \
+ static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \
+ (sizeof(long) * CHAR_BIT - PIXEL_BITS))
+
+ auto e1 = TRUNC(rw.pos);
+ auto e2 = TRUNC(to);
+
+ //vertical clipping
+ if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) {
+ rw.pos = to;
+ return;
+ }
+
+ auto diff = to - rw.pos;
+ auto f1 = rw.pos - SUBPIXELS(e1);
+ SwPoint f2;
+
+ //inside one cell
+ if (e1 == e2) {
+ ;
+ //any horizontal line
+ } else if (diff.y == 0) {
+ e1.x = e2.x;
+ _setCell(rw, e1);
+ } else if (diff.x == 0) {
+ //vertical line up
+ if (diff.y > 0) {
+ do {
+ f2.y = ONE_PIXEL;
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * f1.x * 2;
+ f1.y = 0;
+ ++e1.y;
+ _setCell(rw, e1);
+ } while(e1.y != e2.y);
+ //vertical line down
+ } else {
+ do {
+ f2.y = 0;
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * f1.x * 2;
+ f1.y = ONE_PIXEL;
+ --e1.y;
+ _setCell(rw, e1);
+ } while(e1.y != e2.y);
+ }
+ //any other line
+ } else {
+ Area prod = diff.x * f1.y - diff.y * f1.x;
+
+ /* These macros speed up repetitive divisions by replacing them
+ with multiplications and right shifts. */
+ auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
+ auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
+
+ /* The fundamental value `prod' determines which side and the */
+ /* exact coordinate where the line exits current cell. It is */
+ /* also easily updated when moving from one cell to the next. */
+ do {
+ auto px = diff.x * ONE_PIXEL;
+ auto py = diff.y * ONE_PIXEL;
+
+ //left
+ if (prod <= 0 && prod - px > 0) {
+ f2 = {0, SW_UDIV(-prod, -dx_r)};
+ prod -= py;
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ f1 = {ONE_PIXEL, f2.y};
+ --e1.x;
+ //up
+ } else if (prod - px <= 0 && prod - px + py > 0) {
+ prod -= px;
+ f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ f1 = {f2.x, 0};
+ ++e1.y;
+ //right
+ } else if (prod - px + py <= 0 && prod + py >= 0) {
+ prod += py;
+ f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ f1 = {0, f2.y};
+ ++e1.x;
+ //down
+ } else {
+ f2 = {SW_UDIV(prod, -dy_r), 0};
+ prod += px;
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ f1 = {f2.x, ONE_PIXEL};
+ --e1.y;
+ }
+
+ _setCell(rw, e1);
+
+ } while(e1 != e2);
+ }
+
+ f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)};
+ rw.cover += (f2.y - f1.y);
+ rw.area += (f2.y - f1.y) * (f1.x + f2.x);
+ rw.pos = to;
+}
+
+
+static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
+{
+ auto arc = rw.bezStack;
+ arc[0] = to;
+ arc[1] = ctrl2;
+ arc[2] = ctrl1;
+ arc[3] = rw.pos;
+
+ //Short-cut the arc that crosses the current band
+ auto min = arc[0].y;
+ auto max = arc[0].y;
+
+ SwCoord y;
+ for (auto i = 1; i < 4; ++i) {
+ y = arc[i].y;
+ if (y < min) min = y;
+ if (y > max) max = y;
+ }
+
+ if (TRUNC(min) >= rw.cellMax.y || TRUNC(max) < rw.cellMin.y) goto draw;
+
+ /* Decide whether to split or draw. See `Rapid Termination */
+ /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */
+ /* F. Hain, at */
+ /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */
+ while (true) {
+ {
+ //diff is the P0 - P3 chord vector
+ auto diff = arc[3] - arc[0];
+ auto L = HYPOT(diff);
+
+ //avoid possible arithmetic overflow below by splitting
+ if (L > SHRT_MAX) goto split;
+
+ //max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1)
+ auto sLimit = L * (ONE_PIXEL / 6);
+
+ auto diff1 = arc[1] - arc[0];
+ auto s = diff.y * diff1.x - diff.x * diff1.y;
+ if (s < 0) s = -s;
+ if (s > sLimit) goto split;
+
+ //s is L * the perpendicular distance from P2 to the line P0 - P3
+ auto diff2 = arc[2] - arc[0];
+ s = diff.y * diff2.x - diff.x * diff2.y;
+ if (s < 0) s = -s;
+ if (s > sLimit) goto split;
+
+ /* Split super curvy segments where the off points are so far
+ from the chord that the angles P0-P1-P3 or P0-P2-P3 become
+ acute as detected by appropriate dot products */
+ if (diff1.x * (diff1.x - diff.x) + diff1.y * (diff1.y - diff.y) > 0 ||
+ diff2.x * (diff2.x - diff.x) + diff2.y * (diff2.y - diff.y) > 0)
+ goto split;
+
+ //no reason to split
+ goto draw;
+ }
+ split:
+ mathSplitCubic(arc);
+ arc += 3;
+ continue;
+
+ draw:
+ _lineTo(rw, arc[0]);
+ if (arc == rw.bezStack) return;
+ arc -= 3;
+ }
+}
+
+
+static bool _decomposeOutline(RleWorker& rw)
+{
+ auto outline = rw.outline;
+ auto first = 0; //index of first point in contour
+
+ for (uint32_t n = 0; n < outline->cntrsCnt; ++n) {
+ auto last = outline->cntrs[n];
+ auto limit = outline->pts + last;
+ auto start = UPSCALE(outline->pts[first]);
+ auto pt = outline->pts + first;
+ auto types = outline->types + first;
+
+ /* A contour cannot start with a cubic control point! */
+ if (types[0] == SW_CURVE_TYPE_CUBIC) goto invalid_outline;
+
+ _moveTo(rw, UPSCALE(outline->pts[first]));
+
+ while (pt < limit) {
+ ++pt;
+ ++types;
+
+ //emit a single line_to
+ if (types[0] == SW_CURVE_TYPE_POINT) {
+ _lineTo(rw, UPSCALE(*pt));
+ //types cubic
+ } else {
+ if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC)
+ goto invalid_outline;
+
+ pt += 2;
+ types += 2;
+
+ if (pt <= limit) {
+ _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
+ continue;
+ }
+ _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
+ goto close;
+ }
+ }
+ _lineTo(rw, start);
+ close:
+ first = last + 1;
+ }
+
+ return true;
+
+invalid_outline:
+ TVGERR("SW_ENGINE", "Invalid Outline!");
+ return false;
+}
+
+
+static int _genRle(RleWorker& rw)
+{
+ if (setjmp(rw.jmpBuf) == 0) {
+ auto ret = _decomposeOutline(rw);
+ if (!rw.invalid) _recordCell(rw);
+ if (ret) return 0; //success
+ else return 1; //fail
+ }
+ return -1; //lack of cell memory
+}
+
+
+SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
+{
+ auto out = outSpans;
+ auto spans = targetRle->spans;
+ auto end = targetRle->spans + targetRle->size;
+ auto clipSpans = clip->spans;
+ auto clipEnd = clip->spans + clip->size;
+
+ while (spanCnt > 0 && spans < end) {
+ if (clipSpans == clipEnd) {
+ spans = end;
+ break;
+ }
+ if (clipSpans->y > spans->y) {
+ ++spans;
+ continue;
+ }
+ if (spans->y != clipSpans->y) {
+ ++clipSpans;
+ continue;
+ }
+ auto sx1 = spans->x;
+ auto sx2 = sx1 + spans->len;
+ auto cx1 = clipSpans->x;
+ auto cx2 = cx1 + clipSpans->len;
+
+ if (cx1 < sx1 && cx2 < sx1) {
+ ++clipSpans;
+ continue;
+ }
+ else if (sx1 < cx1 && sx2 < cx1) {
+ ++spans;
+ continue;
+ }
+ auto x = sx1 > cx1 ? sx1 : cx1;
+ auto len = (sx2 < cx2 ? sx2 : cx2) - x;
+ if (len) {
+ auto spansCorverage = spans->coverage;
+ auto clipSpansCoverage = clipSpans->coverage;
+ out->x = sx1 > cx1 ? sx1 : cx1;
+ out->len = (sx2 < cx2 ? sx2 : cx2) - out->x;
+ out->y = spans->y;
+ out->coverage = (uint8_t)(((spansCorverage * clipSpansCoverage) + 0xff) >> 8);
+ ++out;
+ --spanCnt;
+ }
+ if (sx2 < cx2) ++spans;
+ else ++clipSpans;
+ }
+ return out;
+}
+
+
+SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t spanCnt)
+{
+ auto out = outSpans;
+ auto spans = targetRle->spans;
+ auto end = targetRle->spans + targetRle->size;
+ auto minx = static_cast<int16_t>(bbox->min.x);
+ auto miny = static_cast<int16_t>(bbox->min.y);
+ auto maxx = minx + static_cast<int16_t>(bbox->max.x - bbox->min.x) - 1;
+ auto maxy = miny + static_cast<int16_t>(bbox->max.y - bbox->min.y) - 1;
+
+ while (spanCnt && spans < end) {
+ if (spans->y > maxy) {
+ spans = end;
+ break;
+ }
+ if (spans->y < miny || spans->x > maxx || spans->x + spans->len <= minx) {
+ ++spans;
+ continue;
+ }
+ if (spans->x < minx) {
+ out->len = (spans->len - (minx - spans->x)) < (maxx - minx + 1) ? (spans->len - (minx - spans->x)) : (maxx - minx + 1);
+ out->x = minx;
+ }
+ else {
+ out->x = spans->x;
+ out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1);
+ }
+ if (out->len != 0) {
+ out->y = spans->y;
+ out->coverage = spans->coverage;
+ ++out;
+ }
+ ++spans;
+ --spanCnt;
+ }
+ return out;
+}
+
+
+void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
+{
+ free(rle->spans);
+ rle->spans = clippedSpans;
+ rle->size = rle->alloc = size;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
+{
+ constexpr auto RENDER_POOL_SIZE = 16384L;
+ constexpr auto BAND_SIZE = 40;
+
+ //TODO: We can preserve several static workers in advance
+ RleWorker rw;
+ Cell buffer[RENDER_POOL_SIZE / sizeof(Cell)];
+
+ //Init Cells
+ rw.buffer = buffer;
+ rw.bufferSize = sizeof(buffer);
+ rw.yCells = reinterpret_cast<Cell**>(buffer);
+ rw.cells = nullptr;
+ rw.maxCells = 0;
+ rw.cellsCnt = 0;
+ rw.area = 0;
+ rw.cover = 0;
+ rw.invalid = true;
+ rw.cellMin = renderRegion.min;
+ rw.cellMax = renderRegion.max;
+ rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
+ rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
+ rw.ySpan = 0;
+ rw.outline = const_cast<SwOutline*>(outline);
+ rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64
+ rw.bandShoot = 0;
+ rw.antiAlias = antiAlias;
+
+ if (!rle) rw.rle = reinterpret_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
+ else rw.rle = rle;
+
+ //Generate RLE
+ Band bands[BAND_SIZE];
+ Band* band;
+
+ /* set up vertical bands */
+ auto bandCnt = static_cast<int>((rw.cellMax.y - rw.cellMin.y) / rw.bandSize);
+ if (bandCnt == 0) bandCnt = 1;
+ else if (bandCnt >= BAND_SIZE) bandCnt = (BAND_SIZE - 1);
+
+ auto min = rw.cellMin.y;
+ auto yMax = rw.cellMax.y;
+ SwCoord max;
+ int ret;
+
+ for (int n = 0; n < bandCnt; ++n, min = max) {
+ max = min + rw.bandSize;
+ if (n == bandCnt -1 || max > yMax) max = yMax;
+
+ bands[0].min = min;
+ bands[0].max = max;
+ band = bands;
+
+ while (band >= bands) {
+ rw.yCells = static_cast<Cell**>(rw.buffer);
+ rw.yCnt = band->max - band->min;
+
+ int cellStart = sizeof(Cell*) * (int)rw.yCnt;
+ int cellMod = cellStart % sizeof(Cell);
+
+ if (cellMod > 0) cellStart += sizeof(Cell) - cellMod;
+
+ auto cellEnd = rw.bufferSize;
+ cellEnd -= cellEnd % sizeof(Cell);
+
+ auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + cellEnd);
+ rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
+
+ if (rw.cells >= cellsMax) goto reduce_bands;
+
+ rw.maxCells = cellsMax - rw.cells;
+ if (rw.maxCells < 2) goto reduce_bands;
+
+ for (int y = 0; y < rw.yCnt; ++y)
+ rw.yCells[y] = nullptr;
+
+ rw.cellsCnt = 0;
+ rw.invalid = true;
+ rw.cellMin.y = band->min;
+ rw.cellMax.y = band->max;
+ rw.cellYCnt = band->max - band->min;
+
+ ret = _genRle(rw);
+ if (ret == 0) {
+ _sweep(rw);
+ --band;
+ continue;
+ } else if (ret == 1) {
+ goto error;
+ }
+
+ reduce_bands:
+ /* render pool overflow: we will reduce the render band by half */
+ auto bottom = band->min;
+ auto top = band->max;
+ auto middle = bottom + ((top - bottom) >> 1);
+
+ /* This is too complex for a single scanline; there must
+ be some problems */
+ if (middle == bottom) goto error;
+
+ if (bottom - top >= rw.bandSize) ++rw.bandShoot;
+
+ band[1].min = bottom;
+ band[1].max = middle;
+ band[0].min = middle;
+ band[0].max = top;
+ ++band;
+ }
+ }
+
+ if (rw.bandShoot > 8 && rw.bandSize > 16)
+ rw.bandSize = (rw.bandSize >> 1);
+
+ return rw.rle;
+
+error:
+ free(rw.rle);
+ rw.rle = nullptr;
+ return nullptr;
+}
+
+
+void rleReset(SwRleData* rle)
+{
+ if (!rle) return;
+ rle->size = 0;
+}
+
+
+void rleFree(SwRleData* rle)
+{
+ if (!rle) return;
+ if (rle->spans) free(rle->spans);
+ free(rle);
+}
+
+
+void rleClipPath(SwRleData *rle, const SwRleData *clip)
+{
+ if (rle->size == 0 || clip->size == 0) return;
+ auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
+ auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
+ if (!spans) return;
+ auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
+
+ _replaceClipSpan(rle, spans, spansEnd - spans);
+
+ TVGLOG("SW_ENGINE", "Using ClipPath!");
+}
+
+
+void rleClipRect(SwRleData *rle, const SwBBox* clip)
+{
+ if (rle->size == 0) return;
+ auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
+ if (!spans) return;
+ auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
+
+ _replaceClipSpan(rle, spans, spansEnd - spans);
+
+ TVGLOG("SW_ENGINE", "Using ClipRect!");
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
new file mode 100644
index 0000000000..7b49c232b1
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp
@@ -0,0 +1,664 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgSwCommon.h"
+#include "tvgBezier.h"
+#include <float.h>
+#include <math.h>
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct Line
+{
+ Point pt1;
+ Point pt2;
+};
+
+
+static float _lineLength(const Point& pt1, const Point& pt2)
+{
+ /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
+ With alpha = 1, beta = 3/8, giving results with the largest error less
+ than 7% compared to the exact value. */
+ Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
+ if (diff.x < 0) diff.x = -diff.x;
+ if (diff.y < 0) diff.y = -diff.y;
+ return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
+}
+
+
+static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right)
+{
+ auto len = _lineLength(cur.pt1, cur.pt2);
+ auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
+ auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
+ left.pt1 = cur.pt1;
+ left.pt2.x = left.pt1.x + dx;
+ left.pt2.y = left.pt1.y + dy;
+ right.pt1 = left.pt2;
+ right.pt2 = cur.pt2;
+}
+
+
+static bool _growOutlineContour(SwOutline& outline, uint32_t n)
+{
+ if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return false;
+ outline.reservedCntrsCnt = outline.cntrsCnt + n;
+ outline.cntrs = static_cast<uint16_t*>(realloc(outline.cntrs, outline.reservedCntrsCnt * sizeof(uint16_t)));
+ return true;
+}
+
+
+static void _reserveOutlineClose(SwOutline& outline)
+{
+ //Dash outlines are always opened.
+ //Only normal outlines use this information, it sholud be same to their contour counts.
+ if (outline.closed) free(outline.closed);
+ outline.closed = static_cast<bool*>(calloc(outline.reservedCntrsCnt, sizeof(bool)));
+}
+
+
+static void _resetOutlineClose(SwOutline& outline)
+{
+ memset(outline.closed, 0x0, outline.reservedCntrsCnt * sizeof(bool));
+}
+
+
+static void _growOutlinePoint(SwOutline& outline, uint32_t n)
+{
+ if (outline.reservedPtsCnt >= outline.ptsCnt + n) return;
+ outline.reservedPtsCnt = outline.ptsCnt + n;
+ outline.pts = static_cast<SwPoint*>(realloc(outline.pts, outline.reservedPtsCnt * sizeof(SwPoint)));
+ outline.types = static_cast<uint8_t*>(realloc(outline.types, outline.reservedPtsCnt * sizeof(uint8_t)));
+}
+
+
+static void _outlineEnd(SwOutline& outline)
+{
+ if (outline.ptsCnt == 0) return;
+
+ _growOutlineContour(outline, 1);
+ outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
+ ++outline.cntrsCnt;
+}
+
+
+static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(outline, 1);
+
+ outline.pts[outline.ptsCnt] = mathTransform(to, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
+
+ if (outline.ptsCnt > 0) {
+ _growOutlineContour(outline, 1);
+ outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1;
+ ++outline.cntrsCnt;
+ }
+
+ ++outline.ptsCnt;
+}
+
+
+static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(outline, 1);
+
+ outline.pts[outline.ptsCnt] = mathTransform(to, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline.ptsCnt;
+}
+
+
+static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(outline, 3);
+
+ outline.pts[outline.ptsCnt] = mathTransform(ctrl1, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
+ ++outline.ptsCnt;
+
+ outline.pts[outline.ptsCnt] = mathTransform(ctrl2, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC;
+ ++outline.ptsCnt;
+
+ outline.pts[outline.ptsCnt] = mathTransform(to, transform);
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline.ptsCnt;
+}
+
+
+static void _outlineClose(SwOutline& outline)
+{
+ uint32_t i = 0;
+
+ if (outline.cntrsCnt > 0) {
+ i = outline.cntrs[outline.cntrsCnt - 1] + 1;
+ } else {
+ i = 0; //First Path
+ }
+
+ //Make sure there is at least one point in the current path
+ if (outline.ptsCnt == i) return;
+
+ //Close the path
+ _growOutlinePoint(outline, 1);
+
+ outline.pts[outline.ptsCnt] = outline.pts[i];
+ outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT;
+ ++outline.ptsCnt;
+ outline.closed[outline.cntrsCnt] = true;
+}
+
+
+static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
+ _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
+
+ Line cur = {dash.ptCur, *to};
+ auto len = _lineLength(cur.pt1, cur.pt2);
+
+ if (len < dash.curLen) {
+ dash.curLen -= len;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
+ _outlineLineTo(*dash.outline, to, transform);
+ }
+ } else {
+ while (len > dash.curLen) {
+ len -= dash.curLen;
+ Line left, right;
+ _lineSplitAt(cur, dash.curLen, left, right);;
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &left.pt1, transform);
+ _outlineLineTo(*dash.outline, &left.pt2, transform);
+ }
+ dash.curLen = dash.pattern[dash.curIdx];
+ dash.curOpGap = !dash.curOpGap;
+ cur = right;
+ dash.ptCur = cur.pt1;
+ }
+ //leftovers
+ dash.curLen -= len;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &cur.pt1, transform);
+ _outlineLineTo(*dash.outline, &cur.pt2, transform);
+ }
+ if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
+ //move to next dash
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
+ dash.curLen = dash.pattern[dash.curIdx];
+ dash.curOpGap = !dash.curOpGap;
+ }
+ }
+ dash.ptCur = *to;
+}
+
+
+static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
+{
+ _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1);
+ _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1);
+
+ Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
+ auto len = bezLength(cur);
+
+ if (len < dash.curLen) {
+ dash.curLen -= len;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &dash.ptCur, transform);
+ _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
+ }
+ } else {
+ while (len > dash.curLen) {
+ Bezier left, right;
+ len -= dash.curLen;
+ bezSplitAt(cur, dash.curLen, left, right);
+ if (!dash.curOpGap) {
+ // leftovers from a previous command don't require moveTo
+ if (dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
+ _outlineMoveTo(*dash.outline, &left.start, transform);
+ }
+ _outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
+ }
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
+ dash.curLen = dash.pattern[dash.curIdx];
+ dash.curOpGap = !dash.curOpGap;
+ cur = right;
+ dash.ptCur = right.start;
+ }
+ //leftovers
+ dash.curLen -= len;
+ if (!dash.curOpGap) {
+ _outlineMoveTo(*dash.outline, &cur.start, transform);
+ _outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform);
+ }
+ if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
+ //move to next dash
+ dash.curIdx = (dash.curIdx + 1) % dash.cnt;
+ dash.curLen = dash.pattern[dash.curIdx];
+ dash.curOpGap = !dash.curOpGap;
+ }
+ }
+ dash.ptCur = *to;
+}
+
+
+static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
+{
+ const PathCommand* cmds = nullptr;
+ auto cmdCnt = sdata->pathCommands(&cmds);
+
+ const Point* pts = nullptr;
+ auto ptsCnt = sdata->pathCoords(&pts);
+
+ //No actual shape data
+ if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
+
+ SwDashStroke dash;
+ dash.curIdx = 0;
+ dash.curLen = 0;
+ dash.ptStart = {0, 0};
+ dash.ptCur = {0, 0};
+ dash.curOpGap = false;
+
+ const float* pattern;
+ dash.cnt = sdata->strokeDash(&pattern);
+ if (dash.cnt == 0) return nullptr;
+
+ //OPTMIZE ME: Use mempool???
+ dash.pattern = const_cast<float*>(pattern);
+ dash.outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline)));
+
+ //smart reservation
+ auto outlinePtsCnt = 0;
+ auto outlineCntrsCnt = 0;
+
+ for (uint32_t i = 0; i < cmdCnt; ++i) {
+ switch (*(cmds + i)) {
+ case PathCommand::Close: {
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::MoveTo: {
+ ++outlineCntrsCnt;
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::LineTo: {
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ outlinePtsCnt += 3;
+ break;
+ }
+ }
+ }
+
+ ++outlinePtsCnt; //for close
+ ++outlineCntrsCnt; //for end
+
+ //No idea exact count.... Reserve Approximitely 20x...
+ _growOutlinePoint(*dash.outline, outlinePtsCnt * 20);
+ _growOutlineContour(*dash.outline, outlineCntrsCnt * 20);
+
+ while (cmdCnt-- > 0) {
+ switch (*cmds) {
+ case PathCommand::Close: {
+ _dashLineTo(dash, &dash.ptStart, transform);
+ break;
+ }
+ case PathCommand::MoveTo: {
+ //reset the dash
+ dash.curIdx = 0;
+ dash.curLen = *dash.pattern;
+ dash.curOpGap = false;
+ dash.ptStart = dash.ptCur = *pts;
+ ++pts;
+ break;
+ }
+ case PathCommand::LineTo: {
+ _dashLineTo(dash, pts, transform);
+ ++pts;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ _dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
+ pts += 3;
+ break;
+ }
+ }
+ ++cmds;
+ }
+
+ _outlineEnd(*dash.outline);
+
+ return dash.outline;
+}
+
+
+static bool _axisAlignedRect(const SwOutline* outline)
+{
+ //Fast Track: axis-aligned rectangle?
+ if (outline->ptsCnt != 5) return false;
+
+ auto pt1 = outline->pts + 0;
+ auto pt2 = outline->pts + 1;
+ auto pt3 = outline->pts + 2;
+ auto pt4 = outline->pts + 3;
+
+ auto a = SwPoint{pt1->x, pt3->y};
+ auto b = SwPoint{pt3->x, pt1->y};
+
+ if ((*pt2 == a && *pt4 == b) || (*pt2 == b && *pt4 == a)) return true;
+
+ return false;
+}
+
+
+
+static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
+{
+ const PathCommand* cmds = nullptr;
+ auto cmdCnt = sdata->pathCommands(&cmds);
+
+ const Point* pts = nullptr;
+ auto ptsCnt = sdata->pathCoords(&pts);
+
+ //No actual shape data
+ if (cmdCnt == 0 || ptsCnt == 0) return false;
+
+ //smart reservation
+ auto outlinePtsCnt = 0;
+ auto outlineCntrsCnt = 0;
+ auto closeCnt = 0;
+
+ for (uint32_t i = 0; i < cmdCnt; ++i) {
+ switch (*(cmds + i)) {
+ case PathCommand::Close: {
+ ++outlinePtsCnt;
+ ++closeCnt;
+ break;
+ }
+ case PathCommand::MoveTo: {
+ ++outlineCntrsCnt;
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::LineTo: {
+ ++outlinePtsCnt;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ outlinePtsCnt += 3;
+ break;
+ }
+ }
+ }
+
+ if (static_cast<uint32_t>(outlinePtsCnt - closeCnt) > ptsCnt) {
+ TVGERR("SW_ENGINE", "Wrong a pair of the commands & points - required(%d), current(%d)", outlinePtsCnt - closeCnt, ptsCnt);
+ return false;
+ }
+
+ ++outlinePtsCnt; //for close
+ ++outlineCntrsCnt; //for end
+
+ shape->outline = mpoolReqOutline(mpool, tid);
+ auto outline = shape->outline;
+
+ _growOutlinePoint(*outline, outlinePtsCnt);
+
+ if (_growOutlineContour(*outline, outlineCntrsCnt)) {
+ _reserveOutlineClose(*outline);
+ } else {
+ _resetOutlineClose(*outline);
+ }
+
+ //Generate Outlines
+ while (cmdCnt-- > 0) {
+ switch (*cmds) {
+ case PathCommand::Close: {
+ _outlineClose(*outline);
+ break;
+ }
+ case PathCommand::MoveTo: {
+ _outlineMoveTo(*outline, pts, transform);
+ ++pts;
+ break;
+ }
+ case PathCommand::LineTo: {
+ _outlineLineTo(*outline, pts, transform);
+ ++pts;
+ break;
+ }
+ case PathCommand::CubicTo: {
+ _outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
+ pts += 3;
+ break;
+ }
+ }
+ ++cmds;
+ }
+
+ _outlineEnd(*outline);
+
+ outline->fillRule = sdata->fillRule();
+ shape->outline = outline;
+
+ shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline));
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
+{
+ if (!_genOutline(shape, sdata, transform, mpool, tid, hasComposite)) return false;
+ if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
+
+ //Keep it for Rasterization Region
+ shape->bbox = renderRegion;
+
+ //Check valid region
+ if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false;
+
+ //Check boundary
+ if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y ||
+ renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false;
+
+ return true;
+}
+
+
+bool shapePrepared(const SwShape* shape)
+{
+ return shape->rle ? true : false;
+}
+
+
+bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias)
+{
+ //FIXME: Should we draw it?
+ //Case: Stroke Line
+ //if (shape.outline->opened) return true;
+
+ //Case A: Fast Track Rectangle Drawing
+ if (shape->fastTrack) return true;
+
+ //Case B: Normal Shape RLE Drawing
+ if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true;
+
+ return false;
+}
+
+
+void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid)
+{
+ mpoolRetOutline(mpool, tid);
+ shape->outline = nullptr;
+}
+
+
+void shapeReset(SwShape* shape)
+{
+ rleReset(shape->rle);
+ rleReset(shape->strokeRle);
+ shape->fastTrack = false;
+ shape->bbox.reset();
+}
+
+
+void shapeFree(SwShape* shape)
+{
+ rleFree(shape->rle);
+ shapeDelFill(shape);
+
+ if (shape->stroke) {
+ rleFree(shape->strokeRle);
+ strokeFree(shape->stroke);
+ }
+}
+
+
+void shapeDelStroke(SwShape* shape)
+{
+ if (!shape->stroke) return;
+ rleFree(shape->strokeRle);
+ shape->strokeRle = nullptr;
+ strokeFree(shape->stroke);
+ shape->stroke = nullptr;
+}
+
+
+void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform)
+{
+ if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
+ auto stroke = shape->stroke;
+ if (!stroke) return;
+
+ strokeReset(stroke, sdata, transform);
+ rleReset(shape->strokeRle);
+}
+
+
+bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
+{
+ SwOutline* shapeOutline = nullptr;
+ SwOutline* strokeOutline = nullptr;
+ bool freeOutline = false;
+ bool ret = true;
+
+ //Dash Style Stroke
+ if (sdata->strokeDash(nullptr) > 0) {
+ shapeOutline = _genDashOutline(sdata, transform);
+ if (!shapeOutline) return false;
+ freeOutline = true;
+ //Normal Style stroke
+ } else {
+ if (!shape->outline) {
+ if (!_genOutline(shape, sdata, transform, mpool, tid, false)) return false;
+ }
+ shapeOutline = shape->outline;
+ }
+
+ if (!strokeParseOutline(shape->stroke, *shapeOutline)) {
+ ret = false;
+ goto fail;
+ }
+
+ strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
+
+ if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) {
+ ret = false;
+ goto fail;
+ }
+
+ shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true);
+
+fail:
+ if (freeOutline) {
+ if (shapeOutline->cntrs) free(shapeOutline->cntrs);
+ if (shapeOutline->pts) free(shapeOutline->pts);
+ if (shapeOutline->types) free(shapeOutline->types);
+ if (shapeOutline->closed) free(shapeOutline->closed);
+ free(shapeOutline);
+ }
+ mpoolRetStrokeOutline(mpool, tid);
+
+ return ret;
+}
+
+
+bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+{
+ return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
+}
+
+
+bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
+{
+ return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
+}
+
+
+void shapeResetFill(SwShape* shape)
+{
+ if (!shape->fill) {
+ shape->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
+ if (!shape->fill) return;
+ }
+ fillReset(shape->fill);
+}
+
+
+void shapeResetStrokeFill(SwShape* shape)
+{
+ if (!shape->stroke->fill) {
+ shape->stroke->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
+ if (!shape->stroke->fill) return;
+ }
+ fillReset(shape->stroke->fill);
+}
+
+
+void shapeDelFill(SwShape* shape)
+{
+ if (!shape->fill) return;
+ fillFree(shape->fill);
+ shape->fill = nullptr;
+}
+
+
+void shapeDelStrokeFill(SwShape* shape)
+{
+ if (!shape->stroke->fill) return;
+ fillFree(shape->stroke->fill);
+ shape->stroke->fill = nullptr;
+}
diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
new file mode 100644
index 0000000000..c0cfc1be26
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp
@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <string.h>
+#include <math.h>
+#include "tvgSwCommon.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static constexpr auto SW_STROKE_TAG_POINT = 1;
+static constexpr auto SW_STROKE_TAG_CUBIC = 2;
+static constexpr auto SW_STROKE_TAG_BEGIN = 4;
+static constexpr auto SW_STROKE_TAG_END = 8;
+
+static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
+{
+ return (SW_ANGLE_PI2 - static_cast<SwFixed>(s) * SW_ANGLE_PI);
+}
+
+
+static inline void SCALE(const SwStroke& stroke, SwPoint& pt)
+{
+ pt.x = static_cast<SwCoord>(pt.x * stroke.sx);
+ pt.y = static_cast<SwCoord>(pt.y * stroke.sy);
+}
+
+
+static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
+{
+ auto maxOld = border->maxPts;
+ auto maxNew = border->ptsCnt + newPts;
+
+ if (maxNew <= maxOld) return;
+
+ auto maxCur = maxOld;
+
+ while (maxCur < maxNew)
+ maxCur += (maxCur >> 1) + 16;
+ //OPTIMIZE: use mempool!
+ border->pts = static_cast<SwPoint*>(realloc(border->pts, maxCur * sizeof(SwPoint)));
+ border->tags = static_cast<uint8_t*>(realloc(border->tags, maxCur * sizeof(uint8_t)));
+ border->maxPts = maxCur;
+}
+
+
+static void _borderClose(SwStrokeBorder* border, bool reverse)
+{
+ auto start = border->start;
+ auto count = border->ptsCnt;
+
+ //Don't record empty paths!
+ if (count <= start + 1U) {
+ border->ptsCnt = start;
+ } else {
+ /* Copy the last point to the start of this sub-path,
+ since it contains the adjusted starting coordinates */
+ border->ptsCnt = --count;
+ border->pts[start] = border->pts[count];
+
+ if (reverse) {
+ //reverse the points
+ auto pt1 = border->pts + start + 1;
+ auto pt2 = border->pts + count - 1;
+
+ while (pt1 < pt2) {
+ auto tmp = *pt1;
+ *pt1 = *pt2;
+ *pt2 = tmp;
+ ++pt1;
+ --pt2;
+ }
+
+ //reverse the tags
+ auto tag1 = border->tags + start + 1;
+ auto tag2 = border->tags + count - 1;
+
+ while (tag1 < tag2) {
+ auto tmp = *tag1;
+ *tag1 = *tag2;
+ *tag2 = tmp;
+ ++tag1;
+ --tag2;
+ }
+ }
+
+ border->tags[start] |= SW_STROKE_TAG_BEGIN;
+ border->tags[count - 1] |= SW_STROKE_TAG_END;
+ }
+
+ border->start = -1;
+ border->movable = false;
+}
+
+
+static void _borderCubicTo(SwStrokeBorder* border, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
+{
+ _growBorder(border, 3);
+
+ auto pt = border->pts + border->ptsCnt;
+ auto tag = border->tags + border->ptsCnt;
+
+ pt[0] = ctrl1;
+ pt[1] = ctrl2;
+ pt[2] = to;
+
+ tag[0] = SW_STROKE_TAG_CUBIC;
+ tag[1] = SW_STROKE_TAG_CUBIC;
+ tag[2] = SW_STROKE_TAG_POINT;
+
+ border->ptsCnt += 3;
+
+ border->movable = false;
+}
+
+
+static void _borderArcTo(SwStrokeBorder* border, const SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff, SwStroke& stroke)
+{
+ constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2;
+ SwPoint a = {static_cast<SwCoord>(radius), 0};
+ mathRotate(a, angleStart);
+ SCALE(stroke, a);
+ a += center;
+
+ auto total = angleDiff;
+ auto angle = angleStart;
+ auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
+
+ while (total != 0) {
+ auto step = total;
+ if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE;
+ else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE;
+
+ auto next = angle + step;
+ auto theta = step;
+ if (theta < 0) theta = -theta;
+
+ theta >>= 1;
+
+ //compute end point
+ SwPoint b = {static_cast<SwCoord>(radius), 0};
+ mathRotate(b, next);
+ SCALE(stroke, b);
+ b += center;
+
+ //compute first and second control points
+ auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3);
+
+ SwPoint a2 = {static_cast<SwCoord>(length), 0};
+ mathRotate(a2, angle + rotate);
+ SCALE(stroke, a2);
+ a2 += a;
+
+ SwPoint b2 = {static_cast<SwCoord>(length), 0};
+ mathRotate(b2, next - rotate);
+ SCALE(stroke, b2);
+ b2 += b;
+
+ //add cubic arc
+ _borderCubicTo(border, a2, b2, b);
+
+ //process the rest of the arc?
+ a = b;
+ total -= step;
+ angle = next;
+ }
+}
+
+
+static void _borderLineTo(SwStrokeBorder* border, const SwPoint& to, bool movable)
+{
+ if (border->movable) {
+ //move last point
+ border->pts[border->ptsCnt - 1] = to;
+ } else {
+
+ //don't add zero-length line_to
+ if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
+
+ _growBorder(border, 1);
+ border->pts[border->ptsCnt] = to;
+ border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT;
+ border->ptsCnt += 1;
+ }
+
+ border->movable = movable;
+}
+
+
+static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to)
+{
+ //close current open path if any?
+ if (border->start >= 0) _borderClose(border, false);
+
+ border->start = border->ptsCnt;
+ border->movable = false;
+
+ _borderLineTo(border, to, false);
+}
+
+
+static void _arcTo(SwStroke& stroke, int32_t side)
+{
+ auto border = stroke.borders + side;
+ auto rotate = SIDE_TO_ROTATE(side);
+ auto total = mathDiff(stroke.angleIn, stroke.angleOut);
+ if (total == SW_ANGLE_PI) total = -rotate * 2;
+
+ _borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total, stroke);
+ border->movable = false;
+}
+
+
+static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
+{
+ constexpr SwFixed MITER_LIMIT = 4 * (1 << 16);
+
+ auto border = stroke.borders + side;
+
+ if (stroke.join == StrokeJoin::Round) {
+ _arcTo(stroke, side);
+ } else {
+ //this is a mitered (pointed) or beveled (truncated) corner
+ auto rotate = SIDE_TO_ROTATE(side);
+ auto bevel = (stroke.join == StrokeJoin::Bevel) ? true : false;
+ SwFixed phi = 0;
+ SwFixed thcos = 0;
+
+ if (!bevel) {
+ auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
+ if (theta == SW_ANGLE_PI) {
+ theta = rotate;
+ phi = stroke.angleIn;
+ } else {
+ theta /= 2;
+ phi = stroke.angleIn + theta + rotate;
+ }
+
+ thcos = mathCos(theta);
+ auto sigma = mathMultiply(MITER_LIMIT, thcos);
+
+ //is miter limit exceeded?
+ if (sigma < 0x10000L) bevel = true;
+ }
+
+ //this is a bevel (broken angle)
+ if (bevel) {
+ SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, stroke.angleOut + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ border->movable = false;
+ _borderLineTo(border, delta, false);
+ //this is a miter (intersection)
+ } else {
+ auto length = mathDivide(stroke.width, thcos);
+ SwPoint delta = {static_cast<SwCoord>(length), 0};
+ mathRotate(delta, phi);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ _borderLineTo(border, delta, false);
+
+ /* Now add and end point
+ Only needed if not lineto (lineLength is zero for curves) */
+ if (lineLength == 0) {
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, stroke.angleOut + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ _borderLineTo(border, delta, false);
+ }
+ }
+ }
+}
+
+
+static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
+{
+ auto border = stroke.borders + side;
+ auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2;
+ SwPoint delta;
+ bool intersect = false;
+
+ /* Only intersect borders if between two line_to's and both
+ lines are long enough (line length is zero fur curves). */
+ if (border->movable && lineLength > 0) {
+ //compute minimum required length of lines
+ SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta)));
+ if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true;
+ }
+
+ auto rotate = SIDE_TO_ROTATE(side);
+
+ if (!intersect) {
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, stroke.angleOut + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ border->movable = false;
+ } else {
+ //compute median angle
+ auto phi = stroke.angleIn + theta;
+ auto thcos = mathCos(theta);
+ delta = {static_cast<SwCoord>(mathDivide(stroke.width, thcos)), 0};
+ mathRotate(delta, phi + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+ }
+
+ _borderLineTo(border, delta, false);
+}
+
+
+void _processCorner(SwStroke& stroke, SwFixed lineLength)
+{
+ auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
+
+ //no specific corner processing is required if the turn is 0
+ if (turn == 0) return;
+
+ //when we turn to the right, the inside side is 0
+ int32_t inside = 0;
+
+ //otherwise, the inside is 1
+ if (turn < 0) inside = 1;
+
+ //process the inside
+ _inside(stroke, inside, lineLength);
+
+ //process the outside
+ _outside(stroke, 1 - inside, lineLength);
+}
+
+
+void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
+{
+ SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, startAngle + SW_ANGLE_PI2);
+ SCALE(stroke, delta);
+
+ auto pt = stroke.center + delta;
+ auto border = stroke.borders;
+ _borderMoveTo(border, pt);
+
+ pt = stroke.center - delta;
+ ++border;
+ _borderMoveTo(border, pt);
+
+ /* Save angle, position and line length for last join
+ lineLength is zero for curves */
+ stroke.subPathAngle = startAngle;
+ stroke.firstPt = false;
+ stroke.subPathLineLength = lineLength;
+}
+
+
+static void _lineTo(SwStroke& stroke, const SwPoint& to)
+{
+ auto delta = to - stroke.center;
+
+ //a zero-length lineto is a no-op; avoid creating a spurious corner
+ if (delta.zero()) return;
+
+ //compute length of line
+ auto lineLength = mathLength(delta);
+ auto angle = mathAtan(delta);
+
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle + SW_ANGLE_PI2);
+ SCALE(stroke, delta);
+
+ //process corner if necessary
+ if (stroke.firstPt) {
+ /* This is the first segment of a subpath. We need to add a point to each border
+ at their respective starting point locations. */
+ _firstSubPath(stroke, angle, lineLength);
+ } else {
+ //process the current corner
+ stroke.angleOut = angle;
+ _processCorner(stroke, lineLength);
+ }
+
+ //now add a line segment to both the inside and outside paths
+ auto border = stroke.borders;
+ auto side = 1;
+
+ while (side >= 0) {
+ auto pt = to + delta;
+
+ //the ends of lineto borders are movable
+ _borderLineTo(border, pt, true);
+
+ delta.x = -delta.x;
+ delta.y = -delta.y;
+
+ --side;
+ ++border;
+ }
+
+ stroke.angleIn = angle;
+ stroke.center = to;
+ stroke.lineLength = lineLength;
+}
+
+
+static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
+{
+ /* if all control points are coincident, this is a no-op;
+ avoid creating a spurious corner */
+ if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) {
+ stroke.center = to;
+ return;
+ }
+
+ SwPoint bezStack[37]; //TODO: static?
+ auto limit = bezStack + 32;
+ auto arc = bezStack;
+ auto firstArc = true;
+ arc[0] = to;
+ arc[1] = ctrl2;
+ arc[2] = ctrl1;
+ arc[3] = stroke.center;
+
+ while (arc >= bezStack) {
+ SwFixed angleIn, angleOut, angleMid;
+
+ //initialize with current direction
+ angleIn = angleOut = angleMid = stroke.angleIn;
+
+ if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
+ if (stroke.firstPt) stroke.angleIn = angleIn;
+ mathSplitCubic(arc);
+ arc += 3;
+ continue;
+ }
+
+ if (firstArc) {
+ firstArc = false;
+ //process corner if necessary
+ if (stroke.firstPt) {
+ _firstSubPath(stroke, angleIn, 0);
+ } else {
+ stroke.angleOut = angleIn;
+ _processCorner(stroke, 0);
+ }
+ } else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8) / 4) {
+ //if the deviation from one arc to the next is too great add a round corner
+ stroke.center = arc[3];
+ stroke.angleOut = angleIn;
+ stroke.join = StrokeJoin::Round;
+
+ _processCorner(stroke, 0);
+
+ //reinstate line join style
+ stroke.join = stroke.joinSaved;
+ }
+
+ //the arc's angle is small enough; we can add it directly to each border
+ auto theta1 = mathDiff(angleIn, angleMid) / 2;
+ auto theta2 = mathDiff(angleMid, angleOut) / 2;
+ auto phi1 = mathMean(angleIn, angleMid);
+ auto phi2 = mathMean(angleMid, angleOut);
+ auto length1 = mathDivide(stroke.width, mathCos(theta1));
+ auto length2 = mathDivide(stroke.width, mathCos(theta2));
+ SwFixed alpha0 = 0;
+
+ //compute direction of original arc
+ if (stroke.handleWideStrokes) {
+ alpha0 = mathAtan(arc[0] - arc[3]);
+ }
+
+ auto border = stroke.borders;
+ int32_t side = 0;
+
+ while (side <= 1)
+ {
+ auto rotate = SIDE_TO_ROTATE(side);
+
+ //compute control points
+ SwPoint _ctrl1 = {static_cast<SwCoord>(length1), 0};
+ mathRotate(_ctrl1, phi1 + rotate);
+ SCALE(stroke, _ctrl1);
+ _ctrl1 += arc[2];
+
+ SwPoint _ctrl2 = {static_cast<SwCoord>(length2), 0};
+ mathRotate(_ctrl2, phi2 + rotate);
+ SCALE(stroke, _ctrl2);
+ _ctrl2 += arc[1];
+
+ //compute end point
+ SwPoint _end = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(_end, angleOut + rotate);
+ SCALE(stroke, _end);
+ _end += arc[0];
+
+ if (stroke.handleWideStrokes) {
+
+ /* determine whether the border radius is greater than the radius of
+ curvature of the original arc */
+ auto _start = border->pts[border->ptsCnt - 1];
+ auto alpha1 = mathAtan(_end - _start);
+
+ //is the direction of the border arc opposite to that of the original arc?
+ if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) {
+
+ //use the sine rule to find the intersection point
+ auto beta = mathAtan(arc[3] - _start);
+ auto gamma = mathAtan(arc[0] - _end);
+ auto bvec = _end - _start;
+ auto blen = mathLength(bvec);
+ auto sinA = abs(mathSin(alpha1 - gamma));
+ auto sinB = abs(mathSin(beta - gamma));
+ auto alen = mathMulDiv(blen, sinA, sinB);
+
+ SwPoint delta = {static_cast<SwCoord>(alen), 0};
+ mathRotate(delta, beta);
+ delta += _start;
+
+ //circumnavigate the negative sector backwards
+ border->movable = false;
+ _borderLineTo(border, delta, false);
+ _borderLineTo(border, _end, false);
+ _borderCubicTo(border, _ctrl2, _ctrl1, _start);
+
+ //and then move to the endpoint
+ _borderLineTo(border, _end, false);
+
+ ++side;
+ ++border;
+ continue;
+ }
+
+ //else fall through
+ }
+ _borderCubicTo(border, _ctrl1, _ctrl2, _end);
+ ++side;
+ ++border;
+ }
+ arc -= 3;
+ stroke.angleIn = angleOut;
+ }
+ stroke.center = to;
+}
+
+
+static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
+{
+ if (stroke.cap == StrokeCap::Square) {
+ auto rotate = SIDE_TO_ROTATE(side);
+ auto border = stroke.borders + side;
+
+ SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle);
+ SCALE(stroke, delta);
+
+ SwPoint delta2 = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta2, angle + rotate);
+ SCALE(stroke, delta2);
+ delta += stroke.center + delta2;
+
+ _borderLineTo(border, delta, false);
+
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle);
+ SCALE(stroke, delta);
+
+ delta2 = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta2, angle - rotate);
+ SCALE(stroke, delta2);
+ delta += delta2 + stroke.center;
+
+ _borderLineTo(border, delta, false);
+
+ } else if (stroke.cap == StrokeCap::Round) {
+
+ stroke.angleIn = angle;
+ stroke.angleOut = angle + SW_ANGLE_PI;
+ _arcTo(stroke, side);
+ return;
+
+ } else { //Butt
+ auto rotate = SIDE_TO_ROTATE(side);
+ auto border = stroke.borders + side;
+
+ SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle + rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+
+ _borderLineTo(border, delta, false);
+
+ delta = {static_cast<SwCoord>(stroke.width), 0};
+ mathRotate(delta, angle - rotate);
+ SCALE(stroke, delta);
+ delta += stroke.center;
+
+ _borderLineTo(border, delta, false);
+ }
+}
+
+
+static void _addReverseLeft(SwStroke& stroke, bool opened)
+{
+ auto right = stroke.borders + 0;
+ auto left = stroke.borders + 1;
+ auto newPts = left->ptsCnt - left->start;
+
+ if (newPts <= 0) return;
+
+ _growBorder(right, newPts);
+
+ auto dstPt = right->pts + right->ptsCnt;
+ auto dstTag = right->tags + right->ptsCnt;
+ auto srcPt = left->pts + left->ptsCnt - 1;
+ auto srcTag = left->tags + left->ptsCnt - 1;
+
+ while (srcPt >= left->pts + left->start) {
+ *dstPt = *srcPt;
+ *dstTag = *srcTag;
+
+ if (opened) {
+ dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
+ } else {
+ //switch begin/end tags if necessary
+ auto ttag = dstTag[0] & (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
+ if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END)
+ dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
+ }
+
+ --srcPt;
+ --srcTag;
+ ++dstPt;
+ ++dstTag;
+ }
+
+ left->ptsCnt = left->start;
+ right->ptsCnt += newPts;
+ right->movable = false;
+ left->movable = false;
+}
+
+
+static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed)
+{
+ /* We cannot process the first point because there is not enough
+ information regarding its corner/cap. Later, it will be processed
+ in the _endSubPath() */
+
+ stroke.firstPt = true;
+ stroke.center = to;
+ stroke.closedSubPath = closed;
+
+ /* Determine if we need to check whether the border radius is greater
+ than the radius of curvature of a curve, to handle this case specially.
+ This is only required if bevel joins or butt caps may be created because
+ round & miter joins and round & square caps cover the nagative sector
+ created with wide strokes. */
+ if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt))
+ stroke.handleWideStrokes = true;
+ else
+ stroke.handleWideStrokes = false;
+
+ stroke.ptStartSubPath = to;
+ stroke.angleIn = 0;
+}
+
+
+static void _endSubPath(SwStroke& stroke)
+{
+ if (stroke.closedSubPath) {
+ //close the path if needed
+ if (stroke.center != stroke.ptStartSubPath)
+ _lineTo(stroke, stroke.ptStartSubPath);
+
+ //process the corner
+ stroke.angleOut = stroke.subPathAngle;
+ auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
+
+ //No specific corner processing is required if the turn is 0
+ if (turn != 0) {
+
+ //when we turn to the right, the inside is 0
+ int32_t inside = 0;
+
+ //otherwise, the inside is 1
+ if (turn < 0) inside = 1;
+
+ _inside(stroke, inside, stroke.subPathLineLength); //inside
+ _outside(stroke, 1 - inside, stroke.subPathLineLength); //outside
+ }
+
+ _borderClose(stroke.borders + 0, false);
+ _borderClose(stroke.borders + 1, true);
+ } else {
+ auto right = stroke.borders;
+
+ /* all right, this is an opened path, we need to add a cap between
+ right & left, add the reverse of left, then add a final cap
+ between left & right */
+ _addCap(stroke, stroke.angleIn, 0);
+
+ //add reversed points from 'left' to 'right'
+ _addReverseLeft(stroke, true);
+
+ //now add the final cap
+ stroke.center = stroke.ptStartSubPath;
+ _addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
+
+ /* now end the right subpath accordingly. The left one is rewind
+ and deosn't need further processing */
+ _borderClose(right, false);
+ }
+}
+
+
+static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt)
+{
+ auto count = border->ptsCnt;
+ auto tags = border->tags;
+ uint32_t _ptsCnt = 0;
+ uint32_t _cntrsCnt = 0;
+ bool inCntr = false;
+
+ while (count > 0) {
+ if (tags[0] & SW_STROKE_TAG_BEGIN) {
+ if (inCntr) goto fail;
+ inCntr = true;
+ } else if (!inCntr) goto fail;
+
+ if (tags[0] & SW_STROKE_TAG_END) {
+ inCntr = false;
+ ++_cntrsCnt;
+ }
+ --count;
+ ++_ptsCnt;
+ ++tags;
+ }
+
+ if (inCntr) goto fail;
+
+ ptsCnt = _ptsCnt;
+ cntrsCnt = _cntrsCnt;
+
+ return;
+
+fail:
+ ptsCnt = 0;
+ cntrsCnt = 0;
+}
+
+
+static void _exportBorderOutline(const SwStroke& stroke, SwOutline* outline, uint32_t side)
+{
+ auto border = stroke.borders + side;
+
+ if (border->ptsCnt == 0) return; //invalid border
+
+ memcpy(outline->pts + outline->ptsCnt, border->pts, border->ptsCnt * sizeof(SwPoint));
+
+ auto cnt = border->ptsCnt;
+ auto src = border->tags;
+ auto tags = outline->types + outline->ptsCnt;
+ auto cntrs = outline->cntrs + outline->cntrsCnt;
+ uint16_t idx = outline->ptsCnt;
+
+ while (cnt > 0) {
+
+ if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT;
+ else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC;
+ else {
+ //LOG: What type of stroke outline??
+ }
+
+ if (*src & SW_STROKE_TAG_END) {
+ *cntrs = idx;
+ ++cntrs;
+ ++outline->cntrsCnt;
+ }
+ ++src;
+ ++tags;
+ ++idx;
+ --cnt;
+ }
+ outline->ptsCnt = outline->ptsCnt + border->ptsCnt;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void strokeFree(SwStroke* stroke)
+{
+ if (!stroke) return;
+
+ //free borders
+ if (stroke->borders[0].pts) free(stroke->borders[0].pts);
+ if (stroke->borders[0].tags) free(stroke->borders[0].tags);
+ if (stroke->borders[1].pts) free(stroke->borders[1].pts);
+ if (stroke->borders[1].tags) free(stroke->borders[1].tags);
+
+ fillFree(stroke->fill);
+ stroke->fill = nullptr;
+
+ free(stroke);
+}
+
+
+void strokeReset(SwStroke* stroke, const Shape* sdata, const Matrix* transform)
+{
+ if (transform) {
+ stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f));
+ stroke->sy = sqrtf(powf(transform->e12, 2.0f) + powf(transform->e22, 2.0f));
+ } else {
+ stroke->sx = stroke->sy = 1.0f;
+ }
+
+ stroke->width = HALF_STROKE(sdata->strokeWidth());
+ stroke->cap = sdata->strokeCap();
+
+ //Save line join: it can be temporarily changed when stroking curves...
+ stroke->joinSaved = stroke->join = sdata->strokeJoin();
+
+ stroke->borders[0].ptsCnt = 0;
+ stroke->borders[0].start = -1;
+ stroke->borders[1].ptsCnt = 0;
+ stroke->borders[1].start = -1;
+}
+
+
+bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
+{
+ uint32_t first = 0;
+
+ for (uint32_t i = 0; i < outline.cntrsCnt; ++i) {
+ auto last = outline.cntrs[i]; //index of last point in contour
+ auto limit = outline.pts + last;
+
+ //Skip empty points
+ if (last <= first) {
+ first = last + 1;
+ continue;
+ }
+
+ auto start = outline.pts[first];
+ auto pt = outline.pts + first;
+ auto types = outline.types + first;
+ auto type = types[0];
+
+ //A contour cannot start with a cubic control point
+ if (type == SW_CURVE_TYPE_CUBIC) return false;
+
+ auto closed = outline.closed ? outline.closed[i]: false;
+
+ _beginSubPath(*stroke, start, closed);
+
+ while (pt < limit) {
+ ++pt;
+ ++types;
+
+ //emit a signel line_to
+ if (types[0] == SW_CURVE_TYPE_POINT) {
+ _lineTo(*stroke, *pt);
+ //types cubic
+ } else {
+ if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
+
+ pt += 2;
+ types += 2;
+
+ if (pt <= limit) {
+ _cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
+ continue;
+ }
+ _cubicTo(*stroke, pt[-2], pt[-1], start);
+ goto close;
+ }
+ }
+
+ close:
+ if (!stroke->firstPt) _endSubPath(*stroke);
+ first = last + 1;
+ }
+ return true;
+}
+
+
+SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid)
+{
+ uint32_t count1, count2, count3, count4;
+
+ _getCounts(stroke->borders + 0, count1, count2);
+ _getCounts(stroke->borders + 1, count3, count4);
+
+ auto ptsCnt = count1 + count3;
+ auto cntrsCnt = count2 + count4;
+
+ auto outline = mpoolReqStrokeOutline(mpool, tid);
+ if (outline->reservedPtsCnt < ptsCnt) {
+ outline->pts = static_cast<SwPoint*>(realloc(outline->pts, sizeof(SwPoint) * ptsCnt));
+ outline->types = static_cast<uint8_t*>(realloc(outline->types, sizeof(uint8_t) * ptsCnt));
+ outline->reservedPtsCnt = ptsCnt;
+ }
+ if (outline->reservedCntrsCnt < cntrsCnt) {
+ outline->cntrs = static_cast<uint16_t*>(realloc(outline->cntrs, sizeof(uint16_t) * cntrsCnt));
+ outline->reservedCntrsCnt = cntrsCnt;
+ }
+
+ _exportBorderOutline(*stroke, outline, 0); //left
+ _exportBorderOutline(*stroke, outline, 1); //right
+
+ return outline;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgAccessor.cpp b/thirdparty/thorvg/src/lib/tvgAccessor.cpp
new file mode 100644
index 0000000000..085c8a3cbc
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgAccessor.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "tvgIteratorAccessor.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), IteratorAccessor& itrAccessor)
+{
+ while (auto child = it->next()) {
+ //Access the child
+ if (!func(child)) return false;
+
+ //Access the children of the child
+ if (auto it2 = itrAccessor.iterator(child)) {
+ if (!accessChildren(it2, func, itrAccessor)) {
+ delete(it2);
+ return false;
+ }
+ delete(it2);
+ }
+ }
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+unique_ptr<Picture> Accessor::access(unique_ptr<Picture> picture, bool(*func)(const Paint* paint)) noexcept
+{
+ auto p = picture.get();
+ if (!p || !func) return picture;
+
+ //Use the Preorder Tree-Search
+
+ //Root
+ if (!func(p)) return picture;
+
+ //Children
+ IteratorAccessor itrAccessor;
+ if (auto it = itrAccessor.iterator(p)) {
+ accessChildren(it, func, itrAccessor);
+ delete(it);
+ }
+ return picture;
+}
+
+
+Accessor::~Accessor()
+{
+
+}
+
+
+Accessor::Accessor()
+{
+
+}
+
+
+unique_ptr<Accessor> Accessor::gen() noexcept
+{
+ return unique_ptr<Accessor>(new Accessor);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgArray.h b/thirdparty/thorvg/src/lib/tvgArray.h
new file mode 100644
index 0000000000..d04e68e73c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgArray.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_ARRAY_H_
+#define _TVG_ARRAY_H_
+
+#include <memory.h>
+
+namespace tvg
+{
+
+template<class T>
+struct Array
+{
+ T* data = nullptr;
+ uint32_t count = 0;
+ uint32_t reserved = 0;
+
+ void push(T element)
+ {
+ if (count + 1 > reserved) {
+ reserved = (count + 1) * 2;
+ auto p = data;
+ data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
+ if (!data) {
+ data = p;
+ return;
+ }
+ }
+ data[count++] = element;
+ }
+
+ bool reserve(uint32_t size)
+ {
+ if (size > reserved) {
+ reserved = size;
+ auto p = data;
+ data = static_cast<T*>(realloc(data, sizeof(T) * reserved));
+ if (!data) {
+ data = p;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool grow(uint32_t size)
+ {
+ return reserve(count + size);
+ }
+
+ T* ptr()
+ {
+ return data + count;
+ }
+
+ void pop()
+ {
+ if (count > 0) --count;
+ }
+
+ void reset()
+ {
+ if (data) {
+ free(data);
+ data = nullptr;
+ }
+ count = reserved = 0;
+ }
+
+ void clear()
+ {
+ count = 0;
+ }
+
+ void operator=(const Array& rhs)
+ {
+ reserve(rhs.count);
+ if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * reserved);
+ count = rhs.count;
+ }
+
+ ~Array()
+ {
+ if (data) free(data);
+ }
+};
+
+}
+
+#endif //_TVG_ARRAY_H_
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.cpp b/thirdparty/thorvg/src/lib/tvgBezier.cpp
new file mode 100644
index 0000000000..2290afa728
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgBezier.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <float.h>
+#include <math.h>
+#include "tvgBezier.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static float _lineLength(const Point& pt1, const Point& pt2)
+{
+ /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
+ With alpha = 1, beta = 3/8, giving results with the largest error less
+ than 7% compared to the exact value. */
+ Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
+ if (diff.x < 0) diff.x = -diff.x;
+ if (diff.y < 0) diff.y = -diff.y;
+ return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+namespace tvg
+{
+
+void bezSplit(const Bezier&cur, Bezier& left, Bezier& right)
+{
+ auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
+ left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f;
+ right.ctrl2.x = (cur.ctrl2.x + cur.end.x) * 0.5f;
+ left.start.x = cur.start.x;
+ right.end.x = cur.end.x;
+ left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
+ right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
+ left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
+
+ c = (cur.ctrl1.y + cur.ctrl2.y) * 0.5f;
+ left.ctrl1.y = (cur.start.y + cur.ctrl1.y) * 0.5f;
+ right.ctrl2.y = (cur.ctrl2.y + cur.end.y) * 0.5f;
+ left.start.y = cur.start.y;
+ right.end.y = cur.end.y;
+ left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
+ right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
+ left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
+}
+
+
+float bezLength(const Bezier& cur)
+{
+ Bezier left, right;
+ auto len = _lineLength(cur.start, cur.ctrl1) + _lineLength(cur.ctrl1, cur.ctrl2) + _lineLength(cur.ctrl2, cur.end);
+ auto chord = _lineLength(cur.start, cur.end);
+
+ if (fabsf(len - chord) > BEZIER_EPSILON) {
+ bezSplit(cur, left, right);
+ return bezLength(left) + bezLength(right);
+ }
+ return len;
+}
+
+
+void bezSplitLeft(Bezier& cur, float at, Bezier& left)
+{
+ left.start = cur.start;
+
+ left.ctrl1.x = cur.start.x + at * (cur.ctrl1.x - cur.start.x);
+ left.ctrl1.y = cur.start.y + at * (cur.ctrl1.y - cur.start.y);
+
+ left.ctrl2.x = cur.ctrl1.x + at * (cur.ctrl2.x - cur.ctrl1.x); //temporary holding spot
+ left.ctrl2.y = cur.ctrl1.y + at * (cur.ctrl2.y - cur.ctrl1.y); //temporary holding spot
+
+ cur.ctrl2.x = cur.ctrl2.x + at * (cur.end.x - cur.ctrl2.x);
+ cur.ctrl2.y = cur.ctrl2.y + at * (cur.end.y - cur.ctrl2.y);
+
+ cur.ctrl1.x = left.ctrl2.x + at * (cur.ctrl2.x - left.ctrl2.x);
+ cur.ctrl1.y = left.ctrl2.y + at * (cur.ctrl2.y - left.ctrl2.y);
+
+ left.ctrl2.x = left.ctrl1.x + at * (left.ctrl2.x - left.ctrl1.x);
+ left.ctrl2.y = left.ctrl1.y + at * (left.ctrl2.y - left.ctrl1.y);
+
+ left.end.x = cur.start.x = left.ctrl2.x + at * (cur.ctrl1.x - left.ctrl2.x);
+ left.end.y = cur.start.y = left.ctrl2.y + at * (cur.ctrl1.y - left.ctrl2.y);
+}
+
+
+float bezAt(const Bezier& bz, float at)
+{
+ auto len = bezLength(bz);
+ auto biggest = 1.0f;
+ auto smallest = 0.0f;
+ auto t = 0.5f;
+
+ //just in case to prevent an infinite loop
+ if (at <= 0) return 0.0f;
+
+ if (at >= len) return 1.0f;
+
+ while (true) {
+ auto right = bz;
+ Bezier left;
+ bezSplitLeft(right, t, left);
+ len = bezLength(left);
+
+ if (fabsf(len - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
+ break;
+ }
+
+ if (len < at) {
+ smallest = t;
+ t = (t + biggest) * 0.5f;
+ } else {
+ biggest = t;
+ t = (smallest + t) * 0.5f;
+ }
+ }
+ return t;
+}
+
+
+void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
+{
+ right = cur;
+ auto t = bezAt(right, at);
+ bezSplitLeft(right, t, left);
+}
+
+}
diff --git a/thirdparty/thorvg/src/lib/tvgBezier.h b/thirdparty/thorvg/src/lib/tvgBezier.h
new file mode 100644
index 0000000000..866e39148e
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgBezier.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_BEZIER_H_
+#define _TVG_BEZIER_H_
+
+#include "tvgCommon.h"
+
+namespace tvg
+{
+
+#define BEZIER_EPSILON 1e-4f
+
+struct Bezier
+{
+ Point start;
+ Point ctrl1;
+ Point ctrl2;
+ Point end;
+};
+
+void bezSplit(const Bezier&cur, Bezier& left, Bezier& right);
+float bezLength(const Bezier& cur);
+void bezSplitLeft(Bezier& cur, float at, Bezier& left);
+float bezAt(const Bezier& bz, float at);
+void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right);
+
+}
+
+#endif //_TVG_BEZIER_H_
diff --git a/thirdparty/thorvg/src/lib/tvgBinaryDesc.h b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h
new file mode 100644
index 0000000000..30ba2d5a29
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_BINARY_DESC_H_
+#define _TVG_BINARY_DESC_H_
+
+/* TODO: Need to consider whether uin8_t is enough size for extension...
+ Rather than optimal data, we can use enough size and data compress? */
+
+/* Data types, do not change data types once Tvg Format is officially released,
+ That would occur the abi break. */
+
+using TvgBinByte = uint8_t;
+using TvgBinCounter = uint32_t;
+using TvgBinTag = TvgBinByte;
+using TvgBinFlag = TvgBinByte;
+
+
+//Header
+#define TVG_HEADER_SIZE 33 //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE
+#define TVG_HEADER_SIGNATURE "ThorVG"
+#define TVG_HEADER_SIGNATURE_LENGTH 6
+#define TVG_HEADER_VERSION "000400" //Major 00, Minor 04, Micro 00
+#define TVG_HEADER_VERSION_LENGTH 6
+#define TVG_HEADER_RESERVED_LENGTH 1 //Storing flags for extensions
+#define TVG_HEADER_COMPRESS_SIZE 12 //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS
+//Compress Size
+#define TVG_HEADER_UNCOMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
+#define TVG_HEADER_COMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
+#define TVG_HEADER_COMPRESSED_SIZE_BITS 4 //SIZE (TvgBinCounter)
+//Reserved Flag
+#define TVG_HEAD_FLAG_COMPRESSED 0x01
+
+//Paint Type
+#define TVG_TAG_CLASS_PICTURE (TvgBinTag)0xfc
+#define TVG_TAG_CLASS_SHAPE (TvgBinTag)0xfd
+#define TVG_TAG_CLASS_SCENE (TvgBinTag)0xfe
+
+
+//Paint
+#define TVG_TAG_PAINT_OPACITY (TvgBinTag)0x10
+#define TVG_TAG_PAINT_TRANSFORM (TvgBinTag)0x11
+#define TVG_TAG_PAINT_CMP_TARGET (TvgBinTag)0x01
+#define TVG_TAG_PAINT_CMP_METHOD (TvgBinTag)0x20
+
+
+//Scene
+#define TVG_TAG_SCENE_RESERVEDCNT (TvgBinTag)0x30
+
+
+//Shape
+#define TVG_TAG_SHAPE_PATH (TvgBinTag)0x40
+#define TVG_TAG_SHAPE_STROKE (TvgBinTag)0x41
+#define TVG_TAG_SHAPE_FILL (TvgBinTag)0x42
+#define TVG_TAG_SHAPE_COLOR (TvgBinTag)0x43
+#define TVG_TAG_SHAPE_FILLRULE (TvgBinTag)0x44
+#define TVG_TAG_SHAPE_STROKE_CAP (TvgBinTag)0x50
+#define TVG_TAG_SHAPE_STROKE_JOIN (TvgBinTag)0x51
+
+//Stroke
+#define TVG_TAG_SHAPE_STROKE_WIDTH (TvgBinTag)0x52
+#define TVG_TAG_SHAPE_STROKE_COLOR (TvgBinTag)0x53
+#define TVG_TAG_SHAPE_STROKE_FILL (TvgBinTag)0x54
+#define TVG_TAG_SHAPE_STROKE_DASHPTRN (TvgBinTag)0x55
+
+
+//Fill
+#define TVG_TAG_FILL_LINEAR_GRADIENT (TvgBinTag)0x60
+#define TVG_TAG_FILL_RADIAL_GRADIENT (TvgBinTag)0x61
+#define TVG_TAG_FILL_COLORSTOPS (TvgBinTag)0x62
+#define TVG_TAG_FILL_FILLSPREAD (TvgBinTag)0x63
+#define TVG_TAG_FILL_TRANSFORM (TvgBinTag)0x64
+
+
+//Picture
+#define TVG_TAG_PICTURE_RAW_IMAGE (TvgBinTag)0x70
+
+#endif //_TVG_BINARY_DESC_H_
diff --git a/thirdparty/thorvg/src/lib/tvgCanvas.cpp b/thirdparty/thorvg/src/lib/tvgCanvas.cpp
new file mode 100644
index 0000000000..bb42441c62
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgCanvas.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCanvasImpl.h"
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Canvas::Canvas(RenderMethod *pRenderer):pImpl(new Impl(pRenderer))
+{
+}
+
+
+Canvas::~Canvas()
+{
+ delete(pImpl);
+}
+
+
+Result Canvas::reserve(uint32_t n) noexcept
+{
+ if (!pImpl->paints.reserve(n)) return Result::FailedAllocation;
+ return Result::Success;
+}
+
+
+Result Canvas::push(unique_ptr<Paint> paint) noexcept
+{
+ return pImpl->push(move(paint));
+}
+
+
+Result Canvas::clear(bool free) noexcept
+{
+ return pImpl->clear(free);
+}
+
+
+Result Canvas::draw() noexcept
+{
+ return pImpl->draw();
+}
+
+
+Result Canvas::update(Paint* paint) noexcept
+{
+ return pImpl->update(paint, false);
+}
+
+
+Result Canvas::sync() noexcept
+{
+ return pImpl->sync();
+}
diff --git a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
new file mode 100644
index 0000000000..848c03aaed
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_CANVAS_IMPL_H_
+#define _TVG_CANVAS_IMPL_H_
+
+#include "tvgPaint.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct Canvas::Impl
+{
+ Array<Paint*> paints;
+ RenderMethod* renderer;
+ bool refresh = false; //if all paints should be updated by force.
+ bool drawing = false; //on drawing condition?
+
+ Impl(RenderMethod* pRenderer):renderer(pRenderer)
+ {
+ }
+
+ ~Impl()
+ {
+ clear(true);
+ delete(renderer);
+ }
+
+ Result push(unique_ptr<Paint> paint)
+ {
+ //You can not push paints during rendering.
+ if (drawing) return Result::InsufficientCondition;
+
+ auto p = paint.release();
+ if (!p) return Result::MemoryCorruption;
+ paints.push(p);
+
+ return update(p, true);
+ }
+
+ Result clear(bool free)
+ {
+ //Clear render target before drawing
+ if (!renderer || !renderer->clear()) return Result::InsufficientCondition;
+
+ //Free paints
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ (*paint)->pImpl->dispose(*renderer);
+ if (free) delete(*paint);
+ }
+
+ paints.clear();
+
+ drawing = false;
+
+ return Result::Success;
+ }
+
+ void needRefresh()
+ {
+ refresh = true;
+ }
+
+ Result update(Paint* paint, bool force)
+ {
+ if (paints.count == 0 || drawing || !renderer) return Result::InsufficientCondition;
+
+ Array<RenderData> clips;
+ auto flag = RenderUpdateFlag::None;
+ if (refresh || force) flag = RenderUpdateFlag::All;
+
+ //Update single paint node
+ if (paint) {
+ //Optimize Me: Can we skip the searching?
+ for (auto paint2 = paints.data; paint2 < (paints.data + paints.count); ++paint2) {
+ if ((*paint2) == paint) {
+ paint->pImpl->update(*renderer, nullptr, 255, clips, flag);
+ return Result::Success;
+ }
+ }
+ return Result::InvalidArguments;
+ //Update all retained paint nodes
+ } else {
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ (*paint)->pImpl->update(*renderer, nullptr, 255, clips, flag);
+ }
+ }
+
+ refresh = false;
+
+ return Result::Success;
+ }
+
+ Result draw()
+ {
+ if (drawing || paints.count == 0 || !renderer || !renderer->preRender()) return Result::InsufficientCondition;
+
+ bool rendered = false;
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ if ((*paint)->pImpl->render(*renderer)) rendered = true;
+ }
+
+ if (!rendered || !renderer->postRender()) return Result::InsufficientCondition;
+
+ drawing = true;
+
+ return Result::Success;
+ }
+
+ Result sync()
+ {
+ if (!drawing) return Result::InsufficientCondition;
+
+ if (renderer->sync()) {
+ drawing = false;
+ return Result::Success;
+ }
+
+ return Result::InsufficientCondition;
+ }
+};
+
+#endif /* _TVG_CANVAS_IMPL_H_ */
diff --git a/thirdparty/thorvg/src/lib/tvgCommon.h b/thirdparty/thorvg/src/lib/tvgCommon.h
new file mode 100644
index 0000000000..b1c586188c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgCommon.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_COMMON_H_
+#define _TVG_COMMON_H_
+
+#include "config.h"
+#include "thorvg.h"
+
+using namespace std;
+using namespace tvg;
+
+//for MSVC Compat
+#ifdef _MSC_VER
+ #define TVG_UNUSED
+ #define strncasecmp _strnicmp
+ #define strcasecmp _stricmp
+#else
+ #define TVG_UNUSED __attribute__ ((__unused__))
+#endif
+
+// Portable 'fallthrough' attribute
+#if __has_cpp_attribute(fallthrough)
+ #ifdef _MSC_VER
+ #define TVG_FALLTHROUGH [[fallthrough]];
+ #else
+ #define TVG_FALLTHROUGH __attribute__ ((fallthrough));
+ #endif
+#else
+ #define TVG_FALLTHROUGH
+#endif
+
+#if defined(_MSC_VER) && defined(__clang__)
+ #define strncpy strncpy_s
+ #define strdup _strdup
+#endif
+
+//TVG class identifier values
+#define TVG_CLASS_ID_UNDEFINED 0
+#define TVG_CLASS_ID_SHAPE 1
+#define TVG_CLASS_ID_SCENE 2
+#define TVG_CLASS_ID_PICTURE 3
+#define TVG_CLASS_ID_LINEAR 4
+#define TVG_CLASS_ID_RADIAL 5
+
+enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown };
+
+#ifdef THORVG_LOG_ENABLED
+ #define TVGLOG(tag, fmt, ...) fprintf(stdout, tag ": " fmt "\n", ##__VA_ARGS__) //Log Message for notifying user some useful info
+ #define TVGERR(tag, fmt, ...) fprintf(stderr, tag ": " fmt "\n", ##__VA_ARGS__) //Error Message for us to fix it
+#else
+ #define TVGERR(...)
+ #define TVGLOG(...)
+#endif
+
+uint16_t THORVG_VERSION_NUMBER();
+
+#endif //_TVG_COMMON_H_
diff --git a/thirdparty/thorvg/src/lib/tvgFill.cpp b/thirdparty/thorvg/src/lib/tvgFill.cpp
new file mode 100644
index 0000000000..4bfb93c102
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgFill.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgFill.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Fill::Fill():pImpl(new Impl())
+{
+}
+
+
+Fill::~Fill()
+{
+ delete(pImpl);
+}
+
+
+Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
+{
+ if ((!colorStops && cnt > 0) || (colorStops && cnt == 0)) return Result::InvalidArguments;
+
+ if (cnt == 0) {
+ if (pImpl->colorStops) {
+ free(pImpl->colorStops);
+ pImpl->colorStops = nullptr;
+ pImpl->cnt = 0;
+ }
+ return Result::Success;
+ }
+
+ if (pImpl->cnt != cnt) {
+ pImpl->colorStops = static_cast<ColorStop*>(realloc(pImpl->colorStops, cnt * sizeof(ColorStop)));
+ }
+
+ pImpl->cnt = cnt;
+ memcpy(pImpl->colorStops, colorStops, cnt * sizeof(ColorStop));
+
+ return Result::Success;
+}
+
+
+uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
+{
+ if (colorStops) *colorStops = pImpl->colorStops;
+
+ return pImpl->cnt;
+}
+
+
+Result Fill::spread(FillSpread s) noexcept
+{
+ pImpl->spread = s;
+
+ return Result::Success;
+}
+
+
+FillSpread Fill::spread() const noexcept
+{
+ return pImpl->spread;
+}
+
+
+Result Fill::transform(const Matrix& m) noexcept
+{
+ if (!pImpl->transform) {
+ pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
+ }
+ *pImpl->transform = m;
+ return Result::Success;
+}
+
+
+Matrix Fill::transform() const noexcept
+{
+ if (pImpl->transform) return *pImpl->transform;
+ return {1, 0, 0, 0, 1, 0, 0, 0, 1};
+}
+
+
+Fill* Fill::duplicate() const noexcept
+{
+ return pImpl->duplicate();
+}
+
+uint32_t Fill::identifier() const noexcept
+{
+ return pImpl->id;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgFill.h b/thirdparty/thorvg/src/lib/tvgFill.h
new file mode 100644
index 0000000000..912091f8cb
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgFill.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_FILL_H_
+#define _TVG_FILL_H_
+
+#include <cstdlib>
+#include <cstring>
+#include "tvgCommon.h"
+
+template<typename T>
+struct DuplicateMethod
+{
+ virtual ~DuplicateMethod() {}
+ virtual T* duplicate() = 0;
+};
+
+template<class T>
+struct FillDup : DuplicateMethod<Fill>
+{
+ T* inst = nullptr;
+
+ FillDup(T* _inst) : inst(_inst) {}
+ ~FillDup() {}
+
+ Fill* duplicate() override
+ {
+ return inst->duplicate();
+ }
+};
+
+struct Fill::Impl
+{
+ ColorStop* colorStops = nullptr;
+ Matrix* transform = nullptr;
+ uint32_t cnt = 0;
+ FillSpread spread;
+ DuplicateMethod<Fill>* dup = nullptr;
+ uint32_t id;
+
+ ~Impl()
+ {
+ if (dup) delete(dup);
+ free(colorStops);
+ free(transform);
+ }
+
+ void method(DuplicateMethod<Fill>* dup)
+ {
+ this->dup = dup;
+ }
+
+ Fill* duplicate()
+ {
+ auto ret = dup->duplicate();
+ if (!ret) return nullptr;
+
+ ret->pImpl->cnt = cnt;
+ ret->pImpl->spread = spread;
+ ret->pImpl->colorStops = static_cast<ColorStop*>(malloc(sizeof(ColorStop) * cnt));
+ memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
+ if (transform) {
+ ret->pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
+ *ret->pImpl->transform = *transform;
+ }
+ return ret;
+ }
+};
+
+#endif //_TVG_FILL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp b/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp
new file mode 100644
index 0000000000..3a88b6d799
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgGlCanvas.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCanvasImpl.h"
+
+#ifdef THORVG_GL_RASTER_SUPPORT
+ #include "tvgGlRenderer.h"
+#else
+ class GlRenderer : public RenderMethod
+ {
+ //Non Supported. Dummy Class */
+ };
+#endif
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct GlCanvas::Impl
+{
+};
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+#ifdef THORVG_GL_RASTER_SUPPORT
+GlCanvas::GlCanvas() : Canvas(GlRenderer::gen()), pImpl(new Impl)
+#else
+GlCanvas::GlCanvas() : Canvas(nullptr), pImpl(new Impl)
+#endif
+{
+}
+
+
+
+GlCanvas::~GlCanvas()
+{
+ delete(pImpl);
+}
+
+
+Result GlCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept
+{
+#ifdef THORVG_GL_RASTER_SUPPORT
+ //We know renderer type, avoid dynamic_cast for performance.
+ auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer);
+ if (!renderer) return Result::MemoryCorruption;
+
+ if (!renderer->target(buffer, stride, w, h)) return Result::Unknown;
+
+ //Paints must be updated again with this new target.
+ Canvas::pImpl->needRefresh();
+
+ return Result::Success;
+#endif
+ return Result::NonSupport;
+}
+
+
+unique_ptr<GlCanvas> GlCanvas::gen() noexcept
+{
+#ifdef THORVG_GL_RASTER_SUPPORT
+ if (GlRenderer::init() <= 0) return nullptr;
+ return unique_ptr<GlCanvas>(new GlCanvas);
+#endif
+ return nullptr;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgInitializer.cpp b/thirdparty/thorvg/src/lib/tvgInitializer.cpp
new file mode 100644
index 0000000000..83ec50be95
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgInitializer.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCommon.h"
+#include "tvgTaskScheduler.h"
+#include "tvgLoader.h"
+
+#ifdef _WIN32
+ #include <cstring>
+#endif
+
+#ifdef THORVG_SW_RASTER_SUPPORT
+ #include "tvgSwRenderer.h"
+#endif
+
+#ifdef THORVG_GL_RASTER_SUPPORT
+ #include "tvgGlRenderer.h"
+#endif
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static int _initCnt = 0;
+static uint16_t _version = 0;
+
+
+static bool _buildVersionInfo()
+{
+ auto SRC = THORVG_VERSION_STRING; //ex) 0.3.99
+ auto p = SRC;
+ const char* x;
+
+ char major[3];
+ x = strchr(p, '.');
+ if (!x) return false;
+ memcpy(major, p, x - p);
+ major[x - p] = '\0';
+ p = x + 1;
+
+ char minor[3];
+ x = strchr(p, '.');
+ if (!x) return false;
+ memcpy(minor, p, x - p);
+ minor[x - p] = '\0';
+ p = x + 1;
+
+ char micro[3];
+ x = SRC + strlen(THORVG_VERSION_STRING);
+ memcpy(micro, p, x - p);
+ micro[x - p] = '\0';
+
+ char sum[7];
+ snprintf(sum, sizeof(sum), "%s%s%s", major, minor, micro);
+
+ _version = atoi(sum);
+
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept
+{
+ auto nonSupport = true;
+
+ if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
+ #ifdef THORVG_SW_RASTER_SUPPORT
+ if (!SwRenderer::init(threads)) return Result::FailedAllocation;
+ nonSupport = false;
+ #endif
+ } else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
+ #ifdef THORVG_GL_RASTER_SUPPORT
+ if (!GlRenderer::init(threads)) return Result::FailedAllocation;
+ nonSupport = false;
+ #endif
+ } else {
+ return Result::InvalidArguments;
+ }
+
+ if (nonSupport) return Result::NonSupport;
+
+ if (_initCnt++ > 0) return Result::Success;
+
+ if (!_buildVersionInfo()) return Result::Unknown;
+
+ if (!LoaderMgr::init()) return Result::Unknown;
+
+ TaskScheduler::init(threads);
+
+ return Result::Success;
+}
+
+
+Result Initializer::term(CanvasEngine engine) noexcept
+{
+ if (_initCnt == 0) return Result::InsufficientCondition;
+
+ auto nonSupport = true;
+
+ if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Sw)) {
+ #ifdef THORVG_SW_RASTER_SUPPORT
+ if (!SwRenderer::term()) return Result::InsufficientCondition;
+ nonSupport = false;
+ #endif
+ } else if (static_cast<uint32_t>(engine) & static_cast<uint32_t>(CanvasEngine::Gl)) {
+ #ifdef THORVG_GL_RASTER_SUPPORT
+ if (!GlRenderer::term()) return Result::InsufficientCondition;
+ nonSupport = false;
+ #endif
+ } else {
+ return Result::InvalidArguments;
+ }
+
+ if (nonSupport) return Result::NonSupport;
+
+ if (--_initCnt > 0) return Result::Success;
+
+ TaskScheduler::term();
+
+ if (!LoaderMgr::term()) return Result::Unknown;
+
+ return Result::Success;
+}
+
+
+uint16_t THORVG_VERSION_NUMBER()
+{
+ return _version;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h b/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h
new file mode 100644
index 0000000000..10b55a536d
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgIteratorAccessor.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_ITERATOR_ACCESSOR_H_
+#define _TVG_ITERATOR_ACCESSOR_H_
+
+#include "tvgPaint.h"
+
+namespace tvg
+{
+
+class IteratorAccessor
+{
+public:
+ //Utility Method: Iterator Accessor
+ Iterator* iterator(const Paint* paint)
+ {
+ return paint->pImpl->iterator();
+ }
+};
+
+}
+
+#endif //_TVG_ITERATOR_ACCESSOR_H_
diff --git a/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
new file mode 100644
index 0000000000..6ec7ddab20
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLinearGradient.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <float.h>
+#include <math.h>
+#include "tvgFill.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct LinearGradient::Impl
+{
+ float x1 = 0;
+ float y1 = 0;
+ float x2 = 0;
+ float y2 = 0;
+
+ Fill* duplicate()
+ {
+ auto ret = LinearGradient::gen();
+ if (!ret) return nullptr;
+
+ ret->pImpl->x1 = x1;
+ ret->pImpl->y1 = y1;
+ ret->pImpl->x2 = x2;
+ ret->pImpl->y2 = y2;
+
+ return ret.release();
+ }
+};
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+LinearGradient::LinearGradient():pImpl(new Impl())
+{
+ Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
+ Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
+}
+
+
+LinearGradient::~LinearGradient()
+{
+ delete(pImpl);
+}
+
+
+Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
+{
+ pImpl->x1 = x1;
+ pImpl->y1 = y1;
+ pImpl->x2 = x2;
+ pImpl->y2 = y2;
+
+ return Result::Success;
+}
+
+
+Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
+{
+ if (x1) *x1 = pImpl->x1;
+ if (x2) *x2 = pImpl->x2;
+ if (y1) *y1 = pImpl->y1;
+ if (y2) *y2 = pImpl->y2;
+
+ return Result::Success;
+}
+
+
+unique_ptr<LinearGradient> LinearGradient::gen() noexcept
+{
+ return unique_ptr<LinearGradient>(new LinearGradient);
+}
+
+
+uint32_t LinearGradient::identifier() noexcept
+{
+ return TVG_CLASS_ID_LINEAR;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgLoadModule.h b/thirdparty/thorvg/src/lib/tvgLoadModule.h
new file mode 100644
index 0000000000..0c34ecbf6a
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLoadModule.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_LOAD_MODULE_H_
+#define _TVG_LOAD_MODULE_H_
+
+#include "tvgRender.h"
+
+namespace tvg
+{
+
+class LoadModule
+{
+public:
+ //default view box, if any.
+ float vx = 0;
+ float vy = 0;
+ float vw = 0;
+ float vh = 0;
+ float w = 0, h = 0; //default image size
+ bool preserveAspect = true; //keep aspect ratio by default.
+
+ virtual ~LoadModule() {}
+
+ virtual bool open(const string& path) { return false; }
+ virtual bool open(const char* data, uint32_t size, bool copy) { return false; }
+ virtual bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) { return false; }
+
+ //Override this if the vector-format has own resizing policy.
+ virtual bool resize(Paint* paint, float w, float h) { return false; }
+
+ virtual bool read() = 0;
+ virtual bool close() = 0;
+ virtual unique_ptr<Surface> bitmap() { return nullptr; }
+ virtual unique_ptr<Paint> paint() { return nullptr; }
+};
+
+}
+
+#endif //_TVG_LOAD_MODULE_H_
diff --git a/thirdparty/thorvg/src/lib/tvgLoader.cpp b/thirdparty/thorvg/src/lib/tvgLoader.cpp
new file mode 100644
index 0000000000..fb93e0faec
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLoader.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgLoader.h"
+
+#ifdef THORVG_SVG_LOADER_SUPPORT
+ #include "tvgSvgLoader.h"
+#endif
+
+#ifdef THORVG_PNG_LOADER_SUPPORT
+ #include "tvgPngLoader.h"
+#endif
+
+#ifdef THORVG_TVG_LOADER_SUPPORT
+ #include "tvgTvgLoader.h"
+#endif
+
+#ifdef THORVG_JPG_LOADER_SUPPORT
+ #include "tvgJpgLoader.h"
+#endif
+
+#include "tvgRawLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static LoadModule* _find(FileType type)
+{
+ switch(type) {
+ case FileType::Tvg: {
+#ifdef THORVG_TVG_LOADER_SUPPORT
+ return new TvgLoader;
+#endif
+ break;
+ }
+ case FileType::Svg: {
+#ifdef THORVG_SVG_LOADER_SUPPORT
+ return new SvgLoader;
+#endif
+ break;
+ }
+ case FileType::Raw: {
+ return new RawLoader;
+ break;
+ }
+ case FileType::Png: {
+#ifdef THORVG_PNG_LOADER_SUPPORT
+ return new PngLoader;
+#endif
+ break;
+ }
+ case FileType::Jpg: {
+#ifdef THORVG_JPG_LOADER_SUPPORT
+ return new JpgLoader;
+#endif
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+#ifdef THORVG_LOG_ENABLED
+ const char *format;
+ switch(type) {
+ case FileType::Tvg: {
+ format = "TVG";
+ break;
+ }
+ case FileType::Svg: {
+ format = "SVG";
+ break;
+ }
+ case FileType::Raw: {
+ format = "RAW";
+ break;
+ }
+ case FileType::Png: {
+ format = "PNG";
+ break;
+ }
+ case FileType::Jpg: {
+ format = "JPG";
+ break;
+ }
+ default: {
+ format = "???";
+ break;
+ }
+ }
+ TVGLOG("LOADER", "%s format is not supported", format);
+#endif
+ return nullptr;
+}
+
+
+static LoadModule* _findByPath(const string& path)
+{
+ auto ext = path.substr(path.find_last_of(".") + 1);
+ if (!ext.compare("tvg")) return _find(FileType::Tvg);
+ if (!ext.compare("svg")) return _find(FileType::Svg);
+ if (!ext.compare("png")) return _find(FileType::Png);
+ if (!ext.compare("jpg")) return _find(FileType::Jpg);
+ return nullptr;
+}
+
+
+static LoadModule* _findByType(const string& mimeType)
+{
+ if (mimeType.empty()) return nullptr;
+
+ auto type = FileType::Unknown;
+
+ if (mimeType == "tvg") type = FileType::Tvg;
+ else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
+ else if (mimeType == "raw") type = FileType::Raw;
+ else if (mimeType == "png") type = FileType::Png;
+ else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
+ else {
+ TVGLOG("LOADER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
+ return nullptr;
+ }
+
+ return _find(type);
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+bool LoaderMgr::init()
+{
+ //TODO:
+
+ return true;
+}
+
+
+bool LoaderMgr::term()
+{
+ //TODO:
+
+ return true;
+}
+
+
+shared_ptr<LoadModule> LoaderMgr::loader(const string& path, bool* invalid)
+{
+ *invalid = false;
+
+ if (auto loader = _findByPath(path)) {
+ if (loader->open(path)) return shared_ptr<LoadModule>(loader);
+ else delete(loader);
+ *invalid = true;
+ }
+ return nullptr;
+}
+
+
+shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
+{
+ //Try first with the given MimeType
+ if (auto loader = _findByType(mimeType)) {
+ if (loader->open(data, size, copy)) {
+ return shared_ptr<LoadModule>(loader);
+ } else {
+ TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported. Will try again with other types.", mimeType.c_str());
+ delete(loader);
+ }
+ }
+
+ //Abnormal MimeType. Try with the candidates in the order
+ for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) {
+ auto loader = _find(static_cast<FileType>(i));
+ if (loader) {
+ if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader);
+ else delete(loader);
+ }
+ }
+ return nullptr;
+}
+
+
+shared_ptr<LoadModule> LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool copy)
+{
+ //function is dedicated for raw images only
+ auto loader = new RawLoader;
+ if (loader->open(data, w, h, copy)) return shared_ptr<LoadModule>(loader);
+ else delete(loader);
+
+ return nullptr;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgLoader.h b/thirdparty/thorvg/src/lib/tvgLoader.h
new file mode 100644
index 0000000000..8ba3c139fa
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLoader.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_LOADER_H_
+#define _TVG_LOADER_H_
+
+#include "tvgLoadModule.h"
+
+struct LoaderMgr
+{
+ static bool init();
+ static bool term();
+ static shared_ptr<LoadModule> loader(const string& path, bool* invalid);
+ static shared_ptr<LoadModule> loader(const char* data, uint32_t size, const string& mimeType, bool copy);
+ static shared_ptr<LoadModule> loader(const uint32_t* data, uint32_t w, uint32_t h, bool copy);
+};
+
+#endif //_TVG_LOADER_H_
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.cpp b/thirdparty/thorvg/src/lib/tvgLzw.cpp
new file mode 100644
index 0000000000..0049c89962
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLzw.cpp
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Lempel–Ziv–Welch (LZW) encoder/decoder by Guilherme R. Lampert(guilherme.ronaldo.lampert@gmail.com)
+
+ * This is the compression scheme used by the GIF image format and the Unix 'compress' tool.
+ * Main differences from this implementation is that End Of Input (EOI) and Clear Codes (CC)
+ * are not stored in the output and the max code length in bits is 12, vs 16 in compress.
+ *
+ * EOI is simply detected by the end of the data stream, while CC happens if the
+ * dictionary gets filled. Data is written/read from bit streams, which handle
+ * byte-alignment for us in a transparent way.
+
+ * The decoder relies on the hardcoded data layout produced by the encoder, since
+ * no additional reconstruction data is added to the output, so they must match.
+ * The nice thing about LZW is that we can reconstruct the dictionary directly from
+ * the stream of codes generated by the encoder, so this avoids storing additional
+ * headers in the bit stream.
+
+ * The output code length is variable. It starts with the minimum number of bits
+ * required to store the base byte-sized dictionary and automatically increases
+ * as the dictionary gets larger (it starts at 9-bits and grows to 10-bits when
+ * code 512 is added, then 11-bits when 1024 is added, and so on). If the dictionary
+ * is filled (4096 items for a 12-bits dictionary), the whole thing is cleared and
+ * the process starts over. This is the main reason why the encoder and the decoder
+ * must match perfectly, since the lengths of the codes will not be specified with
+ * the data itself.
+
+ * USEFUL LINKS:
+ * https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
+ * http://rosettacode.org/wiki/LZW_compression
+ * http://www.cs.duke.edu/csed/curious/compression/lzw.html
+ * http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html
+ * http://marknelson.us/1989/10/01/lzw-data-compression/
+ */
+#include "config.h"
+
+#if defined(THORVG_TVG_SAVER_SUPPORT) || defined(THORVG_TVG_LOADER_SUPPORT)
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+#include <string>
+#include <memory.h>
+#include "tvgLzw.h"
+
+namespace {
+//LZW Dictionary helper:
+constexpr int Nil = -1;
+constexpr int MaxDictBits = 12;
+constexpr int StartBits = 9;
+constexpr int FirstCode = (1 << (StartBits - 1)); // 256
+constexpr int MaxDictEntries = (1 << MaxDictBits); // 4096
+
+
+//Round up to the next power-of-two number, e.g. 37 => 64
+static int nextPowerOfTwo(int num)
+{
+ --num;
+ for (size_t i = 1; i < sizeof(num) * 8; i <<= 1) {
+ num = num | num >> i;
+ }
+ return ++num;
+}
+
+
+struct BitStreamWriter
+{
+ uint8_t* stream; //Growable buffer to store our bits. Heap allocated & owned by the class instance.
+ int bytesAllocated; //Current size of heap-allocated stream buffer *in bytes*.
+ int granularity; //Amount bytesAllocated multiplies by when auto-resizing in appendBit().
+ int currBytePos; //Current byte being written to, from 0 to bytesAllocated-1.
+ int nextBitPos; //Bit position within the current byte to access next. 0 to 7.
+ int numBitsWritten; //Number of bits in use from the stream buffer, not including byte-rounding padding.
+
+ void internalInit()
+ {
+ stream = nullptr;
+ bytesAllocated = 0;
+ granularity = 2;
+ currBytePos = 0;
+ nextBitPos = 0;
+ numBitsWritten = 0;
+ }
+
+ uint8_t* allocBytes(const int bytesWanted, uint8_t * oldPtr, const int oldSize)
+ {
+ auto newMemory = static_cast<uint8_t *>(malloc(bytesWanted));
+ memset(newMemory, 0, bytesWanted);
+
+ if (oldPtr) {
+ memcpy(newMemory, oldPtr, oldSize);
+ free(oldPtr);
+ }
+ return newMemory;
+ }
+
+ BitStreamWriter()
+ {
+ /* 8192 bits for a start (1024 bytes). It will resize if needed.
+ Default granularity is 2. */
+ internalInit();
+ allocate(8192);
+ }
+
+ BitStreamWriter(const int initialSizeInBits, const int growthGranularity = 2)
+ {
+ internalInit();
+ setGranularity(growthGranularity);
+ allocate(initialSizeInBits);
+ }
+
+ ~BitStreamWriter()
+ {
+ free(stream);
+ }
+
+ void allocate(int bitsWanted)
+ {
+ //Require at least a byte.
+ if (bitsWanted <= 0) bitsWanted = 8;
+
+ //Round upwards if needed:
+ if ((bitsWanted % 8) != 0) bitsWanted = nextPowerOfTwo(bitsWanted);
+
+ //We might already have the required count.
+ const int sizeInBytes = bitsWanted / 8;
+ if (sizeInBytes <= bytesAllocated) return;
+
+ stream = allocBytes(sizeInBytes, stream, bytesAllocated);
+ bytesAllocated = sizeInBytes;
+ }
+
+ void appendBit(const int bit)
+ {
+ const uint32_t mask = uint32_t(1) << nextBitPos;
+ stream[currBytePos] = (stream[currBytePos] & ~mask) | (-bit & mask);
+ ++numBitsWritten;
+
+ if (++nextBitPos == 8) {
+ nextBitPos = 0;
+ if (++currBytePos == bytesAllocated) allocate(bytesAllocated * granularity * 8);
+ }
+ }
+
+ void appendBitsU64(const uint64_t num, const int bitCount)
+ {
+ for (int b = 0; b < bitCount; ++b) {
+ const uint64_t mask = uint64_t(1) << b;
+ const int bit = !!(num & mask);
+ appendBit(bit);
+ }
+ }
+
+ uint8_t* release()
+ {
+ auto oldPtr = stream;
+ internalInit();
+ return oldPtr;
+ }
+
+ void setGranularity(const int growthGranularity)
+ {
+ granularity = (growthGranularity >= 2) ? growthGranularity : 2;
+ }
+
+ int getByteCount() const
+ {
+ int usedBytes = numBitsWritten / 8;
+ int leftovers = numBitsWritten % 8;
+ if (leftovers != 0) ++usedBytes;
+ return usedBytes;
+ }
+};
+
+
+struct BitStreamReader
+{
+ const uint8_t* stream; // Pointer to the external bit stream. Not owned by the reader.
+ const int sizeInBytes; // Size of the stream *in bytes*. Might include padding.
+ const int sizeInBits; // Size of the stream *in bits*, padding *not* include.
+ int currBytePos = 0; // Current byte being read in the stream.
+ int nextBitPos = 0; // Bit position within the current byte to access next. 0 to 7.
+ int numBitsRead = 0; // Total bits read from the stream so far. Never includes byte-rounding padding.
+
+ BitStreamReader(const uint8_t* bitStream, const int byteCount, const int bitCount) : stream(bitStream), sizeInBytes(byteCount), sizeInBits(bitCount)
+ {
+ }
+
+ bool readNextBit(int& bitOut)
+ {
+ if (numBitsRead >= sizeInBits) return false; //We are done.
+
+ const uint32_t mask = uint32_t(1) << nextBitPos;
+ bitOut = !!(stream[currBytePos] & mask);
+ ++numBitsRead;
+
+ if (++nextBitPos == 8) {
+ nextBitPos = 0;
+ ++currBytePos;
+ }
+ return true;
+ }
+
+ uint64_t readBitsU64(const int bitCount)
+ {
+ uint64_t num = 0;
+ for (int b = 0; b < bitCount; ++b) {
+ int bit;
+ if (!readNextBit(bit)) break;
+ /* Based on a "Stanford bit-hack":
+ http://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching */
+ const uint64_t mask = uint64_t(1) << b;
+ num = (num & ~mask) | (-bit & mask);
+ }
+ return num;
+ }
+
+ bool isEndOfStream() const
+ {
+ return numBitsRead >= sizeInBits;
+ }
+};
+
+
+struct Dictionary
+{
+ struct Entry
+ {
+ int code;
+ int value;
+ };
+
+ //Dictionary entries 0-255 are always reserved to the byte/ASCII range.
+ int size;
+ Entry entries[MaxDictEntries];
+
+ Dictionary()
+ {
+ /* First 256 dictionary entries are reserved to the byte/ASCII range.
+ Additional entries follow for the character sequences found in the input.
+ Up to 4096 - 256 (MaxDictEntries - FirstCode). */
+ size = FirstCode;
+
+ for (int i = 0; i < size; ++i) {
+ entries[i].code = Nil;
+ entries[i].value = i;
+ }
+ }
+
+ int findIndex(const int code, const int value) const
+ {
+ if (code == Nil) return value;
+
+ //Linear search for now.
+ //TODO: Worth optimizing with a proper hash-table?
+ for (int i = 0; i < size; ++i) {
+ if (entries[i].code == code && entries[i].value == value) return i;
+ }
+ return Nil;
+ }
+
+ bool add(const int code, const int value)
+ {
+ if (size == MaxDictEntries) return false;
+ entries[size].code = code;
+ entries[size].value = value;
+ ++size;
+ return true;
+ }
+
+ bool flush(int & codeBitsWidth)
+ {
+ if (size == (1 << codeBitsWidth)) {
+ ++codeBitsWidth;
+ if (codeBitsWidth > MaxDictBits) {
+ //Clear the dictionary (except the first 256 byte entries).
+ codeBitsWidth = StartBits;
+ size = FirstCode;
+ return true;
+ }
+ }
+ return false;
+ }
+};
+
+
+static bool outputByte(int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar)
+{
+ if (bytesDecodedSoFar >= outputSizeBytes) return false;
+ *output++ = static_cast<uint8_t>(code);
+ ++bytesDecodedSoFar;
+ return true;
+}
+
+
+static bool outputSequence(const Dictionary& dict, int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar, int& firstByte)
+{
+ /* A sequence is stored backwards, so we have to write
+ it to a temp then output the buffer in reverse. */
+ int i = 0;
+ uint8_t sequence[MaxDictEntries];
+
+ do {
+ sequence[i++] = dict.entries[code].value;
+ code = dict.entries[code].code;
+ } while (code >= 0);
+
+ firstByte = sequence[--i];
+
+ for (; i >= 0; --i) {
+ if (!outputByte(sequence[i], output, outputSizeBytes, bytesDecodedSoFar)) return false;
+ }
+ return true;
+}
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+namespace tvg {
+
+uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes)
+{
+ int code = Nil;
+ int prevCode = Nil;
+ int firstByte = 0;
+ int bytesDecoded = 0;
+ int codeBitsWidth = StartBits;
+ auto uncompressed = (uint8_t*) malloc(sizeof(uint8_t) * uncompressedSizeBytes);
+ auto ptr = uncompressed;
+
+ /* We'll reconstruct the dictionary based on the bit stream codes.
+ Unlike Huffman encoding, we don't store the dictionary as a prefix to the data. */
+ Dictionary dictionary;
+ BitStreamReader bitStream(compressed, compressedSizeBytes, compressedSizeBits);
+
+ /* We check to avoid an overflow of the user buffer.
+ If the buffer is smaller than the decompressed size, we break the loop and return the current decompression count. */
+ while (!bitStream.isEndOfStream()) {
+ code = static_cast<int>(bitStream.readBitsU64(codeBitsWidth));
+
+ if (prevCode == Nil) {
+ if (!outputByte(code, ptr, uncompressedSizeBytes, bytesDecoded)) break;
+ firstByte = code;
+ prevCode = code;
+ continue;
+ }
+ if (code >= dictionary.size) {
+ if (!outputSequence(dictionary, prevCode, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
+ if (!outputByte(firstByte, ptr, uncompressedSizeBytes, bytesDecoded)) break;
+ } else if (!outputSequence(dictionary, code, ptr, uncompressedSizeBytes, bytesDecoded, firstByte)) break;
+
+ dictionary.add(prevCode, firstByte);
+ if (dictionary.flush(codeBitsWidth)) prevCode = Nil;
+ else prevCode = code;
+ }
+
+ return uncompressed;
+}
+
+
+uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits)
+{
+ //LZW encoding context:
+ int code = Nil;
+ int codeBitsWidth = StartBits;
+ Dictionary dictionary;
+
+ //Output bit stream we write to. This will allocate memory as needed to accommodate the encoded data.
+ BitStreamWriter bitStream;
+
+ for (; uncompressedSizeBytes > 0; --uncompressedSizeBytes, ++uncompressed) {
+ const int value = *uncompressed;
+ const int index = dictionary.findIndex(code, value);
+
+ if (index != Nil) {
+ code = index;
+ continue;
+ }
+
+ //Write the dictionary code using the minimum bit-with:
+ bitStream.appendBitsU64(code, codeBitsWidth);
+
+ //Flush it when full so we can restart the sequences.
+ if (!dictionary.flush(codeBitsWidth)) {
+ //There's still space for this sequence.
+ dictionary.add(code, value);
+ }
+ code = value;
+ }
+
+ //Residual code at the end:
+ if (code != Nil) bitStream.appendBitsU64(code, codeBitsWidth);
+
+ //Pass ownership of the compressed data buffer to the user pointer:
+ *compressedSizeBytes = bitStream.getByteCount();
+ *compressedSizeBits = bitStream.numBitsWritten;
+
+ return bitStream.release();
+}
+
+}
+
+#endif
diff --git a/thirdparty/thorvg/src/lib/tvgLzw.h b/thirdparty/thorvg/src/lib/tvgLzw.h
new file mode 100644
index 0000000000..3fdb439a02
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgLzw.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_LZW_H_
+#define _TVG_LZW_H_
+
+namespace tvg
+{
+ uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
+ uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
+}
+
+#endif //_TVG_LZW_H \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h
new file mode 100644
index 0000000000..9e5c915fc3
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgMath.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_MATH_H_
+#define _TVG_MATH_H_
+
+ #define _USE_MATH_DEFINES
+
+#include <float.h>
+#include <math.h>
+#include "tvgCommon.h"
+
+
+static inline bool mathZero(float a)
+{
+ return (fabsf(a) < FLT_EPSILON) ? true : false;
+}
+
+
+static inline bool mathEqual(float a, float b)
+{
+ return (fabsf(a - b) < FLT_EPSILON);
+}
+
+
+static inline bool mathRightAngle(const Matrix* m)
+{
+ auto radian = fabsf(atan2(m->e21, m->e11));
+ if (radian < FLT_EPSILON || mathEqual(radian, float(M_PI_2)) || mathEqual(radian, float(M_PI))) return true;
+ return false;
+}
+
+
+static inline bool mathIdentity(const Matrix* m)
+{
+ if (!mathEqual(m->e11, 1.0f) || !mathZero(m->e12) || !mathZero(m->e13) ||
+ !mathZero(m->e21) || !mathEqual(m->e22, 1.0f) || !mathZero(m->e23) ||
+ !mathZero(m->e31) || !mathZero(m->e32) || !mathEqual(m->e33, 1.0f)) {
+ return false;
+ }
+ return true;
+}
+
+
+static inline bool mathInverse(const Matrix* m, Matrix* out)
+{
+ auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) -
+ m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) +
+ m->e13 * (m->e21 * m->e32 - m->e22 * m->e31);
+
+ if (mathZero(det)) return false;
+
+ auto invDet = 1 / det;
+
+ out->e11 = (m->e22 * m->e33 - m->e32 * m->e23) * invDet;
+ out->e12 = (m->e13 * m->e32 - m->e12 * m->e33) * invDet;
+ out->e13 = (m->e12 * m->e23 - m->e13 * m->e22) * invDet;
+ out->e21 = (m->e23 * m->e31 - m->e21 * m->e33) * invDet;
+ out->e22 = (m->e11 * m->e33 - m->e13 * m->e31) * invDet;
+ out->e23 = (m->e21 * m->e13 - m->e11 * m->e23) * invDet;
+ out->e31 = (m->e21 * m->e32 - m->e31 * m->e22) * invDet;
+ out->e32 = (m->e31 * m->e12 - m->e11 * m->e32) * invDet;
+ out->e33 = (m->e11 * m->e22 - m->e21 * m->e12) * invDet;
+
+ return true;
+}
+
+
+static inline void mathIdentity(Matrix* m)
+{
+ m->e11 = 1.0f;
+ m->e12 = 0.0f;
+ m->e13 = 0.0f;
+ m->e21 = 0.0f;
+ m->e22 = 1.0f;
+ m->e23 = 0.0f;
+ m->e31 = 0.0f;
+ m->e32 = 0.0f;
+ m->e33 = 1.0f;
+}
+
+
+static inline void mathScale(Matrix* m, float scale)
+{
+ m->e11 = scale;
+ m->e22 = scale;
+}
+
+
+static inline void mathTranslate(Matrix* m, float x, float y)
+{
+ m->e13 = x;
+ m->e23 = y;
+}
+
+
+static inline void mathRotate(Matrix* m, float degree)
+{
+ auto radian = degree / 180.0f * M_PI;
+ auto cosVal = cosf(radian);
+ auto sinVal = sinf(radian);
+
+ m->e12 = m->e11 * -sinVal;
+ m->e11 *= cosVal;
+ m->e21 = m->e22 * sinVal;
+ m->e22 *= cosVal;
+}
+
+
+static inline void mathMultiply(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;
+}
+
+
+static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
+{
+ Matrix m;
+
+ m.e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31;
+ m.e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32;
+ m.e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33;
+
+ m.e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31;
+ m.e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32;
+ m.e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33;
+
+ m.e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31;
+ m.e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32;
+ m.e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33;
+
+ return m;
+}
+
+
+#endif //_TVG_MATH_H_ \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.cpp b/thirdparty/thorvg/src/lib/tvgPaint.cpp
new file mode 100644
index 0000000000..30e82fbc60
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgPaint.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgPaint.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+static bool _compFastTrack(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. */
+ auto shape = static_cast<Shape*>(cmpTarget);
+
+ //Rectangle Candidates?
+ const Point* pts;
+ if (shape->pathCoords(&pts) != 4) return false;
+
+ if (rTransform) rTransform->update();
+
+ //No rotational.
+ if (pTransform && !mathRightAngle(&pTransform->m)) return false;
+ if (rTransform && !mathRightAngle(&rTransform->m)) return false;
+
+ //Perpendicular Rectangle?
+ auto pt1 = pts + 0;
+ auto pt2 = pts + 1;
+ auto pt3 = pts + 2;
+ auto pt4 = pts + 3;
+
+ if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) ||
+ (mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) {
+
+ auto v1 = *pt1;
+ auto v2 = *pt3;
+
+ if (rTransform) {
+ mathMultiply(&v1, &rTransform->m);
+ mathMultiply(&v2, &rTransform->m);
+ }
+
+ if (pTransform) {
+ mathMultiply(&v1, &pTransform->m);
+ mathMultiply(&v2, &pTransform->m);
+ }
+
+ //sorting
+ if (v1.x > v2.x) {
+ auto tmp = v2.x;
+ v2.x = v1.x;
+ v1.x = tmp;
+ }
+
+ if (v1.y > v2.y) {
+ auto tmp = v2.y;
+ v2.y = v1.y;
+ v1.y = tmp;
+ }
+
+ viewport.x = static_cast<int32_t>(v1.x);
+ viewport.y = static_cast<int32_t>(v1.y);
+ viewport.w = static_cast<int32_t>(v2.x - v1.x + 0.5f);
+ viewport.h = static_cast<int32_t>(v2.y - v1.y + 0.5f);
+
+ if (viewport.w < 0) viewport.w = 0;
+ if (viewport.h < 0) viewport.h = 0;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+Paint* Paint::Impl::duplicate()
+{
+ auto ret = smethod->duplicate();
+
+ //duplicate Transform
+ if (rTransform) {
+ ret->pImpl->rTransform = new RenderTransform();
+ *ret->pImpl->rTransform = *rTransform;
+ ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
+ }
+
+ ret->pImpl->opacity = opacity;
+
+ if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method);
+
+ return ret;
+}
+
+
+bool Paint::Impl::rotate(float degree)
+{
+ if (rTransform) {
+ if (mathEqual(degree, rTransform->degree)) return true;
+ } else {
+ if (mathZero(degree)) return true;
+ rTransform = new RenderTransform();
+ }
+ rTransform->degree = degree;
+ if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
+
+ return true;
+}
+
+
+bool Paint::Impl::scale(float factor)
+{
+ if (rTransform) {
+ if (mathEqual(factor, rTransform->scale)) return true;
+ } else {
+ if (mathZero(factor)) return true;
+ rTransform = new RenderTransform();
+ }
+ rTransform->scale = factor;
+ if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
+
+ return true;
+}
+
+
+bool Paint::Impl::translate(float x, float y)
+{
+ if (rTransform) {
+ if (mathEqual(x, rTransform->x) && mathEqual(y, rTransform->y)) return true;
+ } else {
+ if (mathZero(x) && mathZero(y)) return true;
+ rTransform = new RenderTransform();
+ }
+ rTransform->x = x;
+ rTransform->y = y;
+ if (!rTransform->overriding) renderFlag |= RenderUpdateFlag::Transform;
+
+ return true;
+}
+
+
+bool Paint::Impl::render(RenderMethod& renderer)
+{
+ Compositor* cmp = nullptr;
+
+ //OPTIMIZE_ME: Can we replace the simple AlphaMasking with ClipPath?
+
+ /* Note: only ClipPath is processed in update() step.
+ Create a composition image. */
+ if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
+ auto region = smethod->bounds(renderer);
+ if (region.w == 0 || region.h == 0) return true;
+ cmp = renderer.target(region);
+ renderer.beginComposite(cmp, CompositeMethod::None, 255);
+ compData->target->pImpl->render(renderer);
+ }
+
+ if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
+
+ auto ret = smethod->render(renderer);
+
+ if (cmp) renderer.endComposite(cmp);
+
+ return ret;
+}
+
+
+void* Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, uint32_t pFlag)
+{
+ if (renderFlag & RenderUpdateFlag::Transform) {
+ if (!rTransform) return nullptr;
+ if (!rTransform->update()) {
+ delete(rTransform);
+ rTransform = nullptr;
+ }
+ }
+
+ /* 1. Composition Pre Processing */
+ void *tdata = nullptr;
+ RenderRegion viewport;
+ bool compFastTrack = false;
+
+ if (compData) {
+ auto target = compData->target;
+ auto method = compData->method;
+ target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset
+
+ /* If transform has no rotation factors && ClipPath / AlphaMasking is a simple rectangle,
+ we can avoid regular ClipPath / AlphaMasking sequence but use viewport for performance */
+ auto tryFastTrack = false;
+ if (method == CompositeMethod::ClipPath) tryFastTrack = true;
+ else if (method == CompositeMethod::AlphaMask && target->identifier() == TVG_CLASS_ID_SHAPE) {
+ auto shape = static_cast<Shape*>(target);
+ uint8_t a;
+ shape->fillColor(nullptr, nullptr, nullptr, &a);
+ if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true;
+ }
+ if (tryFastTrack) {
+ RenderRegion viewport2;
+ if ((compFastTrack = _compFastTrack(target, pTransform, target->pImpl->rTransform, viewport2))) {
+ viewport = renderer.viewport();
+ viewport2.intersect(viewport);
+ renderer.viewport(viewport2);
+ target->pImpl->ctxFlag |= ContextFlag::FastTrack;
+ }
+ }
+ if (!compFastTrack) {
+ tdata = target->pImpl->update(renderer, pTransform, 255, clips, pFlag);
+ if (method == CompositeMethod::ClipPath) clips.push(tdata);
+ }
+ }
+
+ /* 2. Main Update */
+ void *edata = nullptr;
+ auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
+ renderFlag = RenderUpdateFlag::None;
+ opacity = (opacity * this->opacity) / 255;
+
+ if (rTransform && pTransform) {
+ RenderTransform outTransform(pTransform, rTransform);
+ edata = smethod->update(renderer, &outTransform, opacity, clips, newFlag);
+ } else {
+ auto outTransform = pTransform ? pTransform : rTransform;
+ edata = smethod->update(renderer, outTransform, opacity, clips, newFlag);
+ }
+
+ /* 3. Composition Post Processing */
+ if (compFastTrack) renderer.viewport(viewport);
+ else if (tdata && compData->method == CompositeMethod::ClipPath) clips.pop();
+
+ 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;
+
+ //Compute the AABB after transformation
+ for (int i = 0; i < 4; i++) {
+ mathMultiply(&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()
+{
+ delete(pImpl);
+}
+
+
+Result Paint::rotate(float degree) noexcept
+{
+ if (pImpl->rotate(degree)) return Result::Success;
+ return Result::FailedAllocation;
+}
+
+
+Result Paint::scale(float factor) noexcept
+{
+ if (pImpl->scale(factor)) return Result::Success;
+ return Result::FailedAllocation;
+}
+
+
+Result Paint::translate(float x, float y) noexcept
+{
+ if (pImpl->translate(x, y)) return Result::Success;
+ return Result::FailedAllocation;
+}
+
+
+Result Paint::transform(const Matrix& m) noexcept
+{
+ if (pImpl->transform(m)) return Result::Success;
+ return Result::FailedAllocation;
+}
+
+
+Matrix Paint::transform() noexcept
+{
+ auto pTransform = pImpl->transform();
+ if (pTransform) return *pTransform;
+ return {1, 0, 0, 0, 1, 0, 0, 0, 1};
+}
+
+
+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, transform)) return Result::Success;
+ return Result::InsufficientCondition;
+}
+
+
+Paint* Paint::duplicate() const noexcept
+{
+ return pImpl->duplicate();
+}
+
+
+Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
+{
+ auto p = target.release();
+ if (pImpl->composite(this, p, method)) return Result::Success;
+ if (p) delete(p);
+ return Result::InvalidArguments;
+}
+
+
+CompositeMethod Paint::composite(const Paint** target) const noexcept
+{
+ if (pImpl->compData) {
+ if (target) *target = pImpl->compData->target;
+ return pImpl->compData->method;
+ } else {
+ if (target) *target = nullptr;
+ return CompositeMethod::None;
+ }
+}
+
+
+Result Paint::opacity(uint8_t o) noexcept
+{
+ if (pImpl->opacity == o) return Result::Success;
+
+ pImpl->opacity = o;
+ pImpl->renderFlag |= RenderUpdateFlag::Color;
+
+ return Result::Success;
+}
+
+
+uint8_t Paint::opacity() const noexcept
+{
+ return pImpl->opacity;
+}
+
+
+uint32_t Paint::identifier() const noexcept
+{
+ return pImpl->id;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgPaint.h b/thirdparty/thorvg/src/lib/tvgPaint.h
new file mode 100644
index 0000000000..4003bdbeac
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgPaint.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_PAINT_H_
+#define _TVG_PAINT_H_
+
+#include "tvgRender.h"
+
+
+namespace tvg
+{
+ enum ContextFlag {Invalid = 0, FastTrack = 1};
+
+ struct Iterator
+ {
+ virtual ~Iterator() {}
+ virtual const Paint* next() = 0;
+ virtual uint32_t count() = 0;
+ virtual void begin() = 0;
+ };
+
+ struct StrategyMethod
+ {
+ virtual ~StrategyMethod() {}
+
+ 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) = 0;
+ virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
+ virtual Paint* duplicate() = 0;
+ virtual Iterator* iterator() = 0;
+ };
+
+ struct Composite
+ {
+ Paint* target;
+ Paint* source;
+ CompositeMethod method;
+ };
+
+ struct Paint::Impl
+ {
+ StrategyMethod* smethod = nullptr;
+ RenderTransform* rTransform = nullptr;
+ Composite* compData = nullptr;
+ uint32_t renderFlag = RenderUpdateFlag::None;
+ uint32_t ctxFlag = ContextFlag::Invalid;
+ uint32_t id;
+ uint8_t opacity = 255;
+
+ ~Impl()
+ {
+ if (compData) {
+ delete(compData->target);
+ free(compData);
+ }
+ if (smethod) delete(smethod);
+ if (rTransform) delete(rTransform);
+ }
+
+ void method(StrategyMethod* method)
+ {
+ smethod = method;
+ }
+
+ bool transform(const Matrix& m)
+ {
+ if (!rTransform) {
+ rTransform = new RenderTransform();
+ if (!rTransform) return false;
+ }
+ rTransform->override(m);
+ renderFlag |= RenderUpdateFlag::Transform;
+
+ return true;
+ }
+
+ Matrix* transform()
+ {
+ if (rTransform) {
+ rTransform->update();
+ return &rTransform->m;
+ }
+ return nullptr;
+ }
+
+ RenderRegion bounds(RenderMethod& renderer) const
+ {
+ return smethod->bounds(renderer);
+ }
+
+ bool dispose(RenderMethod& renderer)
+ {
+ if (compData) compData->target->pImpl->dispose(renderer);
+ return smethod->dispose(renderer);
+ }
+
+ Iterator* iterator()
+ {
+ return smethod->iterator();
+ }
+
+ bool composite(Paint* source, Paint* target, CompositeMethod method)
+ {
+ //Invalid case
+ if ((!target && method != CompositeMethod::None) || (target && method == CompositeMethod::None)) return false;
+
+ if (compData) {
+ delete(compData->target);
+ //Reset scenario
+ if (!target && method == CompositeMethod::None) {
+ free(compData);
+ compData = nullptr;
+ return true;
+ }
+ } else {
+ if (!target && method == CompositeMethod::None) return true;
+ compData = static_cast<Composite*>(calloc(1, sizeof(Composite)));
+ }
+ compData->target = target;
+ compData->source = source;
+ compData->method = method;
+ return true;
+ }
+
+ 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();
+ };
+
+
+ template<class T>
+ struct PaintMethod : StrategyMethod
+ {
+ T* inst = nullptr;
+
+ PaintMethod(T* _inst) : inst(_inst) {}
+ ~PaintMethod() {}
+
+ bool bounds(float* x, float* y, float* w, float* h) override
+ {
+ return inst->bounds(x, y, w, h);
+ }
+
+ RenderRegion bounds(RenderMethod& renderer) const override
+ {
+ return inst->bounds(renderer);
+ }
+
+ bool dispose(RenderMethod& renderer) override
+ {
+ return inst->dispose(renderer);
+ }
+
+ void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag renderFlag) override
+ {
+ return inst->update(renderer, transform, opacity, clips, renderFlag);
+ }
+
+ bool render(RenderMethod& renderer) override
+ {
+ return inst->render(renderer);
+ }
+
+ Paint* duplicate() override
+ {
+ return inst->duplicate();
+ }
+
+ Iterator* iterator() override
+ {
+ return inst->iterator();
+ }
+ };
+}
+
+#endif //_TVG_PAINT_H_
diff --git a/thirdparty/thorvg/src/lib/tvgPicture.cpp b/thirdparty/thorvg/src/lib/tvgPicture.cpp
new file mode 100644
index 0000000000..1d9776363c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgPicture.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "tvgPictureImpl.h"
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Picture::Picture() : pImpl(new Impl)
+{
+ Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
+ Paint::pImpl->method(new PaintMethod<Picture::Impl>(pImpl));
+}
+
+
+Picture::~Picture()
+{
+ delete(pImpl);
+}
+
+
+unique_ptr<Picture> Picture::gen() noexcept
+{
+ return unique_ptr<Picture>(new Picture);
+}
+
+
+uint32_t Picture::identifier() noexcept
+{
+ return TVG_CLASS_ID_PICTURE;
+}
+
+
+Result Picture::load(const std::string& path) noexcept
+{
+ if (path.empty()) return Result::InvalidArguments;
+
+ return pImpl->load(path);
+}
+
+
+Result Picture::load(const char* data, uint32_t size, const string& mimeType, bool copy) noexcept
+{
+ if (!data || size <= 0) return Result::InvalidArguments;
+
+ return pImpl->load(data, size, mimeType, copy);
+}
+
+
+TVG_DEPRECATED Result Picture::load(const char* data, uint32_t size, bool copy) noexcept
+{
+ return load(data, size, "", copy);
+}
+
+
+Result Picture::load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept
+{
+ if (!data || w <= 0 || h <= 0) return Result::InvalidArguments;
+
+ return pImpl->load(data, w, h, copy);
+}
+
+
+Result Picture::viewbox(float* x, float* y, float* w, float* h) const noexcept
+{
+ if (pImpl->viewbox(x, y, w, h)) return Result::Success;
+ return Result::InsufficientCondition;
+}
+
+
+Result Picture::size(float w, float h) noexcept
+{
+ if (pImpl->size(w, h)) return Result::Success;
+ return Result::InsufficientCondition;
+}
+
+
+Result Picture::size(float* w, float* h) const noexcept
+{
+ if (!pImpl->loader) return Result::InsufficientCondition;
+ if (w) *w = pImpl->w;
+ if (h) *h = pImpl->h;
+ return Result::Success;
+}
+
+
+const uint32_t* Picture::data(uint32_t* w, uint32_t* h) const noexcept
+{
+ //Try it, If not loaded yet.
+ pImpl->reload();
+
+ if (pImpl->loader) {
+ if (w) *w = static_cast<uint32_t>(pImpl->loader->w);
+ if (h) *h = static_cast<uint32_t>(pImpl->loader->h);
+ } else {
+ if (w) *w = 0;
+ if (h) *h = 0;
+ }
+ if (pImpl->surface) return pImpl->surface->buffer;
+ else return nullptr;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgPictureImpl.h b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
new file mode 100644
index 0000000000..00e8aa6dd9
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgPictureImpl.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_PICTURE_IMPL_H_
+#define _TVG_PICTURE_IMPL_H_
+
+#include <string>
+#include "tvgPaint.h"
+#include "tvgLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct PictureIterator : Iterator
+{
+ Paint* paint = nullptr;
+ Paint* ptr = nullptr;
+
+ PictureIterator(Paint* p) : paint(p) {}
+
+ const Paint* next() override
+ {
+ if (!ptr) ptr = paint;
+ else ptr = nullptr;
+ return ptr;
+ }
+
+ uint32_t count() override
+ {
+ if (paint) return 1;
+ else return 0;
+ }
+
+ void begin() override
+ {
+ ptr = nullptr;
+ }
+};
+
+
+struct Picture::Impl
+{
+ shared_ptr<LoadModule> loader = nullptr;
+
+ Paint* paint = nullptr; //vector picture uses
+ Surface* surface = nullptr; //bitmap picture uses
+ void* rdata = nullptr; //engine data
+ float w = 0, h = 0;
+ bool resizing = false;
+
+ ~Impl()
+ {
+ if (paint) delete(paint);
+ free(surface);
+ }
+
+ bool dispose(RenderMethod& renderer)
+ {
+ bool ret = true;
+ if (paint) {
+ ret = paint->pImpl->dispose(renderer);
+ } else if (surface) {
+ ret = renderer.dispose(rdata);
+ rdata = nullptr;
+ }
+ return ret;
+ }
+
+ uint32_t reload()
+ {
+ if (loader) {
+ if (!paint) {
+ if (auto p = loader->paint()) {
+ paint = p.release();
+ loader->close();
+ if (w != loader->w || h != loader->h) {
+ loader->resize(paint, w, h);
+ resizing = false;
+ }
+ if (paint) return RenderUpdateFlag::None;
+ }
+ }
+ free(surface);
+ if ((surface = loader->bitmap().release())) {
+ loader->close();
+ return RenderUpdateFlag::Image;
+ }
+ }
+ return RenderUpdateFlag::None;
+ }
+
+ RenderTransform resizeTransform(const RenderTransform* pTransform)
+ {
+ //Overriding Transformation by the desired image size
+ auto sx = w / loader->w;
+ auto sy = h / loader->h;
+ auto scale = sx < sy ? sx : sy;
+
+ RenderTransform tmp;
+ tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1};
+
+ if (!pTransform) return tmp;
+ else return RenderTransform(pTransform, &tmp);
+ }
+
+ void* update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag)
+ {
+ auto flag = reload();
+
+ if (surface) {
+ auto transform = resizeTransform(pTransform);
+ rdata = renderer.prepare(surface, rdata, &transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
+ } else if (paint) {
+ if (resizing) {
+ loader->resize(paint, w, h);
+ resizing = false;
+ }
+ rdata = paint->pImpl->update(renderer, pTransform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
+ }
+ return rdata;
+ }
+
+ bool render(RenderMethod &renderer)
+ {
+ if (surface) return renderer.renderImage(rdata);
+ else if (paint) return paint->pImpl->render(renderer);
+ return false;
+ }
+
+ bool viewbox(float* x, float* y, float* w, float* h) const
+ {
+ if (!loader) return false;
+ if (x) *x = loader->vx;
+ if (y) *y = loader->vy;
+ if (w) *w = loader->vw;
+ if (h) *h = loader->vh;
+ return true;
+ }
+
+ bool size(float w, float h)
+ {
+ this->w = w;
+ this->h = h;
+ resizing = true;
+ return true;
+ }
+
+ bool bounds(float* x, float* y, float* w, float* h)
+ {
+ if (x) *x = 0;
+ if (y) *y = 0;
+ if (w) *w = this->w;
+ if (h) *h = this->h;
+
+ return true;
+ }
+
+ RenderRegion bounds(RenderMethod& renderer)
+ {
+ if (rdata) return renderer.region(rdata);
+ if (paint) return paint->pImpl->bounds(renderer);
+ return {0, 0, 0, 0};
+ }
+
+ Result load(const string& path)
+ {
+ if (paint || surface) return Result::InsufficientCondition;
+ if (loader) loader->close();
+ bool invalid; //Invalid Path
+ loader = LoaderMgr::loader(path, &invalid);
+ if (!loader) {
+ if (invalid) return Result::InvalidArguments;
+ return Result::NonSupport;
+ }
+ if (!loader->read()) return Result::Unknown;
+ w = loader->w;
+ h = loader->h;
+ return Result::Success;
+ }
+
+ Result load(const char* data, uint32_t size, const string& mimeType, bool copy)
+ {
+ if (paint || surface) return Result::InsufficientCondition;
+ if (loader) loader->close();
+ loader = LoaderMgr::loader(data, size, mimeType, copy);
+ if (!loader) return Result::NonSupport;
+ if (!loader->read()) return Result::Unknown;
+ w = loader->w;
+ h = loader->h;
+ return Result::Success;
+ }
+
+ Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy)
+ {
+ if (paint || surface) return Result::InsufficientCondition;
+ if (loader) loader->close();
+ loader = LoaderMgr::loader(data, w, h, copy);
+ if (!loader) return Result::NonSupport;
+ this->w = loader->w;
+ this->h = loader->h;
+ return Result::Success;
+ }
+
+ Paint* duplicate()
+ {
+ reload();
+
+ auto ret = Picture::gen();
+
+ auto dup = ret.get()->pImpl;
+ if (paint) dup->paint = paint->duplicate();
+
+ dup->loader = loader;
+ if (surface) {
+ dup->surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ *dup->surface = *surface;
+ }
+ dup->w = w;
+ dup->h = h;
+ dup->resizing = resizing;
+
+ return ret.release();
+ }
+
+ Iterator* iterator()
+ {
+ reload();
+ return new PictureIterator(paint);
+ }
+};
+
+#endif //_TVG_PICTURE_IMPL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
new file mode 100644
index 0000000000..42a83461e3
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgRadialGradient.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <float.h>
+#include "tvgFill.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct RadialGradient::Impl
+{
+ float cx = 0;
+ float cy = 0;
+ float radius = 0;
+
+ Fill* duplicate()
+ {
+ auto ret = RadialGradient::gen();
+ if (!ret) return nullptr;
+
+ ret->pImpl->cx = cx;
+ ret->pImpl->cy = cy;
+ ret->pImpl->radius = radius;
+
+ return ret.release();
+ }
+};
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+RadialGradient::RadialGradient():pImpl(new Impl())
+{
+ Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
+ Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
+}
+
+
+RadialGradient::~RadialGradient()
+{
+ delete(pImpl);
+}
+
+
+Result RadialGradient::radial(float cx, float cy, float radius) noexcept
+{
+ if (radius < 0) return Result::InvalidArguments;
+
+ pImpl->cx = cx;
+ pImpl->cy = cy;
+ pImpl->radius = radius;
+
+ return Result::Success;
+}
+
+
+Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
+{
+ if (cx) *cx = pImpl->cx;
+ if (cy) *cy = pImpl->cy;
+ if (radius) *radius = pImpl->radius;
+
+ return Result::Success;
+}
+
+
+unique_ptr<RadialGradient> RadialGradient::gen() noexcept
+{
+ return unique_ptr<RadialGradient>(new RadialGradient);
+}
+
+
+uint32_t RadialGradient::identifier() noexcept
+{
+ return TVG_CLASS_ID_RADIAL;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgRender.cpp b/thirdparty/thorvg/src/lib/tvgRender.cpp
new file mode 100644
index 0000000000..a48075dd04
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgRender.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgMath.h"
+#include "tvgRender.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void RenderTransform::override(const Matrix& m)
+{
+ this->m = m;
+
+ if (m.e11 == 0.0f && m.e12 == 0.0f && m.e13 == 0.0f &&
+ m.e21 == 0.0f && m.e22 == 0.0f && m.e23 == 0.0f &&
+ m.e31 == 0.0f && m.e32 == 0.0f && m.e33 == 0.0f) {
+ overriding = false;
+ } else overriding = true;
+}
+
+
+bool RenderTransform::update()
+{
+ if (overriding) return true;
+
+ //Init Status
+ if (mathZero(x) && mathZero(y) && mathZero(degree) && mathEqual(scale, 1)) return false;
+
+ mathIdentity(&m);
+
+ mathScale(&m, scale);
+
+ if (!mathZero(degree)) mathRotate(&m, degree);
+
+ mathTranslate(&m, x, y);
+
+ return true;
+}
+
+
+RenderTransform::RenderTransform()
+{
+}
+
+
+RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
+{
+ m = mathMultiply(&lhs->m, &rhs->m);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgRender.h b/thirdparty/thorvg/src/lib/tvgRender.h
new file mode 100644
index 0000000000..f927947aa6
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgRender.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_RENDER_H_
+#define _TVG_RENDER_H_
+
+#include "tvgCommon.h"
+#include "tvgArray.h"
+
+namespace tvg
+{
+
+enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255};
+
+struct Surface
+{
+ //TODO: Union for multiple types
+ uint32_t* buffer;
+ uint32_t stride;
+ uint32_t w, h;
+ uint32_t cs;
+};
+
+using RenderData = void*;
+
+struct Compositor
+{
+ CompositeMethod method;
+ uint32_t opacity;
+};
+
+struct RenderRegion
+{
+ int32_t x, y, w, h;
+
+ void intersect(const RenderRegion& rhs)
+ {
+ auto x1 = x + w;
+ auto y1 = y + h;
+ auto x2 = rhs.x + rhs.w;
+ auto y2 = rhs.y + rhs.h;
+
+ x = (x > rhs.x) ? x : rhs.x;
+ y = (y > rhs.y) ? y : rhs.y;
+ w = ((x1 < x2) ? x1 : x2) - x;
+ h = ((y1 < y2) ? y1 : y2) - y;
+
+ if (w < 0) w = 0;
+ if (h < 0) h = 0;
+ }
+};
+
+struct RenderTransform
+{
+ Matrix m; //3x3 Matrix Elements
+ float x = 0.0f;
+ float y = 0.0f;
+ float degree = 0.0f; //rotation degree
+ float scale = 1.0f; //scale factor
+ bool overriding = false; //user transform?
+
+ bool update();
+ void override(const Matrix& m);
+
+ RenderTransform();
+ RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
+};
+
+
+class RenderMethod
+{
+public:
+ virtual ~RenderMethod() {}
+ virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
+ virtual RenderData prepare(Surface* image, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
+ virtual bool preRender() = 0;
+ virtual bool renderShape(RenderData data) = 0;
+ virtual bool renderImage(RenderData data) = 0;
+ virtual bool postRender() = 0;
+ virtual bool dispose(RenderData data) = 0;
+ virtual RenderRegion region(RenderData data) = 0;
+ virtual RenderRegion viewport() = 0;
+ virtual bool viewport(const RenderRegion& vp) = 0;
+
+ virtual bool clear() = 0;
+ virtual bool sync() = 0;
+
+ virtual Compositor* target(const RenderRegion& region) = 0;
+ virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) = 0;
+ virtual bool endComposite(Compositor* cmp) = 0;
+};
+
+}
+
+#endif //_TVG_RENDER_H_
diff --git a/thirdparty/thorvg/src/lib/tvgSaveModule.h b/thirdparty/thorvg/src/lib/tvgSaveModule.h
new file mode 100644
index 0000000000..2a0f427f11
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgSaveModule.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SAVE_MODULE_H_
+#define _TVG_SAVE_MODULE_H_
+
+#include "tvgIteratorAccessor.h"
+
+namespace tvg
+{
+
+class SaveModule : public IteratorAccessor
+{
+public:
+ virtual ~SaveModule() {}
+
+ virtual bool save(Paint* paint, const string& path, bool compress) = 0;
+ virtual bool close() = 0;
+};
+
+}
+
+#endif //_TVG_SAVE_MODULE_H_
diff --git a/thirdparty/thorvg/src/lib/tvgSaver.cpp b/thirdparty/thorvg/src/lib/tvgSaver.cpp
new file mode 100644
index 0000000000..1a3e8614a6
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgSaver.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCommon.h"
+#include "tvgSaveModule.h"
+
+#ifdef THORVG_TVG_SAVER_SUPPORT
+ #include "tvgTvgSaver.h"
+#endif
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct Saver::Impl
+{
+ SaveModule* saveModule = nullptr;
+ ~Impl()
+ {
+ if (saveModule) delete(saveModule);
+ }
+};
+
+
+static SaveModule* _find(FileType type)
+{
+ switch(type) {
+ case FileType::Tvg: {
+#ifdef THORVG_TVG_SAVER_SUPPORT
+ return new TvgSaver;
+#endif
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+#ifdef THORVG_LOG_ENABLED
+ const char *format;
+ switch(type) {
+ case FileType::Tvg: {
+ format = "TVG";
+ break;
+ }
+ default: {
+ format = "???";
+ break;
+ }
+ }
+ TVGLOG("SAVER", "%s format is not supported", format);
+#endif
+ return nullptr;
+}
+
+
+static SaveModule* _find(const string& path)
+{
+ auto ext = path.substr(path.find_last_of(".") + 1);
+ if (!ext.compare("tvg")) {
+ return _find(FileType::Tvg);
+ }
+ return nullptr;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Saver::Saver() : pImpl(new Impl())
+{
+}
+
+
+Saver::~Saver()
+{
+ delete(pImpl);
+}
+
+
+Result Saver::save(std::unique_ptr<Paint> paint, const string& path, bool compress) noexcept
+{
+ auto p = paint.release();
+ if (!p) return Result::MemoryCorruption;
+
+ //Already on saving an other resource.
+ if (pImpl->saveModule) {
+ delete(p);
+ return Result::InsufficientCondition;
+ }
+
+ if (auto saveModule = _find(path)) {
+ if (saveModule->save(p, path, compress)) {
+ pImpl->saveModule = saveModule;
+ return Result::Success;
+ } else {
+ delete(p);
+ delete(saveModule);
+ return Result::Unknown;
+ }
+ }
+ delete(p);
+ return Result::NonSupport;
+}
+
+
+Result Saver::sync() noexcept
+{
+ if (!pImpl->saveModule) return Result::InsufficientCondition;
+ pImpl->saveModule->close();
+ delete(pImpl->saveModule);
+ pImpl->saveModule = nullptr;
+
+ return Result::Success;
+}
+
+
+unique_ptr<Saver> Saver::gen() noexcept
+{
+ return unique_ptr<Saver>(new Saver);
+}
diff --git a/thirdparty/thorvg/src/lib/tvgScene.cpp b/thirdparty/thorvg/src/lib/tvgScene.cpp
new file mode 100644
index 0000000000..4b2f77dde9
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgScene.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgSceneImpl.h"
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Scene::Scene() : pImpl(new Impl())
+{
+ Paint::pImpl->id = TVG_CLASS_ID_SCENE;
+ Paint::pImpl->method(new PaintMethod<Scene::Impl>(pImpl));
+}
+
+
+Scene::~Scene()
+{
+ delete(pImpl);
+}
+
+
+unique_ptr<Scene> Scene::gen() noexcept
+{
+ return unique_ptr<Scene>(new Scene);
+}
+
+
+uint32_t Scene::identifier() noexcept
+{
+ return TVG_CLASS_ID_SCENE;
+}
+
+
+Result Scene::push(unique_ptr<Paint> paint) noexcept
+{
+ auto p = paint.release();
+ if (!p) return Result::MemoryCorruption;
+ pImpl->paints.push(p);
+
+ return Result::Success;
+}
+
+
+Result Scene::reserve(uint32_t size) noexcept
+{
+ if (!pImpl->paints.reserve(size)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+Result Scene::clear(bool free) noexcept
+{
+ pImpl->clear(free);
+
+ return Result::Success;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgSceneImpl.h b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
new file mode 100644
index 0000000000..de94a66e2f
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgSceneImpl.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SCENE_IMPL_H_
+#define _TVG_SCENE_IMPL_H_
+
+#include <float.h>
+#include "tvgPaint.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct SceneIterator : Iterator
+{
+ Array<Paint*>* paints;
+ uint32_t idx = 0;
+
+ SceneIterator(Array<Paint*>* p) : paints(p)
+ {
+ }
+
+ const Paint* next() override
+ {
+ if (idx >= paints->count) return nullptr;
+ return paints->data[idx++];
+ }
+
+ uint32_t count() override
+ {
+ return paints->count;
+ }
+
+ void begin() override
+ {
+ idx = 0;
+ }
+};
+
+struct Scene::Impl
+{
+ Array<Paint*> paints;
+ uint8_t opacity; //for composition
+ RenderMethod* renderer = nullptr; //keep it for explicit clear
+
+ ~Impl()
+ {
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ delete(*paint);
+ }
+ }
+
+ bool dispose(RenderMethod& renderer)
+ {
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ (*paint)->pImpl->dispose(renderer);
+ }
+
+ this->renderer = nullptr;
+
+ return true;
+ }
+
+ bool needComposition(uint32_t opacity)
+ {
+ //Half translucent requires intermediate composition.
+ if (opacity == 255 || opacity == 0) return false;
+
+ //If scene has several children or only scene, it may require composition.
+ if (paints.count > 1) return true;
+ if (paints.count == 1 && (*paints.data)->identifier() == TVG_CLASS_ID_SCENE) return true;
+ return false;
+ }
+
+ void* update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flag)
+ {
+ /* Overriding opacity value. If this scene is half-translucent,
+ It must do intermeidate composition with that opacity value. */
+ this->opacity = static_cast<uint8_t>(opacity);
+ if (needComposition(opacity)) opacity = 255;
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ (*paint)->pImpl->update(renderer, transform, opacity, clips, static_cast<uint32_t>(flag));
+ }
+
+ /* FXIME: it requires to return list of children engine data
+ This is necessary for scene composition */
+
+ this->renderer = &renderer;
+
+ return nullptr;
+ }
+
+ bool render(RenderMethod& renderer)
+ {
+ Compositor* cmp = nullptr;
+
+ if (needComposition(opacity)) {
+ cmp = renderer.target(bounds(renderer));
+ renderer.beginComposite(cmp, CompositeMethod::None, opacity);
+ }
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ if (!(*paint)->pImpl->render(renderer)) return false;
+ }
+
+ if (cmp) renderer.endComposite(cmp);
+
+ return true;
+ }
+
+ RenderRegion bounds(RenderMethod& renderer) const
+ {
+ if (paints.count == 0) return {0, 0, 0, 0};
+
+ int32_t x1 = INT32_MAX;
+ int32_t y1 = INT32_MAX;
+ int32_t x2 = 0;
+ int32_t y2 = 0;
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ auto region = (*paint)->pImpl->bounds(renderer);
+
+ //Merge regions
+ if (region.x < x1) x1 = region.x;
+ if (x2 < region.x + region.w) x2 = (region.x + region.w);
+ if (region.y < y1) y1 = region.y;
+ if (y2 < region.y + region.h) y2 = (region.y + region.h);
+ }
+
+ return {x1, y1, (x2 - x1), (y2 - y1)};
+ }
+
+ bool bounds(float* px, float* py, float* pw, float* ph)
+ {
+ if (paints.count == 0) return false;
+
+ auto x1 = FLT_MAX;
+ auto y1 = FLT_MAX;
+ auto x2 = -FLT_MAX;
+ auto y2 = -FLT_MAX;
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ auto x = FLT_MAX;
+ auto y = FLT_MAX;
+ auto w = 0.0f;
+ auto h = 0.0f;
+
+ if ((*paint)->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue;
+
+ //Merge regions
+ if (x < x1) x1 = x;
+ if (x2 < x + w) x2 = (x + w);
+ if (y < y1) y1 = y;
+ if (y2 < y + h) y2 = (y + h);
+ }
+
+ if (px) *px = x1;
+ if (py) *py = y1;
+ if (pw) *pw = (x2 - x1);
+ if (ph) *ph = (y2 - y1);
+
+ return true;
+ }
+
+ Paint* duplicate()
+ {
+ auto ret = Scene::gen();
+
+ auto dup = ret.get()->pImpl;
+
+ dup->paints.reserve(paints.count);
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ dup->paints.push((*paint)->duplicate());
+ }
+
+ return ret.release();
+ }
+
+ void clear(bool free)
+ {
+ auto dispose = renderer ? true : false;
+
+ for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) {
+ if (dispose) (*paint)->pImpl->dispose(*renderer);
+ if (free) delete(*paint);
+ }
+ paints.clear();
+ renderer = nullptr;
+ }
+
+ Iterator* iterator()
+ {
+ return new SceneIterator(&paints);
+ }
+};
+
+#endif //_TVG_SCENE_IMPL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgShape.cpp b/thirdparty/thorvg/src/lib/tvgShape.cpp
new file mode 100644
index 0000000000..8db5635932
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgShape.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "tvgMath.h"
+#include "tvgShapeImpl.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+constexpr auto PATH_KAPPA = 0.552284f;
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+Shape :: Shape() : pImpl(new Impl(this))
+{
+ Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
+ Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));
+}
+
+
+Shape :: ~Shape()
+{
+ delete(pImpl);
+}
+
+
+unique_ptr<Shape> Shape::gen() noexcept
+{
+ return unique_ptr<Shape>(new Shape);
+}
+
+
+uint32_t Shape::identifier() noexcept
+{
+ return TVG_CLASS_ID_SHAPE;
+}
+
+
+Result Shape::reset() noexcept
+{
+ pImpl->path.reset();
+ pImpl->flag = RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
+{
+ if (!cmds) return 0;
+
+ *cmds = pImpl->path.cmds;
+
+ return pImpl->path.cmdCnt;
+}
+
+
+uint32_t Shape::pathCoords(const Point** pts) const noexcept
+{
+ if (!pts) return 0;
+
+ *pts = pImpl->path.pts;
+
+ return pImpl->path.ptsCnt;
+}
+
+
+Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept
+{
+ if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments;
+
+ pImpl->path.grow(cmdCnt, ptsCnt);
+ pImpl->path.append(cmds, cmdCnt, pts, ptsCnt);
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::moveTo(float x, float y) noexcept
+{
+ pImpl->path.moveTo(x, y);
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::lineTo(float x, float y) noexcept
+{
+ pImpl->path.lineTo(x, y);
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
+{
+ pImpl->path.cubicTo(cx1, cy1, cx2, cy2, x, y);
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::close() noexcept
+{
+ pImpl->path.close();
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
+{
+ auto rxKappa = rx * PATH_KAPPA;
+ auto ryKappa = ry * PATH_KAPPA;
+
+ pImpl->path.grow(6, 13);
+ pImpl->path.moveTo(cx, cy - ry);
+ pImpl->path.cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
+ pImpl->path.cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
+ pImpl->path.cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
+ pImpl->path.cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
+ pImpl->path.close();
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept
+{
+ //just circle
+ if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
+
+ startAngle = (startAngle * M_PI) / 180.0f;
+ sweep = sweep * M_PI / 180.0f;
+
+ auto nCurves = ceil(fabsf(sweep / float(M_PI_2)));
+ auto sweepSign = (sweep < 0 ? -1 : 1);
+ auto fract = fmodf(sweep, float(M_PI_2));
+ fract = (mathZero(fract)) ? float(M_PI_2) * sweepSign : fract;
+
+ //Start from here
+ Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
+
+ if (pie) {
+ pImpl->path.moveTo(cx, cy);
+ pImpl->path.lineTo(start.x + cx, start.y + cy);
+ } else {
+ pImpl->path.moveTo(start.x + cx, start.y + cy);
+ }
+
+ for (int i = 0; i < nCurves; ++i) {
+ auto endAngle = startAngle + ((i != nCurves - 1) ? float(M_PI_2) * sweepSign : fract);
+ Point end = {radius * cosf(endAngle), radius * sinf(endAngle)};
+
+ //variables needed to calculate bezier control points
+
+ //get bezier control points using article:
+ //(http://itc.ktu.lt/index.php/ITC/article/view/11812/6479)
+ auto ax = start.x;
+ auto ay = start.y;
+ auto bx = end.x;
+ auto by = end.y;
+ auto q1 = ax * ax + ay * ay;
+ auto q2 = ax * bx + ay * by + q1;
+ auto k2 = (4.0f/3.0f) * ((sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx));
+
+ start = end; //Next start point is the current end point
+
+ end.x += cx;
+ end.y += cy;
+
+ Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy};
+ Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy};
+
+ pImpl->path.cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y);
+
+ startAngle = endAngle;
+ }
+
+ if (pie) pImpl->path.close();
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept
+{
+ auto halfW = w * 0.5f;
+ auto halfH = h * 0.5f;
+
+ //clamping cornerRadius by minimum size
+ if (rx > halfW) rx = halfW;
+ if (ry > halfH) ry = halfH;
+
+ //rectangle
+ if (rx == 0 && ry == 0) {
+ pImpl->path.grow(5, 4);
+ pImpl->path.moveTo(x, y);
+ pImpl->path.lineTo(x + w, y);
+ pImpl->path.lineTo(x + w, y + h);
+ pImpl->path.lineTo(x, y + h);
+ pImpl->path.close();
+ //circle
+ } else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
+ return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
+ } else {
+ auto hrx = rx * 0.5f;
+ auto hry = ry * 0.5f;
+ pImpl->path.grow(10, 17);
+ pImpl->path.moveTo(x + rx, y);
+ pImpl->path.lineTo(x + w - rx, y);
+ pImpl->path.cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
+ pImpl->path.lineTo(x + w, y + h - ry);
+ pImpl->path.cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
+ pImpl->path.lineTo(x + rx, y + h);
+ pImpl->path.cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
+ pImpl->path.lineTo(x, y + ry);
+ pImpl->path.cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
+ pImpl->path.close();
+ }
+
+ pImpl->flag |= RenderUpdateFlag::Path;
+
+ return Result::Success;
+}
+
+
+Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
+{
+ pImpl->color[0] = r;
+ pImpl->color[1] = g;
+ pImpl->color[2] = b;
+ pImpl->color[3] = a;
+ pImpl->flag |= RenderUpdateFlag::Color;
+
+ if (pImpl->fill) {
+ delete(pImpl->fill);
+ pImpl->fill = nullptr;
+ pImpl->flag |= RenderUpdateFlag::Gradient;
+ }
+
+ return Result::Success;
+}
+
+
+Result Shape::fill(unique_ptr<Fill> f) noexcept
+{
+ auto p = f.release();
+ if (!p) return Result::MemoryCorruption;
+
+ if (pImpl->fill && pImpl->fill != p) delete(pImpl->fill);
+ pImpl->fill = p;
+ pImpl->flag |= RenderUpdateFlag::Gradient;
+
+ return Result::Success;
+}
+
+
+Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
+{
+ if (r) *r = pImpl->color[0];
+ if (g) *g = pImpl->color[1];
+ if (b) *b = pImpl->color[2];
+ if (a) *a = pImpl->color[3];
+
+ return Result::Success;
+}
+
+const Fill* Shape::fill() const noexcept
+{
+ return pImpl->fill;
+}
+
+
+Result Shape::stroke(float width) noexcept
+{
+ if (!pImpl->strokeWidth(width)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+float Shape::strokeWidth() const noexcept
+{
+ if (!pImpl->stroke) return 0;
+ return pImpl->stroke->width;
+}
+
+
+Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
+{
+ if (!pImpl->strokeColor(r, g, b, a)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
+{
+ if (!pImpl->stroke) return Result::InsufficientCondition;
+
+ if (r) *r = pImpl->stroke->color[0];
+ if (g) *g = pImpl->stroke->color[1];
+ if (b) *b = pImpl->stroke->color[2];
+ if (a) *a = pImpl->stroke->color[3];
+
+ return Result::Success;
+}
+
+
+Result Shape::stroke(unique_ptr<Fill> f) noexcept
+{
+ return pImpl->strokeFill(move(f));
+}
+
+
+const Fill* Shape::strokeFill() const noexcept
+{
+ if (!pImpl->stroke) return nullptr;
+
+ return pImpl->stroke->fill;
+}
+
+
+Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
+{
+ if ((cnt == 1) || (!dashPattern && cnt > 0) || (dashPattern && cnt == 0)) {
+ return Result::InvalidArguments;
+ }
+
+ for (uint32_t i = 0; i < cnt; i++)
+ if (dashPattern[i] < FLT_EPSILON) return Result::InvalidArguments;
+
+ if (!pImpl->strokeDash(dashPattern, cnt)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
+{
+ if (!pImpl->stroke) return 0;
+
+ if (dashPattern) *dashPattern = pImpl->stroke->dashPattern;
+
+ return pImpl->stroke->dashCnt;
+}
+
+
+Result Shape::stroke(StrokeCap cap) noexcept
+{
+ if (!pImpl->strokeCap(cap)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+Result Shape::stroke(StrokeJoin join) noexcept
+{
+ if (!pImpl->strokeJoin(join)) return Result::FailedAllocation;
+
+ return Result::Success;
+}
+
+
+StrokeCap Shape::strokeCap() const noexcept
+{
+ if (!pImpl->stroke) return StrokeCap::Square;
+
+ return pImpl->stroke->cap;
+}
+
+
+StrokeJoin Shape::strokeJoin() const noexcept
+{
+ if (!pImpl->stroke) return StrokeJoin::Bevel;
+
+ return pImpl->stroke->join;
+}
+
+
+Result Shape::fill(FillRule r) noexcept
+{
+ pImpl->rule = r;
+
+ return Result::Success;
+}
+
+
+FillRule Shape::fillRule() const noexcept
+{
+ return pImpl->rule;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgShapeImpl.h b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
new file mode 100644
index 0000000000..dcf4e6e954
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgShapeImpl.h
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_SHAPE_IMPL_H_
+#define _TVG_SHAPE_IMPL_H_
+
+#include <memory.h>
+#include "tvgPaint.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct ShapeStroke
+{
+ float width;
+ uint8_t color[4];
+ Fill *fill;
+ float* dashPattern;
+ uint32_t dashCnt;
+ StrokeCap cap;
+ StrokeJoin join;
+
+ void copy(const ShapeStroke* src)
+ {
+ width = src->width;
+ dashCnt = src->dashCnt;
+ cap = src->cap;
+ join = src->join;
+
+ memcpy(color, src->color, sizeof(color));
+ if (dashCnt > 0) {
+ dashPattern = static_cast<float*>(malloc(sizeof(float) * dashCnt));
+ memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt);
+ }
+ if (src->fill) fill = src->fill->duplicate();
+ }
+
+ void clear()
+ {
+ if (dashPattern) free(dashPattern);
+ if (fill) delete(fill);
+ }
+};
+
+
+struct ShapePath
+{
+ PathCommand* cmds = nullptr;
+ uint32_t cmdCnt = 0;
+ uint32_t reservedCmdCnt = 0;
+
+ Point *pts = nullptr;
+ uint32_t ptsCnt = 0;
+ uint32_t reservedPtsCnt = 0;
+
+ ~ShapePath()
+ {
+ if (cmds) free(cmds);
+ if (pts) free(pts);
+ }
+
+ ShapePath()
+ {
+ }
+
+ void duplicate(const ShapePath* src)
+ {
+ if (src->cmdCnt == 0 || src->ptsCnt == 0) return;
+
+ cmdCnt = src->cmdCnt;
+ reservedCmdCnt = src->reservedCmdCnt;
+ ptsCnt = src->ptsCnt;
+ reservedPtsCnt = src->reservedPtsCnt;
+
+ cmds = static_cast<PathCommand*>(malloc(sizeof(PathCommand) * reservedCmdCnt));
+ if (!cmds) return;
+ memcpy(cmds, src->cmds, sizeof(PathCommand) * cmdCnt);
+
+ pts = static_cast<Point*>(malloc(sizeof(Point) * reservedPtsCnt));
+ if (!pts) {
+ free(cmds);
+ return;
+ }
+ memcpy(pts, src->pts, sizeof(Point) * ptsCnt);
+ }
+
+ void reserveCmd(uint32_t cmdCnt)
+ {
+ if (cmdCnt <= reservedCmdCnt) return;
+ reservedCmdCnt = cmdCnt;
+ cmds = static_cast<PathCommand*>(realloc(cmds, sizeof(PathCommand) * reservedCmdCnt));
+ }
+
+ void reservePts(uint32_t ptsCnt)
+ {
+ if (ptsCnt <= reservedPtsCnt) return;
+ reservedPtsCnt = ptsCnt;
+ pts = static_cast<Point*>(realloc(pts, sizeof(Point) * reservedPtsCnt));
+ }
+
+ void grow(uint32_t cmdCnt, uint32_t ptsCnt)
+ {
+ reserveCmd(this->cmdCnt + cmdCnt);
+ reservePts(this->ptsCnt + ptsCnt);
+ }
+
+ void reset()
+ {
+ cmdCnt = 0;
+ ptsCnt = 0;
+ }
+
+ void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
+ {
+ memcpy(this->cmds + this->cmdCnt, cmds, sizeof(PathCommand) * cmdCnt);
+ memcpy(this->pts + this->ptsCnt, pts, sizeof(Point) * ptsCnt);
+ this->cmdCnt += cmdCnt;
+ this->ptsCnt += ptsCnt;
+ }
+
+ void moveTo(float x, float y)
+ {
+ if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
+ if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
+
+ cmds[cmdCnt++] = PathCommand::MoveTo;
+ pts[ptsCnt++] = {x, y};
+ }
+
+ void lineTo(float x, float y)
+ {
+ if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
+ if (ptsCnt + 2 > reservedPtsCnt) reservePts((ptsCnt + 2) * 2);
+
+ cmds[cmdCnt++] = PathCommand::LineTo;
+ pts[ptsCnt++] = {x, y};
+ }
+
+ void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
+ {
+ if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
+ if (ptsCnt + 3 > reservedPtsCnt) reservePts((ptsCnt + 3) * 2);
+
+ cmds[cmdCnt++] = PathCommand::CubicTo;
+ pts[ptsCnt++] = {cx1, cy1};
+ pts[ptsCnt++] = {cx2, cy2};
+ pts[ptsCnt++] = {x, y};
+ }
+
+ void close()
+ {
+ if (cmdCnt > 0 && cmds[cmdCnt - 1] == PathCommand::Close) return;
+
+ if (cmdCnt + 1 > reservedCmdCnt) reserveCmd((cmdCnt + 1) * 2);
+ cmds[cmdCnt++] = PathCommand::Close;
+ }
+
+ bool bounds(float* x, float* y, float* w, float* h) const
+ {
+ if (ptsCnt == 0) return false;
+
+ Point min = { pts[0].x, pts[0].y };
+ Point max = { pts[0].x, pts[0].y };
+
+ for (uint32_t i = 1; i < ptsCnt; ++i) {
+ if (pts[i].x < min.x) min.x = pts[i].x;
+ if (pts[i].y < min.y) min.y = pts[i].y;
+ if (pts[i].x > max.x) max.x = pts[i].x;
+ if (pts[i].y > max.y) max.y = pts[i].y;
+ }
+
+ if (x) *x = min.x;
+ if (y) *y = min.y;
+ if (w) *w = max.x - min.x;
+ if (h) *h = max.y - min.y;
+
+ return true;
+ }
+};
+
+
+struct Shape::Impl
+{
+ ShapePath path;
+ Fill *fill = nullptr;
+ ShapeStroke *stroke = nullptr;
+ uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
+ FillRule rule = FillRule::Winding;
+ RenderData rdata = nullptr; //engine data
+ Shape *shape = nullptr;
+ uint32_t flag = RenderUpdateFlag::None;
+
+ Impl(Shape* s) : shape(s)
+ {
+ }
+
+ ~Impl()
+ {
+ if (fill) delete(fill);
+ if (stroke) {
+ stroke->clear();
+ free (stroke);
+ }
+ }
+
+ bool dispose(RenderMethod& renderer)
+ {
+ auto ret = renderer.dispose(rdata);
+ rdata = nullptr;
+ return ret;
+ }
+
+ bool render(RenderMethod& renderer)
+ {
+ return renderer.renderShape(rdata);
+ }
+
+ void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag pFlag)
+ {
+ this->rdata = renderer.prepare(*shape, this->rdata, transform, opacity, clips, static_cast<RenderUpdateFlag>(pFlag | flag));
+ flag = RenderUpdateFlag::None;
+ return this->rdata;
+ }
+
+ RenderRegion bounds(RenderMethod& renderer)
+ {
+ return renderer.region(rdata);
+ }
+
+ bool bounds(float* x, float* y, float* w, float* h)
+ {
+ auto ret = path.bounds(x, y, w, h);
+
+ //Stroke feathering
+ if (stroke) {
+ if (x) *x -= stroke->width * 0.5f;
+ if (y) *y -= stroke->width * 0.5f;
+ if (w) *w += stroke->width;
+ if (h) *h += stroke->width;
+ }
+ return ret;
+ }
+
+ bool strokeWidth(float width)
+ {
+ //TODO: Size Exception?
+
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ stroke->width = width;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ bool strokeCap(StrokeCap cap)
+ {
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ stroke->cap = cap;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ bool strokeJoin(StrokeJoin join)
+ {
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ stroke->join = join;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+ {
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ if (stroke->fill) {
+ delete(stroke->fill);
+ stroke->fill = nullptr;
+ flag |= RenderUpdateFlag::GradientStroke;
+ }
+
+ stroke->color[0] = r;
+ stroke->color[1] = g;
+ stroke->color[2] = b;
+ stroke->color[3] = a;
+
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ Result strokeFill(unique_ptr<Fill> f)
+ {
+ auto p = f.release();
+ if (!p) return Result::MemoryCorruption;
+
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ if (stroke->fill && stroke->fill != p) delete(stroke->fill);
+ stroke->fill = p;
+
+ flag |= RenderUpdateFlag::Stroke;
+ flag |= RenderUpdateFlag::GradientStroke;
+
+ return Result::Success;
+ }
+
+ bool strokeDash(const float* pattern, uint32_t cnt)
+ {
+ //Reset dash
+ if (!pattern && cnt == 0) {
+ free(stroke->dashPattern);
+ stroke->dashPattern = nullptr;
+ } else {
+ if (!stroke) stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ if (stroke->dashCnt != cnt) {
+ free(stroke->dashPattern);
+ stroke->dashPattern = nullptr;
+ }
+ if (!stroke->dashPattern) {
+ stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
+ if (!stroke->dashPattern) return false;
+ }
+ for (uint32_t i = 0; i < cnt; ++i) {
+ stroke->dashPattern[i] = pattern[i];
+ }
+ }
+ stroke->dashCnt = cnt;
+ flag |= RenderUpdateFlag::Stroke;
+
+ return true;
+ }
+
+ Paint* duplicate()
+ {
+ auto ret = Shape::gen();
+
+ auto dup = ret.get()->pImpl;
+ dup->rule = rule;
+
+ //Color
+ memcpy(dup->color, color, sizeof(color));
+ dup->flag = RenderUpdateFlag::Color;
+
+ //Path
+ dup->path.duplicate(&path);
+ dup->flag |= RenderUpdateFlag::Path;
+
+ //Stroke
+ if (stroke) {
+ dup->stroke = static_cast<ShapeStroke*>(calloc(sizeof(ShapeStroke), 1));
+ dup->stroke->copy(stroke);
+ dup->flag |= RenderUpdateFlag::Stroke;
+
+ if (stroke->fill)
+ dup->flag |= RenderUpdateFlag::GradientStroke;
+ }
+
+ //Fill
+ if (fill) {
+ dup->fill = fill->duplicate();
+ dup->flag |= RenderUpdateFlag::Gradient;
+ }
+
+ return ret.release();
+ }
+
+ Iterator* iterator()
+ {
+ return nullptr;
+ }
+};
+
+#endif //_TVG_SHAPE_IMPL_H_
diff --git a/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp
new file mode 100644
index 0000000000..f7a03b819c
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "tvgCanvasImpl.h"
+
+#ifdef THORVG_SW_RASTER_SUPPORT
+ #include "tvgSwRenderer.h"
+#else
+ class SwRenderer : public RenderMethod
+ {
+ //Non Supported. Dummy Class */
+ };
+#endif
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct SwCanvas::Impl
+{
+};
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+#ifdef THORVG_SW_RASTER_SUPPORT
+SwCanvas::SwCanvas() : Canvas(SwRenderer::gen()), pImpl(new Impl)
+#else
+SwCanvas::SwCanvas() : Canvas(nullptr), pImpl(new Impl)
+#endif
+{
+}
+
+
+SwCanvas::~SwCanvas()
+{
+ delete(pImpl);
+}
+
+
+Result SwCanvas::mempool(MempoolPolicy policy) noexcept
+{
+#ifdef THORVG_SW_RASTER_SUPPORT
+ //We know renderer type, avoid dynamic_cast for performance.
+ auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
+ if (!renderer) return Result::MemoryCorruption;
+
+ //It can't change the policy during the running.
+ if (Canvas::pImpl->paints.count > 0) return Result::InsufficientCondition;
+
+ if (policy == MempoolPolicy::Individual) renderer->mempool(false);
+ else renderer->mempool(true);
+
+ return Result::Success;
+#endif
+ return Result::NonSupport;
+}
+
+
+Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
+{
+#ifdef THORVG_SW_RASTER_SUPPORT
+ //We know renderer type, avoid dynamic_cast for performance.
+ auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
+ if (!renderer) return Result::MemoryCorruption;
+
+ if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments;
+
+ //Paints must be updated again with this new target.
+ Canvas::pImpl->needRefresh();
+
+ return Result::Success;
+#endif
+ return Result::NonSupport;
+}
+
+
+unique_ptr<SwCanvas> SwCanvas::gen() noexcept
+{
+#ifdef THORVG_SW_RASTER_SUPPORT
+ if (SwRenderer::init() <= 0) return nullptr;
+ return unique_ptr<SwCanvas>(new SwCanvas);
+#endif
+ return nullptr;
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
new file mode 100644
index 0000000000..780127b87b
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <deque>
+#include <thread>
+#include <vector>
+#include <atomic>
+#include <condition_variable>
+#include "tvgTaskScheduler.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+namespace tvg {
+
+struct TaskQueue {
+ deque<Task*> taskDeque;
+ mutex mtx;
+ condition_variable ready;
+ bool done = false;
+
+ bool tryPop(Task** task)
+ {
+ unique_lock<mutex> lock{mtx, try_to_lock};
+ if (!lock || taskDeque.empty()) return false;
+ *task = taskDeque.front();
+ taskDeque.pop_front();
+
+ return true;
+ }
+
+ bool tryPush(Task* task)
+ {
+ {
+ unique_lock<mutex> lock{mtx, try_to_lock};
+ if (!lock) return false;
+ taskDeque.push_back(task);
+ }
+
+ ready.notify_one();
+
+ return true;
+ }
+
+ void complete()
+ {
+ {
+ unique_lock<mutex> lock{mtx};
+ done = true;
+ }
+ ready.notify_all();
+ }
+
+ bool pop(Task** task)
+ {
+ unique_lock<mutex> lock{mtx};
+
+ while (taskDeque.empty() && !done) {
+ ready.wait(lock);
+ }
+
+ if (taskDeque.empty()) return false;
+
+ *task = taskDeque.front();
+ taskDeque.pop_front();
+
+ return true;
+ }
+
+ void push(Task* task)
+ {
+ {
+ unique_lock<mutex> lock{mtx};
+ taskDeque.push_back(task);
+ }
+
+ ready.notify_one();
+ }
+
+};
+
+
+class TaskSchedulerImpl
+{
+public:
+ unsigned threadCnt;
+ vector<thread> threads;
+ vector<TaskQueue> taskQueues;
+ atomic<unsigned> idx{0};
+
+ TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt)
+ {
+ for (unsigned i = 0; i < threadCnt; ++i) {
+ threads.emplace_back([&, i] { run(i); });
+ }
+ }
+
+ ~TaskSchedulerImpl()
+ {
+ for (auto& queue : taskQueues) queue.complete();
+ for (auto& thread : threads) thread.join();
+ }
+
+ void run(unsigned i)
+ {
+ Task* task;
+
+ //Thread Loop
+ while (true) {
+ auto success = false;
+ for (unsigned x = 0; x < threadCnt * 2; ++x) {
+ if (taskQueues[(i + x) % threadCnt].tryPop(&task)) {
+ success = true;
+ break;
+ }
+ }
+
+ if (!success && !taskQueues[i].pop(&task)) break;
+ (*task)(i);
+ }
+ }
+
+ void request(Task* task)
+ {
+ //Async
+ if (threadCnt > 0) {
+ task->prepare();
+ auto i = idx++;
+ for (unsigned n = 0; n < threadCnt; ++n) {
+ if (taskQueues[(i + n) % threadCnt].tryPush(task)) return;
+ }
+ taskQueues[i % threadCnt].push(task);
+ //Sync
+ } else {
+ task->run(0);
+ }
+ }
+};
+
+}
+
+static TaskSchedulerImpl* inst = nullptr;
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void TaskScheduler::init(unsigned threads)
+{
+ if (inst) return;
+ inst = new TaskSchedulerImpl(threads);
+}
+
+
+void TaskScheduler::term()
+{
+ if (!inst) return;
+ delete(inst);
+ inst = nullptr;
+}
+
+
+void TaskScheduler::request(Task* task)
+{
+ if (inst) inst->request(task);
+}
+
+
+unsigned TaskScheduler::threads()
+{
+ if (inst) return inst->threadCnt;
+ return 0;
+}
diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
new file mode 100644
index 0000000000..f30bdf9a8f
--- /dev/null
+++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _TVG_TASK_SCHEDULER_H_
+#define _TVG_TASK_SCHEDULER_H_
+
+#include <mutex>
+#include <condition_variable>
+#include "tvgCommon.h"
+
+namespace tvg
+{
+
+struct Task;
+
+struct TaskScheduler
+{
+ static unsigned threads();
+ static void init(unsigned threads);
+ static void term();
+ static void request(Task* task);
+};
+
+struct Task
+{
+private:
+ mutex mtx;
+ condition_variable cv;
+ bool ready{true};
+ bool pending{false};
+
+public:
+ virtual ~Task() = default;
+
+ void done()
+ {
+ if (!pending) return;
+
+ unique_lock<mutex> lock(mtx);
+ while (!ready) cv.wait(lock);
+ pending = false;
+ }
+
+protected:
+ virtual void run(unsigned tid) = 0;
+
+private:
+ void operator()(unsigned tid)
+ {
+ run(tid);
+
+ lock_guard<mutex> lock(mtx);
+ ready = true;
+ cv.notify_one();
+ }
+
+ void prepare()
+ {
+ ready = false;
+ pending = true;
+ }
+
+ friend class TaskSchedulerImpl;
+};
+
+
+
+}
+
+#endif //_TVG_TASK_SCHEDULER_H_