feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -22,12 +22,20 @@ Rafał Mikrut <mikrutrafal@protonmail.com>
Martin Capitanio <capnm@capitanio.org>
RuiwenTang <tangruiwen1989@gmail.com>
YouJin Lee <ol-of@naver.com>
SergeyLebedkin <sergii@lottiefiles.com>
Sergii Liebodkin <sergii@lottiefiles.com>
Jinny You <jinny@lottiefiles.com>
Nattu Adnan <nattu@reallynattu.com>
Gabor Kiss-Vamosi <kisvegabor@gmail.com>
Lorcán Mc Donagh <lorcan@lmdsp.com>
Lucas Niu <hoiyu3twon9@gmail.com>
Francisco Ramírez <franchuti688@gmail.com>
Francisco Ramírez <franchuti688@gmail.com>
Abdelrahman Ashraf <a.theashraf@gmail.com>
Neo Xu <neo.xu1990@gmail.com>
Thaddeus Crews <repiteo@outlook.com>
Josh Soref <jsoref@gmail.com>
Elliott Sales de Andrade <quantum.analyst@gmail.com>
Łukasz Pomietło <oficjalnyadreslukasza@gmail.com>
Kelly Loh <kelly@lottiefiles.com>
Dragoș Tiselice <dragos@lottiefiles.com>
Marcin Baszczewski <marcin@baszczewski.pl>
Fabian Blatz <fabianblatz@gmail.com>

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.15.10"
#endif

View file

@ -1,6 +1,7 @@
#ifndef _THORVG_H_
#define _THORVG_H_
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
@ -157,7 +158,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. @deprecated Use Paint::clip() instead.
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 +166,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)
};
@ -176,24 +179,49 @@ enum class CompositeMethod
*
* @see Paint::blend()
*
* @note Experimental API
* @since 0.15
*/
enum class BlendMethod : uint8_t
{
Normal = 0, ///< Perform the alpha blending(default). S if (Sa == 255), otherwise (Sa * S) + (255 - Sa) * D
Add, ///< Simply adds pixel values of one layer with the other. (S + D)
Screen, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D)
Multiply, ///< Takes the RGB channel values from 0 to 255 of each pixel in the top layer and multiples them with the values for the corresponding pixel from the bottom layer. (S * D)
Screen, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (S * D)
Overlay, ///< Combines Multiply and Screen blend modes. (2 * S * D) if (2 * D < Da), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D)
Difference, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S)
Exclusion, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d)
SrcOver, ///< Replace the bottom layer with the top layer.
Darken, ///< Creates a pixel that retains the smallest components of the top and bottom layer pixels. min(S, D)
Lighten, ///< Only has the opposite action of Darken Only. max(S, D)
ColorDodge, ///< Divides the bottom layer by the inverted top layer. D / (255 - S)
ColorBurn, ///< Divides the inverted bottom layer by the top layer, and then inverts the result. 255 - (255 - D) / S
HardLight, ///< The same as Overlay but with the color roles reversed. (2 * S * D) if (S < Sa), otherwise (Sa * Da) - 2 * (Da - S) * (Sa - D)
SoftLight ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D)
SoftLight, ///< The same as Overlay but with applying pure black or white does not result in pure black or white. (1 - 2 * S) * (D ^ 2) + (2 * S * D)
Difference, ///< Subtracts the bottom layer from the top layer or the other way around, to always get a non-negative value. (S - D) if (S > D), otherwise (D - S)
Exclusion, ///< The result is twice the product of the top and bottom layers, subtracted from their sum. s + d - (2 * s * d)
Hue, ///< Reserved. Not supported.
Saturation, ///< Reserved. Not supported.
Color, ///< Reserved. Not supported.
Luminosity, ///< Reserved. Not supported.
Add, ///< Simply adds pixel values of one layer with the other. (S + D)
HardMix ///< Reserved. Not supported.
};
/**
* @brief Enumeration that defines methods used for Scene Effects.
*
* This enum provides options to apply various post-processing effects to a scene.
* Scene effects are typically applied to modify the final appearance of a rendered scene, such as blurring.
*
* @see Scene::push(SceneEffect effect, ...)
*
* @note Experimental API
*/
enum class SceneEffect : uint8_t
{
ClearAll = 0, ///< Reset all previously applied scene effects, restoring the scene to its original state.
GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(3) = {sigma(float)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]}
DropShadow, ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(float)[0 - 360], distance(float), blur_sigma(float)[> 0], quality(int)[0 - 100]}
Fill, ///< Override the scene content color with a given fill information (Experimental API). Param(5) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255]}
Tint, ///< Tinting the current scene color with a given black, white color paramters (Experimental API). Param(7) = {black_R(int)[0 - 255], black_G(int)[0 - 255], black_B(int)[0 - 255], white_R(int)[0 - 255], white_G(int)[0 - 255], white_B(int)[0 - 255], intensity(float)[0 - 100]}
Tritone ///< Apply a tritone color effect to the scene using three color parameters for shadows, midtones, and highlights (Experimental API). Param(9) = {Shadow_R(int)[0 - 255], Shadow_G(int)[0 - 255], Shadow_B(int)[0 - 255], Midtone_R(int)[0 - 255], Midtone_G(int)[0 - 255], Midtone_B(int)[0 - 255], Highlight_R(int)[0 - 255], Highlight_G(int)[0 - 255], Highlight_B(int)[0 - 255]}
};
@ -204,7 +232,29 @@ enum class CanvasEngine
{
Sw = (1 << 1), ///< CPU rasterizer.
Gl = (1 << 2), ///< OpenGL rasterizer.
Wg = (1 << 3), ///< WebGPU rasterizer. (Experimental API)
Wg = (1 << 3), ///< WebGPU rasterizer. @since 0.15
};
/**
* @brief Enumeration specifying the ThorVG class type value.
*
* ThorVG's drawing objects can return class type values, allowing you to identify the specific class of each object.
*
* @see Paint::type()
* @see Fill::type()
*
* @note Experimental API
*/
enum class Type : uint8_t
{
Undefined = 0, ///< Unkown class
Shape, ///< Shape class
Scene, ///< Scene class
Picture, ///< Picture class
Text, ///< Text class
LinearGradient = 10, ///< LinearGradient class
RadialGradient ///< RadialGradient class
};
@ -232,34 +282,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
*
@ -300,7 +322,7 @@ public:
/**
* @brief Sets the values by which the object is moved in a two-dimensional space.
*
* The origin of the coordinate system is in the upper left corner of the canvas.
* The origin of the coordinate system is in the upper-left corner of the canvas.
* The horizontal and vertical axes point to the right and down, respectively.
*
* @param[in] x The value of the horizontal shift.
@ -338,7 +360,6 @@ public:
* @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque.
*
* @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible.
* @note ClipPath won't use the opacity value. (see: enum class CompositeMethod::ClipPath)
*/
Result opacity(uint8_t o) noexcept;
@ -350,6 +371,20 @@ public:
*/
Result composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept;
/**
* @brief Clip the drawing region of the paint object.
*
* This function restricts the drawing area of the paint object to the specified shape's paths.
*
* @param[in] clipper The shape object as the clipper.
*
* @retval Result::NonSupport If the @p clipper type is not Shape.
*
* @note @p clipper only supports the Shape type.
* @note Experimental API
*/
Result clip(std::unique_ptr<Paint> clipper) noexcept;
/**
* @brief Sets the blending method for the paint object.
*
@ -361,7 +396,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 +406,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,22 +447,29 @@ public:
CompositeMethod composite(const Paint** target) const noexcept;
/**
* @brief Gets the blending method of the object.
* @brief Returns the ID value of this class.
*
* @return The blending method
* This method can be used to check the current concrete instance type.
*
* @note Experimental API
* @return The class type ID of the Paint instance.
*
* @since Experimental API
*/
BlendMethod blend() const noexcept;
virtual Type type() const noexcept = 0;
/**
* @brief Return the unique id value of the paint instance.
* @brief Unique ID of this instance.
*
* This method can be called for checking the current concrete instance type.
* This is reserved to specify an paint instance in a scene.
*
* @return The type id of the Paint instance.
* @since Experimental API
*/
uint32_t identifier() const noexcept;
uint32_t id = 0;
/**
* @see Paint::type()
*/
TVG_DEPRECATED uint32_t identifier() const noexcept;
_TVG_DECLARE_PRIVATE(Paint);
};
@ -519,13 +562,20 @@ public:
Fill* duplicate() const noexcept;
/**
* @brief Return the unique id value of the Fill instance.
* @brief Returns the ID value of this class.
*
* This method can be called for checking the current concrete instance type.
* This method can be used to check the current concrete instance type.
*
* @return The type id of the Fill instance.
* @return The class type ID of the Fill instance.
*
* @since Experimental API
*/
uint32_t identifier() const noexcept;
virtual Type type() const noexcept = 0;
/**
* @see Fill::type()
*/
TVG_DEPRECATED uint32_t identifier() const noexcept;
_TVG_DECLARE_PRIVATE(Fill);
};
@ -554,7 +604,7 @@ public:
*
* This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree.
*
* @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync().
* @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync().
* @see Canvas::sync()
*
* @note Experimental API
@ -630,7 +680,7 @@ public:
* @warning It's not allowed to change the viewport during Canvas::push() - Canvas::sync() or Canvas::update() - Canvas::sync().
*
* @note When resetting the target, the viewport will also be reset to the target size.
* @note Experimental API
* @since 0.15
*/
virtual Result viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept;
@ -675,7 +725,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;
@ -701,13 +752,20 @@ public:
static std::unique_ptr<LinearGradient> gen() noexcept;
/**
* @brief Return the unique id value of this class.
* @brief Returns the ID value of this class.
*
* This method can be referred for identifying the LinearGradient class type.
* This method can be used to check the current concrete instance type.
*
* @return The type id of the LinearGradient class.
* @return The class type ID of the LinearGradient instance.
*
* @since Experimental API
*/
static uint32_t identifier() noexcept;
Type type() const noexcept override;
/**
* @see LinearGradient::type()
*/
TVG_DEPRECATED static uint32_t identifier() noexcept;
_TVG_DECLARE_PRIVATE(LinearGradient);
};
@ -734,6 +792,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;
@ -757,13 +817,20 @@ public:
static std::unique_ptr<RadialGradient> gen() noexcept;
/**
* @brief Return the unique id value of this class.
* @brief Returns the ID value of this class.
*
* This method can be referred for identifying the RadialGradient class type.
* This method can be used to check the current concrete instance type.
*
* @return The type id of the RadialGradient class.
* @return The class type ID of the LinearGradient instance.
*
* @since Experimental API
*/
static uint32_t identifier() noexcept;
Type type() const noexcept override;
/**
* @see RadialGradient::type()
*/
TVG_DEPRECATED static uint32_t identifier() noexcept;
_TVG_DECLARE_PRIVATE(RadialGradient);
};
@ -787,11 +854,11 @@ public:
~Shape();
/**
* @brief Resets the properties of the shape path.
* @brief Resets the shape path.
*
* The transformation matrix, the color, the fill and the stroke properties are retained.
* The transformation matrix, color, fill, and stroke properties are retained.
*
* @note The memory, where the path data is stored, is not deallocated at this stage for caching effect.
* @note The memory where the path data is stored is not deallocated at this stage to allow for caching.
*/
Result reset() noexcept;
@ -849,15 +916,15 @@ public:
* The rectangle with rounded corners can be achieved by setting non-zero values to @p rx and @p ry arguments.
* The @p rx and @p ry values specify the radii of the ellipse defining the rounding of the corners.
*
* The position of the rectangle is specified by the coordinates of its upper left corner - @p x and @p y arguments.
* The position of the rectangle is specified by the coordinates of its upper-left corner - @p x and @p y arguments.
*
* The rectangle is treated as a new sub-path - it is not connected with the previous sub-path.
*
* The value of the current point is set to (@p x + @p rx, @p y) - in case @p rx is greater
* than @p w/2 the current point is set to (@p x + @p w/2, @p y)
*
* @param[in] x The horizontal coordinate of the upper left corner of the rectangle.
* @param[in] y The vertical coordinate of the upper left corner of the rectangle.
* @param[in] x The horizontal coordinate of the upper-left corner of the rectangle.
* @param[in] y The vertical coordinate of the upper-left corner of the rectangle.
* @param[in] w The width of the rectangle.
* @param[in] h The height of the rectangle.
* @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle.
@ -899,7 +966,7 @@ public:
*
* @note Setting @p sweep value greater than 360 degrees, is equivalent to calling appendCircle(cx, cy, radius, radius).
*/
Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept;
TVG_DEPRECATED Result appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept;
/**
* @brief Appends a given sub-path to the path.
@ -990,7 +1057,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.
@ -1012,7 +1079,6 @@ public:
* @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0.
*
* @note Either a solid color or a gradient fill is applied, depending on what was set as last.
* @note ClipPath won't use the fill values. (see: enum class CompositeMethod::ClipPath)
*/
Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept;
@ -1076,7 +1142,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;
@ -1144,18 +1209,6 @@ public:
*/
float strokeMiterlimit() const noexcept;
/**
* @brief Gets the trim of the stroke along the defined path segment.
*
* @param[out] begin The starting point of the segment to display along the path.
* @param[out] end Specifies the end of the segment to display along the path.
*
* @return @c true if trimming is applied simultaneously to all paths of the shape, @c false otherwise.
*
* @note Experimental API
*/
bool strokeTrim(float* begin, float* end) const noexcept;
/**
* @brief Creates a new Shape object.
*
@ -1164,13 +1217,20 @@ public:
static std::unique_ptr<Shape> gen() noexcept;
/**
* @brief Return the unique id value of this class.
* @brief Returns the ID value of this class.
*
* This method can be referred for identifying the Shape class type.
* This method can be used to check the current concrete instance type.
*
* @return The type id of the Shape class.
* @return The class type ID of the Shape instance.
*
* @since Experimental API
*/
static uint32_t identifier() noexcept;
Type type() const noexcept override;
/**
* @see Shape::type()
*/
TVG_DEPRECATED static uint32_t identifier() noexcept;
_TVG_DECLARE_PRIVATE(Shape);
};
@ -1219,7 +1279,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.
@ -1227,7 +1287,7 @@ public:
* @retval Result::InvalidArguments In case no data are provided or the @p size is zero or less.
* @retval Result::NonSupport When trying to load a file with an unknown extension.
*
* @warning: It's the user responsibility to release the @p data memory.
* @warning It's the user responsibility to release the @p data memory.
*
* @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out.
* @since 0.5
@ -1256,7 +1316,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
@ -1273,39 +1333,19 @@ public:
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.
@ -1315,13 +1355,20 @@ public:
static std::unique_ptr<Picture> gen() noexcept;
/**
* @brief Return the unique id value of this class.
* @brief Returns the ID value of this class.
*
* This method can be referred for identifying the Picture class type.
* This method can be used to check the current concrete instance type.
*
* @return The type id of the Picture class.
* @return The class type ID of the Picture instance.
*
* @since Experimental API
*/
static uint32_t identifier() noexcept;
Type type() const noexcept override;
/**
* @see Picture::type()
*/
TVG_DEPRECATED static uint32_t identifier() noexcept;
_TVG_DECLARE_ACCESSOR(Animation);
_TVG_DECLARE_PRIVATE(Picture);
@ -1365,9 +1412,9 @@ public:
*
* This function provides the list of paint nodes, allowing users a direct opportunity to modify the scene tree.
*
* @warning Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync().
* @warning Please avoid accessing the paints during Scene update/draw. You can access them after calling Canvas::sync().
* @see Canvas::sync()
* @see Scene::push()
* @see Scene::push(std::unique_ptr<Paint> paint)
* @see Scene::clear()
*
* @note Experimental API
@ -1386,6 +1433,20 @@ public:
*/
Result clear(bool free = true) noexcept;
/**
* @brief Apply a post-processing effect to the scene.
*
* This function adds a specified scene effect, such as clearing all effects or applying a Gaussian blur,
* to the scene after it has been rendered. Multiple effects can be applied in sequence.
*
* @param[in] effect The scene effect to apply. Options are defined in the SceneEffect enum.
* For example, use SceneEffect::GaussianBlur to apply a blur with specific parameters.
* @param[in] ... Additional variadic parameters required for certain effects (e.g., sigma and direction for GaussianBlur).
*
* @note Experimental API
*/
Result push(SceneEffect effect, ...) noexcept;
/**
* @brief Creates a new Scene object.
*
@ -1394,13 +1455,20 @@ public:
static std::unique_ptr<Scene> gen() noexcept;
/**
* @brief Return the unique id value of this class.
* @brief Returns the ID value of this class.
*
* This method can be referred for identifying the Scene class type.
* This method can be used to check the current concrete instance type.
*
* @return The type id of the Scene class.
* @return The class type ID of the Scene instance.
*
* @since Experimental API
*/
static uint32_t identifier() noexcept;
Type type() const noexcept override;
/**
* @see Scene::type()
*/
TVG_DEPRECATED static uint32_t identifier() noexcept;
_TVG_DECLARE_PRIVATE(Scene);
};
@ -1411,7 +1479,7 @@ public:
*
* @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text.
*
* @note Experimental API
* @since 0.15
*/
class TVG_API Text final : public Paint
{
@ -1454,11 +1522,9 @@ 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
* @since 0.15
*/
Result fill(uint8_t r, uint8_t g, uint8_t b) noexcept;
@ -1469,12 +1535,10 @@ 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
*
* @see Text::font()
*
* @since 0.15
*/
Result fill(std::unique_ptr<Fill> f) noexcept;
@ -1490,9 +1554,9 @@ public:
* @retval Result::InvalidArguments In case the @p path is invalid.
* @retval Result::NonSupport When trying to load a file with an unknown extension.
*
* @note Experimental API
*
* @see Text::unload(const std::string& path)
*
* @since 0.15
*/
static Result load(const std::string& path) noexcept;
@ -1513,13 +1577,13 @@ public:
* @retval Result::NonSupport When trying to load a file with an unsupported extension.
* @retval Result::InsufficientCondition If attempting to unload the font data that has not been previously loaded.
*
* @warning: It's the user responsibility to release the @p data memory.
* @warning It's the user responsibility to release the @p data memory.
*
* @note To unload the font data loaded using this API, pass the proper @p name and @c nullptr as @p data.
* @note If you are unsure about the MIME type, you can provide an empty value like @c "", and thorvg will attempt to figure it out.
* @note Experimental API
*
* @see Text::font(const char* name, float size, const char* style)
*
* @note 0.15
*/
static Result load(const char* name, const char* data, uint32_t size, const std::string& mimeType = "ttf", bool copy = false) noexcept;
@ -1533,9 +1597,9 @@ public:
* @retval Result::InsufficientCondition Fails if the loader is not initialized.
*
* @note If the font data is currently in use, it will not be immediately unloaded.
* @note Experimental API
*
* @see Text::load(const std::string& path)
*
* @since 0.15
*/
static Result unload(const std::string& path) noexcept;
@ -1544,18 +1608,20 @@ public:
*
* @return A new Text object.
*
* @note Experimental API
* @since 0.15
*/
static std::unique_ptr<Text> gen() noexcept;
/**
* @brief Return the unique id value of this class.
* @brief Returns the ID value of this class.
*
* This method can be referred for identifying the Text class type.
* This method can be used to check the current concrete instance type.
*
* @return The type id of the Text class.
* @return The class type ID of the Text instance.
*
* @since Experimental API
*/
static uint32_t identifier() noexcept;
Type type() const noexcept override;
_TVG_DECLARE_PRIVATE(Text);
};
@ -1654,8 +1720,6 @@ public:
*
* @brief A class for the rendering graphic elements with a GL raster engine.
*
* @warning Please do not use it. This class is not fully supported yet.
*
* @since 0.14
*/
class TVG_API GlCanvas final : public Canvas
@ -1704,7 +1768,7 @@ public:
*
* @warning Please do not use it. This class is not fully supported yet.
*
* @note Experimental API
* @since 0.15
*/
class TVG_API WgCanvas final : public Canvas
{
@ -1718,6 +1782,7 @@ public:
* @param[in] surface WGPUSurface, handle to a presentable surface.
* @param[in] w The width of the surface.
* @param[in] h The height of the surface.
* @param[in] device WGPUDevice, a desired handle for the wgpu device. If it is @c nullptr, ThorVG will assign an appropriate device internally.
*
* @retval Result::InsufficientCondition if the canvas is performing rendering. Please ensure the canvas is synced.
* @retval Result::NonSupport In case the wg engine is not supported.
@ -1727,14 +1792,14 @@ public:
* @see Canvas::viewport()
* @see Canvas::sync()
*/
Result target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept;
Result target(void* instance, void* surface, uint32_t w, uint32_t h, void* device = nullptr) noexcept;
/**
* @brief Creates a new WgCanvas object.
*
* @return A new WgCanvas object.
*
* @note Experimental API
* @since 0.15
*/
static std::unique_ptr<WgCanvas> gen() noexcept;
@ -1781,6 +1846,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.
*
* @since 0.15
*/
static const char* version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept;
_TVG_DISABLE_CTOR(Initializer);
};
@ -1879,9 +1957,10 @@ 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
*/
Result segment(float begin, float end) noexcept;
@ -1920,7 +1999,7 @@ public:
* It's useful when you need to save the composed scene or image from a paint object and recreate it later.
*
* The file format is decided by the extension name(i.e. "*.tvg") while the supported formats depend on the TVG packaging environment.
* If it doesn't support the file format, the save() method returns the @c Result::NonSuppport result.
* If it doesn't support the file format, the save() method returns the @c Result::NonSupport result.
*
* Once you export a paint to the file successfully, you can recreate it using the Picture class.
*
@ -2030,17 +2109,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] paint The paint 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(Paint* paint, 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

@ -0,0 +1,13 @@
diff --git a/thirdparty/thorvg/src/common/tvgMath.cpp b/thirdparty/thorvg/src/common/tvgMath.cpp
index cb7f24ff40..f27f69faeb 100644
--- a/thirdparty/thorvg/src/common/tvgMath.cpp
+++ b/thirdparty/thorvg/src/common/tvgMath.cpp
@@ -79,7 +79,7 @@ float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc
Bezier left;
right.split(t, left);
length = _bezLength(left, lineLengthFunc);
- if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < 1e-3f) {
+ if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
break;
}
if (length < at) {

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

@ -468,7 +468,7 @@ size_t b64Decode(const char* encoded, const size_t len, char** decoded)
encoded += 4;
}
*decoded = output;
return reserved;
return idx;
}
@ -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

