sw_engine: implement linear gradient feature 52/235952/8
authorHermet Park <chuneon.park@samsung.com>
Thu, 11 Jun 2020 06:06:17 +0000 (15:06 +0900)
committerHermet Park <chuneon.park@samsung.com>
Sat, 13 Jun 2020 03:09:39 +0000 (12:09 +0900)
also added testLinearGradient

Change-Id: I9cce74b9fc40c4ebd978939ee50955e44e7f44f2

16 files changed:
inc/tizenvg.h
src/lib/sw_engine/meson.build
src/lib/sw_engine/tvgSwCommon.h
src/lib/sw_engine/tvgSwFill.cpp [new file with mode: 0644]
src/lib/sw_engine/tvgSwMath.cpp
src/lib/sw_engine/tvgSwRaster.cpp
src/lib/sw_engine/tvgSwRenderer.cpp
src/lib/sw_engine/tvgSwShape.cpp
src/lib/sw_engine/tvgSwStroke.cpp
src/lib/tvgCanvasImpl.h
src/lib/tvgLinearGradient.cpp
src/lib/tvgRadialGradient.cpp
src/lib/tvgScene.cpp
src/lib/tvgSceneImpl.h
src/lib/tvgShape.cpp
test/testLinearGradient.cpp

index 85d8e27..044cdfe 100644 (file)
@@ -51,14 +51,10 @@ protected: \
 #define _TVG_DECLARE_ACCESSOR(A) \
     friend A
 
-#define _TVG_DECLARE_ACCESSORS(A, B) \
-    friend A; \
-    friend B
-
 #define _TVG_DECALRE_IDENTIFIER() \
+    auto id() const { return _id; } \
 protected: \
-    unsigned id
-
+    unsigned _id
 
 namespace tvg
 {
@@ -101,7 +97,6 @@ public:
     virtual Result bounds(float* x, float* y, float* w, float* h) const = 0;
 
     _TVG_DECALRE_IDENTIFIER();
-    _TVG_DECLARE_ACCESSORS(Canvas, Scene);
 };
 
 
@@ -264,7 +259,8 @@ public:
     static std::unique_ptr<Shape> gen() noexcept;
 
     _TVG_DECLARE_PRIVATE(Shape);
-    _TVG_DECLARE_ACCESSORS(Canvas, Scene);
+    _TVG_DECLARE_ACCESSOR(Canvas);
+    _TVG_DECLARE_ACCESSOR(Scene);
 };
 
 
index f4998f4..f96fe97 100644 (file)
@@ -1,5 +1,6 @@
 source_file = [
    'tvgSwCommon.h',
+   'tvgSwFill.cpp',
    'tvgSwMath.cpp',
    'tvgSwRenderer.h',
    'tvgSwRaster.cpp',
index e6f432d..7a0bd41 100644 (file)
 
 using namespace tvg;
 
-constexpr auto SW_CURVE_TYPE_POINT = 0;
-constexpr auto SW_CURVE_TYPE_CUBIC = 1;
-
-constexpr auto SW_OUTLINE_FILL_WINDING = 0;
-constexpr auto SW_OUTLINE_FILL_EVEN_ODD = 1;
+#define SW_CURVE_TYPE_POINT 0
+#define SW_CURVE_TYPE_CUBIC 1
+#define SW_OUTLINE_FILL_WINDING 0
+#define SW_OUTLINE_FILL_EVEN_ODD 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;
@@ -153,22 +156,28 @@ struct SwDashStroke
     bool curOpGap;
 };
 
+struct SwFill
+{
+    uint32_t* ctable;
+    float x1, y1, x2, y2;
+    float dx, dy;
+    float len;
+    float offset;
+    FillSpread spread;
+    bool translucent;
+};
+
 struct SwShape
 {
     SwOutline*   outline;
     SwStroke*    stroke;
+    SwFill*      fill;
     SwRleData*   rle;
     SwRleData*   strokeRle;
     SwBBox       bbox;
 };
 
 
-constexpr static SwFixed ANGLE_PI = (180L << 16);
-constexpr static SwFixed ANGLE_2PI = (ANGLE_PI << 1);
-constexpr static SwFixed ANGLE_PI2 = (ANGLE_PI >> 1);
-constexpr static SwFixed ANGLE_PI4 = (ANGLE_PI >> 2);
-
-
 static inline SwPoint TO_SWPOINT(const Point* pt)
 {
     return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};
@@ -181,6 +190,33 @@ static inline SwCoord TO_SWCOORD(float val)
 }
 
 
+static inline uint32_t COLOR_ALPHA(uint32_t rgba)
+{
+  return (rgba >> 24) & 0xff;
+}
+
+
+static inline uint32_t COLOR_ALPHA_BLEND(uint32_t rgba, uint32_t alpha)
+{
+  return (((((rgba >> 8) & 0x00ff00ff) * alpha) & 0xff00ff00) +
+          ((((rgba & 0x00ff00ff) * alpha) >> 8) & 0x00ff00ff));
+}
+
+
+static inline uint32_t COLOR_INTERPOLATE(uint32_t rgba1, uint32_t a, uint32_t rgba2, uint32_t b)
+{
+   auto t = (((rgba1 & 0xff00ff) * a + (rgba2 & 0xff00ff) * b) >> 8) & 0xff00ff;
+   rgba1 = (((rgba1 >> 8) & 0xff00ff) * a + ((rgba2 >> 8) & 0xff00ff) * b) & 0xff00ff00;
+   return (rgba1 |= t);
+}
+
+
+static inline uint32_t COLOR_ARGB_JOIN(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+  return (a << 24 | r << 16 | g << 8 | b);
+}
+
+
 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);
@@ -202,16 +238,26 @@ void shapeDelOutline(SwShape& shape);
 void shapeResetStroke(SwShape& shape, const Shape& sdata);
 bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip);
 void shapeFree(SwShape* shape);
+void shapeDelStroke(SwShape& shape);
+bool shapeGenFillColors(SwShape& shape, const Fill* fill);
+void shapeResetFill(SwShape& shape, const Fill* fill);
+void shapeDelFill(SwShape& shape);
 
 void strokeReset(SwStroke& stroke, const Shape& shape);
 bool strokeParseOutline(SwStroke& stroke, const SwOutline& outline);
 SwOutline* strokeExportOutline(SwStroke& stroke);
 void strokeFree(SwStroke* stroke);
 
+bool fillGenColorTable(SwFill* fill, const Fill* fdata);
+void fillReset(SwFill* fill, const Fill* fdata);
+void fillFree(SwFill* fill);
+void fillFetch(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
+
 SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip);
 void rleFree(SwRleData* rle);
 
-bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
-bool rasterStroke(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+bool rasterGradientShape(Surface& surface, SwShape& shape);
+bool rasterSolidShape(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+bool rasterStroke(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
 
 #endif /* _TVG_SW_COMMON_H_ */
diff --git a/src/lib/sw_engine/tvgSwFill.cpp b/src/lib/sw_engine/tvgSwFill.cpp
new file mode 100644 (file)
index 0000000..9c4927b
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *               http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+#ifndef _TVG_SW_FILL_CPP_
+#define _TVG_SW_FILL_CPP_
+
+#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 LinearGradient* linear)
+{
+    assert(fill && linear);
+
+    if (!fill->ctable) {
+        fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
+        assert(fill->ctable);
+    }
+
+    const Fill::ColorStop* colors;
+    auto cnt = linear->colorStops(&colors);
+    if (cnt == 0 || !colors) return false;
+
+    auto pColors = colors;
+
+    if (pColors->a < 255) fill->translucent = true;
+
+    auto rgba = COLOR_ARGB_JOIN(pColors->r, pColors->g, pColors->b, pColors->a);
+    auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
+    auto pos = 1.5f * inc;
+    uint32_t i = 0;
+
+    fill->ctable[i++] = rgba;
+
+    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;
+        assert(curr && next);
+        auto delta = 1.0f / (next->offset - curr->offset);
+        if (next->a < 255) fill->translucent = true;
+        auto rgba2 = COLOR_ARGB_JOIN(next->r, next->g, next->b, next->a);
+
+        while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
+            auto t = (pos - curr->offset) * delta;
+            auto dist = static_cast<int32_t>(256 * t);
+            auto dist2 = 256 - dist;
+            fill->ctable[i] = COLOR_INTERPOLATE(rgba, dist2, rgba2, dist);
+            ++i;
+            pos += inc;
+        }
+        rgba = rgba2;
+    }
+
+    for (; i < GRADIENT_STOP_SIZE; ++i)
+        fill->ctable[i] = rgba;
+
+    //Make sure the lat 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)
+{
+    assert(fill && linear);
+
+    if (linear->linear(&fill->x1, &fill->y1, &fill->x2, &fill->y2) != Result::Success) return false;
+
+    fill->dx = fill->x2 - fill->x1;
+    fill->dy = fill->y2 - fill->y1;
+    fill->len = fill->dx * fill->dx + fill->dy * fill->dy;
+    fill->offset = 0;
+
+    if (fill->len < FLT_EPSILON) return true;
+
+    fill->dx /= fill->len;
+    fill->dy /= fill->len;
+    fill->offset = -fill->dx * fill->x1 - fill->dy * fill->y1;
+
+    return _updateColorTable(fill, linear);
+}
+
+
+bool _prepareRadial(SwFill* fill, const RadialGradient* radial)
+{
+    assert(fill && radial);
+
+    return true;
+}
+
+
+static inline uint32_t _clamp(const SwFill* fill, uint32_t pos)
+{
+    switch (fill->spread) {
+        case FillSpread::Pad: {
+            if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1;
+            break;
+        }
+        case FillSpread::Repeat: {
+            pos = pos % GRADIENT_STOP_SIZE;
+            break;
+        }
+        case FillSpread::Reflect: {
+            auto limit = GRADIENT_STOP_SIZE * 2;
+            pos = pos % limit;
+            if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1);
+            break;
+        }
+    }
+    return pos;
+}
+
+
+static inline uint32_t _fixedPixel(const SwFill* fill, uint32_t pos)
+{
+    auto 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<uint32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f);
+    return fill->ctable[_clamp(fill, i)];
+}
+
+
+static inline void _write(uint32_t *dst, uint32_t val, uint32_t len)
+{
+   if (len <= 0) return;
+
+   // Cute hack to align future memcopy operation
+   // and do unroll the loop a bit. Not sure it is
+   // the most efficient, but will do for now.
+   auto n = (len + 7) / 8;
+
+   switch (len & 0x07) {
+        case 0: do { *dst++ = val;
+        case 7:      *dst++ = val;
+        case 6:      *dst++ = val;
+        case 5:      *dst++ = val;
+        case 4:      *dst++ = val;
+        case 3:      *dst++ = val;
+        case 2:      *dst++ = val;
+        case 1:      *dst++ = val;
+        } while (--n > 0);
+    }
+}
+
+
+/************************************************************************/
+/* External Class Implementation                                        */
+/************************************************************************/
+
+void fillFetch(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
+{
+    assert(fill->len > 0);
+
+    //TODO: Rotation???
+    auto rx = x + 0.5f;
+    auto ry = y + 0.5f;
+    auto t = (fill->dx * rx + fill->dy * ry + fill->offset) * (GRADIENT_STOP_SIZE - 1);
+    auto inc = (fill->dx) * (GRADIENT_STOP_SIZE - 1);
+
+    if (fabsf(inc) < FLT_EPSILON) {
+        auto color = _fixedPixel(fill, static_cast<uint32_t>(t * FIXPT_SIZE));
+        _write(dst, color, 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<uint32_t>(t * FIXPT_SIZE);
+        auto inc2 = static_cast<uint32_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 {
+        while (dst < dst + len) {
+            *dst = _pixel(fill, t / GRADIENT_STOP_SIZE);
+            ++dst;
+            t += inc;
+        }
+    }
+}
+
+
+bool fillGenColorTable(SwFill* fill, const Fill* fdata)
+{
+    if (!fill) return false;
+
+    assert(fdata);
+
+    fill->spread = fdata->spread();
+
+    if (fdata->id() == FILL_ID_LINEAR) {
+        return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata));
+    } else if (fdata->id() == FILL_ID_RADIAL) {
+        return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata));
+    }
+
+    cout << "What type of gradient?!" << endl;
+
+    return false;
+}
+
+
+void fillReset(SwFill* fill, const Fill* fdata)
+{
+    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);
+}
+
+#endif /* _TVG_SW_FILL_CPP_ */
\ No newline at end of file
index a0a0ea1..53cc8ce 100644 (file)
@@ -93,15 +93,15 @@ static void _polarize(SwPoint& pt)
             auto tmp = v.y;
             v.y = -v.x;
             v.x = tmp;
