thorvg: Update to 0.14.7

Fixes #95861.

(cherry picked from commit f16d4af342)
This commit is contained in:
Rémi Verschelde 2024-08-27 15:54:05 +02:00
parent f7ad4dca8c
commit 4abc358952
No known key found for this signature in database
GPG Key ID: C3336907360768E1
37 changed files with 667 additions and 913 deletions

View File

@ -882,7 +882,7 @@ instead of `miniz.h` as an external dependency.
## thorvg ## thorvg
- Upstream: https://github.com/thorvg/thorvg - Upstream: https://github.com/thorvg/thorvg
- Version: 0.14.2 (f6c4d8a94e0b2194fe911d6e19a550683055dd50, 2024) - Version: 0.14.7 (e3a6bf5229a9671c385ee78bc33e6e6b611a9729, 2024)
- License: MIT - License: MIT
Files extracted from upstream source: Files extracted from upstream source:

View File

@ -15,5 +15,5 @@
// For internal debugging: // For internal debugging:
//#define THORVG_LOG_ENABLED //#define THORVG_LOG_ENABLED
#define THORVG_VERSION_STRING "0.14.2" #define THORVG_VERSION_STRING "0.14.7"
#endif #endif

View File

@ -157,7 +157,7 @@ enum class FillRule
enum class CompositeMethod enum class CompositeMethod
{ {
None = 0, ///< No composition is applied. None = 0, ///< No composition is applied.
ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. Note that ClipPath only supports the Shape type.
AlphaMask, ///< Alpha Masking using the compositing target's pixels as an alpha value. AlphaMask, ///< Alpha Masking using the compositing target's pixels as an alpha value.
InvAlphaMask, ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value. InvAlphaMask, ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value.
LumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9 LumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9
@ -165,7 +165,9 @@ enum class CompositeMethod
AddMask, ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) (Experimental API) AddMask, ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) (Experimental API)
SubtractMask, ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) (Experimental API) SubtractMask, ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) (Experimental API)
IntersectMask, ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) (Experimental API) IntersectMask, ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) (Experimental API)
DifferenceMask ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) (Experimental API) DifferenceMask, ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) (Experimental API)
LightenMask, ///< Where multiple masks intersect, the highest transparency value is used. (Experimental API)
DarkenMask ///< Where multiple masks intersect, the lowest transparency value is used. (Experimental API)
}; };
@ -232,34 +234,6 @@ struct Matrix
}; };
/**
* @brief A data structure representing a texture mesh vertex
*
* @param pt The vertex coordinate
* @param uv The normalized texture coordinate in the range (0.0..1.0, 0.0..1.0)
*
* @note Experimental API
*/
struct Vertex
{
Point pt;
Point uv;
};
/**
* @brief A data structure representing a triange in a texture mesh
*
* @param vertex The three vertices that make up the polygon
*
* @note Experimental API
*/
struct Polygon
{
Vertex vertex[3];
};
/** /**
* @class Paint * @class Paint
* *
@ -361,7 +335,7 @@ public:
* *
* @note Experimental API * @note Experimental API
*/ */
Result blend(BlendMethod method) const noexcept; Result blend(BlendMethod method) noexcept;
/** /**
* @deprecated Use bounds(float* x, float* y, float* w, float* h, bool transformed) instead * @deprecated Use bounds(float* x, float* y, float* w, float* h, bool transformed) instead
@ -371,15 +345,16 @@ public:
/** /**
* @brief Gets the axis-aligned bounding box of the paint object. * @brief Gets the axis-aligned bounding box of the paint object.
* *
* In case @p transform is @c true, all object's transformations are applied first, and then the bounding box is established. Otherwise, the bounding box is determined before any transformations. * @param[out] x The x-coordinate of the upper-left corner of the object.
* * @param[out] y The y-coordinate of the upper-left corner of the object.
* @param[out] x The x coordinate of the upper left corner of the object.
* @param[out] y The y coordinate of the upper left corner of the object.
* @param[out] w The width of the object. * @param[out] w The width of the object.
* @param[out] h The height of the object. * @param[out] h The height of the object.
* @param[in] transformed If @c true, the paint's transformations are taken into account, otherwise they aren't. * @param[in] transformed If @c true, the paint's transformations are taken into account in the scene it belongs to. Otherwise they aren't.
* *
* @note This is useful when you need to figure out the bounding box of the paint in the canvas space.
* @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object. * @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object.
* @note If @p transformed is @c true, the paint needs to be pushed into a canvas and updated before this api is called.
* @see Canvas::update()
*/ */
Result bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept; Result bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept;
@ -411,9 +386,9 @@ public:
CompositeMethod composite(const Paint** target) const noexcept; CompositeMethod composite(const Paint** target) const noexcept;
/** /**
* @brief Gets the blending method of the object. * @brief Retrieves the current blending method applied to the paint object.
* *
* @return The blending method * @return The currently set blending method.
* *
* @note Experimental API * @note Experimental API
*/ */
@ -428,6 +403,15 @@ public:
*/ */
uint32_t identifier() const noexcept; uint32_t identifier() const noexcept;
/**
* @brief Unique ID of this instance.
*
* This is reserved to specify an paint instance in a scene.
*
* @since Experimental API
*/
uint32_t id = 0;
_TVG_DECLARE_PRIVATE(Paint); _TVG_DECLARE_PRIVATE(Paint);
}; };
@ -675,7 +659,8 @@ public:
* @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds. * @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds.
* @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds. * @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds.
* *
* @note In case the first and the second points are equal, an object filled with such a gradient fill is not rendered. * @note In case the first and the second points are equal, an object is filled with a single color using the last color specified in the colorStops().
* @see Fill::colorStops()
*/ */
Result linear(float x1, float y1, float x2, float y2) noexcept; Result linear(float x1, float y1, float x2, float y2) noexcept;
@ -734,6 +719,8 @@ public:
* @param[in] radius The radius of the bounding circle. * @param[in] radius The radius of the bounding circle.
* *
* @retval Result::InvalidArguments in case the @p radius value is zero or less. * @retval Result::InvalidArguments in case the @p radius value is zero or less.
*
* @note In case the @p radius is zero, an object is filled with a single color using the last color specified in the colorStops().
*/ */
Result radial(float cx, float cy, float radius) noexcept; Result radial(float cx, float cy, float radius) noexcept;
@ -990,7 +977,7 @@ public:
/** /**
* @brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible. * @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. * If the values of the arguments @p begin and @p end exceed the 0-1 range, they are wrapped around in a manner similar to angle wrapping, effectively treating the range as circular.
* *
* @param[in] begin Specifies the start of the segment to display along the path. * @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] end Specifies the end of the segment to display along the path.
@ -1076,7 +1063,6 @@ public:
* @param[out] b The blue color channel value in the range [0 ~ 255]. * @param[out] b The blue color channel value in the range [0 ~ 255].
* @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
* *
* @return Result::Success when succeed.
*/ */
Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept; Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept;
@ -1219,7 +1205,7 @@ public:
* when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations * when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
* for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data. * for the sharable @p data. Instead, ThorVG will reuse the previously loaded picture data.
* *
* @param[in] data A pointer to a memory location where the content of the picture file is stored. * @param[in] data A pointer to a memory location where the content of the picture file is stored. A null-terminated string is expected for non-binary data if @p copy is @c false.
* @param[in] size The size in bytes of the memory occupied by the @p data. * @param[in] size The size in bytes of the memory occupied by the @p data.
* @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "lottie", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one. * @param[in] mimeType Mimetype or extension of data such as "jpg", "jpeg", "lottie", "svg", "svg+xml", "png", etc. In case an empty string or an unknown type is provided, the loaders will be tried one by one.
* @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not. * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
@ -1256,7 +1242,7 @@ public:
Result size(float* w, float* h) const noexcept; Result size(float* w, float* h) const noexcept;
/** /**
* @brief Loads a raw data from a memory block with a given size. * @brief Loads raw data in ARGB8888 format from a memory block of the given size.
* *
* ThorVG efficiently caches the loaded data using the specified @p data address as a key * ThorVG efficiently caches the loaded data using the specified @p data address as a key
* when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations * when the @p copy has @c false. This means that loading the same data again will not result in duplicate operations
@ -1265,47 +1251,27 @@ public:
* @param[in] data A pointer to a memory location where the content of the picture raw data is stored. * @param[in] data A pointer to a memory location where the content of the picture raw data is stored.
* @param[in] w The width of the image @p data in pixels. * @param[in] w The width of the image @p data in pixels.
* @param[in] h The height of the image @p data in pixels. * @param[in] h The height of the image @p data in pixels.
* @param[in] premultiplied If @c true, the given image data is alpha-premultiplied.
* @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not. * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
* *
* @note It expects premultiplied alpha data.
* @since 0.9 * @since 0.9
*/ */
Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept; Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept;
/** /**
* @brief Sets or removes the triangle mesh to deform the image. * @brief Retrieve a paint object from the Picture scene by its Unique ID.
* *
* If a mesh is provided, the transform property of the Picture will apply to the triangle mesh, and the * This function searches for a paint object within the Picture scene that matches the provided @p id.
* image data will be used as the texture.
* *
* If @p triangles is @c nullptr, or @p triangleCnt is 0, the mesh will be removed. * @param[in] id The Unique ID of the paint object.
* *
* Only raster image types are supported at this time (png, jpg). Vector types like svg and tvg do not support. * @return A pointer to the paint object that matches the given identifier, or @c nullptr if no matching paint object is found.
* mesh deformation. However, if required you should be able to render a vector image to a raster image and then apply a mesh.
* *
* @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh. * @see Accessor::id()
* @param[in] triangleCnt The number of Polygons(triangles) provided, or 0 to remove the mesh.
*
* @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect.
* @warning Please do not use it, this API is not official one. It could be modified in the next version.
* *
* @note Experimental API * @note Experimental API
*/ */
Result mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept; const Paint* paint(uint32_t id) noexcept;
/**
* @brief Return the number of triangles in the mesh, and optionally get a pointer to the array of triangles in the mesh.
*
* @param[out] triangles Optional. A pointer to the array of Polygons used by this mesh.
*
* @return The number of polygons in the array.
*
* @note Modifying the triangles returned by this method will modify them directly within the mesh.
* @warning Please do not use it, this API is not official one. It could be modified in the next version.
*
* @note Experimental API
*/
uint32_t mesh(const Polygon** triangles) const noexcept;
/** /**
* @brief Creates a new Picture object. * @brief Creates a new Picture object.
@ -1454,8 +1420,6 @@ public:
* @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0.
* @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0.
* *
* @retval Result::InsufficientCondition when the font has not been set up prior to this operation.
*
* @see Text::font() * @see Text::font()
* *
* @note Experimental API * @note Experimental API
@ -1469,8 +1433,6 @@ public:
* *
* @param[in] f The unique pointer to the gradient fill. * @param[in] f The unique pointer to the gradient fill.
* *
* @retval Result::InsufficientCondition when the font has not been set up prior to this operation.
*
* @note Either a solid color or a gradient fill is applied, depending on what was set as last. * @note Either a solid color or a gradient fill is applied, depending on what was set as last.
* @note Experimental API * @note Experimental API
* *
@ -1781,6 +1743,19 @@ public:
*/ */
static Result term(CanvasEngine engine) noexcept; static Result term(CanvasEngine engine) noexcept;
/**
* @brief Retrieves the version of the TVG engine.
*
* @param[out] major A major version number.
* @param[out] minor A minor version number.
* @param[out] micro A micro version number.
*
* @return The version of the engine in the format major.minor.micro, or a @p nullptr in case of an internal error.
*
* @note Experimental API
*/
static const char* version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept;
_TVG_DISABLE_CTOR(Initializer); _TVG_DISABLE_CTOR(Initializer);
}; };
@ -1879,7 +1854,7 @@ public:
* @retval Result::InsufficientCondition In case the animation is not loaded. * @retval Result::InsufficientCondition In case the animation is not loaded.
* @retval Result::NonSupport When it's not animatable. * @retval Result::NonSupport When it's not animatable.
* *
* @note Range from 0.0~1.0 * @note Animation allows a range from 0.0 to 1.0. @p end should not be higher than @p begin.
* @note If a marker has been specified, its range will be disregarded. * @note If a marker has been specified, its range will be disregarded.
* @see LottieAnimation::segment(const char* marker) * @see LottieAnimation::segment(const char* marker)
* @note Experimental API * @note Experimental API
@ -2030,17 +2005,36 @@ class TVG_API Accessor final
public: public:
~Accessor(); ~Accessor();
TVG_DEPRECATED std::unique_ptr<Picture> set(std::unique_ptr<Picture> picture, std::function<bool(const Paint* paint)> func) noexcept;
/** /**
* @brief Set the access function for traversing the Picture scene tree nodes. * @brief Set the access function for traversing the Picture scene tree nodes.
* *
* @param[in] picture The picture node to traverse the internal scene-tree. * @param[in] picture The picture node to traverse the internal scene-tree.
* @param[in] func The callback function calling for every paint nodes of the Picture. * @param[in] func The callback function calling for every paint nodes of the Picture.
* * @param[in] data Data passed to the @p func as its argument.
* @return Return the given @p picture instance.
* *
* @note The bitmap based picture might not have the scene-tree. * @note The bitmap based picture might not have the scene-tree.
*
* @note Experimental API
*/ */
std::unique_ptr<Picture> set(std::unique_ptr<Picture> picture, std::function<bool(const Paint* paint)> func) noexcept; Result set(const Picture* picture, std::function<bool(const Paint* paint, void* data)> func, void* data) noexcept;
/**
* @brief Generate a unique ID (hash key) from a given name.
*
* This function computes a unique identifier value based on the provided string.
* You can use this to assign a unique ID to the Paint object.
*
* @param[in] name The input string to generate the unique identifier from.
*
* @return The generated unique identifier value.
*
* @see Paint::id
*
* @note Experimental API
*/
static uint32_t id(const char* name) noexcept;
/** /**
* @brief Creates a new Accessor object. * @brief Creates a new Accessor object.

View File

@ -59,7 +59,7 @@ struct Array
data[count++] = element; data[count++] = element;
} }
void push(Array<T>& rhs) void push(const Array<T>& rhs)
{ {
if (rhs.count == 0) return; if (rhs.count == 0) return;
grow(rhs.count); grow(rhs.count);

View File

@ -478,6 +478,8 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
unsigned long djb2Encode(const char* str) unsigned long djb2Encode(const char* str)
{ {
if (!str) return 0;
unsigned long hash = 5381; unsigned long hash = 5381;
int c; int c;

View File

@ -100,7 +100,7 @@ struct Inlist
if (element == tail) tail = element->prev; if (element == tail) tail = element->prev;
} }
bool empty() bool empty() const
{ {
return head ? false : true; return head ? false : true;
} }

View File

@ -79,7 +79,7 @@ float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc
Bezier left; Bezier left;
bezSplitLeft(right, t, left); bezSplitLeft(right, t, left);
length = _bezLength(left, lineLengthFunc); length = _bezLength(left, lineLengthFunc);
if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) { if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < 1e-3f) {
break; break;
} }
if (length < at) { if (length < at) {

View File

@ -25,8 +25,6 @@
#ifdef THORVG_THREAD_SUPPORT #ifdef THORVG_THREAD_SUPPORT
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
#include <mutex> #include <mutex>
#include "tvgTaskScheduler.h" #include "tvgTaskScheduler.h"

View File

@ -78,17 +78,17 @@ bool mathIdentity(const Matrix* m);
Matrix operator*(const Matrix& lhs, const Matrix& rhs); Matrix operator*(const Matrix& lhs, const Matrix& rhs);
bool operator==(const Matrix& lhs, const Matrix& rhs); bool operator==(const Matrix& lhs, const Matrix& rhs);
static inline bool mathRightAngle(const Matrix* m) static inline bool mathRightAngle(const Matrix& m)
{ {
auto radian = fabsf(mathAtan2(m->e21, m->e11)); auto radian = fabsf(mathAtan2(m.e21, m.e11));
if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true; if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true;
return false; return false;
} }
static inline bool mathSkewed(const Matrix* m) static inline bool mathSkewed(const Matrix& m)
{ {
return !mathZero(m->e21 + m->e12); return !mathZero(m.e21 + m.e12);
} }
@ -233,6 +233,17 @@ static inline Point operator/(const Point& lhs, const float rhs)
} }
static inline Point mathNormal(const Point& p1, const Point& p2)
{
auto dir = p2 - p1;
auto len = mathLength(dir);
if (mathZero(len)) return {};
auto unitDir = dir / len;
return {-unitDir.y, unitDir.x};
}
static inline void mathLog(const Point& pt) static inline void mathLog(const Point& pt)
{ {
TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y); TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y);

View File

@ -710,15 +710,16 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
return true; 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] == ')') { } 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 th, ts, tb;
const char *content, *hue, *saturation, *brightness; const char* content = _skipSpace(str + 4, nullptr);
content = str + 4; const char* hue = nullptr;
content = _skipSpace(content, nullptr);
if (_parseNumber(&content, &hue, &th) && hue) { if (_parseNumber(&content, &hue, &th) && hue) {
const char* saturation = nullptr;
th = float(uint32_t(th) % 360); th = float(uint32_t(th) % 360);
hue = _skipSpace(hue, nullptr); hue = _skipSpace(hue, nullptr);
hue = (char*)_skipComma(hue); hue = (char*)_skipComma(hue);
hue = _skipSpace(hue, nullptr); hue = _skipSpace(hue, nullptr);
if (_parseNumber(&hue, &saturation, &ts) && saturation && *saturation == '%') { if (_parseNumber(&hue, &saturation, &ts) && saturation && *saturation == '%') {
const char* brightness = nullptr;
ts /= 100.0f; ts /= 100.0f;
saturation = _skipSpace(saturation + 1, nullptr); saturation = _skipSpace(saturation + 1, nullptr);
saturation = (char*)_skipComma(saturation); saturation = (char*)_skipComma(saturation);

View File

@ -134,7 +134,6 @@ struct SwFill
{ {
struct SwLinear { struct SwLinear {
float dx, dy; float dx, dy;
float len;
float offset; float offset;
}; };
@ -154,6 +153,7 @@ struct SwFill
uint32_t* ctable; uint32_t* ctable;
FillSpread spread; FillSpread spread;
bool solid = false; //solid color fill with the last color from colorStops
bool translucent; bool translucent;
}; };
@ -301,8 +301,8 @@ static inline uint32_t JOIN(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3)
static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a) static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
{ {
return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) + ++a;
((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff)); return (((((c >> 8) & 0x00ff00ff) * a) & 0xff00ff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff));
} }
static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a) static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
@ -494,38 +494,39 @@ SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
SwFixed mathLength(const SwPoint& pt); SwFixed mathLength(const SwPoint& pt);
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
SwFixed mathMean(SwFixed angle1, SwFixed angle2); SwFixed mathMean(SwFixed angle1, SwFixed angle2);
SwPoint mathTransform(const Point* to, const Matrix* transform); SwPoint mathTransform(const Point* to, const Matrix& transform);
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack); bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee); bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
void shapeReset(SwShape* shape); void shapeReset(SwShape* shape);
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite); bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
bool shapePrepared(const SwShape* shape); bool shapePrepared(const SwShape* shape);
bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias); bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid); void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform); void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform);
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
void shapeFree(SwShape* shape); void shapeFree(SwShape* shape);
void shapeDelStroke(SwShape* shape); void shapeDelStroke(SwShape* shape);
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable); bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable); bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
void shapeResetFill(SwShape* shape); void shapeResetFill(SwShape* shape);
void shapeResetStrokeFill(SwShape* shape); void shapeResetStrokeFill(SwShape* shape);
void shapeDelFill(SwShape* shape); void shapeDelFill(SwShape* shape);
void shapeDelStrokeFill(SwShape* shape); void shapeDelStrokeFill(SwShape* shape);
void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix* transform); void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix& transform);
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
void strokeFree(SwStroke* stroke); void strokeFree(SwStroke* stroke);
bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias); bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
void imageReset(SwImage* image); void imageReset(SwImage* image);
void imageFree(SwImage* image); void imageFree(SwImage* image);
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable); bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata);
void fillReset(SwFill* fill); void fillReset(SwFill* fill);
void fillFree(SwFill* fill); void fillFree(SwFill* fill);
@ -561,11 +562,11 @@ SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx);
void mpoolRetDashOutline(SwMpool* mpool, unsigned idx); void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
bool rasterCompositor(SwSurface* surface); bool rasterCompositor(SwSurface* surface);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id); bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity); bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h); bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len); void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);

View File

@ -58,7 +58,7 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f
auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA; auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
det = b * b + (rr - radial->fr * radial->fr) * radial->invA; det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr; deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr * 0.5f;
deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr; deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
} }
@ -125,6 +125,8 @@ static void _applyAA(const SwFill* fill, uint32_t begin, uint32_t end)
static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity) static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
{ {
if (fill->solid) return true;
if (!fill->ctable) { if (!fill->ctable) {
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t))); fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
if (!fill->ctable) return false; if (!fill->ctable) return false;
@ -205,28 +207,33 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
} }
bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* transform) bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& transform)
{ {
float x1, x2, y1, y2; float x1, x2, y1, y2;
if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false; if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
fill->linear.dx = x2 - x1; fill->linear.dx = x2 - x1;
fill->linear.dy = y2 - y1; fill->linear.dy = y2 - y1;
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy; auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
if (fill->linear.len < FLOAT_EPSILON) return true; if (len < FLOAT_EPSILON) {
if (mathZero(fill->linear.dx) && mathZero(fill->linear.dy)) {
fill->solid = true;
}
return true;
}
fill->linear.dx /= fill->linear.len; fill->linear.dx /= len;
fill->linear.dy /= fill->linear.len; fill->linear.dy /= len;
fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1; fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
auto gradTransform = linear->transform(); auto gradTransform = linear->transform();
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (isTransformation) { if (isTransformation) {
if (transform) gradTransform = *transform * gradTransform; gradTransform = transform * gradTransform;
} else if (transform) { } else {
gradTransform = *transform; gradTransform = transform;
isTransformation = true; isTransformation = true;
} }
@ -239,15 +246,13 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
auto dx = fill->linear.dx; auto dx = fill->linear.dx;
fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21; fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22; fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22;
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
} }
return true; return true;
} }
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform) bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& transform)
{ {
auto cx = P(radial)->cx; auto cx = P(radial)->cx;
auto cy = P(radial)->cy; auto cy = P(radial)->cy;
@ -256,7 +261,10 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
auto fy = P(radial)->fy; auto fy = P(radial)->fy;
auto fr = P(radial)->fr; auto fr = P(radial)->fr;
if (r < FLOAT_EPSILON) return true; if (mathZero(r)) {
fill->solid = true;
return true;
}
fill->radial.dr = r - fr; fill->radial.dr = r - fr;
fill->radial.dx = cx - fx; fill->radial.dx = cx - fx;
@ -289,13 +297,11 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
auto gradTransform = radial->transform(); auto gradTransform = radial->transform();
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform)); bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (transform) { if (isTransformation) gradTransform = transform * gradTransform;
if (isTransformation) gradTransform = *transform * gradTransform;
else { else {
gradTransform = *transform; gradTransform = transform;
isTransformation = true; isTransformation = true;
} }
}
if (isTransformation) { if (isTransformation) {
Matrix invTransform; Matrix invTransform;
@ -816,25 +822,32 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
} }
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
{ {
if (!fill) return false; if (!fill) return false;
fill->spread = fdata->spread(); fill->spread = fdata->spread();
if (ctable) {
if (!_updateColorTable(fill, fdata, surface, opacity)) return false;
}
if (fdata->identifier() == TVG_CLASS_ID_LINEAR) { if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform); if (!_prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform)) return false;
} else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) { } else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform); if (!_prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform)) return false;
} }
//LOG: What type of gradient?! if (ctable) return _updateColorTable(fill, fdata, surface, opacity);
return true;
}
return false;
const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata)
{
if (!fill->solid) return nullptr;
const Fill::ColorStop* colors;
auto cnt = fdata->colorStops(&colors);
if (cnt == 0 || !colors) return nullptr;
return colors + cnt - 1;
} }
@ -845,6 +858,7 @@ void fillReset(SwFill* fill)
fill->ctable = nullptr; fill->ctable = nullptr;
} }
fill->translucent = false; fill->translucent = false;
fill->solid = false;
} }

View File

@ -27,14 +27,14 @@
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
static inline bool _onlyShifted(const Matrix* m) static inline bool _onlyShifted(const Matrix& m)
{ {
if (mathEqual(m->e11, 1.0f) && mathEqual(m->e22, 1.0f) && mathZero(m->e12) && mathZero(m->e21)) return true; if (mathEqual(m.e11, 1.0f) && mathEqual(m.e22, 1.0f) && mathZero(m.e12) && mathZero(m.e21)) return true;
return false; return false;
} }
static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* transform, SwMpool* mpool, unsigned tid) static bool _genOutline(SwImage* image, const Matrix& transform, SwMpool* mpool, unsigned tid)
{ {
image->outline = mpoolReqOutline(mpool, tid); image->outline = mpoolReqOutline(mpool, tid);
auto outline = image->outline; auto outline = image->outline;
@ -45,48 +45,12 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
outline->closed.reserve(1); outline->closed.reserve(1);
Point to[4]; Point to[4];
if (mesh->triangleCnt > 0) {
// TODO: Optimise me. We appear to calculate this exact min/max bounding area in multiple
// places. We should be able to re-use one we have already done? Also see:
// tvgPicture.h --> bounds
// tvgSwRasterTexmap.h --> _rasterTexmapPolygonMesh
//
// TODO: Should we calculate the exact path(s) of the triangle mesh instead?
// i.e. copy tvgSwShape.capp -> _genOutline?
//
// TODO: Cntrs?
auto triangles = mesh->triangles;
auto min = triangles[0].vertex[0].pt;
auto max = triangles[0].vertex[0].pt;
for (uint32_t i = 0; i < mesh->triangleCnt; ++i) {
if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
}
to[0] = {min.x, min.y};
to[1] = {max.x, min.y};
to[2] = {max.x, max.y};
to[3] = {min.x, max.y};
} else {
auto w = static_cast<float>(image->w); auto w = static_cast<float>(image->w);
auto h = static_cast<float>(image->h); auto h = static_cast<float>(image->h);
to[0] = {0, 0}; to[0] = {0, 0};
to[1] = {w, 0}; to[1] = {w, 0};
to[2] = {w, h}; to[2] = {w, h};
to[3] = {0, h}; to[3] = {0, h};
}
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
outline->pts.push(mathTransform(&to[i], transform)); outline->pts.push(mathTransform(&to[i], transform));
@ -108,25 +72,25 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
{ {
image->direct = _onlyShifted(transform); image->direct = _onlyShifted(transform);
//Fast track: Non-transformed image but just shifted. //Fast track: Non-transformed image but just shifted.
if (image->direct) { if (image->direct) {
image->ox = -static_cast<int32_t>(nearbyint(transform->e13)); image->ox = -static_cast<int32_t>(nearbyint(transform.e13));
image->oy = -static_cast<int32_t>(nearbyint(transform->e23)); image->oy = -static_cast<int32_t>(nearbyint(transform.e23));
//Figure out the scale factor by transform matrix //Figure out the scale factor by transform matrix
} else { } else {
auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21)); auto scaleX = sqrtf((transform.e11 * transform.e11) + (transform.e21 * transform.e21));
auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12)); auto scaleY = sqrtf((transform.e22 * transform.e22) + (transform.e12 * transform.e12));
image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX; image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
if (mathZero(transform->e12) && mathZero(transform->e21)) image->scaled = true; if (mathZero(transform.e12) && mathZero(transform.e21)) image->scaled = true;
else image->scaled = false; else image->scaled = false;
} }
if (!_genOutline(image, mesh, transform, mpool, tid)) return false; if (!_genOutline(image, transform, mpool, tid)) return false;
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct); return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
} }

View File

@ -254,12 +254,10 @@ SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
} }
SwPoint mathTransform(const Point* to, const Matrix* transform) SwPoint mathTransform(const Point* to, const Matrix& transform)
{ {
if (!transform) return {TO_SWCOORD(to->x), TO_SWCOORD(to->y)}; auto tx = to->x * transform.e11 + to->y * transform.e12 + transform.e13;
auto ty = to->x * transform.e21 + to->y * transform.e22 + transform.e23;
auto tx = to->x * transform->e11 + to->y * transform->e12 + transform->e13;
auto ty = to->x * transform->e21 + to->y * transform->e22 + transform->e23;
return {TO_SWCOORD(tx), TO_SWCOORD(ty)}; return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
} }

View File

@ -194,10 +194,21 @@ static inline uint8_t _opMaskDifference(uint8_t s, uint8_t d, uint8_t a)
} }
static inline uint8_t _opMaskLighten(uint8_t s, uint8_t d, uint8_t a)
{
return (s > d) ? s : d;
}
static inline uint8_t _opMaskDarken(uint8_t s, uint8_t d, uint8_t a)
{
return (s < d) ? s : d;
}
static inline bool _direct(CompositeMethod method) static inline bool _direct(CompositeMethod method)
{ {
//subtract & Intersect allows the direct composition if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask || method == CompositeMethod::DarkenMask) return true;
if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask) return true;
return false; return false;
} }
@ -209,6 +220,8 @@ static inline SwMask _getMaskOp(CompositeMethod method)
case CompositeMethod::SubtractMask: return _opMaskSubtract; case CompositeMethod::SubtractMask: return _opMaskSubtract;
case CompositeMethod::DifferenceMask: return _opMaskDifference; case CompositeMethod::DifferenceMask: return _opMaskDifference;
case CompositeMethod::IntersectMask: return _opMaskIntersect; case CompositeMethod::IntersectMask: return _opMaskIntersect;
case CompositeMethod::LightenMask: return _opMaskLighten;
case CompositeMethod::DarkenMask: return _opMaskDarken;
default: return nullptr; default: return nullptr;
} }
} }
@ -832,7 +845,7 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, cons
} }
static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported scaled rle image!"); TVGERR("SW_ENGINE", "Not supported scaled rle image!");
@ -841,9 +854,7 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
Matrix itransform; Matrix itransform;
if (transform) { if (!mathInverse(&transform, &itransform)) return true;
if (!mathInverse(transform, &itransform)) return false;
} else mathIdentity(&itransform);
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity); if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity);
@ -1197,13 +1208,11 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
} }
static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity)
{ {
Matrix itransform; Matrix itransform;
if (transform) { if (!mathInverse(&transform, &itransform)) return true;
if (!mathInverse(transform, &itransform)) return false;
} else mathIdentity(&itransform);
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity); if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity);
@ -1389,14 +1398,12 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale image!");
return false;
}
auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
//32bits channels
if (surface->channelSize == sizeof(uint32_t)) {
auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
for (auto y = region.min.y; y < region.max.y; ++y) { for (auto y = region.min.y; y < region.max.y; ++y) {
auto dst = dbuffer; auto dst = dbuffer;
auto src = sbuffer; auto src = sbuffer;
@ -1413,6 +1420,24 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S
dbuffer += surface->stride; dbuffer += surface->stride;
sbuffer += image->stride; sbuffer += image->stride;
} }
//8bits grayscale
} else if (surface->channelSize == sizeof(uint8_t)) {
auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x];
for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image->stride) {
auto dst = dbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
*dst = *src + MULTIPLY(*dst, ~*src);
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
*dst = INTERPOLATE8(*src, *dst, opacity);
}
}
}
}
return true; return true;
} }
@ -1433,7 +1458,7 @@ static bool _directImage(SwSurface* surface, const SwImage* image, const SwBBox&
//Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed] //Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed]
static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity)
{ {
//RLE Image //RLE Image
if (image->rle) { if (image->rle) {
@ -1574,8 +1599,6 @@ static bool _rasterSolidGradientRect(SwSurface* surface, const SwBBox& region, c
static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
{ {
if (fill->linear.len < FLOAT_EPSILON) return false;
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRect<FillLinear>(surface, region, fill); if (_matting(surface)) return _rasterGradientMattedRect<FillLinear>(surface, region, fill);
else return _rasterGradientMaskedRect<FillLinear>(surface, region, fill); else return _rasterGradientMaskedRect<FillLinear>(surface, region, fill);
@ -1902,10 +1925,16 @@ void rasterPremultiply(Surface* surface)
} }
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id) bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity)
{ {
if (!shape->fill) return false; if (!shape->fill) return false;
if (auto color = fillFetchSolid(shape->fill, fdata)) {
auto a = MULTIPLY(color->a, opacity);
return a > 0 ? rasterShape(surface, shape, color->r, color->g, color->b, a) : true;
}
auto id = fdata->identifier();
if (shape->fastTrack) { if (shape->fastTrack) {
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill); if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill); else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
@ -1917,10 +1946,16 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
} }
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id) bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity)
{ {
if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false; if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false;
if (auto color = fillFetchSolid(shape->stroke->fill, fdata)) {
auto a = MULTIPLY(color->a, opacity);
return a > 0 ? rasterStroke(surface, shape, color->r, color->g, color->b, a) : true;
}
auto id = fdata->identifier();
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill); if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill); else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
@ -1952,13 +1987,12 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
} }
bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity) bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity)
{ {
//Outside of the viewport, skip the rendering //Outside of the viewport, skip the rendering
if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return true; if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return true;
if (mesh && mesh->triangleCnt > 0) return _rasterTexmapPolygonMesh(surface, image, mesh, transform, &bbox, opacity); return _rasterImage(surface, image, transform, bbox, opacity);
else return _rasterImage(surface, image, transform, bbox, opacity);
} }

View File

@ -53,12 +53,10 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
regionBottom = image->rle->spans[image->rle->size - 1].y; regionBottom = image->rle->spans[image->rle->size - 1].y;
} }
if (yStart >= regionBottom) return false;
if (yStart < regionTop) yStart = regionTop; if (yStart < regionTop) yStart = regionTop;
if (yEnd > regionBottom) yEnd = regionBottom; if (yEnd > regionBottom) yEnd = regionBottom;
return true; return yEnd > yStart;
} }
@ -868,11 +866,9 @@ static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t re
static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2) static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
{ {
if (lines[y].length[eidx] < abs(x - x2)) {
lines[y].length[eidx] = abs(x - x2); lines[y].length[eidx] = abs(x - x2);
lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1)); lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
} }
}
/* /*
@ -897,9 +893,14 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
ptx[1] = tx[1]; \ ptx[1] = tx[1]; \
} while (0) } while (0)
struct Point
{
int32_t x, y;
};
int32_t y = 0; int32_t y = 0;
SwPoint pEdge = {-1, -1}; //previous edge point Point pEdge = {-1, -1}; //previous edge point
SwPoint edgeDiff = {0, 0}; //temporary used for point distance Point edgeDiff = {0, 0}; //temporary used for point distance
/* store bigger to tx[0] between prev and current edge's x positions. */ /* store bigger to tx[0] between prev and current edge's x positions. */
int32_t tx[2] = {0, 0}; int32_t tx[2] = {0, 0};
@ -1024,6 +1025,7 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
static bool _apply(SwSurface* surface, AASpans* aaSpans) static bool _apply(SwSurface* surface, AASpans* aaSpans)
{ {
auto end = surface->buf32 + surface->h * surface->stride;
auto y = aaSpans->yStart; auto y = aaSpans->yStart;
uint32_t pixel; uint32_t pixel;
uint32_t* dst; uint32_t* dst;
@ -1044,8 +1046,13 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
dst = surface->buf32 + (offset + line->x[0]); dst = surface->buf32 + (offset + line->x[0]);
if (line->x[0] > 1) pixel = *(dst - 1); if (line->x[0] > 1) pixel = *(dst - 1);
else pixel = *dst; else pixel = *dst;
pos = 1; pos = 1;
//exceptional handling. out of memory bound.
if (dst + line->length[0] >= end) {
pos += (dst + line->length[0] - end);
}
while (pos <= line->length[0]) { while (pos <= line->length[0]) {
*dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos); *dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos);
++dst; ++dst;
@ -1053,13 +1060,17 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
} }
//Right edge //Right edge
dst = surface->buf32 + (offset + line->x[1] - 1); dst = surface->buf32 + offset + line->x[1] - 1;
if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1); if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
else pixel = *dst; else pixel = *dst;
pos = line->length[1];
pos = width; //exceptional handling. out of memory bound.
while ((int32_t)(width - line->length[1]) < pos) { if (dst - pos < surface->buf32) --pos;
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * (line->length[1] - (width - pos))));
while (pos > 0) {
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos));
--dst; --dst;
--pos; --pos;
} }
@ -1084,7 +1095,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
| / | | / |
3 -- 2 3 -- 2
*/ */
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity) static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!"); TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
@ -1092,7 +1103,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
} }
//Exceptions: No dedicated drawing area? //Exceptions: No dedicated drawing area?
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true;
/* Prepare vertices. /* Prepare vertices.
shift XY coordinates to match the sub-pixeling technique. */ shift XY coordinates to match the sub-pixeling technique. */
@ -1104,7 +1115,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
float ys = FLT_MAX, ye = -1.0f; float ys = FLT_MAX, ye = -1.0f;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (transform) vertices[i].pt *= *transform; vertices[i].pt *= transform;
if (vertices[i].pt.y < ys) ys = vertices[i].pt.y; if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
} }
@ -1135,68 +1146,3 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
#endif #endif
return _apply(surface, aaSpans); return _apply(surface, aaSpans);
} }
/*
Provide any number of triangles to draw a mesh using the supplied image.
Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one.
Example:
0 -- 1 0 -- 1 0
| / | --> | / / |
| / | | / / |
2 -- 3 2 1 -- 2
Should provide two Polygons, one for each triangle.
// TODO: region?
*/
static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
return false;
}
//Exceptions: No dedicated drawing area?
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
// Step polygons once to transform
auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * mesh->triangleCnt);
float ys = FLT_MAX, ye = -1.0f;
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
transformedTris[i] = mesh->triangles[i];
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;
if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y;
else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y;
if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y;
else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y;
// Convert normalized UV coordinates to image coordinates
transformedTris[i].vertex[0].uv.x *= (float)image->w;
transformedTris[i].vertex[0].uv.y *= (float)image->h;
transformedTris[i].vertex[1].uv.x *= (float)image->w;
transformedTris[i].vertex[1].uv.y *= (float)image->h;
transformedTris[i].vertex[2].uv.x *= (float)image->w;
transformedTris[i].vertex[2].uv.y *= (float)image->h;
}
// Get AA spans and step polygons again to draw
if (auto aaSpans = _AASpans(ys, ye, image, region)) {
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
_rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
}
#if 0
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
#endif
_apply(surface, aaSpans);
}
free(transformedTris);
return true;
}

