ThorVG: update from v0.11.2 to v0.11.6

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

Godot related:
    + [Renderer] Improved the internal structure for compact
      scene-hierarchy traversing.
    + [SwEngine] Improved trigonometric & image scaler performance.
    + [SwEngine] Fixed a loss of image pixels during image down-scaling.
    + [Renderer/Engine] Improved safety measures.
    + [SwEngine] Resolved a bug causing strokes to be improperly
      invisible due to clipping.
      thorvg/thorvg#1785
    + [Renderer] Rectified the precision of rounded rectangle corners.
      thorvg/thorvg#1824
    + [Portability] Resolved compiler shadowing warnings.
      thorvg/thorvg#1811

    Fixes godotengine#85465 Clipped strokes from outside the canvas.
    Fixes godotengine#86012 Rounded rectangles in SVG files rendering incorrectly.
This commit is contained in:
Martin Capitanio 2023-12-30 02:19:05 +01:00
parent 13a0d6e9b2
commit 74ed6d63bf
28 changed files with 380 additions and 588 deletions

View file

@ -861,7 +861,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
- Version: 0.11.2 (b01fe9bf4461146304d3520d6dc699cf580a3744, 2023)
- Version: 0.11.6 (3dba4f12f8f05f86acbc51096ca3a15f5d96bc06, 2023)
- License: MIT
Files extracted from upstream source:

View file

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

View file

@ -1664,14 +1664,14 @@ public:
* @param[in] no The index of the animation frame to be displayed. The index should be less than the totalFrame().
*
* @retval Result::Success Successfully set the frame.
* @retval Result::InsufficientCondition No animatable data loaded from the Picture.
* @retval Result::NonSupport The Picture data does not support animations.
* @retval Result::InsufficientCondition if the given @p no is the same as the current frame value.
* @retval Result::NonSupport The current Picture data does not support animations.
*
* @see totalFrame()
*
* @BETA_API
*/
Result frame(uint32_t no) noexcept;
Result frame(float no) noexcept;
/**
* @brief Retrieves a picture instance associated with this animation instance.
@ -1695,12 +1695,12 @@ public:
*
* @note If the Picture is not properly configured, this function will return 0.
*
* @see Animation::frame(uint32_t no)
* @see Animation::frame(float no)
* @see Animation::totalFrame()
*
* @BETA_API
*/
uint32_t curFrame() const noexcept;
float curFrame() const noexcept;
/**
* @brief Retrieves the total number of frames in the animation.
@ -1712,7 +1712,7 @@ public:
*
* @BETA_API
*/
uint32_t totalFrame() const noexcept;
float totalFrame() const noexcept;
/**
* @brief Retrieves the duration of the animation in seconds.
@ -1784,6 +1784,31 @@ public:
*/
Result save(std::unique_ptr<Paint> paint, const std::string& path, bool compress = true) noexcept;
/**
* @brief Export the provided animation data to the specified file path.
*
* This function exports the given animation data to the provided file path. You can also specify the desired frame rate in frames per second (FPS) by providing the fps parameter.
*
* @param[in] animation The animation to be saved, including all associated properties.
* @param[in] path The path to the file where the animation will be saved.
* @param[in] quality The encoded quality level. @c 0 is the minimum, @c 100 is the maximum value(recommended).
* @param[in] fps The desired frames per second (FPS). For example, to encode data at 60 FPS, pass 60. Pass 0 to keep the original frame data.
*
* @return Result::Success if the export succeeds.
* @return Result::InsufficientCondition if there are ongoing resource-saving operations.
* @return Result::NonSupport if an attempt is made to save the file with an unknown extension or in an unsupported format.
* @return Result::MemoryCorruption in case of an internal error.
* @return Result::Unknown if attempting to save an empty paint.
*
* @note A higher frames per second (FPS) would result in a larger file size. It is recommended to use the default value.
* @note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call sync() afterwards.
*
* @see Saver::sync()
*
* @note: Experimental API
*/
Result save(std::unique_ptr<Animation> animation, const std::string& path, uint32_t quality = 100, uint32_t fps = 0) noexcept;
/**
* @brief Guarantees that the saving task is finished.
*

View file

@ -25,6 +25,7 @@
#include <memory.h>
#include <cstdint>
#include <cstdlib>
namespace tvg
{

View file

@ -107,9 +107,9 @@ unique_ptr<Surface> PngLoader::bitmap()
//TODO: It's better to keep this surface instance in the loader side
auto surface = new Surface;
surface->buf32 = content;
surface->stride = w;
surface->w = w;
surface->h = h;
surface->stride = (uint32_t)w;
surface->w = (uint32_t)w;
surface->h = (uint32_t)h;
surface->cs = cs;
surface->channelSize = sizeof(uint32_t);
surface->owner = true;

View file

@ -1456,7 +1456,11 @@ void jpeg_decoder::locate_sof_marker()
int c = process_markers();
switch (c) {
case M_SOF2: m_progressive_flag = true;
case M_SOF2: {
m_progressive_flag = true;
read_sof_marker();
break;
}
case M_SOF0: /* baseline DCT */
case M_SOF1: { /* extended sequential DCT */
read_sof_marker();

View file

@ -43,11 +43,16 @@ static double timeStamp()
#define SW_ANGLE_PI (180L << 16)
#define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
#define SW_ANGLE_PI4 (SW_ANGLE_PI >> 2)
using SwCoord = signed long;
using SwFixed = signed long long;
static inline float TO_FLOAT(SwCoord val)
{
return static_cast<float>(val) / 64.0f;
}
struct SwPoint
{
SwCoord x, y;
@ -92,6 +97,10 @@ struct SwPoint
else return false;
}
Point toPoint() const
{
return {TO_FLOAT(x), TO_FLOAT(y)};
}
};
struct SwSize

