ThorVG: Update to 0.13.7

This commit is contained in:
Martin Capitanio 2024-06-08 19:42:09 +02:00
parent 5833f59786
commit 18650de06a
21 changed files with 351 additions and 244 deletions

View File

@ -882,7 +882,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
- Version: 0.13.3 (6235068cad8cad176ccd0cbcf82f25e985fbc258, 2024)
- Version: 0.13.7 (d2c0428a99f7305c086caffe0c730add601ebd6e, 2024)
- License: MIT
Files extracted from upstream source:

View File

@ -13,5 +13,5 @@
// For internal debugging:
//#define THORVG_LOG_ENABLED
#define THORVG_VERSION_STRING "0.13.5"
#define THORVG_VERSION_STRING "0.13.7"
#endif

View File

@ -80,7 +80,7 @@ enum class Result
InsufficientCondition, ///< The value returned in case the request cannot be processed - e.g. asking for properties of an object, which does not exist.
FailedAllocation, ///< The value returned in case of unsuccessful memory allocation.
MemoryCorruption, ///< The value returned in the event of bad memory handling - e.g. failing in pointer releasing or casting
NonSupport, ///< The value returned in case of choosing unsupported options.
NonSupport, ///< The value returned in case of choosing unsupported engine features(options).
Unknown ///< The value returned in all other cases.
};
@ -982,7 +982,7 @@ public:
*
* @param[in] width The width of the stroke. The default value is 0.
*
* @retval Result::Success when succeed, Result::FailedAllocation otherwise.
* @retval Result::Success when succeed.
*/
Result stroke(float width) noexcept;
@ -994,7 +994,7 @@ public:
* @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0.
* @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0.
*
* @retval Result::Success when succeed, Result::FailedAllocation otherwise.
* @retval Result::Success when succeed.
*/
Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept;
@ -1004,8 +1004,7 @@ public:
* @param[in] f The gradient fill.
*
* @retval Result::Success When succeed.
* @retval Result::FailedAllocation An internal error with a memory allocation for an object to be filled.
* @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument.
* @retval Result::MemoryCorruption In case a @c nullptr is passed as the argument or an error with accessing it.
*/
Result stroke(std::unique_ptr<Fill> f) noexcept;
@ -1029,7 +1028,7 @@ public:
*
* @param[in] cap The cap style value. The default value is @c StrokeCap::Square.
*
* @retval Result::Success when succeed, Result::FailedAllocation otherwise.
* @retval Result::Success when succeed.
*/
Result stroke(StrokeCap cap) noexcept;
@ -1040,22 +1039,37 @@ public:
*
* @param[in] join The join style value. The default value is @c StrokeJoin::Bevel.
*
* @retval Result::Success when succeed, Result::FailedAllocation otherwise.
* @retval Result::Success when succeed.
*/
Result stroke(StrokeJoin join) noexcept;
/**
* @brief Sets the stroke miterlimit.
*
* @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join, when the @c StrokeJoin::Miter join style is set. The default value is 4.
*
* @retval Result::Success when succeed, Result::NonSupport unsupported value, Result::FailedAllocation otherwise.
* @retval Result::Success when succeed or Result::InvalidArgument for @p miterlimit values less than zero.
*
* @since 0.11
*/
Result strokeMiterlimit(float miterlimit) noexcept;
/**
* @brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible.
*
* The values of the arguments @p begin, @p end, and @p offset are in the range of 0.0 to 1.0, representing the beginning of the path and the end, respectively.
*
* @param[in] begin Specifies the start of the segment to display along the path.
* @param[in] end Specifies the end of the segment to display along the path.
* @param[in] simultaneous Determines how to trim multiple paths within a single shape. If set to @c true (default), trimming is applied simultaneously to all paths;
* Otherwise, all paths are treated as a single entity with a combined length equal to the sum of their individual lengths and are trimmed as such.
*
* @retval Result::Success when succeed.
*
* @note Experimental API
*/
Result strokeTrim(float begin, float end, bool simultaneous = true) noexcept;
/**
* @brief Sets the solid color for all of the figures from the path.
*
@ -1095,19 +1109,17 @@ public:
*/
Result fill(FillRule r) noexcept;
/**
* @brief Sets the rendering order of the stroke and the fill.
*
* @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option).
*
* @retval Result::Success when succeed, Result::FailedAllocation otherwise.
* @retval Result::Success when succeed.
*
* @since 0.10
*/
Result order(bool strokeFirst) noexcept;
/**
* @brief Gets the commands data of the path.
*
@ -1210,6 +1222,18 @@ public:
*/
float strokeMiterlimit() const noexcept;
/**
* @brief Gets the trim of the stroke along the defined path segment.
*
* @param[out] begin The starting point of the segment to display along the path.
* @param[out] end Specifies the end of the segment to display along the path.
*
* @retval @c true if trimming is applied simultaneously to all paths of the shape, @c false otherwise.
*
* @note Experimental API
*/
bool strokeTrim(float* begin, float* end) const noexcept;
/**
* @brief Creates a new Shape object.
*

View File

@ -472,4 +472,19 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
}
/************************************************************************/
/* DJB2 Implementation */
/************************************************************************/
unsigned long djb2Encode(const char* str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c; // hash * 33 + c
}
return hash;
}
}