@ -1,245 +0,0 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgMath.h"
#include "tvgLines.h"
#define BEZIER_EPSILON 1e-2f
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static float _lineLengthApprox(const Point& pt1, const Point& pt2)
{
/* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
With alpha = 1, beta = 3/8, giving results with the largest error less
than 7% compared to the exact value. */
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
if (diff.x < 0) diff.x = -diff.x;
if (diff.y < 0) diff.y = -diff.y;
return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
}
static float _lineLength(const Point& pt1, const Point& pt2)
{
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
return sqrtf(diff.x * diff.x + diff.y * diff.y);
}
template<typename LengthFunc>
float _bezLength(const Bezier& cur, LengthFunc lineLengthFunc)
{
Bezier left, right;
auto len = lineLengthFunc(cur.start, cur.ctrl1) + lineLengthFunc(cur.ctrl1, cur.ctrl2) + lineLengthFunc(cur.ctrl2, cur.end);
auto chord = lineLengthFunc(cur.start, cur.end);
if (fabsf(len - chord) > BEZIER_EPSILON) {
tvg::bezSplit(cur, left, right);
return _bezLength(left, lineLengthFunc) + _bezLength(right, lineLengthFunc);
}
return len;
}
template<typename LengthFunc>
float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc)
{
auto biggest = 1.0f;
auto smallest = 0.0f;
auto t = 0.5f;
//just in case to prevent an infinite loop
if (at <= 0) return 0.0f;
if (at >= length) return 1.0f;
while (true) {
auto right = bz;
Bezier left;
bezSplitLeft(right, t, left);
length = _bezLength(left, lineLengthFunc);
if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
break;
}
if (length < at) {
smallest = t;
t = (t + biggest) * 0.5f;
} else {
biggest = t;
t = (smallest + t) * 0.5f;
}
}
return t;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
namespace tvg
{
float lineLength(const Point& pt1, const Point& pt2)
{
return _lineLength(pt1, pt2);
}
void lineSplitAt(const Line& cur, float at, Line& left, Line& right)
{
auto len = lineLength(cur.pt1, cur.pt2);
auto dx = ((cur.pt2.x - cur.pt1.x) / len) * at;
auto dy = ((cur.pt2.y - cur.pt1.y) / len) * at;
left.pt1 = cur.pt1;
left.pt2.x = left.pt1.x + dx;
left.pt2.y = left.pt1.y + dy;
right.pt1 = left.pt2;
right.pt2 = cur.pt2;
}
void bezSplit(const Bezier& cur, Bezier& left, Bezier& right)
{
auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f;
left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f;
right.ctrl2.x = (cur.ctrl2.x + cur.end.x) * 0.5f;
left.start.x = cur.start.x;
right.end.x = cur.end.x;
left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
c = (cur.ctrl1.y + cur.ctrl2.y) * 0.5f;
left.ctrl1.y = (cur.start.y + cur.ctrl1.y) * 0.5f;
right.ctrl2.y = (cur.ctrl2.y + cur.end.y) * 0.5f;
left.start.y = cur.start.y;
right.end.y = cur.end.y;
left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
}
float bezLength(const Bezier& cur)
{
return _bezLength(cur, _lineLength);
}
float bezLengthApprox(const Bezier& cur)
{
return _bezLength(cur, _lineLengthApprox);
}
void bezSplitLeft(Bezier& cur, float at, Bezier& left)
{
left.start = cur.start;
left.ctrl1.x = cur.start.x + at * (cur.ctrl1.x - cur.start.x);
left.ctrl1.y = cur.start.y + at * (cur.ctrl1.y - cur.start.y);
left.ctrl2.x = cur.ctrl1.x + at * (cur.ctrl2.x - cur.ctrl1.x); //temporary holding spot
left.ctrl2.y = cur.ctrl1.y + at * (cur.ctrl2.y - cur.ctrl1.y); //temporary holding spot
cur.ctrl2.x = cur.ctrl2.x + at * (cur.end.x - cur.ctrl2.x);
cur.ctrl2.y = cur.ctrl2.y + at * (cur.end.y - cur.ctrl2.y);
cur.ctrl1.x = left.ctrl2.x + at * (cur.ctrl2.x - left.ctrl2.x);
cur.ctrl1.y = left.ctrl2.y + at * (cur.ctrl2.y - left.ctrl2.y);
left.ctrl2.x = left.ctrl1.x + at * (left.ctrl2.x - left.ctrl1.x);
left.ctrl2.y = left.ctrl1.y + at * (left.ctrl2.y - left.ctrl1.y);
left.end.x = cur.start.x = left.ctrl2.x + at * (cur.ctrl1.x - left.ctrl2.x);
left.end.y = cur.start.y = left.ctrl2.y + at * (cur.ctrl1.y - left.ctrl2.y);
}
float bezAt(const Bezier& bz, float at, float length)
{
return _bezAt(bz, at, length, _lineLength);
}
float bezAtApprox(const Bezier& bz, float at, float length)
{
return _bezAt(bz, at, length, _lineLengthApprox);
}
void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right)
{
right = cur;
auto t = bezAt(right, at, bezLength(right));
bezSplitLeft(right, t, left);
}
Point bezPointAt(const Bezier& bz, float t)
{
Point cur;
auto it = 1.0f - t;
auto ax = bz.start.x * it + bz.ctrl1.x * t;
auto bx = bz.ctrl1.x * it + bz.ctrl2.x * t;
auto cx = bz.ctrl2.x * it + bz.end.x * t;
ax = ax * it + bx * t;
bx = bx * it + cx * t;
cur.x = ax * it + bx * t;
float ay = bz.start.y * it + bz.ctrl1.y * t;
float by = bz.ctrl1.y * it + bz.ctrl2.y * t;
float cy = bz.ctrl2.y * it + bz.end.y * t;
ay = ay * it + by * t;
by = by * it + cy * t;
cur.y = ay * it + by * t;
return cur;
}
float bezAngleAt(const Bezier& bz, float t)
{
if (t < 0 || t > 1) return 0;
//derivate
// p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 *
// t^2) * p2 + t^2 * p3)
float mt = 1.0f - t;
float d = t * t;
float a = -mt * mt;
float b = 1 - 4 * t + 3 * d;
float c = 2 * t - 3 * d;
Point pt ={a * bz.start.x + b * bz.ctrl1.x + c * bz.ctrl2.x + d * bz.end.x, a * bz.start.y + b * bz.ctrl1.y + c * bz.ctrl2.y + d * bz.end.y};
pt.x *= 3;
pt.y *= 3;
return mathRad2Deg(mathAtan2(pt.y, pt.x));
}
}

View file

@ -1,61 +0,0 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_LINES_H_
#define _TVG_LINES_H_
#include "tvgCommon.h"
namespace tvg
{
struct Line
{
Point pt1;
Point pt2;
};
float lineLength(const Point& pt1, const Point& pt2);
void lineSplitAt(const Line& cur, float at, Line& left, Line& right);
struct Bezier
{
Point start;
Point ctrl1;
Point ctrl2;
Point end;
};
void bezSplit(const Bezier&cur, Bezier& left, Bezier& right);
float bezLength(const Bezier& cur);
void bezSplitLeft(Bezier& cur, float at, Bezier& left);
float bezAt(const Bezier& bz, float at, float length);
void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right);
Point bezPointAt(const Bezier& bz, float t);
float bezAngleAt(const Bezier& bz, float t);
float bezLengthApprox(const Bezier& cur);
float bezAtApprox(const Bezier& bz, float at, float length);
}
#endif //_TVG_LINES_H_

View file

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

View file

@ -22,11 +22,88 @@
#include "tvgMath.h"
//see: https://en.wikipedia.org/wiki/Remez_algorithm
float mathAtan2(float y, float x)
#define BEZIER_EPSILON 1e-2f
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static float _lineLengthApprox(const Point& pt1, const Point& pt2)
{
/* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
With alpha = 1, beta = 3/8, giving results with the largest error less
than 7% compared to the exact value. */
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
if (diff.x < 0) diff.x = -diff.x;
if (diff.y < 0) diff.y = -diff.y;
return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f);
}
static float _lineLength(const Point& pt1, const Point& pt2)
{
Point diff = {pt2.x - pt1.x, pt2.y - pt1.y};
return sqrtf(diff.x * diff.x + diff.y * diff.y);
}
template<typename LengthFunc>
float _bezLength(const Bezier& cur, LengthFunc lineLengthFunc)
{
Bezier left, right;
auto len = lineLengthFunc(cur.start, cur.ctrl1) + lineLengthFunc(cur.ctrl1, cur.ctrl2) + lineLengthFunc(cur.ctrl2, cur.end);
auto chord = lineLengthFunc(cur.start, cur.end);
if (fabsf(len - chord) > BEZIER_EPSILON) {
cur.split(left, right);
return _bezLength(left, lineLengthFunc) + _bezLength(right, lineLengthFunc);
}
return len;
}
template<typename LengthFunc>
float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc)
{
auto biggest = 1.0f;
auto smallest = 0.0f;
auto t = 0.5f;
//just in case to prevent an infinite loop
if (at <= 0) return 0.0f;
if (at >= length) return 1.0f;
while (true) {
auto right = bz;
Bezier left;
right.split(t, left);
length = _bezLength(left, lineLengthFunc);
if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) {
break;
}
if (length < at) {
smallest = t;
t = (t + biggest) * 0.5f;
} else {
biggest = t;
t = (smallest + t) * 0.5f;
}
}
return t;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
namespace tvg {
//https://en.wikipedia.org/wiki/Remez_algorithm
float atan2(float y, float x)
{
if (y == 0.0f && x == 0.0f) return 0.0f;
auto a = std::min(fabsf(x), fabsf(y)) / std::max(fabsf(x), fabsf(y));
auto s = a * a;
auto r = ((-0.0464964749f * s + 0.15931422f) * s - 0.327622764f) * s * a + a;
@ -37,15 +114,14 @@ float mathAtan2(float y, float x)
}
bool mathInverse(const Matrix* m, Matrix* out)
bool inverse(const Matrix* m, Matrix* out)
{
auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) -
m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) +
m->e13 * (m->e21 * m->e32 - m->e22 * m->e31);
if (mathZero(det)) return false;
auto invDet = 1 / det;
auto invDet = 1.0f / det;
if (std::isinf(invDet)) return false;
out->e11 = (m->e22 * m->e33 - m->e32 * m->e23) * invDet;
out->e12 = (m->e13 * m->e32 - m->e12 * m->e33) * invDet;
@ -61,7 +137,7 @@ bool mathInverse(const Matrix* m, Matrix* out)
}
bool mathIdentity(const Matrix* m)
bool identity(const Matrix* m)
{
if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
@ -72,7 +148,7 @@ bool mathIdentity(const Matrix* m)
}
void mathRotate(Matrix* m, float degree)
void rotate(Matrix* m, float degree)
{
if (degree == 0.0f) return;
@ -109,9 +185,9 @@ Matrix operator*(const Matrix& lhs, const Matrix& rhs)
bool operator==(const Matrix& lhs, const Matrix& rhs)
{
if (!mathEqual(lhs.e11, rhs.e11) || !mathEqual(lhs.e12, rhs.e12) || !mathEqual(lhs.e13, rhs.e13) ||
!mathEqual(lhs.e21, rhs.e21) || !mathEqual(lhs.e22, rhs.e22) || !mathEqual(lhs.e23, rhs.e23) ||
!mathEqual(lhs.e31, rhs.e31) || !mathEqual(lhs.e32, rhs.e32) || !mathEqual(lhs.e33, rhs.e33)) {
if (!tvg::equal(lhs.e11, rhs.e11) || !tvg::equal(lhs.e12, rhs.e12) || !tvg::equal(lhs.e13, rhs.e13) ||
!tvg::equal(lhs.e21, rhs.e21) || !tvg::equal(lhs.e22, rhs.e22) || !tvg::equal(lhs.e23, rhs.e23) ||
!tvg::equal(lhs.e31, rhs.e31) || !tvg::equal(lhs.e32, rhs.e32) || !tvg::equal(lhs.e33, rhs.e33)) {
return false;
}
return true;
@ -133,3 +209,166 @@ Point operator*(const Point& pt, const Matrix& m)
auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23;
return {tx, ty};
}
Point normal(const Point& p1, const Point& p2)
{
auto dir = p2 - p1;
auto len = length(dir);
if (tvg::zero(len)) return {};
auto unitDir = dir / len;
return {-unitDir.y, unitDir.x};
}
float Line::length() const
{
return _lineLength(pt1, pt2);
}
void Line::split(float at, Line& left, Line& right) const
{
auto len = length();
auto dx = ((pt2.x - pt1.x) / len) * at;
auto dy = ((pt2.y - pt1.y) / len) * at;
left.pt1 = pt1;
left.pt2.x = left.pt1.x + dx;
left.pt2.y = left.pt1.y + dy;
right.pt1 = left.pt2;
right.pt2 = pt2;
}
void Bezier::split(Bezier& left, Bezier& right) const
{
auto c = (ctrl1.x + ctrl2.x) * 0.5f;
left.ctrl1.x = (start.x + ctrl1.x) * 0.5f;
right.ctrl2.x = (ctrl2.x + end.x) * 0.5f;
left.start.x = start.x;
right.end.x = end.x;
left.ctrl2.x = (left.ctrl1.x + c) * 0.5f;
right.ctrl1.x = (right.ctrl2.x + c) * 0.5f;
left.end.x = right.start.x = (left.ctrl2.x + right.ctrl1.x) * 0.5f;
c = (ctrl1.y + ctrl2.y) * 0.5f;
left.ctrl1.y = (start.y + ctrl1.y) * 0.5f;
right.ctrl2.y = (ctrl2.y + end.y) * 0.5f;
left.start.y = start.y;
right.end.y = end.y;
left.ctrl2.y = (left.ctrl1.y + c) * 0.5f;
right.ctrl1.y = (right.ctrl2.y + c) * 0.5f;
left.end.y = right.start.y = (left.ctrl2.y + right.ctrl1.y) * 0.5f;
}
void Bezier::split(float at, Bezier& left, Bezier& right) const
{
right = *this;
auto t = right.at(at, right.length());
right.split(t, left);
}
float Bezier::length() const
{
return _bezLength(*this, _lineLength);
}
float Bezier::lengthApprox() const
{
return _bezLength(*this, _lineLengthApprox);
}
void Bezier::split(float t, Bezier& left)
{
left.start = start;
left.ctrl1.x = start.x + t * (ctrl1.x - start.x);
left.ctrl1.y = start.y + t * (ctrl1.y - start.y);
left.ctrl2.x = ctrl1.x + t * (ctrl2.x - ctrl1.x); //temporary holding spot
left.ctrl2.y = ctrl1.y + t * (ctrl2.y - ctrl1.y); //temporary holding spot
ctrl2.x = ctrl2.x + t * (end.x - ctrl2.x);
ctrl2.y = ctrl2.y + t * (end.y - ctrl2.y);
ctrl1.x = left.ctrl2.x + t * (ctrl2.x - left.ctrl2.x);
ctrl1.y = left.ctrl2.y + t * (ctrl2.y - left.ctrl2.y);
left.ctrl2.x = left.ctrl1.x + t * (left.ctrl2.x - left.ctrl1.x);
left.ctrl2.y = left.ctrl1.y + t * (left.ctrl2.y - left.ctrl1.y);
left.end.x = start.x = left.ctrl2.x + t * (ctrl1.x - left.ctrl2.x);
left.end.y = start.y = left.ctrl2.y + t * (ctrl1.y - left.ctrl2.y);
}
float Bezier::at(float at, float length) const
{
return _bezAt(*this, at, length, _lineLength);
}
float Bezier::atApprox(float at, float length) const
{
return _bezAt(*this, at, length, _lineLengthApprox);
}
Point Bezier::at(float t) const
{
Point cur;
auto it = 1.0f - t;
auto ax = start.x * it + ctrl1.x * t;
auto bx = ctrl1.x * it + ctrl2.x * t;
auto cx = ctrl2.x * it + end.x * t;
ax = ax * it + bx * t;
bx = bx * it + cx * t;
cur.x = ax * it + bx * t;
float ay = start.y * it + ctrl1.y * t;
float by = ctrl1.y * it + ctrl2.y * t;
float cy = ctrl2.y * it + end.y * t;
ay = ay * it + by * t;
by = by * it + cy * t;
cur.y = ay * it + by * t;
return cur;
}
float Bezier::angle(float t) const
{
if (t < 0 || t > 1) return 0;
//derivate
// p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 *
// t^2) * p2 + t^2 * p3)
float mt = 1.0f - t;
float d = t * t;
float a = -mt * mt;
float b = 1 - 4 * t + 3 * d;
float c = 2 * t - 3 * d;
Point pt ={a * start.x + b * ctrl1.x + c * ctrl2.x + d * end.x, a * start.y + b * ctrl1.y + c * ctrl2.y + d * end.y};
pt.x *= 3;
pt.y *= 3;
return rad2deg(tvg::atan2(pt.y, pt.x));
}
uint8_t lerp(const uint8_t &start, const uint8_t &end, float t)
{
auto result = static_cast<int>(start + (end - start) * t);
tvg::clamp(result, 0, 255);
return static_cast<uint8_t>(result);
}
}

View file

@ -26,73 +26,80 @@
#define _USE_MATH_DEFINES
#include <float.h>
#include <math.h>
#include <cmath>
#include "tvgCommon.h"
namespace tvg
{
#define MATH_PI 3.14159265358979323846f
#define MATH_PI2 1.57079632679489661923f
#define FLOAT_EPSILON 1.0e-06f //1.192092896e-07f
#define PATH_KAPPA 0.552284f
#define mathMin(x, y) (((x) < (y)) ? (x) : (y))
#define mathMax(x, y) (((x) > (y)) ? (x) : (y))
/************************************************************************/
/* General functions */
/************************************************************************/
float mathAtan2(float y, float x);
float atan2(float y, float x);
static inline float mathDeg2Rad(float degree)
static inline float deg2rad(float degree)
{
return degree * (MATH_PI / 180.0f);
}
static inline float mathRad2Deg(float radian)
static inline float rad2deg(float radian)
{
return radian * (180.0f / MATH_PI);
}
static inline bool mathZero(float a)
static inline bool zero(float a)
{
return (fabsf(a) <= FLOAT_EPSILON) ? true : false;
}
static inline bool mathEqual(float a, float b)
static inline bool equal(float a, float b)
{
return mathZero(a - b);
return tvg::zero(a - b);
}
template <typename T>
static inline void clamp(T& v, const T& min, const T& max)
{
if (v < min) v = min;
else if (v > max) v = max;
}
/************************************************************************/
/* Matrix functions */
/************************************************************************/
void mathRotate(Matrix* m, float degree);
bool mathInverse(const Matrix* m, Matrix* out);
bool mathIdentity(const Matrix* m);
void rotate(Matrix* m, float degree);
bool inverse(const Matrix* m, Matrix* out);
bool identity(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 rightAngle(const Matrix& m)
{
auto radian = fabsf(mathAtan2(m->e21, m->e11));
if (radian < FLOAT_EPSILON || mathEqual(radian, MATH_PI2) || mathEqual(radian, MATH_PI)) return true;
auto radian = fabsf(tvg::atan2(m.e21, m.e11));
if (radian < FLOAT_EPSILON || tvg::equal(radian, MATH_PI2) || tvg::equal(radian, MATH_PI)) return true;
return false;
}
static inline bool mathSkewed(const Matrix* m)
static inline bool skewed(const Matrix& m)
{
return !mathZero(m->e21 + m->e12);
return !tvg::zero(m.e21 + m.e12);
}
static inline void mathIdentity(Matrix* m)
static inline void identity(Matrix* m)
{
m->e11 = 1.0f;
m->e12 = 0.0f;
@ -106,14 +113,14 @@ static inline void mathIdentity(Matrix* m)
}
static inline void mathScale(Matrix* m, float sx, float sy)
static inline void scale(Matrix* m, float sx, float sy)
{
m->e11 *= sx;
m->e22 *= sy;
}
static inline void mathScaleR(Matrix* m, float x, float y)
static inline void scaleR(Matrix* m, float x, float y)
{
if (x != 1.0f) {
m->e11 *= x;
@ -126,14 +133,14 @@ static inline void mathScaleR(Matrix* m, float x, float y)
}
static inline void mathTranslate(Matrix* m, float x, float y)
static inline void translate(Matrix* m, float x, float y)
{
m->e13 += x;
m->e23 += y;
}
static inline void mathTranslateR(Matrix* m, float x, float y)
static inline void translateR(Matrix* m, float x, float y)
{
if (x == 0.0f && y == 0.0f) return;
m->e13 += (x * m->e11 + y * m->e12);
@ -153,7 +160,7 @@ static inline void operator*=(Matrix& lhs, const Matrix& rhs)
}
static inline void mathLog(const Matrix& m)
static inline void log(const Matrix& m)
{
TVGLOG("COMMON", "Matrix: [%f %f %f] [%f %f %f] [%f %f %f]", m.e11, m.e12, m.e13, m.e21, m.e22, m.e23, m.e31, m.e32, m.e33);
}
@ -165,15 +172,21 @@ static inline void mathLog(const Matrix& m)
void operator*=(Point& pt, const Matrix& m);
Point operator*(const Point& pt, const Matrix& m);
Point normal(const Point& p1, const Point& p2);
static inline bool mathZero(const Point& p)
static inline float cross(const Point& lhs, const Point& rhs)
{
return mathZero(p.x) && mathZero(p.y);
return lhs.x * rhs.y - rhs.x * lhs.y;
}
static inline float mathLength(const Point* a, const Point* b)
static inline bool zero(const Point& p)
{
return tvg::zero(p.x) && tvg::zero(p.y);
}
static inline float length(const Point* a, const Point* b)
{
auto x = b->x - a->x;
auto y = b->y - a->y;
@ -185,7 +198,7 @@ static inline float mathLength(const Point* a, const Point* b)
}
static inline float mathLength(const Point& a)
static inline float length(const Point& a)
{
return sqrtf(a.x * a.x + a.y * a.y);
}
@ -193,7 +206,7 @@ static inline float mathLength(const Point& a)
static inline bool operator==(const Point& lhs, const Point& rhs)
{
return mathEqual(lhs.x, rhs.x) && mathEqual(lhs.y, rhs.y);
return tvg::equal(lhs.x, rhs.x) && tvg::equal(lhs.y, rhs.y);
}
@ -233,20 +246,61 @@ static inline Point operator/(const Point& lhs, const float rhs)
}
static inline void mathLog(const Point& pt)
static inline void log(const Point& pt)
{
TVGLOG("COMMON", "Point: [%f %f]", pt.x, pt.y);
}
/************************************************************************/
/* Line functions */
/************************************************************************/
struct Line
{
Point pt1;
Point pt2;
void split(float at, Line& left, Line& right) const;
float length() const;
};
/************************************************************************/
/* Bezier functions */
/************************************************************************/
struct Bezier
{
Point start;
Point ctrl1;
Point ctrl2;
Point end;
void split(float t, Bezier& left);
void split(Bezier& left, Bezier& right) const;
void split(float at, Bezier& left, Bezier& right) const;
float length() const;
float lengthApprox() const;
float at(float at, float length) const;
float atApprox(float at, float length) const;
Point at(float t) const;
float angle(float t) const;
};
/************************************************************************/
/* Interpolation functions */
/************************************************************************/
template <typename T>
static inline T mathLerp(const T &start, const T &end, float t)
static inline T lerp(const T &start, const T &end, float t)
{
return static_cast<T>(start + (end - start) * t);
}
uint8_t lerp(const uint8_t &start, const uint8_t &end, float t);
}
#endif //_TVG_MATH_H_

View file

@ -183,7 +183,7 @@ float strToFloat(const char *nPtr, char **endPtr)
auto scale = 1.0f;
while (exponentPart >= 8U) {
scale *= 1E8;
scale *= 1E8f;
exponentPart -= 8U;
}
while (exponentPart > 0U) {
@ -207,16 +207,6 @@ error:
return 0.0f;
}
int str2int(const char* str, size_t n)
{
int ret = 0;
for(size_t i = 0; i < n; ++i) {
ret = ret * 10 + (str[i] - '0');
}
return ret;
}
char* strDuplicate(const char *str, size_t n)
{
auto len = strlen(str);
@ -229,6 +219,14 @@ char* strDuplicate(const char *str, size_t n)
return (char *) memcpy(ret, str, n);
}
char* strAppend(char* lhs, const char* rhs, size_t n)
{
if (!rhs) return lhs;
if (!lhs) return strDuplicate(rhs, n);
lhs = (char*)realloc(lhs, strlen(lhs) + n + 1);
return strncat(lhs, rhs, n);
}
char* strDirname(const char* path)
{
const char *ptr = strrchr(path, '/');

View file

@ -29,8 +29,8 @@ namespace tvg
{
float strToFloat(const char *nPtr, char **endPtr); //convert to float
int str2int(const char* str, size_t n); //convert to integer
char* strDuplicate(const char *str, size_t n); //copy the string
char* strAppend(char* lhs, const char* rhs, size_t n); //append the rhs to the lhs
char* strDirname(const char* path); //return the full directory name
}

View file

@ -67,6 +67,7 @@ bool PngLoader::open(const string& path)
bool PngLoader::open(const char* data, uint32_t size, bool copy)
{
#ifdef THORVG_FILE_IO_SUPPORT
image->opaque = NULL;
if (!png_image_begin_read_from_memory(image, data, size)) return false;
@ -75,6 +76,9 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy)
h = (float)image->height;
return true;
#else
return false;
#endif
}
@ -86,10 +90,10 @@ bool PngLoader::read()
if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
image->format = PNG_FORMAT_BGRA;
surface.cs = ColorSpace::ARGB8888;
surface.cs = ColorSpace::ARGB8888S;
} else {
image->format = PNG_FORMAT_RGBA;
surface.cs = ColorSpace::ABGR8888;
surface.cs = ColorSpace::ABGR8888S;
}
auto buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image))));