-            theta = ANGLE_PI2;
+            theta = SW_ANGLE_PI2;
         } else {
-            theta = v.y > 0 ? ANGLE_PI : -ANGLE_PI;
+            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 = -ANGLE_PI2;
+            theta = -SW_ANGLE_PI2;
             auto tmp = -v.y;
             v.y = v.x;
             v.x = tmp;
@@ -144,18 +144,18 @@ static void _rotate(SwPoint& pt, SwFixed theta)
     SwFixed y = pt.y;
 
     //Rotate inside [-PI/4, PI/4] sector
-    while (theta < -ANGLE_PI4) {
+    while (theta < -SW_ANGLE_PI4) {
         auto tmp = y;
         y = -x;
         x = tmp;
-        theta += ANGLE_PI2;
+        theta += SW_ANGLE_PI2;
     }
 
-    while (theta > ANGLE_PI4) {
+    while (theta > SW_ANGLE_PI4) {
         auto tmp = -y;
         y = x;
         x = tmp;
-        theta -= ANGLE_PI2;
+        theta -= SW_ANGLE_PI2;
     }
 
     auto atan = ATAN_TBL;
@@ -236,7 +236,7 @@ bool mathSmallCubic(SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed&
     auto theta1 = abs(mathDiff(angleIn, angleMid));
     auto theta2 = abs(mathDiff(angleMid, angleOut));
 
-    if ((theta1 < (ANGLE_PI / 8)) && (theta2 < (ANGLE_PI / 8))) return true;
+    if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true;
     else return false;
 }
 
@@ -346,7 +346,7 @@ SwFixed mathAtan(const SwPoint& pt)
 
 SwFixed mathSin(SwFixed angle)
 {
-    return mathCos(ANGLE_PI2 - angle);
+    return mathCos(SW_ANGLE_PI2 - angle);
 }
 
 
@@ -408,9 +408,9 @@ SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
 {
     auto delta = angle2 - angle1;
 
-    delta %= ANGLE_2PI;
-    if (delta < 0) delta += ANGLE_2PI;
-    if (delta > ANGLE_PI) delta -= ANGLE_2PI;
+    delta %= SW_ANGLE_2PI;
+    if (delta < 0) delta += SW_ANGLE_2PI;
+    if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI;
 
     return delta;
 }
index 78d507b..581f8b7 100644 (file)
 /* Internal Class Implementation                                        */
 /************************************************************************/
 
-static inline uint32_t COLOR_ALPHA(uint32_t color)
+static bool _rasterTranslucentRle(Surface& surface, SwRleData* rle, uint32_t color)
 {
-  return (color >> 24) & 0xff;
-}
-
-
-static inline uint32_t COLOR_ALPHA_BLEND(uint32_t color, uint32_t alpha)
-{
-  return (((((color >> 8) & 0x00ff00ff) * alpha) & 0xff00ff00) +
-          ((((color & 0x00ff00ff) * alpha) >> 8) & 0x00ff00ff));
-}
+    if (!rle) return false;
 
+    auto span = rle->spans;
+    auto stride = surface.stride;
+    uint32_t tmp;
 
-static inline uint32_t COLOR_ARGB_JOIN(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
-{
-  return (a << 24 | r << 16 | g << 8 | b);
+    for (uint32_t i = 0; i < rle->size; ++i) {
+        auto dst = &surface.buffer[span->y * stride + span->x];
+        if (span->coverage < 255) tmp = COLOR_ALPHA_BLEND(color, span->coverage);
+        else tmp = color;
+        for (uint32_t i = 0; i < span->len; ++i) {
+            dst[i] = tmp + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(tmp));
+        }
+        ++span;
+    }
+    return true;
 }
 
 
-static void
-_rasterTranslucent(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov)
+static bool _rasterSolidRle(Surface& surface, SwRleData* rle, uint32_t color)
 {
-  //OPTIMIZE ME: SIMD
-
-  if (cov < 255) color = COLOR_ALPHA_BLEND(color, cov);
-  auto ialpha = 255 - COLOR_ALPHA(color);
-
-  for (uint32_t i = 0; i < len; ++i) {
-    dst[i] = color + COLOR_ALPHA_BLEND(dst[i], ialpha);
-  }
-}
-
+    if (!rle) return false;
 
-static void
-_rasterSolid(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov)
-{
-  //OPTIMIZE ME: SIMD
+    auto span = rle->spans;
+    auto stride = surface.stride;
 
-  //Fully Opaque
-  if (cov == 255) {
-    for (uint32_t i = 0; i < len; ++i) {
-      dst[i] = color;
-    }
-  } else {
-    auto ialpha = 255 - cov;
-    for (uint32_t i = 0; i < len; ++i) {
-      dst[i] = COLOR_ALPHA_BLEND(color, cov) + COLOR_ALPHA_BLEND(dst[i], ialpha);
+    for (uint32_t i = 0; i < rle->size; ++i) {
+        auto dst = &surface.buffer[span->y * stride + span->x];
+        if (span->coverage == 255) {
+            for (uint32_t i = 0; i < span->len; ++i) {
+              dst[i] = color;
+            }
+        } else {
+            for (uint32_t i = 0; i < span->len; ++i) {
+                dst[i] = COLOR_ALPHA_BLEND(color, span->coverage) + COLOR_ALPHA_BLEND(dst[i], 255 - span->coverage);
+            }
+        }
+        ++span;
     }
-  }
+    return true;
 }
 
 
-static bool
-_rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a)
+static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill)
 {
-    if (!rle) return false;
+    if (!rle || !fill) return false;
+
+    auto buf = static_cast<uint32_t*>(alloca(surface.w * sizeof(uint32_t)));
+    if (!buf) return false;
 
     auto span = rle->spans;
     auto stride = surface.stride;
 
-    for (uint32_t i = 0; i < rle->size; ++i) {
-        assert(span);
-
-        auto dst = &surface.buffer[span->y * stride + span->x];
-
-        if (a == 255) _rasterSolid(dst, span->len, color, span->coverage);
-        else _rasterTranslucent(dst, span->len, color, span->coverage);
-
-        ++span;
+    //Translucent Gradient
+    if (fill->translucent) {
+        uint32_t tmp;
+        for (uint32_t i = 0; i < rle->size; ++i) {
+            auto dst = &surface.buffer[span->y * stride + span->x];
+            fillFetch(fill, buf, span->y, span->x, span->len);
+            if (span->coverage == 255) {
+                for (uint32_t i = 0; i < span->len; ++i) {
+                    dst[i] = buf[i] + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(buf[i]));
+                }
+            } else {
+                for (uint32_t i = 0; i < span->len; ++i) {
+                    tmp = COLOR_ALPHA_BLEND(buf[i], span->coverage);
+                    dst[i] = tmp + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(tmp));
+                }
+            }
+            ++span;
+        }
+    //Opaque Gradient
+    } else {
+        for (uint32_t i = 0; i < rle->size; ++i) {
+            auto dst = &surface.buffer[span->y * stride + span->x];
+            if (span->coverage == 255) {
+                fillFetch(fill, dst, span->y, span->x, span->len);
+            } else {
+                fillFetch(fill, buf, span->y, span->x, span->len);
+                auto ialpha = 255 - span->coverage;
+                for (uint32_t i = 0; i < span->len; ++i) {
+                    dst[i] = COLOR_ALPHA_BLEND(buf[i], span->coverage) + COLOR_ALPHA_BLEND(dst[i], ialpha);
+                }
+            }
+            ++span;
+        }
     }
-
     return true;
 }
 
@@ -103,15 +121,23 @@ _rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a)
 /* External Class Implementation                                        */
 /************************************************************************/
 
-bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+bool rasterGradientShape(Surface& surface, SwShape& shape)
+{
+    return _rasterGradientRle(surface, shape.rle, shape.fill);
+}
+
+
+bool rasterSolidShape(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
-    return _rasterRle(surface, sdata.rle, COLOR_ARGB_JOIN(r, g, b, a), a);
+    if (a == 255) return _rasterSolidRle(surface, shape.rle, COLOR_ARGB_JOIN(r, g, b, a));
+    return _rasterTranslucentRle(surface, shape.rle, COLOR_ARGB_JOIN(r, g, b, a));
 }
 
 
-bool rasterStroke(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+bool rasterStroke(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
 {
-    return _rasterRle(surface, sdata.strokeRle, COLOR_ARGB_JOIN(r, g, b, a), a);
+    if (a == 255) return _rasterSolidRle(surface, shape.strokeRle, COLOR_ARGB_JOIN(r, g, b, a));
+    return _rasterTranslucentRle(surface, shape.strokeRle, COLOR_ARGB_JOIN(r, g, b, a));
 }
 
 
index 43c2628..e99bf38 100644 (file)
@@ -57,28 +57,32 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
     return true;
 }
 
-bool SwRenderer::render(const Shape& shape, void *data)
+bool SwRenderer::render(const Shape& sdata, void *data)
 {
-    SwShape* sdata = static_cast<SwShape*>(data);
-    if (!sdata) return false;
+    SwShape* shape = static_cast<SwShape*>(data);
+    if (!shape) return false;
 
     uint8_t r, g, b, a;
 
-    shape.fill(&r, &g, &b, &a);
-    if (a > 0) rasterShape(surface, *sdata, r, g, b, a);
+    if (sdata.fill()) {
+        rasterGradientShape(surface, *shape);
+    } else {
+        sdata.fill(&r, &g, &b, &a);
+        if (a > 0) rasterSolidShape(surface, *shape, r, g, b, a);
+    }
 
-    shape.strokeColor(&r, &g, &b, &a);
-    if (a > 0) rasterStroke(surface, *sdata, r, g, b, a);
+    sdata.strokeColor(&r, &g, &b, &a);
+    if (a > 0) rasterStroke(surface, *shape, r, g, b, a);
 
     return true;
 }
 
 
-bool SwRenderer::dispose(const Shape& shape, void *data)
+bool SwRenderer::dispose(const Shape& sdata, void *data)
 {
-    auto sdata = static_cast<SwShape*>(data);
-    if (!sdata) return true;
-    shapeFree(sdata);
+    auto shape = static_cast<SwShape*>(data);
+    if (!shape) return true;
+    shapeFree(shape);
     return true;
 }
 
@@ -103,11 +107,22 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
         shapeReset(*shape);
         uint8_t alpha = 0;
         sdata.fill(nullptr, nullptr, nullptr, &alpha);
-        if (alpha > 0) {
+        if (alpha > 0 || sdata.fill()) {
             if (!shapeGenRle(*shape, sdata, clip, transform)) return shape;
         }
     }
 
+    //Fill
+    if (flags & (RenderUpdateFlag::Gradient)) {
+        auto fill = sdata.fill();
+        if (fill) {
+            shapeResetFill(*shape, fill);
+            if (!shapeGenFillColors(*shape, fill)) return shape;
+        } else {
+            shapeDelFill(*shape);
+        }
+    }
+
     //Stroke
     if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
         if (sdata.strokeWidth() > 0.5) {
@@ -117,6 +132,8 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
             if (alpha > 0) {
                 if (!shapeGenStrokeRle(*shape, sdata, clip)) return shape;
             }
+        } else {
+            shapeDelStroke(*shape);
         }
     }
 
