diff --git a/src/renderer/wg_engine/meson.build b/src/renderer/wg_engine/meson.build index 4165a765..9aed3df7 100755 --- a/src/renderer/wg_engine/meson.build +++ b/src/renderer/wg_engine/meson.build @@ -12,7 +12,6 @@ source_file = [ 'tvgWgBindGroups.cpp', 'tvgWgCommon.cpp', 'tvgWgCompositor.cpp', - 'tvgWgGeometry.cpp', 'tvgWgPipelines.cpp', 'tvgWgRenderData.cpp', 'tvgWgRenderer.cpp', diff --git a/src/renderer/wg_engine/tvgWgCompositor.cpp b/src/renderer/wg_engine/tvgWgCompositor.cpp index bf68d636..62cdadd3 100755 --- a/src/renderer/wg_engine/tvgWgCompositor.cpp +++ b/src/renderer/wg_engine/tvgWgCompositor.cpp @@ -48,9 +48,9 @@ void WgCompositor::initialize(WgContext& context, uint32_t width, uint32_t heigh storageInterm.initialize(context, width, height); storageDstCopy.initialize(context, width, height); // composition and blend geometries - WgGeometryData geometryData; - geometryData.appendBlitBox(); - meshData.update(context, &geometryData); + WgVertexBufferInd vertexBuffer; + vertexBuffer.appendBlitBox(); + meshData.update(context, vertexBuffer); } diff --git a/src/renderer/wg_engine/tvgWgGeometry.cpp b/src/renderer/wg_engine/tvgWgGeometry.cpp deleted file mode 100755 index 9002e61a..00000000 --- a/src/renderer/wg_engine/tvgWgGeometry.cpp +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (c) 2023 - 2024 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. - */ - -#include "tvgWgGeometry.h" - -//*********************************************************************** -// WgPolyline -//*********************************************************************** - -WgMath* WgGeometryData::gMath = nullptr; - -void WgMath::initialize() -{ - if (initialized) return; - initialized = true; - constexpr uint32_t nPoints = 360; - sinus.reserve(nPoints); - cosin.reserve(nPoints); - for (uint32_t i = 0; i < nPoints; i++) { - float angle = i * (2 * MATH_PI) / nPoints; - sinus.push(sin(angle)); - cosin.push(cos(angle)); - } -}; - - -void WgMath::release() { - sinus.clear(); - cosin.clear(); -}; - -//*********************************************************************** -// WgPolyline -//*********************************************************************** - -WgPolyline::WgPolyline() -{ - constexpr uint32_t nPoints = 360; - pts.reserve(nPoints); - dist.reserve(nPoints); - closed = false; -} - - -void WgPolyline::appendPoint(WgPoint pt) -{ - if (pts.count > 0) { - float distance = pts.last().dist(pt); - // adjust precision because of real user data points - // can be further than the accepted accuracy, - // but still be considered tha same - if (!tvg::zero(distance*1e-1)) { - // update min and max indexes - iminx = pts[iminx].x >= pt.x ? pts.count : iminx; - imaxx = pts[imaxx].x <= pt.x ? pts.count : imaxx; - iminy = pts[iminy].y >= pt.y ? pts.count : iminy; - imaxy = pts[imaxy].y <= pt.y ? pts.count : imaxy; - // update total length - len += distance; - // update points and distances - pts.push(pt); - dist.push(distance); - }; - } else { - // reset min and max indexes and total length - iminx = imaxx = iminy = imaxy = 0; - len = 0.0f; - // update points and distances - pts.push(pt); - dist.push(0.0f); - } -} - - -void WgPolyline::appendCubic(WgPoint p1, WgPoint p2, WgPoint p3, size_t nsegs) -{ - WgPoint p0 = pts.count > 0 ? pts.last() : WgPoint(0.0f, 0.0f); - nsegs = nsegs == 0 ? 1 : nsegs; - float dt = 1.0f / (float)nsegs; - for (auto t = 0.0f; t <= 1.0f; t += dt) { - // get cubic spline interpolation coefficients - float t0 = 1.0f * (1.0f - t) * (1.0f - t) * (1.0f - t); - float t1 = 3.0f * (1.0f - t) * (1.0f - t) * t; - float t2 = 3.0f * (1.0f - t) * t * t; - float t3 = 1.0f * t * t * t; - appendPoint(p0 * t0 + p1 * t1 + p2 * t2 + p3 * t3); - } -} - - -void WgPolyline::trim(WgPolyline* polyline, float trimBegin, float trimEnd) const -{ - assert(polyline); - float begLen = len * trimBegin; - float endLen = len * trimEnd; - float currentLength = 0.0f; - // find start point - uint32_t indexStart = 0; - WgPoint pointStart{}; - currentLength = 0.0f; - for (indexStart = 1; indexStart < pts.count; indexStart++) { - currentLength += dist[indexStart]; - if(currentLength >= begLen) { - float t = 1.0f - (currentLength - begLen) / dist[indexStart]; - pointStart = pts[indexStart-1] * (1.0f - t) + pts[indexStart] * t; - break; - } - } - if (indexStart >= pts.count) return; - // find end point - uint32_t indexEnd = 0; - WgPoint pointEnd{}; - currentLength = 0.0f; - for (indexEnd = 1; indexEnd < pts.count; indexEnd++) { - currentLength += dist[indexEnd]; - if(currentLength >= endLen) { - float t = 1.0f - (currentLength - endLen) / dist[indexEnd]; - pointEnd = pts[indexEnd-1] * (1.0f - t) + pts[indexEnd] * t; - break; - } - } - if (indexEnd >= pts.count) return; - // fill polyline - polyline->appendPoint(pointStart); - for (uint32_t i = indexStart; i <= indexEnd - 1; i++) - polyline->appendPoint(pts[i]); - polyline->appendPoint(pointEnd); -} - - -void WgPolyline::close() -{ - if (pts.count > 0) appendPoint(pts[0]); - closed = true; -} - - -void WgPolyline::clear() -{ - // reset min and max indexes and total length - iminx = imaxx = iminy = imaxy = 0; - len = 0.0f; - // clear points and distances - pts.clear(); - dist.clear(); - closed = false; -} - - -void WgPolyline::getBBox(WgPoint& pmin, WgPoint& pmax) const -{ - pmin.x = pts[iminx].x; - pmin.y = pts[iminy].y; - pmax.x = pts[imaxx].x; - pmax.y = pts[imaxy].y; -} - -//*********************************************************************** -// WgGeometryData -//*********************************************************************** - -WgGeometryData::WgGeometryData() { - constexpr uint32_t nPoints = 10240; - positions.pts.reserve(nPoints); - texCoords.reserve(nPoints); - indexes.reserve(nPoints); -} - - -void WgGeometryData::clear() -{ - indexes.clear(); - positions.clear(); - texCoords.clear(); -} - - -void WgGeometryData::appendBox(WgPoint pmin, WgPoint pmax) -{ - appendRect( - { pmin.x, pmin.y }, - { pmax.x, pmin.y }, - { pmin.x, pmax.y }, - { pmax.x, pmax.y }); -} - - -void WgGeometryData::appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3) -{ - uint32_t index = positions.pts.count; - positions.appendPoint(p0); // +0 - positions.appendPoint(p1); // +1 - positions.appendPoint(p2); // +2 - positions.appendPoint(p3); // +3 - indexes.push(index + 0); - indexes.push(index + 1); - indexes.push(index + 2); - indexes.push(index + 1); - indexes.push(index + 3); - indexes.push(index + 2); -} - - -void WgGeometryData::appendCircle(WgPoint center, float radius, float scale) -{ - uint32_t indexCenter = positions.pts.count; - positions.appendPoint(center); - uint32_t index = positions.pts.count; - positions.appendPoint({ center.x + gMath->sinus[0] * radius, center.y + gMath->cosin[0] * radius }); - uint32_t nPoints = (uint32_t)(scale * radius * 2.0f); - nPoints = nPoints < 8 ? 8 : nPoints; - const uint32_t step = gMath->sinus.count / nPoints; - for (uint32_t i = step; i < gMath->sinus.count; i += step) { - positions.appendPoint({ center.x + gMath->sinus[i] * radius, center.y + gMath->cosin[i] * radius }); - indexes.push(index); // prev point - indexes.push(indexCenter); // center point - indexes.push(index + 1); // curr point - index++; - } - positions.appendPoint({ center.x + gMath->sinus[0] * radius, center.y + gMath->cosin[0] * radius }); - indexes.push(index); // prev point - indexes.push(indexCenter); // center point - indexes.push(index + 1); // curr point - positions.appendPoint(center); -} - - -void WgGeometryData::appendImageBox(float w, float h) -{ - positions.appendPoint({ 0.0f, 0.0f }); - positions.appendPoint({ w , 0.0f }); - positions.appendPoint({ w , h }); - positions.appendPoint({ 0.0f, h }); - texCoords.push({ 0.0f, 0.0f }); - texCoords.push({ 1.0f, 0.0f }); - texCoords.push({ 1.0f, 1.0f }); - texCoords.push({ 0.0f, 1.0f }); - indexes.push(0); - indexes.push(1); - indexes.push(2); - indexes.push(0); - indexes.push(2); - indexes.push(3); -} - - -void WgGeometryData::appendBlitBox() -{ - positions.appendPoint({ -1.0f, +1.0f }); - positions.appendPoint({ +1.0f, +1.0f }); - positions.appendPoint({ +1.0f, -1.0f }); - positions.appendPoint({ -1.0f, -1.0f }); - texCoords.push({ 0.0f, 0.0f }); - texCoords.push({ 1.0f, 0.0f }); - texCoords.push({ 1.0f, 1.0f }); - texCoords.push({ 0.0f, 1.0f }); - indexes.push(0); - indexes.push(1); - indexes.push(2); - indexes.push(0); - indexes.push(2); - indexes.push(3); -} - - -void WgGeometryData::appendStrokeDashed(const WgPolyline* polyline, const RenderStroke *stroke, float scale) -{ - assert(stroke); - assert(polyline); - static WgPolyline dashed; - dashed.clear(); - // ignore single points polyline - // append multiple points dashed polyline - if (polyline->pts.count >= 2) { - auto& pts = polyline->pts; - auto& dist = polyline->dist; - // starting state - uint32_t dashIndex = 0; - float currentLength = stroke->dashPattern[dashIndex]; - // iterate by polyline points - for (uint32_t i = 0; i < pts.count - 1; i++) { - // append current polyline point - if (dashIndex % 2 == 0) - dashed.appendPoint(pts[i]); - // move inside polyline segment - while(currentLength < dist[i+1]) { - // get current point - float t = currentLength / dist[i+1]; - dashed.appendPoint(pts[i] + (pts[i+1] - pts[i]) * t); - // update current state - dashIndex = (dashIndex + 1) % stroke->dashCnt; - currentLength += stroke->dashPattern[dashIndex]; - // append stroke if dash - if (dashIndex % 2 != 0) { - appendStroke(&dashed, stroke, scale); - dashed.clear(); - } - } - // update current subline length - currentLength -= dist[i+1]; - } - // draw last subline - if (dashIndex % 2 == 0) { - dashed.appendPoint(pts.last()); - appendStroke(&dashed, stroke, scale); - dashed.clear(); - } - } -} - - -void WgGeometryData::appendStrokeJoin(const WgPoint& v0, const WgPoint& v1, const WgPoint& v2, StrokeJoin join, float halfWidth, float miterLimit, float scale) -{ - WgPoint dir0 = (v1 - v0).normal(); - WgPoint dir1 = (v2 - v1).normal(); - WgPoint nrm0 { +dir0.y, -dir0.x }; - WgPoint nrm1 { +dir1.y, -dir1.x }; - WgPoint offset0 = nrm0 * halfWidth; - WgPoint offset1 = nrm1 * halfWidth; - if (join == StrokeJoin::Round) { - appendCircle(v1, halfWidth, scale); - } else if (join == StrokeJoin::Bevel) { - appendRect(v1 - offset0, v1 + offset1, v1 - offset1, v1 + offset0); - } else if (join == StrokeJoin::Miter) { - WgPoint nrm = (nrm0 + nrm1); - // adjust precision because dot product could return above 1 that results acos returns Nan - if (!tvg::zero((dir0.x * dir1.y - dir0.y * dir1.x)*1e-1)) { - nrm.normalize(); - float cosine = nrm.dot(nrm0); - float angle = std::acos(dir0.dot(dir1.negative())); - float miterRatio = 1.0f / (std::sin(angle) * 0.5); - if (miterRatio <= miterLimit) { - appendRect(v1 + nrm * (halfWidth / cosine), v1 + offset0, v1 + offset1, v1); - appendRect(v1 - nrm * (halfWidth / cosine), v1 - offset0, v1 - offset1, v1); - } else { - appendRect(v1 - offset0, v1 + offset1, v1 - offset1, v1 + offset0); - } - } - } -} - - -void WgGeometryData::appendStroke(const WgPolyline* polyline, const RenderStroke *stroke, float scale) -{ - assert(stroke); - assert(polyline); - float wdt = stroke->width * 0.5f; - - // single line sub-path - if (polyline->pts.count == 2) { - WgPoint v0 = polyline->pts[0]; - WgPoint v1 = polyline->pts[1]; - WgPoint dir0 = (v1 - v0) / polyline->dist[1]; - WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x }; - if (stroke->cap == StrokeCap::Round) { - appendRect(v0 - nrm0 * wdt, v0 + nrm0 * wdt, v1 - nrm0 * wdt, v1 + nrm0 * wdt); - appendCircle(polyline->pts[0], wdt, scale); - appendCircle(polyline->pts[1], wdt, scale); - } else if (stroke->cap == StrokeCap::Butt) { - appendRect(v0 - nrm0 * wdt, v0 + nrm0 * wdt, v1 - nrm0 * wdt, v1 + nrm0 * wdt); - } else if (stroke->cap == StrokeCap::Square) { - appendRect( - v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt, - v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt - ); - } - } else if (polyline->pts.count > 2) { // multi-lined sub-path - if (polyline->closed) { - WgPoint v0 = polyline->pts[polyline->pts.count - 2]; - WgPoint v1 = polyline->pts[0]; - WgPoint v2 = polyline->pts[1]; - appendStrokeJoin(v0, v1, v2, stroke->join, wdt, stroke->miterlimit, scale); - } else { - // append first cap - WgPoint v0 = polyline->pts[0]; - WgPoint v1 = polyline->pts[1]; - WgPoint dir0 = (v1 - v0) / polyline->dist[1]; - WgPoint nrm0 = WgPoint{ -dir0.y, +dir0.x }; - if (stroke->cap == StrokeCap::Round) { - appendCircle(v0, wdt, scale); - } else if (stroke->cap == StrokeCap::Butt) { - // no cap needed - } else if (stroke->cap == StrokeCap::Square) { - appendRect(v0 - nrm0 * wdt - dir0 * wdt, v0 + nrm0 * wdt - dir0 * wdt, v0 - nrm0 * wdt, v0 + nrm0 * wdt); - } - - // append last cap - v0 = polyline->pts[polyline->pts.count - 2]; - v1 = polyline->pts[polyline->pts.count - 1]; - dir0 = (v1 - v0) / polyline->dist[polyline->pts.count - 1]; - nrm0 = WgPoint{ -dir0.y, +dir0.x }; - if (stroke->cap == StrokeCap::Round) { - appendCircle(v1, wdt, scale); - } else if (stroke->cap == StrokeCap::Butt) { - // no cap needed - } else if (stroke->cap == StrokeCap::Square) { - appendRect(v1 - nrm0 * wdt, v1 + nrm0 * wdt, v1 - nrm0 * wdt + dir0 * wdt, v1 + nrm0 * wdt + dir0 * wdt); - } - } - - // append sub-lines - for (uint32_t i = 0; i < polyline->pts.count - 1; i++) { - float dist1 = polyline->dist[i + 1]; - WgPoint v1 = polyline->pts[i + 0]; - WgPoint v2 = polyline->pts[i + 1]; - WgPoint dir1 = (v2 - v1) / dist1; - WgPoint nrm1 { +dir1.y, -dir1.x }; - WgPoint offset1 = nrm1 * wdt; - appendRect(v1 - offset1, v1 + offset1, v2 - offset1, v2 + offset1); - - if (i > 0) { - WgPoint v0 = polyline->pts[i - 1]; - appendStrokeJoin(v0, v1, v2, stroke->join, wdt, stroke->miterlimit, scale); - } - } - } -} diff --git a/src/renderer/wg_engine/tvgWgGeometry.h b/src/renderer/wg_engine/tvgWgGeometry.h index ff0f61de..b3e79a72 100755 --- a/src/renderer/wg_engine/tvgWgGeometry.h +++ b/src/renderer/wg_engine/tvgWgGeometry.h @@ -24,102 +24,434 @@ #define _TVG_WG_GEOMETRY_H_ #include +#include #include "tvgMath.h" #include "tvgRender.h" -#include "tvgArray.h" -class WgPoint -{ -public: - float x; - float y; +// base vector operations +static Point operator-(const Point& a) { return {-a.x, -a.y}; } +static inline float length2(const Point& a) { return a.x*a.x+a.y*a.y; }; +static inline float distance2(const Point& a, const Point& b) { return length2(a - b); }; +static inline float distance(const Point& a, const Point& b) { return length(a - b); }; +static inline float dot(const Point& a, const Point& b) { return a.x*b.x + a.y*b.y; }; +static inline Point min(const Point& a, const Point& b) { return { std::min(a.x, b.x), std::min(a.y, b.y) }; }; +static inline Point max(const Point& a, const Point& b) { return { std::max(a.x, b.x), std::max(a.y, b.y) }; }; +static inline Point lerp(const Point& a, const Point& b, float t) { return a * (1.0f - t) + b * t; }; +static inline Point normalize(const Point& a) { float rlen = 1.0f / length(a); return { a.x * rlen, a.y * rlen }; } - WgPoint() {} - WgPoint(float x, float y): x(x), y(y) {} - WgPoint(const Point& p): x(p.x), y(p.y) {} +// default size of vertex and index buffers +#define WG_POINTS_COUNT 16384 - WgPoint operator + (const WgPoint& p) const { return { x + p.x, y + p.y }; } - WgPoint operator - (const WgPoint& p) const { return { x - p.x, y - p.y }; } - WgPoint operator * (const WgPoint& p) const { return { x * p.x, y * p.y }; } - WgPoint operator / (const WgPoint& p) const { return { x / p.x, y / p.y }; } - - WgPoint operator + (const float c) const { return { x + c, y + c }; } - WgPoint operator - (const float c) const { return { x - c, y - c }; } - WgPoint operator * (const float c) const { return { x * c, y * c }; } - WgPoint operator / (const float c) const { return { x / c, y / c }; } - - WgPoint negative() const { return {-x, -y}; } - inline void negate() { x = -x; y = -y; } - inline float length() const { return sqrt(x*x + y*y); } - inline float length2() const { return x*x + y*y; } - inline float dot(const WgPoint& p) const { return x * p.x + y * p.y; } - inline float dist(const WgPoint& p) const { return sqrt(dist2(p)); } - inline float dist2(const WgPoint& p) const { return ((p.x - x)*(p.x - x) + (p.y - y)*(p.y - y)); } - inline bool equal(const WgPoint& p) const { return tvg::equal(x, p.x) && tvg::equal(y, p.y); } - inline void normalize() { float rlen = 1.0f / length(); x *= rlen; y *= rlen; } - inline WgPoint normal() const { float rlen = 1.0f / length(); return { x * rlen, y * rlen }; } - inline WgPoint lerp(const WgPoint& p, float t) const { return { x + (p.x - x) * t, y + (p.y - y) * t }; }; - inline WgPoint trans(const Matrix& m) const { return { x * m.e11 + y * m.e12 + m.e13, x * m.e21 + y * m.e22 + m.e23 }; }; -}; - -struct WgMath -{ - Array sinus; - Array cosin; - bool initialized{}; - - void initialize(); - void release(); -}; - - -struct WgPolyline -{ - Array pts; - Array dist; - // polyline bbox points indexes - uint32_t iminx{}; - uint32_t iminy{}; - uint32_t imaxx{}; - uint32_t imaxy{}; - // total polyline length - float len{}; +// simple vertex buffer +struct WgVertexBuffer { + Point vbuff[WG_POINTS_COUNT]; // vertex buffer + float vdist[WG_POINTS_COUNT]; // distance to previous point + float vleng[WG_POINTS_COUNT]; // distance to the first point through all previous points + size_t vcount{}; bool closed{}; - WgPolyline(); + // callback for external process of polyline + using onPolylineFn = std::function; - void appendPoint(WgPoint pt); - void appendCubic(WgPoint p1, WgPoint p2, WgPoint p3, size_t nsegs = 16); + // reset buffer + void reset() { + vcount = 0; + closed = false; + } - void trim(WgPolyline* polyline, float trimBegin, float trimEnd) const; + // get the last point with optional index offset from the end + Point last(size_t offset = 0) const { + return vbuff[vcount - offset - 1]; + } - void close(); - void clear(); + // get the last distance with optional index offset from the end + float lastDist(size_t offset = 0) const { + return vdist[vcount - offset - 1]; + } - void getBBox(WgPoint& pmin, WgPoint& pmax) const; + // get total length + float total() const { + return (vcount == 0) ? 0.0f : vleng[vcount-1]; + } + + // get next vertex index by length using binary search + size_t getIndexByLength(float len) const { + if (vcount <= 1) return 0; + size_t left = 0; + size_t right = vcount - 1; + while (left <= right) { + size_t mid = left + (right - left) / 2; + if (vleng[mid] == len) return mid; + else if (vleng[mid] < len) left = mid + 1; + else right = mid - 1; + } + return right + 1; + } + + // get min and max values of the buffer + void getMinMax(Point& pmin, Point& pmax) const { + if (vcount == 0) return; + pmax = pmin = vbuff[0]; + for (size_t i = 1; i < vcount; i++) { + pmin = min(pmin, vbuff[i]); + pmax = max(pmax, vbuff[i]); + } + } + + // update points distancess to the prev point and total length + void updateDistances() { + if (vcount == 0) return; + vdist[0] = 0.0f; + vleng[0] = 0.0f; + for (size_t i = 1; i < vcount; i++) { + vdist[i] = distance(vbuff[i-1], vbuff[i]); + vleng[i] = vleng[i-1] + vdist[i]; + } + } + + // close vertex buffer + void close() { + // check if last point is not to close to the first point + if (!tvg::zero(distance2(vbuff[0], last()))) + append(vbuff[0]); + closed = true; + } + + // append point + void append(const Point& p) { + vbuff[vcount] = p; + vcount++; + } + + // append source vertex buffer in index range from start to end (end not included) + void appendRange(const WgVertexBuffer& buff, size_t start_index, size_t end_index) { + if (start_index <= end_index) + for (size_t i = start_index; i < end_index; i++) + append(buff.vbuff[i]); + if (start_index > end_index) { + for (size_t i = start_index; i < buff.vcount; i++) + append(buff.vbuff[i]); + for (size_t i = 0; i < end_index; i++) + append(buff.vbuff[i]); + } + } + + // append circle (list of triangles) + void appendCircle(float radius) { + // get approx circle length + float clen = 2.0f * radius * MATH_PI; + size_t nsegs = std::max((uint32_t)(clen / 8), 16U); + // append circle + Point prev { std::sin(0.0f) * radius, std::cos(0.0f) * radius }; + for (size_t i = 1; i <= nsegs; i++) { + float t = (2.0f * MATH_PI * i) / nsegs; + Point curr { std::sin(t) * radius, std::cos(t) * radius }; + append(Point{0.0f, 0.0f}); + append(prev); + append(curr); + prev = curr; + } + } + + // append cubic spline + void appendCubic(const Point& v0, const Point& v1, const Point& v2, const Point& v3) { + // get approx cubic length + float clen = distance(v0, v1) + distance(v1, v2) + distance(v2, v3); + size_t nsegs = std::max((uint32_t)(clen / 8), 16U); + // append cubic + Bezier bezier{v0, v1, v2, v3}; + for (size_t i = 1; i <= nsegs; i++) + append(bezier.at((float)i / nsegs)); + } + + // trim source buffer + void trim(const WgVertexBuffer& buff, float beg, float end) { + // empty buffer guard + if (buff.vcount == 0) return; + // initialize + float len_beg = buff.total() * beg; + float len_end = buff.total() * end; + // find points + size_t index_beg = buff.getIndexByLength(len_beg); + size_t index_end = buff.getIndexByLength(len_end); + float len_total_beg = buff.vleng[index_beg]; + float len_total_end = buff.vleng[index_end]; + float len_seg_beg = buff.vdist[index_beg]; + float len_seg_end = buff.vdist[index_end]; + // append points + float t_beg = len_seg_beg > 0.0f ? 1.0f - (len_total_beg - len_beg) / len_seg_beg : 0.0f; + float t_end = len_seg_end > 0.0f ? 1.0f - (len_total_end - len_end) / len_seg_end : 0.0f; + if (index_beg > 0) append(lerp(buff.vbuff[index_beg-1], buff.vbuff[index_beg], t_beg)); + appendRange(buff, index_beg, index_end); + if (index_end > 0) append(lerp(buff.vbuff[index_end-1], buff.vbuff[index_end], t_end)); + } + + + // decode path with callback for external prcesses + void decodePath(const RenderShape& rshape, bool update_dist, onPolylineFn onPolyline) { + // decode path + reset(); + size_t pntIndex = 0; + for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) { + PathCommand cmd = rshape.path.cmds[cmdIndex]; + if (cmd == PathCommand::MoveTo) { + // after path decoding we need to update distances and total length + if (update_dist) updateDistances(); + if ((onPolyline) && (vcount != 0)) + onPolyline(*this); + reset(); + append(rshape.path.pts[pntIndex]); + pntIndex++; + } else if (cmd == PathCommand::LineTo) { + append(rshape.path.pts[pntIndex]); + pntIndex++; + } else if (cmd == PathCommand::Close) { + close(); + } else if (cmd == PathCommand::CubicTo) { + appendCubic(vbuff[vcount - 1], rshape.path.pts[pntIndex + 0], rshape.path.pts[pntIndex + 1], rshape.path.pts[pntIndex + 2]); + pntIndex += 3; + } + } + // after path decoding we need to update distances and total length + if (update_dist) updateDistances(); + if ((onPolyline) && (vcount != 0)) + onPolyline(*this); + } }; +// simple indexed vertex buffer +struct WgVertexBufferInd { + Point vbuff[WG_POINTS_COUNT*2]; + Point tbuff[WG_POINTS_COUNT*2]; + uint32_t ibuff[WG_POINTS_COUNT*4]; + size_t vcount = 0; + size_t icount = 0; -struct WgGeometryData -{ - static WgMath* gMath; + // reset buffer + void reset() { + icount = vcount = 0; + } - WgPolyline positions{}; - Array texCoords{}; - Array indexes{}; + // get min and max values of the buffer + void getMinMax(Point& pmin, Point& pmax) const { + if (vcount == 0) return; + pmax = pmin = vbuff[0]; + for (size_t i = 1; i < vcount; i++) { + pmin = min(pmin, vbuff[i]); + pmax = max(pmax, vbuff[i]); + } + } - WgGeometryData(); - void clear(); + // append image box with tex coords + void appendImageBox(float w, float h) { + Point points[4] { { 0.0f, 0.0f }, { w, 0.0f }, { w, h }, { 0.0f, h } }; + appendImageBox(points); + } - void appendBox(WgPoint pmin, WgPoint pmax); - void appendRect(WgPoint p0, WgPoint p1, WgPoint p2, WgPoint p3); - void appendCircle(WgPoint center, float radius, float scale = 1.0f); - void appendImageBox(float w, float h); - void appendBlitBox(); - void appendStrokeDashed(const WgPolyline* polyline, const RenderStroke *stroke, float scale); - void appendStrokeJoin(const WgPoint& v0, const WgPoint& v1, const WgPoint& v2, - StrokeJoin join, float halfWidth, float miterLimit, float scale); - void appendStroke(const WgPolyline* polyline, const RenderStroke *stroke, float scale); + // append blit box with tex coords + void appendBlitBox() { + Point points[4] { { -1.0f, +1.0f }, { +1.0f, +1.0f }, { +1.0f, -1.0f }, { -1.0f, -1.0f } }; + appendImageBox(points); + } + + // append image box with tex coords + void appendImageBox(Point points[4]) { + // append vertexes + vbuff[vcount+0] = points[0]; + vbuff[vcount+1] = points[1]; + vbuff[vcount+2] = points[2]; + vbuff[vcount+3] = points[3]; + // append tex coords + tbuff[vcount+0] = { 0.0f, 0.0f }; + tbuff[vcount+1] = { 1.0f, 0.0f }; + tbuff[vcount+2] = { 1.0f, 1.0f }; + tbuff[vcount+3] = { 0.0f, 1.0f }; + // append indexes + ibuff[icount+0] = vcount + 0; + ibuff[icount+1] = vcount + 1; + ibuff[icount+2] = vcount + 2; + ibuff[icount+3] = vcount + 0; + ibuff[icount+4] = vcount + 2; + ibuff[icount+5] = vcount + 3; + // update buffer + vcount += 4; + icount += 6; + } + + // append quad - two triangles formed from four points + void appendQuad(const Point& p0, const Point& p1, const Point& p2, const Point& p3) { + // append vertexes + vbuff[vcount+0] = p0; + vbuff[vcount+1] = p1; + vbuff[vcount+2] = p2; + vbuff[vcount+3] = p3; + // append indexes + ibuff[icount+0] = vcount + 0; + ibuff[icount+1] = vcount + 1; + ibuff[icount+2] = vcount + 2; + ibuff[icount+3] = vcount + 1; + ibuff[icount+4] = vcount + 3; + ibuff[icount+5] = vcount + 2; + // update buffer + vcount += 4; + icount += 6; + } + + // dash buffer by pattern + void appendStrokesDashed(const WgVertexBuffer& buff, const RenderStroke* rstroke) { + // dashed buffer + WgVertexBuffer dashed; + dashed.reset(); + // ignore single points polyline + if (buff.vcount < 2) return; + const float* dashPattern = rstroke->dashPattern; + size_t dashCnt = rstroke->dashCnt; + // starting state + uint32_t index_dash = 0; + float len_total = dashPattern[index_dash]; + // iterate by polyline points + for (uint32_t i = 0; i < buff.vcount - 1; i++) { + // append current polyline point + if (index_dash % 2 == 0) + dashed.append(buff.vbuff[i]); + // move inside polyline segment + while(len_total < buff.vdist[i+1]) { + // get current point + float t = len_total / buff.vdist[i+1]; + dashed.append(buff.vbuff[i] + (buff.vbuff[i+1] - buff.vbuff[i]) * t); + // update current state + index_dash = (index_dash + 1) % dashCnt; + len_total += dashPattern[index_dash]; + // preceed stroke if dash + if (index_dash % 2 != 0) { + dashed.updateDistances(); + appendStrokes(dashed, rstroke); + dashed.reset(); + } + } + // update current subline length + len_total -= buff.vdist[i+1]; + } + // draw last subline + if (index_dash % 2 == 0) { + dashed.append(buff.last()); + dashed.updateDistances(); + appendStrokes(dashed, rstroke); + } + } + + // append buffer with optional offset + void appendBuffer(const WgVertexBuffer& buff, Point offset = Point{0.0f, 0.0f}) { + for (uint32_t i = 0; i < buff.vcount; i++ ) { + vbuff[vcount + i] = buff.vbuff[i] + offset; + ibuff[icount + i] = vcount + i; + } + vcount += buff.vcount; + icount += buff.vcount; + }; + + // append line + void appendLine(const Point& v0, const Point& v1, float dist, float halfWidth) { + Point sub = v1 - v0; + Point nrm = { +sub.y / dist * halfWidth, -sub.x / dist * halfWidth }; + appendQuad(v0 - nrm, v0 + nrm, v1 - nrm, v1 + nrm); + } + + // append bevel joint + void appendBevel(const Point& v0, const Point& v1, const Point& v2, float dist1, float dist2, float halfWidth) { + Point sub1 = v1 - v0; + Point sub2 = v2 - v1; + Point nrm1 { +sub1.y / dist1 * halfWidth, -sub1.x / dist1 * halfWidth }; + Point nrm2 { +sub2.y / dist2 * halfWidth, -sub2.x / dist2 * halfWidth }; + appendQuad(v1 - nrm1, v1 + nrm1, v1 - nrm2, v1 + nrm2); + } + + // append miter joint + void appendMitter(const Point& v0, const Point& v1, const Point& v2, float dist1, float dist2, float halfWidth, float miterLimit) { + Point sub1 = v1 - v0; + Point sub2 = v2 - v1; + Point nrm1 { +sub1.y / dist1, -sub1.x / dist1 }; + Point nrm2 { +sub2.y / dist2, -sub2.x / dist2 }; + Point offset1 = nrm1 * halfWidth; + Point offset2 = nrm2 * halfWidth; + Point nrm = normalize(nrm1 + nrm2); + float cosine = dot(nrm, nrm1); + float angle = std::acos(dot(nrm1, -nrm2)); + float miterRatio = 1.0f / (std::sin(angle) * 0.5f); + if (miterRatio <= miterLimit) { + appendQuad(v1 + nrm * (halfWidth / cosine), v1 + offset2, v1 + offset1, v1); + appendQuad(v1 - nrm * (halfWidth / cosine), v1 - offset2, v1 - offset1, v1); + } else { + appendQuad(v1 - offset1, v1 + offset2, v1 - offset2, v1 + offset1); + } + } + + // append square cap + void appendSquare(Point v0, Point v1, float dist, float halfWidth) { + Point sub = v1 - v0; + Point offset = sub / dist * halfWidth; + Point nrm = { +offset.y, -offset.x }; + appendQuad(v1 - nrm, v1 + nrm, v1 + offset - nrm, v1 + offset + nrm); + } + + // append strokes + void appendStrokes(const WgVertexBuffer& buff, const RenderStroke* rstroke) { + assert(rstroke); + // empty buffer gueard + if (buff.vcount < 2) return; + float halfWidth = rstroke->width * 0.5f; + + // append core lines + for (size_t i = 1; i < buff.vcount; i++) + appendLine(buff.vbuff[i-1], buff.vbuff[i], buff.vdist[i], halfWidth); + + // append caps (square) + if ((rstroke->cap == StrokeCap::Square) && !buff.closed) { + appendSquare(buff.vbuff[1], buff.vbuff[0], buff.vdist[1], halfWidth); + appendSquare(buff.last(1), buff.last(0), buff.lastDist(0), halfWidth); + } + + // append round joints and caps + if ((rstroke->join == StrokeJoin::Round) || (rstroke->cap == StrokeCap::Round)) { + // create mesh for circle + WgVertexBuffer circle; + circle.appendCircle(halfWidth); + // append caps (round) + if (rstroke->cap == StrokeCap::Round) { + appendBuffer(circle, buff.vbuff[0]); + // append ending cap if polyline is not closed + if (!buff.closed) + appendBuffer(circle, buff.last()); + } + // append joints (round) + if (rstroke->join == StrokeJoin::Round) { + for (size_t i = 1; i < buff.vcount - 1; i++) + appendBuffer(circle, buff.vbuff[i]); + if (buff.closed) appendBuffer(circle, buff.last()); + } + } + + // append closed endings + if (buff.closed) { + // close by bevel + if (rstroke->join == StrokeJoin::Bevel) + appendBevel(buff.last(1), buff.vbuff[0], buff.vbuff[1], buff.lastDist(0), buff.vdist[1], halfWidth); + // close by mitter + else if (rstroke->join == StrokeJoin::Miter) { + appendMitter(buff.last(1), buff.vbuff[0], buff.vbuff[1], buff.lastDist(0), buff.vdist[1], halfWidth, rstroke->miterlimit); + } + } + + // append joints (bevel) + if (rstroke->join == StrokeJoin::Bevel) { + for (size_t i = 1; i < buff.vcount - 1; i++) + appendBevel(buff.vbuff[i-1], buff.vbuff[i], buff.vbuff[i+1], buff.vdist[i], buff.vdist[i+1], halfWidth); + // append joints (mitter) + } else if (rstroke->join == StrokeJoin::Miter) { + for (size_t i = 1; i < buff.vcount - 1; i++) + appendMitter(buff.vbuff[i-1], buff.vbuff[i], buff.vbuff[i+1], buff.vdist[i], buff.vdist[i+1], halfWidth, rstroke->miterlimit); + } + } }; #endif // _TVG_WG_GEOMETRY_H_ \ No newline at end of file diff --git a/src/renderer/wg_engine/tvgWgRenderData.cpp b/src/renderer/wg_engine/tvgWgRenderData.cpp index b8f9f194..f577dce5 100755 --- a/src/renderer/wg_engine/tvgWgRenderData.cpp +++ b/src/renderer/wg_engine/tvgWgRenderData.cpp @@ -55,35 +55,36 @@ void WgMeshData::drawImage(WgContext& context, WGPURenderPassEncoder renderPassE }; -void WgMeshData::update(WgContext& context, const WgPolyline* polyline) +void WgMeshData::update(WgContext& context, const WgVertexBuffer& vertexBuffer) { - assert(polyline); - assert(polyline->pts.count > 2); - vertexCount = polyline->pts.count; - indexCount = (polyline->pts.count - 2) * 3; - context.allocateBufferVertex(bufferPosition, (float *)&polyline->pts[0], vertexCount * sizeof(float) * 2); + assert(vertexBuffer.vcount > 2); + vertexCount = vertexBuffer.vcount; + indexCount = (vertexBuffer.vcount - 2) * 3; + // buffer position data create and write + context.allocateBufferVertex(bufferPosition, (float *)&vertexBuffer.vbuff, vertexCount * sizeof(float) * 2); + // buffer index data create and write context.allocateBufferIndexFan(vertexCount); } -void WgMeshData::update(WgContext& context, const WgGeometryData* geometryData) +void WgMeshData::update(WgContext& context, const WgVertexBufferInd& vertexBufferInd) { - assert(geometryData); - vertexCount = geometryData->positions.pts.count; - indexCount = geometryData->indexes.count; + assert(vertexBufferInd.vcount > 2); + vertexCount = vertexBufferInd.vcount; + indexCount = vertexBufferInd.icount; // buffer position data create and write - if (geometryData->positions.pts.count > 0) - context.allocateBufferVertex(bufferPosition, (float *)&geometryData->positions.pts[0], vertexCount * sizeof(float) * 2); + if (vertexCount > 0) + context.allocateBufferVertex(bufferPosition, (float *)&vertexBufferInd.vbuff, vertexCount * sizeof(float) * 2); // buffer tex coords data create and write - if (geometryData->texCoords.count > 0) - context.allocateBufferVertex(bufferTexCoord, (float *)&geometryData->texCoords[0], vertexCount * sizeof(float) * 2); + if (vertexCount > 0) + context.allocateBufferVertex(bufferTexCoord, (float *)&vertexBufferInd.tbuff, vertexCount * sizeof(float) * 2); // buffer index data create and write - if (geometryData->indexes.count > 0) - context.allocateBufferIndex(bufferIndex, &geometryData->indexes[0], indexCount * sizeof(uint32_t)); + if (indexCount > 0) + context.allocateBufferIndex(bufferIndex, vertexBufferInd.ibuff, indexCount * sizeof(uint32_t)); }; -void WgMeshData::update(WgContext& context, const WgPoint pmin, const WgPoint pmax) +void WgMeshData::update(WgContext& context, const Point pmin, const Point pmax) { vertexCount = 4; indexCount = 6; @@ -144,25 +145,23 @@ WgMeshDataPool* WgMeshDataGroup::gMeshDataPool = nullptr; // WgMeshDataGroup //*********************************************************************** -void WgMeshDataGroup::append(WgContext& context, const WgPolyline* polyline) +void WgMeshDataGroup::append(WgContext& context, const WgVertexBuffer& vertexBuffer) { - assert(polyline); - assert(polyline->pts.count >= 3); + assert(vertexBuffer.vcount >= 3); meshes.push(gMeshDataPool->allocate(context)); - meshes.last()->update(context, polyline); + meshes.last()->update(context, vertexBuffer); } -void WgMeshDataGroup::append(WgContext& context, const WgGeometryData* geometryData) +void WgMeshDataGroup::append(WgContext& context, const WgVertexBufferInd& vertexBufferInd) { - assert(geometryData); - assert(geometryData->positions.pts.count >= 3); + assert(vertexBufferInd.vcount >= 3); meshes.push(gMeshDataPool->allocate(context)); - meshes.last()->update(context, geometryData); + meshes.last()->update(context, vertexBufferInd); } -void WgMeshDataGroup::append(WgContext& context, const WgPoint pmin, const WgPoint pmax) +void WgMeshDataGroup::append(WgContext& context, const Point pmin, const Point pmax) { meshes.push(gMeshDataPool->allocate(context)); meshes.last()->update(context, pmin, pmax); @@ -307,7 +306,29 @@ void WgRenderDataPaint::updateClips(tvg::Array &clips) { // WgRenderDataShape //*********************************************************************** -void WgRenderDataShape::updateBBox(WgPoint pmin, WgPoint pmax) +void WgRenderDataShape::appendShape(WgContext context, const WgVertexBuffer& vertexBuffer) +{ + if (vertexBuffer.vcount < 3) return; + Point pmin{}, pmax{}; + vertexBuffer.getMinMax(pmin, pmax); + meshGroupShapes.append(context, vertexBuffer); + meshGroupShapesBBox.append(context, pmin, pmax); + updateBBox(pmin, pmax); +} + + +void WgRenderDataShape::appendStroke(WgContext context, const WgVertexBufferInd& vertexBufferInd) +{ + if (vertexBufferInd.vcount < 3) return; + Point pmin{}, pmax{}; + vertexBufferInd.getMinMax(pmin, pmax); + meshGroupStrokes.append(context, vertexBufferInd); + meshGroupStrokesBBox.append(context, pmin, pmax); + updateBBox(pmin, pmax); +} + + +void WgRenderDataShape::updateBBox(Point pmin, Point pmax) { pMin.x = std::min(pMin.x, pmin.x); pMin.y = std::min(pMin.y, pmin.y); @@ -316,11 +337,11 @@ void WgRenderDataShape::updateBBox(WgPoint pmin, WgPoint pmax) } -void WgRenderDataShape::updateAABB(const Matrix& rt) { - WgPoint p0 = WgPoint(pMin.x, pMin.y).trans(rt); - WgPoint p1 = WgPoint(pMax.x, pMin.y).trans(rt); - WgPoint p2 = WgPoint(pMin.x, pMax.y).trans(rt); - WgPoint p3 = WgPoint(pMax.x, pMax.y).trans(rt); +void WgRenderDataShape::updateAABB(const Matrix& tr) { + Point p0 = Point{pMin.x, pMin.y} * tr; + Point p1 = Point{pMax.x, pMin.y} * tr; + Point p2 = Point{pMin.x, pMax.y} * tr; + Point p3 = Point{pMax.x, pMax.y} * tr; aabb.x = std::min({p0.x, p1.x, p2.x, p3.x}); aabb.y = std::min({p0.y, p1.y, p2.y, p3.y}); aabb.w = std::max({p0.x, p1.x, p2.x, p3.x}) - aabb.x; @@ -333,143 +354,75 @@ void WgRenderDataShape::updateMeshes(WgContext &context, const RenderShape &rsha releaseMeshes(context); strokeFirst = rshape.stroke ? rshape.stroke->strokeFirst : false; - float scale = std::max(sqrt(tr.e11*tr.e11 + tr.e21*tr.e21), 1.0f); - Array polylines{}; - // decode path - size_t pntIndex = 0; - for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) { - PathCommand cmd = rshape.path.cmds[cmdIndex]; - if (cmd == PathCommand::MoveTo) { - // proceed current polyline - polylines.push(new WgPolyline); - polylines.last()->appendPoint(rshape.path.pts[pntIndex]); - pntIndex++; - } else if (cmd == PathCommand::LineTo) { - polylines.last()->appendPoint(rshape.path.pts[pntIndex]); - pntIndex++; - } else if (cmd == PathCommand::Close) { - polylines.last()->close(); - } else if (cmd == PathCommand::CubicTo) { - assert(polylines.last()->pts.count > 0); - WgPoint pt0 = polylines.last()->pts.last().trans(tr); - WgPoint pt1 = WgPoint(rshape.path.pts[pntIndex + 0]).trans(tr); - WgPoint pt2 = WgPoint(rshape.path.pts[pntIndex + 1]).trans(tr); - WgPoint pt3 = WgPoint(rshape.path.pts[pntIndex + 2]).trans(tr); - uint32_t nsegs = std::max((uint32_t)(pt0.dist(pt1) + pt1.dist(pt2) + pt2.dist(pt3)), 32U); - polylines.last()->appendCubic( - rshape.path.pts[pntIndex + 0], - rshape.path.pts[pntIndex + 1], - rshape.path.pts[pntIndex + 2], - nsegs / 4); - pntIndex += 3; - } - } - // proceed shapes - float totalLen{}; - for (uint32_t i = 0; i < polylines.count; i++) { - totalLen += polylines[i]->len; - updateShapes(context, polylines[i]); - } - // proceed strokes - if (rshape.stroke) { - float trimBegin{}; - float trimEnd{}; - if (!rshape.stroke->strokeTrim(trimBegin, trimEnd)) { trimBegin = 0.0f; trimEnd = 1.0f; } - if (rshape.stroke->trim.simultaneous) { - for (uint32_t i = 0; i < polylines.count; i++) - updateStrokes(context, polylines[i], rshape.stroke, scale, trimBegin, trimEnd); - } else { - if (trimBegin <= trimEnd) { - updateStrokesList(context, polylines, rshape.stroke, totalLen, scale, trimBegin, trimEnd); - } else { - updateStrokesList(context, polylines, rshape.stroke, totalLen, scale, 0.0f, trimEnd); - updateStrokesList(context, polylines, rshape.stroke, totalLen, scale, trimBegin, 1.0f); - } - } - } - // delete polylines - for (uint32_t i = 0; i < polylines.count; i++) - delete polylines[i]; + // path decoded vertex buffer + WgVertexBuffer pbuff; + // append shape without strokes + if (!rshape.stroke) { + pbuff.decodePath(rshape, false, [&](const WgVertexBuffer& path_buff) { + appendShape(context, path_buff); + }); + // append shape with strokes + } else if (rshape.stroke->trim.simultaneous) { + float tbeg{}, tend{}; + if (!rshape.stroke->strokeTrim(tbeg, tend)) { tbeg = 0.0f; tend = 1.0f; } + if (tbeg == tend) return; + pbuff.decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) { + appendShape(context, path_buff); + proceedStrokes(context, rshape.stroke, tbeg, tend, path_buff); + }); + // append shape with strokes with simultaneous flag + } else { + float totalLen = 0.0f; + // append shapes + pbuff.decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) { + appendShape(context, path_buff); + totalLen += path_buff.total(); + }); + // append strokes + float tbeg{}, tend{}; + if (!rshape.stroke->strokeTrim(tbeg, tend)) { tbeg = 0.0f; tend = 1.0f; } + if (tbeg == tend) return; + float len_beg = totalLen * tbeg; // trim length begin + float len_end = totalLen * tend; // trim length end + float len_acc = 0.0; // accumulated length + // append strokes + pbuff.decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) { + float len_path = path_buff.total(); // current path length + float tbeg = ((len_acc <= len_beg) && (len_acc + len_path > len_beg)) ? (len_beg - len_acc) / len_path : 0.0f; + float tend = ((len_acc <= len_end) && (len_acc + len_path > len_end)) ? (len_end - len_acc) / len_path : 1.0f; + if ((len_acc + len_path >= len_beg) && (len_acc <= len_end)) + proceedStrokes(context, rshape.stroke, tbeg, tend, path_buff); + len_acc += len_path; + }); + } // update shapes bbox updateAABB(tr); meshDataBBox.update(context, pMin, pMax); } -void WgRenderDataShape::updateShapes(WgContext& context, const WgPolyline* polyline) +void WgRenderDataShape::proceedStrokes(WgContext context, const RenderStroke* rstroke, float tbeg, float tend, const WgVertexBuffer& buff) { - assert(polyline); - // generate fill geometry - if (polyline->pts.count >= 3) { - WgPoint pmin{}, pmax{}; - polyline->getBBox(pmin, pmax); - meshGroupShapes.append(context, polyline); - meshGroupShapesBBox.append(context, pmin, pmax); - updateBBox(pmin, pmax); - } -} - -void WgRenderDataShape::updateStrokesList(WgContext& context, Array polylines, const RenderStroke* rstroke, float scale, float totalLen, float trimBegin, float trimEnd) -{ - float tp1 = totalLen * trimBegin; // trim point begin - float tp2 = totalLen * trimEnd; // trim point end - float pc = 0; // point current - for (uint32_t i = 0; i < polylines.count; i++) { - float pl = polylines[i]->len; // current polyline length - float trimBegin = ((pc <= tp1) && (pc + pl > tp1)) ? (tp1 - pc) / pl : 0.0f; - float trimEnd = ((pc <= tp2) && (pc + pl > tp2)) ? (tp2 - pc) / pl : 1.0f; - if ((pc + pl >= tp1) && (pc <= tp2)) - updateStrokes(context, polylines[i], rstroke, scale, trimBegin, trimEnd); - pc += pl; - // break if reached the tail - if (pc > tp2) break; - } -} - - -void WgRenderDataShape::updateStrokes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke, float scale, float trimBegin, float trimEnd) -{ - assert(polyline); - // generate strokes geometry - if ((polyline->pts.count >= 1) && rstroke && (rstroke->width > 0.0f)) { - static WgGeometryData geometryData; geometryData.clear(); - static WgPolyline trimmed; - // trim -> split -> stroke - if (trimBegin == trimEnd) return; - if ((rstroke->dashPattern) && ((trimBegin != 0.0f) || (trimEnd != 1.0f))) { - trimmed.clear(); - if (trimBegin < trimEnd) - polyline->trim(&trimmed, trimBegin, trimEnd); - else { - polyline->trim(&trimmed, trimBegin, 1.0f); - polyline->trim(&trimmed, 0.0f, trimEnd); - } - geometryData.appendStrokeDashed(&trimmed, rstroke, scale); - } else // trim -> stroke - if ((trimBegin != 0.0f) || (trimEnd != 1.0f)) { - trimmed.clear(); - if (trimBegin < trimEnd) - polyline->trim(&trimmed, trimBegin, trimEnd); - else { - polyline->trim(&trimmed, trimBegin, 1.0f); - polyline->trim(&trimmed, 0.0f, trimEnd); - } - geometryData.appendStroke(&trimmed, rstroke, scale); - } else // split -> stroke - if (rstroke->dashPattern) { - geometryData.appendStrokeDashed(polyline, rstroke, scale); - } else { // stroke - geometryData.appendStroke(polyline, rstroke, scale); - } - // append render meshes and bboxes - if(geometryData.positions.pts.count >= 3) { - WgPoint pmin{}, pmax{}; - geometryData.positions.getBBox(pmin, pmax); - meshGroupStrokes.append(context, &geometryData); - meshGroupStrokesBBox.append(context, pmin, pmax); - updateBBox(pmin, pmax); - } - } + assert(rstroke); + WgVertexBufferInd stroke_buff; + // trim -> dash -> stroke + if ((tbeg != 0.0f) || (tend != 1.0f)) { + if (tbeg == tend) return; + WgVertexBuffer trimed_buff; + trimed_buff.trim(buff, tbeg, tend); + trimed_buff.updateDistances(); + // trim ->dash -> stroke + if (rstroke->dashPattern) stroke_buff.appendStrokesDashed(trimed_buff, rstroke); + // trim -> stroke + else stroke_buff.appendStrokes(trimed_buff, rstroke); + } else + // dash -> stroke + if (rstroke->dashPattern) { + stroke_buff.appendStrokesDashed(buff, rstroke); + // stroke + } else + stroke_buff.appendStrokes(buff, rstroke); + appendStroke(context, stroke_buff); } diff --git a/src/renderer/wg_engine/tvgWgRenderData.h b/src/renderer/wg_engine/tvgWgRenderData.h index b6629965..37863234 100755 --- a/src/renderer/wg_engine/tvgWgRenderData.h +++ b/src/renderer/wg_engine/tvgWgRenderData.h @@ -37,9 +37,9 @@ struct WgMeshData { void drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder); void drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder); - void update(WgContext& context, const WgPolyline* polyline); - void update(WgContext& context, const WgGeometryData* geometryData); - void update(WgContext& context, const WgPoint pmin, const WgPoint pmax); + void update(WgContext& context, const WgVertexBuffer& vertexBuffer); + void update(WgContext& context, const WgVertexBufferInd& vertexBufferInd); + void update(WgContext& context, const Point pmin, const Point pmax); void release(WgContext& context); }; @@ -58,9 +58,9 @@ struct WgMeshDataGroup { Array meshes{}; - void append(WgContext& context, const WgPolyline* polyline); - void append(WgContext& context, const WgGeometryData* geometryData); - void append(WgContext& context, const WgPoint pmin, const WgPoint pmax); + void append(WgContext& context, const WgVertexBuffer& vertexBuffer); + void append(WgContext& context, const WgVertexBufferInd& vertexBufferInd); + void append(WgContext& context, const Point pmin, const Point pmax); void release(WgContext& context); }; @@ -119,17 +119,17 @@ struct WgRenderDataShape: public WgRenderDataPaint WgMeshData meshDataBBox{}; WgMeshDataGroup meshGroupStrokes{}; WgMeshDataGroup meshGroupStrokesBBox{}; - WgPoint pMin{}; - WgPoint pMax{}; + Point pMin{}; + Point pMax{}; bool strokeFirst{}; FillRule fillRule{}; - void updateBBox(WgPoint pmin, WgPoint pmax); - void updateAABB(const Matrix& rt); + void appendShape(WgContext context, const WgVertexBuffer& vertexBuffer); + void appendStroke(WgContext context, const WgVertexBufferInd& vertexBufferInd); + void updateBBox(Point pmin, Point pmax); + void updateAABB(const Matrix& tr); void updateMeshes(WgContext& context, const RenderShape& rshape, const Matrix& tr); - void updateShapes(WgContext& context, const WgPolyline* polyline); - void updateStrokesList(WgContext& context, Array polylines, const RenderStroke* rstroke, float scale, float totalLen, float trimBegin, float trimEnd); - void updateStrokes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke, float scale, float trimBegin, float trimEnd); + void proceedStrokes(WgContext context, const RenderStroke* rstroke, float tbeg, float tend, const WgVertexBuffer& buff); void releaseMeshes(WgContext& context); void release(WgContext& context) override; Type type() override { return Type::Shape; }; diff --git a/src/renderer/wg_engine/tvgWgRenderer.cpp b/src/renderer/wg_engine/tvgWgRenderer.cpp index fc30c2af..6abc2815 100755 --- a/src/renderer/wg_engine/tvgWgRenderer.cpp +++ b/src/renderer/wg_engine/tvgWgRenderer.cpp @@ -24,8 +24,6 @@ WgRenderer::WgRenderer() { - WgGeometryData::gMath = new WgMath(); - WgGeometryData::gMath->initialize(); } @@ -33,8 +31,6 @@ WgRenderer::~WgRenderer() { release(); mContext.release(); - WgGeometryData::gMath->release(); - delete WgGeometryData::gMath; } @@ -130,11 +126,11 @@ RenderData WgRenderer::prepare(RenderSurface* surface, RenderData data, const Ma // update image data if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Image)) { - WgGeometryData geometryData; - geometryData.appendImageBox(surface->w, surface->h); + WgVertexBufferInd vertexBufferInd; + vertexBufferInd.appendImageBox(surface->w, surface->h); mContext.pipelines->layouts.releaseBindGroup(renderDataPicture->bindGroupPicture); renderDataPicture->meshData.release(mContext); - renderDataPicture->meshData.update(mContext, &geometryData); + renderDataPicture->meshData.update(mContext, vertexBufferInd); renderDataPicture->imageData.update(mContext, surface); renderDataPicture->bindGroupPicture = mContext.pipelines->layouts.createBindGroupTexSampled( mContext.samplerLinearRepeat, renderDataPicture->imageData.textureView