View file

@ -68,6 +68,7 @@ WebpLoader::~WebpLoader()
bool WebpLoader::open(const string& path)
{
#ifdef THORVG_FILE_IO_SUPPORT
auto webpFile = fopen(path.c_str(), "rb");
if (!webpFile) return false;
@ -96,6 +97,9 @@ bool WebpLoader::open(const string& path)
finalize:
fclose(webpFile);
return ret;
#else
return false;
#endif
}
@ -134,7 +138,7 @@ bool WebpLoader::read()
}
Surface* WebpLoader::bitmap()
RenderSurface* WebpLoader::bitmap()
{
this->done();

View file

@ -36,7 +36,7 @@ public:
bool open(const char* data, uint32_t size, bool copy) override;
bool read() override;
Surface* bitmap() override;
RenderSurface* bitmap() override;
private:
void run(unsigned tid) override;

View file

@ -70,6 +70,7 @@ JpgLoader::~JpgLoader()
bool JpgLoader::open(const string& path)
{
#ifdef THORVG_FILE_IO_SUPPORT
int width, height;
decoder = jpgdHeader(path.c_str(), &width, &height);
if (!decoder) return false;
@ -78,6 +79,9 @@ bool JpgLoader::open(const string& path)
h = static_cast<float>(height);
return true;
#else
return false;
#endif
}
@ -125,7 +129,7 @@ bool JpgLoader::close()
}
Surface* JpgLoader::bitmap()
RenderSurface* JpgLoader::bitmap()
{
this->done();
return ImageLoader::bitmap();

View file

@ -46,7 +46,7 @@ public:
bool read() override;
bool close() override;
Surface* bitmap() override;
RenderSurface* bitmap() override;
};
#endif //_TVG_JPG_LOADER_H_

View file

@ -36,6 +36,8 @@
#include <stdio.h>
#include <setjmp.h>
#include <stdint.h>
#include "tvgCommon.h"
#include "tvgJpgd.h"
#ifdef _MSC_VER
@ -70,7 +72,7 @@ enum jpgd_status
JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMETIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
@ -1382,9 +1384,9 @@ int jpeg_decoder::process_markers()
read_dht_marker();
break;
}
// No arithmitic support - dumb patents!
// No arithmetic support - dumb patents!
case M_DAC: {
stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
break;
}
case M_DQT: {
@ -1466,8 +1468,8 @@ void jpeg_decoder::locate_sof_marker()
read_sof_marker();
break;
}
case M_SOF9: { /* Arithmitic coding */
stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
case M_SOF9: { /* Arithmetic coding */
stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
break;
}
default: {
@ -1738,7 +1740,8 @@ void jpeg_decoder::transform_mcu_expand(int mcu_row)
DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr);
break;
default:
JPGD_ASSERT(false);
TVGERR("JPG", "invalid transform_mcu_expand");
return;
}
DCT_Upsample::Matrix44 a(P + Q); P -= Q;
DCT_Upsample::Matrix44& b = P;
@ -1831,7 +1834,7 @@ void jpeg_decoder::process_restart()
int i;
int c = 0;
// Align to a byte boundry
// Align to a byte boundary
// FIXME: Is this really necessary? get_bits_no_markers() never reads in markers!
//get_bits_no_markers(m_bits_left & 7);

View file

@ -177,6 +177,7 @@ static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengt
else if (strstr(str, "%")) {
if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0f) * svgParse->global.h;
else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0f) * svgParse->global.w;
else if (type == SvgParserLengthType::Diagonal) parsedValue = (sqrtf(powf(svgParse->global.w, 2) + powf(svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
else //if other than it's radius
{
float max = svgParse->global.w;
@ -199,7 +200,7 @@ static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool&
isPercentage = false;
if (strstr(str, "%")) {
parsedValue = parsedValue / 100.0;
parsedValue = parsedValue / 100.0f;
isPercentage = true;
}
else if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
@ -224,7 +225,7 @@ static float _toOffset(const char* str)
auto ptr = strstr(str, "%");
if (ptr) {
parsedValue = parsedValue / 100.0;
parsedValue = parsedValue / 100.0f;
if (end != ptr || (end + 1) != strEnd) return 0;
} else if (end != strEnd) return 0;
@ -588,15 +589,20 @@ static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* re
float _red = 0, _green = 0, _blue = 0;
uint32_t i = 0;
if (mathZero(saturation)) _red = _green = _blue = brightness;
while (hue < 0) hue += 360.0f;
hue = fmod(hue, 360.0f);
saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f;
brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f;
if (tvg::zero(saturation)) _red = _green = _blue = brightness;
else {
if (mathEqual(hue, 360.0)) hue = 0.0f;
if (tvg::equal(hue, 360.0)) hue = 0.0f;
hue /= 60.0f;
v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation));
p = brightness + brightness - v;
if (!mathZero(v)) sv = (v - p) / v;
if (!tvg::zero(v)) sv = (v - p) / v;
else sv = 0;
i = static_cast<uint8_t>(hue);
@ -710,15 +716,15 @@ 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) {
th = float(uint32_t(th) % 360);
const char* saturation = nullptr;
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);
@ -853,8 +859,8 @@ static Matrix* _parseTransformationMatrix(const char* value)
//Transform to signed.
points[0] = fmodf(points[0], 360.0f);
if (points[0] < 0) points[0] += 360.0f;
auto c = cosf(mathDeg2Rad(points[0]));
auto s = sinf(mathDeg2Rad(points[0]));
auto c = cosf(deg2rad(points[0]));
auto s = sinf(deg2rad(points[0]));
if (ptCount == 1) {
Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
*matrix *= tmp;
@ -877,12 +883,12 @@ static Matrix* _parseTransformationMatrix(const char* value)
*matrix *= tmp;
} else if (state == MatrixState::SkewX) {
if (ptCount != 1) goto error;
auto deg = tanf(mathDeg2Rad(points[0]));
auto deg = tanf(deg2rad(points[0]));
Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
*matrix *= tmp;
} else if (state == MatrixState::SkewY) {
if (ptCount != 1) goto error;
auto deg = tanf(mathDeg2Rad(points[0]));
auto deg = tanf(deg2rad(points[0]));
Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
*matrix *= tmp;
}
@ -1061,7 +1067,7 @@ static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, co
static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
{
node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Diagonal);
}
@ -1609,7 +1615,7 @@ static constexpr struct
} circleTags[] = {
{"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
{"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
{"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)}
{"r", SvgParserLengthType::Diagonal, sizeof("r"), offsetof(SvgCircleNode, r)}
};
@ -2178,6 +2184,7 @@ static SvgNode* _createTextNode(SvgLoaderData* loader, SvgNode* parent, const ch
//TODO: support the def font and size as used in a system?
loader->svgParse->node->node.text.fontSize = 10.0f;
loader->svgParse->node->node.text.fontFamily = nullptr;
loader->svgParse->node->node.text.text = nullptr;
func(buf, bufLength, _attrParseTextNode, loader);
@ -2548,6 +2555,18 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
}
static SvgColor* _findLatestColor(const SvgLoaderData* loader)
{
auto parent = loader->stack.count > 0 ? loader->stack.last() : loader->doc;
while (parent != nullptr) {
if (parent->style->curColorSet) return &parent->style->color;
parent = parent->parent;
}
return nullptr;
}
static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
@ -2557,7 +2576,13 @@ static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
stop->a = _toOpacity(value);
loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
} else if (!strcmp(key, "stop-color")) {
if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) {
if (!strcmp(value, "currentColor")) {
if (auto latestColor = _findLatestColor(loader)) {
stop->r = latestColor->r;
stop->g = latestColor->g;
stop->b = latestColor->b;
}
} else if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) {
loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
}
} else {
@ -2580,7 +2605,13 @@ static bool _attrParseStops(void* data, const char* key, const char* value)
stop->a = _toOpacity(value);
}
} else if (!strcmp(key, "stop-color")) {
if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) {
if (!strcmp(value, "currentColor")) {
if (auto latestColor = _findLatestColor(loader)) {
stop->r = latestColor->r;
stop->g = latestColor->g;
stop->b = latestColor->b;
}
} else if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) {
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
}
} else if (!strcmp(key, "style")) {
@ -3283,6 +3314,7 @@ static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content,
for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) {
if (!strncmp(tagName, graphicsTags[i].tag, sz)) {
loader->currentGraphicsNode = nullptr;
if (!strncmp(tagName, "text", 4)) loader->openedTag = OpenedTagType::Other;
loader->stack.pop();
break;
}
@ -3334,7 +3366,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
else parent = loader->doc;
if (!strcmp(tagName, "style")) {
// TODO: For now only the first style node is saved. After the css id selector
// is introduced this if condition shouldin't be necessary any more
// is introduced this if condition shouldn't be necessary any more
if (!loader->cssStyle) {
node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
loader->cssStyle = node;
@ -3356,11 +3388,9 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
if (node && !empty) {
if (!strcmp(tagName, "text")) loader->openedTag = OpenedTagType::Text;
else {
auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr);
loader->stack.push(defs);
loader->currentGraphicsNode = node;
}
auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr);
loader->stack.push(defs);
loader->currentGraphicsNode = node;
}
} else if ((gradientMethod = _findGradientFactory(tagName))) {
SvgStyleGradient* gradient;
@ -3396,9 +3426,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
static void _svgLoaderParserText(SvgLoaderData* loader, const char* content, unsigned int length)
{
auto text = &loader->svgParse->node->node.text;
if (text->text) free(text->text);
text->text = strDuplicate(content, length);
loader->openedTag = OpenedTagType::Other;
text->text = strAppend(text->text, content, length);
}
@ -3945,6 +3973,7 @@ bool SvgLoader::open(const char* data, uint32_t size, bool copy)
bool SvgLoader::open(const string& path)
{
#ifdef THORVG_FILE_IO_SUPPORT
clear();
ifstream f;
@ -3962,6 +3991,9 @@ bool SvgLoader::open(const string& path)
size = filePath.size();
return header();
#else
return false;
#endif
}

View file

@ -215,6 +215,7 @@ enum class SvgParserLengthType
{
Vertical,
Horizontal,
Diagonal,
//In case of, for example, radius of radial gradient
Other
};

View file

@ -126,7 +126,7 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P
rx = fabsf(rx);
ry = fabsf(ry);
angle = mathDeg2Rad(angle);
angle = deg2rad(angle);
cosPhi = cosf(angle);
sinPhi = sinf(angle);
dx2 = (sx - x) / 2.0f;
@ -190,14 +190,14 @@ void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, P
cx += (sx + x) / 2.0f;
cy += (sy + y) / 2.0f;
//Sstep 4 (F6.5.4)
//Step 4 (F6.5.4)
//We dont' use arccos (as per w3c doc), see
//http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
//Note: atan2 (0.0, 1.0) == 0.0
at = mathAtan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
at = tvg::atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at;
nat = mathAtan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
nat = tvg::atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at;
if (sweep) {
@ -402,10 +402,10 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
case 'q':
case 'Q': {
Point p[3];
float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0 / 3.0);
float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0 / 3.0);
float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0 / 3.0);
float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0 / 3.0);
float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0f / 3.0f);
float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0f / 3.0f);
float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0f / 3.0f);
float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0f / 3.0f);
cmds->push(PathCommand::CubicTo);
p[0] = {ctrl_x0, ctrl_y0};
p[1] = {ctrl_x1, ctrl_y1};
@ -428,10 +428,10 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
} else {
ctrl = *cur;
}
float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0 / 3.0);
float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0 / 3.0);
float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0 / 3.0);
float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0 / 3.0);
float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0f / 3.0f);
float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0f / 3.0f);
float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0f / 3.0f);
float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0f / 3.0f);
cmds->push(PathCommand::CubicTo);
p[0] = {ctrl_x0, ctrl_y0};
p[1] = {ctrl_x1, ctrl_y1};
@ -469,12 +469,12 @@ static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cm
}
case 'a':
case 'A': {
if (mathZero(arr[0]) || mathZero(arr[1])) {
if (tvg::zero(arr[0]) || tvg::zero(arr[1])) {
Point p = {arr[5], arr[6]};
cmds->push(PathCommand::LineTo);
pts->push(p);
*cur = {arr[5], arr[6]};
} else if (!mathEqual(cur->x, arr[5]) || !mathEqual(cur->y, arr[6])) {
} else if (!tvg::equal(cur->x, arr[5]) || !tvg::equal(cur->y, arr[6])) {
_pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], fabsf(arr[0]), fabsf(arr[1]), arr[2], arr[3], arr[4]);
*cur = *curCtl = {arr[5], arr[6]};
*isQuadratic = false;

View file

@ -153,7 +153,7 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
if (isTransform) finalTransform = *g->transform;
if (g->userSpace) {
//The radius scalling is done according to the Units section:
//The radius scaling is done according to the Units section:
//https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
g->radial->cx = g->radial->cx * vBox.w;
g->radial->cy = g->radial->cy * vBox.h;
@ -213,9 +213,9 @@ static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape*
Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
finalTransform *= m;
}
if (child->transform) finalTransform = *child->transform * finalTransform;
if (child->transform) finalTransform *= *child->transform;
return _appendClipShape(loaderData, child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
return _appendClipShape(loaderData, child, shape, vBox, svgPath, identity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
}
@ -272,8 +272,7 @@ static void _applyComposition(SvgLoaderData& loaderData, Paint* paint, const Svg
if (valid) {
Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath);
comp->transform(finalTransform);
paint->composite(std::move(comp), CompositeMethod::ClipPath);
paint->clip(std::move(comp));
}
node->style->clipPath.applying = false;
@ -714,7 +713,6 @@ static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMee
static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite)
{
unique_ptr<Scene> finalScene;
auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1, isMaskWhite);
// mUseTransform = mUseTransform * mTranslate
@ -736,10 +734,10 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
auto vh = (symbol.hasViewBox ? symbol.vh : height);
Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if ((!mathEqual(width, vw) || !mathEqual(height, vh)) && vw > 0 && vh > 0) {
if ((!tvg::equal(width, vw) || !tvg::equal(height, vh)) && vw > 0 && vh > 0) {
Box box = {symbol.vx, symbol.vy, vw, vh};
mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box);
} else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) {
} else if (!tvg::zero(symbol.vx) || !tvg::zero(symbol.vy)) {
mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1};
}
@ -751,9 +749,7 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
mSceneTransform = mUseTransform * mSceneTransform;
scene->transform(mSceneTransform);
if (node->node.use.symbol->node.symbol.overflowVisible) {
finalScene = std::move(scene);
} else {
if (!node->node.use.symbol->node.symbol.overflowVisible) {
auto viewBoxClip = Shape::gen();
viewBoxClip->appendRect(0, 0, width, height, 0, 0);
@ -764,21 +760,13 @@ static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNod
}
viewBoxClip->transform(mClipTransform);
auto compositeLayer = Scene::gen();
compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath);
compositeLayer->push(std::move(scene));
auto root = Scene::gen();
root->push(std::move(compositeLayer));
finalScene = std::move(root);
scene->clip(std::move(viewBoxClip));
}
} else {
scene->transform(mUseTransform);
finalScene = std::move(scene);
}
return finalScene;
return scene;
}
@ -821,7 +809,7 @@ static unique_ptr<Text> _textBuildHelper(SvgLoaderData& loaderData, const SvgNod
Matrix textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if (node->transform) textTransform = *node->transform;
mathTranslateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize);
translateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize);
text->transform(textTransform);
//TODO: handle def values of font and size as used in a system?
@ -926,10 +914,10 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe
if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag);
if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) {
if (!tvg::equal(w, vBox.w) || !tvg::equal(h, vBox.h)) {
Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox);
docNode->transform(m);
} else if (!mathZero(vBox.x) || !mathZero(vBox.y)) {
} else if (!tvg::zero(vBox.x) || !tvg::zero(vBox.y)) {
docNode->translate(-vBox.x, -vBox.y);
}
@ -937,7 +925,7 @@ Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, Aspe
viewBoxClip->appendRect(0, 0, w, h);
auto compositeLayer = Scene::gen();
compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath);
compositeLayer->clip(std::move(viewBoxClip));
compositeLayer->push(std::move(docNode));
auto root = Scene::gen();

View file

@ -26,7 +26,7 @@
#ifdef _WIN32
#include <malloc.h>
#elif defined(__linux__)
#elif defined(__linux__) || defined(__ZEPHYR__)
#include <alloca.h>
#else
#include <stdlib.h>
@ -492,13 +492,13 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
key[0] = '\0';
val[0] = '\0';
if (next == nullptr && sep != nullptr) {
if (sep != nullptr && next == nullptr) {
memcpy(key, buf, sep - buf);
key[sep - buf] = '\0';
memcpy(val, sep + 1, end - sep - 1);
val[end - sep - 1] = '\0';
} else if (sep < next && sep != nullptr) {
} else if (sep != nullptr && sep < next) {
memcpy(key, buf, sep - buf);
key[sep - buf] = '\0';
@ -522,8 +522,9 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
}
}
if (!next) break;
buf = next + 1;
} while (next != nullptr);
} while (true);
return true;
}

View file

@ -23,8 +23,8 @@
#ifndef _TVG_SW_COMMON_H_
#define _TVG_SW_COMMON_H_
#include <algorithm>
#include "tvgCommon.h"
#include "tvgMath.h"
#include "tvgRender.h"
#define SW_CURVE_TYPE_POINT 0
@ -113,7 +113,7 @@ struct SwSpan
uint8_t coverage;
};
struct SwRleData
struct SwRle
{
SwSpan *spans;
uint32_t alloc;
@ -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;
};
@ -211,8 +211,8 @@ struct SwShape
SwOutline* outline = nullptr;
SwStroke* stroke = nullptr;
SwFill* fill = nullptr;
SwRleData* rle = nullptr;
SwRleData* strokeRle = nullptr;
SwRle* rle = nullptr;
SwRle* strokeRle = nullptr;
SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling.
bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips?
@ -221,7 +221,7 @@ struct SwShape
struct SwImage
{
SwOutline* outline = nullptr;
SwRleData* rle = nullptr;
SwRle* rle = nullptr;
union {
pixel_t* data; //system based data pointer
uint32_t* buf32; //for explicit 32bits channels
@ -244,13 +244,13 @@ typedef uint8_t(*SwAlpha)(uint8_t*); //bl
struct SwCompositor;
struct SwSurface : Surface
struct SwSurface : RenderSurface
{
SwJoin join;
SwAlpha alphas[4]; //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5
SwBlender blender = nullptr; //blender (optional)
SwCompositor* compositor = nullptr; //compositor (optional)
BlendMethod blendMethod; //blending method (uint8_t)
BlendMethod blendMethod = BlendMethod::Normal;
SwAlpha alpha(CompositeMethod method)
{
@ -262,7 +262,7 @@ struct SwSurface : Surface
{
}
SwSurface(const SwSurface* rhs) : Surface(rhs)
SwSurface(const SwSurface* rhs) : RenderSurface(rhs)
{
join = rhs->join;
memcpy(alphas, rhs->alphas, sizeof(alphas));
@ -272,7 +272,7 @@ struct SwSurface : Surface
}
};
struct SwCompositor : Compositor
struct SwCompositor : RenderCompositor
{
SwSurface* recoverSfc; //Recover surface when composition is started
SwCompositor* recoverCmp; //Recover compositor when composition is done
@ -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)
@ -379,10 +379,13 @@ static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint
static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
//A + B - 2AB
auto c1 = std::min(255, C1(s) + C1(d) - std::min(255, (C1(s) * C1(d)) << 1));
auto c2 = std::min(255, C2(s) + C2(d) - std::min(255, (C2(s) * C2(d)) << 1));
auto c3 = std::min(255, C3(s) + C3(d) - std::min(255, (C3(s) * C3(d)) << 1));
// (s + d) - (2 * s * d)
auto c1 = C1(s) + C1(d) - 2 * MULTIPLY(C1(s), C1(d));
tvg::clamp(c1, 0, 255);
auto c2 = C2(s) + C2(d) - 2 * MULTIPLY(C2(s), C2(d));
tvg::clamp(c2, 0, 255);
auto c3 = C3(s) + C3(d) - 2 * MULTIPLY(C3(s), C3(d));
tvg::clamp(c3, 0, 255);
return JOIN(255, c1, c2, c3);
}
@ -404,7 +407,6 @@ static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
// s * d
@ -414,7 +416,6 @@ static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
// if (2 * d < da) => 2 * s * d,
@ -446,10 +447,10 @@ static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
static inline uint32_t opBlendColorDodge(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
// d / (1 - s)
auto is = 0xffffffff - s;
auto c1 = (C1(is) > 0) ? (C1(d) / C1(is)) : C1(d);
auto c2 = (C2(is) > 0) ? (C2(d) / C2(is)) : C2(d);
auto c3 = (C3(is) > 0) ? (C3(d) / C3(is)) : C3(d);
s = 0xffffffff - s;
auto c1 = (C1(s) == 0) ? C1(d) : std::min(C1(d) * 255 / C1(s), 255);
auto c2 = (C2(s) == 0) ? C2(d) : std::min(C2(d) * 255 / C2(s), 255);
auto c3 = (C3(s) == 0) ? C3(d) : std::min(C3(d) * 255 / C3(s), 255);
return JOIN(255, c1, c2, c3);
}
@ -457,14 +458,17 @@ static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8
{
// 1 - (1 - d) / s
auto id = 0xffffffff - d;
auto c1 = 255 - ((C1(s) > 0) ? (C1(id) / C1(s)) : C1(id));
auto c2 = 255 - ((C2(s) > 0) ? (C2(id) / C2(s)) : C2(id));
auto c3 = 255 - ((C3(s) > 0) ? (C3(id) / C3(s)) : C3(id));
auto c1 = (C1(s) == 0) ? C1(d) : 255 - std::min(C1(id) * 255 / C1(s), 255);
auto c2 = (C2(s) == 0) ? C2(d) : 255 - std::min(C2(id) * 255 / C2(s), 255);
auto c3 = (C3(s) == 0) ? C3(d) : 255 - std::min(C3(id) * 255 / C3(s), 255);
return JOIN(255, c1, c2, c3);
}
static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
// if (s < sa), (2 * s * d)
// else (sa * da) - 2 * (da - s) * (sa - d)
auto c1 = (C1(s) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
auto c2 = (C2(s) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
auto c3 = (C3(s) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
@ -474,9 +478,9 @@ static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8
static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
//(255 - 2 * s) * (d * d) + (2 * s * b)
auto c1 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
auto c2 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
auto c3 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
auto c1 = MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + MULTIPLY(std::min(255, 2 * C1(s)), C1(d));
auto c2 = MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + MULTIPLY(std::min(255, 2 * C2(s)), C2(d));
auto c3 = MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + MULTIPLY(std::min(255, 2 * C3(s)), C3(d));
return JOIN(255, c1, c2, c3);
}
@ -490,42 +494,44 @@ SwFixed mathAtan(const SwPoint& pt);
SwFixed mathCos(SwFixed angle);
SwFixed mathSin(SwFixed angle);
void mathSplitCubic(SwPoint* base);
void mathSplitLine(SwPoint* base);
SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
SwFixed mathLength(const SwPoint& pt);
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
int mathCubicAngle(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);
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee);
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);
@ -542,13 +548,13 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
SwRleData* rleRender(const SwBBox* bbox);
void rleFree(SwRleData* rle);
void rleReset(SwRleData* rle);
void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2);
void rleClipPath(SwRleData* rle, const SwRleData* clip);
void rleClipRect(SwRleData* rle, const SwBBox* clip);
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
SwRle* rleRender(const SwBBox* bbox);
void rleFree(SwRle* rle);
void rleReset(SwRle* rle);
void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2);
bool rleClip(SwRle* rle, const SwRle* clip);
bool rleClip(SwRle* rle, const SwBBox* clip);
SwMpool* mpoolInit(uint32_t threads);
bool mpoolTerm(SwMpool* mpool);
@ -561,16 +567,33 @@ 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 rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
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, pixel_t val = 0);
void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);
void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
void rasterUnpremultiply(Surface* surface);
void rasterPremultiply(Surface* surface);
bool rasterConvertCS(Surface* surface, ColorSpace to);
void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped);
void rasterUnpremultiply(RenderSurface* surface);
void rasterPremultiply(RenderSurface* surface);
bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
uint32_t rasterUnpremultiply(uint32_t data);
bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params);
bool effectGaussianBlurRegion(RenderEffectGaussianBlur* effect);
void effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform);
bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, bool direct);
bool effectDropShadowRegion(RenderEffectDropShadow* effect);
void effectDropShadowUpdate(RenderEffectDropShadow* effect, const Matrix& transform);
void effectFillUpdate(RenderEffectFill* effect);
bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct);
void effectTintUpdate(RenderEffectTint* effect);
bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct);
void effectTritoneUpdate(RenderEffectTritone* effect);
bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct);
#endif /* _TVG_SW_COMMON_H_ */

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;
}
@ -66,15 +66,15 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f
static uint32_t _estimateAAMargin(const Fill* fdata)
{
constexpr float marginScalingFactor = 800.0f;
if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
if (fdata->type() == Type::RadialGradient) {
auto radius = P(static_cast<const RadialGradient*>(fdata))->r;
return mathZero(radius) ? 0 : static_cast<uint32_t>(marginScalingFactor / radius);
return tvg::zero(radius) ? 0 : static_cast<uint32_t>(marginScalingFactor / radius);
}
auto grad = P(static_cast<const LinearGradient*>(fdata));
Point p1 {grad->x1, grad->y1};
Point p2 {grad->x2, grad->y2};
auto length = mathLength(&p1, &p2);
return mathZero(length) ? 0 : static_cast<uint32_t>(marginScalingFactor / length);
auto len = length(&p1, &p2);
return tvg::zero(len) ? 0 : static_cast<uint32_t>(marginScalingFactor / len);
}
@ -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,49 +207,52 @@ 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 (tvg::zero(fill->linear.dx) && tvg::zero(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));
bool isTransformation = !identity((const Matrix*)(&gradTransform));
if (isTransformation) {
if (transform) gradTransform = *transform * gradTransform;
} else if (transform) {
gradTransform = *transform;
gradTransform = transform * gradTransform;
} else {
gradTransform = transform;
isTransformation = true;
}
if (isTransformation) {
Matrix invTransform;
if (!mathInverse(&gradTransform, &invTransform)) return false;
if (!inverse(&gradTransform, &invTransform)) return false;
fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23;
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 (tvg::zero(r)) {
fill->solid = true;
return true;
}
fill->radial.dr = r - fr;
fill->radial.dx = cx - fx;
@ -287,19 +295,17 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
auto gradTransform = radial->transform();
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
bool isTransformation = !identity((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) {
Matrix invTransform;
if (!mathInverse(&gradTransform, &invTransform)) return false;
if (!inverse(&gradTransform, &invTransform)) return false;
fill->radial.a11 = invTransform.e11;
fill->radial.a12 = invTransform.e12;
fill->radial.a13 = invTransform.e13;
@ -481,6 +487,7 @@ void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
auto tmp = maskOp(src, *cmp, 0);
*dst = tmp + MULTIPLY(*dst, ~tmp);
det += deltaDet;
deltaDet += deltaDeltaDet;
b += deltaB;
}
@ -547,7 +554,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
if (opacity == 255) {
if (mathZero(inc)) {
if (tvg::zero(inc)) {
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
*dst = opBlendNormal(color, *dst, alpha(cmp));
@ -578,7 +585,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
}
}
} else {
if (mathZero(inc)) {
if (tvg::zero(inc)) {
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
*dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity));
@ -620,7 +627,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
if (mathZero(inc)) {
if (tvg::zero(inc)) {
auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
for (uint32_t i = 0; i < len; ++i, ++dst) {
*dst = maskOp(src, *dst, ~src);
@ -637,7 +644,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
for (uint32_t j = 0; j < len; ++j, ++dst) {
auto src = MULTIPLY(_fixedPixel(fill, t2), a);
auto src = MULTIPLY(A(_fixedPixel(fill, t2)), a);
*dst = maskOp(src, *dst, ~src);
t2 += inc2;
}
@ -645,7 +652,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
} else {
uint32_t counter = 0;
while (counter++ < len) {
auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a);
auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
*dst = maskOp(src, *dst, ~src);
++dst;
t += inc;
@ -662,7 +669,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
if (mathZero(inc)) {
if (tvg::zero(inc)) {
auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
src = MULTIPLY(src, a);
for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
@ -709,7 +716,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
if (mathZero(inc)) {
if (tvg::zero(inc)) {
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
for (uint32_t i = 0; i < len; ++i, ++dst) {
*dst = op(color, *dst, a);
@ -749,7 +756,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
if (mathZero(inc)) {
if (tvg::zero(inc)) {
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
if (a == 255) {
for (uint32_t i = 0; i < len; ++i, ++dst) {
@ -816,25 +823,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->type() == Type::LinearGradient) {
if (!_prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform)) return false;
} else if (fdata->type() == Type::RadialGradient) {
if (!_prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform)) return false;
}
if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform);
} else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform);
}
if (ctable) return _updateColorTable(fill, fdata, surface, opacity);
return true;
}
//LOG: What type of gradient?!
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 +859,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 (tvg::equal(m.e11, 1.0f) && tvg::equal(m.e22, 1.0f) && tvg::zero(m.e12) && tvg::zero(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 (tvg::zero(transform.e12) && tvg::zero(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

@ -44,7 +44,7 @@ SwFixed mathMean(SwFixed angle1, SwFixed angle2)
}
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
{
auto d1 = base[2] - base[3];
auto d2 = base[1] - base[2];
@ -54,7 +54,7 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
if (d2.small()) {
if (d3.small()) {
angleIn = angleMid = angleOut = 0;
return true;
return -1; //ignoreable
} else {
angleIn = angleMid = angleOut = mathAtan(d3);
}
@ -90,8 +90,8 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
auto theta1 = abs(mathDiff(angleIn, angleMid));
auto theta2 = abs(mathDiff(angleMid, angleOut));
if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true;
return false;
if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return 0; //small size
return 1;
}
@ -179,7 +179,7 @@ SwFixed mathTan(SwFixed angle)
SwFixed mathAtan(const SwPoint& pt)
{
if (pt.zero()) return 0;
return SwFixed(mathAtan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
return SwFixed(tvg::atan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
}
@ -242,6 +242,15 @@ void mathSplitCubic(SwPoint* base)
}
void mathSplitLine(SwPoint* base)
{
base[2] = base[1];
base[1].x = (base[0].x + base[1].x) >> 1;
base[1].y = (base[0].y + base[1].y) >> 1;
}
SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
{
auto delta = angle2 - angle1;
@ -254,30 +263,28 @@ 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)};
}
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee)
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee)
{
clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x;
clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y;
clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x;
clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y;
clippee.max.x = (clippee.max.x < clipper.max.x) ? clippee.max.x : clipper.max.x;
clippee.max.y = (clippee.max.y < clipper.max.y) ? clippee.max.y : clipper.max.y;
clippee.min.x = (clippee.min.x > clipper.min.x) ? clippee.min.x : clipper.min.x;
clippee.min.y = (clippee.min.y > clipper.min.y) ? clippee.min.y : clipper.min.y;
//Check valid region
if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false;
if (clippee.max.x - clippee.min.x < 1 && clippee.max.y - clippee.min.y < 1) return false;
//Check boundary
if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y ||
clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false;
if (clippee.min.x >= clipper.max.x || clippee.min.y >= clipper.max.y ||
clippee.max.x <= clipper.min.x || clippee.max.y <= clipper.min.y) return false;
return true;
}
@ -305,9 +312,7 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
if (yMin > pt->y) yMin = pt->y;
if (yMax < pt->y) yMax = pt->y;
}
//Since no antialiasing is applied in the Fast Track case,
//the rasterization region has to be rearranged.
//https://github.com/Samsung/thorvg/issues/916
if (fastTrack) {
renderRegion.min.x = static_cast<SwCoord>(nearbyint(xMin / 64.0f));
renderRegion.max.x = static_cast<SwCoord>(nearbyint(xMax / 64.0f));

View file

@ -0,0 +1,589 @@
/*
* Copyright (c) 2024 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgMath.h"
#include "tvgSwCommon.h"
/************************************************************************/
/* Gaussian Blur Implementation */
/************************************************************************/
struct SwGaussianBlur
{
static constexpr int MAX_LEVEL = 3;
int level;
int kernel[MAX_LEVEL];
int extends;
};
static inline int _gaussianEdgeWrap(int end, int idx)
{
auto r = idx % (end + 1);
return (r < 0) ? (end + 1) + r : r;
}
static inline int _gaussianEdgeExtend(int end, int idx)
{
if (idx < 0) return 0;
else if (idx > end) return end;
return idx;
}
template<int border>
static inline int _gaussianRemap(int end, int idx)
{
if (border == 1) return _gaussianEdgeWrap(end, idx);
return _gaussianEdgeExtend(end, idx);
}
//TODO: SIMD OPTIMIZATION?
template<int border = 0>
static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, bool flipped)
{
if (flipped) {
src += (bbox.min.x * stride + bbox.min.y) << 2;
dst += (bbox.min.x * stride + bbox.min.y) << 2;
} else {
src += (bbox.min.y * stride + bbox.min.x) << 2;
dst += (bbox.min.y * stride + bbox.min.x) << 2;
}
auto iarr = 1.0f / (dimension + dimension + 1);
auto end = w - 1;
#pragma omp parallel for
for (int y = 0; y < h; ++y) {
auto p = y * stride;
auto i = p * 4; //current index
auto l = -(dimension + 1); //left index
auto r = dimension; //right index
int acc[4] = {0, 0, 0, 0}; //sliding accumulator
//initial accumulation
for (int x = l; x < r; ++x) {
auto id = (_gaussianRemap<border>(end, x) + p) * 4;
acc[0] += src[id++];
acc[1] += src[id++];
acc[2] += src[id++];
acc[3] += src[id];
}
//perform filtering
for (int x = 0; x < w; ++x, ++r, ++l) {
auto rid = (_gaussianRemap<border>(end, r) + p) * 4;
auto lid = (_gaussianRemap<border>(end, l) + p) * 4;
acc[0] += src[rid++] - src[lid++];
acc[1] += src[rid++] - src[lid++];
acc[2] += src[rid++] - src[lid++];
acc[3] += src[rid] - src[lid];
//ignored rounding for the performance. It should be originally: acc[idx] * iarr + 0.5f
dst[i++] = static_cast<uint8_t>(acc[0] * iarr);
dst[i++] = static_cast<uint8_t>(acc[1] * iarr);
dst[i++] = static_cast<uint8_t>(acc[2] * iarr);
dst[i++] = static_cast<uint8_t>(acc[3] * iarr);
}
}
}
static int _gaussianInit(SwGaussianBlur* data, float sigma, int quality)
{
const auto MAX_LEVEL = SwGaussianBlur::MAX_LEVEL;
if (tvg::zero(sigma)) return 0;
data->level = int(SwGaussianBlur::MAX_LEVEL * ((quality - 1) * 0.01f)) + 1;
//compute box kernel sizes
auto wl = (int) sqrt((12 * sigma / MAX_LEVEL) + 1);
if (wl % 2 == 0) --wl;
auto wu = wl + 2;
auto mi = (12 * sigma - MAX_LEVEL * wl * wl - 4 * MAX_LEVEL * wl - 3 * MAX_LEVEL) / (-4 * wl - 4);
auto m = int(mi + 0.5f);
auto extends = 0;
for (int i = 0; i < data->level; i++) {
data->kernel[i] = ((i < m ? wl : wu) - 1) / 2;
extends += data->kernel[i];
}
return extends;
}
bool effectGaussianBlurRegion(RenderEffectGaussianBlur* params)
{
//bbox region expansion for feathering
auto& region = params->extend;
auto extra = static_cast<SwGaussianBlur*>(params->rd)->extends;
if (params->direction != 2) {
region.x = -extra;
region.w = extra * 2;
}
if (params->direction != 1) {
region.y = -extra;
region.h = extra * 2;
}
return true;
}
void effectGaussianBlurUpdate(RenderEffectGaussianBlur* params, const Matrix& transform)
{
if (!params->rd) params->rd = (SwGaussianBlur*)malloc(sizeof(SwGaussianBlur));
auto rd = static_cast<SwGaussianBlur*>(params->rd);
//compute box kernel sizes
auto scale = sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12);
rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality);
//invalid
if (rd->extends == 0) {
params->valid = false;
return;
}
params->valid = true;
}
bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params)
{
auto& buffer = surface->compositor->image;
auto data = static_cast<SwGaussianBlur*>(params->rd);
auto& bbox = cmp->bbox;
auto w = (bbox.max.x - bbox.min.x);
auto h = (bbox.max.y - bbox.min.y);
auto stride = cmp->image.stride;
auto front = cmp->image.buf32;
auto back = buffer.buf32;
auto swapped = false;
TVGLOG("SW_ENGINE", "GaussianFilter region(%ld, %ld, %ld, %ld) params(%f %d %d), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->sigma, params->direction, params->border, data->level);
/* It is best to take advantage of the Gaussian blurs separable property
by dividing the process into two passes. horizontal and vertical.
We can expect fewer calculations. */
//horizontal
if (params->direction != 2) {
for (int i = 0; i < data->level; ++i) {
_gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, w, h, bbox, data->kernel[i], false);
std::swap(front, back);
swapped = !swapped;
}
}
//vertical. x/y flipping and horionztal access is pretty compatible with the memory architecture.
if (params->direction != 1) {
rasterXYFlip(front, back, stride, w, h, bbox, false);
std::swap(front, back);
for (int i = 0; i < data->level; ++i) {
_gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, h, w, bbox, data->kernel[i], true);
std::swap(front, back);
swapped = !swapped;
}
rasterXYFlip(front, back, stride, h, w, bbox, true);
std::swap(front, back);
}
if (swapped) std::swap(cmp->image.buf8, buffer.buf8);
return true;
}
/************************************************************************/
/* Drop Shadow Implementation */
/************************************************************************/
struct SwDropShadow : SwGaussianBlur
{
SwPoint offset;
};
//TODO: SIMD OPTIMIZATION?
static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const SwBBox& bbox, int32_t dimension, uint32_t color, bool flipped)
{
if (flipped) {
src += (bbox.min.x * stride + bbox.min.y);
dst += (bbox.min.x * stride + bbox.min.y);
} else {
src += (bbox.min.y * stride + bbox.min.x);
dst += (bbox.min.y * stride + bbox.min.x);
}
auto iarr = 1.0f / (dimension + dimension + 1);
auto end = w - 1;
#pragma omp parallel for
for (int y = 0; y < h; ++y) {
auto p = y * stride;
auto i = p; //current index
auto l = -(dimension + 1); //left index
auto r = dimension; //right index
int acc = 0; //sliding accumulator
//initial accumulation
for (int x = l; x < r; ++x) {
auto id = _gaussianEdgeExtend(end, x) + p;
acc += A(src[id]);
}
//perform filtering
for (int x = 0; x < w; ++x, ++r, ++l) {
auto rid = _gaussianEdgeExtend(end, r) + p;
auto lid = _gaussianEdgeExtend(end, l) + p;
acc += A(src[rid]) - A(src[lid]);
//ignored rounding for the performance. It should be originally: acc * iarr
dst[i++] = ALPHA_BLEND(color, static_cast<uint8_t>(acc * iarr));
}
}
}
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int stride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct)
{
src += (region.min.y * stride + region.min.x);
dst += (region.min.y * stride + region.min.x);
auto w = region.max.x - region.min.x;
auto h = region.max.y - region.min.y;
auto translucent = (direct || opacity < 255);
//shift offset
if (region.min.x + offset.x < 0) src -= offset.x;
else dst += offset.x;
if (region.min.y + offset.y < 0) src -= (offset.y * stride);
else dst += (offset.y * stride);
for (auto y = 0; y < h; ++y) {
if (translucent) rasterTranslucentPixel32(dst, src, w, opacity);
else rasterPixel32(dst, src, w, opacity);
src += stride;
dst += stride;
}
}
bool effectDropShadowRegion(RenderEffectDropShadow* params)
{
//bbox region expansion for feathering
auto& region = params->extend;
auto& offset = static_cast<SwDropShadow*>(params->rd)->offset;
auto extra = static_cast<SwDropShadow*>(params->rd)->extends;
region.x = -extra;
region.w = extra * 2;
region.y = -extra;
region.h = extra * 2;
region.x = std::min(region.x + (int32_t)offset.x, region.x);
region.y = std::min(region.y + (int32_t)offset.y, region.y);
region.w += abs(offset.x);
region.h += abs(offset.y);
return true;
}
void effectDropShadowUpdate(RenderEffectDropShadow* params, const Matrix& transform)
{
if (!params->rd) params->rd = (SwDropShadow*)malloc(sizeof(SwDropShadow));
auto rd = static_cast<SwDropShadow*>(params->rd);
//compute box kernel sizes
auto scale = sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12);
rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality);
//invalid
if (rd->extends == 0 || params->color[3] == 0) {
params->valid = false;
return;
}
//offset
if (params->distance > 0.0f) {
auto radian = tvg::deg2rad(90.0f - params->angle);
rd->offset = {(SwCoord)(params->distance * cosf(radian)), (SwCoord)(-1.0f * params->distance * sinf(radian))};
} else {
rd->offset = {0, 0};
}
params->valid = true;
}
//A quite same integration with effectGaussianBlur(). See it for detailed comments.
//surface[0]: the original image, to overlay it into the filtered image.
//surface[1]: temporary buffer for generating the filtered image.
bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffectDropShadow* params, bool direct)
{
//FIXME: if the body is partially visible due to clipping, the shadow also becomes partially visible.
auto data = static_cast<SwDropShadow*>(params->rd);
auto& bbox = cmp->bbox;
auto w = (bbox.max.x - bbox.min.x);
auto h = (bbox.max.y - bbox.min.y);
//outside the screen
if (abs(data->offset.x) >= w || abs(data->offset.y) >= h) return true;
SwImage* buffer[] = {&surface[0]->compositor->image, &surface[1]->compositor->image};
auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255);
auto stride = cmp->image.stride;
auto front = cmp->image.buf32;
auto back = buffer[1]->buf32;
auto opacity = direct ? MULTIPLY(params->color[3], cmp->opacity) : params->color[3];
TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level);
//saving the original image in order to overlay it into the filtered image.
_dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false);
std::swap(front, buffer[0]->buf32);
std::swap(front, back);
//horizontal
for (int i = 1; i < data->level; ++i) {
_dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[i], color, false);
std::swap(front, back);
}
//vertical
rasterXYFlip(front, back, stride, w, h, bbox, false);
std::swap(front, back);
for (int i = 0; i < data->level; ++i) {
_dropShadowFilter(back, front, stride, h, w, bbox, data->kernel[i], color, true);
std::swap(front, back);
}
rasterXYFlip(front, back, stride, h, w, bbox, true);
std::swap(cmp->image.buf32, back);
//draw to the main surface directly
if (direct) {
_dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, stride, bbox, data->offset, opacity, direct);
std::swap(cmp->image.buf32, buffer[0]->buf32);
return true;
}
//draw to the intermediate surface
rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h);
_dropShadowShift(buffer[1]->buf32, cmp->image.buf32, stride, bbox, data->offset, opacity, direct);
std::swap(cmp->image.buf32, buffer[1]->buf32);
//compositing shadow and body
auto s = buffer[0]->buf32 + (bbox.min.y * buffer[0]->stride + bbox.min.x);
auto d = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (auto y = 0; y < h; ++y) {
rasterTranslucentPixel32(d, s, w, 255);
s += buffer[0]->stride;
d += cmp->image.stride;
}
return true;
}
/************************************************************************/
/* Fill Implementation */
/************************************************************************/
void effectFillUpdate(RenderEffectFill* params)
{
params->valid = true;
}
bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct)
{
auto opacity = direct ? MULTIPLY(params->color[3], cmp->opacity) : params->color[3];
auto& bbox = cmp->bbox;
auto w = size_t(bbox.max.x - bbox.min.x);
auto h = size_t(bbox.max.y - bbox.min.y);
auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255);
TVGLOG("SW_ENGINE", "Fill region(%ld, %ld, %ld, %ld), param(%d %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->color[0], params->color[1], params->color[2], params->color[3]);
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
auto src = sbuffer;
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
auto a = MULTIPLY(opacity, A(*src));
auto tmp = ALPHA_BLEND(color, a);
*dst = tmp + ALPHA_BLEND(*dst, 255 - a);
}
dbuffer += cmp->image.stride;
sbuffer += cmp->recoverSfc->stride;
}
cmp->valid = true; //no need the subsequent composition
} else {
auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
for (size_t x = 0; x < w; ++x, ++dst) {
*dst = ALPHA_BLEND(color, MULTIPLY(opacity, A(*dst)));
}
dbuffer += cmp->image.stride;
}
}
return true;
}
/************************************************************************/
/* Tint Implementation */
/************************************************************************/
void effectTintUpdate(RenderEffectTint* params)
{
params->valid = true;
}
bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct)
{
auto& bbox = cmp->bbox;
auto w = size_t(bbox.max.x - bbox.min.x);
auto h = size_t(bbox.max.y - bbox.min.y);
auto black = cmp->recoverSfc->join(params->black[0], params->black[1], params->black[2], 255);
auto white = cmp->recoverSfc->join(params->white[0], params->white[1], params->white[2], 255);
auto opacity = cmp->opacity;
auto luma = cmp->recoverSfc->alphas[2]; //luma function
TVGLOG("SW_ENGINE", "Tint region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->black[0], params->black[1], params->black[2], params->white[0], params->white[1], params->white[2], params->intensity);
/* Tint Formula: (1 - L) * Black + L * White, where the L is Luminance. */
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
auto src = sbuffer;
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
auto tmp = rasterUnpremultiply(*src);
auto val = INTERPOLATE(INTERPOLATE(black, white, luma((uint8_t*)&tmp)), tmp, params->intensity);
*dst = INTERPOLATE(val, *dst, MULTIPLY(opacity, A(tmp)));
}
dbuffer += cmp->image.stride;
sbuffer += cmp->recoverSfc->stride;
}
cmp->valid = true; //no need the subsequent composition
} else {
auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
for (size_t x = 0; x < w; ++x, ++dst) {
auto tmp = rasterUnpremultiply(*dst);
auto val = INTERPOLATE(INTERPOLATE(black, white, luma((uint8_t*)&tmp)), tmp, params->intensity);
*dst = ALPHA_BLEND(val, A(tmp));
}
dbuffer += cmp->image.stride;
}
}
return true;
}
/************************************************************************/
/* Tritone Implementation */
/************************************************************************/
static uint32_t _trintone(uint32_t s, uint32_t m, uint32_t h, int l)
{
/* Tritone Formula:
if (L < 0.5) { (1 - 2L) * Shadow + 2L * Midtone }
else { (1 - 2(L - 0.5)) * Midtone + (2(L - 0.5)) * Highlight }
Where the L is Luminance. */
if (l < 128) {
auto a = std::min(l * 2, 255);
return ALPHA_BLEND(s, 255 - a) + ALPHA_BLEND(m, a);
} else {
auto a = 2 * std::max(0, l - 128);
return ALPHA_BLEND(m, 255 - a) + ALPHA_BLEND(h, a);
}
}
void effectTritoneUpdate(RenderEffectTritone* params)
{
params->valid = true;
}
bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct)
{
auto& bbox = cmp->bbox;
auto w = size_t(bbox.max.x - bbox.min.x);
auto h = size_t(bbox.max.y - bbox.min.y);
auto shadow = cmp->recoverSfc->join(params->shadow[0], params->shadow[1], params->shadow[2], 255);
auto midtone = cmp->recoverSfc->join(params->midtone[0], params->midtone[1], params->midtone[2], 255);
auto highlight = cmp->recoverSfc->join(params->highlight[0], params->highlight[1], params->highlight[2], 255);
auto opacity = cmp->opacity;
auto luma = cmp->recoverSfc->alphas[2]; //luma function
TVGLOG("SW_ENGINE", "Tritone region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->shadow[0], params->shadow[1], params->shadow[2], params->midtone[0], params->midtone[1], params->midtone[2], params->highlight[0], params->highlight[1], params->highlight[2]);
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
auto src = sbuffer;
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
auto tmp = rasterUnpremultiply(*src);
*dst = INTERPOLATE(_trintone(shadow, midtone, highlight, luma((uint8_t*)&tmp)), *dst, MULTIPLY(opacity, A(tmp)));
}
dbuffer += cmp->image.stride;
sbuffer += cmp->recoverSfc->stride;
}
cmp->valid = true; //no need the subsequent composition
} else {
auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
for (size_t y = 0; y < h; ++y) {
auto dst = dbuffer;
for (size_t x = 0; x < w; ++x, ++dst) {
auto tmp = rasterUnpremultiply(*dst);
*dst = ALPHA_BLEND(_trintone(shadow, midtone, highlight, luma((uint8_t*)&tmp)), A(tmp));
}
dbuffer += cmp->image.stride;
}
}
return true;
}