index dcb57c7..4dcf210 100644 (file)
@@ -666,19 +666,29 @@ bool shapeGenOutline(SwShape& shape, const Shape& sdata)
 }
 
 
-void shapeFree(SwShape* sdata)
+void shapeFree(SwShape* shape)
 {
-    assert(sdata);
+    assert(shape);
 
-    shapeDelOutline(*sdata);
-    rleFree(sdata->rle);
+    shapeDelOutline(*shape);
+    rleFree(shape->rle);
 
-    if (sdata->stroke) {
-        rleFree(sdata->strokeRle);
-        strokeFree(sdata->stroke);
+    if (shape->stroke) {
+        rleFree(shape->strokeRle);
+        strokeFree(shape->stroke);
     }
 
-    free(sdata);
+    free(shape);
+}
+
+
+void shapeDelStroke(SwShape& shape)
+{
+    if (!shape.stroke) return;
+    rleFree(shape.strokeRle);
+    shape.strokeRle = nullptr;
+    strokeFree(shape.stroke);
+    shape.stroke = nullptr;
 }
 
 
@@ -730,4 +740,32 @@ bool shapeGenStrokeRle(SwShape& shape, const Shape& sdata, const SwSize& clip)
 }
 
 
+bool shapeGenFillColors(SwShape& shape, const Fill* fill)
+{
+    assert(fill);
+
+    fillGenColorTable(shape.fill, fill);
+    return true;
+}
+
+
+void shapeResetFill(SwShape& shape, const Fill* fill)
+{
+    assert(fill);
+
+    if (!shape.fill) shape.fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
+    assert(shape.fill);
+
+    fillReset(shape.fill, fill);
+}
+
+
+void shapeDelFill(SwShape& shape)
+{
+    if (!shape.fill) return;
+    fillFree(shape.fill);
+    shape.fill = nullptr;
+}
+
+
 #endif /* _TVG_SW_SHAPE_H_ */
index fdc8cae..8b6f1ce 100644 (file)
@@ -30,7 +30,7 @@ static constexpr auto SW_STROKE_TAG_END = 8;
 
 static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
 {
-    return (ANGLE_PI2 - (s) * ANGLE_PI);
+    return (SW_ANGLE_PI2 - (s) * SW_ANGLE_PI);
 }
 
 
@@ -134,14 +134,14 @@ static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl
 
 static void _borderArcTo(SwStrokeBorder* border, SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff)
 {
-    constexpr SwFixed ARC_CUBIC_ANGLE = ANGLE_PI / 2;
+    constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2;
     SwPoint a = {radius, 0};
     mathRotate(a, angleStart);
     a += center;
 
     auto total = angleDiff;
     auto angle = angleStart;
-    auto rotate = (angleDiff >= 0) ? ANGLE_PI2 : -ANGLE_PI2;
+    auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
 
     while (total != 0) {
         auto step = total;
@@ -222,7 +222,7 @@ 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 == ANGLE_PI) total = -rotate * 2;
+    if (total == SW_ANGLE_PI) total = -rotate * 2;
 
     _borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total);
     border->movable = false;
