From 2c048f72e8ccb15a09d77367d68a3269cc7f43ea Mon Sep 17 00:00:00 2001 From: Sergii Liebodkin Date: Wed, 18 Jun 2025 16:32:00 +0300 Subject: [PATCH] gl_engine: remove the outdated tesseletion mechanics After introducing Triangle Fan-like Triangulation there is no necessary to have heavy and complex tessellator All unused entities are removed --- src/common/tvgArray.h | 25 - src/renderer/gl_engine/meson.build | 1 - src/renderer/gl_engine/tvgGlCommon.h | 67 +- src/renderer/gl_engine/tvgGlGeometry.cpp | 6 - src/renderer/gl_engine/tvgGlList.h | 90 -- src/renderer/gl_engine/tvgGlTessellator.cpp | 1393 +------------------ src/renderer/gl_engine/tvgGlTessellator.h | 42 - 7 files changed, 33 insertions(+), 1591 deletions(-) delete mode 100644 src/renderer/gl_engine/tvgGlList.h diff --git a/src/common/tvgArray.h b/src/common/tvgArray.h index ff11eeae..af53eec2 100644 --- a/src/common/tvgArray.h +++ b/src/common/tvgArray.h @@ -186,35 +186,10 @@ struct Array return count == reserved; } - template void sort() - { - qsort(data, 0, (int32_t)(count - 1)); - } - ~Array() { tvg::free(data); } - -private: - template - void qsort(T* arr, int32_t low, int32_t high) - { - if (low < high) { - auto i = low; - auto j = high; - auto tmp = arr[low]; - while (i < j) { - while (i < j && !COMPARE{}(arr[j], tmp)) --j; - if (i < j) arr[i++] = arr[j]; - while (i < j && COMPARE{}(arr[i], tmp)) ++i; - if (i < j) arr[j--] = arr[i]; - } - arr[i] = tmp; - qsort(arr, low, i - 1); - qsort(arr, i + 1, high); - } - } }; } diff --git a/src/renderer/gl_engine/meson.build b/src/renderer/gl_engine/meson.build index 2ca77f84..766fa446 100644 --- a/src/renderer/gl_engine/meson.build +++ b/src/renderer/gl_engine/meson.build @@ -2,7 +2,6 @@ source_file = [ 'tvgGl.h', 'tvgGlCommon.h', 'tvgGlGpuBuffer.h', - 'tvgGlList.h', 'tvgGlProgram.h', 'tvgGlRenderer.h', 'tvgGlRenderPass.h', diff --git a/src/renderer/gl_engine/tvgGlCommon.h b/src/renderer/gl_engine/tvgGlCommon.h index 1dc2492d..f073dc50 100644 --- a/src/renderer/gl_engine/tvgGlCommon.h +++ b/src/renderer/gl_engine/tvgGlCommon.h @@ -23,31 +23,29 @@ #ifndef _TVG_GL_COMMON_H_ #define _TVG_GL_COMMON_H_ -#include +#include #include "tvgGl.h" #include "tvgRender.h" #include "tvgMath.h" #define MIN_GL_STROKE_WIDTH 1.0f -#define MVP_MATRIX(w, h) \ - float mvp[4*4] = { \ - 2.f / w, 0.0, 0.0f, 0.0f, \ - 0.0, -2.f / h, 0.0f, 0.0f, \ - 0.0f, 0.0f, -1.f, 0.0f, \ - -1.f, 1.f, 0.0f, 1.0f \ +#define MVP_MATRIX(w, h) \ + float mvp[4*4] = { \ + 2.f / w, 0.0, 0.0f, 0.0f, \ + 0.0, -2.f / h, 0.0f, 0.0f, \ + 0.0f, 0.0f, -1.0f, 0.0f, \ + -1.f, 1.f, 0.0f, 1.0f \ }; -#define MULTIPLY_MATRIX(A, B, transform) \ - for(auto i = 0; i < 4; ++i) \ - { \ - for(auto j = 0; j < 4; ++j) \ - { \ - float sum = 0.0; \ - for (auto k = 0; k < 4; ++k) \ +#define MULTIPLY_MATRIX(A, B, transform) \ + for(auto i = 0; i < 4; ++i) { \ + for(auto j = 0; j < 4; ++j) { \ + float sum = 0.0; \ + for (auto k = 0; k < 4; ++k) \ sum += A[k*4+i] * B[j*4+k]; \ - transform[j*4+i] = sum; \ - } \ + transform[j*4+i] = sum; \ + } \ } /** @@ -61,24 +59,24 @@ */ // All GPU use 4x4 matrix with column major order -#define GET_MATRIX44(mat3, mat4) \ - do { \ - mat4[0] = mat3.e11; \ - mat4[1] = mat3.e21; \ - mat4[2] = 0; \ - mat4[3] = mat3.e31; \ - mat4[4] = mat3.e12; \ - mat4[5] = mat3.e22; \ - mat4[6] = 0; \ - mat4[7] = mat3.e32; \ - mat4[8] = 0; \ - mat4[9] = 0; \ - mat4[10] = 1; \ - mat4[11] = 0; \ - mat4[12] = mat3.e13; \ - mat4[13] = mat3.e23; \ - mat4[14] = 0; \ - mat4[15] = mat3.e33; \ +#define GET_MATRIX44(mat3, mat4) \ + do { \ + mat4[0] = mat3.e11; \ + mat4[1] = mat3.e21; \ + mat4[2] = 0; \ + mat4[3] = mat3.e31; \ + mat4[4] = mat3.e12; \ + mat4[5] = mat3.e22; \ + mat4[6] = 0; \ + mat4[7] = mat3.e32; \ + mat4[8] = 0; \ + mat4[9] = 0; \ + mat4[10] = 1; \ + mat4[11] = 0; \ + mat4[12] = mat3.e13; \ + mat4[13] = mat3.e23; \ + mat4[14] = 0; \ + mat4[15] = mat3.e33; \ } while (false) @@ -115,7 +113,6 @@ struct GlGeometry { bool tesselate(const RenderShape& rshape, RenderUpdateFlag flag); bool tesselate(const RenderSurface* image, RenderUpdateFlag flag); - void disableVertex(uint32_t location); bool draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdateFlag flag); GlStencilMode getStencilMode(RenderUpdateFlag flag); RenderRegion getBounds() const; diff --git a/src/renderer/gl_engine/tvgGlGeometry.cpp b/src/renderer/gl_engine/tvgGlGeometry.cpp index 97cf2ce8..3f1c932a 100644 --- a/src/renderer/gl_engine/tvgGlGeometry.cpp +++ b/src/renderer/gl_engine/tvgGlGeometry.cpp @@ -108,12 +108,6 @@ bool GlGeometry::tesselate(const RenderSurface* image, RenderUpdateFlag flag) } -void GlGeometry::disableVertex(uint32_t location) -{ - GL_CHECK(glDisableVertexAttribArray(location)); -} - - bool GlGeometry::draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdateFlag flag) { if (flag == RenderUpdateFlag::None) return false; diff --git a/src/renderer/gl_engine/tvgGlList.h b/src/renderer/gl_engine/tvgGlList.h deleted file mode 100644 index d7c479f6..00000000 --- a/src/renderer/gl_engine/tvgGlList.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2023 - 2025 the ThorVG project. All rights reserved. - - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _TVG_LIST_H_ -#define _TVG_LIST_H_ - -namespace tvg { - -template -struct LinkedList -{ - T *head = nullptr; - T *tail = nullptr; - - LinkedList() = default; - LinkedList(T *head, T *tail) : head(head), tail(tail) - { - } - - template - static void insert(T *t, T *prev, T *next, T **head, T **tail) - { - t->*Prev = prev; - t->*Next = next; - - if (prev) { - prev->*Next = t; - } else if (head) { - *head = t; - } - - if (next) { - next->*Prev = t; - } else if (tail) { - *tail = t; - } - } - - template - static void remove(T *t, T **head, T **tail) - { - if (t->*Prev) { - t->*Prev->*Next = t->*Next; - } else if (head) { - *head = t->*Next; - } - - if (t->*Next) { - t->*Next->*Prev = t->*Prev; - } else if (tail) { - *tail = t->*Prev; - } - - t->*Prev = t->*Next = nullptr; - } - - template - static bool contains(T *t, T **head, T **tail) { - for (T *it = *head; it; it = it->*Next) { - if (it == t) { - return true; - } - } - - return false; - } -}; - -} - -#endif // _TVG_LIST_H_ diff --git a/src/renderer/gl_engine/tvgGlTessellator.cpp b/src/renderer/gl_engine/tvgGlTessellator.cpp index d263e64e..e65d79da 100644 --- a/src/renderer/gl_engine/tvgGlTessellator.cpp +++ b/src/renderer/gl_engine/tvgGlTessellator.cpp @@ -20,717 +20,11 @@ * SOFTWARE. */ -#include -#define _USE_MATH_DEFINES -#include -#include - -#include "tvgGlCommon.h" #include "tvgGlTessellator.h" -#include "tvgRender.h" -#include "tvgGlList.h" namespace tvg { -// common obj for memory control -struct Object -{ - virtual ~Object() = default; -}; - -class ObjectHeap -{ -public: - ObjectHeap() = default; - ~ObjectHeap() - { - auto count = pObjs.count; - auto first = pObjs.data; - - for (uint32_t i = 0; i < count; ++i) { - delete static_cast(first[i]); - } - } - - template - T *allocate(Args &&...args) - { - pObjs.push(new T(std::forward(args)...)); - return static_cast(pObjs.data[pObjs.count - 1]); - } - -private: - Array pObjs = {}; -}; - -struct Vertex : public Object -{ - // list - Vertex *prev = nullptr; - Vertex *next = nullptr; - - uint32_t index = 0xFFFFFFFF; - /** - * All edge above and end with this vertex - * - * head tail - * \ ... / - * v - * - */ - LinkedList edge_above = {}; - - /** - * All edge below this vertex - * - * - * v - * / \ - * head tail - * / \ - * ... - * - */ - LinkedList edge_below = {}; - - // left enclosing edge during sweep line - Edge *left = nullptr; - // right enclosing edge during sweep line - Edge *right = nullptr; - - Point point = {0.0f, 0.0f}; - - Vertex() = default; - - Vertex(const Point &p) - { - point = {ceilf(p.x * 100.f) / 100.f, ceilf(p.y * 100.f) / 100.f}; - } - - ~Vertex() override = default; - - bool isConnected() const - { - return edge_above.head || edge_below.head; - } - - void insertAbove(Edge *e); - void insertBelow(Edge *e); -}; - - -// we sort point by top first then left -struct VertexCompare -{ - inline bool operator()(Vertex *v1, Vertex *v2) - { - return compare(v1->point, v2->point); - } - - static bool compare(const Point &a, const Point &b); -}; - - -// double linked list for all vertex in shape -struct VertexList : public LinkedList -{ - VertexList() = default; - VertexList(Vertex *head, Vertex *tail) : LinkedList(head, tail) - { - } - - void insert(Vertex *v, Vertex *prev, Vertex *next); - void remove(Vertex *v); - void append(VertexList const &other); - void append(Vertex *v); - void prepend(Vertex *v); - void close(); -}; - - -struct Edge : public Object -{ - Vertex *top = nullptr; - Vertex *bottom = nullptr; - - Edge *abovePrev = nullptr; - Edge *aboveNext = nullptr; - Edge *belowPrev = nullptr; - Edge *belowNext = nullptr; - - Edge *left = nullptr; // left edge in active list during sweep line - Edge *right = nullptr; // right edge in active list during sweep line - - // edge list in polygon - Edge *rightPolyPrev = nullptr; - Edge *rightPolyNext = nullptr; - Edge *leftPolyPrev = nullptr; - Edge *leftPolyNext = nullptr; - - Polygon *leftPoly = nullptr; // left polygon during sweep line - Polygon *rightPoly = nullptr; // right polygon during sweep line - - bool usedInLeft = false; - bool usedInRight = false; - - int32_t winding = 1; - - Edge(Vertex *top, Vertex *bottom, int32_t winding); - - ~Edge() override = default; - - // https://stackoverflow.com/questions/1560492/how-to-tell-whether-a-point-is-to-the-right-or-left-side-of-a-line - // return > 0 means point in left - // return < 0 means point in right - float sideDist(const Point& p); - - bool isRightOf(const Point& p) - { - return sideDist(p) < 0.0f; - } - - bool isLeftOf(const Point& p) - { - return sideDist(p) > 0.0f; - } - - // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection - bool intersect(Edge *other, Point* point); - void recompute(); - void setBottom(Vertex *v); - void setTop(Vertex *v); - void disconnect(); - -private: - float le_a; - float le_b; - float le_c; -}; - - -// Active Edge List (AEL) or Active Edge Table (AET) during sweep line -struct ActiveEdgeList : public LinkedList -{ - ActiveEdgeList() = default; - ~ActiveEdgeList() = default; - - void insert(Edge *edge, Edge *prev, Edge *next); - void insert(Edge *edge, Edge *prev); - - void append(Edge *edge); - void remove(Edge *edge); - - bool contains(Edge *edge); - - // move event point from current to dst - void rewind(Vertex **current, Vertex *dst); - - void findEnclosing(Vertex *v, Edge **left, Edge **right); - - bool valid(); -}; - - -enum class Side -{ - kLeft, - kRight, -}; - - -struct Polygon : public Object -{ - Vertex *first_vert = nullptr; - int32_t winding; - - // vertex count - int32_t count = 0; - Polygon *parent = nullptr; - Polygon *next = nullptr; - - MonotonePolygon *head = nullptr; - MonotonePolygon *tail = nullptr; - - Polygon(Vertex *first, int32_t winding) : first_vert(first), winding(winding) - { - } - - ~Polygon() override = default; - - Polygon *addEdge(Edge *e, Side side, ObjectHeap *heap); - - Vertex *lastVertex() const; -}; - - -struct MonotonePolygon : public Object -{ - Side side; - - Edge *first = nullptr; - Edge *last = nullptr; - - int32_t winding; - - MonotonePolygon *prev = nullptr; - MonotonePolygon *next = nullptr; - - void addEdge(Edge *edge); - - MonotonePolygon(Edge *edge, Side side, int32_t winding) : side(side), winding(winding) - { - addEdge(edge); - } - - ~MonotonePolygon() override = default; -}; - - -// ----------------------------- Impl ------------------------------- -void Vertex::insertAbove(Edge *e) -{ - if (e->top->point == e->bottom->point || // no edge - VertexCompare::compare(e->bottom->point, e->top->point)) { // not above - return; - } - - if (LinkedList::contains<&Edge::aboveNext>(e, &this->edge_above.head, &this->edge_above.tail)) { - return; - } - - Edge *abovePrev = nullptr; - Edge *aboveNext = nullptr; - - // find insertion point - for (aboveNext = this->edge_above.head; aboveNext; aboveNext = aboveNext->aboveNext) { - if (aboveNext->isRightOf(e->top->point)) { - break; - } - - abovePrev = aboveNext; - } - - LinkedList::insert<&Edge::abovePrev, &Edge::aboveNext>(e, abovePrev, aboveNext, &this->edge_above.head, - &this->edge_above.tail); -} - - -void Vertex::insertBelow(Edge *e) -{ - if (e->top->point == e->bottom->point || // no edge - VertexCompare::compare(e->bottom->point, e->top->point)) { // not below - return; - } - - if (LinkedList::contains<&Edge::belowNext>(e, &this->edge_below.head, &this->edge_below.tail)) { - return; - } - - Edge *belowPrev = nullptr; - Edge *belowNext = nullptr; - - // find insertion point - for (belowNext = this->edge_below.head; belowNext; belowNext = belowNext->belowNext) { - if (belowNext->isRightOf(e->bottom->point)) { - break; - } - - belowPrev = belowNext; - } - - LinkedList::insert<&Edge::belowPrev, &Edge::belowNext>(e, belowPrev, belowNext, &this->edge_below.head, - &this->edge_below.tail); -} - - -bool VertexCompare::compare(const Point& a, const Point& b) -{ - return a.y < b.y || (a.y == b.y && a.x < b.x); -} - - -void VertexList::insert(Vertex *v, Vertex *prev, Vertex *next) -{ - LinkedList::insert<&Vertex::prev, &Vertex::next>(v, prev, next, &head, &tail); -} - - -void VertexList::remove(Vertex *v) -{ - LinkedList::remove<&Vertex::prev, &Vertex::next>(v, &head, &tail); -} - - -void VertexList::append(VertexList const &other) -{ - if (!other.head) { - return; - } - - if (tail) { - tail->next = other.head; - other.head->prev = tail; - } else { - head = other.head; - } - - tail = other.tail; -} - - -void VertexList::append(Vertex *v) -{ - insert(v, tail, nullptr); -} - - -void VertexList::prepend(Vertex *v) -{ - insert(v, nullptr, head); -} - - -void VertexList::close() -{ - if (head && tail) { - tail->next = head; - head->prev = tail; - } -} - - -Edge::Edge(Vertex *top, Vertex *bottom, int32_t winding) - : top(top), - bottom(bottom), - winding(winding), - le_a(bottom->point.y - top->point.y), - le_b(top->point.x - bottom->point.x), - le_c(top->point.y * bottom->point.x - top->point.x * bottom->point.y) -{ -} - - -float Edge::sideDist(const Point& p) -{ - return le_a * p.x + le_b * p.y + le_c; -} - - -bool Edge::intersect(Edge *other, Point* point) -{ - if (top == other->top || bottom == other->bottom || top == other->bottom || bottom == other->top) return false; - - // check if two aabb bounds is intersect - if (std::min(top->point.x, bottom->point.x) > std::max(other->top->point.x, other->bottom->point.x) || - std::max(top->point.x, bottom->point.x) < std::min(other->top->point.x, other->bottom->point.x) || - std::min(top->point.y, bottom->point.y) > std::max(other->top->point.y, other->bottom->point.y) || - std::max(top->point.y, bottom->point.y) < std::min(other->top->point.y, other->bottom->point.y)) { - return false; - } - - auto denom = le_a * other->le_b - le_b * other->le_a; - if (tvg::zero(denom)) return false; - - auto dx = other->top->point.x - top->point.x; - auto dy = other->top->point.y - top->point.y; - auto s_number = dy * other->le_b + dx * other->le_a; - auto t_number = dy * le_b + dx * le_a; - - if (denom > 0.0f ? (s_number < 0.0f || s_number > denom || t_number < 0.0f || t_number > denom) : (s_number > 0.0f || s_number < denom || t_number > 0.0f || t_number < denom)) return false; - - auto scale = 1.0f / denom; - point->x = nearbyintf(top->point.x - s_number * le_b * scale); - point->y = nearbyintf(top->point.y + s_number * le_a * scale); - - if (std::isinf(point->x) || std::isinf(point->y)) return false; - if (fabsf(point->x - top->point.x) < 1e-6f && fabsf(point->y - top->point.y) < 1e-6f) return false; - if (fabsf(point->x - bottom->point.x) < 1e-6f && fabsf(point->y - bottom->point.y) < 1e-6f) return false; - if (fabsf(point->x - other->top->point.x) < 1e-6f && fabsf(point->y - other->top->point.y) < 1e-6f) return false; - if (fabsf(point->x - other->bottom->point.x) < 1e-6f && fabsf(point->y - other->bottom->point.y) < 1e-6f) return false; - - return true; -} - - -void Edge::recompute() -{ - le_a = bottom->point.y - top->point.y; - le_b = top->point.x - bottom->point.x; - le_c = top->point.y * bottom->point.x - top->point.x * bottom->point.y; -} - - -void Edge::setBottom(Vertex *v) -{ - // remove this edge from bottom's above list - LinkedList::remove<&Edge::abovePrev, &Edge::aboveNext>(this, &bottom->edge_above.head, - &bottom->edge_above.tail); - // update bottom vertex - bottom = v; - // recompute line equation - recompute(); - // insert self to new bottom's above list - bottom->insertAbove(this); -} - - -void Edge::setTop(Vertex *v) -{ - // remove this edge from top's below list - LinkedList::remove<&Edge::belowPrev, &Edge::belowNext>(this, &top->edge_below.head, &top->edge_below.tail); - // update top vertex - top = v; - // recompute line equation - recompute(); - // insert self to new top's below list - top->insertBelow(this); -} - - -static void remove_edge_above(Edge *edge) -{ - LinkedList::remove<&Edge::abovePrev, &Edge::aboveNext>(edge, &edge->bottom->edge_above.head, &edge->bottom->edge_above.tail); -} - - -static void remove_edge_below(Edge *edge) -{ - LinkedList::remove<&Edge::belowPrev, &Edge::belowNext>(edge, &edge->top->edge_below.head, &edge->top->edge_below.tail); -} - - -void Edge::disconnect() -{ - remove_edge_above(this); - remove_edge_below(this); -} - - -void ActiveEdgeList::insert(Edge *e, Edge *prev, Edge *next) -{ - LinkedList::insert<&Edge::left, &Edge::right>(e, prev, next, &head, &tail); - if (!valid()) { - return; - } -} - - -void ActiveEdgeList::insert(Edge *e, Edge *prev) -{ - auto next = prev ? prev->right : head; - - insert(e, prev, next); -} - - -void ActiveEdgeList::append(Edge *e) -{ - insert(e, tail, nullptr); -} - - -void ActiveEdgeList::remove(Edge *e) -{ - LinkedList::remove<&Edge::left, &Edge::right>(e, &head, &tail); -} - - -bool ActiveEdgeList::contains(Edge *edge) -{ - return edge->left || edge->right || head == edge; -} - - -void ActiveEdgeList::rewind(Vertex **current, Vertex *dst) -{ - if (!current || *current == dst || VertexCompare::compare((*current)->point, dst->point)) return; - - auto v = *current; - - while (v != dst) { - v = v->prev; - - for (auto e = v->edge_below.head; e; e = e->belowNext) { - this->remove(e); - } - - auto left = v->left; - - for (auto e = v->edge_above.head; e; e = e->aboveNext) { - this->insert(e, left); - left = e; - auto top = e->top; - if (VertexCompare::compare(top->point, dst->point) && - ((top->left && !top->left->isLeftOf(e->top->point)) || - (top->right && !top->right->isRightOf(e->top->point)))) { - dst = top; - } - } - } - *current = v; -} - - -void ActiveEdgeList::findEnclosing(Vertex *v, Edge **left, Edge **right) -{ - if (v->edge_above.head && v->edge_above.tail) { - *left = v->edge_above.head->left; - *right = v->edge_above.tail->right; - return; - } - - Edge *prev = nullptr; - Edge *next = nullptr; - - // walk through aet to get left most edge - for (prev = tail; prev != nullptr; prev = prev->left) { - if (prev->isLeftOf(v->point)) { - break; - } - next = prev; - } - - *left = prev; - *right = next; -} - - -static bool _validEdgePair(Edge* left, Edge* right) { - if (!left || !right) { - return true; - } - - if (left->top == right->top) { - if (!left->isLeftOf(right->bottom->point)) { - return false; - } - if (!right->isRightOf(left->bottom->point)) { - return false; - } - } else if (VertexCompare::compare(left->top->point, right->top->point)) { - if (!left->isLeftOf(right->top->point)) { - return false; - } - } else { - if (!right->isRightOf(left->top->point)) { - return false; - } - } - - if (left->bottom == right->bottom) { - if (!left->isLeftOf(right->top->point)) { - return false; - } - if (!right->isRightOf(left->top->point)) { - return false; - } - } else if (VertexCompare::compare(right->bottom->point, left->bottom->point)) { - if (!left->isLeftOf(right->bottom->point)) { - return false; - } - } else { - if (!right->isRightOf(left->bottom->point)) { - return false; - } - } - - return true; -} - - -bool ActiveEdgeList::valid() -{ - auto left = head; - if (!left && !tail) { - return true; - } else if (!left || !tail) { - return false; - } - - for(auto right = left->right; right; right = right->right) { - if (!_validEdgePair(left, right)) { - return false; - } - - left = right; - } - - return true; -} - - -Polygon *Polygon::addEdge(Edge *e, Side side, ObjectHeap *heap) -{ - auto p_parent = this->parent; - - auto poly = this; - - if (side == Side::kRight) { - if (e->usedInRight) { // already in this polygon - return this; - } - } else { - if (e->usedInLeft) { // already in this polygon - return this; - } - } - - if (p_parent) { - this->parent = p_parent->parent = nullptr; - } - - if (!this->tail) { - this->head = this->tail = heap->allocate(e, side, this->winding); - this->count += 2; - } else if (e->bottom == this->tail->last->bottom) { - // close this polygon - return poly; - } else if (side == this->tail->side) { - this->tail->addEdge(e); - this->count++; - } else { - e = heap->allocate(this->tail->last->bottom, e->bottom, 1); - this->tail->addEdge(e); - this->count++; - - if (p_parent) { - p_parent->addEdge(e, side, heap); - poly = p_parent; - } else { - auto m = heap->allocate(e, side, this->winding); - m->prev = this->tail; - - this->tail->next = m; - - this->tail = m; - } - } - - return poly; -} - - -Vertex *Polygon::lastVertex() const -{ - if (tail) { - return tail->last->bottom; - } - - return first_vert; -} - - -void MonotonePolygon::addEdge(Edge *edge) -{ - if (this->side == Side::kRight) { - LinkedList::insert<&Edge::rightPolyPrev, &Edge::rightPolyNext>(edge, this->last, nullptr, &this->first, &this->last); - } else { - LinkedList::insert<&Edge::leftPolyPrev, &Edge::leftPolyNext>(edge, last, nullptr, &this->first, &this->last); - } -} - - static bool _bezIsFlatten(const Bezier& bz) { float diff1_x = fabs((bz.ctrl1.x * 3.f) - (bz.start.x * 2.f) - bz.end.x); @@ -781,24 +75,6 @@ static Bezier _bezFromArc(const Point& start, const Point& end, float radius) } -static Point _upScalePoint(const Point& p) -{ - return Point{p.x * 1000.f, p.y * 1000.f}; -} - - -static Point _downScalePoint(const Point& p) -{ - return Point {p.x / 1000.f, p.y / 1000.f}; -} - - -static float _downScaleFloat(float v) -{ - return v / 1000.f; -} - - static uint32_t _pushVertex(Array& array, float x, float y) { array.push(x); @@ -823,673 +99,6 @@ static Orientation _calcOrientation(const Point& p1, const Point& p2, const Poin } -static Orientation _calcOrientation(const Point& dir1, const Point& dir2) -{ - auto val = (dir2.x - dir1.x) * (dir1.y + dir2.y); - if (std::abs(val) < 0.0001f) return Orientation::Linear; - return val > 0 ? Orientation::Clockwise : Orientation::CounterClockwise; -} - - -Tessellator::Tessellator(GlGeometryBuffer* buffer) - : pHeap(new ObjectHeap), - outlines(), - pMesh(new VertexList), - pPolygon(), - buffer(buffer) -{ -} - -Tessellator::~Tessellator() -{ - for (uint32_t i = 0; i < outlines.count; i++) { - delete outlines[i]; - } - - delete pHeap; - delete pMesh; -} - - -void Tessellator::tessellate(const Array &shapes) -{ - fillRule = FillRule::NonZero; - - for (uint32_t i = 0; i < shapes.count; i++) { - RenderPath trimmedPath; - if (shapes[i]->trimpath()) { - if (!shapes[i]->stroke->trim.trim(shapes[i]->path, trimmedPath)) continue; - visitShape(trimmedPath); - } else visitShape(shapes[i]->path); - } - - buildMesh(); - mergeVertices(); - simplifyMesh(); - tessMesh(); - - // output triangles - for (auto poly = pPolygon; poly; poly = poly->next) { - if (!matchFillRule(poly->winding)) continue; - if (poly->count < 3) continue; - for (auto m = poly->head; m; m = m->next) { - emitPoly(m); - } - } -} - - -void Tessellator::visitShape(const RenderPath& path) -{ - // all points at least need to be visit once - // so the points count is at least the same as the count in shape - buffer->vertex.reserve(path.pts.count * 2); - // triangle fans, the indices count is at least triangles number * 3 - buffer->index.reserve((path.pts.count - 2) * 3); - - const Point *firstPt = nullptr; - auto pts = path.pts.data; - - ARRAY_FOREACH(cmd, path.cmds) { - switch (*cmd) { - case PathCommand::MoveTo: { - outlines.push(new VertexList); - auto last = outlines.last(); - last->append(pHeap->allocate(_upScalePoint(*pts))); - firstPt = pts; - pts++; - break; - } - case PathCommand::LineTo: { - auto last = outlines.last(); - last->append(pHeap->allocate(_upScalePoint(*pts))); - pts++; - break; - } - case PathCommand::CubicTo: { - // bezier curve needs to calculate how many segment to split - // for now just break curve into 16 segments for convenient - auto last = outlines.last(); - Point start = _downScalePoint(Point{last->tail->point.x, last->tail->point.y}); - Point c1 = pts[0]; - Point c2 = pts[1]; - Point end = pts[2]; - Bezier curve{start, c1, c2, end}; - - auto stepCount = _bezierCurveCount(curve); - if (stepCount <= 1) stepCount = 2; - auto step = 1.f / stepCount; - - for (uint32_t s = 1; s < static_cast(stepCount); s++) { - last->append(pHeap->allocate(_upScalePoint(curve.at(step * s)))); - } - - last->append(pHeap->allocate(_upScalePoint(end))); - pts += 3; - break; - } - case PathCommand::Close: { - if (firstPt && outlines.count > 0) { - auto last = outlines.last(); - last->append(pHeap->allocate(_upScalePoint(*firstPt))); - firstPt = nullptr; - } - break; - } - default: break; - } - } -} - - -void Tessellator::buildMesh() -{ - Array temp{}; - - for (uint32_t i = 0; i < outlines.count; i++) { - auto list = outlines[i]; - auto prev = list->tail; - auto v = list->head; - - while (v) { - auto next = v->next; - auto edge = this->makeEdge(prev, v); - - if (edge) { - edge->bottom->insertAbove(edge); - edge->top->insertBelow(edge); - } - - temp.push(v); - - prev = v; - v = next; - } - } - - temp.sort(); - - for (uint32_t i = 0; i < temp.count; i++) { - this->pMesh->append(temp[i]); - } -} - - -void Tessellator::mergeVertices() -{ - if (!pMesh->head) return; - - for (auto v = pMesh->head->next; v;) { - auto next = v->next; - - // already sorted, this means these two points is same - if (VertexCompare::compare(v->point, v->prev->point) || length(v->point - v->prev->point) <= 0.025f) { - v->point = v->prev->point; - } - - if (v->point == v->prev->point) { - // merge v into v->prev - while (auto e = v->edge_above.head) { - e->setBottom(v->prev); - } - while (auto e = v->edge_below.head) { - e->setTop(v->prev); - } - pMesh->remove(v); - } - v = next; - } -} - - -bool Tessellator::simplifyMesh() -{ - /// this is a basic sweep line algorithm - /// https://www.youtube.com/watch?v=qkhUNzCGDt0&t=293s - /// in this function, we walk through all edges from top to bottom, and find - /// all intersections edge and break them into flat segments by adding - /// intersection point - - ActiveEdgeList ael{}; - - for (auto v = pMesh->head; v; v = v->next) { - if (!v->isConnected()) continue; - - Edge *left_enclosing = nullptr; - Edge *right_enclosing = nullptr; - auto intersected = false; - - do { - intersected = false; - - ael.findEnclosing(v, &left_enclosing, &right_enclosing); - - v->left = left_enclosing; - v->right = right_enclosing; - - // If AEL is not valid, means we meet the problem caused by floating point precision - if (!ael.valid()) return false; - - if (v->edge_below.head) { - for (auto e = v->edge_below.head; e; e = e->belowNext) { - // check current edge is intersected by left or right neighbor edges - if (checkIntersection(left_enclosing, e, &ael, &v) || - checkIntersection(e, right_enclosing, &ael, &v)) { - intersected = true; - // find intersection between current and it's neighbor - break; - } - } - } else { - // check left and right intersection - if (checkIntersection(left_enclosing, right_enclosing, &ael, &v)) { - intersected = true; - } - } - } while (intersected); - - // If AEL is not valid, means we meet the problem caused by floating point precision - if (!ael.valid()) return false; - - // we are done for all edge end with current point - for (auto e = v->edge_above.head; e; e = e->aboveNext) { - ael.remove(e); - } - - auto left = left_enclosing; - - // insert all edge start from current point into ael - for (auto e = v->edge_below.head; e; e = e->belowNext) { - ael.insert(e, left); - left = e; - } - } - return true; -} - - -bool Tessellator::tessMesh() -{ - /// this function also use sweep line algorithm - /// but during the process, we calculate the winding number of left and right - /// polygon and add edge to them - - ActiveEdgeList ael{}; - - for (auto v = pMesh->head; v; v = v->next) { - if (!v->isConnected()) continue; - if (!ael.valid()) return false; - - Edge *left_enclosing = nullptr; - Edge *right_enclosing = nullptr; - - ael.findEnclosing(v, &left_enclosing, &right_enclosing); - - /** - * - * ... - * \ - * leftPoly head - * v - * - */ - Polygon *leftPoly = nullptr; - /** - * - * ... - * / - * tail rightPoly - * v - * - */ - Polygon *rightPoly = nullptr; - - if (v->edge_above.head) { - leftPoly = v->edge_above.head->leftPoly; - rightPoly = v->edge_above.tail->rightPoly; - } else { - leftPoly = left_enclosing ? left_enclosing->rightPoly : nullptr; - rightPoly = right_enclosing ? right_enclosing->leftPoly : nullptr; - } - - if (v->edge_above.head) { - // add above edge first - if (leftPoly) { - leftPoly = leftPoly->addEdge(v->edge_above.head, Side::kRight, pHeap); - } - - if (rightPoly) { - rightPoly = rightPoly->addEdge(v->edge_above.tail, Side::kLeft, pHeap); - } - - // walk through all edges end with this vertex - for (auto e = v->edge_above.head; e != v->edge_above.tail; e = e->aboveNext) { - auto right_edge = e->aboveNext; - - ael.remove(e); - - if (e->rightPoly) { - e->rightPoly->addEdge(right_edge, Side::kLeft, pHeap); - } - - // this means there is a new polygon between e and right_edge - if (right_edge->leftPoly && right_edge->leftPoly != e->rightPoly) { - right_edge->leftPoly->addEdge(e, Side::kRight, pHeap); - } - } - - ael.remove(v->edge_above.tail); - - // there is no edge begin with this vertex - if (!v->edge_below.head) { - if (leftPoly && rightPoly && leftPoly != rightPoly) { - // polygon not closed at this point - // need to mark these two polygon each other, because they will be - // linked by a cross edge later - - leftPoly->parent = rightPoly; - rightPoly->parent = leftPoly; - } - } - } - - if (v->edge_below.head) { - if (!v->edge_above.head) { - // there is no edge end with this vertex - if (leftPoly && rightPoly) { - - if (leftPoly == rightPoly) { - /** - * leftPoly rightPoly - * - * v - * / \ - * / \ - * ... - */ - if (leftPoly->tail && leftPoly->tail->side == Side::kLeft) { - leftPoly = this->makePoly(leftPoly->lastVertex(), leftPoly->winding); - - left_enclosing->rightPoly = leftPoly; - } else { - rightPoly = this->makePoly(rightPoly->lastVertex(), rightPoly->winding); - - right_enclosing->leftPoly = rightPoly; - } - } - - // need to link this vertex to above polygon - auto join = pHeap->allocate(leftPoly->lastVertex(), v, 1); - - leftPoly = leftPoly->addEdge(join, Side::kRight, pHeap); - rightPoly = rightPoly->addEdge(join, Side::kLeft, pHeap); - } - } - - auto left_edge = v->edge_below.head; - left_edge->leftPoly = leftPoly; - - ael.insert(left_edge, left_enclosing); - - for (auto right_edge = left_edge->belowNext; right_edge; right_edge = right_edge->belowNext) { - ael.insert(right_edge, left_edge); - - int32_t winding = left_edge->leftPoly ? left_edge->leftPoly->winding : 0; - - winding += left_edge->winding; - - if (winding != 0) { - auto poly = this->makePoly(v, winding); - - left_edge->rightPoly = right_edge->leftPoly = poly; - } - - left_edge = right_edge; - } - - v->edge_below.tail->rightPoly = rightPoly; - } - } - return true; -} - - -bool Tessellator::matchFillRule(int32_t winding) -{ - if (fillRule == FillRule::NonZero) { - return winding != 0; - } else { - return (winding & 0x1) != 0; - } -} - - -Edge *Tessellator::makeEdge(Vertex *a, Vertex *b) -{ - if (!a || !b || a->point == b->point) { - return nullptr; - } - - int32_t winding = 1; - - if (VertexCompare::compare(b->point, a->point)) { - winding = -1; - std::swap(a, b); - } - - return pHeap->allocate(a, b, winding); -} - - -bool Tessellator::checkIntersection(Edge *left, Edge *right, ActiveEdgeList *ael, Vertex **current) -{ - if (!left || !right) return false; - - Point p; - - if (left->intersect(right, &p) && !std::isinf(p.x) && !std::isinf(p.y)) { - Vertex *v; - Vertex *top = *current; - - // the vertex in mesh is sorted, so walk to prev can find latest top point - while (top && VertexCompare::compare(p, top->point)) { - top = top->prev; - } - - if (p == left->top->point) { - v = left->top; - } else if (p == left->bottom->point) { - v = left->bottom; - } else if (p == right->top->point) { - v = right->top; - } else if (p == right->bottom->point) { - v = right->bottom; - } else { - // intersect point is between start and end point - // need to insert new vertex - auto prev = top; - while (prev && VertexCompare::compare(p, prev->point)) { - prev = prev->prev; - } - - auto next = prev ? prev->next : pMesh->head; - while (next && VertexCompare::compare(next->point, p)) { - prev = next; - next = next->next; - } - - // check if point is already in mesh - if (prev && prev->point == p) { - v = prev; - } else if (next && next->point == p) { - v = next; - } else { - v = pHeap->allocate(p); - v->point = p; - pMesh->insert(v, prev, next); - } - } - - ael->rewind(current, top ? top : v); - this->splitEdge(left, v, ael, current); - this->splitEdge(right, v, ael, current); - - return true; - } - return this->intersectPairEdge(left, right, ael, current); -} - - -bool Tessellator::splitEdge(Edge *edge, Vertex *v, ActiveEdgeList *ael, Vertex **current) -{ - if (!edge->top || !edge->bottom || v == edge->top || v == edge->bottom) return false; - - auto winding = edge->winding; - Vertex *top; - Vertex *bottom; - - if (VertexCompare::compare(v->point, edge->top->point)) { - /** - * - * v - * \ - * \ - * top - * \ - * \ - * \ - * bottom - */ - top = v; - bottom = edge->top; - winding *= -1; - edge->setTop(v); - } else if (VertexCompare::compare(edge->bottom->point, v->point)) { - /** - * - * top - * \ - * \ - * bottom - * \ - * \ - * \ - * v - */ - top = edge->bottom; - bottom = v; - winding *= -1; - edge->setBottom(v); - } else { - /** - * - * top - * \ - * \ - * v - * \ - * \ - * \ - * bottom - */ - top = v; - bottom = edge->bottom; - edge->setBottom(v); - } - - auto new_edge = pHeap->allocate(top, bottom, winding); - - bottom->insertAbove(new_edge); - top->insertBelow(new_edge); - - if (new_edge->abovePrev == nullptr && new_edge->aboveNext == nullptr) return false; - if (new_edge->belowPrev == nullptr && new_edge->belowNext == nullptr) return false; - - return true; -} - - -bool Tessellator::intersectPairEdge(Edge *left, Edge *right, ActiveEdgeList *ael, - Vertex **current) -{ - if (!left->top || !left->bottom || !right->top || !right->bottom) return false; - if (left->top == right->top || left->bottom == right->bottom) return false; - - if (_calcOrientation(left->bottom->point - left->top->point, right->bottom->point - right->top->point) == Orientation::Linear) return false; - - Edge *split = nullptr; - Vertex *split_at = nullptr; - - // check if these two edge is intersected - if (VertexCompare::compare(left->top->point, right->top->point)) { - if (!left->isLeftOf(right->top->point)) { - split = left; - split_at = right->top; - } - } else { - if (!right->isRightOf(left->top->point)) { - split = right; - split_at = left->top; - } - } - - if (VertexCompare::compare(right->bottom->point, left->bottom->point)) { - if (!left->isLeftOf(right->bottom->point)) { - split = left; - split_at = right->bottom; - } - } else { - if (!right->isRightOf(left->bottom->point)) { - split = right; - split_at = left->bottom; - } - } - - if (!split) return false; - - ael->rewind(current, split->top); - - return splitEdge(split, split_at, ael, current); -} - - -Polygon *Tessellator::makePoly(Vertex *v, int32_t winding) -{ - auto poly = pHeap->allocate(v, winding); - poly->next = this->pPolygon; - this->pPolygon = poly; - return poly; -} - - -void Tessellator::emitPoly(MonotonePolygon *poly) -{ - auto e = poly->first; - VertexList vertices; - - vertices.append(e->top); - int32_t count = 1; - while (e != nullptr) { - if (poly->side == Side::kRight) { - vertices.append(e->bottom); - e = e->rightPolyNext; - } else { - vertices.prepend(e->bottom); - e = e->leftPolyNext; - } - count += 1; - } - - if (count < 3) return; - - auto first = vertices.head; - auto v = first->next; - - while (v != vertices.tail) { - auto prev = v->prev; - auto curr = v; - auto next = v->next; - - if (count == 3) { - emitTriangle(prev, curr, next); - return; - } - - auto ax = curr->point.x - prev->point.x; - auto ay = curr->point.y - prev->point.y; - auto bx = next->point.x - curr->point.x; - auto by = next->point.y - curr->point.y; - - if (ax * by - ay * bx >= 0.0f) { - emitTriangle(prev, curr, next); - v->prev->next = v->next; - v->next->prev = v->prev; - - count--; - - if (v->prev == first) v = v->next; - else v = v->prev; - - } else { - v = v->next; - } - } -} - - -void Tessellator::emitTriangle(Vertex *p1, Vertex *p2, Vertex *p3) -{ - // check if index is generated - if (p1->index == 0xFFFFFFFF) p1->index = _pushVertex(buffer->vertex, _downScaleFloat(p1->point.x), _downScaleFloat(p1->point.y)); - if (p2->index == 0xFFFFFFFF) p2->index = _pushVertex(buffer->vertex, _downScaleFloat(p2->point.x), _downScaleFloat(p2->point.y)); - if (p3->index == 0xFFFFFFFF) p3->index = _pushVertex(buffer->vertex, _downScaleFloat(p3->point.x), _downScaleFloat(p3->point.y)); - - buffer->index.push(p1->index); - buffer->index.push(p2->index); - buffer->index.push(p3->index); -} - - Stroker::Stroker(GlGeometryBuffer* buffer, const Matrix& matrix) : mBuffer(buffer), mMatrix(matrix) { } @@ -1768,7 +377,7 @@ void Stroker::strokeRoundPoint(const Point &p) // Fixme: just use bezier curve to calculate step count auto count = _bezierCurveCount(_bezFromArc(p, p, strokeRadius())) * 2; auto c = _pushVertex(mBuffer->vertex, p.x, p.y); - auto step = 2 * M_PI / (count - 1); + auto step = 2 * MATH_PI / (count - 1); for (uint32_t i = 1; i <= static_cast(count); i++) { float angle = i * step; diff --git a/src/renderer/gl_engine/tvgGlTessellator.h b/src/renderer/gl_engine/tvgGlTessellator.h index a4ad1f36..4c975b8b 100644 --- a/src/renderer/gl_engine/tvgGlTessellator.h +++ b/src/renderer/gl_engine/tvgGlTessellator.h @@ -23,51 +23,11 @@ #ifndef _TVG_GL_TESSELLATOR_H_ #define _TVG_GL_TESSELLATOR_H_ -#include #include "tvgGlCommon.h" namespace tvg { -class ObjectHeap; -struct Vertex; -struct Edge; -struct Polygon; -struct MonotonePolygon; -struct VertexList; -struct ActiveEdgeList; -struct RenderShape; - -class Tessellator final -{ -public: - Tessellator(GlGeometryBuffer* buffer); - ~Tessellator(); - void tessellate(const Array &shapes); - -private: - void visitShape(const RenderPath& path); - void buildMesh(); - void mergeVertices(); - bool simplifyMesh(); - bool tessMesh(); - bool matchFillRule(int32_t winding); - Edge *makeEdge(Vertex* p1, Vertex* p2); - bool checkIntersection(Edge* left, Edge* right, ActiveEdgeList* ael, Vertex** current); - bool splitEdge(Edge* edge, Vertex* v, ActiveEdgeList* ael, Vertex** current); - bool intersectPairEdge(Edge* left, Edge* right, ActiveEdgeList* ael, Vertex** current); - Polygon *makePoly(Vertex* v, int32_t winding); - void emitPoly(MonotonePolygon* poly); - void emitTriangle(Vertex* p1, Vertex* p2, Vertex* p3); - - FillRule fillRule = FillRule::NonZero; - ObjectHeap* pHeap; - Array outlines; - VertexList* pMesh; - Polygon* pPolygon; - GlGeometryBuffer* buffer; -}; - class Stroker { struct State @@ -79,7 +39,6 @@ class Stroker }; public: Stroker(GlGeometryBuffer* buffer, const Matrix& matrix); - ~Stroker() = default; void stroke(const RenderShape *rshape, const RenderPath& path); RenderRegion bounds() const; @@ -147,7 +106,6 @@ class BWTessellator { public: BWTessellator(GlGeometryBuffer* buffer); - ~BWTessellator() = default; void tessellate(const RenderPath& path, const Matrix& matrix); RenderRegion bounds() const;