File diff suppressed because it is too large Load diff

View file

@ -158,7 +158,7 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u
}
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
@ -185,7 +185,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, ui
}
//2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once
//In order to avoid unneccessary avx variables declarations a check is made whether there are any iterations at all
//In order to avoid unnecessary avx variables declarations a check is made whether there are any iterations at all
uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG;
uint32_t avxFilled = 0;
if (iterations > 0) {

View file

@ -20,6 +20,38 @@
* SOFTWARE.
*/
template<typename PIXEL_T>
static void inline cRasterTranslucentPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity)
{
//TODO: 64bits faster?
if (opacity == 255) {
for (uint32_t x = 0; x < len; ++x, ++dst, ++src) {
*dst = *src + ALPHA_BLEND(*dst, IA(*src));
}
} else {
for (uint32_t x = 0; x < len; ++x, ++dst, ++src) {
auto tmp = ALPHA_BLEND(*src, opacity);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
}
template<typename PIXEL_T>
static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity)
{
//TODO: 64bits faster?
if (opacity == 255) {
for (uint32_t x = 0; x < len; ++x, ++dst, ++src) {
*dst = *src;
}
} else {
cRasterTranslucentPixels(dst, src, len, opacity);
}
}
template<typename PIXEL_T>
static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len)
{
@ -60,7 +92,7 @@ static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int
}
static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
@ -125,7 +157,7 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi
}
static bool inline cRasterABGRtoARGB(Surface* surface)
static bool inline cRasterABGRtoARGB(RenderSurface* surface)
{
TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h);
@ -156,7 +188,7 @@ static bool inline cRasterABGRtoARGB(Surface* surface)
}
static bool inline cRasterARGBtoABGR(Surface* surface)
static bool inline cRasterARGBtoABGR(RenderSurface* surface)
{
//exactly same with ABGRtoARGB
return cRasterABGRtoARGB(surface);

View file

@ -89,7 +89,7 @@ static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int3
}
static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;

View file

@ -20,6 +20,17 @@
* SOFTWARE.
*/
struct Vertex
{
Point pt;
Point uv;
};
struct Polygon
{
Vertex vertex[3];
};
struct AALine
{
int32_t x[2];
@ -64,197 +75,8 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
{
TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()");
return false;
#if 0 //Enable it when GRAYSCALE image is supported
auto maskOp = _getMaskOp(surface->compositor->method);
auto direct = _direct(surface->compositor->method);
float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf8;
int32_t sw = static_cast<int32_t>(image->stride);
int32_t sh = image->h;
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0;
float dx, u, v, iptr;
SwSpan* span = nullptr; //used only when rle based.
if (!_arrange(image, region, yStart, yEnd)) return false;
//Loop through all lines in the segment
uint32_t spanIdx = 0;
if (region) {
minx = region->min.x;
maxx = region->max.x;
} else {
span = image->rle->spans;
while (span->y < yStart) {
++span;
++spanIdx;
}
}
y = yStart;
while (y < yEnd) {
x1 = (int32_t)_xa;
x2 = (int32_t)_xb;
if (!region) {
minx = INT32_MAX;
maxx = 0;
//one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size) {
if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len;
++span;
++spanIdx;
}
}
if (x1 < minx) x1 = minx;
if (x2 > maxx) x2 = maxx;
//Anti-Aliasing frames
ay = y - aaSpans->yStart;
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
//Range allowed
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
//Perform subtexel pre-stepping on UV
dx = 1 - (_xa - x1);
u = _ua + dx * _dudx;
v = _va + dx * _dvdx;
x = x1;
auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1];
auto dst = &surface->buf8[y * surface->stride + x1];
if (opacity == 255) {
//Draw horizontal line
while (x++ < x2) {
uu = (int) u;
if (uu >= sw) continue;
vv = (int) v;
if (vv >= sh) continue;
ar = (int)(255 * (1 - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * sw) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * sw) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
}
if (direct) {
auto tmp = maskOp(px, *cmp, 0); //not use alpha
*dst = tmp + MULTIPLY(*dst, ~tmp);
++dst;
} else {
*cmp = maskOp(px, *cmp, ~px);
}
++cmp;
//Step UV horizontally
u += _dudx;
v += _dvdx;
//range over?
if ((uint32_t)v >= image->h) break;
}
} else {
//Draw horizontal line
while (x++ < x2) {
uu = (int) u;
if (uu >= sw) continue;
vv = (int) v;
if (vv >= sh) continue;
ar = (int)(255 * (1 - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * sw) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * sw) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
}
if (direct) {
auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0);
*dst = tmp + MULTIPLY(*dst, ~tmp);
++dst;
} else {
auto tmp = MULTIPLY(px, opacity);
*cmp = maskOp(tmp, *cmp, ~px);
}
++cmp;
//Step UV horizontally
u += _dudx;
v += _dvdx;
//range over?
if ((uint32_t)v >= image->h) break;
}
}
}
//Step along both edges
_xa += _dxdya;
_xb += _dxdyb;
_ua += _dudya;
_va += _dvdya;
if (!region && spanIdx >= image->rle->size) break;
++y;
}
xa = _xa;
xb = _xb;
ua = _ua;
va = _va;
return true;
#endif
}
@ -265,9 +87,8 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf32;
auto dbuf = surface->buf32;
int32_t sw = static_cast<int32_t>(image->stride);
int32_t sh = image->h;
int32_t dw = surface->stride;
int32_t sw = static_cast<int32_t>(image->w);
int32_t sh = static_cast<int32_t>(image->h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0;
@ -324,7 +145,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
u = _ua + dx * _dudx;
v = _va + dx * _dvdx;
buf = dbuf + ((y * dw) + x1);
buf = dbuf + ((y * surface->stride) + x1);
x = x1;
@ -332,32 +153,32 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
//Draw horizontal line
while (x++ < x2) {
uu = (int) u;
if (uu >= sw) continue;
vv = (int) v;
if (vv >= sh) continue;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
ar = (int)(255 * (1 - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * sw) + uu);
px = *(sbuf + (vv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * sw) + iru);
int px2 = *(sbuf + (vv * image->stride) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * sw) + uu);
int px2 = *(sbuf + (irv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * sw) + iru);
int px3 = *(sbuf + (irv * image->stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
@ -368,39 +189,37 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
//Step UV horizontally
u += _dudx;
v += _dvdx;
//range over?
if ((uint32_t)v >= image->h) break;
}
} else {
//Draw horizontal line
while (x++ < x2) {
uu = (int) u;
if (uu >= sw) continue;
vv = (int) v;
if (vv >= sh) continue;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
ar = (int)(255 * (1 - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * sw) + uu);
px = *(sbuf + (vv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * sw) + iru);
int px2 = *(sbuf + (vv * image->stride) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * sw) + uu);
int px2 = *(sbuf + (irv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * sw) + iru);
int px3 = *(sbuf + (irv * image->stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
@ -412,8 +231,6 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
//Step UV horizontally
u += _dudx;
v += _dvdx;
//range over?
if ((uint32_t)v >= image->h) break;
}
}
}
@ -442,9 +259,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf32;
auto dbuf = surface->buf32;
int32_t sw = static_cast<int32_t>(image->stride);
int32_t sh = image->h;
int32_t dw = surface->stride;
int32_t sw = static_cast<int32_t>(image->w);
int32_t sh = static_cast<int32_t>(image->h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0;
@ -506,7 +322,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
u = _ua + dx * _dudx;
v = _va + dx * _dvdx;
buf = dbuf + ((y * dw) + x1);
buf = dbuf + ((y * surface->stride) + x1);
x = x1;
@ -516,32 +332,32 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
//Draw horizontal line
while (x++ < x2) {
uu = (int) u;
if (uu >= sw) continue;
vv = (int) v;
if (vv >= sh) continue;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * sw) + uu);
px = *(sbuf + (vv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * sw) + iru);
int px2 = *(sbuf + (vv * image->stride) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * sw) + uu);
int px2 = *(sbuf + (irv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * sw) + iru);
int px3 = *(sbuf + (irv * image->stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
@ -559,8 +375,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
//Step UV horizontally
u += _dudx;
v += _dvdx;
//range over?
if ((uint32_t)v >= image->h) break;
}
} else {
//Draw horizontal line
@ -568,30 +382,30 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
uu = (int) u;
vv = (int) v;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
if (vv >= sh) continue;
px = *(sbuf + (vv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * sw) + iru);
int px2 = *(sbuf + (vv * image->stride) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * sw) + uu);
int px2 = *(sbuf + (irv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * sw) + iru);
int px3 = *(sbuf + (irv * image->stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
@ -609,8 +423,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
//Step UV horizontally
u += _dudx;
v += _dvdx;
//range over?
if ((uint32_t)v >= image->h) break;
}
}
}
@ -675,7 +487,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
//Skip poly if it's an infinitely thin line
if (mathZero(denom)) return;
if (tvg::zero(denom)) return;
denom = 1 / denom; //Reciprocal for speeding up
dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
@ -691,8 +503,8 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Determine which side of the polygon the longer edge is on
auto side = (dxdy[1] > dxdy[0]) ? true : false;
if (mathEqual(y[0], y[1])) side = x[0] > x[1];
if (mathEqual(y[1], y[2])) side = x[2] > x[1];
if (tvg::equal(y[0], y[1])) side = x[0] > x[1];
if (tvg::equal(y[1], y[2])) side = x[2] > x[1];
auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
auto compositing = _compositing(surface); //Composition required
@ -868,10 +680,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 +707,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 +839,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 +860,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 +874,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 +909,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 +917,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 +929,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 +960,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