View File

@ -30,6 +30,7 @@ namespace tvg
uint8_t* lzwEncode(const uint8_t* uncompressed, uint32_t uncompressedSizeBytes, uint32_t* compressedSizeBytes, uint32_t* compressedSizeBits);
uint8_t* lzwDecode(const uint8_t* compressed, uint32_t compressedSizeBytes, uint32_t compressedSizeBits, uint32_t uncompressedSizeBytes);
size_t b64Decode(const char* encoded, const size_t len, char** decoded);
unsigned long djb2Encode(const char* str);
}
#endif //_TVG_COMPRESSOR_H_

View File

@ -47,23 +47,14 @@ bool mathInverse(const Matrix* m, Matrix* out)
}
Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs)
bool mathIdentity(const Matrix* m)
{
Matrix m;
m.e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31;
m.e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32;
m.e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33;
m.e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31;
m.e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32;
m.e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33;
m.e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31;
m.e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32;
m.e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33;
return m;
if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
m->e31 != 0.0f || m->e32 != 0.0f || m->e33 != 1.0f) {
return false;
}
return true;
}
@ -82,21 +73,41 @@ void mathRotate(Matrix* m, float degree)
}
bool mathIdentity(const Matrix* m)
Matrix operator*(const Matrix& lhs, const Matrix& rhs)
{
if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
m->e31 != 0.0f || m->e32 != 0.0f || m->e33 != 1.0f) {
Matrix m;
m.e11 = lhs.e11 * rhs.e11 + lhs.e12 * rhs.e21 + lhs.e13 * rhs.e31;
m.e12 = lhs.e11 * rhs.e12 + lhs.e12 * rhs.e22 + lhs.e13 * rhs.e32;
m.e13 = lhs.e11 * rhs.e13 + lhs.e12 * rhs.e23 + lhs.e13 * rhs.e33;
m.e21 = lhs.e21 * rhs.e11 + lhs.e22 * rhs.e21 + lhs.e23 * rhs.e31;
m.e22 = lhs.e21 * rhs.e12 + lhs.e22 * rhs.e22 + lhs.e23 * rhs.e32;
m.e23 = lhs.e21 * rhs.e13 + lhs.e22 * rhs.e23 + lhs.e23 * rhs.e33;
m.e31 = lhs.e31 * rhs.e11 + lhs.e32 * rhs.e21 + lhs.e33 * rhs.e31;
m.e32 = lhs.e31 * rhs.e12 + lhs.e32 * rhs.e22 + lhs.e33 * rhs.e32;
m.e33 = lhs.e31 * rhs.e13 + lhs.e32 * rhs.e23 + lhs.e33 * rhs.e33;
return m;
}
bool operator==(const Matrix& lhs, const Matrix& rhs)
{
if (!mathEqual(lhs.e11, rhs.e11) || !mathEqual(lhs.e12, rhs.e12) || !mathEqual(lhs.e13, rhs.e13) ||
!mathEqual(lhs.e21, rhs.e21) || !mathEqual(lhs.e22, rhs.e22) || !mathEqual(lhs.e23, rhs.e23) ||
!mathEqual(lhs.e31, rhs.e31) || !mathEqual(lhs.e32, rhs.e32) || !mathEqual(lhs.e33, rhs.e33)) {
return false;
}
return true;
}
void mathMultiply(Point* pt, const Matrix* transform)
void operator*=(Point& pt, const Matrix& m)
{
auto tx = pt->x * transform->e11 + pt->y * transform->e12 + transform->e13;
auto ty = pt->x * transform->e21 + pt->y * transform->e22 + transform->e23;
pt->x = tx;
pt->y = ty;
auto tx = pt.x * m.e11 + pt.y * m.e12 + m.e13;
auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23;
pt.x = tx;
pt.y = ty;
}