View file

@ -20,7 +20,7 @@
* SOFTWARE.
*/
#include <math.h>
#include "tvgMath.h"
#include "tvgSwCommon.h"
@ -28,173 +28,9 @@
/* Internal Class Implementation */
/************************************************************************/
//clz: count leading zeros
#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
static uint32_t __inline _clz(uint32_t value)
{
unsigned long leadingZero = 0;
if (_BitScanReverse(&leadingZero, value)) return 31 - leadingZero;
else return 32;
}
#else
#define _clz(x) __builtin_clz((x))
#endif
constexpr SwFixed CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
constexpr static auto ATAN_MAX = 23;
constexpr static SwFixed ATAN_TBL[] = {
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
57L, 29L, 14L, 7L, 4L, 2L, 1L};
static inline SwCoord SATURATE(const SwCoord x)
static float TO_RADIAN(SwFixed angle)
{
return (x >> (sizeof(SwCoord) * 8 - 1));
}
static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
{
return (((x) + ((n)/2)) & ~((n)-1));
}
static SwCoord _downscale(SwFixed x)
{
//multiply a give value by the CORDIC shrink factor
auto s = abs(x);
int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
s = static_cast<SwFixed>(t >> 32);
if (x < 0) s = -s;
return s;
}
static int32_t _normalize(SwPoint& pt)
{
/* the highest bit in overflow-safe vector components
MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
constexpr auto SAFE_MSB = 29;
auto v = pt;
//High order bit(MSB)
int32_t shift = 31 - _clz(abs(v.x) | abs(v.y));
if (shift <= SAFE_MSB) {
shift = SAFE_MSB - shift;
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
} else {
shift -= SAFE_MSB;
pt.x = v.x >> shift;
pt.y = v.y >> shift;
shift = -shift;
}
return shift;
}
static void _polarize(SwPoint& pt)
{
auto v = pt;
SwFixed theta;
//Get the vector into [-PI/4, PI/4] sector
if (v.y > v.x) {
if (v.y > -v.x) {
auto tmp = v.y;
v.y = -v.x;
v.x = tmp;
theta = SW_ANGLE_PI2;
} else {
theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
v.x = -v.x;
v.y = -v.y;
}
} else {
if (v.y < -v.x) {
theta = -SW_ANGLE_PI2;
auto tmp = -v.y;
v.y = v.x;
v.x = tmp;
} else {
theta = 0;
}
}
auto atan = ATAN_TBL;
uint32_t i;
SwFixed j;
//Pseudorotations. with right shifts
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
if (v.y > 0) {
auto tmp = v.x + ((v.y + j) >> i);
v.y = v.y - ((v.x + j) >> i);
v.x = tmp;
theta += *atan++;
} else {
auto tmp = v.x - ((v.y + j) >> i);
v.y = v.y + ((v.x + j) >> i);
v.x = tmp;
theta -= *atan++;
}
}
//round theta
if (theta >= 0) theta = PAD_ROUND(theta, 32);
else theta = -PAD_ROUND(-theta, 32);
pt.x = v.x;
pt.y = theta;
}
static void _rotate(SwPoint& pt, SwFixed theta)
{
SwFixed x = pt.x;
SwFixed y = pt.y;
//Rotate inside [-PI/4, PI/4] sector
while (theta < -SW_ANGLE_PI4) {
auto tmp = y;
y = -x;
x = tmp;
theta += SW_ANGLE_PI2;
}
while (theta > SW_ANGLE_PI4) {
auto tmp = -y;
y = x;
x = tmp;
theta -= SW_ANGLE_PI2;
}
auto atan = ATAN_TBL;
uint32_t i;
SwFixed j;
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
if (theta < 0) {
auto tmp = x + ((y + j) >> i);
y = y - ((x + j) >> i);
x = tmp;
theta += *atan++;
} else {
auto tmp = x - ((y + j) >> i);
y = y + ((x + j) >> i);
x = tmp;
theta -= *atan++;
}
}
pt.x = static_cast<SwCoord>(x);
pt.y = static_cast<SwCoord>(y);
return (float(angle) / 65536.0f) * (MATH_PI / 180.0f);
}
@ -214,11 +50,17 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
auto d2 = base[1] - base[2];
auto d3 = base[0] - base[1];
if (d1 == d2 || d2 == d3) {
if (d3.small()) angleIn = angleMid = angleOut = 0;
else angleIn = angleMid = angleOut = mathAtan(d3);
return true;
}
if (d1.small()) {
if (d2.small()) {
if (d3.small()) {
//basically a point.
//do nothing to retain original direction
angleIn = angleMid = angleOut = 0;
return true;
} else {
angleIn = angleMid = angleOut = mathAtan(d3);
}
@ -320,77 +162,63 @@ int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
void mathRotate(SwPoint& pt, SwFixed angle)
{
if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
if (angle == 0 || pt.zero()) return;
auto v = pt;
auto shift = _normalize(v);
Point v = pt.toPoint();
auto theta = angle;
_rotate(v, theta);
auto radian = TO_RADIAN(angle);
auto cosv = cosf(radian);
auto sinv = sinf(radian);
v.x = _downscale(v.x);
v.y = _downscale(v.y);
if (shift > 0) {
auto half = static_cast<int32_t>(1L << (shift - 1));
pt.x = (v.x + half + SATURATE(v.x)) >> shift;
pt.y = (v.y + half + SATURATE(v.y)) >> shift;
} else {
shift = -shift;
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
}
pt.x = SwCoord(roundf((v.x * cosv - v.y * sinv) * 64.0f));
pt.y = SwCoord(roundf((v.x * sinv + v.y * cosv) * 64.0f));
}
SwFixed mathTan(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
_rotate(v, angle);
return mathDivide(v.y, v.x);
if (angle == 0) return 0;
return SwFixed(tanf(TO_RADIAN(angle)) * 65536.0f);
}
SwFixed mathAtan(const SwPoint& pt)
{
if (pt.x == 0 && pt.y == 0) return 0;
auto v = pt;
_normalize(v);
_polarize(v);
return v.y;
if (pt.zero()) return 0;
return SwFixed(atan2f(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
}
SwFixed mathSin(SwFixed angle)
{
if (angle == 0) return 0;
return mathCos(SW_ANGLE_PI2 - angle);
}
SwFixed mathCos(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
_rotate(v, angle);
return (v.x + 0x80L) >> 8;
return SwFixed(cosf(TO_RADIAN(angle)) * 65536.0f);
}
SwFixed mathLength(const SwPoint& pt)
{
auto v = pt;
if (pt.zero()) return 0;
//trivial case
if (v.x == 0) return abs(v.y);
if (v.y == 0) return abs(v.x);
if (pt.x == 0) return abs(pt.y);
if (pt.y == 0) return abs(pt.x);
//general case
auto shift = _normalize(v);
_polarize(v);
v.x = _downscale(v.x);
auto v = pt.toPoint();
//return static_cast<SwFixed>(sqrtf(v.x * v.x + v.y * v.y) * 65536.0f);
if (shift > 0) return (v.x + (static_cast<SwFixed>(1) << (shift -1))) >> shift;
return static_cast<SwFixed>((uint32_t)v.x << -shift);
/* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
With alpha = 1, beta = 3/8, giving results with the largest error less
than 7% compared to the exact value. */
if (v.x < 0) v.x = -v.x;
if (v.y < 0) v.y = -v.y;
return static_cast<SwFixed>((v.x > v.y) ? (v.x + v.y * 0.375f) : (v.y + v.x * 0.375f));
}
@ -401,22 +229,22 @@ void mathSplitCubic(SwPoint* base)
base[6].x = base[3].x;
c = base[1].x;
d = base[2].x;
base[1].x = a = (base[0].x + c) / 2;
base[5].x = b = (base[3].x + d) / 2;
c = (c + d) / 2;
base[2].x = a = (a + c) / 2;
base[4].x = b = (b + c) / 2;
base[3].x = (a + b) / 2;
base[1].x = a = (base[0].x + c) >> 1;
base[5].x = b = (base[3].x + d) >> 1;
c = (c + d) >> 1;
base[2].x = a = (a + c) >> 1;
base[4].x = b = (b + c) >> 1;
base[3].x = (a + b) >> 1;
base[6].y = base[3].y;
c = base[1].y;
d = base[2].y;
base[1].y = a = (base[0].y + c) / 2;
base[5].y = b = (base[3].y + d) / 2;
c = (c + d) / 2;
base[2].y = a = (a + c) / 2;
base[4].y = b = (b + c) / 2;
base[3].y = (a + b) / 2;
base[1].y = a = (base[0].y + c) >> 1;
base[5].y = b = (base[3].y + d) >> 1;
c = (c + d) >> 1;
base[2].y = a = (a + c) >> 1;
base[4].y = b = (b + c) >> 1;
base[3].y = (a + b) >> 1;
}