@ -20,6 +20,9 @@
* SOFTWARE.
*/
#ifdef THORVG_SW_OPENMP_SUPPORT
#include <omp.h>
#endif
#include <algorithm>
#include "tvgMath.h"
#include "tvgSwCommon.h"
@ -38,8 +41,8 @@ struct SwTask : Task
{
SwSurface* surface = nullptr;
SwMpool* mpool = nullptr;
SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
Matrix* transform = nullptr;
SwBBox bbox; //Rendering Region
Matrix transform;
Array<RenderData> clips;
RenderUpdateFlag flags = RenderUpdateFlag::None;
uint8_t opacity;
@ -65,13 +68,8 @@ struct SwTask : Task
}
virtual void dispose() = 0;
virtual bool clip(SwRleData* target) = 0;
virtual SwRleData* rle() = 0;
virtual ~SwTask()
{
free(transform);
}
virtual bool clip(SwRle* target) = 0;
virtual ~SwTask() {}
};
@ -95,40 +93,32 @@ struct SwShapeTask : SwTask
if (!rshape->stroke) return 0.0f;
auto width = rshape->stroke->width;
if (mathZero(width)) return 0.0f;
if (tvg::zero(width)) return 0.0f;
if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f;
if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
if (tvg::zero(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));
}
bool clip(SwRleData* target) override
bool clip(SwRle* target) override
{
if (shape.fastTrack) rleClipRect(target, &bbox);
else if (shape.rle) rleClipPath(target, shape.rle);
else return false;
return true;
}
SwRleData* rle() override
{
if (!shape.rle && shape.fastTrack) {
shape.rle = rleRender(&shape.bbox);
}
return shape.rle;
if (shape.fastTrack) return rleClip(target, &bbox);
else if (shape.rle) return rleClip(target, shape.rle);
return false;
}
void run(unsigned tid) override
{
if (opacity == 0 && !clipper) return; //Invisible
//Invisible
if (opacity == 0 && !clipper) {
bbox.reset();
return;
}
auto strokeWidth = validStrokeWidth();
bool visibleFill = false;
auto clipRegion = bbox;
SwBBox renderRegion{};
auto visibleFill = false;
//This checks also for the case, if the invisible shape turned to visible by alpha.
auto prepareShape = false;
@ -140,10 +130,11 @@ struct SwShapeTask : SwTask
rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
alpha = MULTIPLY(alpha, opacity);
visibleFill = (alpha > 0 || rshape->fill);
shapeReset(&shape);
if (visibleFill || clipper) {
shapeReset(&shape);
if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) {
if (!shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) {
visibleFill = false;
renderRegion.reset();
}
}
}
@ -164,8 +155,8 @@ struct SwShapeTask : SwTask
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
if (strokeWidth > 0.0f) {
shapeResetStroke(&shape, rshape, transform);
if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err;
if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err;
if (auto fill = rshape->strokeFill()) {
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
if (ctable) shapeResetStrokeFill(&shape);
@ -184,14 +175,16 @@ struct SwShapeTask : SwTask
//Clip Path
for (auto clip = clips.begin(); clip < clips.end(); ++clip) {
auto clipper = static_cast<SwTask*>(*clip);
//Clip shape rle
if (shape.rle && !clipper->clip(shape.rle)) goto err;
//Clip stroke rle
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
if (shape.rle && !clipper->clip(shape.rle)) goto err; //Clip shape rle
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; //Clip stroke rle
}
bbox = renderRegion; //sync
return;
err:
bbox.reset();
shapeReset(&shape);
shapeDelOutline(&shape, mpool, tid);
}
@ -203,77 +196,17 @@ 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
RenderSurface* source; //Image source
bool clip(SwRleData* target) override
bool clip(SwRle* target) override
{
TVGERR("SW_ENGINE", "Image is used as ClipPath?");
return true;
}
SwRleData* rle() override
{
TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?");
return nullptr;
}
void run(unsigned tid) override
{
auto clipRegion = bbox;
@ -293,10 +226,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 +268,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 +280,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 +412,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);
}
@ -512,27 +444,18 @@ bool SwRenderer::blend(BlendMethod method)
surface->blendMethod = method;
switch (method) {
case BlendMethod::Add:
surface->blender = opBlendAdd;
break;
case BlendMethod::Screen:
surface->blender = opBlendScreen;
case BlendMethod::Normal:
surface->blender = nullptr;
break;
case BlendMethod::Multiply:
surface->blender = opBlendMultiply;
break;
case BlendMethod::Screen:
surface->blender = opBlendScreen;
break;
case BlendMethod::Overlay:
surface->blender = opBlendOverlay;
break;
case BlendMethod::Difference:
surface->blender = opBlendDifference;
break;
case BlendMethod::Exclusion:
surface->blender = opBlendExclusion;
break;
case BlendMethod::SrcOver:
surface->blender = opBlendSrcOver;
break;
case BlendMethod::Darken:
surface->blender = opBlendDarken;
break;
@ -551,7 +474,17 @@ bool SwRenderer::blend(BlendMethod method)
case BlendMethod::SoftLight:
surface->blender = opBlendSoftLight;
break;
case BlendMethod::Difference:
surface->blender = opBlendDifference;
break;
case BlendMethod::Exclusion:
surface->blender = opBlendExclusion;
break;
case BlendMethod::Add:
surface->blender = opBlendAdd;
break;
default:
TVGLOG("SW_ENGINE", "Non supported blending option = %d", (int) method);
surface->blender = nullptr;
break;
}
@ -565,7 +498,7 @@ RenderRegion SwRenderer::region(RenderData data)
}
bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity)
bool SwRenderer::beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity)
{
if (!cmp) return false;
auto p = static_cast<SwCompositor*>(cmp);
@ -603,13 +536,60 @@ bool SwRenderer::mempool(bool shared)
}
const Surface* SwRenderer::mainSurface()
const RenderSurface* SwRenderer::mainSurface()
{
return surface;
}
Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
SwSurface* SwRenderer::request(int channelSize, bool square)
{
SwSurface* cmp = nullptr;
uint32_t w, h;
if (square) {
//Same Dimensional Size is demanded for the Post Processing Fast Flipping
w = h = std::max(surface->w, surface->h);
} else {
w = surface->w;
h = surface->h;
}
//Use cached data
for (auto p = compositors.begin(); p < compositors.end(); ++p) {
auto cur = *p;
if (cur->compositor->valid && cur->compositor->image.channelSize == channelSize) {
if (w == cur->w && h == cur->h) {
cmp = *p;
break;
}
}
}
//New Composition
if (!cmp) {
//Inherits attributes from main surface
cmp = new SwSurface(surface);
cmp->compositor = new SwCompositor;
cmp->compositor->image.data = (pixel_t*)malloc(channelSize * w * h);
cmp->w = cmp->compositor->image.w = w;
cmp->h = cmp->compositor->image.h = h;
cmp->stride = cmp->compositor->image.stride = w;
cmp->compositor->image.direct = true;
cmp->compositor->valid = true;
cmp->channelSize = cmp->compositor->image.channelSize = channelSize;
compositors.push(cmp);
}
//Sync. This may have been modified by post-processing.
cmp->data = cmp->compositor->image.data;
return cmp;
}
RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags)
{
auto x = region.x;
auto y = region.y;
@ -621,35 +601,16 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
//Out of boundary
if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr;
SwSurface* cmp = nullptr;
auto reqChannelSize = CHANNEL_SIZE(cs);
//Use cached data
for (auto p = compositors.begin(); p < compositors.end(); ++p) {
if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) {
cmp = *p;
break;
}
}
//New Composition
if (!cmp) {
//Inherits attributes from main surface
cmp = new SwSurface(surface);
cmp->compositor = new SwCompositor;
//TODO: We can optimize compositor surface size from (surface->stride x surface->h) to Parameter(w x h)
cmp->compositor->image.data = (pixel_t*)malloc(reqChannelSize * surface->stride * surface->h);
cmp->channelSize = cmp->compositor->image.channelSize = reqChannelSize;
compositors.push(cmp);
}
auto cmp = request(CHANNEL_SIZE(cs), (flags & CompositionFlag::PostProcessing));
//Boundary Check
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + w > sw) w = (sw - x);
if (y + h > sh) h = (sh - y);
if (w == 0 || h == 0) return nullptr;
cmp->compositor->recoverSfc = surface;
cmp->compositor->recoverCmp = surface->compositor;
cmp->compositor->valid = false;
@ -657,16 +618,11 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
cmp->compositor->bbox.min.y = y;
cmp->compositor->bbox.max.x = x + w;
cmp->compositor->bbox.max.y = y + h;
cmp->compositor->image.stride = surface->stride;
cmp->compositor->image.w = surface->w;
cmp->compositor->image.h = surface->h;
cmp->compositor->image.direct = true;
cmp->data = cmp->compositor->image.data;
cmp->w = cmp->compositor->image.w;
cmp->h = cmp->compositor->image.h;
rasterClear(cmp, x, y, w, h);
/* TODO: Currently, only blending might work.
Blending and composition must be handled together. */
auto color = (surface->blender && !surface->compositor) ? 0x00ffffff : 0x00000000;
rasterClear(cmp, x, y, w, h, color);
//Switch render target
surface = cmp;
@ -675,26 +631,89 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
}
bool SwRenderer::endComposite(Compositor* cmp)
bool SwRenderer::endComposite(RenderCompositor* cmp)
{
if (!cmp) return false;
auto p = static_cast<SwCompositor*>(cmp);
p->valid = true;
//Recover Context
surface = p->recoverSfc;
surface->compositor = p->recoverCmp;
//only invalid (currently used) surface can be composited
if (p->valid) return true;
p->valid = true;
//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;
}
void SwRenderer::prepare(RenderEffect* effect, const Matrix& transform)
{
switch (effect->type) {
case SceneEffect::GaussianBlur: effectGaussianBlurUpdate(static_cast<RenderEffectGaussianBlur*>(effect), transform); break;
case SceneEffect::DropShadow: effectDropShadowUpdate(static_cast<RenderEffectDropShadow*>(effect), transform); break;
case SceneEffect::Fill: effectFillUpdate(static_cast<RenderEffectFill*>(effect)); break;
case SceneEffect::Tint: effectTintUpdate(static_cast<RenderEffectTint*>(effect)); break;
case SceneEffect::Tritone: effectTritoneUpdate(static_cast<RenderEffectTritone*>(effect)); break;
default: break;
}
}
bool SwRenderer::region(RenderEffect* effect)
{
switch (effect->type) {
case SceneEffect::GaussianBlur: return effectGaussianBlurRegion(static_cast<RenderEffectGaussianBlur*>(effect));
case SceneEffect::DropShadow: return effectDropShadowRegion(static_cast<RenderEffectDropShadow*>(effect));
default: return false;
}
}
bool SwRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, bool direct)
{
auto p = static_cast<SwCompositor*>(cmp);
if (p->image.channelSize != sizeof(uint32_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!");
return false;
}
switch (effect->type) {
case SceneEffect::GaussianBlur: {
return effectGaussianBlur(p, request(surface->channelSize, true), static_cast<const RenderEffectGaussianBlur*>(effect));
}
case SceneEffect::DropShadow: {
auto cmp1 = request(surface->channelSize, true);
cmp1->compositor->valid = false;
auto cmp2 = request(surface->channelSize, true);
SwSurface* surfaces[] = {cmp1, cmp2};
auto ret = effectDropShadow(p, surfaces, static_cast<const RenderEffectDropShadow*>(effect), direct);
cmp1->compositor->valid = true;
return ret;
}
case SceneEffect::Fill: {
return effectFill(p, static_cast<const RenderEffectFill*>(effect), direct);
}
case SceneEffect::Tint: {
return effectTint(p, static_cast<const RenderEffectTint*>(effect), direct);
}
case SceneEffect::Tritone: {
return effectTritone(p, static_cast<const RenderEffectTritone*>(effect), direct);
}
default: return false;
}
}
ColorSpace SwRenderer::colorSpace()
{
if (surface) return surface->cs;
@ -714,7 +733,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,29 +746,20 @@ 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;
task->mpool = mpool;
task->flags = flags;
task->bbox.min.x = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
task->bbox.min.y = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
task->bbox.max.x = mathMin(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
task->bbox.max.y = mathMin(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
task->bbox.min.x = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
task->bbox.min.y = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
task->bbox.max.x = std::min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
task->bbox.max.y = std::min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
if (!task->pushed) {
task->pushed = true;
@ -762,7 +772,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(RenderSurface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
{
//prepare task
auto task = static_cast<SwImageTask*>(data);
@ -770,33 +780,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);
@ -834,6 +823,10 @@ bool SwRenderer::init(uint32_t threads)
int32_t SwRenderer::init()
{
#ifdef THORVG_SW_OPENMP_SUPPORT
omp_set_num_threads(TaskScheduler::threads());
#endif
return initEngineCnt;
}

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(RenderSurface* 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;
@ -49,18 +48,22 @@ public:
bool viewport(const RenderRegion& vp) override;
bool blend(BlendMethod method) override;
ColorSpace colorSpace() override;
const Surface* mainSurface() override;
const RenderSurface* mainSurface() override;
bool clear() override;
bool sync() override;
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
bool mempool(bool shared);
Compositor* target(const RenderRegion& region, ColorSpace cs) override;
bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override;
bool endComposite(Compositor* cmp) override;
RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override;
bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override;
bool endComposite(RenderCompositor* cmp) override;
void clearCompositors();
void prepare(RenderEffect* effect, const Matrix& transform) override;
bool region(RenderEffect* effect) override;
bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override;
static SwRenderer* gen();
static bool init(uint32_t threads);
static int32_t init();
@ -77,7 +80,8 @@ private:
SwRenderer();
~SwRenderer();
RenderData prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
SwSurface* request(int channelSize, bool square);
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
};
}

View file

@ -188,7 +188,6 @@
* http://www.freetype.org
*/
#include <setjmp.h>
#include <limits.h>
#include <memory.h>
#include "tvgSwCommon.h"
@ -217,7 +216,7 @@ struct Cell
struct RleWorker
{
SwRleData* rle;
SwRle* rle;
SwPoint cellPos;
SwPoint cellMin;
@ -235,6 +234,7 @@ struct RleWorker
SwPoint pos;
SwPoint bezStack[32 * 3 + 1];
SwPoint lineStack[32 + 1];
int levStack[32];
SwOutline* outline;
@ -242,8 +242,6 @@ struct RleWorker
int bandSize;
int bandShoot;
jmp_buf jmpBuf;
void* buffer;
long bufferSize;
@ -297,7 +295,7 @@ static inline SwCoord HYPOT(SwPoint pt)
}
static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount)
static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord aCount)
{
x += rw.cellMin.x;
y += rw.cellMin.y;
@ -341,11 +339,11 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) {
//Clip x range
SwCoord xOver = 0;
if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
//span->len += (acount + xOver) - 1;
span->len += (acount + xOver);
//span->len += (aCount + xOver) - 1;
span->len += (aCount + xOver);
return;
}
}
@ -361,20 +359,20 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
//Clip x range
SwCoord xOver = 0;
if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
if (x < rw.cellMin.x) {
xOver -= (rw.cellMin.x - x);
x = rw.cellMin.x;
}
//Nothing to draw
if (acount + xOver <= 0) return;
if (aCount + xOver <= 0) return;
//add a span to the current list
auto span = rle->spans + rle->size;
span->x = x;
span->y = y;
span->len = (acount + xOver);
span->len = (aCount + xOver);
span->coverage = coverage;
rle->size++;
}
@ -417,7 +415,7 @@ static Cell* _findCell(RleWorker& rw)
pcell = &cell->next;
}
if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1);
if (rw.cellsCnt >= rw.maxCells) return nullptr;
auto cell = rw.cells + rw.cellsCnt++;
cell->x = x;
@ -430,17 +428,22 @@ static Cell* _findCell(RleWorker& rw)
}
static void _recordCell(RleWorker& rw)
static bool _recordCell(RleWorker& rw)
{
if (rw.area | rw.cover) {
auto cell = _findCell(rw);
if (cell == nullptr) return false;
cell->area += rw.area;
cell->cover += rw.cover;
}
return true;
}
static void _setCell(RleWorker& rw, SwPoint pos)
static bool _setCell(RleWorker& rw, SwPoint pos)
{
/* Move the cell pointer to a new position. We set the `invalid' */
/* flag to indicate that the cell isn't part of those we're interested */
@ -457,22 +460,26 @@ static void _setCell(RleWorker& rw, SwPoint pos)
pos.x -= rw.cellMin.x;
pos.y -= rw.cellMin.y;
if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
//exceptions
if (pos.x < 0) pos.x = -1;
else if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
//Are we moving to a different cell?
if (pos != rw.cellPos) {
//Record the current one if it is valid
if (!rw.invalid) _recordCell(rw);
if (!rw.invalid) {
if (!_recordCell(rw)) return false;
}
rw.area = rw.cover = 0;
rw.cellPos = pos;
}
rw.area = 0;
rw.cover = 0;
rw.cellPos = pos;
rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt);
return true;
}
static void _startCell(RleWorker& rw, SwPoint pos)
static bool _startCell(RleWorker& rw, SwPoint pos)
{
if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x;
@ -482,23 +489,27 @@ static void _startCell(RleWorker& rw, SwPoint pos)
rw.cellPos = pos - rw.cellMin;
rw.invalid = false;
_setCell(rw, pos);
return _setCell(rw, pos);
}
static void _moveTo(RleWorker& rw, const SwPoint& to)
static bool _moveTo(RleWorker& rw, const SwPoint& to)
{
//record current cell, if any */
if (!rw.invalid) _recordCell(rw);
if (!rw.invalid) {
if (!_recordCell(rw)) return false;
}
//start to a new position
_startCell(rw, TRUNC(to));
if (!_startCell(rw, TRUNC(to))) return false;
rw.pos = to;
return true;
}
static void _lineTo(RleWorker& rw, const SwPoint& to)
static bool _lineTo(RleWorker& rw, const SwPoint& to)
{
#define SW_UDIV(a, b) \
static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \
@ -510,105 +521,123 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
//vertical clipping
if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) {
rw.pos = to;
return;
return true;
}
auto diff = to - rw.pos;
auto f1 = rw.pos - SUBPIXELS(e1);
SwPoint f2;
auto line = rw.lineStack;
line[0] = to;
line[1] = rw.pos;
//inside one cell
if (e1 == e2) {
;
//any horizontal line
} else if (diff.y == 0) {
e1.x = e2.x;
_setCell(rw, e1);
} else if (diff.x == 0) {
//vertical line up
if (diff.y > 0) {
do {
f2.y = ONE_PIXEL;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * f1.x * 2;
f1.y = 0;
++e1.y;
_setCell(rw, e1);
} while(e1.y != e2.y);
//vertical line down
} else {
do {
f2.y = 0;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * f1.x * 2;
f1.y = ONE_PIXEL;
--e1.y;
_setCell(rw, e1);
} while(e1.y != e2.y);
while (true) {
auto diff = line[0] - line[1];
auto L = HYPOT(diff);
if (L > SHRT_MAX) {
mathSplitLine(line);
++line;
continue;
}
//any other line
} else {
Area prod = diff.x * f1.y - diff.y * f1.x;
e1 = TRUNC(line[1]);
e2 = TRUNC(line[0]);
/* These macros speed up repetitive divisions by replacing them
with multiplications and right shifts. */
auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
auto f1 = line[1] - SUBPIXELS(e1);
SwPoint f2;
/* The fundamental value `prod' determines which side and the */
/* exact coordinate where the line exits current cell. It is */
/* also easily updated when moving from one cell to the next. */
do {
auto px = diff.x * ONE_PIXEL;
auto py = diff.y * ONE_PIXEL;
//left
if (prod <= 0 && prod - px > 0) {
f2 = {0, SW_UDIV(-prod, -dx_r)};
prod -= py;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {ONE_PIXEL, f2.y};
--e1.x;
//up
} else if (prod - px <= 0 && prod - px + py > 0) {
prod -= px;
f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {f2.x, 0};
++e1.y;
//right
} else if (prod - px + py <= 0 && prod + py >= 0) {
prod += py;
f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {0, f2.y};
++e1.x;
//down
//inside one cell
if (e1 == e2) {
;
//any horizontal line
} else if (diff.y == 0) {
e1.x = e2.x;
if (!_setCell(rw, e1)) return false;
} else if (diff.x == 0) {
//vertical line up
if (diff.y > 0) {
do {
f2.y = ONE_PIXEL;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * f1.x * 2;
f1.y = 0;
++e1.y;
if (!_setCell(rw, e1)) return false;
} while(e1.y != e2.y);
//vertical line down
} else {
f2 = {SW_UDIV(prod, -dy_r), 0};
prod += px;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {f2.x, ONE_PIXEL};
--e1.y;
do {
f2.y = 0;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * f1.x * 2;
f1.y = ONE_PIXEL;
--e1.y;
if (!_setCell(rw, e1)) return false;
} while(e1.y != e2.y);
}
//any other line
} else {
Area prod = diff.x * f1.y - diff.y * f1.x;
_setCell(rw, e1);
/* These macros speed up repetitive divisions by replacing them
with multiplications and right shifts. */
auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
} while(e1 != e2);
/* The fundamental value `prod' determines which side and the */
/* exact coordinate where the line exits current cell. It is */
/* also easily updated when moving from one cell to the next. */
do {
auto px = diff.x * ONE_PIXEL;
auto py = diff.y * ONE_PIXEL;
//left
if (prod <= 0 && prod - px > 0) {
f2 = {0, SW_UDIV(-prod, -dx_r)};
prod -= py;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {ONE_PIXEL, f2.y};
--e1.x;
//up
} else if (prod - px <= 0 && prod - px + py > 0) {
prod -= px;
f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {f2.x, 0};
++e1.y;
//right
} else if (prod - px + py <= 0 && prod + py >= 0) {
prod += py;
f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {0, f2.y};
++e1.x;
//down
} else {
f2 = {SW_UDIV(prod, -dy_r), 0};
prod += px;
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
f1 = {f2.x, ONE_PIXEL};
--e1.y;
}
if (!_setCell(rw, e1)) return false;
} while(e1 != e2);
}
f2 = {line[0].x - SUBPIXELS(e2.x), line[0].y - SUBPIXELS(e2.y)};
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
rw.pos = line[0];
if (line-- == rw.lineStack) return true;
}
f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)};
rw.cover += (f2.y - f1.y);
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
rw.pos = to;
}
static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
static bool _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
{
auto arc = rw.bezStack;
arc[0] = to;
@ -672,14 +701,14 @@ static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2,
continue;
draw:
_lineTo(rw, arc[0]);
if (arc == rw.bezStack) return;
if (!_lineTo(rw, arc[0])) return false;
if (arc == rw.bezStack) return true;
arc -= 3;
}
}
static void _decomposeOutline(RleWorker& rw)
static bool _decomposeOutline(RleWorker& rw)
{
auto outline = rw.outline;
auto first = 0; //index of first point in contour
@ -690,48 +719,49 @@ static void _decomposeOutline(RleWorker& rw)
auto start = UPSCALE(outline->pts[first]);
auto pt = outline->pts.data + first;
auto types = outline->types.data + first;
++types;
_moveTo(rw, UPSCALE(outline->pts[first]));
if (!_moveTo(rw, UPSCALE(outline->pts[first]))) return false;
while (pt < limit) {
++pt;
++types;
//emit a single line_to
if (types[0] == SW_CURVE_TYPE_POINT) {
_lineTo(rw, UPSCALE(*pt));
++pt;
++types;
if (!_lineTo(rw, UPSCALE(*pt))) return false;
//types cubic
} else {
pt += 2;
types += 2;
pt += 3;
types += 3;
if (pt <= limit) {
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
continue;
if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]))) return false;
}
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
goto close;
else if (pt - 1 == limit) {
if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start)) return false;
}
else goto close;
}
}
_lineTo(rw, start);
close:
if (!_lineTo(rw, start)) return false;
first = last + 1;
}
return true;
}
static int _genRle(RleWorker& rw)
{
if (setjmp(rw.jmpBuf) == 0) {
_decomposeOutline(rw);
if (!rw.invalid) _recordCell(rw);
return 0;
if (!_decomposeOutline(rw)) return -1;
if (!rw.invalid) {
if (!_recordCell(rw)) return -1;
}
return -1; //lack of cell memory
return 0;
}
static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans, uint32_t outSpansCnt)
static SwSpan* _intersectSpansRegion(const SwRle *clip, const SwRle *target, SwSpan *outSpans, uint32_t outSpansCnt)
{
auto out = outSpans;
auto spans = target->spans;
@ -740,7 +770,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
auto clipEnd = clip->spans + clip->size;
while (spans < end && clipSpans < clipEnd) {
//align y cooridnates.
//align y-coordinates.
if (clipSpans->y > spans->y) {
++spans;
continue;
@ -750,7 +780,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
continue;
}
//Try clipping with all clip spans which have a same y coordinate.
//Try clipping with all clip spans which have a same y-coordinate.
auto temp = clipSpans;
while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) {
auto sx1 = spans->x;
@ -783,7 +813,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
}
static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t outSpansCnt)
static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRle *targetRle, SwSpan *outSpans, uint32_t outSpansCnt)
{
auto out = outSpans;
auto spans = targetRle->spans;
@ -822,47 +852,7 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRl
}
static SwSpan* _mergeSpansRegion(const SwRleData *clip1, const SwRleData *clip2, SwSpan *outSpans)
{
auto out = outSpans;
auto spans1 = clip1->spans;
auto end1 = clip1->spans + clip1->size;
auto spans2 = clip2->spans;
auto end2 = clip2->spans + clip2->size;
//list two spans up in y order
//TODO: Remove duplicated regions?
while (spans1 < end1 && spans2 < end2) {
while (spans1 < end1 && spans1->y <= spans2->y) {
*out = *spans1;
++spans1;
++out;
}
if (spans1 >= end1) break;
while (spans2 < end2 && spans2->y <= spans1->y) {
*out = *spans2;
++spans2;
++out;
}
}
//Leftovers
while (spans1 < end1) {
*out = *spans1;
++spans1;
++out;
}
while (spans2 < end2) {
*out = *spans2;
++spans2;
++out;
}
return out;
}
void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size)
{
free(rle->spans);
rle->spans = clippedSpans;
@ -874,7 +864,7 @@ void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
/* External Class Implementation */
/************************************************************************/
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
{
constexpr auto RENDER_POOL_SIZE = 16384L;
constexpr auto BAND_SIZE = 40;
@ -902,7 +892,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
rw.bandShoot = 0;
rw.antiAlias = antiAlias;
if (!rle) rw.rle = reinterpret_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
if (!rle) rw.rle = reinterpret_cast<SwRle*>(calloc(1, sizeof(SwRle)));
else rw.rle = rle;
//Generate RLE
@ -993,12 +983,12 @@ error:
}
SwRleData* rleRender(const SwBBox* bbox)
SwRle* rleRender(const SwBBox* bbox)
{
auto width = static_cast<uint16_t>(bbox->max.x - bbox->min.x);
auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y);
auto rle = static_cast<SwRleData*>(malloc(sizeof(SwRleData)));
auto rle = static_cast<SwRle*>(malloc(sizeof(SwRle)));
rle->spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * height));
rle->size = height;
rle->alloc = height;
@ -1015,14 +1005,14 @@ SwRleData* rleRender(const SwBBox* bbox)
}
void rleReset(SwRleData* rle)
void rleReset(SwRle* rle)
{
if (!rle) return;
rle->size = 0;
}
void rleFree(SwRleData* rle)
void rleFree(SwRle* rle)
{
if (!rle) return;
if (rle->spans) free(rle->spans);
@ -1030,65 +1020,31 @@ void rleFree(SwRleData* rle)
}
void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2)
bool rleClip(SwRle *rle, const SwRle *clip)
{
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;
if (rle->size == 0 || clip->size == 0) return false;
auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
_replaceClipSpan(rle, spans, spansEnd - spans);
TVGLOG("SW_ENGINE", "Using ClipPath!");
TVGLOG("SW_ENGINE", "Using Path Clipping!");
return true;
}
void rleClipRect(SwRleData *rle, const SwBBox* clip)
bool rleClip(SwRle *rle, const SwBBox* clip)
{
if (rle->size == 0) return;
if (rle->size == 0) return false;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
_replaceClipSpan(rle, spans, spansEnd - spans);
TVGLOG("SW_ENGINE", "Using ClipRect!");
TVGLOG("SW_ENGINE", "Using Box Clipping!");
return true;
}

View file

@ -22,7 +22,6 @@
#include "tvgSwCommon.h"
#include "tvgMath.h"
#include "tvgLines.h"
/************************************************************************/
/* Internal Class Implementation */
@ -49,7 +48,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 +59,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,12 +98,12 @@ 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);
auto len = cur.length();
if (mathZero(len)) {
if (tvg::zero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
//draw the current line fully
} else if (len <= dash.curLen) {
@ -122,7 +121,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
Line left, right;
if (dash.curLen > 0) {
len -= dash.curLen;
lineSplitAt(cur, dash.curLen, left, right);
cur.split(dash.curLen, left, right);
if (!dash.curOpGap) {
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
_outlineMoveTo(*dash.outline, &left.pt1, transform);
@ -160,13 +159,13 @@ 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);
auto len = cur.length();
//draw the current line fully
if (mathZero(len)) {
if (tvg::zero(len)) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
} else if (len <= dash.curLen) {
dash.curLen -= len;
@ -183,7 +182,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
Bezier left, right;
if (dash.curLen > 0) {
len -= dash.curLen;
bezSplitAt(cur, dash.curLen, left, right);
cur.split(dash.curLen, left, right);
if (!dash.curOpGap) {
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
_outlineMoveTo(*dash.outline, &left.start, transform);
@ -210,7 +209,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
}
_outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform);
}
if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
if (dash.curLen < 0.1f && TO_SWCOORD(len) > 1) {
//move to next dash
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
dash.curLen = dash.pattern[dash.curIdx];
@ -221,7 +220,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 +244,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) {
@ -284,7 +283,7 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32
if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
const Point* close = nullptr;
auto length = 0.0f;
auto len = 0.0f;
//must begin with moveTo
if (cmds[0] == PathCommand::MoveTo) {
@ -297,34 +296,34 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32
while (cmdCnt-- > 0) {
switch (*cmds) {
case PathCommand::Close: {
length += mathLength(pts - 1, close);
if (subpath) return length;
len += length(pts - 1, close);
if (subpath) return len;
break;
}
case PathCommand::MoveTo: {
if (subpath) return length;
if (subpath) return len;
close = pts;
++pts;
break;
}
case PathCommand::LineTo: {
length += mathLength(pts - 1, pts);
len += length(pts - 1, pts);
++pts;
break;
}
case PathCommand::CubicTo: {
length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
len += Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length();
pts += 3;
break;
}
}
++cmds;
}
return length;
return len;
}
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 +340,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);
@ -353,7 +354,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
//offset
auto patternLength = 0.0f;
uint32_t offIdx = 0;
if (!mathZero(offset)) {
if (!tvg::zero(offset)) {
for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
bool isOdd = dash.cnt % 2;
if (isOdd) patternLength *= 2;
@ -372,7 +373,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 +388,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 +437,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,12 +493,11 @@ 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;
//Keep it for Rasterization Region
shape->bbox = renderRegion;
//Check valid region
@ -575,7 +575,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 +586,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 +629,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

@ -374,8 +374,12 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to)
{
auto delta = to - stroke.center;
//a zero-length lineto is a no-op; avoid creating a spurious corner
if (delta.zero()) return;
//a zero-length lineto is a no-op
if (delta.zero()) {
//round and square caps are expected to be drawn as a dot even for zero-length lines
if (stroke.firstPt && stroke.cap != StrokeCap::Butt) _firstSubPath(stroke, 0, 0);
return;
}
/* The lineLength is used to determine the intersection of strokes outlines.
The scale needs to be reverted since the stroke width has not been scaled.
@ -441,13 +445,26 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
//initialize with current direction
angleIn = angleOut = angleMid = stroke.angleIn;
if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
auto valid = mathCubicAngle(arc, angleIn, angleMid, angleOut);
//valid size
if (valid > 0 && arc < limit) {
if (stroke.firstPt) stroke.angleIn = angleIn;
mathSplitCubic(arc);
arc += 3;
continue;
}
//ignoreable size
if (valid < 0 && arc == bezStack) {
stroke.center = to;
//round and square caps are expected to be drawn as a dot even for zero-length lines
if (stroke.firstPt && stroke.cap != StrokeCap::Butt) _firstSubPath(stroke, 0, 0);
return;
}
//small size
if (firstArc) {
firstArc = false;
//process corner if necessary
@ -662,7 +679,7 @@ static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed)
/* Determine if we need to check whether the border radius is greater
than the radius of curvature of a curve, to handle this case specially.
This is only required if bevel joins or butt caps may be created because
round & miter joins and round & square caps cover the nagative sector
round & miter joins and round & square caps cover the negative sector
created with wide strokes. */
if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt))
stroke.handleWideStrokes = true;
@ -715,7 +732,7 @@ static void _endSubPath(SwStroke& stroke)
_addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
/* now end the right subpath accordingly. The left one is rewind
and deosn't need further processing */
and doesn't need further processing */
_borderClose(right, false);
}
}
@ -805,15 +822,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);
@ -850,31 +862,25 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
//A contour cannot start with a cubic control point
if (type == SW_CURVE_TYPE_CUBIC) return false;
++types;
auto closed = outline.closed.data ? outline.closed.data[i]: false;
_beginSubPath(*stroke, start, closed);
while (pt < limit) {
++pt;
++types;
//emit a signel line_to
//emit a single line_to
if (types[0] == SW_CURVE_TYPE_POINT) {
++pt;
++types;
_lineTo(*stroke, *pt);
//types cubic
} else {
if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
pt += 2;
types += 2;
if (pt <= limit) {
_cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
continue;
}
_cubicTo(*stroke, pt[-2], pt[-1], start);
goto close;
pt += 3;
types += 3;
if (pt <= limit) _cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
else if (pt - 1 == limit) _cubicTo(*stroke, pt[-2], pt[-1], start);
else goto close;
}
}
close:

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;
};
//Use the Preorder Tree-Search
set(picture.get(), backward, reinterpret_cast<void*>(&func));
return picture;
}
Result Accessor::set(Paint* paint, function<bool(const Paint* paint, void* data)> func, void* data) noexcept
{
if (!paint || !func) return Result::InvalidArguments;
//Use the Preorder Tree-Searc
//Root
if (!func(p)) return picture;
if (!func(paint, data)) return Result::Success;
//Children
if (auto it = IteratorAccessor::iterator(p)) {
accessChildren(it, func);
if (auto it = IteratorAccessor::iterator(paint)) {
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.0f || end > 1.0f || begin > end) return Result::InvalidArguments;
auto loader = pImpl->picture->pImpl->loader;
if (!loader) return Result::InsufficientCondition;

View file

@ -36,7 +36,7 @@ using TvgBinFlag = TvgBinByte;
#define TVG_HEADER_SIZE 33 //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE
#define TVG_HEADER_SIGNATURE "ThorVG"
#define TVG_HEADER_SIGNATURE_LENGTH 6
#define TVG_HEADER_VERSION "001200" //Major 00, Minor 12, Micro 00
#define TVG_HEADER_VERSION "001500" //Major 00, Minor 15, Micro 00
#define TVG_HEADER_VERSION_LENGTH 6
#define TVG_HEADER_RESERVED_LENGTH 1 //Storing flags for extensions
#define TVG_HEADER_COMPRESS_SIZE 12 //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS

View file

@ -26,7 +26,7 @@
#include "tvgPaint.h"
enum Status : uint8_t {Synced = 0, Updating, Drawing, Damanged};
enum Status : uint8_t {Synced = 0, Updating, Drawing, Damaged};
struct Canvas::Impl
{
@ -42,7 +42,7 @@ struct Canvas::Impl
~Impl()
{
//make it sure any deffered jobs
//make it sure any deferred jobs
renderer->sync();
renderer->clear();
@ -61,7 +61,7 @@ struct Canvas::Impl
Result push(unique_ptr<Paint> paint)
{
//You can not push paints during rendering.
//You cannot push paints during rendering.
if (status == Status::Drawing) return Result::InsufficientCondition;
auto p = paint.release();
@ -91,13 +91,15 @@ struct Canvas::Impl
Array<RenderData> clips;
auto flag = RenderUpdateFlag::None;
if (status == Status::Damanged || force) flag = RenderUpdateFlag::All;
if (status == Status::Damaged || 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;
@ -106,7 +108,7 @@ struct Canvas::Impl
Result draw()
{
if (status == Status::Damanged) update(nullptr, false);
if (status == Status::Damaged) update(nullptr, false);
if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition;
bool rendered = false;
@ -122,7 +124,7 @@ struct Canvas::Impl
Result sync()
{
if (status == Status::Synced || status == Status::Damanged) return Result::InsufficientCondition;
if (status == Status::Synced || status == Status::Damaged) return Result::InsufficientCondition;
if (renderer->sync()) {
status = Status::Synced;
@ -134,7 +136,7 @@ struct Canvas::Impl
Result viewport(int32_t x, int32_t y, int32_t w, int32_t h)
{
if (status != Status::Damanged && status != Status::Synced) return Result::InsufficientCondition;
if (status != Status::Damaged && status != Status::Synced) return Result::InsufficientCondition;
RenderRegion val = {x, y, w, h};
//intersect if the target buffer is already set.
@ -145,7 +147,7 @@ struct Canvas::Impl
if (vport == val) return Result::Success;
renderer->viewport(val);
vport = val;
status = Status::Damanged;
status = Status::Damaged;
return Result::Success;
}
};

View file

@ -54,15 +54,6 @@ using namespace tvg;
#define strdup _strdup
#endif
//TVG class identifier values
#define TVG_CLASS_ID_UNDEFINED 0
#define TVG_CLASS_ID_SHAPE 1
#define TVG_CLASS_ID_SCENE 2
#define TVG_CLASS_ID_PICTURE 3
#define TVG_CLASS_ID_LINEAR 4
#define TVG_CLASS_ID_RADIAL 5
#define TVG_CLASS_ID_TEXT 6
enum class FileType { Png = 0, Jpg, Webp, Tvg, Svg, Lottie, Ttf, Raw, Gif, Unknown };
using Size = Point;

View file

@ -155,15 +155,14 @@ Fill* Fill::duplicate() const noexcept
}
uint32_t Fill::identifier() const noexcept
TVG_DEPRECATED uint32_t Fill::identifier() const noexcept
{
return pImpl->id;
return (uint32_t) type();
}
RadialGradient::RadialGradient():pImpl(new Impl())
{
Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
}
@ -196,15 +195,20 @@ unique_ptr<RadialGradient> RadialGradient::gen() noexcept
}
uint32_t RadialGradient::identifier() noexcept
TVG_DEPRECATED uint32_t RadialGradient::identifier() noexcept
{
return TVG_CLASS_ID_RADIAL;
return (uint32_t) Type::RadialGradient;
}
Type RadialGradient::type() const noexcept
{
return Type::RadialGradient;
}
LinearGradient::LinearGradient():pImpl(new Impl())
{
Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
}
@ -243,8 +247,13 @@ unique_ptr<LinearGradient> LinearGradient::gen() noexcept
}
uint32_t LinearGradient::identifier() noexcept
TVG_DEPRECATED uint32_t LinearGradient::identifier() noexcept
{
return TVG_CLASS_ID_LINEAR;
return (uint32_t) Type::LinearGradient;
}
Type LinearGradient::type() const noexcept
{
return Type::LinearGradient;
}

View file

@ -55,7 +55,6 @@ struct Fill::Impl
uint32_t cnt = 0;
FillSpread spread;
DuplicateMethod<Fill>* dup = nullptr;
uint8_t id;
~Impl()
{
@ -77,7 +76,7 @@ struct Fill::Impl
ret->pImpl->cnt = cnt;
ret->pImpl->spread = spread;
ret->pImpl->colorStops = static_cast<ColorStop*>(malloc(sizeof(ColorStop) * cnt));
memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
if (cnt > 0) memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
if (transform) {
ret->pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
*ret->pImpl->transform = *transform;

View file

@ -62,7 +62,7 @@ GlCanvas::~GlCanvas()
Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
{
#ifdef THORVG_GL_RASTER_SUPPORT
if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) {
return Result::InsufficientCondition;
}
@ -75,7 +75,7 @@ Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
renderer->viewport(Canvas::pImpl->vport);
//Paints must be updated again with this new target.
Canvas::pImpl->status = Status::Damanged;
Canvas::pImpl->status = Status::Damaged;
return Result::Success;
#endif

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

@ -80,14 +80,14 @@ struct ImageLoader : LoadModule
static ColorSpace cs; //desired value
float w = 0, h = 0; //default image size
Surface surface;
RenderSurface surface;
ImageLoader(FileType type) : LoadModule(type) {}
virtual bool animatable() { return false; } //true if this loader supports animation.
virtual Paint* paint() { return nullptr; }
virtual Surface* bitmap()
virtual RenderSurface* bitmap()
{
if (surface.data) return &surface;
return nullptr;
@ -101,7 +101,8 @@ struct FontLoader : LoadModule
FontLoader(FileType type) : LoadModule(type) {}
virtual bool request(Shape* shape, char* text, bool italic = false) = 0;
virtual bool request(Shape* shape, char* text) = 0;
virtual bool transform(Paint* paint, float fontSize, bool italic) = 0;
};
#endif //_TVG_LOAD_MODULE_H_

View file

@ -172,6 +172,7 @@ static LoadModule* _find(FileType type)
}
#ifdef THORVG_FILE_IO_SUPPORT
static LoadModule* _findByPath(const string& path)
{
auto ext = path.substr(path.find_last_of(".") + 1);
@ -185,6 +186,7 @@ static LoadModule* _findByPath(const string& path)
if (!ext.compare("otf") || !ext.compare("otc")) return _find(FileType::Ttf);
return nullptr;
}
#endif
static FileType _convert(const string& mimeType)
@ -292,12 +294,13 @@ bool LoaderMgr::retrieve(LoadModule* loader)
LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
{
#ifdef THORVG_FILE_IO_SUPPORT
*invalid = false;
//TODO: lottie is not sharable.
//TODO: svg & lottie is not sharable.
auto allowCache = true;
auto ext = path.substr(path.find_last_of(".") + 1);
if (!ext.compare("json")) allowCache = false;
if (!ext.compare("svg") || !ext.compare("json")) allowCache = false;
if (allowCache) {
if (auto loader = _findFromCache(path)) return loader;
@ -317,7 +320,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
}
delete(loader);
}
//Unkown MimeType. Try with the candidates in the order
//Unknown MimeType. Try with the candidates in the order
for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
if (auto loader = _find(static_cast<FileType>(i))) {
if (loader->open(path)) {
@ -335,6 +338,7 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
}
}
*invalid = true;
#endif
return nullptr;
}
@ -392,7 +396,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
}
}
}
//Unkown MimeType. Try with the candidates in the order
//Unknown MimeType. Try with the candidates in the order
for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
auto loader = _find(static_cast<FileType>(i));
if (loader) {

View file

@ -32,16 +32,16 @@
/************************************************************************/
#define PAINT_METHOD(ret, METHOD) \
switch (id) { \
case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \
case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \
case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \
case TVG_CLASS_ID_TEXT: ret = P((Text*)paint)->METHOD; break; \
switch (paint->type()) { \
case Type::Shape: ret = P((Shape*)paint)->METHOD; break; \
case Type::Scene: ret = P((Scene*)paint)->METHOD; break; \
case Type::Picture: ret = P((Picture*)paint)->METHOD; break; \
case Type::Text: ret = P((Text*)paint)->METHOD; break; \
default: ret = {}; \
}
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 ((!rightAngle(pm) || skewed(pm))) tryClip = true;
if ((!rightAngle(rm) || skewed(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;
@ -103,23 +102,17 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Ren
auto pt3 = pts + 2;
auto pt4 = pts + 3;
if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) ||
(mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) {
if ((tvg::equal(pt1->x, pt2->x) && tvg::equal(pt2->y, pt3->y) && tvg::equal(pt3->x, pt4->x) && tvg::equal(pt1->y, pt4->y)) ||
(tvg::equal(pt2->x, pt3->x) && tvg::equal(pt1->y, pt2->y) && tvg::equal(pt1->x, pt4->x) && tvg::equal(pt3->y, pt4->y))) {
RenderRegion after;
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,21 +151,20 @@ 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;
if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method);
if (clipper) ret->pImpl->clip(clipper->duplicate());
return ret;
}
@ -180,14 +172,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 (tvg::equal(degree, tr.degree)) return true;
tr.degree = degree;
renderFlag |= RenderUpdateFlag::Transform;
return true;
@ -196,14 +183,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 (tvg::equal(factor, tr.scale)) return true;
tr.scale = factor;
renderFlag |= RenderUpdateFlag::Transform;
return true;
@ -212,15 +194,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 (tvg::equal(x, tr.m.e13) && tvg::equal(y, tr.m.e23)) return true;
tr.m.e13 = x;
tr.m.e23 = y;
renderFlag |= RenderUpdateFlag::Transform;
return true;
@ -229,17 +206,17 @@ bool Paint::Impl::translate(float x, float y)
bool Paint::Impl::render(RenderMethod* renderer)
{
Compositor* cmp = nullptr;
if (opacity == 0) return true;
/* Note: only ClipPath is processed in update() step.
Create a composition image. */
if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
RenderCompositor* cmp = nullptr;
if (compData && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
RenderRegion region;
PAINT_METHOD(region, bounds(renderer));
if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer));
if (region.w == 0 || region.h == 0) return true;
cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method), CompositionFlag::Masking);
if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) {
compData->target->pImpl->render(renderer);
}
@ -247,8 +224,6 @@ bool Paint::Impl::render(RenderMethod* renderer)
if (cmp) renderer->beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
renderer->blend(blendMethod);
bool ret;
PAINT_METHOD(ret, render(renderer));
@ -258,7 +233,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,72 +241,79 @@ 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
RenderRegion viewport;
Result compFastTrack = Result::InsufficientCondition;
bool childClipper = false;
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. */
auto tryFastTrack = false;
if (target->identifier() == TVG_CLASS_ID_SHAPE) {
if (method == CompositeMethod::ClipPath) tryFastTrack = true;
else {
auto shape = static_cast<Shape*>(target);
uint8_t a;
shape->fillColor(nullptr, nullptr, nullptr, &a);
//no gradient fill & no compositions of the composition target.
if (!shape->fill() && !(PP(shape)->compData)) {
if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true;
else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true;
}
}
if (tryFastTrack) {
viewport = renderer->viewport();
if ((compFastTrack = _compFastTrack(renderer, target, pTransform, target->pImpl->rTransform, viewport)) == Result::Success) {
target->pImpl->ctxFlag |= ContextFlag::FastTrack;
/* If the transformation has no rotational factors and the Alpha(InvAlpha)Masking involves a simple rectangle,
we can optimize by using the viewport instead of the regular AlphaMasking sequence for improved performance. */
if (target->type() == Type::Shape) {
auto shape = static_cast<Shape*>(target);
uint8_t a;
shape->fillColor(nullptr, nullptr, nullptr, &a);
//no gradient fill & no compositions of the composition target.
if (!shape->fill() && !(PP(shape)->compData)) {
if ((method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) || (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0))) {
viewport = renderer->viewport();
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);
if (childClipper) clips.push(trd);
trd = P(target)->update(renderer, pm, clips, 255, pFlag, false);
}
}
/* 2. Main Update */
/* 2. Clipping */
if (this->clipper) {
P(this->clipper)->ctxFlag &= ~ContextFlag::FastTrack; //reset
viewport = renderer->viewport();
/* TODO: Intersect the clipper's clipper, if both are FastTrack.
Update the subsequent clipper first and check its ctxFlag. */
if (!P(this->clipper)->clipper && (compFastTrack = _compFastTrack(renderer, this->clipper, pm, viewport)) == Result::Success) {
P(this->clipper)->ctxFlag |= ContextFlag::FastTrack;
}
if (compFastTrack == Result::InsufficientCondition) {
trd = P(this->clipper)->update(renderer, pm, clips, 255, pFlag, true);
clips.push(trd);
}
}
/* 3. Main Update */
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
renderFlag = RenderUpdateFlag::None;
opacity = MULTIPLY(opacity, this->opacity);
RenderData rd = nullptr;
RenderTransform outTransform(pTransform, rTransform);
PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper));
/* 3. Composition Post Processing */
tr.cm = pm * tr.m;
PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
/* 4. Composition Post Processing */
if (compFastTrack == Result::Success) renderer->viewport(viewport);
else if (childClipper) clips.pop();
else if (this->clipper) clips.pop();
return rd;
}
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 || identity(&m)) {
PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
return ret;
}
@ -355,7 +337,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 +354,32 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
}
void Paint::Impl::reset()
{
if (clipper) {
delete(clipper);
clipper = nullptr;
}
if (compData) {
if (P(compData->target)->unref() == 0) delete(compData->target);
free(compData);
compData = nullptr;
}
tvg::identity(&tr.m);
tr.degree = 0.0f;
tr.scale = 1.0f;
tr.overriding = false;
blendMethod = BlendMethod::Normal;
renderFlag = RenderUpdateFlag::None;
ctxFlag = ContextFlag::Default;
opacity = 255;
paint->id = 0;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
@ -417,9 +425,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 +435,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;
}
@ -442,10 +448,27 @@ Paint* Paint::duplicate() const noexcept
}
Result Paint::clip(std::unique_ptr<Paint> clipper) noexcept
{
auto p = clipper.release();
if (p && p->type() != Type::Shape) {
TVGERR("RENDERER", "Clipping only supports the Shape!");
return Result::NonSupport;
}
pImpl->clip(p);
return Result::Success;
}
Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
{
//TODO: remove. Keep this for the backward compatibility
if (target && method == CompositeMethod::ClipPath) return clip(std::move(target));
auto p = target.release();
if (pImpl->composite(this, p, method)) return Result::Success;
delete(p);
return Result::InvalidArguments;
}
@ -457,6 +480,11 @@ CompositeMethod Paint::composite(const Paint** target) const noexcept
if (target) *target = pImpl->compData->target;
return pImpl->compData->method;
} else {
//TODO: remove. Keep this for the backward compatibility
if (pImpl->clipper) {
if (target) *target = pImpl->clipper;
return CompositeMethod::ClipPath;
}
if (target) *target = nullptr;
return CompositeMethod::None;
}
@ -480,14 +508,17 @@ uint8_t Paint::opacity() const noexcept
}
uint32_t Paint::identifier() const noexcept
TVG_DEPRECATED uint32_t Paint::identifier() const noexcept
{
return pImpl->id;
return (uint32_t) type();
}
Result Paint::blend(BlendMethod method) const noexcept
Result Paint::blend(BlendMethod method) noexcept
{
//TODO: Remove later
if (method == BlendMethod::Hue || method == BlendMethod::Saturation || method == BlendMethod::Color || method == BlendMethod::Luminosity || method == BlendMethod::HardMix) return Result::NonSupport;
if (pImpl->blendMethod != method) {
pImpl->blendMethod = method;
pImpl->renderFlag |= RenderUpdateFlag::Blend;
@ -495,9 +526,3 @@ Result Paint::blend(BlendMethod method) const noexcept
return Result::Success;
}
BlendMethod Paint::blend() const noexcept
{
return pImpl->blendMethod;
}

View file

@ -28,7 +28,7 @@
namespace tvg
{
enum ContextFlag : uint8_t {Invalid = 0, FastTrack = 1};
enum ContextFlag : uint8_t {Default = 0, FastTrack = 1};
struct Iterator
{
@ -48,17 +48,40 @@ namespace tvg
struct Paint::Impl
{
Paint* paint = nullptr;
RenderTransform* rTransform = nullptr;
Composite* compData = nullptr;
Paint* clipper = 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;
struct {
Matrix m; //input matrix
Matrix cm; //multipled parents matrix
float degree; //rotation degree
float scale; //scale factor
bool overriding; //user transform?
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;
tvg::scale(&m, scale, scale);
tvg::rotate(&m, degree);
}
} tr;
BlendMethod blendMethod;
uint8_t renderFlag;
uint8_t ctxFlag;
uint8_t opacity;
uint8_t refCnt = 0; //reference count
Impl(Paint* pnt) : paint(pnt) {}
Impl(Paint* pnt) : paint(pnt)
{
reset();
}
~Impl()
{
@ -66,7 +89,7 @@ namespace tvg
if (P(compData->target)->unref() == 0) delete(compData->target);
free(compData);
}
delete(rTransform);
if (clipper && P(clipper)->unref() == 0) delete(clipper);
if (renderer && (renderer->unref() == 0)) delete(renderer);
}
@ -84,23 +107,33 @@ namespace tvg
bool transform(const Matrix& m)
{
if (!rTransform) {
if (mathIdentity(&m)) return true;
rTransform = new RenderTransform();
}
rTransform->override(m);
if (&tr.m != &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;
//update transform
if (renderFlag & RenderUpdateFlag::Transform) tr.update();
if (origin) return tr.cm;
return tr.m;
}
void clip(Paint* clp)
{
if (this->clipper) {
P(this->clipper)->unref();
if (this->clipper != clp && P(this->clipper)->refCnt == 0) {
delete(this->clipper);
}
}
return nullptr;
this->clipper = clp;
if (!clp) return;
P(clipper)->ref();
}
bool composite(Paint* source, Paint* target, CompositeMethod method)
@ -135,10 +168,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

@ -20,6 +20,7 @@
* SOFTWARE.
*/
#include "tvgPaint.h"
#include "tvgPicture.h"
/************************************************************************/
@ -55,29 +56,33 @@ RenderUpdateFlag Picture::Impl::load()
}
bool Picture::Impl::needComposition(uint8_t opacity)
void Picture::Impl::queryComposition(uint8_t opacity)
{
cFlag = CompositionFlag::Invalid;
//In this case, paint(scene) would try composition itself.
if (opacity < 255) return false;
if (opacity < 255) return;
//Composition test
const Paint* target;
auto method = picture->composite(&target);
if (!target || method == tvg::CompositeMethod::ClipPath) return false;
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
if (!target || method == tvg::CompositeMethod::ClipPath) return;
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return;
return true;
cFlag = CompositionFlag::Opacity;
}
bool Picture::Impl::render(RenderMethod* renderer)
{
bool ret = false;
renderer->blend(PP(picture)->blendMethod);
if (surface) return renderer->renderImage(rd);
else if (paint) {
Compositor* cmp = nullptr;
if (needComp) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
RenderCompositor* cmp = nullptr;
if (cFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(cFlag));
renderer->beginComposite(cmp, CompositeMethod::None, 255);
}
ret = paint->pImpl->render(renderer);
@ -104,21 +109,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.
@ -147,7 +137,6 @@ Result Picture::Impl::load(ImageLoader* loader)
Picture::Picture() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
}
@ -163,17 +152,28 @@ unique_ptr<Picture> Picture::gen() noexcept
}
uint32_t Picture::identifier() noexcept
TVG_DEPRECATED uint32_t Picture::identifier() noexcept
{
return TVG_CLASS_ID_PICTURE;
return (uint32_t) Type::Picture;
}
Type Picture::type() const noexcept
{
return Type::Picture;
}
Result Picture::load(const std::string& path) noexcept
{
#ifdef THORVG_FILE_IO_SUPPORT
if (path.empty()) return Result::InvalidArguments;
return pImpl->load(path);
#else
TVGLOG("RENDERER", "FILE IO is disabled!");
return Result::NonSupport;
#endif
}
@ -215,18 +215,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

@ -60,16 +60,14 @@ struct Picture::Impl
ImageLoader* loader = nullptr;
Paint* paint = nullptr; //vector picture uses
Surface* surface = nullptr; //bitmap picture uses
RenderSurface* surface = nullptr; //bitmap picture uses
RenderData rd = nullptr; //engine data
float w = 0, h = 0;
RenderMesh rm; //mesh data
Picture* picture = nullptr;
uint8_t cFlag = CompositionFlag::Invalid;
bool resizing = false;
bool needComp = false; //need composition
RenderTransform resizeTransform(const RenderTransform* pTransform);
bool needComposition(uint8_t opacity);
void queryComposition(uint8_t opacity);
bool render(RenderMethod* renderer);
bool size(float w, float h);
RenderRegion bounds(RenderMethod* renderer);
@ -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);
queryComposition(opacity);
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,8 @@
#ifndef _TVG_RENDER_H_
#define _TVG_RENDER_H_
#include <math.h>
#include <cstdarg>
#include "tvgCommon.h"
#include "tvgArray.h"
#include "tvgLock.h"
@ -34,10 +36,10 @@ using RenderData = void*;
using pixel_t = uint32_t;
enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
enum CompositionFlag : uint8_t {Invalid = 0, Opacity = 1, Blending = 2, Masking = 4, PostProcessing = 8}; //Composition Purpose
struct Surface;
enum ColorSpace
//TODO: Move this in public header unifying with SwCanvas::Colorspace
enum ColorSpace : uint8_t
{
ABGR8888 = 0, //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
ARGB8888, //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
@ -47,7 +49,7 @@ enum ColorSpace
Unsupported //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace.
};
struct Surface
struct RenderSurface
{
union {
pixel_t* data = nullptr; //system based data pointer
@ -61,11 +63,11 @@ struct Surface
uint8_t channelSize = 0;
bool premultiplied = false; //Alpha-premultiplied
Surface()
RenderSurface()
{
}
Surface(const Surface* rhs)
RenderSurface(const RenderSurface* rhs)
{
data = rhs->data;
stride = rhs->stride;
@ -79,21 +81,10 @@ struct Surface
};
struct Compositor
struct RenderCompositor
{
CompositeMethod method;
uint8_t opacity;
};
struct RenderMesh
{
Polygon* triangles = nullptr;
uint32_t triangleCnt = 0;
~RenderMesh()
{
free(triangles);
}
uint8_t opacity;
};
struct RenderRegion
@ -110,24 +101,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 +120,58 @@ 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;
dashOffset = rhs.dashOffset;
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 +216,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;
}
@ -236,11 +261,126 @@ struct RenderShape
float strokeMiterlimit() const
{
if (!stroke) return 4.0f;
return stroke->miterlimit;;
}
};
struct RenderEffect
{
RenderData rd = nullptr;
RenderRegion extend = {0, 0, 0, 0};
SceneEffect type;
bool valid = false;
virtual ~RenderEffect()
{
free(rd);
}
};
struct RenderEffectGaussianBlur : RenderEffect
{
float sigma;
uint8_t direction; //0: both, 1: horizontal, 2: vertical
uint8_t border; //0: duplicate, 1: wrap
uint8_t quality; //0 ~ 100 (optional)
static RenderEffectGaussianBlur* gen(va_list& args)
{
auto inst = new RenderEffectGaussianBlur;
inst->sigma = std::max((float) va_arg(args, double), 0.0f);
inst->direction = std::min(va_arg(args, int), 2);
inst->border = std::min(va_arg(args, int), 1);
inst->quality = std::min(va_arg(args, int), 100);
inst->type = SceneEffect::GaussianBlur;
return inst;
}
};
struct RenderEffectDropShadow : RenderEffect
{
uint8_t color[4]; //rgba
float angle;
float distance;
float sigma;
uint8_t quality; //0 ~ 100 (optional)
static RenderEffectDropShadow* gen(va_list& args)
{
auto inst = new RenderEffectDropShadow;
inst->color[0] = va_arg(args, int);
inst->color[1] = va_arg(args, int);
inst->color[2] = va_arg(args, int);
inst->color[3] = va_arg(args, int);
inst->angle = (float) va_arg(args, double);
inst->distance = (float) va_arg(args, double);
inst->sigma = std::max((float) va_arg(args, double), 0.0f);
inst->quality = std::min(va_arg(args, int), 100);
inst->type = SceneEffect::DropShadow;
return inst;
}
};
struct RenderEffectFill : RenderEffect
{
uint8_t color[4]; //rgba
static RenderEffectFill* gen(va_list& args)
{
auto inst = new RenderEffectFill;
inst->color[0] = va_arg(args, int);
inst->color[1] = va_arg(args, int);
inst->color[2] = va_arg(args, int);
inst->color[3] = va_arg(args, int);
inst->type = SceneEffect::Fill;
return inst;
}
};
struct RenderEffectTint : RenderEffect
{
uint8_t black[3]; //rgb
uint8_t white[3]; //rgb
uint8_t intensity; //0 - 255
static RenderEffectTint* gen(va_list& args)
{
auto inst = new RenderEffectTint;
inst->black[0] = va_arg(args, int);
inst->black[1] = va_arg(args, int);
inst->black[2] = va_arg(args, int);
inst->white[0] = va_arg(args, int);
inst->white[1] = va_arg(args, int);
inst->white[2] = va_arg(args, int);
inst->intensity = (uint8_t)(va_arg(args, double) * 2.55);
inst->type = SceneEffect::Tint;
return inst;
}
};
struct RenderEffectTritone : RenderEffect
{
uint8_t shadow[3]; //rgb
uint8_t midtone[3]; //rgb
uint8_t highlight[3]; //rgb
static RenderEffectTritone* gen(va_list& args)
{
auto inst = new RenderEffectTritone;
inst->shadow[0] = va_arg(args, int);
inst->shadow[1] = va_arg(args, int);
inst->shadow[2] = va_arg(args, int);
inst->midtone[0] = va_arg(args, int);
inst->midtone[1] = va_arg(args, int);
inst->midtone[2] = va_arg(args, int);
inst->highlight[0] = va_arg(args, int);
inst->highlight[1] = va_arg(args, int);
inst->highlight[2] = va_arg(args, int);
inst->type = SceneEffect::Tritone;
return inst;
}
};
class RenderMethod
{
private:
@ -252,9 +392,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(RenderSurface* 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;
@ -265,14 +404,18 @@ public:
virtual bool viewport(const RenderRegion& vp) = 0;
virtual bool blend(BlendMethod method) = 0;
virtual ColorSpace colorSpace() = 0;
virtual const Surface* mainSurface() = 0;
virtual const RenderSurface* mainSurface() = 0;
virtual bool clear() = 0;
virtual bool sync() = 0;
virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0;
virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
virtual bool endComposite(Compositor* cmp) = 0;
virtual RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) = 0;
virtual bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
virtual bool endComposite(RenderCompositor* cmp) = 0;
virtual void prepare(RenderEffect* effect, const Matrix& transform) = 0;
virtual bool region(RenderEffect* effect) = 0;
virtual bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) = 0;
};
static inline bool MASK_REGION_MERGING(CompositeMethod method)
@ -288,6 +431,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 +466,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:
@ -337,7 +484,6 @@ static inline uint8_t MULTIPLY(uint8_t c, uint8_t a)
return (((c) * (a) + 0xff) >> 8);
}
}
#endif //_TVG_RENDER_H_

View file

@ -20,6 +20,7 @@
* SOFTWARE.
*/
#include <cstring>
#include "tvgCommon.h"
#include "tvgSaveModule.h"
#include "tvgPaint.h"
@ -122,7 +123,7 @@ Result Saver::save(std::unique_ptr<Paint> paint, const string& path, bool compre
auto p = paint.release();
if (!p) return Result::MemoryCorruption;
//Already on saving an other resource.
//Already on saving another resource.
if (pImpl->saveModule) {
if (P(p)->refCnt == 0) delete(p);
return Result::InsufficientCondition;
@ -160,12 +161,12 @@ Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t
//animation holds the picture, it must be 1 at the bottom.
auto remove = PP(a->picture())->refCnt <= 1 ? true : false;
if (mathZero(a->totalFrame())) {
if (tvg::zero(a->totalFrame())) {
if (remove) delete(a);
return Result::InsufficientCondition;
}
//Already on saving an other resource.
//Already on saving another resource.
if (pImpl->saveModule) {
if (remove) delete(a);
return Result::InsufficientCondition;

View file

@ -20,15 +20,32 @@
* SOFTWARE.
*/
#include <cstdarg>
#include "tvgScene.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
Result Scene::Impl::resetEffects()
{
if (effects) {
for (auto e = effects->begin(); e < effects->end(); ++e) {
delete(*e);
}
delete(effects);
effects = nullptr;
}
return Result::Success;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Scene::Scene() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_SCENE;
}
@ -44,9 +61,15 @@ unique_ptr<Scene> Scene::gen() noexcept
}
uint32_t Scene::identifier() noexcept
TVG_DEPRECATED uint32_t Scene::identifier() noexcept
{
return TVG_CLASS_ID_SCENE;
return (uint32_t) Type::Scene;
}
Type Scene::type() const noexcept
{
return Type::Scene;
}
@ -54,7 +77,11 @@ Result Scene::push(unique_ptr<Paint> paint) noexcept
{
auto p = paint.release();
if (!p) return Result::MemoryCorruption;
PP(p)->ref();
P(p)->ref();
//Relocated the paint to the current scene space
P(p)->renderFlag |= RenderUpdateFlag::Transform;
pImpl->paints.push_back(p);
return Result::Success;
@ -79,3 +106,46 @@ list<Paint*>& Scene::paints() noexcept
{
return pImpl->paints;
}
Result Scene::push(SceneEffect effect, ...) noexcept
{
if (effect == SceneEffect::ClearAll) return pImpl->resetEffects();
if (!pImpl->effects) pImpl->effects = new Array<RenderEffect*>;
va_list args;
va_start(args, effect);
RenderEffect* re = nullptr;
switch (effect) {
case SceneEffect::GaussianBlur: {
re = RenderEffectGaussianBlur::gen(args);
break;
}
case SceneEffect::DropShadow: {
re = RenderEffectDropShadow::gen(args);
break;
}
case SceneEffect::Fill: {
re = RenderEffectFill::gen(args);
break;
}
case SceneEffect::Tint: {
re = RenderEffectTint::gen(args);
break;
}
case SceneEffect::Tritone: {
re = RenderEffectTritone::gen(args);
break;
}
default: break;
}
if (!re) return Result::InvalidArguments;
pImpl->effects->push(re);
return Result::Success;
}

View file

@ -23,10 +23,9 @@
#ifndef _TVG_SCENE_H_
#define _TVG_SCENE_H_
#include <float.h>
#include "tvgMath.h"
#include "tvgPaint.h"
struct SceneIterator : Iterator
{
list<Paint*>* paints;
@ -61,8 +60,10 @@ struct Scene::Impl
list<Paint*> paints;
RenderData rd = nullptr;
Scene* scene = nullptr;
uint8_t opacity; //for composition
bool needComp = false; //composite or not
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
Array<RenderEffect*>* effects = nullptr;
uint8_t compFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition
Impl(Scene* s) : scene(s)
{
@ -70,6 +71,8 @@ struct Scene::Impl
~Impl()
{
resetEffects();
for (auto paint : paints) {
if (P(paint)->unref() == 0) delete(paint);
}
@ -79,59 +82,63 @@ struct Scene::Impl
}
}
bool needComposition(uint8_t opacity)
uint8_t needComposition(uint8_t opacity)
{
if (opacity == 0 || paints.empty()) return false;
compFlag = CompositionFlag::Invalid;
//Masking may require composition (even if opacity == 255)
if (opacity == 0 || paints.empty()) return 0;
//post effects, masking, blending may require composition
if (effects) compFlag |= CompositionFlag::PostProcessing;
auto compMethod = scene->composite(nullptr);
if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
//Blending may require composition (even if opacity == 255)
if (scene->blend() != BlendMethod::Normal) return true;
if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) compFlag |= CompositionFlag::Masking;
if (PP(scene)->blendMethod != BlendMethod::Normal) compFlag |= CompositionFlag::Blending;
//Half translucent requires intermediate composition.
if (opacity == 255) return false;
if (opacity == 255) return compFlag;
//If scene has several children or only scene, it may require composition.
//OPTIMIZE: the bitmap type of the picture would not need the composition.
//OPTIMIZE: a single paint of a scene would not need the composition.
if (paints.size() == 1 && paints.front()->identifier() == TVG_CLASS_ID_SHAPE) return false;
if (paints.size() == 1 && paints.front()->type() == Type::Shape) return compFlag;
return true;
compFlag |= CompositionFlag::Opacity;
return 1;
}
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))) {
this->vport = renderer->viewport();
if (needComposition(opacity)) {
/* Overriding opacity value. If this scene is half-translucent,
It must do intermeidate composition with that opacity value. */
It must do intermediate composition with that opacity value. */
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);
}
if (effects) {
for (auto e = effects->begin(); e < effects->end(); ++e) {
renderer->prepare(*e, transform);
}
}
return nullptr;
}
bool render(RenderMethod* renderer)
{
Compositor* cmp = nullptr;
RenderCompositor* cmp = nullptr;
auto ret = true;
if (needComp) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
renderer->blend(PP(scene)->blendMethod);
if (compFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(compFlag));
renderer->beginComposite(cmp, CompositeMethod::None, opacity);
}
@ -139,7 +146,17 @@ struct Scene::Impl
ret &= paint->pImpl->render(renderer);
}
if (cmp) renderer->endComposite(cmp);
if (cmp) {
//Apply post effects if any.
if (effects) {
//Notify the possiblity of the direct composition of the effect result to the origin surface.
auto direct = (effects->count == 1) & (compFlag == CompositionFlag::PostProcessing);
for (auto e = effects->begin(); e < effects->end(); ++e) {
if ((*e)->valid) renderer->render(cmp, *e, direct);
}
}
renderer->endComposite(cmp);
}
return ret;
}
@ -163,7 +180,23 @@ struct Scene::Impl
if (y2 < region.y + region.h) y2 = (region.y + region.h);
}
return {x1, y1, (x2 - x1), (y2 - y1)};
//Extends the render region if post effects require
int32_t ex = 0, ey = 0, ew = 0, eh = 0;
if (effects) {
for (auto e = effects->begin(); e < effects->end(); ++e) {
auto effect = *e;
if (effect->valid && renderer->region(effect)) {
ex = std::min(ex, effect->extend.x);
ey = std::min(ey, effect->extend.y);
ew = std::max(ew, effect->extend.w);
eh = std::max(eh, effect->extend.h);
}
}
}
auto ret = RenderRegion{x1 + ex, y1 + ey, (x2 - x1) + ew, (y2 - y1) + eh};
ret.intersect(this->vport);
return ret;
}
bool bounds(float* px, float* py, float* pw, float* ph, bool stroking)
@ -198,10 +231,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 +244,9 @@ struct Scene::Impl
dup->paints.push_back(cdup);
}
return ret;
if (effects) TVGERR("RENDERER", "TODO: Duplicate Effects?");
return scene;
}
void clear(bool free)
@ -224,6 +261,8 @@ struct Scene::Impl
{
return new SceneIterator(&paints);
}
Result resetEffects();
};
#endif //_TVG_SCENE_H_

View file

@ -34,7 +34,6 @@
Shape :: Shape() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
}
@ -52,7 +51,13 @@ unique_ptr<Shape> Shape::gen() noexcept
uint32_t Shape::identifier() noexcept
{
return TVG_CLASS_ID_SHAPE;
return (uint32_t) Type::Shape;
}
Type Shape::type() const noexcept
{
return Type::Shape;
}
@ -61,7 +66,7 @@ Result Shape::reset() noexcept
pImpl->rs.path.cmds.clear();
pImpl->rs.path.pts.clear();
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -88,7 +93,7 @@ Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point*
pImpl->grow(cmdCnt, ptsCnt);
pImpl->append(cmds, cmdCnt, pts, ptsCnt);
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -106,7 +111,7 @@ Result Shape::lineTo(float x, float y) noexcept
{
pImpl->lineTo(x, y);
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -116,7 +121,7 @@ Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float
{
pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y);
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -126,7 +131,7 @@ Result Shape::close() noexcept
{
pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -145,20 +150,20 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept
TVG_DEPRECATED Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept
{
//just circle
if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
const float arcPrecision = 1e-5f;
startAngle = mathDeg2Rad(startAngle);
sweep = mathDeg2Rad(sweep);
startAngle = deg2rad(startAngle);
sweep = deg2rad(sweep);
auto nCurves = static_cast<int>(fabsf(sweep / MATH_PI2));
if (fabsf(sweep / MATH_PI2) - nCurves > arcPrecision) ++nCurves;
@ -207,7 +212,7 @@ Result Shape::appendArc(float cx, float cy, float radius, float startAngle, floa
if (pie) pImpl->close();
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -247,7 +252,7 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry)
pImpl->close();
}
pImpl->flag |= RenderUpdateFlag::Path;
pImpl->rFlag |= RenderUpdateFlag::Path;
return Result::Success;
}
@ -258,7 +263,7 @@ Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
if (pImpl->rs.fill) {
delete(pImpl->rs.fill);
pImpl->rs.fill = nullptr;
pImpl->flag |= RenderUpdateFlag::Gradient;
pImpl->rFlag |= RenderUpdateFlag::Gradient;
}
if (r == pImpl->rs.color[0] && g == pImpl->rs.color[1] && b == pImpl->rs.color[2] && a == pImpl->rs.color[3]) return Result::Success;
@ -267,7 +272,7 @@ Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
pImpl->rs.color[1] = g;
pImpl->rs.color[2] = b;
pImpl->rs.color[3] = a;
pImpl->flag |= RenderUpdateFlag::Color;
pImpl->rFlag |= RenderUpdateFlag::Color;
return Result::Success;
}
@ -280,7 +285,7 @@ Result Shape::fill(unique_ptr<Fill> f) noexcept
if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill);
pImpl->rs.fill = p;
pImpl->flag |= RenderUpdateFlag::Gradient;
pImpl->rFlag |= RenderUpdateFlag::Gradient;
return Result::Success;
}
@ -409,12 +414,6 @@ Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
}
bool Shape::strokeTrim(float* begin, float* end) const noexcept
{
return pImpl->strokeTrim(begin, end);
}
Result Shape::fill(FillRule r) noexcept
{
pImpl->rs.rule = r;

View file

@ -33,9 +33,9 @@ struct Shape::Impl
RenderShape rs; //shape data
RenderData rd = nullptr; //engine data
Shape* shape;
uint8_t flag = RenderUpdateFlag::None;
uint8_t rFlag = RenderUpdateFlag::None;
uint8_t cFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition
bool needComp = false; //composite or not
Impl(Shape* s) : shape(s)
{
@ -50,20 +50,26 @@ struct Shape::Impl
bool render(RenderMethod* renderer)
{
Compositor* cmp = nullptr;
bool ret;
if (!rd) return false;
if (needComp) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
RenderCompositor* cmp = nullptr;
renderer->blend(PP(shape)->blendMethod);
if (cFlag) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace(), static_cast<CompositionFlag>(cFlag));
renderer->beginComposite(cmp, CompositeMethod::None, opacity);
}
ret = renderer->renderShape(rd);
auto ret = renderer->renderShape(rd);
if (cmp) renderer->endComposite(cmp);
return ret;
}
bool needComposition(uint8_t opacity)
{
cFlag = CompositionFlag::Invalid;
if (opacity == 0) return false;
//Shape composition is only necessary when stroking & fill are valid.
@ -71,14 +77,17 @@ struct Shape::Impl
if (!rs.fill && rs.color[3] == 0) return false;
//translucent fill & stroke
if (opacity < 255) return true;
if (opacity < 255) {
cFlag = CompositionFlag::Opacity;
return true;
}
//Composition test
const Paint* target;
auto method = shape->composite(&target);
if (!target || method == CompositeMethod::ClipPath) return false;
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) {
if (target->identifier() == TVG_CLASS_ID_SHAPE) {
if (target->type() == Type::Shape) {
auto shape = static_cast<const Shape*>(target);
if (!shape->fill()) {
uint8_t r, g, b, a;
@ -92,27 +101,29 @@ struct Shape::Impl
}
}
cFlag = CompositionFlag::Masking;
return true;
}
RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
{
if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd;
if (static_cast<RenderUpdateFlag>(pFlag | rFlag) == RenderUpdateFlag::None) return rd;
if ((needComp = needComposition(opacity))) {
if (needComposition(opacity)) {
/* Overriding opacity value. If this scene is half-translucent,
It must do intermeidate composition with that opacity value. */
It must do intermediate composition with that opacity value. */
this->opacity = opacity;
opacity = 255;
}
rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
flag = RenderUpdateFlag::None;
rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | rFlag), clipper);
rFlag = RenderUpdateFlag::None;
return rd;
}
RenderRegion bounds(RenderMethod* renderer)
{
if (!rd) return {0, 0, 0, 0};
return renderer->region(rd);
}
@ -203,7 +214,7 @@ struct Shape::Impl
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::Stroke;
}
void strokeTrim(float begin, float end, bool simultaneous)
@ -213,30 +224,13 @@ struct Shape::Impl
rs.stroke = new RenderStroke();
}
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
if (tvg::equal(rs.stroke->trim.begin, begin) && tvg::equal(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;
flag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::Stroke;
}
bool strokeTrim(float* begin, float* end)
@ -256,21 +250,21 @@ struct Shape::Impl
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->cap = cap;
flag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::Stroke;
}
void strokeJoin(StrokeJoin join)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->join = join;
flag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::Stroke;
}
void strokeMiterlimit(float miterlimit)
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->miterlimit = miterlimit;
flag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::Stroke;
}
void strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
@ -279,7 +273,7 @@ struct Shape::Impl
if (rs.stroke->fill) {
delete(rs.stroke->fill);
rs.stroke->fill = nullptr;
flag |= RenderUpdateFlag::GradientStroke;
rFlag |= RenderUpdateFlag::GradientStroke;
}
rs.stroke->color[0] = r;
@ -287,7 +281,7 @@ struct Shape::Impl
rs.stroke->color[2] = b;
rs.stroke->color[3] = a;
flag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::Stroke;
}
Result strokeFill(unique_ptr<Fill> f)
@ -300,8 +294,8 @@ struct Shape::Impl
rs.stroke->fill = p;
rs.stroke->color[3] = 0;
flag |= RenderUpdateFlag::Stroke;
flag |= RenderUpdateFlag::GradientStroke;
rFlag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::GradientStroke;
return Result::Success;
}
@ -336,7 +330,7 @@ struct Shape::Impl
}
rs.stroke->dashCnt = cnt;
rs.stroke->dashOffset = offset;
flag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::Stroke;
return Result::Success;
}
@ -351,55 +345,64 @@ struct Shape::Impl
{
if (!rs.stroke) rs.stroke = new RenderStroke();
rs.stroke->strokeFirst = strokeFirst;
flag |= RenderUpdateFlag::Stroke;
rFlag |= RenderUpdateFlag::Stroke;
}
void update(RenderUpdateFlag flag)
{
this->flag |= flag;
rFlag |= 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->rFlag = 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

@ -82,7 +82,7 @@ Result SwCanvas::mempool(MempoolPolicy policy) noexcept
Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
{
#ifdef THORVG_SW_RASTER_SUPPORT
if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) {
return Result::InsufficientCondition;
}
@ -98,7 +98,7 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
ImageLoader::cs = static_cast<ColorSpace>(cs);
//Paints must be updated again with this new target.
Canvas::pImpl->status = Status::Damanged;
Canvas::pImpl->status = Status::Damaged;
return Result::Success;
#endif

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,9 +35,8 @@
/************************************************************************/
Text::Text() : pImpl(new Impl)
Text::Text() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_TEXT;
}
@ -61,13 +60,17 @@ Result Text::font(const char* name, float size, const char* style) noexcept
Result Text::load(const std::string& path) noexcept
{
#ifdef THORVG_FILE_IO_SUPPORT
bool invalid; //invalid path
if (!LoaderMgr::loader(path, &invalid)) {
if (invalid) return Result::InvalidArguments;
else return Result::NonSupport;
}
return Result::Success;
#else
TVGLOG("RENDERER", "FILE IO is disabled!");
return Result::NonSupport;
#endif
}
@ -88,27 +91,25 @@ Result Text::load(const char* name, const char* data, uint32_t size, const strin
Result Text::unload(const std::string& path) noexcept
{
#ifdef THORVG_FILE_IO_SUPPORT
if (LoaderMgr::retrieve(path)) return Result::Success;
return Result::InsufficientCondition;
#else
TVGLOG("RENDERER", "FILE IO is disabled!");
return Result::NonSupport;
#endif
}
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));
}
@ -118,7 +119,7 @@ unique_ptr<Text> Text::gen() noexcept
}
uint32_t Text::identifier() noexcept
Type Text::type() const noexcept
{
return TVG_CLASS_ID_TEXT;
return Type::Text;
}

View file

@ -26,37 +26,27 @@
#include <cstring>
#include "tvgShape.h"
#include "tvgFill.h"
#ifdef THORVG_TTF_LOADER_SUPPORT
#include "tvgTtfLoader.h"
#else
#include "tvgLoader.h"
#endif
#include "tvgLoader.h"
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)
@ -74,6 +64,11 @@ struct Text::Impl
auto loader = LoaderMgr::loader(name);
if (!loader) return Result::InsufficientCondition;
if (style && strstr(style, "italic")) italic = true;
else italic = false;
fontSize = size;
//Same resource has been loaded.
if (this->loader == loader) {
this->loader->sharing--; //make it sure the reference counting.
@ -83,52 +78,44 @@ 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;
return Result::Success;
}
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;
if (!loader) return true;
renderer->blend(PP(paint)->blendMethod);
return PP(shape)->render(renderer);
}
bool load()
{
if (!loader) return false;
loader->request(shape, utf8);
//reload
if (changed) {
loader->request(paint, utf8, italic);
loader->read();
changed = false;
}
if (paint) {
loader->resize(paint, fontSize, fontSize);
return true;
}
return false;
return loader->transform(shape, fontSize, italic);
}
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)->rFlag & RenderUpdateFlag::Gradient) {
auto scale = 1.0f / loader->scale;
if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
if (fill->type() == Type::LinearGradient) {
P(static_cast<LinearGradient*>(fill))->x1 *= scale;
P(static_cast<LinearGradient*>(fill))->y1 *= scale;
P(static_cast<LinearGradient*>(fill))->x2 *= 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

@ -40,7 +40,7 @@ struct WgCanvas::Impl
/************************************************************************/
#ifdef THORVG_WG_RASTER_SUPPORT
WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(new Impl)
WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(nullptr)
#else
WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
#endif
@ -50,14 +50,17 @@ WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
WgCanvas::~WgCanvas()
{
delete pImpl;
#ifdef THORVG_WG_RASTER_SUPPORT
auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer);
renderer->target(nullptr, 0, 0);
#endif
}
Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept
Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h, void* device) noexcept
{
#ifdef THORVG_WG_RASTER_SUPPORT
if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
if (Canvas::pImpl->status != Status::Damaged && Canvas::pImpl->status != Status::Synced) {
return Result::InsufficientCondition;
}
@ -67,12 +70,12 @@ Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) n
auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer);
if (!renderer) return Result::MemoryCorruption;
if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h)) return Result::Unknown;
if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h, (WGPUDevice)device)) return Result::Unknown;
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
renderer->viewport(Canvas::pImpl->vport);
//Paints must be updated again with this new target.
Canvas::pImpl->status = Status::Damanged;
Canvas::pImpl->status = Status::Damaged;
return Result::Success;
#endif

View file

@ -1,16 +1,22 @@
#!/bin/bash -e
VERSION=0.14.2
VERSION=0.15.10
# Uncomment and set a git hash to use specific commit instead of tag.
#GIT_COMMIT=
cd thirdparty/thorvg/ || true
pushd "$(dirname "$0")"
rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/
mkdir tmp/ && pushd tmp/
# Release
curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.tar.gz
# Current Github main branch tip
#curl -L -O https://github.com/thorvg/thorvg/archive/refs/heads/main.tar.gz
if [ ! -z "$GIT_COMMIT" ]; then
echo "Updating ThorVG to commit:" $GIT_COMMIT
curl -L -O https://github.com/thorvg/thorvg/archive/$GIT_COMMIT.tar.gz
else
echo "Updating ThorVG to tagged release:" $VERSION
curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.tar.gz
fi
tar --strip-components=1 -xvf *.tar.gz
rm *.tar.gz
@ -70,4 +76,4 @@ cp -rv src/loaders/jpg ../src/loaders/
popd
rm -rf tmp
popd