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:
parent
99ac3d332a
commit
73589f6db6
|
@ -859,7 +859,7 @@ instead of `miniz.h` as an external dependency.
|
|||
## thorvg
|
||||
|
||||
- Upstream: https://github.com/thorvg/thorvg
|
||||
- Version: 0.12.1 (d761e3c5622c0ffba2e5bb40da05751e2451e495, 2024)
|
||||
- Version: 0.12.3 (9d79f0ccef632fd3b43b8ea02def529b6a8d2288, 2024)
|
||||
- License: MIT
|
||||
|
||||
Files extracted from upstream source:
|
||||
|
|
|
@ -9,5 +9,5 @@
|
|||
// For internal debugging:
|
||||
//#define THORVG_LOG_ENABLED
|
||||
|
||||
#define THORVG_VERSION_STRING "0.12.1"
|
||||
#define THORVG_VERSION_STRING "0.12.3"
|
||||
#endif
|
||||
|
|
|
@ -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_
|
|
@ -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
|
||||
* https://www.w3.org/TR/SVG/coords.html#TransformAttribute
|
||||
*/
|
||||
|
@ -751,14 +725,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
|
|||
if (state == MatrixState::Matrix) {
|
||||
if (ptCount != 6) goto error;
|
||||
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) {
|
||||
if (ptCount == 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) {
|
||||
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 if (state == MatrixState::Rotate) {
|
||||
//Transform to signed.
|
||||
|
@ -768,14 +742,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
|
|||
auto s = sinf(points[0] * (M_PI / 180.0));
|
||||
if (ptCount == 1) {
|
||||
Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
|
||||
_matrixCompose(matrix, &tmp, matrix);
|
||||
*matrix = mathMultiply(matrix, &tmp);
|
||||
} else if (ptCount == 3) {
|
||||
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 };
|
||||
_matrixCompose(matrix, &tmp, matrix);
|
||||
*matrix = mathMultiply(matrix, &tmp);
|
||||
tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
|
||||
_matrixCompose(matrix, &tmp, matrix);
|
||||
*matrix = mathMultiply(matrix, &tmp);
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
|
@ -785,7 +759,17 @@ static Matrix* _parseTransformationMatrix(const char* value)
|
|||
auto sy = sx;
|
||||
if (ptCount == 2) sy = points[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;
|
||||
|
|
|
@ -257,8 +257,8 @@ static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride,
|
|||
auto ry2 = ry + 1;
|
||||
if (ry2 >= h) ry2 = h - 1;
|
||||
|
||||
auto dx = static_cast<size_t>((sx - rx) * 255.0f);
|
||||
auto dy = static_cast<size_t>((sy - ry) * 255.0f);
|
||||
auto dx = static_cast<uint8_t>((sx - rx) * 255.0f);
|
||||
auto dy = static_cast<uint8_t>((sy - ry) * 255.0f);
|
||||
|
||||
auto c1 = img[rx + 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;
|
||||
if (maxx >= (int32_t)w) maxx = w;
|
||||
|
||||
int32_t inc = (n / 2) + 1;
|
||||
n = 0;
|
||||
|
||||
auto src = img + minx + miny * stride;
|
||||
|
||||
for (auto y = miny; y < maxy; ++y) {
|
||||
for (auto y = miny; y < maxy; y += inc) {
|
||||
auto p = src;
|
||||
for (auto x = minx; x < maxx; ++x, ++p) {
|
||||
c[0] += *p >> 24;
|
||||
c[1] += (*p >> 16) & 0xff;
|
||||
c[2] += (*p >> 8) & 0xff;
|
||||
c[3] += *p & 0xff;
|
||||
for (auto x = minx; x < maxx; x += inc, p += inc) {
|
||||
c[0] += A(*p);
|
||||
c[1] += C1(*p);
|
||||
c[2] += C2(*p);
|
||||
c[3] += C3(*p);
|
||||
++n;
|
||||
}
|
||||
src += stride;
|
||||
src += (stride * inc);
|
||||
}
|
||||
|
||||
n = (maxy - miny) * (maxx - minx);
|
||||
|
||||
c[0] /= n;
|
||||
c[1] /= n;
|
||||
c[2] /= n;
|
||||
|
@ -1855,7 +1857,7 @@ void rasterUnpremultiply(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;
|
||||
surface->premultiplied = true;
|
||||
|
||||
|
@ -1936,7 +1938,7 @@ bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, con
|
|||
|
||||
bool rasterConvertCS(Surface* surface, ColorSpace to)
|
||||
{
|
||||
unique_lock<mutex> lock{surface->mtx};
|
||||
ScopedLock lock(surface->key);
|
||||
if (surface->cs == to) return true;
|
||||
|
||||
//TOOD: Support SIMD accelerations
|
||||
|
|
|
@ -528,8 +528,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
vv = (int) v;
|
||||
if (vv >= sh) continue;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
|
||||
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
|
@ -576,8 +576,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
uu = (int) u;
|
||||
vv = (int) v;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
|
||||
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
clearCompositors();
|
||||
|
||||
if (!surface) surface = new SwSurface;
|
||||
|
||||
surface->data = data;
|
||||
|
@ -474,7 +476,6 @@ bool SwRenderer::postRender()
|
|||
}
|
||||
tasks.clear();
|
||||
|
||||
clearCompositors();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,12 +64,16 @@ static void _outlineEnd(SwOutline& outline)
|
|||
{
|
||||
if (outline.pts.empty()) return;
|
||||
outline.cntrs.push(outline.pts.count - 1);
|
||||
outline.closed.push(false);
|
||||
}
|
||||
|
||||
|
||||
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.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);
|
||||
}
|
||||
} else {
|
||||
while (len > dash.curLen) {
|
||||
while (len - dash.curLen > 0.0001f) {
|
||||
Line left, right;
|
||||
if (dash.curLen > 0) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
while (len > dash.curLen) {
|
||||
while ((len - dash.curLen) > 0.0001f) {
|
||||
Bezier left, right;
|
||||
if (dash.curLen > 0) {
|
||||
len -= dash.curLen;
|
||||
|
@ -315,21 +319,6 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
|||
|
||||
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) {
|
||||
switch (*cmds) {
|
||||
case PathCommand::Close: {
|
||||
|
@ -435,29 +424,9 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
|
|||
//No actual shape data
|
||||
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);
|
||||
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
|
||||
while (cmdCnt-- > 0) {
|
||||
switch (*cmds) {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "tvgInlist.h"
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgLock.h"
|
||||
|
||||
#ifdef THORVG_SVG_LOADER_SUPPORT
|
||||
#include "tvgSvgLoader.h"
|
||||
|
@ -65,7 +66,7 @@ uint64_t HASH_KEY(const char* data, uint64_t size)
|
|||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static mutex mtx;
|
||||
static Key key;
|
||||
static Inlist<LoadModule> _activeLoaders;
|
||||
|
||||
|
||||
|
@ -211,7 +212,7 @@ static LoadModule* _findByType(const string& mimeType)
|
|||
|
||||
static LoadModule* _findFromCache(const string& path)
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
ScopedLock lock(key);
|
||||
|
||||
auto loader = _activeLoaders.head;
|
||||
|
||||
|
@ -231,7 +232,7 @@ static LoadModule* _findFromCache(const char* data, uint32_t size, const string&
|
|||
auto type = _convert(mimeType);
|
||||
if (type == FileType::Unknown) return nullptr;
|
||||
|
||||
unique_lock<mutex> lock{mtx};
|
||||
ScopedLock lock(key);
|
||||
auto loader = _activeLoaders.head;
|
||||
|
||||
auto key = HASH_KEY(data, size);
|
||||
|
@ -279,7 +280,7 @@ bool LoaderMgr::retrieve(LoadModule* loader)
|
|||
if (!loader) return false;
|
||||
if (loader->close()) {
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.remove(loader);
|
||||
}
|
||||
delete(loader);
|
||||
|
@ -298,7 +299,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
|
|||
if (loader->open(path)) {
|
||||
loader->hashpath = strdup(path.c_str());
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(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 (loader->open(data, size, copy)) {
|
||||
loader->hashkey = HASH_KEY(data, size);
|
||||
unique_lock<mutex> lock{mtx};
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(loader);
|
||||
return loader;
|
||||
} else {
|
||||
|
@ -356,7 +357,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
|
|||
if (loader->open(data, size, copy)) {
|
||||
loader->hashkey = HASH_KEY(data, size);
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(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)) {
|
||||
loader->hashkey = HASH_KEY((const char*)data, w * h);
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(loader);
|
||||
}
|
||||
return loader;
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
#ifndef _TVG_RENDER_H_
|
||||
#define _TVG_RENDER_H_
|
||||
|
||||
#include <mutex>
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgArray.h"
|
||||
#include "tvgLock.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
@ -54,7 +54,7 @@ struct Surface
|
|||
uint32_t* buf32; //for explicit 32bits channels
|
||||
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 w = 0, h = 0;
|
||||
ColorSpace cs = ColorSpace::Unsupported;
|
||||
|
|
|
@ -130,11 +130,11 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
|
|||
auto ryKappa = ry * PATH_KAPPA;
|
||||
|
||||
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 - 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 + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
|
||||
pImpl->close();
|
||||
|
||||
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->close();
|
||||
//circle
|
||||
} else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
|
||||
return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
|
||||
} else {
|
||||
auto hrx = rx * PATH_KAPPA;
|
||||
auto hry = ry * PATH_KAPPA;
|
||||
pImpl->grow(10, 17);
|
||||
pImpl->moveTo(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->moveTo(x + w, y + 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->lineTo(x + rx, y + h);
|
||||
pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
|
||||
pImpl->lineTo(x, y + ry);
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,19 +20,28 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include "tvgArray.h"
|
||||
#include "tvgInlist.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
#ifdef THORVG_THREAD_SUPPORT
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
namespace tvg {
|
||||
|
||||
struct TaskSchedulerImpl;
|
||||
static TaskSchedulerImpl* inst = nullptr;
|
||||
|
||||
#ifdef THORVG_THREAD_SUPPORT
|
||||
|
||||
static thread_local bool _async = true;
|
||||
|
||||
struct TaskQueue {
|
||||
Inlist<Task> taskDeque;
|
||||
mutex mtx;
|
||||
|
@ -61,7 +70,7 @@ struct TaskQueue {
|
|||
void complete()
|
||||
{
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
lock_guard<mutex> lock{mtx};
|
||||
done = true;
|
||||
}
|
||||
ready.notify_all();
|
||||
|
@ -84,7 +93,7 @@ struct TaskQueue {
|
|||
void push(Task* task)
|
||||
{
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
lock_guard<mutex> lock{mtx};
|
||||
taskDeque.back(task);
|
||||
}
|
||||
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
|
||||
{
|
||||
Array<thread*> threads;
|
||||
Array<TaskQueue*> taskQueues;
|
||||
atomic<uint32_t> idx{0};
|
||||
|
||||
TaskSchedulerImpl(unsigned threadCnt)
|
||||
TaskSchedulerImpl(uint32_t threadCnt)
|
||||
{
|
||||
threads.reserve(threadCnt);
|
||||
taskQueues.reserve(threadCnt);
|
||||
|
||||
for (unsigned i = 0; i < threadCnt; ++i) {
|
||||
for (uint32_t i = 0; i < threadCnt; ++i) {
|
||||
taskQueues.push(new TaskQueue);
|
||||
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); });
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +142,7 @@ struct TaskSchedulerImpl
|
|||
//Thread Loop
|
||||
while (true) {
|
||||
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)) {
|
||||
success = true;
|
||||
break;
|
||||
|
@ -154,7 +160,7 @@ struct TaskSchedulerImpl
|
|||
if (threads.count > 0 && _async) {
|
||||
task->prepare();
|
||||
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;
|
||||
}
|
||||
taskQueues[i % threads.count]->push(task);
|
||||
|
@ -163,17 +169,33 @@ struct TaskSchedulerImpl
|
|||
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 */
|
||||
/************************************************************************/
|
||||
|
||||
void TaskScheduler::init(unsigned threads)
|
||||
void TaskScheduler::init(uint32_t threads)
|
||||
{
|
||||
if (inst) return;
|
||||
inst = new TaskSchedulerImpl(threads);
|
||||
|
@ -182,7 +204,6 @@ void TaskScheduler::init(unsigned threads)
|
|||
|
||||
void TaskScheduler::term()
|
||||
{
|
||||
if (!inst) return;
|
||||
delete(inst);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void TaskScheduler::async(bool on)
|
||||
{
|
||||
//toggle async tasking for each thread on/off
|
||||
_async = on;
|
||||
}
|
||||
|
|
|
@ -25,22 +25,13 @@
|
|||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgInlist.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
namespace tvg {
|
||||
|
||||
struct Task;
|
||||
|
||||
struct TaskScheduler
|
||||
{
|
||||
static unsigned threads();
|
||||
static void init(unsigned threads);
|
||||
static void term();
|
||||
static void request(Task* task);
|
||||
static void async(bool on);
|
||||
};
|
||||
#ifdef THORVG_THREAD_SUPPORT
|
||||
|
||||
struct Task
|
||||
{
|
||||
|
@ -86,7 +77,36 @@ private:
|
|||
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_
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
VERSION=0.12.1
|
||||
VERSION=0.12.3
|
||||
|
||||
cd thirdparty/thorvg/ || true
|
||||
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
|
||||
|
|
Loading…
Reference in New Issue