View file

@ -248,17 +248,17 @@ static inline uint32_t _sampleSize(float scale)
//Bilinear Interpolation
//OPTIMIZE_ME: Skip the function pointer access
static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, TVG_UNUSED uint32_t n, TVG_UNUSED uint32_t n2)
static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, TVG_UNUSED int32_t miny, TVG_UNUSED int32_t maxy, TVG_UNUSED int32_t n)
{
auto rx = (uint32_t)(sx);
auto ry = (uint32_t)(sy);
auto rx = (size_t)(sx);
auto ry = (size_t)(sy);
auto rx2 = rx + 1;
if (rx2 >= w) rx2 = w - 1;
auto ry2 = ry + 1;
if (ry2 >= h) ry2 = h - 1;
auto dx = static_cast<uint32_t>((sx - rx) * 255.0f);
auto dy = static_cast<uint32_t>((sy - ry) * 255.0f);
auto dx = static_cast<size_t>((sx - rx) * 255.0f);
auto dy = static_cast<size_t>((sy - ry) * 255.0f);
auto c1 = img[rx + ry * w];
auto c2 = img[rx2 + ry * w];
@ -271,18 +271,21 @@ static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride,
//2n x 2n Mean Kernel
//OPTIMIZE_ME: Skip the function pointer access
static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, uint32_t n, uint32_t n2)
static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, float sx, TVG_UNUSED float sy, int32_t miny, int32_t maxy, int32_t n)
{
uint32_t rx = lroundf(sx);
uint32_t ry = lroundf(sy);
uint32_t c[4] = {0, 0, 0, 0};
auto src = img + rx - n + (ry - n) * stride;
size_t c[4] = {0, 0, 0, 0};
for (auto y = ry - n; y < ry + n; ++y) {
if (y >= h) continue;
int32_t minx = (int32_t)sx - n;
if (minx < 0) minx = 0;
int32_t maxx = (int32_t)sx + n;
if (maxx >= (int32_t)w) maxx = w;
auto src = img + minx + miny * stride;
for (auto y = miny; y < maxy; ++y) {
auto p = src;
for (auto x = rx - n; x < rx + n; ++x, ++p) {
if (x >= w) continue;
for (auto x = minx; x < maxx; ++x, ++p) {
c[0] += *p >> 24;
c[1] += (*p >> 16) & 0xff;
c[2] += (*p >> 8) & 0xff;
@ -290,9 +293,14 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t
}
src += stride;
}
for (auto i = 0; i < 4; ++i) {
c[i] = (c[i] >> 2) / n2;
}
n = (maxy - miny) * (maxx - minx);
c[0] /= n;
c[1] /= n;
c[2] /= n;
c[3] /= n;
return (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
}
@ -660,34 +668,39 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g,
/* RLE Scaled Image */
/************************************************************************/
#define SCALED_IMAGE_RANGE_Y(y) \
auto sy = (y) * itransform->e22 + itransform->e23; \
auto my = (int32_t)round(sy); \
if (my < 0 || (uint32_t)sy >= image->h) continue; \
if (scaleMethod == _interpDownScaler) { \
miny = my - (int32_t)sampleSize; \
if (miny < 0) miny = 0; \
maxy = my + (int32_t)sampleSize; \
if (maxy >= (int32_t)image->h) maxy = (int32_t)image->h; \
}
#define SCALED_IMAGE_RANGE_X \
auto sx = x * itransform->e11 + itransform->e13; \
if ((int32_t)round(sx) < 0 || (uint32_t) sx >= image->w) continue;
#if 0 //Enable it when GRAYSCALE image is supported
static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto span = image->rle->spans;
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
SCALED_IMAGE_RANGE_Y(span->y)
auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
auto a = MULTIPLY(span->coverage, opacity);
if (a == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
*cmp = maskOp(src, *cmp, ~src);
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = MULTIPLY(src, a);
*cmp = maskOp(tmp, *cmp, ~tmp);
}
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (a < 255) src = MULTIPLY(src, a);
*cmp = maskOp(src, *cmp, ~src);
}
}
return true;
@ -698,31 +711,20 @@ static bool _rasterDirectScaledMaskedRleImage(SwSurface* surface, const SwImage*
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto span = image->rle->spans;
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
SCALED_IMAGE_RANGE_Y(span->y)
auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
auto dst = &surface->buf8[span->y * surface->stride + span->x];
auto a = MULTIPLY(span->coverage, opacity);
if (a == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = maskOp(src, *cmp, 0); //not use alpha
*dst = tmp + MULTIPLY(*dst, ~tmp);
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = maskOp(MULTIPLY(src, a), *cmp, 0); //not use alpha
*dst = tmp + MULTIPLY(*dst, ~tmp);
}
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (a < 255) src = MULTIPLY(src, a);
src = maskOp(src, *cmp, 0); //not use alpha
*dst = src + MULTIPLY(*dst, ~src);
}
}
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
@ -752,32 +754,20 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image
auto span = image->rle->spans;
auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
SCALED_IMAGE_RANGE_Y(span->y)
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize];
auto a = MULTIPLY(span->coverage, opacity);
if (a == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto tmp = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), alpha(cmp));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = ALPHA_BLEND(src, MULTIPLY(alpha(cmp), a));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
src = ALPHA_BLEND(src, (a == 255) ? alpha(cmp) : MULTIPLY(alpha(cmp), a));
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
}
@ -790,34 +780,24 @@ static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* ima
auto span = image->rle->spans;
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
SCALED_IMAGE_RANGE_Y(span->y)
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, A(src));
}
} else if (opacity == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src)));
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity);
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) src = ALPHA_BLEND(src, opacity);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src)));
}
@ -832,27 +812,17 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, cons
auto span = image->rle->spans;
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto sy = span->y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
SCALED_IMAGE_RANGE_Y(span->y)
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), alpha);
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (alpha < 255) src = ALPHA_BLEND(src, alpha);
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
}
return true;
@ -1067,29 +1037,18 @@ static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage*
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
int32_t miny = 0, maxy = 0;
for (auto y = region.min.y; y < region.max.y; ++y) {
auto sy = y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
SCALED_IMAGE_RANGE_Y(y)
auto cmp = cbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
*cmp = maskOp(src, *cmp, ~src);
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = MULTIPLY(src, opacity);
*cmp = maskOp(tmp, *cmp, ~tmp);
}
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) src = MULTIPLY(src, opacity);
*cmp = maskOp(src, *cmp, ~src);
}
cbuffer += cstride;
}
@ -1101,33 +1060,21 @@ static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* im
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x);
int32_t miny = 0, maxy = 0;
for (auto y = region.min.y; y < region.max.y; ++y) {
auto sy = y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
SCALED_IMAGE_RANGE_Y(y)
auto cmp = cbuffer;
auto dst = dbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = maskOp(src, *cmp, 0); //not use alpha
*dst = tmp + MULTIPLY(*dst, ~tmp);
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = MULTIPLY(src, opacity);
auto tmp2 = maskOp(tmp, *cmp, 0); //not use alpha
*dst = tmp2 + MULTIPLY(*dst, ~tmp2);
}
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) src = MULTIPLY(src, opacity);
src = maskOp(src, *cmp, 0); //not use alpha
*dst = src + MULTIPLY(*dst, ~src);
}
cbuffer += cstride;
dbuffer += surface->stride;
@ -1160,29 +1107,17 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
int32_t miny = 0, maxy = 0;
for (auto y = region.min.y; y < region.max.y; ++y) {
auto sy = y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
SCALED_IMAGE_RANGE_Y(y)
auto dst = dbuffer;
auto cmp = cbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto temp = ALPHA_BLEND(src, alpha(cmp));
*dst = temp + ALPHA_BLEND(*dst, IA(temp));
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto temp = ALPHA_BLEND(src, MULTIPLY(opacity, alpha(cmp)));
*dst = temp + ALPHA_BLEND(*dst, IA(temp));
}
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto tmp = ALPHA_BLEND(src, opacity == 255 ? alpha(cmp) : MULTIPLY(opacity, alpha(cmp)));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
dbuffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize;
@ -1196,28 +1131,17 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
int32_t miny = 0, maxy = 0;
for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
auto sy = y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
SCALED_IMAGE_RANGE_Y(y)
auto dst = dbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, A(src));
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, A(src));
}
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) ALPHA_BLEND(src, opacity);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, A(src));
}
}
return true;
@ -1229,26 +1153,16 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto sampleSize2 = sampleSize * sampleSize;
int32_t miny = 0, maxy = 0;
for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
auto sy = y * itransform->e22 + itransform->e23;
if ((uint32_t)sy >= image->h) continue;
SCALED_IMAGE_RANGE_Y(y)
auto dst = dbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2);
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue;
auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity);
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) src = ALPHA_BLEND(src, opacity);
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
}
return true;

View file

@ -139,7 +139,9 @@ struct SwShapeTask : SwTask
visibleFill = (alpha > 0 || rshape->fill);
if (visibleFill || clipper) {
shapeReset(&shape);
if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) {
visibleFill = false;
}
}
}
//Fill

View file

@ -558,11 +558,15 @@ void shapeReset(SwShape* shape)
void shapeFree(SwShape* shape)
{
rleFree(shape->rle);
shape->rle = nullptr;
shapeDelFill(shape);
if (shape->stroke) {
rleFree(shape->strokeRle);
shape->strokeRle = nullptr;
strokeFree(shape->stroke);
shape->stroke = nullptr;
}
}

View file

@ -62,7 +62,7 @@ Animation::Animation() : pImpl(new Impl)
}
Result Animation::frame(uint32_t no) noexcept
Result Animation::frame(float no) noexcept
{
auto loader = pImpl->picture->pImpl->loader.get();
@ -80,7 +80,7 @@ Picture* Animation::picture() const noexcept
}
uint32_t Animation::curFrame() const noexcept
float Animation::curFrame() const noexcept
{
auto loader = pImpl->picture->pImpl->loader.get();
@ -91,7 +91,7 @@ uint32_t Animation::curFrame() const noexcept
}
uint32_t Animation::totalFrame() const noexcept
float Animation::totalFrame() const noexcept
{
auto loader = pImpl->picture->pImpl->loader.get();

View file

@ -63,9 +63,9 @@ Result Canvas::clear(bool free) noexcept
Result Canvas::draw() noexcept
{
TVGLOG("COMMON", "Draw S. -------------------------------- Canvas(%p)", this);
TVGLOG("RENDERER", "Draw S. -------------------------------- Canvas(%p)", this);
auto ret = pImpl->draw();
TVGLOG("COMMON", "Draw E. -------------------------------- Canvas(%p)", this);
TVGLOG("RENDERER", "Draw E. -------------------------------- Canvas(%p)", this);
return ret;
}
@ -73,9 +73,9 @@ Result Canvas::draw() noexcept
Result Canvas::update(Paint* paint) noexcept
{
TVGLOG("COMMON", "Update S. ------------------------------ Canvas(%p)", this);
TVGLOG("RENDERER", "Update S. ------------------------------ Canvas(%p)", this);
auto ret = pImpl->update(paint, false);
TVGLOG("COMMON", "Update E. ------------------------------ Canvas(%p)", this);
TVGLOG("RENDERER", "Update E. ------------------------------ Canvas(%p)", this);
return ret;
}

View file

@ -62,7 +62,7 @@ using namespace tvg;
#define TVG_CLASS_ID_LINEAR 4
#define TVG_CLASS_ID_RADIAL 5
enum class FileType { Tvg = 0, Svg, Lottie, Raw, Png, Jpg, Webp, Unknown };
enum class FileType { Tvg = 0, Svg, Lottie, Raw, Png, Jpg, Webp, Gif, Unknown };
using Size = Point;

View file

@ -33,10 +33,9 @@ class FrameModule: public LoadModule
public:
virtual ~FrameModule() {}
virtual bool frame(uint32_t frameNo) = 0; //set the current frame number
virtual uint32_t totalFrame() = 0; //return the total frame count
virtual uint32_t curFrame() = 0; //return the current frame number
virtual bool frame(float no) = 0; //set the current frame number
virtual float totalFrame() = 0; //return the total frame count
virtual float curFrame() = 0; //return the current frame number
virtual float duration() = 0; //return the animation duration in seconds
virtual bool animatable() override { return true; }

View file

@ -136,7 +136,7 @@ static LoadModule* _find(FileType type)
break;
}
}
TVGLOG("LOADER", "%s format is not supported", format);
TVGLOG("RENDERER", "%s format is not supported", format);
#endif
return nullptr;
}
@ -170,7 +170,7 @@ static LoadModule* _findByType(const string& mimeType)
else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
else if (mimeType == "webp") type = FileType::Webp;
else {
TVGLOG("LOADER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
TVGLOG("RENDERER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
return nullptr;
}

View file

@ -22,11 +22,22 @@
#include "tvgMath.h"
#include "tvgPaint.h"
#include "tvgShape.h"
#include "tvgPicture.h"
#include "tvgScene.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
#define PAINT_METHOD(ret, METHOD) \
switch (id) { \
case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \
case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \
case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \
default: ret = {}; \
}
static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& viewport)
{
@ -93,9 +104,36 @@ static bool _compFastTrack(Paint* cmpTarget, const RenderTransform* pTransform,
}
RenderRegion Paint::Impl::bounds(RenderMethod& renderer) const
{
RenderRegion ret;
PAINT_METHOD(ret, bounds(renderer));
return ret;
}
bool Paint::Impl::dispose(RenderMethod& renderer)
{
if (compData) compData->target->pImpl->dispose(renderer);
bool ret;
PAINT_METHOD(ret, dispose(renderer));
return ret;
}
Iterator* Paint::Impl::iterator()
{
Iterator* ret;
PAINT_METHOD(ret, iterator());
return ret;
}
Paint* Paint::Impl::duplicate()
{
auto ret = smethod->duplicate();
Paint* ret;
PAINT_METHOD(ret, duplicate());
//duplicate Transform
if (rTransform) {
@ -165,8 +203,10 @@ bool Paint::Impl::render(RenderMethod& renderer)
/* Note: only ClipPath is processed in update() step.
Create a composition image. */
if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
auto region = smethod->bounds(renderer);
if (MASK_REGION_MERGING(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer));
RenderRegion region;
PAINT_METHOD(region, bounds(renderer));
if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer));
if (region.w == 0 || region.h == 0) return true;
cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) {
@ -177,7 +217,9 @@ bool Paint::Impl::render(RenderMethod& renderer)
if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
renderer.blend(blendMethod);
auto ret = smethod->render(renderer);
bool ret;
PAINT_METHOD(ret, render(renderer));
if (cmp) renderer.endComposite(cmp);
@ -189,10 +231,7 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
{
if (renderFlag & RenderUpdateFlag::Transform) {
if (!rTransform) return nullptr;
if (!rTransform->update()) {
delete(rTransform);
rTransform = nullptr;
}
rTransform->update();
}
/* 1. Composition Pre Processing */
@ -239,18 +278,13 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
}
/* 2. Main Update */
RenderData rd = nullptr;
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
renderFlag = RenderUpdateFlag::None;
opacity = MULTIPLY(opacity, this->opacity);
if (rTransform && pTransform) {
RenderTransform outTransform(pTransform, rTransform);
rd = smethod->update(renderer, &outTransform, clips, opacity, newFlag, clipper);
} else {
auto outTransform = pTransform ? pTransform : rTransform;
rd = smethod->update(renderer, outTransform, clips, opacity, newFlag, clipper);
}
RenderData rd = nullptr;
RenderTransform outTransform(pTransform, rTransform);
PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper));
/* 3. Composition Post Processing */
if (compFastTrack) renderer.viewport(viewport);
@ -263,9 +297,13 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
{
Matrix* m = nullptr;
bool ret;
//Case: No transformed, quick return!
if (!transformed || !(m = this->transform())) return smethod->bounds(x, y, w, h, stroking);
if (!transformed || !(m = this->transform())) {
PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
return ret;
}
//Case: Transformed
auto tx = 0.0f;
@ -273,7 +311,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
auto tw = 0.0f;
auto th = 0.0f;
auto ret = smethod->bounds(&tx, &ty, &tw, &th, stroking);
PAINT_METHOD(ret, bounds(&tx, &ty, &tw, &th, stroking));
//Get vertices
Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
@ -307,7 +345,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
/* External Class Implementation */
/************************************************************************/
Paint :: Paint() : pImpl(new Impl())
Paint :: Paint() : pImpl(new Impl(this))
{
}
@ -419,7 +457,10 @@ uint32_t Paint::identifier() const noexcept
Result Paint::blend(BlendMethod method) const noexcept
{
pImpl->blendMethod = method;
if (pImpl->blendMethod != method) {
pImpl->blendMethod = method;
pImpl->renderFlag |= RenderUpdateFlag::Blend;
}
return Result::Success;
}

View file

@ -38,19 +38,6 @@ namespace tvg
virtual void begin() = 0;
};
struct StrategyMethod
{
virtual ~StrategyMethod() {}
virtual bool dispose(RenderMethod& renderer) = 0; //return true if the deletion is allowed.
virtual void* update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) = 0; //Return engine data if it has.
virtual bool render(RenderMethod& renderer) = 0;
virtual bool bounds(float* x, float* y, float* w, float* h, bool stroking) = 0;
virtual RenderRegion bounds(RenderMethod& renderer) const = 0;
virtual Paint* duplicate() = 0;
virtual Iterator* iterator() = 0;
};
struct Composite
{
Paint* target;
@ -60,7 +47,7 @@ namespace tvg
struct Paint::Impl
{
StrategyMethod* smethod = nullptr;
Paint* paint = nullptr;
RenderTransform* rTransform = nullptr;
Composite* compData = nullptr;
BlendMethod blendMethod = BlendMethod::Normal; //uint8_t
@ -70,13 +57,16 @@ namespace tvg
uint8_t opacity = 255;
uint8_t refCnt = 0;
Impl(Paint* pnt) : paint(pnt)
{
}
~Impl()
{
if (compData) {
delete(compData->target);
if (P(compData->target)->unref() == 0) delete(compData->target);
free(compData);
}
delete(smethod);
delete(rTransform);
}
@ -92,11 +82,6 @@ namespace tvg
return (--refCnt);
}
void method(StrategyMethod* method)
{
smethod = method;
}
bool transform(const Matrix& m)
{
if (!rTransform) {
@ -119,29 +104,16 @@ namespace tvg
return nullptr;
}
RenderRegion bounds(RenderMethod& renderer) const
{
return smethod->bounds(renderer);
}
bool dispose(RenderMethod& renderer)
{
if (compData) compData->target->pImpl->dispose(renderer);
return smethod->dispose(renderer);
}
Iterator* iterator()
{
return smethod->iterator();
}
bool composite(Paint* source, Paint* target, CompositeMethod method)
{
//Invalid case
if ((!target && method != CompositeMethod::None) || (target && method == CompositeMethod::None)) return false;
if (compData) {
delete(compData->target);
P(compData->target)->unref();
if ((compData->target != target) && P(compData->target)->refCnt == 0) {
delete(compData->target);
}
//Reset scenario
if (!target && method == CompositeMethod::None) {
free(compData);
@ -152,12 +124,16 @@ namespace tvg
if (!target && method == CompositeMethod::None) return true;
compData = static_cast<Composite*>(calloc(1, sizeof(Composite)));
}
P(target)->ref();
compData->target = target;
compData->source = source;
compData->method = method;
return true;
}
RenderRegion bounds(RenderMethod& renderer) const;
bool dispose(RenderMethod& renderer);
Iterator* iterator();
bool rotate(float degree);
bool scale(float factor);
bool translate(float x, float y);
@ -166,51 +142,6 @@ namespace tvg
bool render(RenderMethod& renderer);
Paint* duplicate();
};
template<class T>
struct PaintMethod : StrategyMethod
{
T* inst = nullptr;
PaintMethod(T* _inst) : inst(_inst) {}
~PaintMethod() {}
bool bounds(float* x, float* y, float* w, float* h, bool stroking) override
{
return inst->bounds(x, y, w, h, stroking);
}
RenderRegion bounds(RenderMethod& renderer) const override
{
return inst->bounds(renderer);
}
bool dispose(RenderMethod& renderer) override
{
return inst->dispose(renderer);
}
RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag renderFlag, bool clipper) override
{
return inst->update(renderer, transform, clips, opacity, renderFlag, clipper);
}
bool render(RenderMethod& renderer) override
{
return inst->render(renderer);
}
Paint* duplicate() override
{
return inst->duplicate();
}
Iterator* iterator() override
{
return inst->iterator();
}
};
}
#endif //_TVG_PAINT_H_

View file

@ -62,7 +62,6 @@ RenderUpdateFlag Picture::Impl::load()
Picture::Picture() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
Paint::pImpl->method(new PaintMethod<Picture::Impl>(pImpl));
}

View file

@ -39,12 +39,9 @@ void RenderTransform::override(const Matrix& m)
}
bool RenderTransform::update()
void RenderTransform::update()
{
if (overriding) return true;
//Init Status
if (mathZero(x) && mathZero(y) && mathZero(degree) && mathEqual(scale, 1)) return false;
if (overriding) return;
mathIdentity(&m);
@ -53,17 +50,13 @@ bool RenderTransform::update()
if (!mathZero(degree)) mathRotate(&m, degree);
mathTranslate(&m, x, y);
return true;
}
RenderTransform::RenderTransform()
{
}
RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
{
m = mathMultiply(&lhs->m, &rhs->m);
if (lhs && rhs) m = mathMultiply(&lhs->m, &rhs->m);
else if (lhs) m = lhs->m;
else if (rhs) m = rhs->m;
else mathIdentity(&m);
}

View file

@ -32,7 +32,7 @@ namespace tvg
using RenderData = void*;
using pixel_t = uint32_t;
enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255};
enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
struct Surface;
@ -123,10 +123,10 @@ struct RenderTransform
float scale = 1.0f; //scale factor
bool overriding = false; //user transform?
bool update();
void update();
void override(const Matrix& m);
RenderTransform();
RenderTransform() {}
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
};

View file

@ -34,6 +34,7 @@ public:
virtual ~SaveModule() {}
virtual bool save(Paint* paint, const string& path, bool compress) = 0;
virtual bool save(Animation* animation, const string& path, uint32_t quality, uint32_t fps) = 0;
virtual bool close() = 0;
};

View file

@ -26,6 +26,9 @@
#ifdef THORVG_TVG_SAVER_SUPPORT
#include "tvgTvgSaver.h"
#endif
#ifdef THORVG_GIF_SAVER_SUPPORT
#include "tvgGifSaver.h"
#endif
/************************************************************************/
/* Internal Class Implementation */
@ -47,6 +50,12 @@ static SaveModule* _find(FileType type)
case FileType::Tvg: {
#ifdef THORVG_TVG_SAVER_SUPPORT
return new TvgSaver;
#endif
break;
}
case FileType::Gif: {
#ifdef THORVG_GIF_SAVER_SUPPORT
return new GifSaver;
#endif
break;
}
@ -62,12 +71,16 @@ static SaveModule* _find(FileType type)
format = "TVG";
break;
}
case FileType::Gif: {
format = "GIF";
break;
}
default: {
format = "???";
break;
}
}
TVGLOG("SAVER", "%s format is not supported", format);
TVGLOG("RENDERER", "%s format is not supported", format);
#endif
return nullptr;
}
@ -78,6 +91,8 @@ static SaveModule* _find(const string& path)
auto ext = path.substr(path.find_last_of(".") + 1);
if (!ext.compare("tvg")) {
return _find(FileType::Tvg);
} else if (!ext.compare("gif")) {
return _find(FileType::Gif);
}
return nullptr;
}
@ -124,6 +139,37 @@ Result Saver::save(std::unique_ptr<Paint> paint, const string& path, bool compre
}
Result Saver::save(std::unique_ptr<Animation> animation, const string& path, uint32_t quality, uint32_t fps) noexcept
{
auto a = animation.release();
if (!a) return Result::MemoryCorruption;
if (mathZero(a->totalFrame())) {
delete(a);
return Result::InsufficientCondition;
}
//Already on saving an other resource.
if (pImpl->saveModule) {
delete(a);
return Result::InsufficientCondition;
}
if (auto saveModule = _find(path)) {
if (saveModule->save(a, path, quality, fps)) {
pImpl->saveModule = saveModule;
return Result::Success;
} else {
delete(a);
delete(saveModule);
return Result::Unknown;
}
}
delete(a);
return Result::NonSupport;
}
Result Saver::sync() noexcept
{
if (!pImpl->saveModule) return Result::InsufficientCondition;

View file

@ -29,7 +29,6 @@
Scene::Scene() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_SCENE;
Paint::pImpl->method(new PaintMethod<Scene::Impl>(pImpl));
}

