From 6b54eb5c22342caad3dbc2731187ba0c6a25014e Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 27 May 2020 11:03:07 +0900 Subject: [PATCH 01/16] updated AUTHORS Change-Id: Id756d25e475e8f8cb8379577032e5b41e772b88e --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 13ce511..bcfd4d6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ Hermet Park +Prudhvi Raj Vasireddi -- 2.7.4 From c51241f26b8f6ae726d0cf40cde9827e13116f0b Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sat, 23 May 2020 20:06:40 +0900 Subject: [PATCH 02/16] sw_engine: implment basic stroke functions. Change-Id: Ib9203b4d133ce7ffd80b40d7ad0cac3519b5273d --- .gitignore | 1 + src/lib/sw_engine/meson.build | 5 +- src/lib/sw_engine/tvgSwCommon.h | 93 ++++- src/lib/sw_engine/tvgSwRaster.cpp | 18 +- src/lib/sw_engine/tvgSwRenderer.cpp | 44 ++- src/lib/sw_engine/tvgSwRle.cpp | 20 +- src/lib/sw_engine/tvgSwShape.cpp | 111 +++--- src/lib/sw_engine/tvgSwStroke.cpp | 658 ++++++++++++++++++++++++++++++++++++ test/makefile | 23 +- test/testStroke.cpp | 64 +++- 10 files changed, 936 insertions(+), 101 deletions(-) create mode 100644 src/lib/sw_engine/tvgSwStroke.cpp diff --git a/.gitignore b/.gitignore index 6210eb8..1353d08 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ testDirectUpdate testScene testTransform testSceneTransform +testStroke diff --git a/src/lib/sw_engine/meson.build b/src/lib/sw_engine/meson.build index a33059d..b666471 100644 --- a/src/lib/sw_engine/meson.build +++ b/src/lib/sw_engine/meson.build @@ -1,10 +1,11 @@ source_file = [ 'tvgSwCommon.h', 'tvgSwRenderer.h', + 'tvgSwRaster.cpp', 'tvgSwRenderer.cpp', - 'tvgSwShape.cpp', 'tvgSwRle.cpp', - 'tvgSwRaster.cpp', + 'tvgSwShape.cpp', + 'tvgSwStroke.cpp', ] swraster_dep = declare_dependency( diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 62a6b29..66d6fe0 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -23,15 +23,27 @@ 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; +constexpr auto SW_STROKE_TAG_ON = 1; +constexpr auto SW_STROKE_TAG_BEGIN = 4; +constexpr auto SW_STROKE_TAG_END = 8; + 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}; } @@ -56,14 +68,15 @@ struct SwSize struct SwOutline { - size_t* cntrs; //the contour end points - size_t cntrsCnt; //number of contours in glyph - size_t reservedCntrsCnt; - SwPoint* pts; //the outline's points - size_t ptsCnt; //number of points in the glyph - size_t reservedPtsCnt; - uint8_t* types; //curve type - uint8_t fillMode; //outline fill mode + uint32_t* cntrs; //the contour end points + uint32_t cntrsCnt; //number of contours in glyph + uint32_t reservedCntrsCnt; + SwPoint* pts; //the outline's points + uint32_t ptsCnt; //number of points in the glyph + uint32_t reservedPtsCnt; + uint8_t* types; //curve type + uint8_t fillMode; //outline fill mode + bool opened; //opened path? }; struct SwSpan @@ -76,8 +89,8 @@ struct SwSpan struct SwRleData { SwSpan *spans; - size_t alloc; - size_t size; + uint32_t alloc; + uint32_t size; }; struct SwBBox @@ -85,19 +98,77 @@ struct SwBBox SwPoint min, max; }; +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 + bool valid; +}; + +struct SwStroke +{ + SwRleData* rle; + + SwFixed angleIn; + SwFixed angleOut; + SwPoint center; + SwFixed lineLength; + SwPoint subPathStart; + SwFixed subPathLineLength; + SwFixed width; + + StrokeCap cap; + StrokeJoin join; + StrokeJoin joinSaved; + + SwStrokeBorder borders[2]; + + bool firstPt; + bool subPathOpen; + bool subPathAngle; + bool handleWideStrokes; +}; + struct SwShape { SwOutline* outline; SwRleData* rle; + SwStroke* stroke; SwBBox bbox; }; +static inline SwPoint TO_SWPOINT(const Point* pt) +{ + return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)}; +} + + +static inline SwCoord TO_SWCOORD(float val) +{ + return SwCoord(val * 64); +} + + void shapeReset(SwShape& sdata); bool shapeGenOutline(const Shape& shape, SwShape& sdata); -void shapeDelOutline(SwShape& sdata); bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip); +void shapeDelOutline(SwShape& sdata); +void shapeResetStroke(const Shape& shape, SwShape& sdata); +bool shapeGenStrokeOutline(const Shape& shape, SwShape& sdata); +bool shapeGenStrokeRle(const Shape& shape, SwShape& sdata, const SwSize& clip); void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform); +void shapeFree(SwShape* sdata); + +void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join); +bool strokeParseOutline(SwStroke& stroke, SwOutline& outline); +void strokeFree(SwStroke* stroke); + SwRleData* rleRender(const SwShape& sdata, const SwSize& clip); +SwRleData* rleStrokeRender(const SwShape& sdata); bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a); diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 0b813fd..ba7c6a3 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -24,52 +24,52 @@ /* Internal Class Implementation */ /************************************************************************/ -static inline size_t COLOR_ALPHA(size_t color) +static inline uint32_t COLOR_ALPHA(uint32_t color) { return (color >> 24) & 0xff; } -static inline size_t COLOR_ALPHA_BLEND(size_t color, size_t alpha) +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 size_t COLOR_ARGB_JOIN(uint8_t r, uint8_t g, uint8_t b, uint8_t a) +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, size_t len, size_t color, size_t cov) +_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 (size_t i = 0; i < len; ++i) { + for (uint32_t i = 0; i < len; ++i) { dst[i] = color + COLOR_ALPHA_BLEND(dst[i], ialpha); } } static void -_rasterSolid(uint32_t* dst, size_t len, size_t color, size_t cov) +_rasterSolid(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov) { //OPTIMIZE ME: SIMD //Fully Opaque if (cov == 255) { - for (size_t i = 0; i < len; ++i) { + for (uint32_t i = 0; i < len; ++i) { dst[i] = color; } } else { auto ialpha = 255 - cov; - for (size_t i = 0; i < len; ++i) { + for (uint32_t i = 0; i < len; ++i) { dst[i] = COLOR_ALPHA_BLEND(color, cov) + COLOR_ALPHA_BLEND(dst[i], ialpha); } } @@ -89,7 +89,7 @@ bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t auto stride = surface.stride; auto color = COLOR_ARGB_JOIN(r, g, b, a); - for (size_t i = 0; i < rle->size; ++i) { + for (uint32_t i = 0; i < rle->size; ++i) { assert(span); auto dst = &surface.buffer[span->y * stride + span->x]; diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 36e367b..bf97c9b 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -37,8 +37,8 @@ bool SwRenderer::clear() assert(surface.stride > 0 && surface.w > 0 && surface.h > 0); //OPTIMIZE ME: SIMD! - for (size_t i = 0; i < surface.h; i++) { - for (size_t j = 0; j < surface.w; j++) + for (uint32_t i = 0; i < surface.h; i++) { + for (uint32_t j = 0; j < surface.w; j++) surface.buffer[surface.stride * i + j] = 0xff000000; //Solid Black } @@ -57,16 +57,20 @@ bool SwRenderer::target(uint32_t* buffer, size_t stride, size_t w, size_t h) return true; } - bool SwRenderer::render(const Shape& shape, void *data) { SwShape* sdata = static_cast(data); if (!sdata) return false; - //invisible? size_t r, g, b, a; shape.fill(&r, &g, &b, &a); + size_t sa; + shape.strokeColor(nullptr, nullptr, nullptr, &sa); + + //invisible? + if (a == 0 && sa == 0) return false; + //TODO: Threading return rasterShape(surface, *sdata, r, g, b, a); } @@ -74,10 +78,9 @@ bool SwRenderer::render(const Shape& shape, void *data) bool SwRenderer::dispose(const Shape& shape, void *data) { - SwShape* sdata = static_cast(data); + auto sdata = static_cast(data); if (!sdata) return false; - shapeReset(*sdata); - free(sdata); + shapeFree(sdata); return true; } @@ -93,21 +96,33 @@ void* SwRenderer::prepare(const Shape& shape, void* data, const RenderTransform* if (flags == RenderUpdateFlag::None) return sdata; //invisible? - size_t alpha; - shape.fill(nullptr, nullptr, nullptr, &alpha); - if (alpha == 0) return sdata; + size_t a, sa; + shape.fill(nullptr, nullptr, nullptr, &a); + shape.strokeColor(nullptr, nullptr, nullptr, &sa); + if (a == 0 && sa == 0) return sdata; //TODO: Threading + + SwSize clip = {static_cast(surface.w), static_cast(surface.h)}; + + //Shape if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) { shapeReset(*sdata); if (!shapeGenOutline(shape, *sdata)) return sdata; if (transform) shapeTransformOutline(shape, *sdata, *transform); - - SwSize clip = {static_cast(surface.w), static_cast(surface.h)}; if (!shapeGenRle(shape, *sdata, clip)) return sdata; - shapeDelOutline(*sdata); } + //Stroke + if (flags & RenderUpdateFlag::Stroke) { + shapeResetStroke(shape, *sdata); + if (shape.strokeWidth() > 0.01) { + if (!shapeGenStrokeRle(shape, *sdata, clip)) return sdata; + } + } + + shapeDelOutline(*sdata); + return sdata; } @@ -141,5 +156,4 @@ SwRenderer* SwRenderer::inst() return dynamic_cast(RenderInitializer::inst(renderInit)); } - -#endif /* _TVG_SW_RENDERER_CPP_ */ +#endif /* _TVG_SW_RENDERER_CPP_ */ \ No newline at end of file diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index ea2e451..3a4b8ed 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -137,7 +137,7 @@ static inline SwCoord HYPOT(SwPoint pt) return ((pt.x > pt.y) ? (pt.x + (3 * pt.y >> 3)) : (pt.y + (3 * pt.x >> 3))); } -static void _genSpan(SwRleData* rle, SwSpan* spans, size_t count) +static void _genSpan(SwRleData* rle, SwSpan* spans, uint32_t count) { assert(rle && spans); @@ -598,15 +598,19 @@ static bool _decomposeOutline(RleWorker& rw) auto first = 0; //index of first point in contour - for (size_t n = 0; n < outline->cntrsCnt; ++n) { + for (uint32_t n = 0; n < outline->cntrsCnt; ++n) { auto last = outline->cntrs[n]; if (last < 0) goto invalid_outline; auto limit = outline->pts + last; assert(limit); + auto start = UPSCALE(outline->pts[first]); + auto pt = outline->pts + first; + assert(pt); auto types = outline->types + first; + assert(types); /* A contour cannot start with a cubic control point! */ if (types[0] == SW_CURVE_TYPE_CUBIC) goto invalid_outline; @@ -632,7 +636,7 @@ static bool _decomposeOutline(RleWorker& rw) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0])); continue; } - _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(outline->pts[first])); + _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start); goto close; } } @@ -665,6 +669,7 @@ static bool _genRle(RleWorker& rw) } + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -792,4 +797,13 @@ error: return nullptr; } + +SwRleData* rleStrokeRender(const SwShape& sdata) +{ + auto stroke = sdata.stroke; + assert(stroke); + + return nullptr; +} + #endif /* _TVG_SW_RLE_H_ */ diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index d7229dc..f1ab06e 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -23,13 +23,7 @@ /* Internal Class Implementation */ /************************************************************************/ -static inline SwPoint TO_SWPOINT(const Point* pt) -{ - return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)}; -} - - -static void _growOutlineContour(SwOutline& outline, size_t n) +static void _growOutlineContour(SwOutline& outline, uint32_t n) { if (n == 0) { free(outline.cntrs); @@ -42,12 +36,12 @@ static void _growOutlineContour(SwOutline& outline, size_t n) //cout << "Grow Cntrs: " << outline.reservedCntrsCnt << " -> " << outline.cntrsCnt + n << endl;; outline.reservedCntrsCnt = n; - outline.cntrs = static_cast(realloc(outline.cntrs, n * sizeof(size_t))); + outline.cntrs = static_cast(realloc(outline.cntrs, n * sizeof(uint32_t))); assert(outline.cntrs); } -static void _growOutlinePoint(SwOutline& outline, size_t n) +static void _growOutlinePoint(SwOutline& outline, uint32_t n) { if (n == 0) { free(outline.pts); @@ -132,9 +126,9 @@ static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* } -static bool _outlineClose(SwOutline& outline) +static void _outlineClose(SwOutline& outline) { - size_t i = 0; + uint32_t i = 0; if (outline.cntrsCnt > 0) { i = outline.cntrs[outline.cntrsCnt - 1] + 1; @@ -143,7 +137,10 @@ static bool _outlineClose(SwOutline& outline) } //Make sure there is at least one point in the current path - if (outline.ptsCnt == i) return false; + if (outline.ptsCnt == i) { + outline.opened = true; + return; + } //Close the path _growOutlinePoint(outline, 1); @@ -152,7 +149,7 @@ static bool _outlineClose(SwOutline& outline) outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT; ++outline.ptsCnt; - return true; + outline.opened = false; } @@ -183,7 +180,7 @@ static bool _updateBBox(SwShape& sdata) ++pt; - for(size_t i = 1; i < outline->ptsCnt; ++i, ++pt) { + for(uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) { assert(pt); if (xMin > pt->x) xMin = pt->x; if (xMax < pt->x) xMax = pt->x; @@ -201,16 +198,31 @@ static bool _updateBBox(SwShape& sdata) } -void _deleteRle(SwShape& sdata) +static bool _checkValid(SwShape& sdata, const SwSize& clip) { - if (sdata.rle) { - if (sdata.rle->spans) free(sdata.rle->spans); - free(sdata.rle); - } + assert(sdata.outline); + + if (sdata.outline->ptsCnt == 0 || sdata.outline->cntrsCnt <= 0) return false; + + //Check boundary + if ((sdata.bbox.min.x > clip.w || sdata.bbox.min.y > clip.h) || + (sdata.bbox.min.x + sdata.bbox.max.x < 0) || + (sdata.bbox.min.y + sdata.bbox.max.y < 0)) return false; + + return true; +} + + +static void _deleteRle(SwShape& sdata) +{ + if (!sdata.rle) return; + if (sdata.rle->spans) free(sdata.rle->spans); + free(sdata.rle); sdata.rle = nullptr; } + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -220,7 +232,7 @@ void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTrans auto outline = sdata.outline; assert(outline); - for(size_t i = 0; i < outline->ptsCnt; ++i) { + for(uint32_t i = 0; i < outline->ptsCnt; ++i) { auto dx = static_cast(outline->pts[i].x >> 6); auto dy = static_cast(outline->pts[i].y >> 6); auto tx = dx * transform.e11 + dy * transform.e12 + transform.e13; @@ -233,13 +245,8 @@ void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTrans bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip) { - if (sdata.outline->ptsCnt == 0 || sdata.outline->cntrsCnt <= 0) goto end; if (!_updateBBox(sdata)) goto end; - - //Check boundary - if ((sdata.bbox.min.x > clip.w || sdata.bbox.min.y > clip.h) || - (sdata.bbox.min.x + sdata.bbox.max.x < 0) || - (sdata.bbox.min.y + sdata.bbox.max.y < 0)) goto end; + if (!_checkValid(sdata, clip)) goto end; sdata.rle = rleRender(sdata, clip); @@ -251,14 +258,13 @@ end: void shapeDelOutline(SwShape& sdata) { - if (!sdata.outline) return; + auto outline = sdata.outline; + if (!outline) return; - SwOutline* outline = sdata.outline; if (outline->cntrs) free(outline->cntrs); if (outline->pts) free(outline->pts); if (outline->types) free(outline->types); free(outline); - sdata.outline = nullptr; } @@ -286,7 +292,7 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata) auto outlinePtsCnt = 0; auto outlineCntrsCnt = 0; - for (size_t i = 0; i < cmdCnt; ++i) { + for (uint32_t i = 0; i < cmdCnt; ++i) { switch(*(cmds + i)) { case PathCommand::Close: { ++outlinePtsCnt; @@ -311,14 +317,9 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata) ++outlinePtsCnt; //for close ++outlineCntrsCnt; //for end - SwOutline* outline = sdata.outline; - - if (!outline) { - outline = static_cast(calloc(1, sizeof(SwOutline))); - assert(outline); - } else { - cout << "Outline was already allocated? How?" << endl; - } + auto outline = sdata.outline; + if (!outline) outline = static_cast(calloc(1, sizeof(SwOutline))); + assert(outline); _growOutlinePoint(*outline, outlinePtsCnt); _growOutlineContour(*outline, outlineCntrsCnt); @@ -360,4 +361,38 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata) } +void shapeFree(SwShape* sdata) +{ + assert(sdata); + + shapeDelOutline(*sdata); + _deleteRle(*sdata); + strokeFree(sdata->stroke); + free(sdata); +} + + +void shapeResetStroke(const Shape& shape, SwShape& sdata) +{ + if (!sdata.stroke) sdata.stroke = static_cast(calloc(1, sizeof(SwStroke))); + auto stroke = sdata.stroke; + assert(stroke); + + strokeReset(*stroke, shape.strokeWidth(), shape.strokeCap(), shape.strokeJoin()); +} + + +bool shapeGenStrokeRle(const Shape& shape, SwShape& sdata, const SwSize& clip) +{ + if (!sdata.outline) { + if (!shapeGenOutline(shape, sdata)) return false; + } + + if (!_checkValid(sdata, clip)) return false; + + if (!strokeParseOutline(*sdata.stroke, *sdata.outline)) return false; + + return true; +} + #endif /* _TVG_SW_SHAPE_H_ */ diff --git a/src/lib/sw_engine/tvgSwStroke.cpp b/src/lib/sw_engine/tvgSwStroke.cpp new file mode 100644 index 0000000..2f810a1 --- /dev/null +++ b/src/lib/sw_engine/tvgSwStroke.cpp @@ -0,0 +1,658 @@ +/* + * 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_STROKER_H_ +#define _TVG_SW_STROKER_H_ + +#include "tvgSwCommon.h" + + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ +constexpr auto CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32 +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); + +//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(long) * 8 - 1)); +} + + +static inline SwFixed SIDE_TO_ROTATE(int32_t s) +{ + return (ANGLE_PI2 - (s) * ANGLE_PI); +} + + +static int64_t _multiply(int64_t a, int64_t b) +{ + int32_t s = 1; + + //move sign + if (a < 0) { + a = -a; + s = -s; + } + //move sign + if (b < 0) { + b = -b; + s = -s; + } + int64_t c = (a * b + 0x8000L ) >> 16; + return (s > 0) ? c : -c; +} + + +static int64_t _divide(int64_t a, int64_t b) +{ + int32_t s = 1; + + //move sign + if (a < 0) { + a = -a; + s = -s; + } + //move sign + if (b < 0) { + b = -b; + s = -s; + } + int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL; + return (s < 0 ? -q : q); +} + + +static SwFixed _angleDiff(SwFixed angle1, SwFixed angle2) +{ + auto delta = angle2 - angle1; + + delta %= ANGLE_2PI; + if (delta < 0) delta += ANGLE_2PI; + if (delta > ANGLE_PI) delta -= ANGLE_2PI; + + return delta; +} + + +static void _trigDownscale(SwPoint& pt) +{ + //multiply a give value by the CORDIC shrink factor + + auto s = pt; + + //abs + if (pt.x < 0) pt.x = -pt.x; + if (pt.y < 0) pt.y = -pt.y; + + int64_t vx = (pt.x * static_cast(CORDIC_FACTOR)) + 0x100000000UL; + int64_t vy = (pt.y * static_cast(CORDIC_FACTOR)) + 0x100000000UL; + + pt.x = static_cast(vx >> 32); + pt.y = static_cast(vy >> 32); + + if (s.x < 0) pt.x = -pt.x; + if (s.y < 0) pt.y = -pt.y; +} + + +static int32_t _trigPrenorm(SwPoint& pt) +{ + /* the highest bit in overflow-safe vector components + MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */ + constexpr auto TRIG_SAFE_MSB = 29; + + auto v = pt; + + //High order bit(MSB) + //clz: count leading zero’s + auto shift = 31 - __builtin_clz(abs(v.x) | abs(v.y)); + + if (shift <= TRIG_SAFE_MSB) { + shift = TRIG_SAFE_MSB - shift; + pt.x = static_cast((unsigned long)v.x << shift); + pt.y = static_cast((unsigned long)v.y << shift); + } else { + shift -= TRIG_SAFE_MSB; + pt.x = v.x >> shift; + pt.y = v.y >> shift; + shift = -shift; + } + return shift; +} + + +static void _trigPseudoRotate(SwPoint& pt, SwFixed theta) +{ + auto v = pt; + + //Rotate inside [-PI/4, PI/4] sector + while (theta < -ANGLE_PI4) { + auto temp = v.y; + v.y = -v.x; + v.x = temp; + theta += ANGLE_PI2; + } + + while (theta > ANGLE_PI4) { + auto temp = -v.y; + v.y = v.x; + v.x = temp; + theta -= 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 temp = v.x + ((v.y + j) >> i); + v.y = v.y - ((v.x + j) >> i); + v.x = temp; + theta += *atan++; + }else { + auto temp = v.x - ((v.y + j) >> i); + v.y = v.y + ((v.x + j) >> i); + v.x = temp; + theta -= *atan++; + } + } + + pt = v; +} + + +static void _rotate(SwPoint& pt, SwFixed angle) +{ + if (angle == 0 || (pt.x == 0 && pt.y == 0)) return; + + auto v = pt; + auto shift = _trigPrenorm(v); + _trigPseudoRotate(v, angle); + _trigDownscale(v); + + if (shift > 0) { + auto half = static_cast(1L << (shift - 1)); + v.x = (v.x + half + SATURATE(v.x)) >> shift; + v.y = (v.y + half + SATURATE(v.y)) >> shift; + } else { + shift = -shift; + v.x = static_cast((unsigned long)v.x << shift); + v.y = static_cast((unsigned long)v.y << shift); + } +} + + +static SwFixed _tan(SwFixed angle) +{ + SwPoint v = {CORDIC_FACTOR >> 8, 0}; + _rotate(v, angle); + return _divide(v.y, v.x); +} + + +static SwFixed _cos(SwFixed angle) +{ + SwPoint v = {CORDIC_FACTOR >> 8, 0}; + _rotate(v, angle); + return (v.x + 0x80L) >> 8; +} + + +static void _lineTo(SwStroke& stroke, const SwPoint& to) +{ + +} + + +static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) +{ + +} + + +static void _arcTo(SwStroke& stroke, int32_t side) +{ + +} + + +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; + + border->pts = static_cast(realloc(border->pts, maxCur * sizeof(SwPoint))); + assert(border->pts); + + border->tags = static_cast(realloc(border->tags, maxCur * sizeof(uint8_t))); + assert(border->tags); + + border->maxPts = maxCur; + + printf("realloc border!!! (%u => %u)\n", maxOld, maxCur); +} + + + +static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable) +{ + constexpr SwPoint EPSILON = 2; + + assert(border && border->start >= 0); + + if (border->movable) { + //move last point + border->pts[border->ptsCnt - 1] = to; + } else { + //don't add zero-length line_to + auto diff = border->pts[border->ptsCnt - 1] - to; + if (border->ptsCnt > 0 && abs(diff.x) < EPSILON && abs(diff.y) < EPSILON) return; + + _growBorder(border, 1); + border->pts[border->ptsCnt] = to; + border->tags[border->ptsCnt] = SW_STROKE_TAG_ON; + border->ptsCnt += 1; + } + + border->movable = movable; +} + + +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 = {stroke.width, 0}; + _rotate(delta, angle); + + SwPoint delta2 = {stroke.width, 0}; + _rotate(delta2, angle + rotate); + + delta += stroke.center + delta2; + + _borderLineTo(border, delta, false); + + delta = {stroke.width, 0}; + _rotate(delta, angle); + + delta2 = {stroke.width, 0}; + _rotate(delta2, angle - rotate); + + delta += delta2 + stroke.center; + + _borderLineTo(border, delta, false); + + } else if (stroke.cap == StrokeCap::Round) { + + stroke.angleIn = angle; + stroke.angleOut = angle + ANGLE_PI; + _arcTo(stroke, side); + return; + + } else { //Butt + auto rotate = SIDE_TO_ROTATE(side); + auto border = stroke.borders + side; + + SwPoint delta = {stroke.width, 0}; + _rotate(delta, angle + rotate); + + delta += stroke.center; + + _borderLineTo(border, delta, false); + + delta = {stroke.width, 0}; + _rotate(delta, angle - rotate); + + delta += stroke.center; + + _borderLineTo(border, delta, false); + } +} + + +static void _addReverseLeft(SwStroke& stroke, bool opened) +{ + auto right = stroke.borders + 0; + auto left = stroke.borders + 1; + assert(left->start >= 0); + + 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 || 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 _closeBorder(SwStrokeBorder* border, bool reverse) +{ + assert(border && border->start >= 0); + + uint32_t start = border->start; + uint32_t 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; + + for (; pt1 < pt2; pt1++, pt2--) { + auto tmp = *pt1; + *pt1 = *pt2; + *pt2 = tmp; + } + + //reverse the tags + auto tag1 = border->tags + start + 1; + auto tag2 = border->tags + count - 1; + + for (; tag1 < tag2; tag1++, tag2++) { + auto tmp = *tag1; + *tag1 = *tag2; + *tag2 = tmp; + } + } + + border->tags[start] |= SW_STROKE_TAG_BEGIN; + border->tags[count - 1] |= SW_STROKE_TAG_END; + } + + border->start = -1; + border->movable = false; +} + + +static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength) +{ + auto border = stroke.borders + side; + auto theta = _angleDiff(stroke.angleIn, stroke.angleOut) / 2; + SwPoint delta; + bool intersect; + + /* 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) { + intersect = false; + } else { + //compute minimum required length of lines + SwFixed minLength = abs(_multiply(stroke.width, _tan(theta))); + if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true; + } + + auto rotate = SIDE_TO_ROTATE(side); + + if (!intersect) { + delta = {stroke.width, 0}; + _rotate(delta, stroke.angleOut + rotate); + delta += stroke.center; + border->movable = false; + } else { + //compute median angle + delta = {_divide(stroke.width, _cos(theta)), 0}; + _rotate(delta, stroke.angleIn + theta + rotate); + delta += stroke.center; + } + + _borderLineTo(border, delta, false); +} + + +static void _beginSubPath(SwStroke& stroke, SwPoint& to, bool opened) +{ + cout << "stroke opened? = " << opened << endl; + + /* We cannot process the first point because there is not enought + information regarding its corner/cap. Later, it will be processed + in the _strokeEndSubPath() */ + + stroke.firstPt = true; + stroke.center = to; + stroke.subPathOpen = opened; + + /* 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.subPathOpen && stroke.cap == StrokeCap::Butt)) + stroke.handleWideStrokes = true; + else + stroke.handleWideStrokes = false; + + stroke.subPathStart = to; + stroke.angleIn = 0; +} + + +static void _endSubPath(SwStroke& stroke) +{ + if (stroke.subPathOpen) { + auto right = stroke.borders; + assert(right); + + /* 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.subPathStart; + _addCap(stroke, stroke.subPathAngle + ANGLE_PI, 0); + + /* now end the right subpath accordingly. The left one is rewind + and deosn't need further processing */ + _closeBorder(right, false); + } else { + + //close the path if needed + if (stroke.center != stroke.subPathStart) + _lineTo(stroke, stroke.subPathStart); + + //process the corner + stroke.angleOut = stroke.subPathAngle; + auto turn = _angleDiff(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 + auto inside = 0; + + //otherwise, the inside is 1 + if (turn < 0) inside = 1; + + _inside(stroke, inside, stroke.subPathLineLength); //inside + _inside(stroke, 1 - inside, stroke.subPathLineLength); //outside + } + + _closeBorder(stroke.borders + 0, false); + _closeBorder(stroke.borders + 1, true); + } +} + + +static void _deleteRle(SwStroke& stroke) +{ + if (!stroke.rle) return; + if (stroke.rle->spans) free(stroke.rle->spans); + free(stroke.rle); + stroke.rle = nullptr; +} + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +void strokeFree(SwStroke* stroke) +{ + if (!stroke) return; + _deleteRle(*stroke); + free(stroke); +} + + +void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join) +{ + _deleteRle(stroke); + +#if 0 + miterLimit = 4 * (1 >> 16); + + /* ensure miter limit has sensible value */ + if ( stroker->miter_limit < 0x10000 ) + stroker->miter_limit = 0x10000; +#endif + + stroke.width = TO_SWCOORD(width * 0.5f); + stroke.cap = cap; + + //Save line join: it can be temporarily changed when stroking curves... + stroke.joinSaved = stroke.join = join; + + stroke.borders[0].ptsCnt = 0; + stroke.borders[0].start = -1; + stroke.borders[0].valid = false; + + stroke.borders[1].ptsCnt = 0; + stroke.borders[1].start = -1; + stroke.borders[1].valid = false; +} + +bool strokeParseOutline(SwStroke& stroke, 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; + assert(limit); + + //Skip empty points + if (last <= first) { + first = last + 1; + continue; + } + + auto start = outline.pts[first]; + + auto pt = outline.pts + first; + assert(pt); + auto types = outline.types + first; + assert(types); + + auto type = types[0]; + + //A contour cannot start with a cubic control point + if (type == SW_CURVE_TYPE_CUBIC) return false; + + _beginSubPath(stroke, start, outline.opened); + + while (pt < limit) { + assert(++pt); + assert(++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; +} + + +#endif /* _TVG_SW_STROKER_H_ */ diff --git a/test/makefile b/test/makefile index 50710e0..174b93f 100644 --- a/test/makefile +++ b/test/makefile @@ -1,12 +1,13 @@ all: - gcc -o testShape testShape.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testPathCopy testPathCopy.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testBlending testBlending.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testUpdate testUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testDirectUpdate testDirectUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testScene testScene.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testTransform testTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` - gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testShape testShape.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testPathCopy testPathCopy.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testBlending testBlending.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testUpdate testUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testDirectUpdate testDirectUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testScene testScene.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testTransform testTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` +# gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testStroke testStroke.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testStroke.cpp b/test/testStroke.cpp index e2ae1bc..1f865d6 100644 --- a/test/testStroke.cpp +++ b/test/testStroke.cpp @@ -1,4 +1,5 @@ #include +#include using namespace std; @@ -7,33 +8,72 @@ using namespace std; static uint32_t buffer[WIDTH * HEIGHT]; -int main(int argc, char **argv) + +void tvgtest() { //Initialize TizenVG Engine tvg::Engine::init(); + //Initialize TizenVG Engine + tvg::Engine::init(); + //Create a Canvas - auto canvas = tvg::SwCanvas::gen(buffer, WIDTH, HEIGHT); + auto canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); - //Prepare a Shape + //Prepare a Shape (Rectangle + Rectangle + Circle + Circle) auto shape1 = tvg::Shape::gen(); - shape1->rect(0, 0, 400, 400, 0.1); //x, y, w, h, cornerRadius - shape1->fill(0, 255, 0, 255); + shape1->appendRect(0, 0, 200, 200, 0); //x, y, w, h, cornerRadius + shape1->appendRect(100, 100, 300, 300, 100); //x, y, w, h, cornerRadius + shape1->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH + shape1->appendCircle(400, 500, 170, 100); //cx, cy, radiusW, radiusH + shape1->fill(255, 255, 0, 255); //r, g, b, a //Stroke Style - shape1->strokeColor(0, 0, 0, 255); //r, g, b, a - shape1->strokeWidth(1); //1px - shape1->strokeJoin(tvg::StrokeJoin::Miter); - shape1->strokeLineCap(tvg::StrokeLineCap::Butt); + shape1->stroke(255, 255, 255, 255); //color: r, g, b, a + shape1->stroke(5); //width: 5px +// shape1->strokeJoin(tvg::StrokeJoin::Miter); +// shape1->strokeLineCap(tvg::StrokeLineCap::Butt); - uint32_t dash[] = {3, 1, 5, 1}; //dash pattern - shape1->strokeDash(dash, 4); +// uint32_t dash[] = {3, 1, 5, 1}; //dash pattern +// shape1->strokeDash(dash, 4); - //Draw the Shape onto the Canvas canvas->push(move(shape1)); + canvas->draw(); canvas->sync(); //Terminate TizenVG Engine tvg::Engine::term(); } + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + + +int main(int argc, char **argv) +{ + tvgtest(); + + //Show the result using EFL... + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); + + Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(img, WIDTH, HEIGHT); + evas_object_image_data_set(img, buffer); + evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(img); + + elm_win_resize_object_add(win, img); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + elm_run(); + elm_shutdown(); +} -- 2.7.4 From b4cf6660b749dccb686ac4c7f82fa10f71d041a0 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 28 May 2020 20:37:25 +0900 Subject: [PATCH 03/16] test: recover sample build. introduced by mistake. Change-Id: I8fd88054da1e86cace02931791a646a182ab6721 --- test/makefile | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/makefile b/test/makefile index 174b93f..3f314e8 100644 --- a/test/makefile +++ b/test/makefile @@ -1,13 +1,13 @@ all: -# gcc -o testShape testShape.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testPathCopy testPathCopy.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testBlending testBlending.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testUpdate testUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testDirectUpdate testDirectUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testScene testScene.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testTransform testTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -# gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testShape testShape.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testMultiShapes testMultiShapes.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testBoundary testBoundary.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testPath testPath.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testPathCopy testPathCopy.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testBlending testBlending.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testUpdate testUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testDirectUpdate testDirectUpdate.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testScene testScene.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testTransform testTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testStroke testStroke.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` -- 2.7.4 From 674483845305bc2fdd862cdd7e188220f0e4d1d7 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 29 May 2020 13:33:46 +0900 Subject: [PATCH 04/16] sw_engine: Fix build error (no match type) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit ../src/lib/sw_engine/tvgSwStroke.cpp:282:72: error: no match for ‘operator<’ (operand types are ‘long int’ and ‘const SwPoint’) if (border->ptsCnt > 0 && abs(diff.x) < EPSILON && abs(diff.y) < EPSILON) return; Change-Id: I426f8980ba718e3dc908dc32a62fb897b5b5fbbf --- src/lib/sw_engine/tvgSwStroke.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/sw_engine/tvgSwStroke.cpp b/src/lib/sw_engine/tvgSwStroke.cpp index 2f810a1..acaf2c6 100644 --- a/src/lib/sw_engine/tvgSwStroke.cpp +++ b/src/lib/sw_engine/tvgSwStroke.cpp @@ -269,7 +269,7 @@ static void _growBorder(SwStrokeBorder* border, uint32_t newPts) static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable) { - constexpr SwPoint EPSILON = 2; + constexpr SwCoord EPSILON = 2; assert(border && border->start >= 0); -- 2.7.4 From 41dbd9774a73d3c04de1f3c86a4444763bb54b1b Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 29 May 2020 11:47:55 +0900 Subject: [PATCH 05/16] sw_engine: implement stroke cubicto, arcto, lineto Change-Id: I59e95b1031ebfaf54e966cab334e045613ca3830 --- src/lib/sw_engine/meson.build | 1 + src/lib/sw_engine/tvgSwCommon.h | 39 ++- src/lib/sw_engine/tvgSwMath.cpp | 407 ++++++++++++++++++++++ src/lib/sw_engine/tvgSwRle.cpp | 30 +- src/lib/sw_engine/tvgSwStroke.cpp | 711 +++++++++++++++++++++++--------------- 5 files changed, 885 insertions(+), 303 deletions(-) create mode 100644 src/lib/sw_engine/tvgSwMath.cpp diff --git a/src/lib/sw_engine/meson.build b/src/lib/sw_engine/meson.build index b666471..f4998f4 100644 --- a/src/lib/sw_engine/meson.build +++ b/src/lib/sw_engine/meson.build @@ -1,5 +1,6 @@ source_file = [ 'tvgSwCommon.h', + 'tvgSwMath.cpp', 'tvgSwRenderer.h', 'tvgSwRaster.cpp', 'tvgSwRenderer.cpp', diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 66d6fe0..b942ca1 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -27,10 +27,6 @@ constexpr auto SW_CURVE_TYPE_CUBIC = 1; constexpr auto SW_OUTLINE_FILL_WINDING = 0; constexpr auto SW_OUTLINE_FILL_EVEN_ODD = 1; -constexpr auto SW_STROKE_TAG_ON = 1; -constexpr auto SW_STROKE_TAG_BEGIN = 4; -constexpr auto SW_STROKE_TAG_END = 8; - using SwCoord = signed long; using SwFixed = signed long long; @@ -59,6 +55,20 @@ struct SwPoint bool operator!=(const SwPoint& rhs) const { return (x != rhs.x || y != rhs.y); } + + bool zero() + { + if (x == 0 && y == 0) return true; + else return false; + } + + bool small() + { + //2 is epsilon... + if (abs(x) < 2 && abs(y) < 2) return true; + else return false; + } + }; struct SwSize @@ -141,6 +151,13 @@ struct SwShape 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)}; @@ -153,6 +170,20 @@ static inline SwCoord TO_SWCOORD(float val) } +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(SwPoint& pt); +bool mathSmallCubic(SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); +SwFixed mathMean(SwFixed angle1, SwFixed angle2); + void shapeReset(SwShape& sdata); bool shapeGenOutline(const Shape& shape, SwShape& sdata); bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip); diff --git a/src/lib/sw_engine/tvgSwMath.cpp b/src/lib/sw_engine/tvgSwMath.cpp new file mode 100644 index 0000000..919fe1e --- /dev/null +++ b/src/lib/sw_engine/tvgSwMath.cpp @@ -0,0 +1,407 @@ +/* + * 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_MATH_H_ +#define _TVG_SW_MATH_H_ + +#include "tvgSwCommon.h" + + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +constexpr auto 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(SwCoord x) +{ + //multiply a give value by the CORDIC shrink factor + + abs(x); + int64_t t = (x * static_cast(CORDIC_FACTOR)) + 0x100000000UL; + x = static_cast(t >> 32); + if (x < 0) x = -x; + return x; +} + + +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) + //clz: count leading zero’s + auto shift = 31 - __builtin_clz(abs(v.x) | abs(v.y)); + + if (shift <= SAFE_MSB) { + shift = SAFE_MSB - shift; + pt.x = static_cast((unsigned long)v.x << shift); + pt.y = static_cast((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 = ANGLE_PI2; + } else { + theta = v.y > 0 ? ANGLE_PI : -ANGLE_PI; + v.x = -v.x; + v.y = -v.y; + } + } else { + if (v.y < -v.x) { + theta = -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; +} + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +SwFixed mathMean(SwFixed angle1, SwFixed angle2) +{ + return angle1 + mathDiff(angle1, angle2) / 2; +} + + +bool mathSmallCubic(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 < (ANGLE_PI / 8)) && (theta2 < (ANGLE_PI / 8))) return true; + else 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 inside [-PI/4, PI/4] sector + while (theta < -ANGLE_PI4) { + auto tmp = v.y; + v.y = -v.x; + v.x = tmp; + theta += ANGLE_PI2; + } + + while (theta > ANGLE_PI4) { + auto tmp = -v.y; + v.y = v.x; + v.x = tmp; + theta -= 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 = 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++; + } + } + + v.x = _downscale(v.x); + v.y = _downscale(v.y); + + if (shift > 0) { + auto half = static_cast(1L << (shift - 1)); + v.x = (v.x + half + SATURATE(v.x)) >> shift; + v.y = (v.y + half + SATURATE(v.y)) >> shift; + } else { + shift = -shift; + v.x = static_cast((unsigned long)v.x << shift); + v.y = static_cast((unsigned long)v.y << shift); + } +} + +SwFixed mathTan(SwFixed angle) +{ + SwPoint v = {CORDIC_FACTOR >> 8, 0}; + mathRotate(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(ANGLE_PI2 - angle); +} + + +SwFixed mathCos(SwFixed angle) +{ + SwPoint v = {CORDIC_FACTOR >> 8, 0}; + mathRotate(v, angle); + return (v.x + 0x80L) >> 8; +} + + +SwFixed mathLength(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 + (1 << (shift -1))) >> shift; + return static_cast((uint32_t)v.x << -shift); +} + + +void mathSplitCubic(SwPoint* base) +{ + assert(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 %= ANGLE_2PI; + if (delta < 0) delta += ANGLE_2PI; + if (delta > ANGLE_PI) delta -= ANGLE_2PI; + + return delta; +} +#endif /* _TVG_SW_MATH_H_ */ \ No newline at end of file diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index 3a4b8ed..7bbc326 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -490,34 +490,6 @@ static void _lineTo(RleWorker& rw, const SwPoint& to) } -static void _splitCubic(SwPoint* base) -{ - assert(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; -} - - static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) { auto arc = rw.bezStack; @@ -579,7 +551,7 @@ static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, goto draw; } split: - _splitCubic(arc); + mathSplitCubic(arc); arc += 3; continue; diff --git a/src/lib/sw_engine/tvgSwStroke.cpp b/src/lib/sw_engine/tvgSwStroke.cpp index acaf2c6..a0a2573 100644 --- a/src/lib/sw_engine/tvgSwStroke.cpp +++ b/src/lib/sw_engine/tvgSwStroke.cpp @@ -23,271 +23,532 @@ /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ -constexpr auto CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32 -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 constexpr auto SW_STROKE_TAG_ON = 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; -//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 SwFixed SIDE_TO_ROTATE(const int32_t s) +{ + return (ANGLE_PI2 - (s) * ANGLE_PI); +} -static inline SwCoord SATURATE(const SwCoord x) +static void _growBorder(SwStrokeBorder* border, uint32_t newPts) { - return (x >> (sizeof(long) * 8 - 1)); + auto maxOld = border->maxPts; + auto maxNew = border->ptsCnt + newPts; + + if (maxNew <= maxOld) return; + + auto maxCur = maxOld; + + while (maxCur < maxNew) + maxCur += (maxCur >> 1) + 16; + + border->pts = static_cast(realloc(border->pts, maxCur * sizeof(SwPoint))); + assert(border->pts); + + border->tags = static_cast(realloc(border->tags, maxCur * sizeof(uint8_t))); + assert(border->tags); + + border->maxPts = maxCur; + + printf("realloc border!!! (%u => %u)\n", maxOld, maxCur); } -static inline SwFixed SIDE_TO_ROTATE(int32_t s) +static void _borderClose(SwStrokeBorder* border, bool reverse) { - return (ANGLE_PI2 - (s) * ANGLE_PI); + assert(border && border->start >= 0); + + uint32_t start = border->start; + uint32_t 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 int64_t _multiply(int64_t a, int64_t b) +static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& to) { - int32_t s = 1; + assert(border->start >= 0); - //move sign - if (a < 0) { - a = -a; - s = -s; - } - //move sign - if (b < 0) { - b = -b; - s = -s; - } - int64_t c = (a * b + 0x8000L ) >> 16; - return (s > 0) ? c : -c; + _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_ON; + + border->ptsCnt += 3; + border->movable = false; } -static int64_t _divide(int64_t a, int64_t b) +static void _borderArcTo(SwStrokeBorder* border, SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff) { - int32_t s = 1; + constexpr auto ARC_CUBIC_ANGLE = ANGLE_PI / 2; + SwPoint a = {radius, 0}; + mathRotate(a, angleStart); + a += center; - //move sign - if (a < 0) { - a = -a; - s = -s; - } - //move sign - if (b < 0) { - b = -b; - s = -s; + auto total = angleDiff; + auto angle = angleStart; + auto rotate = (angleDiff >= 0) ? ANGLE_PI2 : -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 = {radius, 0}; + mathRotate(b, next); + b += center; + + //compute first and second control points + auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3); + + SwPoint a2 = {length, 0}; + mathRotate(a2, angle + rotate); + a2 += a; + + SwPoint b2 = {length, 0}; + mathRotate(b2, next - rotate); + b2 += b; + + //add cubic arc + _borderCubicTo(border, a2, b2, b); + + //process the rest of the arc? + a = b; + total -= step; + angle = next; } - int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL; - return (s < 0 ? -q : q); } -static SwFixed _angleDiff(SwFixed angle1, SwFixed angle2) +static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable) { - auto delta = angle2 - angle1; + assert(border && border->start >= 0); - delta %= ANGLE_2PI; - if (delta < 0) delta += ANGLE_2PI; - if (delta > ANGLE_PI) delta -= ANGLE_2PI; + if (border->movable) { + //move last point + border->pts[border->ptsCnt - 1] = to; + } else { + //don't add zero-length line_to + auto diff = border->pts[border->ptsCnt - 1] - to; + if (border->ptsCnt > 0 && diff.small()) return; - return delta; + _growBorder(border, 1); + border->pts[border->ptsCnt] = to; + border->tags[border->ptsCnt] = SW_STROKE_TAG_ON; + border->ptsCnt += 1; + } + + border->movable = movable; } -static void _trigDownscale(SwPoint& pt) +static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to) { - //multiply a give value by the CORDIC shrink factor + assert(border); - auto s = pt; + //close current open path if any? + if (border->start >= 0) + _borderClose(border, false); - //abs - if (pt.x < 0) pt.x = -pt.x; - if (pt.y < 0) pt.y = -pt.y; + border->start = border->ptsCnt; + border->movable = false; + + _borderLineTo(border, to, false); +} - int64_t vx = (pt.x * static_cast(CORDIC_FACTOR)) + 0x100000000UL; - int64_t vy = (pt.y * static_cast(CORDIC_FACTOR)) + 0x100000000UL; - pt.x = static_cast(vx >> 32); - pt.y = static_cast(vy >> 32); +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 (s.x < 0) pt.x = -pt.x; - if (s.y < 0) pt.y = -pt.y; + _borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total); + border->movable = false; } -static int32_t _trigPrenorm(SwPoint& pt) +static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength) { - /* the highest bit in overflow-safe vector components - MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */ - constexpr auto TRIG_SAFE_MSB = 29; + constexpr SwFixed MITER_LIMIT = 4 * (1 << 16); - auto v = pt; + assert(MITER_LIMIT >= 0x10000); - //High order bit(MSB) - //clz: count leading zero’s - auto shift = 31 - __builtin_clz(abs(v.x) | abs(v.y)); + auto border = stroke.borders + side; + assert(border); - if (shift <= TRIG_SAFE_MSB) { - shift = TRIG_SAFE_MSB - shift; - pt.x = static_cast((unsigned long)v.x << shift); - pt.y = static_cast((unsigned long)v.y << shift); + if (stroke.join == StrokeJoin::Round) { + _arcTo(stroke, side); } else { - shift -= TRIG_SAFE_MSB; - pt.x = v.x >> shift; - pt.y = v.y >> shift; - shift = -shift; + //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 == 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 = {stroke.width, 0}; + mathRotate(delta, stroke.angleOut + rotate); + delta += stroke.center; + border->movable = false; + _borderLineTo(border, delta, false); + //this is a miter (intersection) + } else { + auto length = mathDivide(stroke.width, thcos); + SwPoint delta = {length, 0}; + mathRotate(delta, phi); + 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 = {stroke.width, 0}; + mathRotate(delta, stroke.angleOut + rotate); + delta += stroke.center; + _borderLineTo(border, delta, false); + } + } } - return shift; } -static void _trigPseudoRotate(SwPoint& pt, SwFixed theta) +static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength) { - auto v = pt; - - //Rotate inside [-PI/4, PI/4] sector - while (theta < -ANGLE_PI4) { - auto temp = v.y; - v.y = -v.x; - v.x = temp; - theta += ANGLE_PI2; - } + auto border = stroke.borders + side; + auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2; + SwPoint delta; + bool intersect; - while (theta > ANGLE_PI4) { - auto temp = -v.y; - v.y = v.x; - v.x = temp; - theta -= ANGLE_PI2; + /* 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) { + intersect = false; + } else { + //compute minimum required length of lines + SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta))); + if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true; } - auto atan = ATAN_TBL; - uint32_t i; - SwFixed j; - - for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) { - if (theta < 0) { - auto temp = v.x + ((v.y + j) >> i); - v.y = v.y - ((v.x + j) >> i); - v.x = temp; - theta += *atan++; - }else { - auto temp = v.x - ((v.y + j) >> i); - v.y = v.y + ((v.x + j) >> i); - v.x = temp; - theta -= *atan++; - } + auto rotate = SIDE_TO_ROTATE(side); + + if (!intersect) { + delta = {stroke.width, 0}; + mathRotate(delta, stroke.angleOut + rotate); + delta += stroke.center; + border->movable = false; + } else { + //compute median angle + delta = {mathDivide(stroke.width, mathCos(theta)), 0}; + mathRotate(delta, stroke.angleIn + theta + rotate); + delta += stroke.center; } - pt = v; + _borderLineTo(border, delta, false); } -static void _rotate(SwPoint& pt, SwFixed angle) +void _processCorner(SwStroke& stroke, SwFixed lineLength) { - if (angle == 0 || (pt.x == 0 && pt.y == 0)) return; + auto turn = mathDiff(stroke.angleIn, stroke.angleOut); - auto v = pt; - auto shift = _trigPrenorm(v); - _trigPseudoRotate(v, angle); - _trigDownscale(v); + //no specific corner processing is required if the turn is 0 + if (turn == 0) return; - if (shift > 0) { - auto half = static_cast(1L << (shift - 1)); - v.x = (v.x + half + SATURATE(v.x)) >> shift; - v.y = (v.y + half + SATURATE(v.y)) >> shift; - } else { - shift = -shift; - v.x = static_cast((unsigned long)v.x << shift); - v.y = static_cast((unsigned long)v.y << shift); - } -} + //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; -static SwFixed _tan(SwFixed angle) -{ - SwPoint v = {CORDIC_FACTOR >> 8, 0}; - _rotate(v, angle); - return _divide(v.y, v.x); + //process the inside + _inside(stroke, inside, lineLength); + + //process the outside + _outside(stroke, 1 - inside, lineLength); } -static SwFixed _cos(SwFixed angle) +void _subPathStart(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength) { - SwPoint v = {CORDIC_FACTOR >> 8, 0}; - _rotate(v, angle); - return (v.x + 0x80L) >> 8; + SwPoint delta = {stroke.width, 0}; + mathRotate(delta, startAngle + ANGLE_PI2); + + 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 = {stroke.width, 0}; + mathRotate(delta, angle + ANGLE_PI2); + + //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. */ + _subPathStart(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 firstArc = true; + auto limit = bezStack + 32; + auto arc = bezStack; + 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) { + _subPathStart(stroke, angleIn, 0); + } else { + stroke.angleOut = angleIn; + _processCorner(stroke, 0); + } + } else if (abs(mathDiff(stroke.angleIn, angleIn)) > (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; + stroke.join = StrokeJoin::Round; -static void _arcTo(SwStroke& stroke, int32_t side) -{ + _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]); + } -static void _growBorder(SwStrokeBorder* border, uint32_t newPts) -{ - auto maxOld = border->maxPts; - auto maxNew = border->ptsCnt + newPts; + auto border = stroke.borders; + int32_t side = 0; - if (maxNew <= maxOld) return; + while (side <= 1) + { + auto rotate = SIDE_TO_ROTATE(side); - auto maxCur = maxOld; + //compute control points + SwPoint _ctrl1 = {length1, 0}; + mathRotate(_ctrl1, phi1 + rotate); + _ctrl1 += arc[2]; - while (maxCur < maxNew) - maxCur += (maxCur >> 1) + 16; + SwPoint _ctrl2 = {length2, 0}; + mathRotate(_ctrl2, phi2 + rotate); + _ctrl2 += arc[1]; - border->pts = static_cast(realloc(border->pts, maxCur * sizeof(SwPoint))); - assert(border->pts); + //compute end point + SwPoint _end = {stroke.width, 0}; + mathRotate(_end, angleOut + rotate); + _end += arc[0]; - border->tags = static_cast(realloc(border->tags, maxCur * sizeof(uint8_t))); - assert(border->tags); + if (stroke.handleWideStrokes) { - border->maxPts = maxCur; + /* 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); - printf("realloc border!!! (%u => %u)\n", maxOld, maxCur); -} + //is the direction of the border arc opposite to that of the original arc? + if (abs(mathDiff(alpha0, alpha1)) > 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 = {alen, 0}; + mathRotate(delta, beta); + delta += _start; -static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable) -{ - constexpr SwCoord EPSILON = 2; + //circumnavigate the negative sector backwards + border->movable = false; + _borderLineTo(border, delta, false); + _borderLineTo(border, _end, false); + _borderCubicTo(border, _ctrl2, _ctrl1, _start); - assert(border && border->start >= 0); + //and thenmove to the endpoint + _borderLineTo(border, _end, false); - if (border->movable) { - //move last point - border->pts[border->ptsCnt - 1] = to; - } else { - //don't add zero-length line_to - auto diff = border->pts[border->ptsCnt - 1] - to; - if (border->ptsCnt > 0 && abs(diff.x) < EPSILON && abs(diff.y) < EPSILON) return; + continue; + } - _growBorder(border, 1); - border->pts[border->ptsCnt] = to; - border->tags[border->ptsCnt] = SW_STROKE_TAG_ON; - border->ptsCnt += 1; + //else fall through + } + _borderCubicTo(border, _ctrl1, _ctrl2, _end); + ++side; + ++border; + } + arc -= 3; + stroke.angleIn = angleOut; } - - border->movable = movable; + stroke.center = to; } @@ -298,20 +559,20 @@ static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side) auto border = stroke.borders + side; SwPoint delta = {stroke.width, 0}; - _rotate(delta, angle); + mathRotate(delta, angle); SwPoint delta2 = {stroke.width, 0}; - _rotate(delta2, angle + rotate); + mathRotate(delta2, angle + rotate); delta += stroke.center + delta2; _borderLineTo(border, delta, false); delta = {stroke.width, 0}; - _rotate(delta, angle); + mathRotate(delta, angle); delta2 = {stroke.width, 0}; - _rotate(delta2, angle - rotate); + mathRotate(delta2, angle - rotate); delta += delta2 + stroke.center; @@ -329,14 +590,14 @@ static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side) auto border = stroke.borders + side; SwPoint delta = {stroke.width, 0}; - _rotate(delta, angle + rotate); + mathRotate(delta, angle + rotate); delta += stroke.center; _borderLineTo(border, delta, false); delta = {stroke.width, 0}; - _rotate(delta, angle - rotate); + mathRotate(delta, angle - rotate); delta += stroke.center; @@ -389,88 +650,6 @@ static void _addReverseLeft(SwStroke& stroke, bool opened) } -static void _closeBorder(SwStrokeBorder* border, bool reverse) -{ - assert(border && border->start >= 0); - - uint32_t start = border->start; - uint32_t 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; - - for (; pt1 < pt2; pt1++, pt2--) { - auto tmp = *pt1; - *pt1 = *pt2; - *pt2 = tmp; - } - - //reverse the tags - auto tag1 = border->tags + start + 1; - auto tag2 = border->tags + count - 1; - - for (; tag1 < tag2; tag1++, tag2++) { - auto tmp = *tag1; - *tag1 = *tag2; - *tag2 = tmp; - } - } - - border->tags[start] |= SW_STROKE_TAG_BEGIN; - border->tags[count - 1] |= SW_STROKE_TAG_END; - } - - border->start = -1; - border->movable = false; -} - - -static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength) -{ - auto border = stroke.borders + side; - auto theta = _angleDiff(stroke.angleIn, stroke.angleOut) / 2; - SwPoint delta; - bool intersect; - - /* 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) { - intersect = false; - } else { - //compute minimum required length of lines - SwFixed minLength = abs(_multiply(stroke.width, _tan(theta))); - if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true; - } - - auto rotate = SIDE_TO_ROTATE(side); - - if (!intersect) { - delta = {stroke.width, 0}; - _rotate(delta, stroke.angleOut + rotate); - delta += stroke.center; - border->movable = false; - } else { - //compute median angle - delta = {_divide(stroke.width, _cos(theta)), 0}; - _rotate(delta, stroke.angleIn + theta + rotate); - delta += stroke.center; - } - - _borderLineTo(border, delta, false); -} - - static void _beginSubPath(SwStroke& stroke, SwPoint& to, bool opened) { cout << "stroke opened? = " << opened << endl; @@ -518,7 +697,7 @@ static void _endSubPath(SwStroke& stroke) /* now end the right subpath accordingly. The left one is rewind and deosn't need further processing */ - _closeBorder(right, false); + _borderClose(right, false); } else { //close the path if needed @@ -527,7 +706,7 @@ static void _endSubPath(SwStroke& stroke) //process the corner stroke.angleOut = stroke.subPathAngle; - auto turn = _angleDiff(stroke.angleIn, stroke.angleOut); + auto turn = mathDiff(stroke.angleIn, stroke.angleOut); //No specific corner processing is required if the turn is 0 if (turn != 0) { @@ -542,8 +721,8 @@ static void _endSubPath(SwStroke& stroke) _inside(stroke, 1 - inside, stroke.subPathLineLength); //outside } - _closeBorder(stroke.borders + 0, false); - _closeBorder(stroke.borders + 1, true); + _borderClose(stroke.borders + 0, false); + _borderClose(stroke.borders + 1, true); } } @@ -572,14 +751,6 @@ void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join) { _deleteRle(stroke); -#if 0 - miterLimit = 4 * (1 >> 16); - - /* ensure miter limit has sensible value */ - if ( stroker->miter_limit < 0x10000 ) - stroker->miter_limit = 0x10000; -#endif - stroke.width = TO_SWCOORD(width * 0.5f); stroke.cap = cap; -- 2.7.4 From 1686af7643cdfbe02859ab6323396f439d228756 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 1 Jun 2020 20:27:43 +0900 Subject: [PATCH 06/16] sw_engine: implement stroke rle part Current stroke fails to merged shapes case... you can test with testStroke example Change-Id: I488af728949cba1d01b88723eb1dc4c49bac6c9b --- src/lib/sw_engine/tvgSwCommon.h | 11 +-- src/lib/sw_engine/tvgSwMath.cpp | 101 +++++++++++++----------- src/lib/sw_engine/tvgSwRaster.cpp | 26 ++++-- src/lib/sw_engine/tvgSwRenderer.cpp | 12 ++- src/lib/sw_engine/tvgSwRle.cpp | 24 +++--- src/lib/sw_engine/tvgSwShape.cpp | 80 +++++++++++-------- src/lib/sw_engine/tvgSwStroke.cpp | 153 ++++++++++++++++++++++++++++++------ test/testStroke.cpp | 5 +- 8 files changed, 271 insertions(+), 141 deletions(-) diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index b942ca1..cfb15bd 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -121,8 +121,6 @@ struct SwStrokeBorder struct SwStroke { - SwRleData* rle; - SwFixed angleIn; SwFixed angleOut; SwPoint center; @@ -146,8 +144,9 @@ struct SwStroke struct SwShape { SwOutline* outline; - SwRleData* rle; SwStroke* stroke; + SwRleData* rle; + SwRleData* strokeRle; SwBBox bbox; }; @@ -196,11 +195,13 @@ void shapeFree(SwShape* sdata); void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join); bool strokeParseOutline(SwStroke& stroke, SwOutline& outline); +SwOutline* strokeExportOutline(SwStroke& stroke); void strokeFree(SwStroke* stroke); -SwRleData* rleRender(const SwShape& sdata, const SwSize& clip); -SwRleData* rleStrokeRender(const SwShape& sdata); +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); #endif /* _TVG_SW_COMMON_H_ */ diff --git a/src/lib/sw_engine/tvgSwMath.cpp b/src/lib/sw_engine/tvgSwMath.cpp index 919fe1e..d2fcc4e 100644 --- a/src/lib/sw_engine/tvgSwMath.cpp +++ b/src/lib/sw_engine/tvgSwMath.cpp @@ -24,7 +24,7 @@ /* Internal Class Implementation */ /************************************************************************/ -constexpr auto CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32 +constexpr SwCoord 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; @@ -45,15 +45,14 @@ static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n) } -static SwCoord _downscale(SwCoord x) +static SwCoord _downscale(SwFixed x) { //multiply a give value by the CORDIC shrink factor - - abs(x); - int64_t t = (x * static_cast(CORDIC_FACTOR)) + 0x100000000UL; - x = static_cast(t >> 32); - if (x < 0) x = -x; - return x; + auto s = abs(x); + int64_t t = (s * static_cast(CORDIC_FACTOR)) + 0x100000000UL; + s = static_cast(t >> 32); + if (x < 0) s = -s; + return s; } @@ -139,6 +138,47 @@ static void _polarize(SwPoint& pt) } +static void _rotate(SwPoint& pt, SwFixed theta) +{ + auto v = pt; + + //Rotate inside [-PI/4, PI/4] sector + while (theta < -ANGLE_PI4) { + auto tmp = v.y; + v.y = -v.x; + v.x = tmp; + theta += ANGLE_PI2; + } + + while (theta > ANGLE_PI4) { + auto tmp = -v.y; + v.y = v.x; + v.x = tmp; + theta -= 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 = 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++; + } + } + + pt = v; +} + + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -267,57 +307,26 @@ void mathRotate(SwPoint& pt, SwFixed angle) auto shift = _normalize(v); auto theta = angle; - //Rotate inside [-PI/4, PI/4] sector - while (theta < -ANGLE_PI4) { - auto tmp = v.y; - v.y = -v.x; - v.x = tmp; - theta += ANGLE_PI2; - } - - while (theta > ANGLE_PI4) { - auto tmp = -v.y; - v.y = v.x; - v.x = tmp; - theta -= 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 = 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++; - } - } + _rotate(v, theta); v.x = _downscale(v.x); v.y = _downscale(v.y); if (shift > 0) { auto half = static_cast(1L << (shift - 1)); - v.x = (v.x + half + SATURATE(v.x)) >> shift; - v.y = (v.y + half + SATURATE(v.y)) >> shift; + pt.x = (v.x + half + SATURATE(v.x)) >> shift; + pt.y = (v.y + half + SATURATE(v.y)) >> shift; } else { shift = -shift; - v.x = static_cast((unsigned long)v.x << shift); - v.y = static_cast((unsigned long)v.y << shift); + pt.x = static_cast((unsigned long)v.x << shift); + pt.y = static_cast((unsigned long)v.y << shift); } } SwFixed mathTan(SwFixed angle) { SwPoint v = {CORDIC_FACTOR >> 8, 0}; - mathRotate(v, angle); + _rotate(v, angle); return mathDivide(v.y, v.x); } @@ -343,7 +352,7 @@ SwFixed mathSin(SwFixed angle) SwFixed mathCos(SwFixed angle) { SwPoint v = {CORDIC_FACTOR >> 8, 0}; - mathRotate(v, angle); + _rotate(v, angle); return (v.x + 0x80L) >> 8; } diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index ba7c6a3..78d507b 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -76,18 +76,13 @@ _rasterSolid(uint32_t* dst, uint32_t len, uint32_t color, uint32_t cov) } -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +static bool +_rasterRle(Surface& surface, SwRleData* rle, uint32_t color, uint8_t a) { - SwRleData* rle = sdata.rle; if (!rle) return false; auto span = rle->spans; auto stride = surface.stride; - auto color = COLOR_ARGB_JOIN(r, g, b, a); for (uint32_t i = 0; i < rle->size; ++i) { assert(span); @@ -103,4 +98,21 @@ bool rasterShape(Surface& surface, SwShape& sdata, uint8_t r, uint8_t g, uint8_t return true; } + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +bool rasterShape(Surface& surface, SwShape& sdata, 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); +} + + +bool rasterStroke(Surface& surface, SwShape& sdata, 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); +} + + #endif /* _TVG_SW_RASTER_CPP_ */ \ No newline at end of file diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index bf97c9b..3ec882e 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -63,16 +63,14 @@ bool SwRenderer::render(const Shape& shape, void *data) if (!sdata) return false; size_t r, g, b, a; - shape.fill(&r, &g, &b, &a); - size_t sa; - shape.strokeColor(nullptr, nullptr, nullptr, &sa); + shape.fill(&r, &g, &b, &a); + if (a > 0) rasterShape(surface, *sdata, r, g, b, a); - //invisible? - if (a == 0 && sa == 0) return false; + shape.strokeColor(&r, &g, &b, &a); + if (a > 0) rasterStroke(surface, *sdata, r, g, b, a); - //TODO: Threading - return rasterShape(surface, *sdata, r, g, b, a); + return true; } diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index 7bbc326..b22b233 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -612,9 +612,7 @@ static bool _decomposeOutline(RleWorker& rw) goto close; } } - - //FIXME: Close the contour with a line segment? - //_lineTo(rw, UPSCALE(outline->pts[first])); + _lineTo(rw, UPSCALE(outline->pts[first])); close: first = last + 1; } @@ -646,13 +644,12 @@ static bool _genRle(RleWorker& rw) /* External Class Implementation */ /************************************************************************/ -SwRleData* rleRender(const SwShape& sdata, const SwSize& clip) +SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip) { //Please adjust when you out of cell memory (default: 16384L) constexpr auto RENDER_POOL_SIZE = 166641L; constexpr auto BAND_SIZE = 40; - auto outline = sdata.outline; assert(outline); assert(outline->cntrs && outline->pts); assert(outline->ptsCnt == outline->cntrs[outline->cntrsCnt - 1] + 1); @@ -671,11 +668,11 @@ SwRleData* rleRender(const SwShape& sdata, const SwSize& clip) rw.area = 0; rw.cover = 0; rw.invalid = true; - rw.cellMin = sdata.bbox.min; - rw.cellMax = sdata.bbox.max; + rw.cellMin = bbox.min; + rw.cellMax = bbox.max; rw.cellXCnt = rw.cellMax.x - rw.cellMin.x; rw.cellYCnt = rw.cellMax.y - rw.cellMin.y; - rw.outline = outline; + rw.outline = const_cast(outline); rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64 rw.bandShoot = 0; rw.clip = clip; @@ -770,12 +767,13 @@ error: } -SwRleData* rleStrokeRender(const SwShape& sdata) +void rleFree(SwRleData* rle) { - auto stroke = sdata.stroke; - assert(stroke); - - return nullptr; + if (!rle) return; + if (rle->spans) free(rle->spans); + free(rle); } + + #endif /* _TVG_SW_RLE_H_ */ diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index f1ab06e..e8e1996 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -64,6 +64,17 @@ static void _growOutlinePoint(SwOutline& outline, uint32_t n) } +static void _freeOutline(SwOutline* outline) +{ + if (!outline) return; + + if (outline->cntrs) free(outline->cntrs); + if (outline->pts) free(outline->pts); + if (outline->types) free(outline->types); + free(outline); +} + + static void _outlineEnd(SwOutline& outline) { _growOutlineContour(outline, 1); @@ -153,23 +164,22 @@ static void _outlineClose(SwOutline& outline) } -static void _initBBox(SwShape& sdata) +static void _initBBox(SwBBox& bbox) { - sdata.bbox.min.x = sdata.bbox.min.y = 0; - sdata.bbox.max.x = sdata.bbox.max.y = 0; + bbox.min.x = bbox.min.y = 0; + bbox.max.x = bbox.max.y = 0; } -static bool _updateBBox(SwShape& sdata) +static bool _updateBBox(SwOutline* outline, SwBBox& bbox) { - auto outline = sdata.outline; - assert(outline); + if (!outline) return false; auto pt = outline->pts; assert(pt); if (outline->ptsCnt <= 0) { - _initBBox(sdata); + _initBBox(bbox); return false; } @@ -187,10 +197,10 @@ static bool _updateBBox(SwShape& sdata) if (yMin > pt->y) yMin = pt->y; if (yMax < pt->y) yMax = pt->y; } - sdata.bbox.min.x = xMin >> 6; - sdata.bbox.max.x = (xMax + 63) >> 6; - sdata.bbox.min.y = yMin >> 6; - sdata.bbox.max.y = (yMax + 63) >> 6; + bbox.min.x = xMin >> 6; + bbox.max.x = (xMax + 63) >> 6; + bbox.min.y = yMin >> 6; + bbox.max.y = (yMax + 63) >> 6; if (xMax - xMin < 1 || yMax - yMin < 1) return false; @@ -213,16 +223,6 @@ static bool _checkValid(SwShape& sdata, const SwSize& clip) } -static void _deleteRle(SwShape& sdata) -{ - if (!sdata.rle) return; - if (sdata.rle->spans) free(sdata.rle->spans); - free(sdata.rle); - sdata.rle = nullptr; -} - - - /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -245,10 +245,10 @@ void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTrans bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip) { - if (!_updateBBox(sdata)) goto end; + if (!_updateBBox(sdata.outline, sdata.bbox)) goto end; if (!_checkValid(sdata, clip)) goto end; - sdata.rle = rleRender(sdata, clip); + sdata.rle = rleRender(sdata.outline, sdata.bbox, clip); end: if (sdata.rle) return true; @@ -259,12 +259,7 @@ end: void shapeDelOutline(SwShape& sdata) { auto outline = sdata.outline; - if (!outline) return; - - if (outline->cntrs) free(outline->cntrs); - if (outline->pts) free(outline->pts); - if (outline->types) free(outline->types); - free(outline); + _freeOutline(outline); sdata.outline = nullptr; } @@ -272,8 +267,9 @@ void shapeDelOutline(SwShape& sdata) void shapeReset(SwShape& sdata) { shapeDelOutline(sdata); - _deleteRle(sdata); - _initBBox(sdata); + rleFree(sdata.rle); + sdata.rle = nullptr; + _initBBox(sdata.bbox); } @@ -366,8 +362,13 @@ void shapeFree(SwShape* sdata) assert(sdata); shapeDelOutline(*sdata); - _deleteRle(*sdata); - strokeFree(sdata->stroke); + rleFree(sdata->rle); + + if (sdata->stroke) { + rleFree(sdata->strokeRle); + strokeFree(sdata->stroke); + } + free(sdata); } @@ -377,8 +378,9 @@ void shapeResetStroke(const Shape& shape, SwShape& sdata) if (!sdata.stroke) sdata.stroke = static_cast(calloc(1, sizeof(SwStroke))); auto stroke = sdata.stroke; assert(stroke); - strokeReset(*stroke, shape.strokeWidth(), shape.strokeCap(), shape.strokeJoin()); + rleFree(sdata.strokeRle); + sdata.strokeRle = nullptr; } @@ -392,6 +394,16 @@ bool shapeGenStrokeRle(const Shape& shape, SwShape& sdata, const SwSize& clip) if (!strokeParseOutline(*sdata.stroke, *sdata.outline)) return false; + auto outline = strokeExportOutline(*sdata.stroke); + if (!outline) return false; + + SwBBox bbox; + _updateBBox(outline, bbox); + + sdata.strokeRle = rleRender(outline, bbox, clip); + + _freeOutline(outline); + return true; } diff --git a/src/lib/sw_engine/tvgSwStroke.cpp b/src/lib/sw_engine/tvgSwStroke.cpp index a0a2573..8477d7c 100644 --- a/src/lib/sw_engine/tvgSwStroke.cpp +++ b/src/lib/sw_engine/tvgSwStroke.cpp @@ -23,7 +23,7 @@ /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ -static constexpr auto SW_STROKE_TAG_ON = 1; +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; @@ -36,6 +36,8 @@ static inline SwFixed SIDE_TO_ROTATE(const int32_t s) static void _growBorder(SwStrokeBorder* border, uint32_t newPts) { + assert(border); + auto maxOld = border->maxPts; auto maxNew = border->ptsCnt + newPts; @@ -53,8 +55,6 @@ static void _growBorder(SwStrokeBorder* border, uint32_t newPts) assert(border->tags); border->maxPts = maxCur; - - printf("realloc border!!! (%u => %u)\n", maxOld, maxCur); } @@ -111,7 +111,7 @@ static void _borderClose(SwStrokeBorder* border, bool reverse) static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl2, SwPoint& to) { - assert(border->start >= 0); + assert(border && border->start >= 0); _growBorder(border, 3); @@ -124,9 +124,10 @@ static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl tag[0] = SW_STROKE_TAG_CUBIC; tag[1] = SW_STROKE_TAG_CUBIC; - tag[2] = SW_STROKE_TAG_ON; + tag[2] = SW_STROKE_TAG_POINT; border->ptsCnt += 3; + border->movable = false; } @@ -188,13 +189,13 @@ static void _borderLineTo(SwStrokeBorder* border, SwPoint& to, bool movable) //move last point border->pts[border->ptsCnt - 1] = to; } else { + //don't add zero-length line_to - auto diff = border->pts[border->ptsCnt - 1] - to; - if (border->ptsCnt > 0 && diff.small()) return; + 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_ON; + border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT; border->ptsCnt += 1; } @@ -207,8 +208,7 @@ static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to) assert(border); //close current open path if any? - if (border->start >= 0) - _borderClose(border, false); + if (border->start >= 0) _borderClose(border, false); border->start = border->ptsCnt; border->movable = false; @@ -318,8 +318,11 @@ static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength) border->movable = false; } else { //compute median angle - delta = {mathDivide(stroke.width, mathCos(theta)), 0}; - mathRotate(delta, stroke.angleIn + theta + rotate); + auto phi = stroke.angleIn + theta; + auto thcos = mathCos(theta); + auto length = mathDivide(stroke.width, thcos); + delta = {length, 0}; + mathRotate(delta, phi + rotate); delta += stroke.center; } @@ -652,11 +655,9 @@ static void _addReverseLeft(SwStroke& stroke, bool opened) static void _beginSubPath(SwStroke& stroke, SwPoint& to, bool opened) { - cout << "stroke opened? = " << opened << endl; - /* We cannot process the first point because there is not enought information regarding its corner/cap. Later, it will be processed - in the _strokeEndSubPath() */ + in the _endSubPath() */ stroke.firstPt = true; stroke.center = to; @@ -712,13 +713,13 @@ static void _endSubPath(SwStroke& stroke) if (turn != 0) { //when we turn to the right, the inside is 0 - auto inside = 0; + int32_t inside = 0; //otherwise, the inside is 1 if (turn < 0) inside = 1; _inside(stroke, inside, stroke.subPathLineLength); //inside - _inside(stroke, 1 - inside, stroke.subPathLineLength); //outside + _outside(stroke, 1 - inside, stroke.subPathLineLength); //outside } _borderClose(stroke.borders + 0, false); @@ -727,14 +728,81 @@ static void _endSubPath(SwStroke& stroke) } -static void _deleteRle(SwStroke& stroke) +static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt) +{ + assert(border); + + 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; + border->valid = true; + ptsCnt = _ptsCnt; + cntrsCnt = _cntrsCnt; + + return; + +fail: + ptsCnt = 0; + cntrsCnt = 0; +} + + +static void _exportBorderOutline(SwStroke& stroke, SwOutline* outline, uint32_t side) { - if (!stroke.rle) return; - if (stroke.rle->spans) free(stroke.rle->spans); - free(stroke.rle); - stroke.rle = nullptr; + auto border = stroke.borders + side; + assert(border); + + if (!border->valid) return; + + 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 cout << "what type of stroke outline??" << endl; + + if (*src & SW_STROKE_TAG_END) { + *cntrs = idx; + ++cntrs; + ++outline->cntrsCnt; + } + + ++src; + ++tags; + ++idx; + --cnt; + } + outline->ptsCnt = outline->ptsCnt + border->ptsCnt; } + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -742,16 +810,20 @@ static void _deleteRle(SwStroke& stroke) void strokeFree(SwStroke* stroke) { if (!stroke) return; - _deleteRle(*stroke); + + //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); + free(stroke); } void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join) { - _deleteRle(stroke); - - stroke.width = TO_SWCOORD(width * 0.5f); + stroke.width = TO_SWCOORD(width * 0.5); stroke.cap = cap; //Save line join: it can be temporarily changed when stroking curves... @@ -826,4 +898,33 @@ bool strokeParseOutline(SwStroke& stroke, SwOutline& outline) } +SwOutline* strokeExportOutline(SwStroke& stroke) +{ + 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 = static_cast(calloc(1, sizeof(SwOutline))); + assert(outline); + + outline->pts = static_cast(malloc(sizeof(SwPoint) * ptsCnt)); + assert(outline->pts); + + outline->types = static_cast(malloc(sizeof(uint8_t) * ptsCnt)); + assert(outline->types); + + outline->cntrs = static_cast(malloc(sizeof(uint32_t) * cntrsCnt)); + assert(outline->cntrs); + + _exportBorderOutline(stroke, outline, 0); //left + _exportBorderOutline(stroke, outline, 1); //right + + return outline; +} + + #endif /* _TVG_SW_STROKER_H_ */ diff --git a/test/testStroke.cpp b/test/testStroke.cpp index 1f865d6..d978b6f 100644 --- a/test/testStroke.cpp +++ b/test/testStroke.cpp @@ -23,11 +23,10 @@ void tvgtest() //Prepare a Shape (Rectangle + Rectangle + Circle + Circle) auto shape1 = tvg::Shape::gen(); - shape1->appendRect(0, 0, 200, 200, 0); //x, y, w, h, cornerRadius + shape1->appendRect(50, 50, 200, 200, 0); //x, y, w, h, cornerRadius shape1->appendRect(100, 100, 300, 300, 100); //x, y, w, h, cornerRadius shape1->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH - shape1->appendCircle(400, 500, 170, 100); //cx, cy, radiusW, radiusH - shape1->fill(255, 255, 0, 255); //r, g, b, a + shape1->fill(50, 50, 50, 255); //r, g, b, a //Stroke Style shape1->stroke(255, 255, 255, 255); //color: r, g, b, a -- 2.7.4 From bab258e004505faa25eeec0ba285141ed4716f8d Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 2 Jun 2020 20:04:44 +0900 Subject: [PATCH 07/16] test: revise stroke example Change-Id: I92a40e905544fd8fb41df88810eabce7429b1b7f --- test/testStroke.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/test/testStroke.cpp b/test/testStroke.cpp index d978b6f..a4678dc 100644 --- a/test/testStroke.cpp +++ b/test/testStroke.cpp @@ -21,23 +21,63 @@ void tvgtest() auto canvas = tvg::SwCanvas::gen(); canvas->target(buffer, WIDTH, WIDTH, HEIGHT); - //Prepare a Shape (Rectangle + Rectangle + Circle + Circle) + //Shape 1 auto shape1 = tvg::Shape::gen(); - shape1->appendRect(50, 50, 200, 200, 0); //x, y, w, h, cornerRadius - shape1->appendRect(100, 100, 300, 300, 100); //x, y, w, h, cornerRadius - shape1->appendCircle(400, 400, 100, 100); //cx, cy, radiusW, radiusH - shape1->fill(50, 50, 50, 255); //r, g, b, a + shape1->appendRect(50, 50, 200, 200, 0); + shape1->fill(50, 50, 50, 255); + shape1->stroke(255, 255, 255, 255); //color: r, g, b, a + shape1->stroke(tvg::StrokeJoin::Bevel); //default is Bevel + shape1->stroke(10); //width: 10px - //Stroke Style - shape1->stroke(255, 255, 255, 255); //color: r, g, b, a - shape1->stroke(5); //width: 5px -// shape1->strokeJoin(tvg::StrokeJoin::Miter); -// shape1->strokeLineCap(tvg::StrokeLineCap::Butt); + canvas->push(move(shape1)); -// uint32_t dash[] = {3, 1, 5, 1}; //dash pattern -// shape1->strokeDash(dash, 4); + //Shape 2 + auto shape2 = tvg::Shape::gen(); + shape2->appendRect(300, 50, 200, 200, 0); + shape2->fill(50, 50, 50, 255); + shape2->stroke(255, 255, 255, 255); + shape2->stroke(tvg::StrokeJoin::Round); + shape2->stroke(10); + + canvas->push(move(shape2)); + + //Shape 3 + auto shape3 = tvg::Shape::gen(); + shape3->appendRect(550, 50, 200, 200, 0); + shape3->fill(50, 50, 50, 255); + shape3->stroke(255, 255, 255, 255); + shape3->stroke(tvg::StrokeJoin::Miter); + shape3->stroke(10); + + canvas->push(move(shape3)); + + //Shape 4 + auto shape4 = tvg::Shape::gen(); + shape4->appendCircle(150, 450, 100, 100); + shape4->fill(50, 50, 50, 255); + shape4->stroke(255, 255, 255, 255); + shape4->stroke(1); + + canvas->push(move(shape4)); + + //Shape 5 + auto shape5 = tvg::Shape::gen(); + shape5->appendCircle(400, 450, 100, 100); + shape5->fill(50, 50, 50, 255); + shape5->stroke(255, 255, 255, 255); + shape5->stroke(2); + + canvas->push(move(shape5)); + + //Shape 6 + auto shape6 = tvg::Shape::gen(); + shape6->appendCircle(650, 450, 100, 100); + shape6->fill(50, 50, 50, 255); + shape6->stroke(255, 255, 255, 255); + shape6->stroke(4); + + canvas->push(move(shape6)); - canvas->push(move(shape1)); canvas->draw(); canvas->sync(); -- 2.7.4 From 8614a2efee4b8066c819db2c714f11c341254588 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 2 Jun 2020 20:25:54 +0900 Subject: [PATCH 08/16] sw_engine: fix stroke join round result. a trivial reversed value was returned that brought the inverted arc drawing... Change-Id: I928f05b3400772a367d1653496d385354032cbad --- src/lib/sw_engine/tvgSwMath.cpp | 2 +- src/lib/sw_engine/tvgSwStroke.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/sw_engine/tvgSwMath.cpp b/src/lib/sw_engine/tvgSwMath.cpp index d2fcc4e..80c7f8e 100644 --- a/src/lib/sw_engine/tvgSwMath.cpp +++ b/src/lib/sw_engine/tvgSwMath.cpp @@ -295,7 +295,7 @@ int64_t mathMulDiv(int64_t a, int64_t b, int64_t c) } int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL; - return (s > 0 ? -d : d); + return (s > 0 ? d : -d); } diff --git a/src/lib/sw_engine/tvgSwStroke.cpp b/src/lib/sw_engine/tvgSwStroke.cpp index 8477d7c..e048ff5 100644 --- a/src/lib/sw_engine/tvgSwStroke.cpp +++ b/src/lib/sw_engine/tvgSwStroke.cpp @@ -134,7 +134,7 @@ static void _borderCubicTo(SwStrokeBorder* border, SwPoint& ctrl1, SwPoint& ctrl static void _borderArcTo(SwStrokeBorder* border, SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff) { - constexpr auto ARC_CUBIC_ANGLE = ANGLE_PI / 2; + constexpr SwFixed ARC_CUBIC_ANGLE = ANGLE_PI / 2; SwPoint a = {radius, 0}; mathRotate(a, angleStart); a += center; -- 2.7.4 From ef9f31577e0fbf94a0135b827e84750be5673f2b Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 2 Jun 2020 20:58:50 +0900 Subject: [PATCH 09/16] common stroke: retype the stroke width from size_t to float Change-Id: I812d06d2037d66408c41d50f7c1ff7ba605558bd --- inc/tizenvg.h | 4 ++-- src/lib/tvgShape.cpp | 6 +++--- src/lib/tvgShapeImpl.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/inc/tizenvg.h b/inc/tizenvg.h index 05f1927..c113975 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -141,7 +141,7 @@ public: int appendPath(const PathCommand* cmds, size_t cmdCnt, const Point* pts, size_t ptsCnt) noexcept; //Stroke - int stroke(size_t width) noexcept; + int stroke(float width) noexcept; int stroke(size_t r, size_t g, size_t b, size_t a) noexcept; int stroke(const size_t* dashPattern, size_t cnt) noexcept; int stroke(StrokeCap cap) noexcept; @@ -161,7 +161,7 @@ public: int fill(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept; int bounds(float* x, float* y, float* w, float* h) const noexcept override; - size_t strokeWidth() const noexcept; + float strokeWidth() const noexcept; int strokeColor(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept; size_t strokeDash(const size_t** dashPattern) const noexcept; StrokeCap strokeCap() const noexcept; diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index a17c53b..fe95eac 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -280,7 +280,7 @@ int Shape::bounds(float* x, float* y, float* w, float* h) const noexcept } -int Shape::stroke(size_t width) noexcept +int Shape::stroke(float width) noexcept { auto impl = pImpl.get(); assert(impl); @@ -291,12 +291,12 @@ int Shape::stroke(size_t width) noexcept } -size_t Shape::strokeWidth() const noexcept +float Shape::strokeWidth() const noexcept { auto impl = pImpl.get(); assert(impl); - if (!impl->stroke) return -1; + if (!impl->stroke) return 0; return impl->stroke->width; } diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index 682c29b..05be16d 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -30,7 +30,7 @@ struct ShapeFill struct ShapeStroke { - size_t width = 0; + float width = 0; size_t color[4] = {0, 0, 0, 0}; size_t* dashPattern = nullptr; size_t dashCnt = 0; @@ -153,7 +153,7 @@ struct Shape::Impl return 0; } - bool strokeWidth(size_t width) + bool strokeWidth(float width) { //TODO: Size Exception? -- 2.7.4 From 7ee25cd78334a24bb6b465ae5db46bb4dbf403cb Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 3 Jun 2020 11:15:40 +0900 Subject: [PATCH 10/16] sw_engine: fix mistached c style alloc/free these are allocated by c style mem alloc. Thus, they should be freed with free() Change-Id: I320fff4d5a5bce2374ace6495a9f96c3e1034cfc --- src/lib/sw_engine/tvgSwStroke.cpp | 1 + src/lib/tvgShapePath.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/sw_engine/tvgSwStroke.cpp b/src/lib/sw_engine/tvgSwStroke.cpp index e048ff5..be18e05 100644 --- a/src/lib/sw_engine/tvgSwStroke.cpp +++ b/src/lib/sw_engine/tvgSwStroke.cpp @@ -838,6 +838,7 @@ void strokeReset(SwStroke& stroke, float width, StrokeCap cap, StrokeJoin join) stroke.borders[1].valid = false; } + bool strokeParseOutline(SwStroke& stroke, SwOutline& outline) { uint32_t first = 0; diff --git a/src/lib/tvgShapePath.h b/src/lib/tvgShapePath.h index 68205f4..7e020e8 100644 --- a/src/lib/tvgShapePath.h +++ b/src/lib/tvgShapePath.h @@ -36,8 +36,8 @@ struct ShapePath ~ShapePath() { - if (cmds) delete(cmds); - if (pts) delete(pts); + if (cmds) free(cmds); + if (pts) free(pts); } void reserveCmd(size_t cmdCnt) -- 2.7.4 From 3bb272877a273c01cde42c35f313128a538642dd Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 3 Jun 2020 11:27:34 +0900 Subject: [PATCH 11/16] sw_engine: fix a missing of variable initializing. Change-Id: I6451b07709fbc56441363e8f0ee0f4647404ae10 --- src/lib/sw_engine/tvgSwRle.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index b22b233..1d5577b 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -672,6 +672,7 @@ SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& rw.cellMax = bbox.max; rw.cellXCnt = rw.cellMax.x - rw.cellMin.x; rw.cellYCnt = rw.cellMax.y - rw.cellMin.y; + rw.ySpan = 0; rw.outline = const_cast(outline); rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64 rw.bandShoot = 0; -- 2.7.4 From 2b53b8018a0275e55f1caca48ab36db66ad3f291 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 3 Jun 2020 11:35:03 +0900 Subject: [PATCH 12/16] test stroke: remove duplicated engine initialize call. Change-Id: Ia592a45581eae4fd5c85e6ba4d9d0eef835b14c1 --- test/testStroke.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/testStroke.cpp b/test/testStroke.cpp index a4678dc..b17c3a1 100644 --- a/test/testStroke.cpp +++ b/test/testStroke.cpp @@ -14,9 +14,6 @@ void tvgtest() //Initialize TizenVG Engine tvg::Engine::init(); - //Initialize TizenVG Engine - tvg::Engine::init(); - //Create a Canvas auto canvas = tvg::SwCanvas::gen(); canvas->target(buffer, WIDTH, WIDTH, HEIGHT); -- 2.7.4 From ad6b74dd1392a2272f71e63e3fba7ede8c5fd83e Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 3 Jun 2020 11:41:40 +0900 Subject: [PATCH 13/16] test scene: remove duplicated engine initialization. Change-Id: I5a2f972544e47578552b0c49367749ce2d01c5f2 --- test/testSceneTransform.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/testSceneTransform.cpp b/test/testSceneTransform.cpp index 38bfb0f..a66996a 100644 --- a/test/testSceneTransform.cpp +++ b/test/testSceneTransform.cpp @@ -13,9 +13,6 @@ tvg::Scene* pScene2 = nullptr; void tvgtest() { - //Initialize TizenVG Engine - tvg::Engine::init(); - //Create a Canvas canvas = tvg::SwCanvas::gen(); canvas->target(buffer, WIDTH, WIDTH, HEIGHT); @@ -96,9 +93,6 @@ void tvgtest() canvas->draw(); canvas->sync(); - - //Terminate TizenVG Engine - tvg::Engine::term(); } void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progress) -- 2.7.4 From 01b550497c1121d88831d844302ecf6eda1b92d1 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 2 Jun 2020 21:00:50 +0900 Subject: [PATCH 14/16] sw_engine: support stroke transformation properly. also updated transform test cases. Yet, this engine is not well optimized, If they are too mch sluggish, you can use ELM_FPS envrionment lowing down the fps when you launch test cases... ex) $ELM_FPS=30 ./testSceneTransform Change-Id: I1871d5bedee010d5d6a3d877d95e257120796e8b --- src/lib/sw_engine/tvgSwRenderer.cpp | 33 +++++++++++++++++++-------------- test/testDirectUpdate.cpp | 3 +++ test/testSceneTransform.cpp | 12 ++++++++---- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 3ec882e..91c0013 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -93,29 +93,34 @@ void* SwRenderer::prepare(const Shape& shape, void* data, const RenderTransform* if (flags == RenderUpdateFlag::None) return sdata; - //invisible? - size_t a, sa; - shape.fill(nullptr, nullptr, nullptr, &a); - shape.strokeColor(nullptr, nullptr, nullptr, &sa); - if (a == 0 && sa == 0) return sdata; - //TODO: Threading SwSize clip = {static_cast(surface.w), static_cast(surface.h)}; //Shape if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) { - shapeReset(*sdata); - if (!shapeGenOutline(shape, *sdata)) return sdata; - if (transform) shapeTransformOutline(shape, *sdata, *transform); - if (!shapeGenRle(shape, *sdata, clip)) return sdata; + + size_t alpha = 0; + shape.fill(nullptr, nullptr, nullptr, &alpha); + + if (alpha > 0) { + shapeReset(*sdata); + if (!shapeGenOutline(shape, *sdata)) return sdata; + if (transform) shapeTransformOutline(shape, *sdata, *transform); + if (!shapeGenRle(shape, *sdata, clip)) return sdata; + } } //Stroke - if (flags & RenderUpdateFlag::Stroke) { - shapeResetStroke(shape, *sdata); - if (shape.strokeWidth() > 0.01) { - if (!shapeGenStrokeRle(shape, *sdata, clip)) return sdata; + if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { + + if (shape.strokeWidth() > 0.5) { + shapeResetStroke(shape, *sdata); + size_t alpha = 0; + shape.strokeColor(nullptr, nullptr, nullptr, &alpha); + if (alpha > 0) { + if (!shapeGenStrokeRle(shape, *sdata, clip)) return sdata; + } } } diff --git a/test/testDirectUpdate.cpp b/test/testDirectUpdate.cpp index 799d135..e1a4c60 100644 --- a/test/testDirectUpdate.cpp +++ b/test/testDirectUpdate.cpp @@ -27,6 +27,8 @@ void tvgtest() //fill property will be retained shape->fill(127, 255, 255, 255); + shape->stroke(0, 0, 255, 255); + shape->stroke(1); canvas->push(move(shape)); @@ -44,6 +46,7 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres pShape->reset(); //reset path pShape->appendRect(-100 + (800 * progress), -100 + (800 * progress), 200, 200, (100 * progress)); + pShape->stroke(30 * progress); //Update shape for drawing (this may work asynchronously) canvas->update(pShape); diff --git a/test/testSceneTransform.cpp b/test/testSceneTransform.cpp index a66996a..975f4fa 100644 --- a/test/testSceneTransform.cpp +++ b/test/testSceneTransform.cpp @@ -25,19 +25,21 @@ void tvgtest() //Prepare Round Rectangle (Scene1) auto shape1 = tvg::Shape::gen(); shape1->appendRect(-235, -250, 400, 400, 50); //x, y, w, h, cornerRadius - shape1->fill(0, 255, 0, 255); //r, g, b, a + shape1->fill(0, 255, 0, 255); //r, g, b, a + shape1->stroke(5); //width + shape1->stroke(255, 255, 255, 255); //r, g, b, a scene->push(move(shape1)); //Prepare Circle (Scene1) auto shape2 = tvg::Shape::gen(); shape2->appendCircle(-165, -150, 200, 200); //cx, cy, radiusW, radiusH - shape2->fill(255, 255, 0, 255); //r, g, b, a + shape2->fill(255, 255, 0, 255); //r, g, b, a scene->push(move(shape2)); //Prepare Ellipse (Scene1) auto shape3 = tvg::Shape::gen(); - shape3->appendCircle(265, 250, 150, 100); //cx, cy, radiusW, radiusH - shape3->fill(0, 255, 255, 255); //r, g, b, a + shape3->appendCircle(265, 250, 150, 100); //cx, cy, radiusW, radiusH + shape3->fill(0, 255, 255, 255); //r, g, b, a scene->push(move(shape3)); scene->translate(350, 350); @@ -64,6 +66,8 @@ void tvgtest() shape4->lineTo(-53, -5.5); shape4->close(); shape4->fill(0, 0, 127, 127); + shape4->stroke(3); //width + shape4->stroke(0, 0, 255, 255); //r, g, b, a scene2->push(move(shape4)); //Circle (Scene2) -- 2.7.4 From f335779ce52f39755828fe70d6996fa087803b06 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 3 Jun 2020 19:08:18 +0900 Subject: [PATCH 15/16] sw_engine stroke: stabilizing line drawing. Also added StrokeLine test Change-Id: I91143039823d744bf9287534227927556a2f51e1 --- .gitignore | 1 + src/lib/sw_engine/tvgSwRle.cpp | 2 +- src/lib/sw_engine/tvgSwShape.cpp | 1 + src/lib/sw_engine/tvgSwStroke.cpp | 9 ++-- test/makefile | 1 + test/testStrokeLine.cpp | 101 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 test/testStrokeLine.cpp diff --git a/.gitignore b/.gitignore index 1353d08..ba439cb 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ testScene testTransform testSceneTransform testStroke +testStrokeLine diff --git a/src/lib/sw_engine/tvgSwRle.cpp b/src/lib/sw_engine/tvgSwRle.cpp index 1d5577b..2757c0d 100644 --- a/src/lib/sw_engine/tvgSwRle.cpp +++ b/src/lib/sw_engine/tvgSwRle.cpp @@ -612,7 +612,7 @@ static bool _decomposeOutline(RleWorker& rw) goto close; } } - _lineTo(rw, UPSCALE(outline->pts[first])); + _lineTo(rw, start); close: first = last + 1; } diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index e8e1996..a4fa7fb 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -316,6 +316,7 @@ bool shapeGenOutline(const Shape& shape, SwShape& sdata) auto outline = sdata.outline; if (!outline) outline = static_cast(calloc(1, sizeof(SwOutline))); assert(outline); + outline->opened = true; _growOutlinePoint(*outline, outlinePtsCnt); _growOutlineContour(*outline, outlineCntrsCnt); diff --git a/src/lib/sw_engine/tvgSwStroke.cpp b/src/lib/sw_engine/tvgSwStroke.cpp index be18e05..06ec989 100644 --- a/src/lib/sw_engine/tvgSwStroke.cpp +++ b/src/lib/sw_engine/tvgSwStroke.cpp @@ -635,15 +635,14 @@ static void _addReverseLeft(SwStroke& stroke, bool opened) } 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 || SW_STROKE_TAG_END)) { - 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; + ++dstPt; + ++dstTag; } left->ptsCnt = left->start; diff --git a/test/makefile b/test/makefile index 3f314e8..2aba05d 100644 --- a/test/makefile +++ b/test/makefile @@ -11,3 +11,4 @@ all: gcc -o testTransform testTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` gcc -o testStroke testStroke.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` + gcc -o testStrokeLine testStrokeLine.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg` diff --git a/test/testStrokeLine.cpp b/test/testStrokeLine.cpp new file mode 100644 index 0000000..8687b85 --- /dev/null +++ b/test/testStrokeLine.cpp @@ -0,0 +1,101 @@ +#include +#include + +using namespace std; + +#define WIDTH 800 +#define HEIGHT 800 + +static uint32_t buffer[WIDTH * HEIGHT]; + +void tvgtest() +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + //Create a Canvas + auto canvas = tvg::SwCanvas::gen(); + canvas->target(buffer, WIDTH, WIDTH, HEIGHT); + + for (int i = 0; i < 10; ++i) { + auto shape = tvg::Shape::gen(); + shape->moveTo(50, 50 + (25 * i)); + shape->lineTo(750, 50 + (25 * i)); + shape->stroke(255, 255, 255, 255); //color: r, g, b, a + shape->stroke(i + 1); //stroke width + shape->stroke(tvg::StrokeCap::Round); //default is Square + canvas->push(move(shape)); + } + + auto shape1 = tvg::Shape::gen(); + shape1->moveTo(20, 350); + shape1->lineTo(250, 350); + shape1->lineTo(220, 500); + shape1->lineTo(70, 470); + shape1->lineTo(70, 330); + shape1->stroke(255, 0, 0, 255); + shape1->stroke(10); + shape1->stroke(tvg::StrokeJoin::Round); + shape1->stroke(tvg::StrokeCap::Round); + canvas->push(move(shape1)); + + auto shape2 = tvg::Shape::gen(); + shape2->moveTo(270, 350); + shape2->lineTo(500, 350); + shape2->lineTo(470, 500); + shape2->lineTo(320, 470); + shape2->lineTo(320, 330); + shape2->stroke(255, 255, 0, 255); + shape2->stroke(10); + shape2->stroke(tvg::StrokeJoin::Bevel); + shape2->stroke(tvg::StrokeCap::Square); + canvas->push(move(shape2)); + + auto shape3 = tvg::Shape::gen(); + shape3->moveTo(520, 350); + shape3->lineTo(750, 350); + shape3->lineTo(720, 500); + shape3->lineTo(570, 470); + shape3->lineTo(570, 330); + shape3->stroke(0, 255, 0, 255); + shape3->stroke(10); + shape3->stroke(tvg::StrokeJoin::Miter); + shape3->stroke(tvg::StrokeCap::Butt); + canvas->push(move(shape3)); + + canvas->draw(); + canvas->sync(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +} + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + +int main(int argc, char **argv) +{ + tvgtest(); + + //Show the result using EFL... + elm_init(argc, argv); + + Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test"); + evas_object_smart_callback_add(win, "delete,request", win_del, 0); + + Eo* img = evas_object_image_filled_add(evas_object_evas_get(win)); + evas_object_image_size_set(img, WIDTH, HEIGHT); + evas_object_image_data_set(img, buffer); + evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_show(img); + + elm_win_resize_object_add(win, img); + evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT); + evas_object_show(win); + + elm_run(); + elm_shutdown(); +} -- 2.7.4 From 98edb4112bf586a66f9bb095360eae1e9836adc8 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 3 Jun 2020 19:16:44 +0900 Subject: [PATCH 16/16] sw_engine shape: ++ comment for later optimization Change-Id: Ie6cd622748b88e2bce0c9d9a79cc4528a95c9c5c --- src/lib/sw_engine/tvgSwShape.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index a4fa7fb..30b79c8 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -245,6 +245,8 @@ void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTrans bool shapeGenRle(const Shape& shape, SwShape& sdata, const SwSize& clip) { + /* OPTIMIZE ME: We may avoid this bounding box calculation in this stage + if this shape has stroke and stroke bbox can be used here... */ if (!_updateBBox(sdata.outline, sdata.bbox)) goto end; if (!_checkValid(sdata, clip)) goto end; -- 2.7.4