View File

@ -38,11 +38,9 @@
#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
bool mathInverse(const Matrix* m, Matrix* out);
Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs);
void mathRotate(Matrix* m, float degree);
bool mathIdentity(const Matrix* m);
void mathMultiply(Point* pt, const Matrix* transform);
/************************************************************************/
/* General functions */
/************************************************************************/
static inline float mathDeg2Rad(float degree)
@ -63,28 +61,21 @@ static inline bool mathZero(float a)
}
static inline bool mathZero(const Point& p)
{
return mathZero(p.x) && mathZero(p.y);
}
static inline bool mathEqual(float a, float b)
{
return mathZero(a - b);
}
static inline bool mathEqual(const Matrix& a, const Matrix& b)
{
if (!mathEqual(a.e11, b.e11) || !mathEqual(a.e12, b.e12) || !mathEqual(a.e13, b.e13) ||
!mathEqual(a.e21, b.e21) || !mathEqual(a.e22, b.e22) || !mathEqual(a.e23, b.e23) ||
!mathEqual(a.e31, b.e31) || !mathEqual(a.e32, b.e32) || !mathEqual(a.e33, b.e33)) {
return false;
}
return true;
}
/************************************************************************/
/* Matrix functions */
/************************************************************************/
void mathRotate(Matrix* m, float degree);
bool mathInverse(const Matrix* m, Matrix* out);
bool mathIdentity(const Matrix* m);
Matrix operator*(const Matrix& lhs, const Matrix& rhs);
bool operator==(const Matrix& lhs, const Matrix& rhs);
static inline bool mathRightAngle(const Matrix* m)
{
@ -114,15 +105,6 @@ static inline void mathIdentity(Matrix* m)
}
static inline void mathTransform(Matrix* transform, Point* coord)
{
auto x = coord->x;
auto y = coord->y;
coord->x = x * transform->e11 + y * transform->e12 + transform->e13;
coord->y = x * transform->e21 + y * transform->e22 + transform->e23;
}
static inline void mathScale(Matrix* m, float sx, float sy)
{
m->e11 *= sx;
@ -158,12 +140,37 @@ static inline void mathTranslateR(Matrix* m, float x, float y)
}
static inline bool operator!=(const Matrix& lhs, const Matrix& rhs)
{
return !(lhs == rhs);
}
static inline void operator*=(Matrix& lhs, const Matrix& rhs)
{
lhs = lhs * rhs;
}
static inline void mathLog(Matrix* m)
{
TVGLOG("MATH", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m->e11, m->e12, m->e13, m->e21, m->e22, m->e23, m->e31, m->e32, m->e33);
}
/************************************************************************/
/* Point functions */
/************************************************************************/
void operator*=(Point& pt, const Matrix& m);
static inline bool mathZero(const Point& p)
{
return mathZero(p.x) && mathZero(p.y);
}
static inline float mathLength(const Point* a, const Point* b)
{
auto x = b->x - a->x;
@ -182,6 +189,18 @@ static inline float mathLength(const Point& a)
}
static inline bool operator==(const Point& lhs, const Point& rhs)
{
return mathEqual(lhs.x, rhs.x) && mathEqual(lhs.y, rhs.y);
}
static inline bool operator!=(const Point& lhs, const Point& rhs)
{
return !(lhs == rhs);
}
static inline Point operator-(const Point& lhs, const Point& rhs)
{
return {lhs.x - rhs.x, lhs.y - rhs.y};
@ -212,6 +231,10 @@ static inline Point operator/(const Point& lhs, const float rhs)
}
/************************************************************************/
/* Interpolation functions */
/************************************************************************/
template <typename T>
static inline T mathLerp(const T &start, const T &end, float t)
{

View File

@ -709,7 +709,7 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
*ref = _idFromUrl((const char*)(str + 3));
return true;
} else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') {
float th, ts, tb;
float_t th, ts, tb;
const char *content, *hue, *satuation, *brightness;
content = str + 4;
content = _skipSpace(content, nullptr);
@ -840,14 +840,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};
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
} else if (state == MatrixState::Translate) {
if (ptCount == 1) {
Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
} else if (ptCount == 2) {
Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
} else goto error;
} else if (state == MatrixState::Rotate) {
//Transform to signed.
@ -857,14 +857,14 @@ static Matrix* _parseTransformationMatrix(const char* value)
auto s = sinf(mathDeg2Rad(points[0]));
if (ptCount == 1) {
Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
} else if (ptCount == 3) {
Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
} else {
goto error;
}
@ -874,17 +874,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 };
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
} else if (state == MatrixState::SkewX) {
if (ptCount != 1) goto error;
auto deg = tanf(mathDeg2Rad(points[0]));
Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
} else if (state == MatrixState::SkewY) {
if (ptCount != 1) goto error;
auto deg = tanf(mathDeg2Rad(points[0]));
Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
*matrix = mathMultiply(matrix, &tmp);
*matrix *= tmp;
}
}
return matrix;