View File

@ -39,7 +39,7 @@ struct SwTask : Task
SwSurface* surface = nullptr; SwSurface* surface = nullptr;
SwMpool* mpool = nullptr; SwMpool* mpool = nullptr;
SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
Matrix* transform = nullptr; Matrix transform;
Array<RenderData> clips; Array<RenderData> clips;
RenderUpdateFlag flags = RenderUpdateFlag::None; RenderUpdateFlag flags = RenderUpdateFlag::None;
uint8_t opacity; uint8_t opacity;
@ -68,10 +68,7 @@ struct SwTask : Task
virtual bool clip(SwRleData* target) = 0; virtual bool clip(SwRleData* target) = 0;
virtual SwRleData* rle() = 0; virtual SwRleData* rle() = 0;
virtual ~SwTask() virtual ~SwTask() {}
{
free(transform);
}
}; };
@ -100,8 +97,7 @@ struct SwShapeTask : SwTask
if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f; if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f;
if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f; if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
if (transform) return (width * sqrt(transform->e11 * transform->e11 + transform->e12 * transform->e12)); return (width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12));
else return width;
} }
@ -203,64 +199,10 @@ struct SwShapeTask : SwTask
}; };
struct SwSceneTask : SwTask
{
Array<RenderData> scene; //list of paints render data (SwTask)
SwRleData* sceneRle = nullptr;
bool clip(SwRleData* target) override
{
//Only one shape
if (scene.count == 1) {
return static_cast<SwTask*>(*scene.data)->clip(target);
}
//More than one shapes
if (sceneRle) rleClipPath(target, sceneRle);
else TVGLOG("SW_ENGINE", "No clippers in a scene?");
return true;
}
SwRleData* rle() override
{
return sceneRle;
}
void run(unsigned tid) override
{
//TODO: Skip the run if the scene hans't changed.
if (!sceneRle) sceneRle = static_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
else rleReset(sceneRle);
//Merge shapes if it has more than one shapes
if (scene.count > 1) {
//Merge first two clippers
auto clipper1 = static_cast<SwTask*>(*scene.data);
auto clipper2 = static_cast<SwTask*>(*(scene.data + 1));
rleMerge(sceneRle, clipper1->rle(), clipper2->rle());
//Unify the remained clippers
for (auto rd = scene.begin() + 2; rd < scene.end(); ++rd) {
auto clipper = static_cast<SwTask*>(*rd);
rleMerge(sceneRle, sceneRle, clipper->rle());
}
}
}
void dispose() override
{
rleFree(sceneRle);
}
};
struct SwImageTask : SwTask struct SwImageTask : SwTask
{ {
SwImage image; SwImage image;
Surface* source; //Image source Surface* source; //Image source
const RenderMesh* mesh = nullptr; //Should be valid ptr in action
bool clip(SwRleData* target) override bool clip(SwRleData* target) override
{ {
@ -293,10 +235,9 @@ struct SwImageTask : SwTask
imageReset(&image); imageReset(&image);
if (!image.data || image.w == 0 || image.h == 0) goto end; if (!image.data || image.w == 0 || image.h == 0) goto end;
if (!imagePrepare(&image, mesh, transform, clipRegion, bbox, mpool, tid)) goto end; if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
// TODO: How do we clip the triangle mesh? Only clip non-meshed images for now if (clips.count > 0) {
if (mesh->triangleCnt == 0 && clips.count > 0) {
if (!imageGenRle(&image, bbox, false)) goto end; if (!imageGenRle(&image, bbox, false)) goto end;
if (image.rle) { if (image.rle) {
//Clear current task memorypool here if the clippers would use the same memory pool //Clear current task memorypool here if the clippers would use the same memory pool
@ -336,7 +277,7 @@ static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
{ {
uint8_t r, g, b, a; uint8_t r, g, b, a;
if (auto fill = task->rshape->fill) { if (auto fill = task->rshape->fill) {
rasterGradientShape(surface, &task->shape, fill->identifier()); rasterGradientShape(surface, &task->shape, fill, opacity);
} else { } else {
task->rshape->fillColor(&r, &g, &b, &a); task->rshape->fillColor(&r, &g, &b, &a);
a = MULTIPLY(opacity, a); a = MULTIPLY(opacity, a);
@ -348,7 +289,7 @@ static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity
{ {
uint8_t r, g, b, a; uint8_t r, g, b, a;
if (auto strokeFill = task->rshape->strokeFill()) { if (auto strokeFill = task->rshape->strokeFill()) {
rasterGradientStroke(surface, &task->shape, strokeFill->identifier()); rasterGradientStroke(surface, &task->shape, strokeFill, opacity);
} else { } else {
if (task->rshape->strokeColor(&r, &g, &b, &a)) { if (task->rshape->strokeColor(&r, &g, &b, &a)) {
a = MULTIPLY(opacity, a); a = MULTIPLY(opacity, a);
@ -480,7 +421,7 @@ bool SwRenderer::renderImage(RenderData data)
if (task->opacity == 0) return true; if (task->opacity == 0) return true;
return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity); return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
} }
@ -688,7 +629,8 @@ bool SwRenderer::endComposite(Compositor* cmp)
//Default is alpha blending //Default is alpha blending
if (p->method == CompositeMethod::None) { if (p->method == CompositeMethod::None) {
return rasterImage(surface, &p->image, nullptr, nullptr, p->bbox, p->opacity); Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
} }
return true; return true;
@ -714,7 +656,7 @@ void SwRenderer::dispose(RenderData data)
} }
void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
{ {
if (!surface) return task; if (!surface) return task;
if (flags == RenderUpdateFlag::None) return task; if (flags == RenderUpdateFlag::None) return task;
@ -727,20 +669,11 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
} }
task->clips = clips; task->clips = clips;
task->transform = transform;
if (transform) {
if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
*task->transform = transform->m;
} else {
if (task->transform) free(task->transform);
task->transform = nullptr;
}
//zero size? //zero size?
if (task->transform) { if (task->transform.e11 == 0.0f && task->transform.e12 == 0.0f) return task; //zero width
if (task->transform->e11 == 0.0f && task->transform->e12 == 0.0f) return task; //zero width if (task->transform.e21 == 0.0f && task->transform.e22 == 0.0f) return task; //zero height
if (task->transform->e21 == 0.0f && task->transform->e22 == 0.0f) return task; //zero height
}
task->opacity = opacity; task->opacity = opacity;
task->surface = surface; task->surface = surface;
@ -762,7 +695,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
} }
RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) RenderData SwRenderer::prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
{ {
//prepare task //prepare task
auto task = static_cast<SwImageTask*>(data); auto task = static_cast<SwImageTask*>(data);
@ -770,33 +703,12 @@ RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
else task->done(); else task->done();
task->source = surface; task->source = surface;
task->mesh = mesh;
return prepareCommon(task, transform, clips, opacity, flags); return prepareCommon(task, transform, clips, opacity, flags);
} }
RenderData SwRenderer::prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
{
//prepare task
auto task = static_cast<SwSceneTask*>(data);
if (!task) task = new SwSceneTask;
else task->done();
task->scene = scene;
//TODO: Failed threading them. It would be better if it's possible.
//See: https://github.com/thorvg/thorvg/issues/1409
//Guarantee composition targets get ready.
for (auto task = scene.begin(); task < scene.end(); ++task) {
static_cast<SwTask*>(*task)->done();
}
return prepareCommon(task, transform, clips, opacity, flags);
}
RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
{ {
//prepare task //prepare task
auto task = static_cast<SwShapeTask*>(data); auto task = static_cast<SwShapeTask*>(data);

View File

@ -36,9 +36,8 @@ namespace tvg
class SwRenderer : public RenderMethod class SwRenderer : public RenderMethod
{ {
public: public:
RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override; RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
bool preRender() override; bool preRender() override;
bool renderShape(RenderData data) override; bool renderShape(RenderData data) override;
bool renderImage(RenderData data) override; bool renderImage(RenderData data) override;
@ -77,7 +76,7 @@ private:
SwRenderer(); SwRenderer();
~SwRenderer(); ~SwRenderer();
RenderData prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags); RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
}; };
} }

View File

@ -822,46 +822,6 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRl
} }
static SwSpan* _mergeSpansRegion(const SwRleData *clip1, const SwRleData *clip2, SwSpan *outSpans)
{
auto out = outSpans;
auto spans1 = clip1->spans;
auto end1 = clip1->spans + clip1->size;
auto spans2 = clip2->spans;
auto end2 = clip2->spans + clip2->size;
//list two spans up in y order
//TODO: Remove duplicated regions?
while (spans1 < end1 && spans2 < end2) {
while (spans1 < end1 && spans1->y <= spans2->y) {
*out = *spans1;
++spans1;
++out;
}
if (spans1 >= end1) break;
while (spans2 < end2 && spans2->y <= spans1->y) {
*out = *spans2;
++spans2;
++out;
}
}
//Leftovers
while (spans1 < end1) {
*out = *spans1;
++spans1;
++out;
}
while (spans2 < end2) {
*out = *spans2;
++spans2;
++out;
}
return out;
}
void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size) void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
{ {
free(rle->spans); free(rle->spans);
@ -1030,45 +990,6 @@ void rleFree(SwRleData* rle)
} }
void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2)
{
if (!rle || (!clip1 && !clip2)) return;
if (clip1 && clip1->size == 0 && clip2 && clip2->size == 0) return;
TVGLOG("SW_ENGINE", "Unifying Rle!");
//clip1 is empty, just copy clip2
if (!clip1 || clip1->size == 0) {
if (clip2) {
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip2->size)));
memcpy(spans, clip2->spans, clip2->size);
_replaceClipSpan(rle, spans, clip2->size);
} else {
_replaceClipSpan(rle, nullptr, 0);
}
return;
}
//clip2 is empty, just copy clip1
if (!clip2 || clip2->size == 0) {
if (clip1) {
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip1->size)));
memcpy(spans, clip1->spans, clip1->size);
_replaceClipSpan(rle, spans, clip1->size);
} else {
_replaceClipSpan(rle, nullptr, 0);
}
return;
}
auto spanCnt = clip1->size + clip2->size;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * spanCnt));
auto spansEnd = _mergeSpansRegion(clip1, clip2, spans);
_replaceClipSpan(rle, spans, spansEnd - spans);
}
void rleClipPath(SwRleData *rle, const SwRleData *clip) void rleClipPath(SwRleData *rle, const SwRleData *clip)
{ {
if (rle->size == 0 || clip->size == 0) return; if (rle->size == 0 || clip->size == 0) return;

View File

@ -49,7 +49,7 @@ static bool _outlineEnd(SwOutline& outline)
} }
static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform, bool closed = false) static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix& transform, bool closed = false)
{ {
//make it a contour, if the last contour is not closed yet. //make it a contour, if the last contour is not closed yet.
if (!closed) _outlineEnd(outline); if (!closed) _outlineEnd(outline);
@ -60,14 +60,14 @@ static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* tr
} }
static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform) static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix& transform)
{ {
outline.pts.push(mathTransform(to, transform)); outline.pts.push(mathTransform(to, transform));
outline.types.push(SW_CURVE_TYPE_POINT); outline.types.push(SW_CURVE_TYPE_POINT);
} }
static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform) static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
{ {
outline.pts.push(mathTransform(ctrl1, transform)); outline.pts.push(mathTransform(ctrl1, transform));
outline.types.push(SW_CURVE_TYPE_CUBIC); outline.types.push(SW_CURVE_TYPE_CUBIC);
@ -99,7 +99,7 @@ static bool _outlineClose(SwOutline& outline)
} }
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform) static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform)
{ {
Line cur = {dash.ptCur, *to}; Line cur = {dash.ptCur, *to};
auto len = lineLength(cur.pt1, cur.pt2); auto len = lineLength(cur.pt1, cur.pt2);
@ -160,7 +160,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
} }
static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform) static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
{ {
Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to}; Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
auto len = bezLength(cur); auto len = bezLength(cur);
@ -221,7 +221,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
} }
static void _dashClose(SwDashStroke& dash, const Matrix* transform) static void _dashClose(SwDashStroke& dash, const Matrix& transform)
{ {
_dashLineTo(dash, &dash.ptStart, transform); _dashLineTo(dash, &dash.ptStart, transform);
} }
@ -245,10 +245,10 @@ static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const
} }
static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length) static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length, float trimBegin, float trimEnd)
{ {
auto begin = length * rshape->stroke->trim.begin; auto begin = length * trimBegin;
auto end = length * rshape->stroke->trim.end; auto end = length * trimEnd;
//default //default
if (end > begin) { if (end > begin) {
@ -324,7 +324,7 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32
} }
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, bool trimmed, SwMpool* mpool, unsigned tid) static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& transform, bool trimmed, SwMpool* mpool, unsigned tid)
{ {
const PathCommand* cmds = rshape->path.cmds.data; const PathCommand* cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count; auto cmdCnt = rshape->path.cmds.count;
@ -341,6 +341,8 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
auto offset = 0.0f; auto offset = 0.0f;
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset); dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
auto simultaneous = rshape->stroke->trim.simultaneous; auto simultaneous = rshape->stroke->trim.simultaneous;
float trimBegin = 0.0f, trimEnd = 1.0f;
if (trimmed) rshape->stroke->strokeTrim(trimBegin, trimEnd);
if (dash.cnt == 0) { if (dash.cnt == 0) {
if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4); if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
@ -372,7 +374,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
//must begin with moveTo //must begin with moveTo
if (cmds[0] == PathCommand::MoveTo) { if (cmds[0] == PathCommand::MoveTo) {
if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous)); if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous), trimBegin, trimEnd);
_dashMoveTo(dash, offIdx, offset, pts); _dashMoveTo(dash, offIdx, offset, pts);
cmds++; cmds++;
pts++; pts++;
@ -387,7 +389,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
case PathCommand::MoveTo: { case PathCommand::MoveTo: {
if (trimmed) { if (trimmed) {
if (simultaneous) { if (simultaneous) {
_trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true)); _trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true), trimBegin, trimEnd);
_dashMoveTo(dash, offIdx, offset, pts); _dashMoveTo(dash, offIdx, offset, pts);
} else _dashMoveTo(dash, pts); } else _dashMoveTo(dash, pts);
} else _dashMoveTo(dash, offIdx, offset, pts); } else _dashMoveTo(dash, offIdx, offset, pts);
@ -436,7 +438,7 @@ static bool _axisAlignedRect(const SwOutline* outline)
} }
static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite) static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix& transform, SwMpool* mpool, unsigned tid, bool hasComposite)
{ {
const PathCommand* cmds = rshape->path.cmds.data; const PathCommand* cmds = rshape->path.cmds.data;
auto cmdCnt = rshape->path.cmds.count; auto cmdCnt = rshape->path.cmds.count;
@ -492,7 +494,7 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite) bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
{ {
if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false; if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false;
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false; if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
@ -575,7 +577,7 @@ void shapeDelStroke(SwShape* shape)
} }
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform) void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform)
{ {
if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke))); if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
auto stroke = shape->stroke; auto stroke = shape->stroke;
@ -586,7 +588,7 @@ void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* t
} }
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid) bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
{ {
SwOutline* shapeOutline = nullptr; SwOutline* shapeOutline = nullptr;
SwOutline* strokeOutline = nullptr; SwOutline* strokeOutline = nullptr;
@ -629,13 +631,13 @@ clear:
} }
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
{ {
return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable); return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
} }
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
{ {
return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable); return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
} }

