feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
12
engine/thirdparty/thorvg/AUTHORS
vendored
12
engine/thirdparty/thorvg/AUTHORS
vendored
|
|
@ -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>
|
||||
|
|
|
|||
2
engine/thirdparty/thorvg/inc/config.h
vendored
2
engine/thirdparty/thorvg/inc/config.h
vendored
|
|
@ -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
|
||||
|
|
|
|||
428
engine/thirdparty/thorvg/inc/thorvg.h
vendored
428
engine/thirdparty/thorvg/inc/thorvg.h
vendored
|
|
@ -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.
|
||||
|
|
|
|||
13
engine/thirdparty/thorvg/patches/0001-revert-tvglines-bezier-precision.patch
vendored
Normal file
13
engine/thirdparty/thorvg/patches/0001-revert-tvglines-bezier-precision.patch
vendored
Normal 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) {
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ struct Inlist
|
|||
if (element == tail) tail = element->prev;
|
||||
}
|
||||
|
||||
bool empty()
|
||||
bool empty() const
|
||||
{
|
||||
return head ? false : true;
|
||||
}
|
||||
|
|
|
|||
245
engine/thirdparty/thorvg/src/common/tvgLines.cpp
vendored
245
engine/thirdparty/thorvg/src/common/tvgLines.cpp
vendored
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
61
engine/thirdparty/thorvg/src/common/tvgLines.h
vendored
61
engine/thirdparty/thorvg/src/common/tvgLines.h
vendored
|
|
@ -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_
|
||||
|
|
@ -25,8 +25,6 @@
|
|||
|
||||
#ifdef THORVG_THREAD_SUPPORT
|
||||
|
||||
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
|
||||
|
||||
#include <mutex>
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
|
|
|
|||
263
engine/thirdparty/thorvg/src/common/tvgMath.cpp
vendored
263
engine/thirdparty/thorvg/src/common/tvgMath.cpp
vendored
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
120
engine/thirdparty/thorvg/src/common/tvgMath.h
vendored
120
engine/thirdparty/thorvg/src/common/tvgMath.h
vendored
|
|
@ -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_
|
||||
|
|
|
|||
20
engine/thirdparty/thorvg/src/common/tvgStr.cpp
vendored
20
engine/thirdparty/thorvg/src/common/tvgStr.cpp
vendored
|
|
@ -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, '/');
|
||||
|
|
|
|||
2
engine/thirdparty/thorvg/src/common/tvgStr.h
vendored
2
engine/thirdparty/thorvg/src/common/tvgStr.h
vendored
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public:
|
|||
bool read() override;
|
||||
bool close() override;
|
||||
|
||||
Surface* bitmap() override;
|
||||
RenderSurface* bitmap() override;
|
||||
};
|
||||
|
||||
#endif //_TVG_JPG_LOADER_H_
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -215,6 +215,7 @@ enum class SvgParserLengthType
|
|||
{
|
||||
Vertical,
|
||||
Horizontal,
|
||||
Diagonal,
|
||||
//In case of, for example, radius of radial gradient
|
||||
Other
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
589
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp
vendored
Normal file
589
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp
vendored
Normal 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 blur’s 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
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
255
engine/thirdparty/thorvg/src/renderer/tvgPaint.cpp
vendored
255
engine/thirdparty/thorvg/src/renderer/tvgPaint.cpp
vendored
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
78
engine/thirdparty/thorvg/src/renderer/tvgPaint.h
vendored
78
engine/thirdparty/thorvg/src/renderer/tvgPaint.h
vendored
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
240
engine/thirdparty/thorvg/src/renderer/tvgRender.h
vendored
240
engine/thirdparty/thorvg/src/renderer/tvgRender.h
vendored
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
117
engine/thirdparty/thorvg/src/renderer/tvgScene.h
vendored
117
engine/thirdparty/thorvg/src/renderer/tvgScene.h
vendored
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
143
engine/thirdparty/thorvg/src/renderer/tvgShape.h
vendored
143
engine/thirdparty/thorvg/src/renderer/tvgShape.h
vendored
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@
|
|||
#ifndef _TVG_TASK_SCHEDULER_H_
|
||||
#define _TVG_TASK_SCHEDULER_H_
|
||||
|
||||
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
77
engine/thirdparty/thorvg/src/renderer/tvgText.h
vendored
77
engine/thirdparty/thorvg/src/renderer/tvgText.h
vendored
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
18
engine/thirdparty/thorvg/update-thorvg.sh
vendored
18
engine/thirdparty/thorvg/update-thorvg.sh
vendored
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue