ThorVG: update from v0.12.1 to v0.12.3

https://github.com/thorvg/thorvg/releases/tag/v0.12.3

+ Full Changelog:
  https://github.com/thorvg/thorvg/compare/v0.12.1...v0.12.3

Godot-related SVG bug fixes:

+ svg_loader: Add missing transform functions skewX and skewY.
  thorvg/thorvg#1928
+ sw_engine: Rectified dash line drawing issue.
  thorvg/thorvg#1932
This commit is contained in:
Martin Capitanio 2024-01-26 10:04:27 +01:00
parent 99ac3d332a
commit 73589f6db6
14 changed files with 209 additions and 142 deletions

View File

@ -859,7 +859,7 @@ instead of `miniz.h` as an external dependency.
## thorvg ## thorvg
- Upstream: https://github.com/thorvg/thorvg - Upstream: https://github.com/thorvg/thorvg
- Version: 0.12.1 (d761e3c5622c0ffba2e5bb40da05751e2451e495, 2024) - Version: 0.12.3 (9d79f0ccef632fd3b43b8ea02def529b6a8d2288, 2024)
- License: MIT - License: MIT
Files extracted from upstream source: Files extracted from upstream source:

View File

@ -9,5 +9,5 @@
// For internal debugging: // For internal debugging:
//#define THORVG_LOG_ENABLED //#define THORVG_LOG_ENABLED
#define THORVG_VERSION_STRING "0.12.1" #define THORVG_VERSION_STRING "0.12.3"
#endif #endif

70
thirdparty/thorvg/src/common/tvgLock.h vendored Normal file
View File

@ -0,0 +1,70 @@
/*
* Copyright (c) 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.
*/
#ifndef _TVG_LOCK_H_
#define _TVG_LOCK_H_
#ifdef THORVG_THREAD_SUPPORT
#include <mutex>
namespace tvg {
struct Key
{
std::mutex mtx;
};
struct ScopedLock
{
Key* key = nullptr;
ScopedLock(Key& key)
{
key.mtx.lock();
this->key = &key;
}
~ScopedLock()
{
key->mtx.unlock();
}
};
}
#else //THORVG_THREAD_SUPPORT
namespace tvg {
struct Key {};
struct ScopedLock
{
ScopedLock(Key& key) {}
};
}
#endif //THORVG_THREAD_SUPPORT
#endif //_TVG_LOCK_H_

View File

