diff --git a/inc/tizenvg.h b/inc/tizenvg.h index 85d8e27c..044cdfeb 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -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 gen() noexcept; _TVG_DECLARE_PRIVATE(Shape); - _TVG_DECLARE_ACCESSORS(Canvas, Scene); + _TVG_DECLARE_ACCESSOR(Canvas); + _TVG_DECLARE_ACCESSOR(Scene); }; diff --git a/src/lib/sw_engine/meson.build b/src/lib/sw_engine/meson.build index f4998f4b..f96fe970 100644 --- a/src/lib/sw_engine/meson.build +++ b/src/lib/sw_engine/meson.build @@ -1,5 +1,6 @@ source_file = [ 'tvgSwCommon.h', + 'tvgSwFill.cpp', 'tvgSwMath.cpp', 'tvgSwRenderer.h', 'tvgSwRaster.cpp', diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index e6f432df..7a0bd417 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -21,11 +21,14 @@ 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 index 00000000..9c4927b6 --- /dev/null +++ b/src/lib/sw_engine/tvgSwFill.cpp @@ -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<ctable) { + fill->ctable = static_cast(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(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(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(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(t * FIXPT_SIZE)); + _write(dst, color, len); + return; + } + + auto vMax = static_cast(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(t * FIXPT_SIZE); + auto inc2 = static_cast(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(fdata)); + } else if (fdata->id() == FILL_ID_RADIAL) { + return _prepareRadial(fill, static_cast(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 diff --git a/src/lib/sw_engine/tvgSwMath.cpp b/src/lib/sw_engine/tvgSwMath.cpp index a0a0ea16..53cc8ce1 100644 --- a/src/lib/sw_engine/tvgSwMath.cpp +++ b/src/lib/sw_engine/tvgSwMath.cpp @@ -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; } diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 78d507b5..581f8b7e 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -24,60 +24,28 @@ /* 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; -} + if (!rle) return false; + auto span = rle->spans; + auto stride = surface.stride; + uint32_t tmp; -static inline uint32_t COLOR_ALPHA_BLEND(uint32_t color, uint32_t alpha) -{ - return (((((color >> 8) & 0x00ff00ff) * alpha) & 0xff00ff00) + - ((((color & 0x00ff00ff) * alpha) >> 8) & 0x00ff00ff)); -} - - -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); -} - - -static void -_rasterTranslucent(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov) -{ - //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); - } -} - - -static void -_rasterSolid(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov) -{ - //OPTIMIZE ME: SIMD - - //Fully Opaque - if (cov == 255) { - for (uint32_t i = 0; i < len; ++i) { - dst[i] = color; + 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; } - } 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); - } - } + return true; } -static bool -_rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a) +static bool _rasterSolidRle(Surface& surface, SwRleData* rle, uint32_t color) { if (!rle) return false; @@ -85,16 +53,66 @@ _rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a) 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); - + 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 _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill) +{ + if (!rle || !fill) return false; + + auto buf = static_cast(alloca(surface.w * sizeof(uint32_t))); + if (!buf) return false; + + auto span = rle->spans; + auto stride = surface.stride; + + //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 _rasterRle(surface, sdata.rle, COLOR_ARGB_JOIN(r, g, b, a), a); + return _rasterGradientRle(surface, shape.rle, shape.fill); } -bool rasterStroke(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +bool rasterSolidShape(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.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& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t 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)); } diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 43c2628c..e99bf387 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -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(data); - if (!sdata) return false; + SwShape* shape = static_cast(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(data); - if (!sdata) return true; - shapeFree(sdata); + auto shape = static_cast(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); } } diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index dcb57c71..4dcf2109 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -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(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_ */ diff --git a/src/lib/sw_engine/tvgSwStroke.cpp b/src/lib/sw_engine/tvgSwStroke.cpp index fdc8caef..8b6f1ce4 100644 --- a/src/lib/sw_engine/tvgSwStroke.cpp +++ b/src/lib/sw_engine/tvgSwStroke.cpp @@ -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 */ diff --git a/src/lib/tvgCanvasImpl.h b/src/lib/tvgCanvasImpl.h index c6dc0736..42cf4707 100644 --- a/src/lib/tvgCanvasImpl.h +++ b/src/lib/tvgCanvasImpl.h @@ -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(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(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(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(paint); if(!SCENE_IMPL->render(*renderer)) return Result::InsufficientCondition; diff --git a/src/lib/tvgLinearGradient.cpp b/src/lib/tvgLinearGradient.cpp index 9ea803de..4499454f 100644 --- a/src/lib/tvgLinearGradient.cpp +++ b/src/lib/tvgLinearGradient.cpp @@ -35,7 +35,7 @@ struct LinearGradient::Impl LinearGradient::LinearGradient():pImpl(make_unique()) { - id = FILL_ID_LINEAR; + _id = FILL_ID_LINEAR; } diff --git a/src/lib/tvgRadialGradient.cpp b/src/lib/tvgRadialGradient.cpp index 2447e6f1..895cccb2 100644 --- a/src/lib/tvgRadialGradient.cpp +++ b/src/lib/tvgRadialGradient.cpp @@ -35,7 +35,7 @@ struct RadialGradient::Impl RadialGradient::RadialGradient():pImpl(make_unique()) { - id = FILL_ID_RADIAL; + _id = FILL_ID_RADIAL; } diff --git a/src/lib/tvgScene.cpp b/src/lib/tvgScene.cpp index ac2ae82b..1ce0ada4 100644 --- a/src/lib/tvgScene.cpp +++ b/src/lib/tvgScene.cpp @@ -25,7 +25,7 @@ Scene::Scene() : pImpl(make_unique()) { - id = PAINT_ID_SCENE; + _id = PAINT_ID_SCENE; } diff --git a/src/lib/tvgSceneImpl.h b/src/lib/tvgSceneImpl.h index 85962921..2c92d5b5 100644 --- a/src/lib/tvgSceneImpl.h +++ b/src/lib/tvgSceneImpl.h @@ -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(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(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(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(paint); if (!SCENE_IMPL->bounds(&x2, &y2, &w2, &h2)) return false; diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index 012fcb14..aabd5bd2 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f; Shape :: Shape() : pImpl(make_unique()) { - id = PAINT_ID_SHAPE; + _id = PAINT_ID_SHAPE; } diff --git a/test/testLinearGradient.cpp b/test/testLinearGradient.cpp index 4f87cad2..33536c41 100644 --- a/test/testLinearGradient.cpp +++ b/test/testLinearGradient.cpp @@ -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));