From 2f2efb73dd2ffc07c4e7d11d02f86671658bda8d Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 17 May 2020 14:54:55 +0900 Subject: [PATCH 01/16] common shape: revise RenderTransform for Scene Transformation This RenderTransform takes over all transform information. Change-Id: I21b6a55de05feca56c40f3ff402d18445417463c --- src/lib/tvgRenderCommon.h | 75 ++++++++++++++++++++++++++--------------------- src/lib/tvgShape.cpp | 22 ++------------ src/lib/tvgShapeImpl.h | 70 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 101 insertions(+), 66 deletions(-) diff --git a/src/lib/tvgRenderCommon.h b/src/lib/tvgRenderCommon.h index b21c244..a3bfcb6 100644 --- a/src/lib/tvgRenderCommon.h +++ b/src/lib/tvgRenderCommon.h @@ -32,12 +32,27 @@ enum RenderUpdateFlag {None = 0, Path = 1, Fill = 2, Transform = 4, All = 8}; struct RenderTransform { + //3x3 Matrix Elements float e11, e12, e13; float e21, e22, e23; float e31, e32, e33; - void identity() + float x = 0.0f; + float y = 0.0f; + float degree = 0.0f; //rotation degree + float factor = 1.0f; //scale factor + + bool update() { + constexpr auto PI = 3.141592f; + + //Init Status + if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON && + fabsf(degree) <= FLT_EPSILON && fabsf(factor - 1) <= FLT_EPSILON) { + return false; + } + + //identity e11 = 1.0f; e12 = 0.0f; e13 = 0.0f; @@ -47,44 +62,38 @@ struct RenderTransform e31 = 0.0f; e32 = 0.0f; e33 = 1.0f; - } - void rotate(float degree) - { - constexpr auto PI = 3.141592f; + //rotation + if (fabsf(degree) > FLT_EPSILON) { + auto radian = degree / 180.0f * PI; + auto cosVal = cosf(radian); + auto sinVal = sinf(radian); + + auto t11 = e11 * cosVal + e12 * sinVal; + auto t12 = e11 * -sinVal + e12 * cosVal; + auto t21 = e21 * cosVal + e22 * sinVal; + auto t22 = e21 * -sinVal + e22 * cosVal; + auto t31 = e31 * cosVal + e32 * sinVal; + auto t32 = e31 * -sinVal + e32 * cosVal; + + e11 = t11; + e12 = t12; + e21 = t21; + e22 = t22; + e31 = t31; + e32 = t32; + } - if (fabsf(degree) <= FLT_EPSILON) return; - - auto radian = degree / 180.0f * PI; - auto cosVal = cosf(radian); - auto sinVal = sinf(radian); - - auto t11 = e11 * cosVal + e12 * sinVal; - auto t12 = e11 * -sinVal + e12 * cosVal; - auto t21 = e21 * cosVal + e22 * sinVal; - auto t22 = e21 * -sinVal + e22 * cosVal; - auto t31 = e31 * cosVal + e32 * sinVal; - auto t32 = e31 * -sinVal + e32 * cosVal; - - e11 = t11; - e12 = t12; - e21 = t21; - e22 = t22; - e31 = t31; - e32 = t32; - } + //scale + e11 *= factor; + e22 *= factor; + e33 *= factor; - void translate(float x, float y) - { + //translate e31 += x; e32 += y; - } - void scale(float factor) - { - e11 *= factor; - e22 *= factor; - e33 *= factor; + return true; } RenderTransform& operator*=(const RenderTransform rhs) diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index 6665a55..432a8a6 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -247,12 +247,7 @@ int Shape::scale(float factor) noexcept auto impl = pImpl.get(); assert(impl); - if (fabsf(factor - impl->scale) <= FLT_EPSILON) return -1; - - impl->scale = factor; - impl->flag |= RenderUpdateFlag::Transform; - - return 0; + return impl->scale(factor); } @@ -261,12 +256,7 @@ int Shape::rotate(float degree) noexcept auto impl = pImpl.get(); assert(impl); - if (fabsf(degree - impl->rotate) <= FLT_EPSILON) return -1; - - impl->rotate = degree; - impl->flag |= RenderUpdateFlag::Transform; - - return 0; + return impl->rotate(degree); } @@ -275,13 +265,7 @@ int Shape::translate(float x, float y) noexcept auto impl = pImpl.get(); assert(impl); - if (fabsf(x - impl->x) <= FLT_EPSILON && fabsf(y - impl->y) <= FLT_EPSILON) return -1; - - impl->x = x; - impl->y = y; - impl->flag |= RenderUpdateFlag::Transform; - - return 0; + return impl->translate(x, y); } diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index 6b81dc0..e552a3b 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -38,13 +38,11 @@ struct Shape::Impl ShapeFill *fill = nullptr; ShapeStroke *stroke = nullptr; ShapePath *path = nullptr; + RenderTransform *transform = nullptr; uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a - float scale = 1; - float rotate = 0; - float x = 0; - float y = 0; - void *edata = nullptr; //engine data size_t flag = RenderUpdateFlag::None; + void *edata = nullptr; //engine data + Impl() : path(new ShapePath) { @@ -55,6 +53,7 @@ struct Shape::Impl if (path) delete(path); if (stroke) delete(stroke); if (fill) delete(fill); + if (transform) delete(transform); } bool dispose(Shape& shape, RenderMethod& renderer) @@ -70,16 +69,13 @@ struct Shape::Impl bool update(Shape& shape, RenderMethod& renderer) { if (flag & RenderUpdateFlag::Transform) { - RenderTransform transform; - transform.identity(); - transform.rotate(rotate); - transform.scale(scale); - transform.translate(x, y); - edata = renderer.prepare(shape, edata, &transform, static_cast(flag)); - } else { - edata = renderer.prepare(shape, edata, nullptr, static_cast(flag)); + assert(transform); + if (!transform->update()) { + delete(transform); + transform = nullptr; + } } - + edata = renderer.prepare(shape, edata, transform, static_cast(flag)); flag = RenderUpdateFlag::None; if (edata) return true; @@ -91,6 +87,52 @@ struct Shape::Impl assert(path); return path->bounds(x, y, w, h); } + + bool scale(float factor) + { + if (transform) { + if (fabsf(factor - transform->factor) <= FLT_EPSILON) return -1; + } else { + if (fabsf(factor) <= FLT_EPSILON) return -1; + transform = new RenderTransform(); + assert(transform); + } + transform->factor = factor; + flag |= RenderUpdateFlag::Transform; + + return 0; + } + + bool rotate(float degree) + { + if (transform) { + if (fabsf(degree - transform->degree) <= FLT_EPSILON) return -1; + } else { + if (fabsf(degree) <= FLT_EPSILON) return -1; + transform = new RenderTransform(); + assert(transform); + } + transform->degree = degree; + flag |= RenderUpdateFlag::Transform; + + return 0; + } + + bool translate(float x, float y) + { + if (transform) { + if (fabsf(x - transform->x) <= FLT_EPSILON && fabsf(y - transform->y) <= FLT_EPSILON) return -1; + } else { + if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON) return -1; + transform = new RenderTransform(); + assert(transform); + } + transform->x = x; + transform->y = y; + flag |= RenderUpdateFlag::Transform; + + return 0; + } }; #endif //_TVG_SHAPE_IMPL_H_ \ No newline at end of file -- 2.7.4 From fdbf42f478137cf4c4caf4b4345cc7b2592d525c Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 17 May 2020 16:50:19 +0900 Subject: [PATCH 02/16] common: support Scene Transform this contains testSceneTransform example Change-Id: I460b05dc8bc4a842e26e950c800c5c35f8d3da7f --- .gitignore | 1 + src/lib/gl_engine/tvgGlRenderer.cpp | 2 +- src/lib/gl_engine/tvgGlRenderer.h | 2 +- src/lib/sw_engine/tvgSwCommon.h | 2 +- src/lib/sw_engine/tvgSwRenderer.cpp | 2 +- src/lib/sw_engine/tvgSwRenderer.h | 2 +- src/lib/sw_engine/tvgSwShape.cpp | 2 +- src/lib/tvgCanvasImpl.h | 4 +- src/lib/tvgRenderCommon.h | 139 ++++++++++++++++------------- src/lib/tvgScene.cpp | 17 +++- src/lib/tvgSceneImpl.h | 81 ++++++++++++++++- src/lib/tvgShapeImpl.h | 13 ++- test/makefile | 1 + test/testSceneTransform.cpp | 168 ++++++++++++++++++++++++++++++++++++ 14 files changed, 358 insertions(+), 78 deletions(-) create mode 100644 test/testSceneTransform.cpp diff --git a/.gitignore b/.gitignore index 2917573..6210eb8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ testUpdate testDirectUpdate testScene testTransform +testSceneTransform diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 792e524..48a0ee5 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -65,7 +65,7 @@ bool GlRenderer::dispose(const Shape& shape, void *data) } -void* GlRenderer::prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) +void* GlRenderer::prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) { //prepare shape data GlShape* sdata = static_cast(data); diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index 4d6ba79..557f9e7 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -23,7 +23,7 @@ namespace tvg class GlRenderer : public RenderMethod { public: - void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool render(const Shape& shape, void *data) override; bool clear() override; diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 62a6b29..3068b9b 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -96,7 +96,7 @@ 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 shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform); +void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderMatrix& transform); SwRleData* rleRender(const SwShape& sdata, const SwSize& clip); 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/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 36e367b..2d97445 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -81,7 +81,7 @@ bool SwRenderer::dispose(const Shape& shape, void *data) return true; } -void* SwRenderer::prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) +void* SwRenderer::prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) { //prepare shape data SwShape* sdata = static_cast(data); diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 3dfb076..4c0a7ba 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -22,7 +22,7 @@ class SwRenderer : public RenderMethod public: Surface surface; - void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool render(const Shape& shape, void *data) override; bool target(uint32_t* buffer, size_t stride, size_t w, size_t h); diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index d7229dc..efad932 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -215,7 +215,7 @@ void _deleteRle(SwShape& sdata) /* External Class Implementation */ /************************************************************************/ -void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform) +void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderMatrix& transform) { auto outline = sdata.outline; assert(outline); diff --git a/src/lib/tvgCanvasImpl.h b/src/lib/tvgCanvasImpl.h index 20a5efd..afa2a0b 100644 --- a/src/lib/tvgCanvasImpl.h +++ b/src/lib/tvgCanvasImpl.h @@ -71,9 +71,9 @@ struct Canvas::Impl for(auto paint: paints) { if (auto scene = dynamic_cast(paint)) { - if (!SCENE_IMPL->update(*renderer)) return -1; + if (!SCENE_IMPL->update(*renderer, nullptr)) return -1; } else if (auto shape = dynamic_cast(paint)) { - if (!SHAPE_IMPL->update(*shape, *renderer)) return -1; + if (!SHAPE_IMPL->update(*shape, *renderer, nullptr)) return -1; } } return 0; diff --git a/src/lib/tvgRenderCommon.h b/src/lib/tvgRenderCommon.h index a3bfcb6..0ad1d55 100644 --- a/src/lib/tvgRenderCommon.h +++ b/src/lib/tvgRenderCommon.h @@ -30,13 +30,85 @@ struct Surface enum RenderUpdateFlag {None = 0, Path = 1, Fill = 2, Transform = 4, All = 8}; -struct RenderTransform +struct RenderMatrix { //3x3 Matrix Elements float e11, e12, e13; float e21, e22, e23; float e31, e32, e33; + static void rotate(RenderMatrix* out, float degree) + { + constexpr auto PI = 3.141592f; + + if (fabsf(degree) < FLT_EPSILON) return; + + auto radian = degree / 180.0f * PI; + auto cosVal = cosf(radian); + auto sinVal = sinf(radian); + + auto t11 = out->e11 * cosVal + out->e12 * sinVal; + auto t12 = out->e11 * -sinVal + out->e12 * cosVal; + auto t21 = out->e21 * cosVal + out->e22 * sinVal; + auto t22 = out->e21 * -sinVal + out->e22 * cosVal; + auto t31 = out->e31 * cosVal + out->e32 * sinVal; + auto t32 = out->e31 * -sinVal + out->e32 * cosVal; + + out->e11 = t11; + out->e12 = t12; + out->e21 = t21; + out->e22 = t22; + out->e31 = t31; + out->e32 = t32; + } + + static void scale(RenderMatrix* out, float factor) + { + out->e11 *= factor; + out->e22 *= factor; + out->e33 *= factor; + } + + static void identity(RenderMatrix* out) + { + out->e11 = 1.0f; + out->e12 = 0.0f; + out->e13 = 0.0f; + out->e21 = 0.0f; + out->e22 = 1.0f; + out->e23 = 0.0f; + out->e31 = 0.0f; + out->e32 = 0.0f; + out->e33 = 1.0f; + } + + static void translate(RenderMatrix* out, float x, float y) + { + out->e31 += x; + out->e32 += y; + } + + static void multiply(const RenderMatrix* lhs, const RenderMatrix* rhs, RenderMatrix* out) + { + assert(lhs && rhs && out); + + out->e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31; + out->e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32; + out->e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33; + + out->e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31; + out->e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32; + out->e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33; + + out->e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31; + out->e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32; + out->e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33; + } +}; + +struct RenderTransform +{ + RenderMatrix m; float x = 0.0f; float y = 0.0f; float degree = 0.0f; //rotation degree @@ -44,74 +116,19 @@ struct RenderTransform bool update() { - constexpr auto PI = 3.141592f; - //Init Status if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON && fabsf(degree) <= FLT_EPSILON && fabsf(factor - 1) <= FLT_EPSILON) { return false; } - //identity - e11 = 1.0f; - e12 = 0.0f; - e13 = 0.0f; - e21 = 0.0f; - e22 = 1.0f; - e23 = 0.0f; - e31 = 0.0f; - e32 = 0.0f; - e33 = 1.0f; - - //rotation - if (fabsf(degree) > FLT_EPSILON) { - auto radian = degree / 180.0f * PI; - auto cosVal = cosf(radian); - auto sinVal = sinf(radian); - - auto t11 = e11 * cosVal + e12 * sinVal; - auto t12 = e11 * -sinVal + e12 * cosVal; - auto t21 = e21 * cosVal + e22 * sinVal; - auto t22 = e21 * -sinVal + e22 * cosVal; - auto t31 = e31 * cosVal + e32 * sinVal; - auto t32 = e31 * -sinVal + e32 * cosVal; - - e11 = t11; - e12 = t12; - e21 = t21; - e22 = t22; - e31 = t31; - e32 = t32; - } - - //scale - e11 *= factor; - e22 *= factor; - e33 *= factor; - - //translate - e31 += x; - e32 += y; + RenderMatrix::identity(&m); + RenderMatrix::scale(&m, factor); + RenderMatrix::rotate(&m, degree); + RenderMatrix::translate(&m, x, y); return true; } - - RenderTransform& operator*=(const RenderTransform rhs) - { - e11 = e11 * rhs.e11 + e12 * rhs.e21 + e13 * rhs.e31; - e12 = e11 * rhs.e12 + e12 * rhs.e22 + e13 * rhs.e32; - e13 = e11 * rhs.e13 + e12 * rhs.e23 + e13 * rhs.e33; - - e21 = e21 * rhs.e11 + e22 * rhs.e21 + e23 * rhs.e31; - e22 = e21 * rhs.e12 + e22 * rhs.e22 + e23 * rhs.e32; - e23 = e21 * rhs.e13 + e22 * rhs.e23 + e23 * rhs.e33; - - e31 = e31 * rhs.e11 + e32 * rhs.e21 + e33 * rhs.e31; - e32 = e31 * rhs.e12 + e32 * rhs.e22 + e33 * rhs.e32; - e33 = e31 * rhs.e13 + e32 * rhs.e23 + e33 * rhs.e33; - - return *this; - } }; @@ -119,7 +136,7 @@ class RenderMethod { public: virtual ~RenderMethod() {} - virtual void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) = 0; + virtual void* prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) = 0; virtual bool dispose(const Shape& shape, void *data) = 0; virtual bool render(const Shape& shape, void *data) = 0; virtual bool clear() = 0; diff --git a/src/lib/tvgScene.cpp b/src/lib/tvgScene.cpp index 4b2a613..dba9637 100644 --- a/src/lib/tvgScene.cpp +++ b/src/lib/tvgScene.cpp @@ -64,21 +64,30 @@ int Scene::reserve(size_t size) noexcept } -int Scene::scale(float scaleFacator) noexcept +int Scene::scale(float factor) noexcept { - return 0; + auto impl = pImpl.get(); + assert(impl); + + return impl->scale(factor); } int Scene::rotate(float degree) noexcept { - return 0; + auto impl = pImpl.get(); + assert(impl); + + return impl->rotate(degree); } int Scene::translate(float x, float y) noexcept { - return 0; + auto impl = pImpl.get(); + assert(impl); + + return impl->translate(x, y); } diff --git a/src/lib/tvgSceneImpl.h b/src/lib/tvgSceneImpl.h index a2ce90d..4def223 100644 --- a/src/lib/tvgSceneImpl.h +++ b/src/lib/tvgSceneImpl.h @@ -26,11 +26,14 @@ struct Scene::Impl { vector paints; + RenderTransform *transform = nullptr; + size_t flag = RenderUpdateFlag::None; ~Impl() { //Are you sure clear() prior to this? assert(paints.empty()); + if (transform) delete(transform); } bool clear(RenderMethod& renderer) @@ -48,18 +51,44 @@ struct Scene::Impl return true; } - bool update(RenderMethod &renderer) + bool updateInternal(RenderMethod &renderer, const RenderMatrix* transform, size_t flag) { for(auto paint: paints) { if (auto scene = dynamic_cast(paint)) { - if (!SCENE_IMPL->update(renderer)) return false; + if (!SCENE_IMPL->update(renderer, transform, flag)) return false; } else if (auto shape = dynamic_cast(paint)) { - if (!SHAPE_IMPL->update(*shape, renderer)) return false; + if (!SHAPE_IMPL->update(*shape, renderer, transform, flag)) return false; } } return true; } + bool update(RenderMethod &renderer, const RenderMatrix* pTransform = nullptr, size_t pFlag = 0) + { + if (flag & RenderUpdateFlag::Transform) { + assert(transform); + if (!transform->update()) { + delete(transform); + transform = nullptr; + } + } + + auto ret = true; + + if (transform && pTransform) { + RenderMatrix outTransform; + RenderMatrix::multiply(pTransform, &transform->m, &outTransform); + ret = updateInternal(renderer, &outTransform, pFlag | flag); + } else { + auto outTransform = pTransform ? pTransform : &transform->m; + ret = updateInternal(renderer, outTransform, pFlag | flag); + } + + flag = RenderUpdateFlag::None; + + return ret; + } + bool render(RenderMethod &renderer) { for(auto paint: paints) { @@ -94,6 +123,52 @@ struct Scene::Impl } return true; } + + bool scale(float factor) + { + if (transform) { + if (fabsf(factor - transform->factor) <= FLT_EPSILON) return -1; + } else { + if (fabsf(factor) <= FLT_EPSILON) return -1; + transform = new RenderTransform(); + assert(transform); + } + transform->factor = factor; + flag |= RenderUpdateFlag::Transform; + + return 0; + } + + bool rotate(float degree) + { + if (transform) { + if (fabsf(degree - transform->degree) <= FLT_EPSILON) return -1; + } else { + if (fabsf(degree) <= FLT_EPSILON) return -1; + transform = new RenderTransform(); + assert(transform); + } + transform->degree = degree; + flag |= RenderUpdateFlag::Transform; + + return 0; + } + + bool translate(float x, float y) + { + if (transform) { + if (fabsf(x - transform->x) <= FLT_EPSILON && fabsf(y - transform->y) <= FLT_EPSILON) return -1; + } else { + if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON) return -1; + transform = new RenderTransform(); + assert(transform); + } + transform->x = x; + transform->y = y; + flag |= RenderUpdateFlag::Transform; + + return 0; + } }; #endif //_TVG_SCENE_IMPL_H_ \ No newline at end of file diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index e552a3b..3087426 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -66,7 +66,7 @@ struct Shape::Impl return renderer.render(shape, edata); } - bool update(Shape& shape, RenderMethod& renderer) + bool update(Shape& shape, RenderMethod& renderer, const RenderMatrix* pTransform = nullptr, size_t pFlag = 0) { if (flag & RenderUpdateFlag::Transform) { assert(transform); @@ -75,7 +75,16 @@ struct Shape::Impl transform = nullptr; } } - edata = renderer.prepare(shape, edata, transform, static_cast(flag)); + + if (transform && pTransform) { + RenderMatrix outTransform; + RenderMatrix::multiply(pTransform, &transform->m, &outTransform); + edata = renderer.prepare(shape, edata, &outTransform, static_cast(pFlag | flag)); + } else { + auto outTransform = pTransform ? pTransform : &transform->m; + edata = renderer.prepare(shape, edata, outTransform, static_cast(pFlag | flag)); + } + flag = RenderUpdateFlag::None; if (edata) return true; diff --git a/test/makefile b/test/makefile index e437084..50710e0 100644 --- a/test/makefile +++ b/test/makefile @@ -9,3 +9,4 @@ all: 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` diff --git a/test/testSceneTransform.cpp b/test/testSceneTransform.cpp new file mode 100644 index 0000000..622dafc --- /dev/null +++ b/test/testSceneTransform.cpp @@ -0,0 +1,168 @@ +#include +#include + +using namespace std; + +#define WIDTH 800 +#define HEIGHT 800 + +static uint32_t buffer[WIDTH * HEIGHT]; +unique_ptr canvas = nullptr; +tvg::Scene* pScene1 = nullptr; +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); + + //Create a Scene + auto scene = tvg::Scene::gen(); + pScene1 = scene.get(); + scene->reserve(3); //reserve 3 shape nodes (optional) + + //Prepare Round Rectangle + 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 + scene->push(move(shape1)); + + //Prepare Circle + 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 + scene->push(move(shape2)); + + //Prepare Ellipse + 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 + scene->push(move(shape3)); + + scene->translate(350, 350); + scene->scale(0.7); + + //Create another Scene + auto scene2 = tvg::Scene::gen(); + pScene2 = scene2.get(); + scene2->reserve(2); //reserve 2 shape nodes (optional) + +#if 0 + //Star + auto shape4 = tvg::Shape::gen(); + + //Appends Paths + shape4->moveTo(0, -114.5); + shape4->lineTo(54, -5.5); + shape4->lineTo(175, 11.5); + shape4->lineTo(88, 95.5); + shape4->lineTo(108, 216.5); + shape4->lineTo(0, 160.5); + shape4->lineTo(-102, 216.5); + shape4->lineTo(-87, 96.5); + shape4->lineTo(-173, 12.5); + shape4->lineTo(-53, -5.5); + shape4->close(); + shape4->fill(0, 0, 127, 127); + + float x, y, w, h; + shape4->bounds(x, y, w, h); + scene2->push(move(shape4)); + + //Circle + auto shape5 = tvg::Shape::gen(); + + auto cx = -150.0f; + auto cy = -150.0f; + auto radius = 125.0f; + auto halfRadius = radius * 0.552284f; + + //Append Paths + shape5->moveTo(cx, cy - radius); + shape5->cubicTo(cx + halfRadius, cy - radius, cx + radius, cy - halfRadius, cx + radius, cy); + shape5->cubicTo(cx + radius, cy + halfRadius, cx + halfRadius, cy + radius, cx, cy+ radius); + shape5->cubicTo(cx - halfRadius, cy + radius, cx - radius, cy + halfRadius, cx - radius, cy); + shape5->cubicTo(cx - radius, cy - halfRadius, cx - halfRadius, cy - radius, cx, cy - radius); + shape5->fill(127, 0, 0, 127); + scene2->push(move(shape5)); + + scene2->translate(300, 300); + + //Push scene2 onto the scene + scene->push(move(scene2)); +#endif + //Draw the Scene onto the Canvas + canvas->push(move(scene)); + + canvas->draw(); + canvas->sync(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +} + +void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progress) +{ + /* Update scene directly. + You can update only necessary properties of this scene, + while retaining other properties. */ + + pScene1->rotate(360 * progress); + + //Update shape for drawing (this may work asynchronously) + canvas->update(pScene1); + + //Draw Next frames + canvas->draw(); + canvas->sync(); + + //Update Efl Canvas + Eo* img = (Eo*) effect; + evas_object_image_data_update_add(img, 0, 0, WIDTH, HEIGHT); +} + +void +win_del(void *data, Evas_Object *o, void *ev) +{ + elm_exit(); +} + +int main(int argc, char **argv) +{ + //Initialize TizenVG Engine + tvg::Engine::init(); + + 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_Transit *transit = elm_transit_add(); + elm_transit_effect_add(transit, transit_cb, img, nullptr); + elm_transit_duration_set(transit, 2); + elm_transit_repeat_times_set(transit, -1); + elm_transit_go(transit); + + elm_run(); + elm_shutdown(); + + //Terminate TizenVG Engine + tvg::Engine::term(); +} -- 2.7.4 From 2f833298c3b68d41771edf8093da4fb39184bcad Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 17 May 2020 20:34:58 +0900 Subject: [PATCH 03/16] test: update transform sample Change-Id: I7f1416d9e32b241e50b7e2132764cac28d590a93 --- test/testTransform.cpp | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/test/testTransform.cpp b/test/testTransform.cpp index faab048..48d496b 100644 --- a/test/testTransform.cpp +++ b/test/testTransform.cpp @@ -9,6 +9,8 @@ using namespace std; static uint32_t buffer[WIDTH * HEIGHT]; unique_ptr canvas = nullptr; tvg::Shape* pShape = nullptr; +tvg::Shape* pShape2 = nullptr; +tvg::Shape* pShape3 = nullptr; void tvgtest() { @@ -16,7 +18,7 @@ void tvgtest() canvas = tvg::SwCanvas::gen(); canvas->target(buffer, WIDTH, WIDTH, HEIGHT); - //Shape + //Shape1 auto shape = tvg::Shape::gen(); /* Acquire shape pointer to access it again. @@ -28,10 +30,28 @@ void tvgtest() shape->appendCircle(115, 100, 100, 100); shape->appendCircle(115, 200, 170, 100); shape->fill(255, 255, 255, 255); - shape->translate(285, 300); - + shape->translate(385, 400); canvas->push(move(shape)); + //Shape2 + auto shape2 = tvg::Shape::gen(); + pShape2 = shape2.get(); + shape2->appendRect(-50, -50, 100, 100, 0); + shape2->fill(0, 255, 255, 255); + shape2->translate(400, 400); + canvas->push(move(shape2)); + + //Shape3 + auto shape3 = tvg::Shape::gen(); + pShape3 = shape3.get(); + + /* Look, how shape3's origin is different with shape2 + The center of the shape is the anchor point for transformation. */ + shape3->appendRect(100, 100, 150, 50, 20); + shape3->fill(255, 0, 255, 255); + shape3->translate(400, 400); + canvas->push(move(shape3)); + //Draw first frame canvas->draw(); canvas->sync(); @@ -43,12 +63,23 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres You can update only necessary properties of this shape, while retaining other properties. */ + //Update Shape1 pShape->scale(1 - 0.75 * progress); pShape->rotate(360 * progress); //Update shape for drawing (this may work asynchronously) canvas->update(pShape); + //Update Shape2 + pShape2->rotate(360 * progress); + pShape2->translate(400 + progress * 300, 400); + canvas->update(pShape2); + + //Update Shape2 + pShape3->rotate(-360 * progress); + pShape3->scale(0.5 + progress); + canvas->update(pShape3); + //Draw Next frames canvas->draw(); canvas->sync(); -- 2.7.4 From 1f6da2d0b6779b2d3a4779d42d881bad9bc5dd09 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 17 May 2020 20:52:58 +0900 Subject: [PATCH 04/16] test: update samples fix some broken test code. Change-Id: I42b0dd8b4c599ea59e0860a40b828936dca1e54d --- test/testDirectUpdate.cpp | 4 ++-- test/testTransform.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/testDirectUpdate.cpp b/test/testDirectUpdate.cpp index 735231a..799d135 100644 --- a/test/testDirectUpdate.cpp +++ b/test/testDirectUpdate.cpp @@ -25,9 +25,9 @@ void tvgtest() shape->appendRect(-100, -100, 200, 200, 0); - //fill and rotate properties will be retained + //fill property will be retained shape->fill(127, 255, 255, 255); - shape->rotate(45); + canvas->push(move(shape)); //Draw first frame diff --git a/test/testTransform.cpp b/test/testTransform.cpp index 48d496b..0a126cc 100644 --- a/test/testTransform.cpp +++ b/test/testTransform.cpp @@ -75,7 +75,7 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres pShape2->translate(400 + progress * 300, 400); canvas->update(pShape2); - //Update Shape2 + //Update Shape3 pShape3->rotate(-360 * progress); pShape3->scale(0.5 + progress); canvas->update(pShape3); -- 2.7.4 From a5f15a588c5dda01456b6848380aff0547bb541c Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sun, 17 May 2020 21:33:56 +0900 Subject: [PATCH 05/16] common scene: complete scene tranfsormation feature. Each scene could conserve its own transformation, the origin could be the parent Paint (if they were beloned to) Thus, you can compose the multi-tranformed scene group which simultenoulsly working in own spaces. See testTransform and testSceneTransform, how they work. Change-Id: I51deb4d66f8fcd024f8dc7a1e1af57c398a9d7fe --- src/lib/gl_engine/tvgGlRenderer.cpp | 2 +- src/lib/gl_engine/tvgGlRenderer.h | 2 +- src/lib/sw_engine/tvgSwCommon.h | 2 +- src/lib/sw_engine/tvgSwRenderer.cpp | 2 +- src/lib/sw_engine/tvgSwRenderer.h | 2 +- src/lib/sw_engine/tvgSwShape.cpp | 2 +- src/lib/tvgRenderCommon.h | 142 ++++++++++++++++-------------------- src/lib/tvgSceneImpl.h | 9 +-- src/lib/tvgShapeImpl.h | 7 +- test/testSceneTransform.cpp | 27 +++---- 10 files changed, 89 insertions(+), 108 deletions(-) diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 48a0ee5..792e524 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -65,7 +65,7 @@ bool GlRenderer::dispose(const Shape& shape, void *data) } -void* GlRenderer::prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) +void* GlRenderer::prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) { //prepare shape data GlShape* sdata = static_cast(data); diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index 557f9e7..4d6ba79 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -23,7 +23,7 @@ namespace tvg class GlRenderer : public RenderMethod { public: - void* prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool render(const Shape& shape, void *data) override; bool clear() override; diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 3068b9b..62a6b29 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -96,7 +96,7 @@ 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 shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderMatrix& transform); +void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform); SwRleData* rleRender(const SwShape& sdata, const SwSize& clip); 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/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 2d97445..36e367b 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -81,7 +81,7 @@ bool SwRenderer::dispose(const Shape& shape, void *data) return true; } -void* SwRenderer::prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) +void* SwRenderer::prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) { //prepare shape data SwShape* sdata = static_cast(data); diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 4c0a7ba..3dfb076 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -22,7 +22,7 @@ class SwRenderer : public RenderMethod public: Surface surface; - void* prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) override; + void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool render(const Shape& shape, void *data) override; bool target(uint32_t* buffer, size_t stride, size_t w, size_t h); diff --git a/src/lib/sw_engine/tvgSwShape.cpp b/src/lib/sw_engine/tvgSwShape.cpp index efad932..d7229dc 100644 --- a/src/lib/sw_engine/tvgSwShape.cpp +++ b/src/lib/sw_engine/tvgSwShape.cpp @@ -215,7 +215,7 @@ void _deleteRle(SwShape& sdata) /* External Class Implementation */ /************************************************************************/ -void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderMatrix& transform) +void shapeTransformOutline(const Shape& shape, SwShape& sdata, const RenderTransform& transform) { auto outline = sdata.outline; assert(outline); diff --git a/src/lib/tvgRenderCommon.h b/src/lib/tvgRenderCommon.h index 0ad1d55..f40639d 100644 --- a/src/lib/tvgRenderCommon.h +++ b/src/lib/tvgRenderCommon.h @@ -30,85 +30,13 @@ struct Surface enum RenderUpdateFlag {None = 0, Path = 1, Fill = 2, Transform = 4, All = 8}; -struct RenderMatrix +struct RenderTransform { //3x3 Matrix Elements float e11, e12, e13; float e21, e22, e23; float e31, e32, e33; - static void rotate(RenderMatrix* out, float degree) - { - constexpr auto PI = 3.141592f; - - if (fabsf(degree) < FLT_EPSILON) return; - - auto radian = degree / 180.0f * PI; - auto cosVal = cosf(radian); - auto sinVal = sinf(radian); - - auto t11 = out->e11 * cosVal + out->e12 * sinVal; - auto t12 = out->e11 * -sinVal + out->e12 * cosVal; - auto t21 = out->e21 * cosVal + out->e22 * sinVal; - auto t22 = out->e21 * -sinVal + out->e22 * cosVal; - auto t31 = out->e31 * cosVal + out->e32 * sinVal; - auto t32 = out->e31 * -sinVal + out->e32 * cosVal; - - out->e11 = t11; - out->e12 = t12; - out->e21 = t21; - out->e22 = t22; - out->e31 = t31; - out->e32 = t32; - } - - static void scale(RenderMatrix* out, float factor) - { - out->e11 *= factor; - out->e22 *= factor; - out->e33 *= factor; - } - - static void identity(RenderMatrix* out) - { - out->e11 = 1.0f; - out->e12 = 0.0f; - out->e13 = 0.0f; - out->e21 = 0.0f; - out->e22 = 1.0f; - out->e23 = 0.0f; - out->e31 = 0.0f; - out->e32 = 0.0f; - out->e33 = 1.0f; - } - - static void translate(RenderMatrix* out, float x, float y) - { - out->e31 += x; - out->e32 += y; - } - - static void multiply(const RenderMatrix* lhs, const RenderMatrix* rhs, RenderMatrix* out) - { - assert(lhs && rhs && out); - - out->e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31; - out->e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32; - out->e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33; - - out->e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31; - out->e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32; - out->e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33; - - out->e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31; - out->e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32; - out->e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33; - } -}; - -struct RenderTransform -{ - RenderMatrix m; float x = 0.0f; float y = 0.0f; float degree = 0.0f; //rotation degree @@ -116,19 +44,77 @@ struct RenderTransform bool update() { + constexpr auto PI = 3.141592f; + //Init Status if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON && fabsf(degree) <= FLT_EPSILON && fabsf(factor - 1) <= FLT_EPSILON) { return false; } - RenderMatrix::identity(&m); - RenderMatrix::scale(&m, factor); - RenderMatrix::rotate(&m, degree); - RenderMatrix::translate(&m, x, y); + //identity + e11 = 1.0f; + e12 = 0.0f; + e13 = 0.0f; + e21 = 0.0f; + e22 = 1.0f; + e23 = 0.0f; + e31 = 0.0f; + e32 = 0.0f; + e33 = 1.0f; + + //scale + e11 *= factor; + e22 *= factor; + e33 *= factor; + + //rotation + if (fabsf(degree) > FLT_EPSILON) { + auto radian = degree / 180.0f * PI; + auto cosVal = cosf(radian); + auto sinVal = sinf(radian); + + auto t11 = e11 * cosVal + e12 * sinVal; + auto t12 = e11 * -sinVal + e12 * cosVal; + auto t21 = e21 * cosVal + e22 * sinVal; + auto t22 = e21 * -sinVal + e22 * cosVal; + auto t31 = e31 * cosVal + e32 * sinVal; + auto t32 = e31 * -sinVal + e32 * cosVal; + + e11 = t11; + e12 = t12; + e21 = t21; + e22 = t22; + e31 = t31; + e32 = t32; + } + + e31 += x; + e32 += y; return true; } + + RenderTransform() + { + } + + RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs) + { + assert(lhs && rhs); + + auto dx = rhs->x * lhs->factor; + auto dy = rhs->y * lhs->factor; + auto tx = dx * lhs->e11 + dy * lhs->e12 + lhs->e13; + auto ty = dx * lhs->e21 + dy * lhs->e22 + lhs->e23; + + x = lhs->x + tx; + y = lhs->y + ty; + degree = lhs->degree + rhs->degree; + factor = lhs->factor * rhs->factor; + + update(); + } }; @@ -136,7 +122,7 @@ class RenderMethod { public: virtual ~RenderMethod() {} - virtual void* prepare(const Shape& shape, void* data, const RenderMatrix* transform, RenderUpdateFlag flags) = 0; + virtual void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) = 0; virtual bool dispose(const Shape& shape, void *data) = 0; virtual bool render(const Shape& shape, void *data) = 0; virtual bool clear() = 0; diff --git a/src/lib/tvgSceneImpl.h b/src/lib/tvgSceneImpl.h index 4def223..abd29e6 100644 --- a/src/lib/tvgSceneImpl.h +++ b/src/lib/tvgSceneImpl.h @@ -51,7 +51,7 @@ struct Scene::Impl return true; } - bool updateInternal(RenderMethod &renderer, const RenderMatrix* transform, size_t flag) + bool updateInternal(RenderMethod &renderer, const RenderTransform* transform, size_t flag) { for(auto paint: paints) { if (auto scene = dynamic_cast(paint)) { @@ -63,7 +63,7 @@ struct Scene::Impl return true; } - bool update(RenderMethod &renderer, const RenderMatrix* pTransform = nullptr, size_t pFlag = 0) + bool update(RenderMethod &renderer, const RenderTransform* pTransform = nullptr, size_t pFlag = 0) { if (flag & RenderUpdateFlag::Transform) { assert(transform); @@ -76,11 +76,10 @@ struct Scene::Impl auto ret = true; if (transform && pTransform) { - RenderMatrix outTransform; - RenderMatrix::multiply(pTransform, &transform->m, &outTransform); + RenderTransform outTransform(pTransform, transform); ret = updateInternal(renderer, &outTransform, pFlag | flag); } else { - auto outTransform = pTransform ? pTransform : &transform->m; + auto outTransform = pTransform ? pTransform : transform; ret = updateInternal(renderer, outTransform, pFlag | flag); } diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index 3087426..b7f9927 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -66,7 +66,7 @@ struct Shape::Impl return renderer.render(shape, edata); } - bool update(Shape& shape, RenderMethod& renderer, const RenderMatrix* pTransform = nullptr, size_t pFlag = 0) + bool update(Shape& shape, RenderMethod& renderer, const RenderTransform* pTransform = nullptr, size_t pFlag = 0) { if (flag & RenderUpdateFlag::Transform) { assert(transform); @@ -77,11 +77,10 @@ struct Shape::Impl } if (transform && pTransform) { - RenderMatrix outTransform; - RenderMatrix::multiply(pTransform, &transform->m, &outTransform); + RenderTransform outTransform(pTransform, transform); edata = renderer.prepare(shape, edata, &outTransform, static_cast(pFlag | flag)); } else { - auto outTransform = pTransform ? pTransform : &transform->m; + auto outTransform = pTransform ? pTransform : transform; edata = renderer.prepare(shape, edata, outTransform, static_cast(pFlag | flag)); } diff --git a/test/testSceneTransform.cpp b/test/testSceneTransform.cpp index 622dafc..38bfb0f 100644 --- a/test/testSceneTransform.cpp +++ b/test/testSceneTransform.cpp @@ -20,39 +20,38 @@ void tvgtest() canvas = tvg::SwCanvas::gen(); canvas->target(buffer, WIDTH, WIDTH, HEIGHT); - //Create a Scene + //Create a Scene1 auto scene = tvg::Scene::gen(); pScene1 = scene.get(); scene->reserve(3); //reserve 3 shape nodes (optional) - //Prepare Round Rectangle + //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 scene->push(move(shape1)); - //Prepare Circle + //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 scene->push(move(shape2)); - //Prepare Ellipse + //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 scene->push(move(shape3)); scene->translate(350, 350); - scene->scale(0.7); + scene->scale(0.5); - //Create another Scene + //Create Scene2 auto scene2 = tvg::Scene::gen(); pScene2 = scene2.get(); scene2->reserve(2); //reserve 2 shape nodes (optional) -#if 0 - //Star + //Star (Scene2) auto shape4 = tvg::Shape::gen(); //Appends Paths @@ -68,17 +67,14 @@ void tvgtest() shape4->lineTo(-53, -5.5); shape4->close(); shape4->fill(0, 0, 127, 127); - - float x, y, w, h; - shape4->bounds(x, y, w, h); scene2->push(move(shape4)); - //Circle + //Circle (Scene2) auto shape5 = tvg::Shape::gen(); auto cx = -150.0f; auto cy = -150.0f; - auto radius = 125.0f; + auto radius = 100.0f; auto halfRadius = radius * 0.552284f; //Append Paths @@ -90,11 +86,11 @@ void tvgtest() shape5->fill(127, 0, 0, 127); scene2->push(move(shape5)); - scene2->translate(300, 300); + scene2->translate(500, 350); //Push scene2 onto the scene scene->push(move(scene2)); -#endif + //Draw the Scene onto the Canvas canvas->push(move(scene)); @@ -112,6 +108,7 @@ void transit_cb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progres while retaining other properties. */ pScene1->rotate(360 * progress); + pScene2->rotate(360 * progress); //Update shape for drawing (this may work asynchronously) canvas->update(pScene1); -- 2.7.4 From 19999e7abd146b864e590cd1c4054c13f60fa439 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 22 May 2020 16:18:32 +0900 Subject: [PATCH 06/16] common stroke: implement stroke interfaces. Change-Id: I28fe5d5df4cde6640b143e67e241c6afc9c6b1fe --- inc/tizenvg.h | 8 +++++ src/lib/tvgRenderCommon.h | 2 +- src/lib/tvgShape.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib/tvgShapeImpl.h | 61 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) diff --git a/inc/tizenvg.h b/inc/tizenvg.h index 98b6faf..bfaa960 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -136,6 +136,10 @@ public: int appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept; int appendPath(const PathCommand* cmds, size_t cmdCnt, const Point* pts, size_t ptsCnt) noexcept; + int stroke(size_t 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 fill(size_t r, size_t g, size_t b, size_t a) noexcept; int rotate(float degree) noexcept override; @@ -145,6 +149,10 @@ public: size_t pathCommands(const PathCommand** cmds) const noexcept; size_t pathCoords(const Point** pts) const noexcept; int fill(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept; + size_t stroke() const noexcept; + int stroke(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept; + size_t stroke(const size_t** dashPattern) const noexcept; + int bounds(float&x, float& y, float& w, float& h) const noexcept override; static std::unique_ptr gen() noexcept; diff --git a/src/lib/tvgRenderCommon.h b/src/lib/tvgRenderCommon.h index f40639d..61cd655 100644 --- a/src/lib/tvgRenderCommon.h +++ b/src/lib/tvgRenderCommon.h @@ -28,7 +28,7 @@ struct Surface size_t w, h; }; -enum RenderUpdateFlag {None = 0, Path = 1, Fill = 2, Transform = 4, All = 8}; +enum RenderUpdateFlag {None = 0, Path = 1, Fill = 2, Stroke = 4, Transform = 8, All = 16}; struct RenderTransform { diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index 432a8a6..342f456 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -280,4 +280,79 @@ int Shape::bounds(float& x, float& y, float& w, float& h) const noexcept } +int Shape::stroke(size_t width) noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + if (!impl->strokeWidth(width)) return -1; + + return 0; +} + + +size_t Shape::stroke() const noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + if (!impl->stroke) return -1; + return impl->stroke->width; +} + + +int Shape::stroke(size_t r, size_t g, size_t b, size_t a) noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + if (!impl->strokeColor(r, g, b, a)) return -1; + + return 0; +} + + +int Shape::stroke(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + if (!impl->stroke) return -1; + + if (r) *r = impl->stroke->color[0]; + if (g) *g = impl->stroke->color[1]; + if (b) *b = impl->stroke->color[2]; + if (a) *a = impl->stroke->color[3]; + + return 0; +} + + +int Shape::stroke(const size_t* dashPattern, size_t cnt) noexcept +{ + if (cnt < 2 || !dashPattern) return -1; + + auto impl = pImpl.get(); + assert(impl); + + if (!impl->strokeDash(dashPattern, cnt)) return -1; + + return 0; +} + + +size_t Shape::stroke(const size_t** dashPattern) const noexcept +{ + assert(dashPattern); + + auto impl = pImpl.get(); + assert(impl); + + if (!impl->stroke) return 0; + + *dashPattern = impl->stroke->dashPattern; + return impl->stroke->dashCnt; +} + + #endif //_TVG_SHAPE_CPP_ diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index b7f9927..e2ba294 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -30,6 +30,15 @@ struct ShapeFill struct ShapeStroke { + size_t width = 0; + size_t color[4] = {0, 0, 0, 0}; + size_t* dashPattern = nullptr; + size_t dashCnt = 0; + + ~ShapeStroke() + { + if (dashPattern) free(dashPattern); + } }; @@ -141,6 +150,58 @@ struct Shape::Impl return 0; } + + bool strokeWidth(size_t width) + { + //TODO: Size Exception? + + if (!stroke) stroke = new ShapeStroke(); + assert(stroke); + + stroke->width = width; + flag |= RenderUpdateFlag::Stroke; + + return 0; + } + + bool strokeColor(size_t r, size_t g, size_t b, size_t a) + { + if (!stroke) stroke = new ShapeStroke(); + assert(stroke); + + stroke->color[0] = r; + stroke->color[1] = g; + stroke->color[2] = b; + stroke->color[3] = a; + + flag |= RenderUpdateFlag::Stroke; + + return 0; + } + + bool strokeDash(const size_t* pattern, size_t cnt) + { + assert(pattern); + + if (!stroke) stroke = new ShapeStroke(); + assert(stroke); + + if (stroke->dashCnt != cnt) { + if (stroke->dashPattern) free(stroke->dashPattern); + stroke->dashPattern = nullptr; + } + + if (!stroke->dashPattern) stroke->dashPattern = static_cast(malloc(sizeof(size_t) * cnt)); + assert(stroke->dashPattern); + + memcpy(stroke->dashPattern, pattern, cnt); + stroke->dashCnt = cnt; + + flag |= RenderUpdateFlag::Stroke; + + return 0; + + } }; #endif //_TVG_SHAPE_IMPL_H_ \ No newline at end of file -- 2.7.4 From a0521c83c3f56e9c022d5ed590d04f0d8e75baf6 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 22 May 2020 16:30:13 +0900 Subject: [PATCH 07/16] common paint: revise bounds interface. we don't use the reference style for user interfaces. Change-Id: Id70682bf8c2d8ea9ffab2ea6fb567eaa8639da60 --- inc/tizenvg.h | 6 +++--- src/lib/tvgScene.cpp | 7 +------ src/lib/tvgSceneImpl.h | 17 ++++++++++++++--- src/lib/tvgShape.cpp | 2 +- src/lib/tvgShapeImpl.h | 2 +- src/lib/tvgShapePath.h | 10 +++++----- 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/inc/tizenvg.h b/inc/tizenvg.h index bfaa960..dbdcd66 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -81,7 +81,7 @@ public: virtual int scale(float factor) = 0; virtual int translate(float x, float y) = 0; - virtual int bounds(float&x, float& y, float& w, float& h) const = 0; + virtual int bounds(float* x, float* y, float* w, float* h) const = 0; }; @@ -153,7 +153,7 @@ public: int stroke(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept; size_t stroke(const size_t** dashPattern) const noexcept; - int bounds(float&x, float& y, float& w, float& h) const noexcept override; + int bounds(float* x, float* y, float* w, float* h) const noexcept override; static std::unique_ptr gen() noexcept; @@ -183,7 +183,7 @@ public: int scale(float factor) noexcept override; int translate(float x, float y) noexcept override; - int bounds(float&x, float& y, float& w, float& h) const noexcept override; + int bounds(float* x, float* y, float* w, float* h) const noexcept override; static std::unique_ptr gen() noexcept; diff --git a/src/lib/tvgScene.cpp b/src/lib/tvgScene.cpp index dba9637..93a64b1 100644 --- a/src/lib/tvgScene.cpp +++ b/src/lib/tvgScene.cpp @@ -91,16 +91,11 @@ int Scene::translate(float x, float y) noexcept } -int Scene::bounds(float& x, float& y, float& w, float& h) const noexcept +int Scene::bounds(float* x, float* y, float* w, float* h) const noexcept { auto impl = pImpl.get(); assert(impl); - x = FLT_MAX; - y = FLT_MAX; - w = 0; - h = 0; - if (!impl->bounds(x, y, w, h)) return -1; return 0; diff --git a/src/lib/tvgSceneImpl.h b/src/lib/tvgSceneImpl.h index abd29e6..9064b53 100644 --- a/src/lib/tvgSceneImpl.h +++ b/src/lib/tvgSceneImpl.h @@ -100,8 +100,13 @@ struct Scene::Impl return true; } - bool bounds(float& x, float& y, float& w, float& h) + bool bounds(float* px, float* py, float* pw, float* ph) { + auto x = FLT_MAX; + auto y = FLT_MAX; + auto w = 0.0f; + auto h = 0.0f; + for(auto paint: paints) { auto x2 = FLT_MAX; auto y2 = FLT_MAX; @@ -109,9 +114,9 @@ struct Scene::Impl auto h2 = 0.0f; if (auto scene = dynamic_cast(paint)) { - if (!SCENE_IMPL->bounds(x2, y2, w2, h2)) return false; + if (!SCENE_IMPL->bounds(&x2, &y2, &w2, &h2)) return false; } else if (auto shape = dynamic_cast(paint)) { - if (!SHAPE_IMPL->bounds(x2, y2, w2, h2)) return false; + if (!SHAPE_IMPL->bounds(&x2, &y2, &w2, &h2)) return false; } //Merge regions @@ -120,6 +125,12 @@ struct Scene::Impl if (y2 < y) y = x2; if (y + h < y2 + h2) h = (y2 + h2) - y; } + + if (px) *px = x; + if (py) *py = y; + if (pw) *pw = w; + if (ph) *ph = h; + return true; } diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index 342f456..c8483e1 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -269,7 +269,7 @@ int Shape::translate(float x, float y) noexcept } -int Shape::bounds(float& x, float& y, float& w, float& h) const noexcept +int Shape::bounds(float* x, float* y, float* w, float* h) const noexcept { auto impl = pImpl.get(); assert(impl); diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index e2ba294..1800b70 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -99,7 +99,7 @@ struct Shape::Impl return false; } - bool bounds(float& x, float& y, float& w, float& h) + bool bounds(float* x, float* y, float* w, float* h) { assert(path); return path->bounds(x, y, w, h); diff --git a/src/lib/tvgShapePath.h b/src/lib/tvgShapePath.h index 9dafdf9..68205f4 100644 --- a/src/lib/tvgShapePath.h +++ b/src/lib/tvgShapePath.h @@ -113,7 +113,7 @@ struct ShapePath cmds[cmdCnt++] = PathCommand::Close; } - bool bounds(float& x, float& y, float& w, float& h) + bool bounds(float* x, float* y, float* w, float* h) { if (ptsCnt == 0) return false; @@ -127,10 +127,10 @@ struct ShapePath if (pts[i].y > max.y) max.y = pts[i].y; } - x = min.x; - y = min.y; - w = max.x - min.x; - h = max.y - min.y; + if (x) *x = min.x; + if (y) *y = min.y; + if (w) *w = max.x - min.x; + if (h) *h = max.y - min.y; return true; } -- 2.7.4 From 4799426396f16d29a21c1d4632caae32fa383d73 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Sat, 23 May 2020 13:32:28 +0900 Subject: [PATCH 08/16] common shape: introduce stroke cap and join styles. + revise the getter functions for avoiding invalid overloading. Change-Id: Ie8b0cbe57435253d75871e864c7cd263a14d6df3 --- inc/tizenvg.h | 22 +++++++++++++++----- src/lib/tvgShape.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++------ src/lib/tvgShapeImpl.h | 24 ++++++++++++++++++++++ 3 files changed, 89 insertions(+), 11 deletions(-) diff --git a/inc/tizenvg.h b/inc/tizenvg.h index dbdcd66..08c8556 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -53,7 +53,9 @@ protected: \ namespace tvg { -enum class TIZENVG_EXPORT PathCommand { Close, MoveTo, LineTo, CubicTo }; +enum class TIZENVG_EXPORT PathCommand { Close = 0, MoveTo, LineTo, CubicTo }; +enum class TIZENVG_EXPORT StrokeCap { Square = 0, Round, Butt }; +enum class TIZENVG_EXPORT StrokeJoin { Bevel = 0, Round, Miter }; class RenderMethod; class Scene; @@ -127,34 +129,44 @@ public: int reset() noexcept; + //Path int moveTo(float x, float y) noexcept; int lineTo(float x, float y) noexcept; int cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept; int close() noexcept; + //Shape int appendRect(float x, float y, float w, float h, float cornerRadius) noexcept; int appendCircle(float cx, float cy, float radiusW, float radiusH) noexcept; int appendPath(const PathCommand* cmds, size_t cmdCnt, const Point* pts, size_t ptsCnt) noexcept; + //Stroke int stroke(size_t 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; + int stroke(StrokeJoin join) noexcept; + //Fill int fill(size_t r, size_t g, size_t b, size_t a) noexcept; + //Transform int rotate(float degree) noexcept override; int scale(float factor) noexcept override; int translate(float x, float y) noexcept override; + //Getters size_t pathCommands(const PathCommand** cmds) const noexcept; size_t pathCoords(const Point** pts) const noexcept; int fill(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept; - size_t stroke() const noexcept; - int stroke(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept; - size_t stroke(const size_t** dashPattern) const noexcept; - int bounds(float* x, float* y, float* w, float* h) const noexcept override; + size_t 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; + StrokeJoin strokeJoin() const noexcept; + static std::unique_ptr gen() noexcept; _TIZENVG_DECLARE_ACCESSOR(Scene); diff --git a/src/lib/tvgShape.cpp b/src/lib/tvgShape.cpp index c8483e1..a17c53b 100644 --- a/src/lib/tvgShape.cpp +++ b/src/lib/tvgShape.cpp @@ -291,7 +291,7 @@ int Shape::stroke(size_t width) noexcept } -size_t Shape::stroke() const noexcept +size_t Shape::strokeWidth() const noexcept { auto impl = pImpl.get(); assert(impl); @@ -312,7 +312,7 @@ int Shape::stroke(size_t r, size_t g, size_t b, size_t a) noexcept } -int Shape::stroke(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept +int Shape::strokeColor(size_t* r, size_t* g, size_t* b, size_t* a) const noexcept { auto impl = pImpl.get(); assert(impl); @@ -341,18 +341,60 @@ int Shape::stroke(const size_t* dashPattern, size_t cnt) noexcept } -size_t Shape::stroke(const size_t** dashPattern) const noexcept +size_t Shape::strokeDash(const size_t** dashPattern) const noexcept { - assert(dashPattern); - auto impl = pImpl.get(); assert(impl); if (!impl->stroke) return 0; - *dashPattern = impl->stroke->dashPattern; + if (dashPattern) *dashPattern = impl->stroke->dashPattern; return impl->stroke->dashCnt; } +int Shape::stroke(StrokeCap cap) noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + if (!impl->strokeCap(cap)) return -1; + + return 0; +} + + +int Shape::stroke(StrokeJoin join) noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + if (!impl->strokeJoin(join)) return -1; + + return 0; +} + + +StrokeCap Shape::strokeCap() const noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + if (!impl->stroke) return StrokeCap::Square; + + return impl->stroke->cap; +} + + +StrokeJoin Shape::strokeJoin() const noexcept +{ + auto impl = pImpl.get(); + assert(impl); + + if (!impl->stroke) return StrokeJoin::Bevel; + + return impl->stroke->join; +} + + #endif //_TVG_SHAPE_CPP_ diff --git a/src/lib/tvgShapeImpl.h b/src/lib/tvgShapeImpl.h index 1800b70..682c29b 100644 --- a/src/lib/tvgShapeImpl.h +++ b/src/lib/tvgShapeImpl.h @@ -34,6 +34,8 @@ struct ShapeStroke size_t color[4] = {0, 0, 0, 0}; size_t* dashPattern = nullptr; size_t dashCnt = 0; + StrokeCap cap = StrokeCap::Square; + StrokeJoin join = StrokeJoin::Bevel; ~ShapeStroke() { @@ -164,6 +166,28 @@ struct Shape::Impl return 0; } + bool strokeCap(StrokeCap cap) + { + if (!stroke) stroke = new ShapeStroke(); + assert(stroke); + + stroke->cap = cap; + flag |= RenderUpdateFlag::Stroke; + + return 0; + } + + bool strokeJoin(StrokeJoin join) + { + if (!stroke) stroke = new ShapeStroke(); + assert(stroke); + + stroke->join = join; + flag |= RenderUpdateFlag::Stroke; + + return 0; + } + bool strokeColor(size_t r, size_t g, size_t b, size_t a) { if (!stroke) stroke = new ShapeStroke(); -- 2.7.4 From 48e47b272bd474b5a6f5fc1131e2730bfc73e5c0 Mon Sep 17 00:00:00 2001 From: Prudhvi Raj Vasireddi Date: Mon, 11 May 2020 17:39:08 -0400 Subject: [PATCH 09/16] gl_engine: gl infrastructure interfaces Change-Id: Ie1a9d1b6632433413098282c1cfaf4cf8e1cf9b9 Signed-off-by: Prudhvi Raj Vasireddi --- inc/tizenvg.h | 1 + src/lib/gl_engine/meson.build | 9 +- src/lib/gl_engine/tvgGlCommon.h | 19 ++++ src/lib/gl_engine/tvgGlGeometry.h | 180 ++++++++++++++++++++++++++++++++++++ src/lib/gl_engine/tvgGlGpuBuffer.h | 26 ++++++ src/lib/gl_engine/tvgGlProgram.h | 29 ++++++ src/lib/gl_engine/tvgGlRenderer.cpp | 5 - src/lib/gl_engine/tvgGlRenderer.h | 12 +++ src/lib/gl_engine/tvgGlShader.h | 20 ++++ src/lib/tvgGlCanvas.cpp | 11 +++ src/meson.build | 6 +- 11 files changed, 310 insertions(+), 8 deletions(-) create mode 100644 src/lib/gl_engine/tvgGlCommon.h create mode 100644 src/lib/gl_engine/tvgGlGeometry.h create mode 100644 src/lib/gl_engine/tvgGlGpuBuffer.h create mode 100644 src/lib/gl_engine/tvgGlProgram.h create mode 100644 src/lib/gl_engine/tvgGlShader.h diff --git a/inc/tizenvg.h b/inc/tizenvg.h index 98b6faf..c133ace 100644 --- a/inc/tizenvg.h +++ b/inc/tizenvg.h @@ -220,6 +220,7 @@ public: //TODO: Gl Specific methods. Need gl backend configuration methods as well. + int target(uint32_t* buffer, size_t stride, size_t w, size_t h) noexcept; int sync() noexcept override; static std::unique_ptr gen() noexcept; diff --git a/src/lib/gl_engine/meson.build b/src/lib/gl_engine/meson.build index 13e7fef..41a25e0 100644 --- a/src/lib/gl_engine/meson.build +++ b/src/lib/gl_engine/meson.build @@ -1,7 +1,12 @@ source_file = [ - 'tvgGlRenderer.h', + 'tvgGlCommon.h', + 'tvgGlGpuBuffer.h', + 'tvgGlProgram.h', 'tvgGlRenderer.cpp', -] + 'tvgGlRenderer.h', + 'tvgGlShader.h', + 'tvgGlGeometry.h', + ] glraster_dep = declare_dependency( include_directories : include_directories('.'), diff --git a/src/lib/gl_engine/tvgGlCommon.h b/src/lib/gl_engine/tvgGlCommon.h new file mode 100644 index 0000000..f68666a --- /dev/null +++ b/src/lib/gl_engine/tvgGlCommon.h @@ -0,0 +1,19 @@ +#ifndef _TVG_GL_COMMON_H_ +#define _TVG_GL_COMMON_H_ + +#include "tvgCommon.h" +#include "tvgGlProgram.h" +#include "tvgGlShader.h" +#include "tvgGlGeometry.h" + + +struct GlShape +{ + float viewWd; + float viewHt; + RenderUpdateFlag updateFlag; + unique_ptr geometry; +}; + + +#endif /* _TVG_GL_COMMON_H_ */ diff --git a/src/lib/gl_engine/tvgGlGeometry.h b/src/lib/gl_engine/tvgGlGeometry.h new file mode 100644 index 0000000..8f7cc61 --- /dev/null +++ b/src/lib/gl_engine/tvgGlGeometry.h @@ -0,0 +1,180 @@ +#ifndef _TVG_GL_GEOMETRY_H_ +#define _TVG_GL_GEOMETRY_H_ + +#include "tvgGlGpuBuffer.h" + +class GlPoint +{ +public: + float x = 0.0f; + float y = 0.0f; + + GlPoint(float pX, float pY):x(pX), y(pY) + {} + + GlPoint(const Point& rhs):GlPoint(rhs.x, rhs.y) + {} + + GlPoint(const GlPoint& rhs) = default; + GlPoint(GlPoint&& rhs) = default; + + GlPoint& operator= (const GlPoint& rhs) = default; + GlPoint& operator= (GlPoint&& rhs) = default; + + GlPoint& operator= (const Point& rhs) + { + x = rhs.x; + y = rhs.y; + return *this; + } + + bool operator== (const GlPoint& rhs) + { + if (&rhs == this) + return true; + if (rhs.x == this->x && rhs.y == this->y) + return true; + return false; + } + + bool operator!= (const GlPoint& rhs) + { + if (&rhs == this) + return true; + if (rhs.x != this->x || rhs.y != this->y) + return true; + return false; + } + + GlPoint operator+ (const GlPoint& rhs) const + { + return GlPoint(x + rhs.x, y + rhs.y); + } + + GlPoint operator+ (const float c) const + { + return GlPoint(x + c, y + c); + } + + GlPoint operator- (const GlPoint& rhs) const + { + return GlPoint(x - rhs.x, y - rhs.y); + } + + GlPoint operator- (const float c) const + { + return GlPoint(x - c, y - c); + } + + GlPoint operator* (const GlPoint& rhs) const + { + return GlPoint(x * rhs.x, y * rhs.y); + } + + GlPoint operator* (const float c) const + { + return GlPoint(x * c, y * c); + } + + GlPoint operator/ (const GlPoint& rhs) const + { + return GlPoint(x / rhs.x, y / rhs.y); + } + + GlPoint operator/ (const float c) const + { + return GlPoint(x / c, y / c); + } + + void mod() + { + x = fabsf(x); + y = fabsf(y); + } + + void normalize() + { + auto length = sqrtf( (x * x) + (y * y) ); + if (length != 0.0f) + { + const auto inverseLen = 1.0f / length; + x *= inverseLen; + y *= inverseLen; + } + } +}; + +struct SmoothPoint +{ + GlPoint orgPt; + GlPoint fillOuterBlur; + GlPoint fillOuter; + GlPoint strokeOuterBlur; + GlPoint strokeOuter; + GlPoint strokeInnerBlur; + GlPoint strokeInner; + + SmoothPoint(GlPoint pt) + :orgPt(pt), + fillOuterBlur(pt), + fillOuter(pt), + strokeOuterBlur(pt), + strokeOuter(pt), + strokeInnerBlur(pt), + strokeInner(pt) + { + } +}; + +struct PointNormals +{ + GlPoint normal1; + GlPoint normal2; + GlPoint normalF; +}; + +struct VertexData +{ + GlPoint point; + float opacity = 0.0f; +}; + +struct VertexDataArray +{ + vector vertices; + vector indices; +}; + +class GlGeometry +{ +public: + GlGeometry(); + void reset(); + void updateBuffer(const uint32_t location, const VertexDataArray& geometry); + void draw(const VertexDataArray& geometry); + bool decomposeOutline(const Shape& shape); + bool generateAAPoints(const Shape& shape, float strokeWd, RenderUpdateFlag flag); + bool tesselate(const Shape &shape, float viewWd, float viewHt, RenderUpdateFlag flag); + + const VertexDataArray& getFill(); + const VertexDataArray& getStroke(); + +private: + GlPoint normalizePoint(GlPoint &pt, float viewWd, float viewHt); + void addGeometryPoint(VertexDataArray &geometry, GlPoint &pt, float viewWd, float viewHt, float opacity); + GlPoint getNormal(GlPoint &p1, GlPoint &p2); + float dotProduct(GlPoint &p1, GlPoint &p2); + GlPoint extendEdge(GlPoint &pt, GlPoint &normal, float scalar); + void addPoint(const GlPoint &pt); + void addTriangleFanIndices(uint32_t &curPt, vector &indices); + void addQuadIndices(uint32_t &curPt, vector &indices); + bool isBezierFlat(const GlPoint &p1, const GlPoint &c1, const GlPoint &c2, const GlPoint &p2); + void decomposeCubicCurve(const GlPoint &pt1, const GlPoint &cpt1, const GlPoint &cpt2, const GlPoint &pt2); + + unique_ptr mGpuBuffer; + vector mAAPoints; + VertexDataArray mFill; + VertexDataArray mStroke; +}; + +#endif /* _TVG_GL_GEOMETRY_H_ */ diff --git a/src/lib/gl_engine/tvgGlGpuBuffer.h b/src/lib/gl_engine/tvgGlGpuBuffer.h new file mode 100644 index 0000000..fd7086a --- /dev/null +++ b/src/lib/gl_engine/tvgGlGpuBuffer.h @@ -0,0 +1,26 @@ +#ifndef _TVG_GL_GPU_BUFFER_H_ +#define _TVG_GL_GPU_BUFFER_H_ + +#include +#include + +class GlGpuBuffer +{ +public: + enum class Target + { + ARRAY_BUFFER = GL_ARRAY_BUFFER, + ELEMENT_ARRAY_BUFFER = GL_ARRAY_BUFFER + }; + + GlGpuBuffer(); + ~GlGpuBuffer(); + void updateBufferData(Target target, size_t size, void* data); + +private: + uint32_t mGlBufferId = 0; + +}; + +#endif /* _TVG_GL_GPU_BUFFER_H_ */ + diff --git a/src/lib/gl_engine/tvgGlProgram.h b/src/lib/gl_engine/tvgGlProgram.h new file mode 100644 index 0000000..d485d3d --- /dev/null +++ b/src/lib/gl_engine/tvgGlProgram.h @@ -0,0 +1,29 @@ +#ifndef _TVG_GL_PROGRAM_H_ +#define _TVG_GL_PROGRAM_H_ + +#include "tvgGlShader.h" + +#include + + +class GlProgram +{ +public: + GlProgram(shared_ptr shader); + void create(); + void load(); + int32_t getAttributeLocation(const char* name); + int32_t getUniformLocation(const char* name); + void setUniformValue(int32_t location, float r, float g, float b, float a); + +private: + void linkProgram(); + std::shared_ptr mShader; + uint32_t mProgramObj; + static uint32_t mCurrentProgram; + + static std::map mAttributeBuffer; + static std::map mUniformBuffer; +}; + +#endif /* _TVG_GL_PROGRAM_H_ */ diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 792e524..0eb9beb 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -26,11 +26,6 @@ static RenderInitializer renderInit; -struct GlShape -{ - //TODO: -}; - /************************************************************************/ /* External Class Implementation */ /************************************************************************/ diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index 4d6ba79..fcde08f 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -17,15 +17,23 @@ #ifndef _TVG_GL_RENDERER_H_ #define _TVG_GL_RENDERER_H_ +#include "tvgGlCommon.h" + namespace tvg { class GlRenderer : public RenderMethod { public: + Surface surface; + void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool render(const Shape& shape, void *data) override; + bool target(uint32_t* buffer, size_t stride, size_t w, size_t h) + { + return 0; + }; bool clear() override; size_t ref() override; size_t unref() override; @@ -37,6 +45,10 @@ public: private: GlRenderer(){}; ~GlRenderer(){}; + + std::unique_ptr mColorProgram; + int32_t mColorUniform; + uint32_t mVertexAttrID; }; } diff --git a/src/lib/gl_engine/tvgGlShader.h b/src/lib/gl_engine/tvgGlShader.h new file mode 100644 index 0000000..36b125a --- /dev/null +++ b/src/lib/gl_engine/tvgGlShader.h @@ -0,0 +1,20 @@ +#ifndef _TVG_GL_SHADER_H_ +#define _TVG_GL_SHADER_H_ + +class GlShader +{ +public: + static shared_ptr gen(const char * vertSrc, const char * fragSrc); + + uint32_t getVertexShader(); + uint32_t getFragmentShader(); + +private: + void createShader(const char* vertSrc, const char* fragSrc); + uint32_t complileShader(uint32_t type, char* shaderSrc); + + uint32_t mVtShader; + uint32_t mFrShader; +}; + +#endif /* _TVG_GL_SHADER_H_ */ diff --git a/src/lib/tvgGlCanvas.cpp b/src/lib/tvgGlCanvas.cpp index fb769a8..c1b7aeb 100644 --- a/src/lib/tvgGlCanvas.cpp +++ b/src/lib/tvgGlCanvas.cpp @@ -45,6 +45,17 @@ GlCanvas::~GlCanvas() } +int GlCanvas::target(uint32_t* buffer, size_t stride, size_t w, size_t h) noexcept +{ + auto renderer = dynamic_cast(Canvas::pImpl.get()->renderer); + assert(renderer); + + if (!renderer->target(buffer, stride, w, h)) return -1; + + return 0; +} + + int GlCanvas::sync() noexcept { return 0; diff --git a/src/meson.build b/src/meson.build index 495e4c7..aded001 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,8 +2,12 @@ compiler_flags = ['-DTIZENVG_BUILD'] subdir('lib') subdir('examples') +m_dep = meson.get_compiler('cpp').find_library('m') +egl_dep = meson.get_compiler('cpp').find_library('EGL') +gl_dep = meson.get_compiler('cpp').find_library('GLESv2') + +tizenvg_lib_dep = [ src_dep, swraster_dep, glraster_dep, m_dep, egl_dep, gl_dep] -tizenvg_lib_dep = [ src_dep, swraster_dep, glraster_dep ] tizenvg_lib = library( 'tizenvg', -- 2.7.4 From 6b54eb5c22342caad3dbc2731187ba0c6a25014e Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 27 May 2020 11:03:07 +0900 Subject: [PATCH 10/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 11/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 12/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 13/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 14/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 15/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 16/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