View file

@ -66,7 +66,7 @@ struct Scene::Impl
RenderData rd = nullptr;
Scene* scene = nullptr;
uint8_t opacity; //for composition
bool needComp; //composite or not
bool needComp = false; //composite or not
Impl(Scene* s) : scene(s)
{
@ -75,7 +75,7 @@ struct Scene::Impl
~Impl()
{
for (auto paint : paints) {
if (paint->pImpl->unref() == 0) delete(paint);
if (P(paint)->unref() == 0) delete(paint);
}
}
@ -148,6 +148,7 @@ struct Scene::Impl
if (needComp) {
cmp = renderer.target(bounds(renderer), renderer.colorSpace());
renderer.beginComposite(cmp, CompositeMethod::None, opacity);
needComp = false;
}
for (auto paint : paints) {

View file

@ -35,7 +35,6 @@ constexpr auto PATH_KAPPA = 0.552284f;
Shape :: Shape() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
Paint::pImpl->method(new PaintMethod<Shape::Impl>(pImpl));
}
@ -62,7 +61,7 @@ Result Shape::reset() noexcept
pImpl->rs.path.cmds.clear();
pImpl->rs.path.pts.clear();
pImpl->flag = RenderUpdateFlag::Path;
pImpl->flag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -70,18 +69,14 @@ Result Shape::reset() noexcept
uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
{
if (!cmds) return 0;
*cmds = pImpl->rs.path.cmds.data;
if (cmds) *cmds = pImpl->rs.path.cmds.data;
return pImpl->rs.path.cmds.count;
}
uint32_t Shape::pathCoords(const Point** pts) const noexcept
{
if (!pts) return 0;
*pts = pImpl->rs.path.pts.data;
if (pts) *pts = pImpl->rs.path.pts.data;
return pImpl->rs.path.pts.count;
}
@ -224,8 +219,8 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
} else if (mathEqual(rx, halfW) && mathEqual(ry, halfH)) {
return appendCircle(x + (w * 0.5f), y + (h * 0.5f), rx, ry);
} else {
auto hrx = rx * 0.5f;
auto hry = ry * 0.5f;
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);