View File

@ -203,9 +203,9 @@ static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape*
if (node->transform) finalTransform = *node->transform;
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
finalTransform = mathMultiply(&finalTransform, &m);
finalTransform *= m;
}
if (child->transform) finalTransform = mathMultiply(child->transform, &finalTransform);
if (child->transform) finalTransform = *child->transform * finalTransform;
return _appendClipShape(loaderData, child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
}
@ -228,13 +228,13 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg
m = *node->transform;
}
if (compNode->transform) {
m = mathMultiply(&m, compNode->transform);
m *= *compNode->transform;
}
if (!compNode->node.clip.userSpace) {
float x, y, w, h;
P(paint)->bounds(&x, &y, &w, &h, false, false);
Matrix mBBox = {w, 0, x, 0, h, y, 0, 0, 1};
m = mathMultiply(&m, &mBBox);
m *= mBBox;
}
return m;
}
@ -474,7 +474,10 @@ static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* sh
auto ptsCnt = shape->pathCoords(&pts);
auto p = const_cast<Point*>(pts) + currentPtsCnt;
while (currentPtsCnt++ < ptsCnt) mathMultiply(p++, m);
while (currentPtsCnt++ < ptsCnt) {
*p *= *m;
++p;
}
}
_applyProperty(loaderData, node, shape, vBox, svgPath, true);
@ -505,6 +508,7 @@ static constexpr struct
} imageMimeTypes[] = {
{"jpeg", sizeof("jpeg"), imageMimeTypeEncoding::base64},
{"png", sizeof("png"), imageMimeTypeEncoding::base64},
{"webp", sizeof("webp"), imageMimeTypeEncoding::base64},
{"svg+xml", sizeof("svg+xml"), imageMimeTypeEncoding::base64 | imageMimeTypeEncoding::utf8},
};
@ -615,7 +619,7 @@ static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode*
auto sy = node->node.image.h / h;
m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1};
}
if (node->transform) m = mathMultiply(node->transform, &m);
if (node->transform) m = *node->transform * m;
picture->transform(m);
_applyComposition(loaderData, picture.get(), node, vBox, svgPath);
@ -708,7 +712,7 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
if (node->transform) mUseTransform = *node->transform;
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
mUseTransform = mathMultiply(&mUseTransform, &mTranslate);
mUseTransform *= mTranslate;
}
if (node->node.use.symbol) {
@ -732,9 +736,9 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
// mSceneTransform = mUseTransform * mSymbolTransform * mViewBox
Matrix mSceneTransform = mViewBox;
if (node->node.use.symbol->transform) {
mSceneTransform = mathMultiply(node->node.use.symbol->transform, &mViewBox);
mSceneTransform = *node->node.use.symbol->transform * mViewBox;
}
mSceneTransform = mathMultiply(&mUseTransform, &mSceneTransform);
mSceneTransform = mUseTransform * mSceneTransform;
scene->transform(mSceneTransform);
if (node->node.use.symbol->node.symbol.overflowVisible) {
@ -746,7 +750,7 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
// mClipTransform = mUseTransform * mSymbolTransform
Matrix mClipTransform = mUseTransform;
if (node->node.use.symbol->transform) {
mClipTransform = mathMultiply(&mUseTransform, node->node.use.symbol->transform);
mClipTransform = mUseTransform * *node->node.use.symbol->transform;
}
viewBoxClip->transform(mClipTransform);

View File

@ -150,7 +150,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (isTransformation) {
if (transform) gradTransform = mathMultiply(transform, &gradTransform);
if (transform) gradTransform = *transform * gradTransform;
} else if (transform) {
gradTransform = *transform;
isTransformation = true;
@ -216,7 +216,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (transform) {
if (isTransformation) gradTransform = mathMultiply(transform, &gradTransform);
if (isTransformation) gradTransform = *transform * gradTransform;
else {
gradTransform = *transform;
isTransformation = true;

View File

@ -81,7 +81,7 @@ SwMpool* mpoolInit(uint32_t threads)
{
auto allocSize = threads + 1;
auto mpool = static_cast<SwMpool*>(calloc(sizeof(SwMpool), 1));
auto mpool = static_cast<SwMpool*>(calloc(1, sizeof(SwMpool)));
mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
mpool->dashOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));

View File

@ -1108,7 +1108,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
float ys = FLT_MAX, ye = -1.0f;
for (int i = 0; i < 4; i++) {
if (transform) mathMultiply(&vertices[i].pt, transform);
if (transform) vertices[i].pt *= *transform;
if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
}
@ -1169,9 +1169,9 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c
float ys = FLT_MAX, ye = -1.0f;
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
transformedTris[i] = mesh->triangles[i];
mathMultiply(&transformedTris[i].vertex[0].pt, transform);
mathMultiply(&transformedTris[i].vertex[1].pt, transform);
mathMultiply(&transformedTris[i].vertex[2].pt, transform);
transformedTris[i].vertex[0].pt *= *transform;
transformedTris[i].vertex[1].pt *= *transform;
transformedTris[i].vertex[2].pt *= *transform;
if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y;
else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y;

View File

@ -78,7 +78,6 @@ struct SwShapeTask : SwTask
{
SwShape shape;
const RenderShape* rshape = nullptr;
bool cmpStroking = false;
bool clipper = false;
/* We assume that if the stroke width is greater than 2,

View File

@ -928,7 +928,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
rw.ySpan = 0;
rw.outline = const_cast<SwOutline*>(outline);
rw.bandSize = rw.bufferSize / (sizeof(Cell) * 8); //bandSize: 64
rw.bandSize = rw.bufferSize / (sizeof(Cell) * 2); //bandSize: 256
rw.bandShoot = 0;
rw.antiAlias = antiAlias;
@ -966,10 +966,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
if (cellMod > 0) cellStart += sizeof(Cell) - cellMod;
auto cellEnd = rw.bufferSize;
cellEnd -= cellEnd % sizeof(Cell);
auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + cellEnd);
auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + rw.bufferSize);
rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
if (rw.cells >= cellsMax) goto reduce_bands;

View File

@ -107,7 +107,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
if (mathZero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
//draw the current line fully
} else if (len < dash.curLen) {
} else if (len <= dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
if (dash.move) {
@ -168,7 +168,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
//draw the current line fully
if (mathZero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
} else if (len < dash.curLen) {
} else if (len <= dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
if (dash.move) {
@ -245,7 +245,86 @@ static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const
}
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length, SwMpool* mpool, unsigned tid)
static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length)
{
auto begin = length * rshape->stroke->trim.begin;
auto end = length * rshape->stroke->trim.end;
//default
if (end > begin) {
if (begin > 0.0f) dash->cnt = 4;
else dash->cnt = 2;
//looping
} else dash->cnt = 3;
if (dash->cnt == 2) {
dash->pattern[0] = end - begin;
dash->pattern[1] = length - (end - begin);
} else if (dash->cnt == 3) {
dash->pattern[0] = end;
dash->pattern[1] = (begin - end);
dash->pattern[2] = length - begin;
} else {
dash->pattern[0] = 0; //zero dash to start with a space.
dash->pattern[1] = begin;
dash->pattern[2] = end - begin;
dash->pattern[3] = length - end;
}
}
static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32_t shiftCmds, bool subpath)
{
const PathCommand* cmds = rshape->path.cmds.data + shiftCmds;
auto cmdCnt = rshape->path.cmds.count - shiftCmds;
const Point* pts = rshape->path.pts.data + shiftPts;
auto ptsCnt = rshape->path.pts.count - shiftPts;
//No actual shape data
if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
const Point* close = nullptr;
auto length = 0.0f;
//must begin with moveTo
if (cmds[0] == PathCommand::MoveTo) {
close = pts;
cmds++;
pts++;
cmdCnt--;
}
while (cmdCnt-- > 0) {
switch (*cmds) {
case PathCommand::Close: {
length += mathLength(pts - 1, close);
if (subpath) return length;
break;
}
case PathCommand::MoveTo: {
if (subpath) return length;
close = pts;
++pts;
break;
}
case PathCommand::LineTo: {
length += mathLength(pts - 1, pts);
++pts;
break;
}
case PathCommand::CubicTo: {
length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
pts += 3;
break;
}
}
++cmds;
}
return length;
}
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, bool trimmed, SwMpool* mpool, unsigned tid)
{
const PathCommand* cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count;
@ -255,49 +334,23 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
//No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
auto startPts = pts;
auto startCmds = cmds;
SwDashStroke dash;
auto offset = 0.0f;
auto trimmed = false;
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
auto simultaneous = rshape->stroke->trim.simultaneous;
//dash by trimming.
if (length > 0.0f && dash.cnt == 0) {
auto begin = length * rshape->stroke->trim.begin;
auto end = length * rshape->stroke->trim.end;
//TODO: mix trimming + dash style
//default
if (end > begin) {
if (begin > 0.0f) dash.cnt += 4;
else dash.cnt += 2;
//looping
} else dash.cnt += 3;
dash.pattern = (float*)malloc(sizeof(float) * dash.cnt);
if (dash.cnt == 2) {
dash.pattern[0] = end - begin;
dash.pattern[1] = length - (end - begin);
} else if (dash.cnt == 3) {
dash.pattern[0] = end;
dash.pattern[1] = (begin - end);
dash.pattern[2] = length - begin;
if (dash.cnt == 0) {
if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
else return nullptr;
} else {
dash.pattern[0] = 0; //zero dash to start with a space.
dash.pattern[1] = begin;
dash.pattern[2] = end - begin;
dash.pattern[3] = length - end;
//TODO: handle dash + trim - for now trimming ignoring is forced
trimmed = false;
}
trimmed = true;
//just a dasy style.
} else {
if (dash.cnt == 0) return nullptr;
}
//offset?
//offset
auto patternLength = 0.0f;
uint32_t offIdx = 0;
if (!mathZero(offset)) {
@ -319,6 +372,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
//must begin with moveTo
if (cmds[0] == PathCommand::MoveTo) {
if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous));
_dashMoveTo(dash, offIdx, offset, pts);
cmds++;
pts++;
@ -331,8 +385,12 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
break;
}
case PathCommand::MoveTo: {
if (rshape->stroke->trim.individual) _dashMoveTo(dash, pts);
else _dashMoveTo(dash, offIdx, offset, pts);
if (trimmed) {
if (simultaneous) {
_trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true));
_dashMoveTo(dash, offIdx, offset, pts);
} else _dashMoveTo(dash, pts);
} else _dashMoveTo(dash, offIdx, offset, pts);
++pts;
break;
}
@ -358,56 +416,6 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
}
static float _outlineLength(const RenderShape* rshape)
{
const PathCommand* cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count;
const Point* pts = rshape->path.pts.data;
auto ptsCnt = rshape->path.pts.count;
//No actual shape data
if (cmdCnt == 0 || ptsCnt == 0) return 0.0f;
const Point* close = nullptr;
auto length = 0.0f;
auto slength = -1.0f;
auto simultaneous = !rshape->stroke->trim.individual;
//Compute the whole length
while (cmdCnt-- > 0) {
switch (*cmds) {
case PathCommand::Close: {
length += mathLength(pts - 1, close);
//retrieve the max length of the shape if the simultaneous mode.
if (simultaneous) {
if (slength < length) slength = length;
length = 0.0f;
}
break;
}
case PathCommand::MoveTo: {
close = pts;
++pts;
break;
}
case PathCommand::LineTo: {
length += mathLength(pts - 1, pts);
++pts;
break;
}
case PathCommand::CubicTo: {
length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
pts += 3;
break;
}
}
++cmds;
}
if (simultaneous && slength > length) return slength;
else return length;
}
static bool _axisAlignedRect(const SwOutline* outline)
{
//Fast Track: axis-aligned rectangle?
@ -584,11 +592,10 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
auto dashStroking = false;
auto ret = true;
auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
//Dash style (+trimming)
if (rshape->stroke->dashCnt > 0 || length > 0) {
shapeOutline = _genDashOutline(rshape, transform, length, mpool, tid);
auto trimmed = rshape->strokeTrim();
if (rshape->stroke->dashCnt > 0 || trimmed) {
shapeOutline = _genDashOutline(rshape, transform, trimmed, mpool, tid);
if (!shapeOutline) return false;
dashStroking = true;
//Normal style

View File

@ -75,13 +75,13 @@ static Result _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform
auto v2 = *pt3;
if (rTransform) {
mathMultiply(&v1, &rTransform->m);
mathMultiply(&v2, &rTransform->m);
v1 *= rTransform->m;
v2 *= rTransform->m;
}
if (pTransform) {
mathMultiply(&v1, &pTransform->m);
mathMultiply(&v2, &pTransform->m);
v1 *= pTransform->m;
v2 *= pTransform->m;
}
//sorting
@ -327,7 +327,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
//Compute the AABB after transformation
for (int i = 0; i < 4; i++) {
mathMultiply(&pt[i], m);
pt[i] *= *m;
if (pt[i].x < x1) x1 = pt[i].x;
if (pt[i].x > x2) x2 = pt[i].x;

View File

@ -47,7 +47,7 @@ void RenderTransform::update()
mathScale(&m, scale, scale);
if (!mathZero(degree)) mathRotate(&m, degree);
mathRotate(&m, degree);
mathTranslate(&m, x, y);
}
@ -55,7 +55,7 @@ void RenderTransform::update()
RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
{
if (lhs && rhs) m = mathMultiply(&lhs->m, &rhs->m);
if (lhs && rhs) m = lhs->m * rhs->m;
else if (lhs) m = lhs->m;
else if (rhs) m = rhs->m;
else mathIdentity(&m);

View File

@ -142,7 +142,7 @@ struct RenderStroke
struct {
float begin = 0.0f;
float end = 1.0f;
bool individual = false;
bool simultaneous = true;
} trim;
~RenderStroke()

View File

@ -287,16 +287,14 @@ const Fill* Shape::fill() const noexcept
Result Shape::order(bool strokeFirst) noexcept
{
if (!pImpl->strokeFirst(strokeFirst)) return Result::FailedAllocation;
pImpl->strokeFirst(strokeFirst);
return Result::Success;
}
Result Shape::stroke(float width) noexcept
{
if (!pImpl->strokeWidth(width)) return Result::FailedAllocation;
pImpl->strokeWidth(width);
return Result::Success;
}
@ -309,8 +307,7 @@ float Shape::strokeWidth() const noexcept
Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
{
if (!pImpl->strokeColor(r, g, b, a)) return Result::FailedAllocation;
pImpl->strokeColor(r, g, b, a);
return Result::Success;
}
@ -349,27 +346,25 @@ uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
Result Shape::stroke(StrokeCap cap) noexcept
{
if (!pImpl->strokeCap(cap)) return Result::FailedAllocation;
pImpl->strokeCap(cap);
return Result::Success;
}
Result Shape::stroke(StrokeJoin join) noexcept
{
if (!pImpl->strokeJoin(join)) return Result::FailedAllocation;
pImpl->strokeJoin(join);
return Result::Success;
}
Result Shape::strokeMiterlimit(float miterlimit) noexcept
{
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
// - A negative value for stroke-miterlimit must be treated as an illegal value.
if (miterlimit < 0.0f) return Result::NonSupport;
if (miterlimit < 0.0f) return Result::InvalidArguments;
// TODO Find out a reasonable max value.
if (!pImpl->strokeMiterlimit(miterlimit)) return Result::FailedAllocation;
pImpl->strokeMiterlimit(miterlimit);
return Result::Success;
}
@ -385,12 +380,26 @@ StrokeJoin Shape::strokeJoin() const noexcept
return pImpl->rs.strokeJoin();
}
float Shape::strokeMiterlimit() const noexcept
{
return pImpl->rs.strokeMiterlimit();
}
Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
{
pImpl->strokeTrim(begin, end, simultaneous);
return Result::Success;
}
bool Shape::strokeTrim(float* begin, float* end) const noexcept
{
return pImpl->strokeTrim(begin, end);
}
Result Shape::fill(FillRule r) noexcept
{
pImpl->rs.rule = r;

View File

@ -207,60 +207,81 @@ struct Shape::Impl
flag |= RenderUpdateFlag::Path;
}
bool strokeWidth(float width)
void strokeWidth(float width)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
return true;
}
bool strokeTrim(float begin, float end, bool individual)
void strokeTrim(float begin, float end, bool simultaneous)
{
if (!rs.stroke) {
if (begin == 0.0f && end == 1.0f) return true;
if (begin == 0.0f && end == 1.0f) return;
rs.stroke = new RenderStroke();
}
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true;
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
rs.stroke->trim.simultaneous == simultaneous) return;
auto loop = true;
if (begin > 1.0f && end > 1.0f) loop = false;
if (begin < 0.0f && end < 0.0f) loop = false;
if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f && end <= 1.0f) loop = false;
if (begin > 1.0f) begin -= 1.0f;
if (begin < 0.0f) begin += 1.0f;
if (end > 1.0f) end -= 1.0f;
if (end < 0.0f) end += 1.0f;
if ((loop && begin < end) || (!loop && begin > end)) {
auto tmp = begin;
begin = end;
end = tmp;
}
rs.stroke->trim.begin = begin;
rs.stroke->trim.end = end;
rs.stroke->trim.individual = individual;
rs.stroke->trim.simultaneous = simultaneous;
flag |= RenderUpdateFlag::Stroke;
return true;
}
bool strokeCap(StrokeCap cap)
bool strokeTrim(float* begin, float* end)
{
if (rs.stroke) {
if (begin) *begin = rs.stroke->trim.begin;
if (end) *end = rs.stroke->trim.end;
return rs.stroke->trim.simultaneous;
} else {
if (begin) *begin = 0.0f;
if (end) *end = 1.0f;
return false;
}
}
void strokeCap(StrokeCap cap)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->cap = cap;
flag |= RenderUpdateFlag::Stroke;
return true;
}
bool strokeJoin(StrokeJoin join)
void strokeJoin(StrokeJoin join)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->join = join;
flag |= RenderUpdateFlag::Stroke;
return true;
}
bool strokeMiterlimit(float miterlimit)
void strokeMiterlimit(float miterlimit)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->miterlimit = miterlimit;
flag |= RenderUpdateFlag::Stroke;
return true;
}
bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
void strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
if (rs.stroke->fill) {
@ -275,8 +296,6 @@ struct Shape::Impl
rs.stroke->color[3] = a;
flag |= RenderUpdateFlag::Stroke;
return true;
}
Result strokeFill(unique_ptr<Fill> f)
@ -335,13 +354,11 @@ struct Shape::Impl
return rs.stroke->strokeFirst;
}
bool strokeFirst(bool strokeFirst)
void strokeFirst(bool strokeFirst)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->strokeFirst = strokeFirst;
flag |= RenderUpdateFlag::Stroke;
return true;
}
void update(RenderUpdateFlag flag)

View File

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