@@ -249,7 +249,7 @@ static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
 
         if (!bevel) {
             auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
-            if (theta == ANGLE_PI) {
+            if (theta == SW_ANGLE_PI) {
                 theta = rotate;
                 phi = stroke.angleIn;
             } else {
@@ -354,7 +354,7 @@ void _processCorner(SwStroke& stroke, SwFixed lineLength)
 void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
 {
     SwPoint delta = {stroke.width, 0};
-    mathRotate(delta, startAngle + ANGLE_PI2);
+    mathRotate(delta, startAngle + SW_ANGLE_PI2);
 
     auto pt = stroke.center + delta;
     auto border = stroke.borders;
@@ -384,7 +384,7 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to)
     auto angle = mathAtan(delta);
 
     delta = {stroke.width, 0};
-    mathRotate(delta, angle + ANGLE_PI2);
+    mathRotate(delta, angle + SW_ANGLE_PI2);
 
     //process corner if necessary
     if (stroke.firstPt) {
@@ -460,7 +460,7 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
                 stroke.angleOut = angleIn;
                 _processCorner(stroke, 0);
             }
-        } else if (abs(mathDiff(stroke.angleIn, angleIn)) > (ANGLE_PI / 8)) {
+        } else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8)) {
             //if the deviation from one arc to the next is too great add a round corner
             stroke.center = arc[3];
             stroke.angleOut = angleIn;
@@ -515,7 +515,7 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
                 auto alpha1 = mathAtan(_end - _start);
 
                 //is the direction of the border arc opposite to that of the original arc?
-                if (abs(mathDiff(alpha0, alpha1)) > ANGLE_PI / 2) {
+                if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) {
 
                     //use the sine rule to find the intersection point
                     auto beta = mathAtan(arc[3] - _start);
@@ -583,7 +583,7 @@ static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
     } else if (stroke.cap == StrokeCap::Round) {
 
         stroke.angleIn = angle;
-        stroke.angleOut = angle + ANGLE_PI;
+        stroke.angleOut = angle + SW_ANGLE_PI;
         _arcTo(stroke, side);
         return;
 
@@ -692,7 +692,7 @@ static void _endSubPath(SwStroke& stroke)
 
         //now add the final cap
         stroke.center = stroke.ptStartSubPath;
-        _addCap(stroke, stroke.subPathAngle + ANGLE_PI, 0);
+        _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 */
index c6dc073..42cf470 100644 (file)
@@ -53,7 +53,7 @@ struct Canvas::Impl
         assert(renderer);
 
         for (auto paint : paints) {
-            if (paint->id == PAINT_ID_SCENE) {
+            if (paint->id() == PAINT_ID_SCENE) {
                 //We know renderer type, avoid dynamic_cast for performance.
                 auto scene = static_cast<Scene*>(paint);
                 if (!SCENE_IMPL->clear(*renderer)) return Result::InsufficientCondition;
@@ -73,7 +73,7 @@ struct Canvas::Impl
         assert(renderer);
 
         for(auto paint: paints) {
-            if (paint->id == PAINT_ID_SCENE) {
+            if (paint->id() == PAINT_ID_SCENE) {
                 //We know renderer type, avoid dynamic_cast for performance.
                 auto scene = static_cast<Scene*>(paint);
                 if (!SCENE_IMPL->update(*renderer, nullptr)) return Result::InsufficientCondition;
@@ -89,7 +89,7 @@ struct Canvas::Impl
     {
         assert(renderer);
 
-        if (paint->id == PAINT_ID_SCENE) {
+        if (paint->id() == PAINT_ID_SCENE) {
             //We know renderer type, avoid dynamic_cast for performance.
             auto scene = static_cast<Scene*>(paint);
             if (!SCENE_IMPL->update(*renderer)) return Result::InsufficientCondition;
@@ -108,7 +108,7 @@ struct Canvas::Impl
         if (!renderer->clear()) return Result::InsufficientCondition;
 
         for(auto paint: paints) {
-           if (paint->id == PAINT_ID_SCENE) {
+           if (paint->id() == PAINT_ID_SCENE) {
                 //We know renderer type, avoid dynamic_cast for performance.
                 auto scene = static_cast<Scene*>(paint);
                 if(!SCENE_IMPL->render(*renderer)) return Result::InsufficientCondition;
index 9ea803d..4499454 100644 (file)
@@ -35,7 +35,7 @@ struct LinearGradient::Impl
 
 LinearGradient::LinearGradient():pImpl(make_unique<Impl>())
 {
-    id = FILL_ID_LINEAR;
+    _id = FILL_ID_LINEAR;
 }
 
 
index 2447e6f..895cccb 100644 (file)
@@ -35,7 +35,7 @@ struct RadialGradient::Impl
 
 RadialGradient::RadialGradient():pImpl(make_unique<Impl>())
 {
-    id = FILL_ID_RADIAL;
+    _id = FILL_ID_RADIAL;
 }
 
 
index ac2ae82..1ce0ada 100644 (file)
@@ -25,7 +25,7 @@
 
 Scene::Scene() : pImpl(make_unique<Impl>())
 {
-    id = PAINT_ID_SCENE;
+    _id = PAINT_ID_SCENE;
 }
 
 
index 8596292..2c92d5b 100644 (file)
@@ -39,7 +39,7 @@ struct Scene::Impl
     bool clear(RenderMethod& renderer)
     {
         for (auto paint : paints) {
-            if (paint->id == PAINT_ID_SCENE) {
+            if (paint->id() == PAINT_ID_SCENE) {
                 //We know renderer type, avoid dynamic_cast for performance.
                 auto scene = static_cast<Scene*>(paint);
                 if (!SCENE_IMPL->clear(renderer)) return false;
@@ -57,7 +57,7 @@ struct Scene::Impl
     bool updateInternal(RenderMethod &renderer, const RenderTransform* transform, uint32_t flag)
     {
         for(auto paint: paints) {
-            if (paint->id == PAINT_ID_SCENE) {
+            if (paint->id() == PAINT_ID_SCENE) {
                 //We know renderer type, avoid dynamic_cast for performance.
                 auto scene = static_cast<Scene*>(paint);
                 if (!SCENE_IMPL->update(renderer, transform, flag)) return false;
@@ -97,7 +97,7 @@ struct Scene::Impl
     bool render(RenderMethod &renderer)
     {
         for(auto paint: paints) {
-            if (paint->id == PAINT_ID_SCENE) {
+            if (paint->id() == PAINT_ID_SCENE) {
                 //We know renderer type, avoid dynamic_cast for performance.
                 auto scene = static_cast<Scene*>(paint);
                 if(!SCENE_IMPL->render(renderer)) return false;
@@ -122,7 +122,7 @@ struct Scene::Impl
             auto w2 = 0.0f;
             auto h2 = 0.0f;
 
-            if (paint->id == PAINT_ID_SCENE) {
+            if (paint->id() == PAINT_ID_SCENE) {
                 //We know renderer type, avoid dynamic_cast for performance.
                 auto scene = static_cast<Scene*>(paint);
                 if (!SCENE_IMPL->bounds(&x2, &y2, &w2, &h2)) return false;
index 012fcb1..aabd5bd 100644 (file)
@@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f;
 
 Shape :: Shape() : pImpl(make_unique<Impl>())
 {
-    id = PAINT_ID_SHAPE;
+    _id = PAINT_ID_SHAPE;
 }
 
 
index 4f87cad..33536c4 100644 (file)
@@ -18,37 +18,39 @@ void tvgtest()
     canvas->target(buffer, WIDTH, WIDTH, HEIGHT);
     canvas->reserve(3);                          //reserve 3 shape nodes (optional)
 
-    //Linear Gradient Color Stops
-    tvg::Fill::ColorStop colorStops[3];
-    colorStops[0] = {0, 255, 0, 0, 255};
-    colorStops[1] = {0.5, 255, 255, 255, 255};
-    colorStops[2] = {1, 0, 0, 255, 255};
-
     //Prepare Round Rectangle
     auto shape1 = tvg::Shape::gen();
-    shape1->appendRect(0, 0, 400, 400, 50);      //x, y, w, h, cornerRadius
-    shape1->stroke(255, 255, 255, 255);
-    shape1->stroke(2);
+    shape1->appendRect(0, 0, 400, 400, 0);      //x, y, w, h, cornerRadius
 
     //LinearGradient
     auto fill = tvg::LinearGradient::gen();
     fill->linear(0, 0, 400, 400);
-    fill->colorStops(colorStops, 3);
+
+    //Linear Gradient Color Stops
+    tvg::Fill::ColorStop colorStops[2];
+    colorStops[0] = {0, 0, 0, 0, 255};
+    colorStops[1] = {1, 255, 255, 255, 255};
+
+    fill->colorStops(colorStops, 2);
 
     shape1->fill(move(fill));
     canvas->push(move(shape1));
 
-
     //Prepare Circle
     auto shape2 = tvg::Shape::gen();
     shape2->appendCircle(400, 400, 200, 200);    //cx, cy, radiusW, radiusH
-    shape2->stroke(255, 255, 255, 255);
-    shape2->stroke(2);
 
     //LinearGradient
     auto fill2 = tvg::LinearGradient::gen();
     fill2->linear(400, 200, 400, 600);
-    fill2->colorStops(colorStops, 3);
+
+    //Linear Gradient Color Stops
+    tvg::Fill::ColorStop colorStops2[3];
+    colorStops2[0] = {0, 255, 0, 0, 255};
+    colorStops2[1] = {0.5, 255, 255, 0, 255};
+    colorStops2[2] = {1, 255, 255, 255, 255};
+
+    fill2->colorStops(colorStops2, 3);
 
     shape2->fill(move(fill2));
     canvas->push(move(shape2));
@@ -57,13 +59,19 @@ void tvgtest()
     //Prepare Ellipse
     auto shape3 = tvg::Shape::gen();
     shape3->appendCircle(600, 600, 150, 100);    //cx, cy, radiusW, radiusH
-    shape3->stroke(255, 255, 255, 255);
-    shape3->stroke(2);
 
     //LinearGradient
     auto fill3 = tvg::LinearGradient::gen();
     fill3->linear(450, 600, 750, 600);
-    fill3->colorStops(colorStops, 3);
+
+    //Linear Gradient Color Stops
+    tvg::Fill::ColorStop colorStops3[4];
+    colorStops3[0] = {0, 0, 127, 0, 127};
+    colorStops3[1] = {0.25, 0, 170, 170, 170};
+    colorStops3[2] = {0.5, 200, 0, 200, 200};
+    colorStops3[3] = {1, 255, 255, 255, 255};
+
+    fill3->colorStops(colorStops3, 4);
 
     shape3->fill(move(fill3));
     canvas->push(move(shape3));