View file

@ -38,7 +38,7 @@ struct Shape::Impl
Shape* shape;
uint8_t flag = RenderUpdateFlag::None;
uint8_t opacity; //for composition
bool needComp; //composite or not
bool needComp = false; //composite or not
Impl(Shape* s) : shape(s)
{
@ -59,6 +59,7 @@ struct Shape::Impl
if (needComp) {
cmp = renderer.target(bounds(renderer), renderer.colorSpace());
renderer.beginComposite(cmp, CompositeMethod::None, opacity);
needComp = false;
}
ret = renderer.renderShape(rd);
if (cmp) renderer.endComposite(cmp);

View file

@ -1,6 +1,6 @@
#!/bin/bash -e
VERSION=0.11.2
VERSION=0.11.6
cd thirdparty/thorvg/ || true
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
@ -56,9 +56,8 @@ rm -rfv ../src/renderer/gl_engine
# Enabled embedded loaders: raw, JPEG, PNG.
mkdir ../src/loaders
cp -rv src/loaders/svg src/loaders/raw ../src/loaders/
cp -rv src/loaders/svg src/loaders/jpg ../src/loaders/
cp -rv src/loaders/svg src/loaders/png ../src/loaders/
cp -rv src/loaders/svg src/loaders/external_png ../src/loaders/
cp -rv src/loaders/jpg ../src/loaders/
cp -rv src/loaders/png src/loaders/external_png ../src/loaders/
popd
rm -rf tmp