View File

@ -805,15 +805,10 @@ void strokeFree(SwStroke* stroke)
} }
void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* transform) void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix& transform)
{ {
if (transform) { stroke->sx = sqrtf(powf(transform.e11, 2.0f) + powf(transform.e21, 2.0f));
stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f)); stroke->sy = sqrtf(powf(transform.e12, 2.0f) + powf(transform.e22, 2.0f));
stroke->sy = sqrtf(powf(transform->e12, 2.0f) + powf(transform->e22, 2.0f));
} else {
stroke->sx = stroke->sy = 1.0f;
}
stroke->width = HALF_STROKE(rshape->strokeWidth()); stroke->width = HALF_STROKE(rshape->strokeWidth());
stroke->cap = rshape->strokeCap(); stroke->cap = rshape->strokeCap();
stroke->miterlimit = static_cast<SwFixed>(rshape->strokeMiterlimit() * 65536.0f); stroke->miterlimit = static_cast<SwFixed>(rshape->strokeMiterlimit() * 65536.0f);

View File

@ -21,20 +21,21 @@
*/ */
#include "tvgIteratorAccessor.h" #include "tvgIteratorAccessor.h"
#include "tvgCompressor.h"
/************************************************************************/ /************************************************************************/
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
static bool accessChildren(Iterator* it, function<bool(const Paint* paint)> func) static bool accessChildren(Iterator* it, function<bool(const Paint* paint, void* data)> func, void* data)
{ {
while (auto child = it->next()) { while (auto child = it->next()) {
//Access the child //Access the child
if (!func(child)) return false; if (!func(child, data)) return false;
//Access the children of the child //Access the children of the child
if (auto it2 = IteratorAccessor::iterator(child)) { if (auto it2 = IteratorAccessor::iterator(child)) {
if (!accessChildren(it2, func)) { if (!accessChildren(it2, func, data)) {
delete(it2); delete(it2);
return false; return false;
} }
@ -44,26 +45,46 @@ static bool accessChildren(Iterator* it, function<bool(const Paint* paint)> func
return true; return true;
} }
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
unique_ptr<Picture> Accessor::set(unique_ptr<Picture> picture, function<bool(const Paint* paint)> func) noexcept TVG_DEPRECATED unique_ptr<Picture> Accessor::set(unique_ptr<Picture> picture, function<bool(const Paint* paint)> func) noexcept
{ {
auto p = picture.get(); auto backward = [](const tvg::Paint* paint, void* data) -> bool
if (!p || !func) return picture; {
auto func = reinterpret_cast<function<bool(const Paint* paint)>*>(data);
if (!(*func)(paint)) return false;
return true;
};
set(picture.get(), backward, reinterpret_cast<void*>(&func));
return picture;
}
Result Accessor::set(const Picture* picture, function<bool(const Paint* paint, void* data)> func, void* data) noexcept
{
if (!picture || !func) return Result::InvalidArguments;
//Use the Preorder Tree-Search //Use the Preorder Tree-Search
//Root //Root
if (!func(p)) return picture; if (!func(picture, data)) return Result::Success;
//Children //Children
if (auto it = IteratorAccessor::iterator(p)) { if (auto it = IteratorAccessor::iterator(picture)) {
accessChildren(it, func); accessChildren(it, func, data);
delete(it); delete(it);
} }
return picture; return Result::Success;
}
uint32_t Accessor::id(const char* name) noexcept
{
return djb2Encode(name);
} }

View File

@ -95,7 +95,7 @@ float Animation::duration() const noexcept
Result Animation::segment(float begin, float end) noexcept Result Animation::segment(float begin, float end) noexcept
{ {
if (begin < 0.0 || end > 1.0 || begin >= end) return Result::InvalidArguments; if (begin < 0.0 || end > 1.0 || begin > end) return Result::InvalidArguments;
auto loader = pImpl->picture->pImpl->loader; auto loader = pImpl->picture->pImpl->loader;
if (!loader) return Result::InsufficientCondition; if (!loader) return Result::InsufficientCondition;

View File

@ -93,11 +93,13 @@ struct Canvas::Impl
auto flag = RenderUpdateFlag::None; auto flag = RenderUpdateFlag::None;
if (status == Status::Damanged || force) flag = RenderUpdateFlag::All; if (status == Status::Damanged || force) flag = RenderUpdateFlag::All;
auto m = Matrix{1, 0, 0, 0, 1, 0, 0, 0, 1};
if (paint) { if (paint) {
paint->pImpl->update(renderer, nullptr, clips, 255, flag); paint->pImpl->update(renderer, m, clips, 255, flag);
} else { } else {
for (auto paint : paints) { for (auto paint : paints) {
paint->pImpl->update(renderer, nullptr, clips, 255, flag); paint->pImpl->update(renderer, m, clips, 255, flag);
} }
} }
status = Status::Updating; status = Status::Updating;

View File

@ -54,36 +54,30 @@ static constexpr bool operator &(CanvasEngine a, CanvasEngine b)
return int(a) & int(b); return int(a) & int(b);
} }
static bool _buildVersionInfo() static bool _buildVersionInfo(uint32_t* major, uint32_t* minor, uint32_t* micro)
{ {
auto SRC = THORVG_VERSION_STRING; //ex) 0.3.99 auto VER = THORVG_VERSION_STRING;
auto p = SRC; auto p = VER;
const char* x; const char* x;
char major[3]; if (!(x = strchr(p, '.'))) return false;
x = strchr(p, '.'); uint32_t majorVal = atoi(p);
if (!x) return false;
memcpy(major, p, x - p);
major[x - p] = '\0';
p = x + 1; p = x + 1;
char minor[3]; if (!(x = strchr(p, '.'))) return false;
x = strchr(p, '.'); uint32_t minorVal = atoi(p);
if (!x) return false;
memcpy(minor, p, x - p);
minor[x - p] = '\0';
p = x + 1; p = x + 1;
char micro[3]; uint32_t microVal = atoi(p);
x = SRC + strlen(THORVG_VERSION_STRING);
memcpy(micro, p, x - p);
micro[x - p] = '\0';
char sum[7]; char sum[7];
snprintf(sum, sizeof(sum), "%s%s%s", major, minor, micro); snprintf(sum, sizeof(sum), "%d%02d%02d", majorVal, minorVal, microVal);
_version = atoi(sum); _version = atoi(sum);
if (major) *major = majorVal;
if (minor) *minor = minorVal;
if (micro) *micro = microVal;
return true; return true;
} }
@ -122,7 +116,7 @@ Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept
if (_initCnt++ > 0) return Result::Success; if (_initCnt++ > 0) return Result::Success;
if (!_buildVersionInfo()) return Result::Unknown; if (!_buildVersionInfo(nullptr, nullptr, nullptr)) return Result::Unknown;
if (!LoaderMgr::init()) return Result::Unknown; if (!LoaderMgr::init()) return Result::Unknown;
@ -172,8 +166,14 @@ Result Initializer::term(CanvasEngine engine) noexcept
} }
const char* Initializer::version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept
{
if ((!major && ! minor && !micro) || _buildVersionInfo(major, minor, micro)) return THORVG_VERSION_STRING;
return nullptr;
}
uint16_t THORVG_VERSION_NUMBER() uint16_t THORVG_VERSION_NUMBER()
{ {
return _version; return _version;
} }

View File

@ -41,7 +41,7 @@
} }
static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& before) static Result _clipRect(RenderMethod* renderer, const Point* pts, const Matrix& pm, const Matrix& rm, RenderRegion& before)
{ {
//sorting //sorting
Point tmp[4]; Point tmp[4];
@ -50,8 +50,8 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTr
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
tmp[i] = pts[i]; tmp[i] = pts[i];
if (rTransform) tmp[i] *= rTransform->m; tmp[i] *= rm;
if (pTransform) tmp[i] *= pTransform->m; tmp[i] *= pm;
if (tmp[i].x < min.x) min.x = tmp[i].x; if (tmp[i].x < min.x) min.x = tmp[i].x;
if (tmp[i].x > max.x) max.x = tmp[i].x; if (tmp[i].x > max.x) max.x = tmp[i].x;
if (tmp[i].y < min.y) min.y = tmp[i].y; if (tmp[i].y < min.y) min.y = tmp[i].y;
@ -73,7 +73,7 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTr
} }
static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& before) static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Matrix& pm, RenderRegion& before)
{ {
/* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */ /* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */
auto shape = static_cast<Shape*>(cmpTarget); auto shape = static_cast<Shape*>(cmpTarget);
@ -84,18 +84,17 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren
//nothing to clip //nothing to clip
if (ptsCnt == 0) return Result::InvalidArguments; if (ptsCnt == 0) return Result::InvalidArguments;
if (ptsCnt != 4) return Result::InsufficientCondition; if (ptsCnt != 4) return Result::InsufficientCondition;
if (rTransform && (cmpTarget->pImpl->renderFlag & RenderUpdateFlag::Transform)) rTransform->update(); auto& rm = P(cmpTarget)->transform();
//No rotation and no skewing, still can try out clipping the rect region. //No rotation and no skewing, still can try out clipping the rect region.
auto tryClip = false; auto tryClip = false;
if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) tryClip = true; if ((!mathRightAngle(pm) || mathSkewed(pm))) tryClip = true;
if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) tryClip = true; if ((!mathRightAngle(rm) || mathSkewed(rm))) tryClip = true;
if (tryClip) return _clipRect(renderer, pts, pTransform, rTransform, before); if (tryClip) return _clipRect(renderer, pts, pm, rm, before);
//Perpendicular Rectangle? //Perpendicular Rectangle?
auto pt1 = pts + 0; auto pt1 = pts + 0;
@ -110,16 +109,10 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren
auto v1 = *pt1; auto v1 = *pt1;
auto v2 = *pt3; auto v2 = *pt3;
v1 *= rm;
if (rTransform) { v2 *= rm;
v1 *= rTransform->m; v1 *= pm;
v2 *= rTransform->m; v2 *= pm;
}
if (pTransform) {
v1 *= pTransform->m;
v2 *= pTransform->m;
}
//sorting //sorting
if (v1.x > v2.x) std::swap(v1.x, v2.x); if (v1.x > v2.x) std::swap(v1.x, v2.x);
@ -158,17 +151,15 @@ Iterator* Paint::Impl::iterator()
} }
Paint* Paint::Impl::duplicate() Paint* Paint::Impl::duplicate(Paint* ret)
{ {
Paint* ret; if (ret) ret->composite(nullptr, CompositeMethod::None);
PAINT_METHOD(ret, duplicate());
PAINT_METHOD(ret, duplicate(ret));
//duplicate Transform //duplicate Transform
if (rTransform) { ret->pImpl->tr = tr;
ret->pImpl->rTransform = new RenderTransform();
*ret->pImpl->rTransform = *rTransform;
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform; ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
}
ret->pImpl->opacity = opacity; ret->pImpl->opacity = opacity;
@ -180,14 +171,9 @@ Paint* Paint::Impl::duplicate()
bool Paint::Impl::rotate(float degree) bool Paint::Impl::rotate(float degree)
{ {
if (rTransform) { if (tr.overriding) return false;
if (rTransform->overriding) return false; if (mathEqual(degree, tr.degree)) return true;
if (mathEqual(degree, rTransform->degree)) return true; tr.degree = degree;
} else {
if (mathZero(degree)) return true;
rTransform = new RenderTransform();
}
rTransform->degree = degree;
renderFlag |= RenderUpdateFlag::Transform; renderFlag |= RenderUpdateFlag::Transform;
return true; return true;
@ -196,14 +182,9 @@ bool Paint::Impl::rotate(float degree)
bool Paint::Impl::scale(float factor) bool Paint::Impl::scale(float factor)
{ {
if (rTransform) { if (tr.overriding) return false;
if (rTransform->overriding) return false; if (mathEqual(factor, tr.scale)) return true;
if (mathEqual(factor, rTransform->scale)) return true; tr.scale = factor;
} else {
if (mathEqual(factor, 1.0f)) return true;
rTransform = new RenderTransform();
}
rTransform->scale = factor;
renderFlag |= RenderUpdateFlag::Transform; renderFlag |= RenderUpdateFlag::Transform;
return true; return true;
@ -212,15 +193,10 @@ bool Paint::Impl::scale(float factor)
bool Paint::Impl::translate(float x, float y) bool Paint::Impl::translate(float x, float y)
{ {
if (rTransform) { if (tr.overriding) return false;
if (rTransform->overriding) return false; if (mathEqual(x, tr.m.e13) && mathEqual(y, tr.m.e23)) return true;
if (mathEqual(x, rTransform->m.e13) && mathEqual(y, rTransform->m.e23)) return true; tr.m.e13 = x;
} else { tr.m.e23 = y;
if (mathZero(x) && mathZero(y)) return true;
rTransform = new RenderTransform();
}
rTransform->m.e13 = x;
rTransform->m.e23 = y;
renderFlag |= RenderUpdateFlag::Transform; renderFlag |= RenderUpdateFlag::Transform;
return true; return true;
@ -229,6 +205,8 @@ bool Paint::Impl::translate(float x, float y)
bool Paint::Impl::render(RenderMethod* renderer) bool Paint::Impl::render(RenderMethod* renderer)
{ {
if (opacity == 0) return true;
Compositor* cmp = nullptr; Compositor* cmp = nullptr;
/* Note: only ClipPath is processed in update() step. /* Note: only ClipPath is processed in update() step.
@ -258,7 +236,7 @@ bool Paint::Impl::render(RenderMethod* renderer)
} }
RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{ {
if (this->renderer != renderer) { if (this->renderer != renderer) {
if (this->renderer) TVGERR("RENDERER", "paint's renderer has been changed!"); if (this->renderer) TVGERR("RENDERER", "paint's renderer has been changed!");
@ -266,7 +244,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
this->renderer = renderer; this->renderer = renderer;
} }
if (renderFlag & RenderUpdateFlag::Transform) rTransform->update(); if (renderFlag & RenderUpdateFlag::Transform) tr.update();
/* 1. Composition Pre Processing */ /* 1. Composition Pre Processing */
RenderData trd = nullptr; //composite target render data RenderData trd = nullptr; //composite target render data
@ -277,7 +255,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
if (compData) { if (compData) {
auto target = compData->target; auto target = compData->target;
auto method = compData->method; auto method = compData->method;
target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset P(target)->ctxFlag &= ~ContextFlag::FastTrack; //reset
/* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle, /* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle,
we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */ we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */
@ -296,14 +274,14 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
} }
if (tryFastTrack) { if (tryFastTrack) {
viewport = renderer->viewport(); viewport = renderer->viewport();
if ((compFastTrack = _compFastTrack(renderer, target, pTransform, target->pImpl->rTransform, viewport)) == Result::Success) { if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) {
target->pImpl->ctxFlag |= ContextFlag::FastTrack; P(target)->ctxFlag |= ContextFlag::FastTrack;
} }
} }
} }
if (compFastTrack == Result::InsufficientCondition) { if (compFastTrack == Result::InsufficientCondition) {
childClipper = compData->method == CompositeMethod::ClipPath ? true : false; childClipper = compData->method == CompositeMethod::ClipPath ? true : false;
trd = target->pImpl->update(renderer, pTransform, clips, 255, pFlag, childClipper); trd = P(target)->update(renderer, pm, clips, 255, pFlag, childClipper);
if (childClipper) clips.push(trd); if (childClipper) clips.push(trd);
} }
} }
@ -314,8 +292,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
opacity = MULTIPLY(opacity, this->opacity); opacity = MULTIPLY(opacity, this->opacity);
RenderData rd = nullptr; RenderData rd = nullptr;
RenderTransform outTransform(pTransform, rTransform);
PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper)); tr.cm = pm * tr.m;
PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
/* 3. Composition Post Processing */ /* 3. Composition Post Processing */
if (compFastTrack == Result::Success) renderer->viewport(viewport); if (compFastTrack == Result::Success) renderer->viewport(viewport);
@ -325,13 +304,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) bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin)
{ {
Matrix* m = nullptr;
bool ret; bool ret;
const auto& m = this->transform(origin);
//Case: No transformed, quick return! //Case: No transformed, quick return!
if (!transformed || !(m = this->transform())) { if (!transformed || mathIdentity(&m)) {
PAINT_METHOD(ret, bounds(x, y, w, h, stroking)); PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
return ret; return ret;
} }
@ -355,7 +334,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
//Compute the AABB after transformation //Compute the AABB after transformation
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
pt[i] *= *m; pt[i] *= m;
if (pt[i].x < x1) x1 = pt[i].x; if (pt[i].x < x1) x1 = pt[i].x;
if (pt[i].x > x2) x2 = pt[i].x; if (pt[i].x > x2) x2 = pt[i].x;
@ -372,6 +351,26 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
} }
void Paint::Impl::reset()
{
if (compData) {
if (P(compData->target)->unref() == 0) delete(compData->target);
free(compData);
compData = nullptr;
}
mathIdentity(&tr.m);
tr.degree = 0.0f;
tr.scale = 1.0f;
tr.overriding = false;
blendMethod = BlendMethod::Normal;
renderFlag = RenderUpdateFlag::None;
ctxFlag = ContextFlag::Invalid;
opacity = 255;
paint->id = 0;
}
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
@ -417,9 +416,7 @@ Result Paint::transform(const Matrix& m) noexcept
Matrix Paint::transform() noexcept Matrix Paint::transform() noexcept
{ {
auto pTransform = pImpl->transform(); return pImpl->transform();
if (pTransform) return *pTransform;
return {1, 0, 0, 0, 1, 0, 0, 0, 1};
} }
@ -429,9 +426,9 @@ TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) cons
} }
Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept Result Paint::bounds(float* x, float* y, float* w, float* h, bool transformed) const noexcept
{ {
if (pImpl->bounds(x, y, w, h, transform, true)) return Result::Success; if (pImpl->bounds(x, y, w, h, transformed, true, transformed)) return Result::Success;
return Result::InsufficientCondition; return Result::InsufficientCondition;
} }
@ -444,6 +441,11 @@ Paint* Paint::duplicate() const noexcept
Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
{ {
if (method == CompositeMethod::ClipPath && target && target->identifier() != TVG_CLASS_ID_SHAPE) {
TVGERR("RENDERER", "ClipPath only allows the Shape!");
return Result::NonSupport;
}
auto p = target.release(); auto p = target.release();
if (pImpl->composite(this, p, method)) return Result::Success; if (pImpl->composite(this, p, method)) return Result::Success;
delete(p); delete(p);
@ -486,7 +488,7 @@ uint32_t Paint::identifier() const noexcept
} }
Result Paint::blend(BlendMethod method) const noexcept Result Paint::blend(BlendMethod method) noexcept
{ {
if (pImpl->blendMethod != method) { if (pImpl->blendMethod != method) {
pImpl->blendMethod = method; pImpl->blendMethod = method;

View File

@ -48,17 +48,40 @@ namespace tvg
struct Paint::Impl struct Paint::Impl
{ {
Paint* paint = nullptr; Paint* paint = nullptr;
RenderTransform* rTransform = nullptr;
Composite* compData = nullptr; Composite* compData = nullptr;
RenderMethod* renderer = nullptr; RenderMethod* renderer = nullptr;
BlendMethod blendMethod = BlendMethod::Normal; //uint8_t struct {
uint8_t renderFlag = RenderUpdateFlag::None; Matrix m; //input matrix
uint8_t ctxFlag = ContextFlag::Invalid; Matrix cm; //multipled parents matrix
uint8_t id; float degree; //rotation degree
uint8_t opacity = 255; float scale; //scale factor
uint8_t refCnt = 0; //reference count bool overriding; //user transform?
Impl(Paint* pnt) : paint(pnt) {} void update()
{
if (overriding) return;
m.e11 = 1.0f;
m.e12 = 0.0f;
m.e21 = 0.0f;
m.e22 = 1.0f;
m.e31 = 0.0f;
m.e32 = 0.0f;
m.e33 = 1.0f;
mathScale(&m, scale, scale);
mathRotate(&m, degree);
}
} tr;
BlendMethod blendMethod;
uint8_t renderFlag;
uint8_t ctxFlag;
uint8_t opacity;
uint8_t refCnt = 0; //reference count
uint8_t id; //TODO: deprecated, remove it
Impl(Paint* pnt) : paint(pnt)
{
reset();
}
~Impl() ~Impl()
{ {
@ -66,7 +89,6 @@ namespace tvg
if (P(compData->target)->unref() == 0) delete(compData->target); if (P(compData->target)->unref() == 0) delete(compData->target);
free(compData); free(compData);
} }
delete(rTransform);
if (renderer && (renderer->unref() == 0)) delete(renderer); if (renderer && (renderer->unref() == 0)) delete(renderer);
} }
@ -84,23 +106,19 @@ namespace tvg
bool transform(const Matrix& m) bool transform(const Matrix& m)
{ {
if (!rTransform) { tr.m = m;
if (mathIdentity(&m)) return true; tr.overriding = true;
rTransform = new RenderTransform();
}
rTransform->override(m);
renderFlag |= RenderUpdateFlag::Transform; renderFlag |= RenderUpdateFlag::Transform;
return true; return true;
} }
Matrix* transform() Matrix& transform(bool origin = false)
{ {
if (rTransform) { //update transform
if (renderFlag & RenderUpdateFlag::Transform) rTransform->update(); if (renderFlag & RenderUpdateFlag::Transform) tr.update();
return &rTransform->m; if (origin) return tr.cm;
} return tr.m;
return nullptr;
} }
bool composite(Paint* source, Paint* target, CompositeMethod method) bool composite(Paint* source, Paint* target, CompositeMethod method)
@ -135,10 +153,11 @@ namespace tvg
bool rotate(float degree); bool rotate(float degree);
bool scale(float factor); bool scale(float factor);
bool translate(float x, float y); bool translate(float x, float y);
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking); bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin = false);
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); RenderData update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
bool render(RenderMethod* renderer); bool render(RenderMethod* renderer);
Paint* duplicate(); Paint* duplicate(Paint* ret = nullptr);
void reset();
}; };
} }