@ -683,32 +683,6 @@ static constexpr struct
}; };
static void _matrixCompose(const Matrix* m1, const Matrix* m2, Matrix* dst)
{
auto a11 = (m1->e11 * m2->e11) + (m1->e12 * m2->e21) + (m1->e13 * m2->e31);
auto a12 = (m1->e11 * m2->e12) + (m1->e12 * m2->e22) + (m1->e13 * m2->e32);
auto a13 = (m1->e11 * m2->e13) + (m1->e12 * m2->e23) + (m1->e13 * m2->e33);
auto a21 = (m1->e21 * m2->e11) + (m1->e22 * m2->e21) + (m1->e23 * m2->e31);
auto a22 = (m1->e21 * m2->e12) + (m1->e22 * m2->e22) + (m1->e23 * m2->e32);
auto a23 = (m1->e21 * m2->e13) + (m1->e22 * m2->e23) + (m1->e23 * m2->e33);
auto a31 = (m1->e31 * m2->e11) + (m1->e32 * m2->e21) + (m1->e33 * m2->e31);
auto a32 = (m1->e31 * m2->e12) + (m1->e32 * m2->e22) + (m1->e33 * m2->e32);
auto a33 = (m1->e31 * m2->e13) + (m1->e32 * m2->e23) + (m1->e33 * m2->e33);
dst->e11 = a11;
dst->e12 = a12;
dst->e13 = a13;
dst->e21 = a21;
dst->e22 = a22;
dst->e23 = a23;
dst->e31 = a31;
dst->e32 = a32;
dst->e33 = a33;
}
/* parse transform attribute /* parse transform attribute
* https://www.w3.org/TR/SVG/coords.html#TransformAttribute * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
*/ */
@ -751,14 +725,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
if (state == MatrixState::Matrix) { if (state == MatrixState::Matrix) {
if (ptCount != 6) goto error; if (ptCount != 6) goto error;
Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1}; Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
_matrixCompose(matrix, &tmp, matrix); *matrix = mathMultiply(matrix, &tmp);
} else if (state == MatrixState::Translate) { } else if (state == MatrixState::Translate) {
if (ptCount == 1) { if (ptCount == 1) {
Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1}; Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
_matrixCompose(matrix, &tmp, matrix); *matrix = mathMultiply(matrix, &tmp);
} else if (ptCount == 2) { } else if (ptCount == 2) {
Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1}; Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
_matrixCompose(matrix, &tmp, matrix); *matrix = mathMultiply(matrix, &tmp);
} else goto error; } else goto error;
} else if (state == MatrixState::Rotate) { } else if (state == MatrixState::Rotate) {
//Transform to signed. //Transform to signed.
@ -768,14 +742,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
auto s = sinf(points[0] * (M_PI / 180.0)); auto s = sinf(points[0] * (M_PI / 180.0));
if (ptCount == 1) { if (ptCount == 1) {
Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 }; Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
_matrixCompose(matrix, &tmp, matrix); *matrix = mathMultiply(matrix, &tmp);
} else if (ptCount == 3) { } else if (ptCount == 3) {
Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 }; Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
_matrixCompose(matrix, &tmp, matrix); *matrix = mathMultiply(matrix, &tmp);
tmp = { c, -s, 0, s, c, 0, 0, 0, 1 }; tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
_matrixCompose(matrix, &tmp, matrix); *matrix = mathMultiply(matrix, &tmp);
tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 }; tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
_matrixCompose(matrix, &tmp, matrix); *matrix = mathMultiply(matrix, &tmp);
} else { } else {
goto error; goto error;
} }
@ -785,7 +759,17 @@ static Matrix* _parseTransformationMatrix(const char* value)
auto sy = sx; auto sy = sx;
if (ptCount == 2) sy = points[1]; if (ptCount == 2) sy = points[1];
Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 }; Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
_matrixCompose(matrix, &tmp, matrix); *matrix = mathMultiply(matrix, &tmp);
} else if (state == MatrixState::SkewX) {
if (ptCount != 1) goto error;
auto deg = tanf(points[0] * (M_PI / 180.0));
Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
*matrix = mathMultiply(matrix, &tmp);
} else if (state == MatrixState::SkewY) {
if (ptCount != 1) goto error;
auto deg = tanf(points[0] * (M_PI / 180.0));
Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
*matrix = mathMultiply(matrix, &tmp);
} }
} }
return matrix; return matrix;

View File

@ -257,8 +257,8 @@ static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride,
auto ry2 = ry + 1; auto ry2 = ry + 1;
if (ry2 >= h) ry2 = h - 1; if (ry2 >= h) ry2 = h - 1;
auto dx = static_cast<size_t>((sx - rx) * 255.0f); auto dx = static_cast<uint8_t>((sx - rx) * 255.0f);
auto dy = static_cast<size_t>((sy - ry) * 255.0f); auto dy = static_cast<uint8_t>((sy - ry) * 255.0f);
auto c1 = img[rx + ry * w]; auto c1 = img[rx + ry * w];
auto c2 = img[rx2 + ry * w]; auto c2 = img[rx2 + ry * w];
@ -281,21 +281,23 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t
int32_t maxx = (int32_t)sx + n; int32_t maxx = (int32_t)sx + n;
if (maxx >= (int32_t)w) maxx = w; if (maxx >= (int32_t)w) maxx = w;
int32_t inc = (n / 2) + 1;
n = 0;
auto src = img + minx + miny * stride; auto src = img + minx + miny * stride;
for (auto y = miny; y < maxy; ++y) { for (auto y = miny; y < maxy; y += inc) {
auto p = src; auto p = src;
for (auto x = minx; x < maxx; ++x, ++p) { for (auto x = minx; x < maxx; x += inc, p += inc) {
c[0] += *p >> 24; c[0] += A(*p);
c[1] += (*p >> 16) & 0xff; c[1] += C1(*p);
c[2] += (*p >> 8) & 0xff; c[2] += C2(*p);
c[3] += *p & 0xff; c[3] += C3(*p);
++n;
} }
src += stride; src += (stride * inc);
} }
n = (maxy - miny) * (maxx - minx);
c[0] /= n; c[0] /= n;
c[1] /= n; c[1] /= n;
c[2] /= n; c[2] /= n;
@ -1855,7 +1857,7 @@ void rasterUnpremultiply(Surface* surface)
void rasterPremultiply(Surface* surface) void rasterPremultiply(Surface* surface)
{ {
unique_lock<mutex> lock{surface->mtx}; ScopedLock lock(surface->key);
if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return; if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return;
surface->premultiplied = true; surface->premultiplied = true;
@ -1936,7 +1938,7 @@ bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, con
bool rasterConvertCS(Surface* surface, ColorSpace to) bool rasterConvertCS(Surface* surface, ColorSpace to)
{ {
unique_lock<mutex> lock{surface->mtx}; ScopedLock lock(surface->key);
if (surface->cs == to) return true; if (surface->cs == to) return true;
//TOOD: Support SIMD accelerations //TOOD: Support SIMD accelerations

View File

@ -528,8 +528,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
vv = (int) v; vv = (int) v;
if (vv >= sh) continue; if (vv >= sh) continue;
ar = (int)(255 * (1 - modff(u, &iptr))); ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr))); ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
iru = uu + 1; iru = uu + 1;
irv = vv + 1; irv = vv + 1;
@ -576,8 +576,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
uu = (int) u; uu = (int) u;
vv = (int) v; vv = (int) v;
ar = (int)(255 * (1 - modff(u, &iptr))); ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr))); ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
iru = uu + 1; iru = uu + 1;
irv = vv + 1; irv = vv + 1;

