Merge pull request #96167 from akien-mga/thorvg-0.14.7

thorvg: Update to 0.14.7
This commit is contained in:
Rémi Verschelde 2024-08-27 22:27:52 +02:00
commit fe024443e3
No known key found for this signature in database
GPG Key ID: C3336907360768E1
37 changed files with 667 additions and 913 deletions

View File

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

View File

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

View File

@ -157,7 +157,7 @@ enum class FillRule
enum class CompositeMethod
{
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.
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
@ -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)
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)
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
*
@ -361,7 +335,7 @@ public:
*
* @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
@ -371,15 +345,16 @@ public:
/**
* @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] 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 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;
@ -411,9 +386,9 @@ public:
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
*/
@ -428,6 +403,15 @@ public:
*/
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);
};
@ -675,7 +659,8 @@ public:
* @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.
*
* @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;
@ -734,6 +719,8 @@ public:
* @param[in] radius The radius of the bounding circle.
*
* @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;
@ -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.
*
* 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] 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] 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;
@ -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
* 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] 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.
@ -1256,7 +1242,7 @@ public:
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
* 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] w The width 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.
*
* @note It expects premultiplied alpha data.
* @since 0.9
*/
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
* image data will be used as the texture.
* This function searches for a paint object within the Picture scene that matches the provided @p id.
*
* 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.
* mesh deformation. However, if required you should be able to render a vector image to a raster image and then apply a mesh.
* @return A pointer to the paint object that matches the given identifier, or @c nullptr if no matching paint object is found.
*
* @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh.
* @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.
* @see Accessor::id()
*
* @note Experimental API
*/
Result mesh(const Polygon* triangles, uint32_t triangleCnt) 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;
const Paint* paint(uint32_t id) noexcept;
/**
* @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] 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()
*
* @note Experimental API
@ -1469,8 +1433,6 @@ public:
*
* @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 Experimental API
*
@ -1781,6 +1743,19 @@ public:
*/
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);
};
@ -1879,7 +1854,7 @@ public:
* @retval Result::InsufficientCondition In case the animation is not loaded.
* @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.
* @see LottieAnimation::segment(const char* marker)
* @note Experimental API
@ -2030,17 +2005,36 @@ class TVG_API Accessor final
public:
~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.
*
* @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.
*
* @return Return the given @p picture instance.
* @param[in] data Data passed to the @p func as its argument.
*
* @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.

View File

@ -59,7 +59,7 @@ struct Array
data[count++] = element;
}
void push(Array<T>& rhs)
void push(const Array<T>& rhs)
{
if (rhs.count == 0) return;
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)
{
if (!str) return 0;
unsigned long hash = 5381;
int c;

View File

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

View File

@ -79,7 +79,7 @@ float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc
Bezier left;
bezSplitLeft(right, t, left);
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;
}
if (length < at) {

View File

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

View File

@ -78,17 +78,17 @@ bool mathIdentity(const Matrix* m);
Matrix operator*(const Matrix& lhs, const Matrix& rhs);
bool operator==(const Matrix& lhs, const Matrix& rhs);
static inline bool mathRightAngle(const Matrix* m)
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;
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)
{
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;
} 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;
const char *content, *hue, *saturation, *brightness;
content = str + 4;
content = _skipSpace(content, nullptr);
const char* content = _skipSpace(str + 4, nullptr);
const char* hue = nullptr;
if (_parseNumber(&content, &hue, &th) && hue) {
const char* saturation = nullptr;
th = float(uint32_t(th) % 360);
hue = _skipSpace(hue, nullptr);
hue = (char*)_skipComma(hue);
hue = _skipSpace(hue, nullptr);
if (_parseNumber(&hue, &saturation, &ts) && saturation && *saturation == '%') {
const char* brightness = nullptr;
ts /= 100.0f;
saturation = _skipSpace(saturation + 1, nullptr);
saturation = (char*)_skipComma(saturation);

View File

@ -134,7 +134,6 @@ struct SwFill
{
struct SwLinear {
float dx, dy;
float len;
float offset;
};
@ -154,6 +153,7 @@ struct SwFill
uint32_t* ctable;
FillSpread spread;
bool solid = false; //solid color fill with the last color from colorStops
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)
{
return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) +
((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff));
++a;
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)
@ -494,38 +494,39 @@ SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
SwFixed mathLength(const SwPoint& pt);
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
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 mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
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 shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
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);
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);
void shapeFree(SwShape* shape);
void shapeDelStroke(SwShape* shape);
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 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);
void shapeResetFill(SwShape* shape);
void shapeResetStrokeFill(SwShape* shape);
void shapeDelFill(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);
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
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);
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
void imageReset(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 fillFree(SwFill* fill);
@ -561,11 +562,11 @@ SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx);
void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
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 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 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);
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);

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;
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;
}
@ -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)
{
if (fill->solid) return true;
if (!fill->ctable) {
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
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;
if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
fill->linear.dx = x2 - x1;
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.dy /= fill->linear.len;
fill->linear.dx /= len;
fill->linear.dy /= len;
fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
auto gradTransform = linear->transform();
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (isTransformation) {
if (transform) gradTransform = *transform * gradTransform;
} else if (transform) {
gradTransform = *transform;
gradTransform = transform * gradTransform;
} else {
gradTransform = transform;
isTransformation = true;
}
@ -239,15 +246,13 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
auto dx = fill->linear.dx;
fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
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;
}
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 cy = P(radial)->cy;
@ -256,7 +261,10 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
auto fy = P(radial)->fy;
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.dx = cx - fx;
@ -289,12 +297,10 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
auto gradTransform = radial->transform();
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
if (transform) {
if (isTransformation) gradTransform = *transform * gradTransform;
else {
gradTransform = *transform;
isTransformation = true;
}
if (isTransformation) gradTransform = transform * gradTransform;
else {
gradTransform = transform;
isTransformation = true;
}
if (isTransformation) {
@ -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;
fill->spread = fdata->spread();
if (ctable) {
if (!_updateColorTable(fill, fdata, surface, opacity)) return false;
}
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) {
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->translucent = false;
fill->solid = false;
}

View File

@ -27,14 +27,14 @@
/* 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;
}
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);
auto outline = image->outline;
@ -45,48 +45,12 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
outline->closed.reserve(1);
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 h = static_cast<float>(image->h);
to[0] = {0, 0};
to[1] = {w, 0};
to[2] = {w, h};
to[3] = {0, h};
}
auto w = static_cast<float>(image->w);
auto h = static_cast<float>(image->h);
to[0] = {0, 0};
to[1] = {w, 0};
to[2] = {w, h};
to[3] = {0, h};
for (int i = 0; i < 4; i++) {
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 */
/************************************************************************/
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);
//Fast track: Non-transformed image but just shifted.
if (image->direct) {
image->ox = -static_cast<int32_t>(nearbyint(transform->e13));
image->oy = -static_cast<int32_t>(nearbyint(transform->e23));
image->ox = -static_cast<int32_t>(nearbyint(transform.e13));
image->oy = -static_cast<int32_t>(nearbyint(transform.e23));
//Figure out the scale factor by transform matrix
} else {
auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21));
auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12));
auto scaleX = sqrtf((transform.e11 * transform.e11) + (transform.e21 * transform.e21));
auto scaleY = sqrtf((transform.e22 * transform.e22) + (transform.e12 * transform.e12));
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;
}
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);
}

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)};
}

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)
{
//subtract & Intersect allows the direct composition
if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask) return true;
if (method == CompositeMethod::SubtractMask || method == CompositeMethod::IntersectMask || method == CompositeMethod::DarkenMask) return true;
return false;
}
@ -209,6 +220,8 @@ static inline SwMask _getMaskOp(CompositeMethod method)
case CompositeMethod::SubtractMask: return _opMaskSubtract;
case CompositeMethod::DifferenceMask: return _opMaskDifference;
case CompositeMethod::IntersectMask: return _opMaskIntersect;
case CompositeMethod::LightenMask: return _opMaskLighten;
case CompositeMethod::DarkenMask: return _opMaskDarken;
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)) {
TVGERR("SW_ENGINE", "Not supported scaled rle image!");
@ -841,9 +854,7 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
Matrix itransform;
if (transform) {
if (!mathInverse(transform, &itransform)) return false;
} else mathIdentity(&itransform);
if (!mathInverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
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;
if (transform) {
if (!mathInverse(transform, &itransform)) return false;
} else mathIdentity(&itransform);
if (!mathInverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity);
@ -1389,29 +1398,45 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
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);
for (auto y = region.min.y; y < region.max.y; ++y) {
auto dst = dbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
*dst = *src + ALPHA_BLEND(*dst, IA(*src));
//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) {
auto dst = dbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
*dst = *src + ALPHA_BLEND(*dst, IA(*src));
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
auto tmp = ALPHA_BLEND(*src, opacity);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
auto tmp = ALPHA_BLEND(*src, opacity);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
dbuffer += surface->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);
}
}
}
dbuffer += surface->stride;
sbuffer += image->stride;
}
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]
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
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)
{
if (fill->linear.len < FLOAT_EPSILON) return false;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRect<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 (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 (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);
@ -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 (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);
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
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);
else return _rasterImage(surface, image, transform, bbox, opacity);
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;
}
if (yStart >= regionBottom) return false;
if (yStart < regionTop) yStart = regionTop;
if (yEnd > regionBottom) yEnd = regionBottom;
return true;
return yEnd > yStart;
}
@ -868,10 +866,8 @@ 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)
{
if (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].length[eidx] = abs(x - x2);
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]; \
} while (0)
struct Point
{
int32_t x, y;
};
int32_t y = 0;
SwPoint pEdge = {-1, -1}; //previous edge point
SwPoint edgeDiff = {0, 0}; //temporary used for point distance
Point pEdge = {-1, -1}; //previous edge point
Point edgeDiff = {0, 0}; //temporary used for point distance
/* store bigger to tx[0] between prev and current edge's x positions. */
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)
{
auto end = surface->buf32 + surface->h * surface->stride;
auto y = aaSpans->yStart;
uint32_t pixel;
uint32_t* dst;
@ -1044,8 +1046,13 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
dst = surface->buf32 + (offset + line->x[0]);
if (line->x[0] > 1) pixel = *(dst - 1);
else pixel = *dst;
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]) {
*dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos);
++dst;
@ -1053,17 +1060,21 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
}
//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);
else pixel = *dst;
pos = line->length[1];
pos = width;
while ((int32_t)(width - line->length[1]) < pos) {
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * (line->length[1] - (width - pos))));
//exceptional handling. out of memory bound.
if (dst - pos < surface->buf32) --pos;
while (pos > 0) {
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos));
--dst;
--pos;
}
}
}
y++;
}
@ -1084,7 +1095,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
| / |
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)) {
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?
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.
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;
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 > ye) ye = vertices[i].pt.y;
}
@ -1135,68 +1146,3 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
#endif
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;
SwMpool* mpool = nullptr;
SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
Matrix* transform = nullptr;
Matrix transform;
Array<RenderData> clips;
RenderUpdateFlag flags = RenderUpdateFlag::None;
uint8_t opacity;
@ -68,10 +68,7 @@ struct SwTask : Task
virtual bool clip(SwRleData* target) = 0;
virtual SwRleData* rle() = 0;
virtual ~SwTask()
{
free(transform);
}
virtual ~SwTask() {}
};
@ -100,8 +97,7 @@ struct SwShapeTask : SwTask
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 (transform) return (width * sqrt(transform->e11 * transform->e11 + transform->e12 * transform->e12));
else return width;
return (width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12));
}
@ -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
{
SwImage image;
Surface* source; //Image source
const RenderMesh* mesh = nullptr; //Should be valid ptr in action
bool clip(SwRleData* target) override
{
@ -293,10 +235,9 @@ struct SwImageTask : SwTask
imageReset(&image);
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 (mesh->triangleCnt == 0 && clips.count > 0) {
if (clips.count > 0) {
if (!imageGenRle(&image, bbox, false)) goto end;
if (image.rle) {
//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;
if (auto fill = task->rshape->fill) {
rasterGradientShape(surface, &task->shape, fill->identifier());
rasterGradientShape(surface, &task->shape, fill, opacity);
} else {
task->rshape->fillColor(&r, &g, &b, &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;
if (auto strokeFill = task->rshape->strokeFill()) {
rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
rasterGradientStroke(surface, &task->shape, strokeFill, opacity);
} else {
if (task->rshape->strokeColor(&r, &g, &b, &a)) {
a = MULTIPLY(opacity, a);
@ -480,7 +421,7 @@ bool SwRenderer::renderImage(RenderData data)
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
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;
@ -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 (flags == RenderUpdateFlag::None) return task;
@ -727,20 +669,11 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
}
task->clips = clips;
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;
}
task->transform = transform;
//zero size?
if (task->transform) {
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.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
task->opacity = opacity;
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
auto task = static_cast<SwImageTask*>(data);
@ -770,33 +703,12 @@ RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
else task->done();
task->source = surface;
task->mesh = mesh;
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)
{
//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)
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<SwShapeTask*>(data);

View File

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

@ -358,7 +358,7 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
rle->spans = static_cast<SwSpan*>(realloc(rle->spans, rle->alloc * sizeof(SwSpan)));
}
}
//Clip x range
SwCoord xOver = 0;
if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
@ -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)
{
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)
{
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.
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.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.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};
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};
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);
}
@ -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 end = length * rshape->stroke->trim.end;
auto begin = length * trimBegin;
auto end = length * trimEnd;
//default
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;
auto cmdCnt = rshape->path.cmds.count;
@ -341,6 +341,8 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
auto offset = 0.0f;
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
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 (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
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);
cmds++;
pts++;
@ -387,7 +389,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
case PathCommand::MoveTo: {
if (trimmed) {
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);
} else _dashMoveTo(dash, 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;
auto cmdCnt = rshape->path.cmds.count;
@ -492,7 +494,7 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
/* 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 (!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)));
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* 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);
}
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);
}

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->sy = sqrtf(powf(transform->e12, 2.0f) + powf(transform->e22, 2.0f));
} else {
stroke->sx = stroke->sy = 1.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->width = HALF_STROKE(rshape->strokeWidth());
stroke->cap = rshape->strokeCap();
stroke->miterlimit = static_cast<SwFixed>(rshape->strokeMiterlimit() * 65536.0f);

View File

@ -21,20 +21,21 @@
*/
#include "tvgIteratorAccessor.h"
#include "tvgCompressor.h"
/************************************************************************/
/* 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()) {
//Access the child
if (!func(child)) return false;
if (!func(child, data)) return false;
//Access the children of the child
if (auto it2 = IteratorAccessor::iterator(child)) {
if (!accessChildren(it2, func)) {
if (!accessChildren(it2, func, data)) {
delete(it2);
return false;
}
@ -44,26 +45,46 @@ static bool accessChildren(Iterator* it, function<bool(const Paint* paint)> func
return true;
}
/************************************************************************/
/* 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();
if (!p || !func) return picture;
auto backward = [](const tvg::Paint* paint, void* data) -> bool
{
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
//Root
if (!func(p)) return picture;
if (!func(picture, data)) return Result::Success;
//Children
if (auto it = IteratorAccessor::iterator(p)) {
accessChildren(it, func);
if (auto it = IteratorAccessor::iterator(picture)) {
accessChildren(it, func, data);
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
{
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;
if (!loader) return Result::InsufficientCondition;

View File

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

View File

@ -54,36 +54,30 @@ static constexpr bool operator &(CanvasEngine a, CanvasEngine 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 p = SRC;
auto VER = THORVG_VERSION_STRING;
auto p = VER;
const char* x;
char major[3];
x = strchr(p, '.');
if (!x) return false;
memcpy(major, p, x - p);
major[x - p] = '\0';
if (!(x = strchr(p, '.'))) return false;
uint32_t majorVal = atoi(p);
p = x + 1;
char minor[3];
x = strchr(p, '.');
if (!x) return false;
memcpy(minor, p, x - p);
minor[x - p] = '\0';
if (!(x = strchr(p, '.'))) return false;
uint32_t minorVal = atoi(p);
p = x + 1;
char micro[3];
x = SRC + strlen(THORVG_VERSION_STRING);
memcpy(micro, p, x - p);
micro[x - p] = '\0';
uint32_t microVal = atoi(p);
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);
if (major) *major = majorVal;
if (minor) *minor = minorVal;
if (micro) *micro = microVal;
return true;
}
@ -122,7 +116,7 @@ Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept
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;
@ -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()
{
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
Point tmp[4];
@ -50,8 +50,8 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTr
for (int i = 0; i < 4; ++i) {
tmp[i] = pts[i];
if (rTransform) tmp[i] *= rTransform->m;
if (pTransform) tmp[i] *= pTransform->m;
tmp[i] *= rm;
tmp[i] *= pm;
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].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. */
auto shape = static_cast<Shape*>(cmpTarget);
@ -84,18 +84,17 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren
//nothing to clip
if (ptsCnt == 0) return Result::InvalidArguments;
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.
auto tryClip = false;
if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) tryClip = true;
if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) tryClip = true;
if ((!mathRightAngle(pm) || mathSkewed(pm))) 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?
auto pt1 = pts + 0;
@ -110,16 +109,10 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren
auto v1 = *pt1;
auto v2 = *pt3;
if (rTransform) {
v1 *= rTransform->m;
v2 *= rTransform->m;
}
if (pTransform) {
v1 *= pTransform->m;
v2 *= pTransform->m;
}
v1 *= rm;
v2 *= rm;
v1 *= pm;
v2 *= pm;
//sorting
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;
PAINT_METHOD(ret, duplicate());
if (ret) ret->composite(nullptr, CompositeMethod::None);
PAINT_METHOD(ret, duplicate(ret));
//duplicate Transform
if (rTransform) {
ret->pImpl->rTransform = new RenderTransform();
*ret->pImpl->rTransform = *rTransform;
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
}
ret->pImpl->tr = tr;
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
ret->pImpl->opacity = opacity;
@ -180,14 +171,9 @@ Paint* Paint::Impl::duplicate()
bool Paint::Impl::rotate(float degree)
{
if (rTransform) {
if (rTransform->overriding) return false;
if (mathEqual(degree, rTransform->degree)) return true;
} else {
if (mathZero(degree)) return true;
rTransform = new RenderTransform();
}
rTransform->degree = degree;
if (tr.overriding) return false;
if (mathEqual(degree, tr.degree)) return true;
tr.degree = degree;
renderFlag |= RenderUpdateFlag::Transform;
return true;
@ -196,14 +182,9 @@ bool Paint::Impl::rotate(float degree)
bool Paint::Impl::scale(float factor)
{
if (rTransform) {
if (rTransform->overriding) return false;
if (mathEqual(factor, rTransform->scale)) return true;
} else {
if (mathEqual(factor, 1.0f)) return true;
rTransform = new RenderTransform();
}
rTransform->scale = factor;
if (tr.overriding) return false;
if (mathEqual(factor, tr.scale)) return true;
tr.scale = factor;
renderFlag |= RenderUpdateFlag::Transform;
return true;
@ -212,15 +193,10 @@ bool Paint::Impl::scale(float factor)
bool Paint::Impl::translate(float x, float y)
{
if (rTransform) {
if (rTransform->overriding) return false;
if (mathEqual(x, rTransform->m.e13) && mathEqual(y, rTransform->m.e23)) return true;
} else {
if (mathZero(x) && mathZero(y)) return true;
rTransform = new RenderTransform();
}
rTransform->m.e13 = x;
rTransform->m.e23 = y;
if (tr.overriding) return false;
if (mathEqual(x, tr.m.e13) && mathEqual(y, tr.m.e23)) return true;
tr.m.e13 = x;
tr.m.e23 = y;
renderFlag |= RenderUpdateFlag::Transform;
return true;
@ -229,6 +205,8 @@ bool Paint::Impl::translate(float x, float y)
bool Paint::Impl::render(RenderMethod* renderer)
{
if (opacity == 0) return true;
Compositor* cmp = nullptr;
/* 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) TVGERR("RENDERER", "paint's renderer has been changed!");
@ -266,7 +244,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
this->renderer = renderer;
}
if (renderFlag & RenderUpdateFlag::Transform) rTransform->update();
if (renderFlag & RenderUpdateFlag::Transform) tr.update();
/* 1. Composition Pre Processing */
RenderData trd = nullptr; //composite target render data
@ -277,7 +255,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
if (compData) {
auto target = compData->target;
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,
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) {
viewport = renderer->viewport();
if ((compFastTrack = _compFastTrack(renderer, target, pTransform, target->pImpl->rTransform, viewport)) == Result::Success) {
target->pImpl->ctxFlag |= ContextFlag::FastTrack;
if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) {
P(target)->ctxFlag |= ContextFlag::FastTrack;
}
}
}
if (compFastTrack == Result::InsufficientCondition) {
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);
}
}
@ -314,8 +292,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pT
opacity = MULTIPLY(opacity, this->opacity);
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 */
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;
const auto& m = this->transform(origin);
//Case: No transformed, quick return!
if (!transformed || !(m = this->transform())) {
if (!transformed || mathIdentity(&m)) {
PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
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
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 > 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 */
/************************************************************************/
@ -417,9 +416,7 @@ Result Paint::transform(const Matrix& m) noexcept
Matrix Paint::transform() noexcept
{
auto pTransform = pImpl->transform();
if (pTransform) return *pTransform;
return {1, 0, 0, 0, 1, 0, 0, 0, 1};
return pImpl->transform();
}
@ -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;
}
@ -444,6 +441,11 @@ Paint* Paint::duplicate() const 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();
if (pImpl->composite(this, p, method)) return Result::Success;
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) {
pImpl->blendMethod = method;

View File

@ -48,17 +48,40 @@ namespace tvg
struct Paint::Impl
{
Paint* paint = nullptr;
RenderTransform* rTransform = nullptr;
Composite* compData = nullptr;
RenderMethod* renderer = nullptr;
BlendMethod blendMethod = BlendMethod::Normal; //uint8_t
uint8_t renderFlag = RenderUpdateFlag::None;
uint8_t ctxFlag = ContextFlag::Invalid;
uint8_t id;
uint8_t opacity = 255;
uint8_t refCnt = 0; //reference count
struct {
Matrix m; //input matrix
Matrix cm; //multipled parents matrix
float degree; //rotation degree
float scale; //scale factor
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()
{
@ -66,7 +89,6 @@ namespace tvg
if (P(compData->target)->unref() == 0) delete(compData->target);
free(compData);
}
delete(rTransform);
if (renderer && (renderer->unref() == 0)) delete(renderer);
}
@ -84,23 +106,19 @@ namespace tvg
bool transform(const Matrix& m)
{
if (!rTransform) {
if (mathIdentity(&m)) return true;
rTransform = new RenderTransform();
}
rTransform->override(m);
tr.m = m;
tr.overriding = true;
renderFlag |= RenderUpdateFlag::Transform;
return true;
}
Matrix* transform()
Matrix& transform(bool origin = false)
{
if (rTransform) {
if (renderFlag & RenderUpdateFlag::Transform) rTransform->update();
return &rTransform->m;
}
return nullptr;
//update transform
if (renderFlag & RenderUpdateFlag::Transform) tr.update();
if (origin) return tr.cm;
return tr.m;
}
bool composite(Paint* source, Paint* target, CompositeMethod method)
@ -135,10 +153,11 @@ namespace tvg
bool rotate(float degree);
bool scale(float factor);
bool translate(float x, float y);
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking, bool origin = false);
RenderData update(RenderMethod* renderer, const Matrix& pm, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
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)
{
//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;
if (triangles && triangleCnt == 0) return Result::InvalidArguments;
struct Value
{
uint32_t id;
const Paint* ret;
} value = {id, nullptr};
pImpl->mesh(triangles, triangleCnt);
return Result::Success;
}
uint32_t Picture::mesh(const Polygon** triangles) const noexcept
{
if (triangles) *triangles = pImpl->rm.triangles;
return pImpl->rm.triangleCnt;
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
RenderData rd = nullptr; //engine data
float w = 0, h = 0;
RenderMesh rm; //mesh data
Picture* picture = nullptr;
bool resizing = false;
bool needComp = false; //need composition
RenderTransform resizeTransform(const RenderTransform* pTransform);
bool needComposition(uint8_t opacity);
bool render(RenderMethod* renderer);
bool size(float w, float h);
@ -90,58 +88,37 @@ struct Picture::Impl
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());
if (surface) {
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) {
if (resizing) {
loader->resize(paint, w, h);
resizing = 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;
}
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 (y) *y = 0;
if (w) *w = this->w;
if (h) *h = this->h;
}
if (x) *x = 0;
if (y) *y = 0;
if (w) *w = this->w;
if (h) *h = this->h;
return true;
}
@ -176,32 +153,21 @@ struct Picture::Impl
return load(loader);
}
void mesh(const Polygon* triangles, const uint32_t triangleCnt)
Paint* duplicate(Paint* ret)
{
if (triangles && triangleCnt > 0) {
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;
}
}
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
Paint* duplicate()
{
load();
auto ret = Picture::gen().release();
auto dup = ret->pImpl;
auto picture = Picture::gen().release();
auto dup = picture->pImpl;
if (paint) dup->paint = paint->duplicate();
if (loader) {
dup->loader = loader;
++dup->loader->sharing;
PP(ret)->renderFlag |= RenderUpdateFlag::Image;
PP(picture)->renderFlag |= RenderUpdateFlag::Image;
}
dup->surface = surface;
@ -209,13 +175,7 @@ struct Picture::Impl
dup->h = h;
dup->resizing = resizing;
if (rm.triangleCnt > 0) {
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;
return picture;
}
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)
{
auto x1 = x + w;

View File

@ -23,6 +23,7 @@
#ifndef _TVG_RENDER_H_
#define _TVG_RENDER_H_
#include <math.h>
#include "tvgCommon.h"
#include "tvgArray.h"
#include "tvgLock.h"
@ -85,15 +86,15 @@ struct Compositor
uint8_t opacity;
};
struct RenderMesh
struct Vertex
{
Polygon* triangles = nullptr;
uint32_t triangleCnt = 0;
Point pt;
Point uv;
};
~RenderMesh()
{
free(triangles);
}
struct Polygon
{
Vertex vertex[3];
};
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
{
float width = 0.0f;
@ -147,6 +130,57 @@ struct RenderStroke
bool simultaneous = true;
} 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()
{
free(dashPattern);
@ -191,7 +225,7 @@ struct RenderShape
{
if (!stroke) 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;
}
@ -252,9 +286,8 @@ public:
uint32_t unref();
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 Array<RenderData>& scene, RenderData data, const RenderTransform* 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 RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
virtual RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
virtual bool preRender() = 0;
virtual bool renderShape(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
case CompositeMethod::AddMask:
case CompositeMethod::DifferenceMask:
case CompositeMethod::LightenMask:
case CompositeMethod::DarkenMask:
return true;
default:
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::SubtractMask:
case CompositeMethod::IntersectMask:
case CompositeMethod::LightenMask:
case CompositeMethod::DarkenMask:
return ColorSpace::Grayscale8;
//TODO: Optimize Luma/InvLuma colorspace to Grayscale8
case CompositeMethod::LumaMask:

View File

@ -101,7 +101,7 @@ struct Scene::Impl
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))) {
/* Overriding opacity value. If this scene is half-translucent,
@ -109,20 +109,10 @@ struct Scene::Impl
this->opacity = opacity;
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) {
paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
}
return nullptr;
for (auto paint : paints) {
paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
}
return nullptr;
}
bool render(RenderMethod* renderer)
@ -198,10 +188,12 @@ struct Scene::Impl
return true;
}
Paint* duplicate()
Paint* duplicate(Paint* ret)
{
auto ret = Scene::gen().release();
auto dup = ret->pImpl;
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
auto scene = Scene::gen().release();
auto dup = scene->pImpl;
for (auto paint : paints) {
auto cdup = paint->duplicate();
@ -209,7 +201,7 @@ struct Scene::Impl
dup->paints.push_back(cdup);
}
return ret;
return scene;
}
void clear(bool free)

View File

@ -34,6 +34,7 @@ struct Shape::Impl
RenderData rd = nullptr; //engine data
Shape* shape;
uint8_t flag = RenderUpdateFlag::None;
uint8_t opacity; //for composition
bool needComp = false; //composite or not
@ -95,7 +96,7 @@ struct Shape::Impl
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;
@ -216,23 +217,6 @@ struct Shape::Impl
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
rs.stroke->trim.simultaneous == simultaneous) return;
auto loop = true;
if (begin > 1.0f && end > 1.0f) loop = false;
if (begin < 0.0f && end < 0.0f) loop = false;
if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f && end <= 1.0f) loop = false;
if (begin > 1.0f) begin -= 1.0f;
if (begin < 0.0f) begin += 1.0f;
if (end > 1.0f) end -= 1.0f;
if (end < 0.0f) end += 1.0f;
if ((loop && begin < end) || (!loop && begin > end)) {
auto tmp = begin;
begin = end;
end = tmp;
}
rs.stroke->trim.begin = begin;
rs.stroke->trim.end = end;
rs.stroke->trim.simultaneous = simultaneous;
@ -359,47 +343,56 @@ struct Shape::Impl
this->flag |= flag;
}
Paint* duplicate()
Paint* duplicate(Paint* ret)
{
auto ret = Shape::gen().release();
auto dup = ret->pImpl;
auto shape = static_cast<Shape*>(ret);
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;
//Color
memcpy(dup->rs.color, rs.color, sizeof(rs.color));
dup->flag = RenderUpdateFlag::Color;
//Path
if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) {
dup->rs.path.cmds = rs.path.cmds;
dup->rs.path.pts = rs.path.pts;
dup->flag |= RenderUpdateFlag::Path;
}
dup->rs.path.cmds.push(rs.path.cmds);
dup->rs.path.pts.push(rs.path.pts);
//Stroke
if (rs.stroke) {
dup->rs.stroke = new RenderStroke();
if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke;
*dup->rs.stroke = *rs.stroke;
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
if (rs.stroke->dashCnt > 0) {
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
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;
} else {
delete(dup->rs.stroke);
dup->rs.stroke = nullptr;
}
//Fill
if (rs.fill) {
dup->rs.fill = rs.fill->duplicate();
dup->flag |= RenderUpdateFlag::Gradient;
}
if (rs.fill) dup->rs.fill = rs.fill->duplicate();
else dup->rs.fill = nullptr;
return ret;
return shape;
}
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()

View File

@ -23,8 +23,6 @@
#ifndef _TVG_TASK_SCHEDULER_H_
#define _TVG_TASK_SCHEDULER_H_
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
#include <mutex>
#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;
}
@ -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
{
if (!pImpl->paint) return Result::InsufficientCondition;
return pImpl->fill(r, g, b);
return pImpl->shape->fill(r, g, b);
}
Result Text::fill(unique_ptr<Fill> f) noexcept
{
if (!pImpl->paint) return Result::InsufficientCondition;
auto p = f.release();
if (!p) return Result::MemoryCorruption;
return pImpl->fill(p);
return pImpl->shape->fill(std::move(f));
}

View File

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

View File

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