View File

@ -104,21 +104,6 @@ RenderRegion Picture::Impl::bounds(RenderMethod* renderer)
} }
RenderTransform Picture::Impl::resizeTransform(const RenderTransform* pTransform)
{
//Overriding Transformation by the desired image size
auto sx = w / loader->w;
auto sy = h / loader->h;
auto scale = sx < sy ? sx : sy;
RenderTransform tmp;
tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1};
if (!pTransform) return tmp;
else return RenderTransform(pTransform, &tmp);
}
Result Picture::Impl::load(ImageLoader* loader) Result Picture::Impl::load(ImageLoader* loader)
{ {
//Same resource has been loaded. //Same resource has been loaded.
@ -215,18 +200,24 @@ Result Picture::size(float* w, float* h) const noexcept
} }
Result Picture::mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept const Paint* Picture::paint(uint32_t id) noexcept
{ {
if (!triangles && triangleCnt > 0) return Result::InvalidArguments; struct Value
if (triangles && triangleCnt == 0) return Result::InvalidArguments;
pImpl->mesh(triangles, triangleCnt);
return Result::Success;
}
uint32_t Picture::mesh(const Polygon** triangles) const noexcept
{ {
if (triangles) *triangles = pImpl->rm.triangles; uint32_t id;
return pImpl->rm.triangleCnt; const Paint* ret;
} value = {id, nullptr};
auto cb = [](const tvg::Paint* paint, void* data) -> bool
{
auto p = static_cast<Value*>(data);
if (p->id == paint->id) {
p->ret = paint;
return false;
}
return true;
};
tvg::Accessor::gen()->set(this, cb, &value);
return value.ret;
} }