View File

@ -425,6 +425,8 @@ bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h,
{ {
if (!data || stride == 0 || w == 0 || h == 0 || w > stride) return false; if (!data || stride == 0 || w == 0 || h == 0 || w > stride) return false;
clearCompositors();
if (!surface) surface = new SwSurface; if (!surface) surface = new SwSurface;
surface->data = data; surface->data = data;
@ -474,7 +476,6 @@ bool SwRenderer::postRender()
} }
tasks.clear(); tasks.clear();
clearCompositors();
return true; return true;
} }

View File

@ -64,12 +64,16 @@ static void _outlineEnd(SwOutline& outline)
{ {
if (outline.pts.empty()) return; if (outline.pts.empty()) return;
outline.cntrs.push(outline.pts.count - 1); outline.cntrs.push(outline.pts.count - 1);
outline.closed.push(false);
} }
static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform) static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform)
{ {
if (outline.pts.count > 0) outline.cntrs.push(outline.pts.count - 1); if (outline.pts.count > 0) {
outline.cntrs.push(outline.pts.count - 1);
outline.closed.push(false);
}
outline.pts.push(mathTransform(to, transform)); outline.pts.push(mathTransform(to, transform));
outline.types.push(SW_CURVE_TYPE_POINT); outline.types.push(SW_CURVE_TYPE_POINT);
@ -128,7 +132,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
_outlineLineTo(*dash.outline, to, transform); _outlineLineTo(*dash.outline, to, transform);
} }
} else { } else {
while (len > dash.curLen) { while (len - dash.curLen > 0.0001f) {
Line left, right; Line left, right;
if (dash.curLen > 0) { if (dash.curLen > 0) {
len -= dash.curLen; len -= dash.curLen;
@ -185,7 +189,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform); _outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
} }
} else { } else {
while (len > dash.curLen) { while ((len - dash.curLen) > 0.0001f) {
Bezier left, right; Bezier left, right;
if (dash.curLen > 0) { if (dash.curLen > 0) {
len -= dash.curLen; len -= dash.curLen;
@ -315,21 +319,6 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
dash.outline = mpoolReqDashOutline(mpool, tid); dash.outline = mpoolReqDashOutline(mpool, tid);
//smart reservation
auto closeCnt = 0;
auto moveCnt = 0;
for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
if (*cmd == PathCommand::Close) ++closeCnt;
else if (*cmd == PathCommand::MoveTo) ++moveCnt;
}
//No idea exact count.... Reserve Approximitely 20x...
//OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
dash.outline->pts.grow(20 * (closeCnt + ptsCnt + 1));
dash.outline->types.grow(20 * (closeCnt + ptsCnt + 1));
dash.outline->cntrs.grow(20 * (moveCnt + 1));
while (cmdCnt-- > 0) { while (cmdCnt-- > 0) {
switch (*cmds) { switch (*cmds) {
case PathCommand::Close: { case PathCommand::Close: {
@ -435,29 +424,9 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
//No actual shape data //No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return false; if (cmdCnt == 0 || ptsCnt == 0) return false;
//smart reservation
auto moveCnt = 0;
auto closeCnt = 0;
for (auto cmd = rshape->path.cmds.data; cmd < rshape->path.cmds.end(); ++cmd) {
if (*cmd == PathCommand::Close) ++closeCnt;
else if (*cmd == PathCommand::MoveTo) ++moveCnt;
}
shape->outline = mpoolReqOutline(mpool, tid); shape->outline = mpoolReqOutline(mpool, tid);
auto outline = shape->outline; auto outline = shape->outline;
//OPTIMIZE: we can directly copy the path points when the close is occupied with a point.
outline->pts.grow(ptsCnt + closeCnt + 1);
outline->types.grow(ptsCnt + closeCnt + 1);
outline->cntrs.grow(moveCnt + 1);
//Dash outlines are always opened.
//Only normal outlines use this information, it sholud be same to their contour counts.
outline->closed.reserve(outline->cntrs.reserved);
memset(outline->closed.data, 0x0, sizeof(bool) * outline->closed.reserved);
//Generate Outlines //Generate Outlines
while (cmdCnt-- > 0) { while (cmdCnt-- > 0) {
switch (*cmds) { switch (*cmds) {

View File

@ -24,6 +24,7 @@
#include "tvgInlist.h" #include "tvgInlist.h"
#include "tvgLoader.h" #include "tvgLoader.h"
#include "tvgLock.h"
#ifdef THORVG_SVG_LOADER_SUPPORT #ifdef THORVG_SVG_LOADER_SUPPORT
#include "tvgSvgLoader.h" #include "tvgSvgLoader.h"
@ -65,7 +66,7 @@ uint64_t HASH_KEY(const char* data, uint64_t size)
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
static mutex mtx; static Key key;
static Inlist<LoadModule> _activeLoaders; static Inlist<LoadModule> _activeLoaders;
@ -211,7 +212,7 @@ static LoadModule* _findByType(const string& mimeType)
static LoadModule* _findFromCache(const string& path) static LoadModule* _findFromCache(const string& path)
{ {
unique_lock<mutex> lock{mtx}; ScopedLock lock(key);
auto loader = _activeLoaders.head; auto loader = _activeLoaders.head;
@ -231,7 +232,7 @@ static LoadModule* _findFromCache(const char* data, uint32_t size, const string&
auto type = _convert(mimeType); auto type = _convert(mimeType);
if (type == FileType::Unknown) return nullptr; if (type == FileType::Unknown) return nullptr;
unique_lock<mutex> lock{mtx}; ScopedLock lock(key);
auto loader = _activeLoaders.head; auto loader = _activeLoaders.head;
auto key = HASH_KEY(data, size); auto key = HASH_KEY(data, size);
@ -279,7 +280,7 @@ bool LoaderMgr::retrieve(LoadModule* loader)
if (!loader) return false; if (!loader) return false;
if (loader->close()) { if (loader->close()) {
{ {
unique_lock<mutex> lock{mtx}; ScopedLock lock(key);
_activeLoaders.remove(loader); _activeLoaders.remove(loader);
} }
delete(loader); delete(loader);
@ -298,7 +299,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
if (loader->open(path)) { if (loader->open(path)) {
loader->hashpath = strdup(path.c_str()); loader->hashpath = strdup(path.c_str());
{ {
unique_lock<mutex> lock{mtx}; ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
return loader; return loader;
@ -340,7 +341,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
if (auto loader = _findByType(mimeType)) { if (auto loader = _findByType(mimeType)) {
if (loader->open(data, size, copy)) { if (loader->open(data, size, copy)) {
loader->hashkey = HASH_KEY(data, size); loader->hashkey = HASH_KEY(data, size);
unique_lock<mutex> lock{mtx}; ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
return loader; return loader;
} else { } else {
@ -356,7 +357,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
if (loader->open(data, size, copy)) { if (loader->open(data, size, copy)) {
loader->hashkey = HASH_KEY(data, size); loader->hashkey = HASH_KEY(data, size);
{ {
unique_lock<mutex> lock{mtx}; ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
return loader; return loader;
@ -379,7 +380,7 @@ LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool
if (loader->open(data, w, h, copy)) { if (loader->open(data, w, h, copy)) {
loader->hashkey = HASH_KEY((const char*)data, w * h); loader->hashkey = HASH_KEY((const char*)data, w * h);
{ {
unique_lock<mutex> lock{mtx}; ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
return loader; return loader;

View File

@ -23,9 +23,9 @@
#ifndef _TVG_RENDER_H_ #ifndef _TVG_RENDER_H_
#define _TVG_RENDER_H_ #define _TVG_RENDER_H_
#include <mutex>
#include "tvgCommon.h" #include "tvgCommon.h"
#include "tvgArray.h" #include "tvgArray.h"
#include "tvgLock.h"
namespace tvg namespace tvg
{ {
@ -54,7 +54,7 @@ struct Surface
uint32_t* buf32; //for explicit 32bits channels uint32_t* buf32; //for explicit 32bits channels
uint8_t* buf8; //for explicit 8bits grayscale uint8_t* buf8; //for explicit 8bits grayscale
}; };
mutex mtx; //used for thread safety Key key; //a reserved lock for the thread safety
uint32_t stride = 0; uint32_t stride = 0;
uint32_t w = 0, h = 0; uint32_t w = 0, h = 0;
ColorSpace cs = ColorSpace::Unsupported; ColorSpace cs = ColorSpace::Unsupported;

View File

@ -130,11 +130,11 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
auto ryKappa = ry * PATH_KAPPA; auto ryKappa = ry * PATH_KAPPA;
pImpl->grow(6, 13); pImpl->grow(6, 13);
pImpl->moveTo(cx + rx, cy); pImpl->moveTo(cx, cy - ry);
pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry); pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy); pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry); pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
pImpl->close(); pImpl->close();
return Result::Success; return Result::Success;
@ -216,21 +216,19 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
pImpl->lineTo(x, y + h); pImpl->lineTo(x, y + h);
pImpl->close(); pImpl->close();
//circle //circle
} else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
} else { } else {
auto hrx = rx * PATH_KAPPA; auto hrx = rx * PATH_KAPPA;
auto hry = ry * PATH_KAPPA; auto hry = ry * PATH_KAPPA;
pImpl->grow(10, 17); pImpl->grow(10, 17);
pImpl->moveTo(x + rx, y); pImpl->moveTo(x + w, y + ry);
pImpl->lineTo(x + w - rx, y);
pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
pImpl->lineTo(x + w, y + h - ry); pImpl->lineTo(x + w, y + h - ry);
pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h); pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
pImpl->lineTo(x + rx, y + h); pImpl->lineTo(x + rx, y + h);
pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry); pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
pImpl->lineTo(x, y + ry); pImpl->lineTo(x, y + ry);
pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y); pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
pImpl->lineTo(x + w - rx, y);
pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
pImpl->close(); pImpl->close();
} }

View File

@ -20,19 +20,28 @@
* SOFTWARE. * SOFTWARE.
*/ */
#include <thread>
#include <atomic>
#include <condition_variable>
#include "tvgArray.h" #include "tvgArray.h"
#include "tvgInlist.h" #include "tvgInlist.h"
#include "tvgTaskScheduler.h" #include "tvgTaskScheduler.h"
#ifdef THORVG_THREAD_SUPPORT
#include <thread>
#include <atomic>
#endif
/************************************************************************/ /************************************************************************/
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
namespace tvg { namespace tvg {
struct TaskSchedulerImpl;
static TaskSchedulerImpl* inst = nullptr;
#ifdef THORVG_THREAD_SUPPORT
static thread_local bool _async = true;
struct TaskQueue { struct TaskQueue {
Inlist<Task> taskDeque; Inlist<Task> taskDeque;
mutex mtx; mutex mtx;
@ -61,7 +70,7 @@ struct TaskQueue {
void complete() void complete()
{ {
{ {
unique_lock<mutex> lock{mtx}; lock_guard<mutex> lock{mtx};
done = true; done = true;
} }
ready.notify_all(); ready.notify_all();
@ -84,7 +93,7 @@ struct TaskQueue {
void push(Task* task) void push(Task* task)
{ {
{ {
unique_lock<mutex> lock{mtx}; lock_guard<mutex> lock{mtx};
taskDeque.back(task); taskDeque.back(task);
} }
ready.notify_one(); ready.notify_one();
@ -92,25 +101,22 @@ struct TaskQueue {
}; };
static thread_local bool _async = true; //toggle async tasking for each thread on/off
struct TaskSchedulerImpl struct TaskSchedulerImpl
{ {
Array<thread*> threads; Array<thread*> threads;
Array<TaskQueue*> taskQueues; Array<TaskQueue*> taskQueues;
atomic<uint32_t> idx{0}; atomic<uint32_t> idx{0};
TaskSchedulerImpl(unsigned threadCnt) TaskSchedulerImpl(uint32_t threadCnt)
{ {
threads.reserve(threadCnt); threads.reserve(threadCnt);
taskQueues.reserve(threadCnt); taskQueues.reserve(threadCnt);
for (unsigned i = 0; i < threadCnt; ++i) { for (uint32_t i = 0; i < threadCnt; ++i) {
taskQueues.push(new TaskQueue); taskQueues.push(new TaskQueue);
threads.push(new thread); threads.push(new thread);
} }
for (unsigned i = 0; i < threadCnt; ++i) { for (uint32_t i = 0; i < threadCnt; ++i) {
*threads.data[i] = thread([&, i] { run(i); }); *threads.data[i] = thread([&, i] { run(i); });
} }
} }
@ -136,7 +142,7 @@ struct TaskSchedulerImpl
//Thread Loop //Thread Loop
while (true) { while (true) {
auto success = false; auto success = false;
for (unsigned x = 0; x < threads.count * 2; ++x) { for (uint32_t x = 0; x < threads.count * 2; ++x) {
if (taskQueues[(i + x) % threads.count]->tryPop(&task)) { if (taskQueues[(i + x) % threads.count]->tryPop(&task)) {
success = true; success = true;
break; break;
@ -154,7 +160,7 @@ struct TaskSchedulerImpl
if (threads.count > 0 && _async) { if (threads.count > 0 && _async) {
task->prepare(); task->prepare();
auto i = idx++; auto i = idx++;
for (unsigned n = 0; n < threads.count; ++n) { for (uint32_t n = 0; n < threads.count; ++n) {
if (taskQueues[(i + n) % threads.count]->tryPush(task)) return; if (taskQueues[(i + n) % threads.count]->tryPush(task)) return;
} }
taskQueues[i % threads.count]->push(task); taskQueues[i % threads.count]->push(task);
@ -163,17 +169,33 @@ struct TaskSchedulerImpl
task->run(0); task->run(0);
} }
} }
uint32_t threadCnt()
{
return threads.count;
}
}; };
} #else //THORVG_THREAD_SUPPORT
static TaskSchedulerImpl* inst = nullptr; static bool _async = true;
struct TaskSchedulerImpl
{
TaskSchedulerImpl(TVG_UNUSED uint32_t threadCnt) {}
void request(Task* task) { task->run(0); }
uint32_t threadCnt() { return 0; }
};
#endif //THORVG_THREAD_SUPPORT
} //namespace
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
void TaskScheduler::init(unsigned threads) void TaskScheduler::init(uint32_t threads)
{ {
if (inst) return; if (inst) return;
inst = new TaskSchedulerImpl(threads); inst = new TaskSchedulerImpl(threads);
@ -182,7 +204,6 @@ void TaskScheduler::init(unsigned threads)
void TaskScheduler::term() void TaskScheduler::term()
{ {
if (!inst) return;
delete(inst); delete(inst);
inst = nullptr; inst = nullptr;
} }
@ -194,14 +215,15 @@ void TaskScheduler::request(Task* task)
} }
unsigned TaskScheduler::threads() uint32_t TaskScheduler::threads()
{ {
if (inst) return inst->threads.count; if (inst) return inst->threadCnt();
return 0; return 0;
} }
void TaskScheduler::async(bool on) void TaskScheduler::async(bool on)
{ {
//toggle async tasking for each thread on/off
_async = on; _async = on;
} }

View File

@ -25,22 +25,13 @@
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
#include "tvgCommon.h" #include "tvgCommon.h"
#include "tvgInlist.h" #include "tvgInlist.h"
namespace tvg namespace tvg {
{
struct Task; #ifdef THORVG_THREAD_SUPPORT
struct TaskScheduler
{
static unsigned threads();
static void init(unsigned threads);
static void term();
static void request(Task* task);
static void async(bool on);
};
struct Task struct Task
{ {
@ -86,7 +77,36 @@ private:
friend struct TaskSchedulerImpl; friend struct TaskSchedulerImpl;
}; };
} #else //THORVG_THREAD_SUPPORT
struct Task
{
public:
INLIST_ITEM(Task);
virtual ~Task() = default;
void done() {}
protected:
virtual void run(unsigned tid) = 0;
private:
friend struct TaskSchedulerImpl;
};
#endif //THORVG_THREAD_SUPPORT
struct TaskScheduler
{
static uint32_t threads();
static void init(uint32_t threads);
static void term();
static void request(Task* task);
static void async(bool on);
};
} //namespace
#endif //_TVG_TASK_SCHEDULER_H_ #endif //_TVG_TASK_SCHEDULER_H_

View File

@ -1,6 +1,6 @@
#!/bin/bash -e #!/bin/bash -e
VERSION=0.12.1 VERSION=0.12.3
cd thirdparty/thorvg/ || true cd thirdparty/thorvg/ || true
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/ rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/