View File

@ -63,12 +63,10 @@ struct Picture::Impl
Surface* surface = nullptr; //bitmap picture uses Surface* surface = nullptr; //bitmap picture uses
RenderData rd = nullptr; //engine data RenderData rd = nullptr; //engine data
float w = 0, h = 0; float w = 0, h = 0;
RenderMesh rm; //mesh data
Picture* picture = nullptr; Picture* picture = nullptr;
bool resizing = false; bool resizing = false;
bool needComp = false; //need composition bool needComp = false; //need composition
RenderTransform resizeTransform(const RenderTransform* pTransform);
bool needComposition(uint8_t opacity); bool needComposition(uint8_t opacity);
bool render(RenderMethod* renderer); bool render(RenderMethod* renderer);
bool size(float w, float h); bool size(float w, float h);
@ -90,58 +88,37 @@ struct Picture::Impl
delete(paint); delete(paint);
} }
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper)
{ {
auto flag = static_cast<RenderUpdateFlag>(pFlag | load()); auto flag = static_cast<RenderUpdateFlag>(pFlag | load());
if (surface) { if (surface) {
if (flag == RenderUpdateFlag::None) return rd; if (flag == RenderUpdateFlag::None) return rd;
auto transform = resizeTransform(pTransform);
rd = renderer->prepare(surface, &rm, rd, &transform, clips, opacity, flag); //Overriding Transformation by the desired image size
auto sx = w / loader->w;
auto sy = h / loader->h;
auto scale = sx < sy ? sx : sy;
auto m = transform * Matrix{scale, 0, 0, 0, scale, 0, 0, 0, 1};
rd = renderer->prepare(surface, rd, m, clips, opacity, flag);
} else if (paint) { } else if (paint) {
if (resizing) { if (resizing) {
loader->resize(paint, w, h); loader->resize(paint, w, h);
resizing = false; resizing = false;
} }
needComp = needComposition(opacity) ? true : false; needComp = needComposition(opacity) ? true : false;
rd = paint->pImpl->update(renderer, pTransform, clips, opacity, flag, clipper); rd = paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
} }
return rd; return rd;
} }
bool bounds(float* x, float* y, float* w, float* h, bool stroking) bool bounds(float* x, float* y, float* w, float* h, bool stroking)
{ {
if (rm.triangleCnt > 0) {
auto triangles = rm.triangles;
auto min = triangles[0].vertex[0].pt;
auto max = triangles[0].vertex[0].pt;
for (uint32_t i = 0; i < rm.triangleCnt; ++i) {
if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
}
if (x) *x = min.x;
if (y) *y = min.y;
if (w) *w = max.x - min.x;
if (h) *h = max.y - min.y;
} else {
if (x) *x = 0; if (x) *x = 0;
if (y) *y = 0; if (y) *y = 0;
if (w) *w = this->w; if (w) *w = this->w;
if (h) *h = this->h; if (h) *h = this->h;
}
return true; return true;
} }
@ -176,32 +153,21 @@ struct Picture::Impl
return load(loader); return load(loader);
} }
void mesh(const Polygon* triangles, const uint32_t triangleCnt) Paint* duplicate(Paint* ret)
{ {
if (triangles && triangleCnt > 0) { if (ret) TVGERR("RENDERER", "TODO: duplicate()");
this->rm.triangleCnt = triangleCnt;
this->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt);
memcpy(this->rm.triangles, triangles, sizeof(Polygon) * triangleCnt);
} else {
free(this->rm.triangles);
this->rm.triangles = nullptr;
this->rm.triangleCnt = 0;
}
}
Paint* duplicate()
{
load(); load();
auto ret = Picture::gen().release(); auto picture = Picture::gen().release();
auto dup = ret->pImpl; auto dup = picture->pImpl;
if (paint) dup->paint = paint->duplicate(); if (paint) dup->paint = paint->duplicate();
if (loader) { if (loader) {
dup->loader = loader; dup->loader = loader;
++dup->loader->sharing; ++dup->loader->sharing;
PP(ret)->renderFlag |= RenderUpdateFlag::Image; PP(picture)->renderFlag |= RenderUpdateFlag::Image;
} }
dup->surface = surface; dup->surface = surface;
@ -209,13 +175,7 @@ struct Picture::Impl
dup->h = h; dup->h = h;
dup->resizing = resizing; dup->resizing = resizing;
if (rm.triangleCnt > 0) { return picture;
dup->rm.triangleCnt = rm.triangleCnt;
dup->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * rm.triangleCnt);
memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
}
return ret;
} }
Iterator* iterator() Iterator* iterator()

View File

@ -46,41 +46,6 @@ uint32_t RenderMethod::unref()
} }
void RenderTransform::override(const Matrix& m)
{
this->m = m;
overriding = true;
}
void RenderTransform::update()
{
if (overriding) return;
m.e11 = 1.0f;
m.e12 = 0.0f;
m.e21 = 0.0f;
m.e22 = 1.0f;
m.e31 = 0.0f;
m.e32 = 0.0f;
m.e33 = 1.0f;
mathScale(&m, scale, scale);
mathRotate(&m, degree);
}
RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
{
if (lhs && rhs) m = lhs->m * rhs->m;
else if (lhs) m = lhs->m;
else if (rhs) m = rhs->m;
else mathIdentity(&m);
}
void RenderRegion::intersect(const RenderRegion& rhs) void RenderRegion::intersect(const RenderRegion& rhs)
{ {
auto x1 = x + w; auto x1 = x + w;

View File

@ -23,6 +23,7 @@
#ifndef _TVG_RENDER_H_ #ifndef _TVG_RENDER_H_
#define _TVG_RENDER_H_ #define _TVG_RENDER_H_
#include <math.h>
#include "tvgCommon.h" #include "tvgCommon.h"
#include "tvgArray.h" #include "tvgArray.h"
#include "tvgLock.h" #include "tvgLock.h"
@ -85,15 +86,15 @@ struct Compositor
uint8_t opacity; uint8_t opacity;
}; };
struct RenderMesh struct Vertex
{ {
Polygon* triangles = nullptr; Point pt;
uint32_t triangleCnt = 0; Point uv;
};
~RenderMesh() struct Polygon
{ {
free(triangles); Vertex vertex[3];
}
}; };
struct RenderRegion struct RenderRegion
@ -110,24 +111,6 @@ struct RenderRegion
} }
}; };
struct RenderTransform
{
Matrix m;
float degree = 0.0f; //rotation degree
float scale = 1.0f; //scale factor
bool overriding = false; //user transform?
void update();
void override(const Matrix& m);
RenderTransform()
{
m.e13 = m.e23 = 0.0f;
}
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
};
struct RenderStroke struct RenderStroke
{ {
float width = 0.0f; float width = 0.0f;
@ -147,6 +130,57 @@ struct RenderStroke
bool simultaneous = true; bool simultaneous = true;
} trim; } trim;
void operator=(const RenderStroke& rhs)
{
width = rhs.width;
memcpy(color, rhs.color, sizeof(color));
delete(fill);
if (rhs.fill) fill = rhs.fill->duplicate();
else fill = nullptr;
free(dashPattern);
if (rhs.dashCnt > 0) {
dashPattern = static_cast<float*>(malloc(sizeof(float) * rhs.dashCnt));
memcpy(dashPattern, rhs.dashPattern, sizeof(float) * rhs.dashCnt);
} else {
dashPattern = nullptr;
}
dashCnt = rhs.dashCnt;
miterlimit = rhs.miterlimit;
cap = rhs.cap;
join = rhs.join;
strokeFirst = rhs.strokeFirst;
trim = rhs.trim;
}
bool strokeTrim(float& begin, float& end) const
{
begin = trim.begin;
end = trim.end;
if (fabsf(end - begin) >= 1.0f) {
begin = 0.0f;
end = 1.0f;
return false;
}
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)) std::swap(begin, end);
return true;
}
~RenderStroke() ~RenderStroke()
{ {
free(dashPattern); free(dashPattern);
@ -191,7 +225,7 @@ struct RenderShape
{ {
if (!stroke) return false; if (!stroke) return false;
if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false; if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false; if (fabsf(stroke->trim.end - stroke->trim.begin) >= 1.0f) return false;
return true; return true;
} }
@ -252,9 +286,8 @@ public:
uint32_t unref(); uint32_t unref();
virtual ~RenderMethod() {} virtual ~RenderMethod() {}
virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0; virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; virtual RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
virtual bool preRender() = 0; virtual bool preRender() = 0;
virtual bool renderShape(RenderData data) = 0; virtual bool renderShape(RenderData data) = 0;
virtual bool renderImage(RenderData data) = 0; virtual bool renderImage(RenderData data) = 0;
@ -288,6 +321,8 @@ static inline bool MASK_REGION_MERGING(CompositeMethod method)
//these might expand the rendering region //these might expand the rendering region
case CompositeMethod::AddMask: case CompositeMethod::AddMask:
case CompositeMethod::DifferenceMask: case CompositeMethod::DifferenceMask:
case CompositeMethod::LightenMask:
case CompositeMethod::DarkenMask:
return true; return true;
default: default:
TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method); TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method);
@ -321,6 +356,8 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod* renderer, Composi
case CompositeMethod::DifferenceMask: case CompositeMethod::DifferenceMask:
case CompositeMethod::SubtractMask: case CompositeMethod::SubtractMask:
case CompositeMethod::IntersectMask: case CompositeMethod::IntersectMask:
case CompositeMethod::LightenMask:
case CompositeMethod::DarkenMask:
return ColorSpace::Grayscale8; return ColorSpace::Grayscale8;
//TODO: Optimize Luma/InvLuma colorspace to Grayscale8 //TODO: Optimize Luma/InvLuma colorspace to Grayscale8
case CompositeMethod::LumaMask: case CompositeMethod::LumaMask:

View File

@ -101,7 +101,7 @@ struct Scene::Impl
return true; return true;
} }
RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
{ {
if ((needComp = needComposition(opacity))) { if ((needComp = needComposition(opacity))) {
/* Overriding opacity value. If this scene is half-translucent, /* Overriding opacity value. If this scene is half-translucent,
@ -109,21 +109,11 @@ struct Scene::Impl
this->opacity = opacity; this->opacity = opacity;
opacity = 255; opacity = 255;
} }
if (clipper) {
Array<RenderData> rds(paints.size());
for (auto paint : paints) {
rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true));
}
rd = renderer->prepare(rds, rd, transform, clips, opacity, flag);
return rd;
} else {
for (auto paint : paints) { for (auto paint : paints) {
paint->pImpl->update(renderer, transform, clips, opacity, flag, false); paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
} }
return nullptr; return nullptr;
} }
}
bool render(RenderMethod* renderer) bool render(RenderMethod* renderer)
{ {
@ -198,10 +188,12 @@ struct Scene::Impl
return true; return true;
} }
Paint* duplicate() Paint* duplicate(Paint* ret)
{ {
auto ret = Scene::gen().release(); if (ret) TVGERR("RENDERER", "TODO: duplicate()");
auto dup = ret->pImpl;
auto scene = Scene::gen().release();
auto dup = scene->pImpl;
for (auto paint : paints) { for (auto paint : paints) {
auto cdup = paint->duplicate(); auto cdup = paint->duplicate();
@ -209,7 +201,7 @@ struct Scene::Impl
dup->paints.push_back(cdup); dup->paints.push_back(cdup);
} }
return ret; return scene;
} }
void clear(bool free) void clear(bool free)

View File

@ -34,6 +34,7 @@ struct Shape::Impl
RenderData rd = nullptr; //engine data RenderData rd = nullptr; //engine data
Shape* shape; Shape* shape;
uint8_t flag = RenderUpdateFlag::None; uint8_t flag = RenderUpdateFlag::None;
uint8_t opacity; //for composition uint8_t opacity; //for composition
bool needComp = false; //composite or not bool needComp = false; //composite or not
@ -95,7 +96,7 @@ struct Shape::Impl
return true; return true;
} }
RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{ {
if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd; if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd;
@ -216,23 +217,6 @@ struct Shape::Impl
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) && if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
rs.stroke->trim.simultaneous == simultaneous) return; 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.begin = begin;
rs.stroke->trim.end = end; rs.stroke->trim.end = end;
rs.stroke->trim.simultaneous = simultaneous; rs.stroke->trim.simultaneous = simultaneous;
@ -359,47 +343,56 @@ struct Shape::Impl
this->flag |= flag; this->flag |= flag;
} }
Paint* duplicate() Paint* duplicate(Paint* ret)
{ {
auto ret = Shape::gen().release(); auto shape = static_cast<Shape*>(ret);
auto dup = ret->pImpl; if (shape) shape->reset();
else shape = Shape::gen().release();
auto dup = shape->pImpl;
delete(dup->rs.fill);
//Default Properties
dup->flag = RenderUpdateFlag::All;
dup->rs.rule = rs.rule; dup->rs.rule = rs.rule;
//Color //Color
memcpy(dup->rs.color, rs.color, sizeof(rs.color)); memcpy(dup->rs.color, rs.color, sizeof(rs.color));
dup->flag = RenderUpdateFlag::Color;
//Path //Path
if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) { dup->rs.path.cmds.push(rs.path.cmds);
dup->rs.path.cmds = rs.path.cmds; dup->rs.path.pts.push(rs.path.pts);
dup->rs.path.pts = rs.path.pts;
dup->flag |= RenderUpdateFlag::Path;
}
//Stroke //Stroke
if (rs.stroke) { if (rs.stroke) {
dup->rs.stroke = new RenderStroke(); if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke;
*dup->rs.stroke = *rs.stroke; *dup->rs.stroke = *rs.stroke;
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color)); } else {
if (rs.stroke->dashCnt > 0) { delete(dup->rs.stroke);
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt)); dup->rs.stroke = nullptr;
memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
}
if (rs.stroke->fill) {
dup->rs.stroke->fill = rs.stroke->fill->duplicate();
dup->flag |= RenderUpdateFlag::GradientStroke;
}
dup->flag |= RenderUpdateFlag::Stroke;
} }
//Fill //Fill
if (rs.fill) { if (rs.fill) dup->rs.fill = rs.fill->duplicate();
dup->rs.fill = rs.fill->duplicate(); else dup->rs.fill = nullptr;
dup->flag |= RenderUpdateFlag::Gradient;
return shape;
} }
return ret; void reset()
{
PP(shape)->reset();
rs.path.cmds.clear();
rs.path.pts.clear();
rs.color[3] = 0;
rs.rule = FillRule::Winding;
delete(rs.stroke);
rs.stroke = nullptr;
delete(rs.fill);
rs.fill = nullptr;
} }
Iterator* iterator() Iterator* iterator()

View File

@ -23,8 +23,6 @@
#ifndef _TVG_TASK_SCHEDULER_H_ #ifndef _TVG_TASK_SCHEDULER_H_
#define _TVG_TASK_SCHEDULER_H_ #define _TVG_TASK_SCHEDULER_H_
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>

View File

@ -35,7 +35,7 @@
/************************************************************************/ /************************************************************************/
Text::Text() : pImpl(new Impl) Text::Text() : pImpl(new Impl(this))
{ {
Paint::pImpl->id = TVG_CLASS_ID_TEXT; Paint::pImpl->id = TVG_CLASS_ID_TEXT;
} }
@ -95,20 +95,13 @@ Result Text::unload(const std::string& path) noexcept
Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept
{ {
if (!pImpl->paint) return Result::InsufficientCondition; return pImpl->shape->fill(r, g, b);
return pImpl->fill(r, g, b);
} }
Result Text::fill(unique_ptr<Fill> f) noexcept Result Text::fill(unique_ptr<Fill> f) noexcept
{ {
if (!pImpl->paint) return Result::InsufficientCondition; return pImpl->shape->fill(std::move(f));
auto p = f.release();
if (!p) return Result::MemoryCorruption;
return pImpl->fill(p);
} }

View File

@ -36,27 +36,22 @@
struct Text::Impl struct Text::Impl
{ {
FontLoader* loader = nullptr; FontLoader* loader = nullptr;
Shape* paint = nullptr; Text* paint;
Shape* shape;
char* utf8 = nullptr; char* utf8 = nullptr;
float fontSize; float fontSize;
bool italic = false; bool italic = false;
bool changed = false; bool changed = false;
Impl(Text* p) : paint(p), shape(Shape::gen().release())
{
}
~Impl() ~Impl()
{ {
free(utf8); free(utf8);
LoaderMgr::retrieve(loader); LoaderMgr::retrieve(loader);
delete(paint); delete(shape);
}
Result fill(uint8_t r, uint8_t g, uint8_t b)
{
return paint->fill(r, g, b);
}
Result fill(Fill* f)
{
return paint->fill(cast<Fill>(f));
} }
Result text(const char* utf8) Result text(const char* utf8)
@ -83,8 +78,6 @@ struct Text::Impl
} }
this->loader = static_cast<FontLoader*>(loader); this->loader = static_cast<FontLoader*>(loader);
if (!paint) paint = Shape::gen().release();
fontSize = size; fontSize = size;
if (style && strstr(style, "italic")) italic = true; if (style && strstr(style, "italic")) italic = true;
changed = true; changed = true;
@ -93,14 +86,12 @@ struct Text::Impl
RenderRegion bounds(RenderMethod* renderer) RenderRegion bounds(RenderMethod* renderer)
{ {
if (paint) return P(paint)->bounds(renderer); return P(shape)->bounds(renderer);
else return {0, 0, 0, 0};
} }
bool render(RenderMethod* renderer) bool render(RenderMethod* renderer)
{ {
if (paint) return PP(paint)->render(renderer); return PP(shape)->render(renderer);
return true;
} }
bool load() bool load()
@ -109,24 +100,20 @@ struct Text::Impl
//reload //reload
if (changed) { if (changed) {
loader->request(paint, utf8, italic); loader->request(shape, utf8, italic);
loader->read(); loader->read();
changed = false; changed = false;
} }
if (paint) { return loader->resize(shape, fontSize, fontSize);
loader->resize(paint, fontSize, fontSize);
return true;
}
return false;
} }
RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper)
{ {
if (!load()) return nullptr; if (!load()) return nullptr;
//transform the gradient coordinates based on the final scaled font. //transform the gradient coordinates based on the final scaled font.
if (P(paint)->flag & RenderUpdateFlag::Gradient) { auto fill = P(shape)->rs.fill;
auto fill = P(paint)->rs.fill; if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) {
auto scale = 1.0f / loader->scale; auto scale = 1.0f / loader->scale;
if (fill->identifier() == TVG_CLASS_ID_LINEAR) { if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
P(static_cast<LinearGradient*>(fill))->x1 *= scale; P(static_cast<LinearGradient*>(fill))->x1 *= scale;
@ -142,23 +129,25 @@ struct Text::Impl
P(static_cast<RadialGradient*>(fill))->fr *= scale; P(static_cast<RadialGradient*>(fill))->fr *= scale;
} }
} }
return PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper); return PP(shape)->update(renderer, transform, clips, opacity, pFlag, false);
} }
bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking) bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking)
{ {
if (!load() || !paint) return false; if (!load()) return false;
paint->bounds(x, y, w, h, true); PP(shape)->bounds(x, y, w, h, true, true, false);
return true; return true;
} }
Paint* duplicate() Paint* duplicate(Paint* ret)
{ {
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
load(); load();
auto ret = Text::gen().release(); auto text = Text::gen().release();
auto dup = ret->pImpl; auto dup = text->pImpl;
if (paint) dup->paint = static_cast<Shape*>(paint->duplicate()); P(shape)->duplicate(dup->shape);
if (loader) { if (loader) {
dup->loader = loader; dup->loader = loader;
@ -169,7 +158,7 @@ struct Text::Impl
dup->italic = italic; dup->italic = italic;
dup->fontSize = fontSize; dup->fontSize = fontSize;
return ret; return text;
} }
Iterator* iterator() Iterator* iterator()

View File

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