thorvg: Update to 0.15.5

This commit is contained in:
Jakub Marcowski
2024-12-04 13:47:20 +01:00
parent 47bc374edf
commit 5318008ce6
61 changed files with 1947 additions and 1350 deletions

View File

@@ -14,7 +14,6 @@ thirdparty_dir = "#thirdparty/thorvg/"
thirdparty_sources = [
# common
"src/common/tvgCompressor.cpp",
"src/common/tvgLines.cpp",
"src/common/tvgMath.cpp",
"src/common/tvgStr.cpp",
# SVG parser
@@ -52,6 +51,7 @@ thirdparty_sources = [
"src/renderer/sw_engine/tvgSwImage.cpp",
"src/renderer/sw_engine/tvgSwMath.cpp",
"src/renderer/sw_engine/tvgSwMemPool.cpp",
"src/renderer/sw_engine/tvgSwPostEffect.cpp",
"src/renderer/sw_engine/tvgSwRaster.cpp",
"src/renderer/sw_engine/tvgSwRenderer.cpp",
"src/renderer/sw_engine/tvgSwRle.cpp",

View File

@@ -916,7 +916,7 @@ instead of `miniz.h` as an external dependency.
## thorvg
- Upstream: https://github.com/thorvg/thorvg
- Version: 0.14.10 (366dcd72850c360b49e841e568fc5a154d7cce9e, 2024)
- Version: 0.15.5 (89ab573acb253567975b2494069c7ee9abc9267c, 2024)
- License: MIT
Files extracted from upstream source:

View File

@@ -28,6 +28,10 @@ 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>

View File

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

View File

@@ -157,7 +157,7 @@ enum class FillRule
enum class CompositeMethod
{
None = 0, ///< No composition is applied.
ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. Note that ClipPath only supports the Shape type.
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
@@ -178,24 +178,46 @@ 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]}
};
@@ -206,7 +228,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
};
@@ -274,7 +318,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.
@@ -312,7 +356,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;
@@ -324,6 +367,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.
*
@@ -386,22 +443,15 @@ public:
CompositeMethod composite(const Paint** target) const noexcept;
/**
* @brief Retrieves the current blending method applied to the paint object.
* @brief Returns the ID value of this class.
*
* @return The currently set 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;
/**
* @brief Return the unique id value of the paint instance.
*
* This method can be called for checking the current concrete instance type.
*
* @return The type id of the Paint instance.
*/
uint32_t identifier() const noexcept;
virtual Type type() const noexcept = 0;
/**
* @brief Unique ID of this instance.
@@ -412,6 +462,11 @@ public:
*/
uint32_t id = 0;
/**
* @see Paint::type()
*/
TVG_DEPRECATED uint32_t identifier() const noexcept;
_TVG_DECLARE_PRIVATE(Paint);
};
@@ -503,13 +558,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);
};
@@ -538,7 +600,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
@@ -614,7 +676,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;
@@ -686,13 +748,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);
};
@@ -744,13 +813,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);
};
@@ -774,11 +850,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;
@@ -836,15 +912,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.
@@ -886,7 +962,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.
@@ -999,7 +1075,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;
@@ -1130,18 +1205,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.
*
@@ -1150,13 +1213,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);
};
@@ -1213,7 +1283,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
@@ -1251,9 +1321,9 @@ public:
* @param[in] data A pointer to a memory location where the content of the picture raw data is stored.
* @param[in] w The width of the image @p data in pixels.
* @param[in] h The height of the image @p data in pixels.
* @param[in] premultiplied If @c true, the given image data is alpha-premultiplied.
* @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not.
*
* @note It expects premultiplied alpha data.
* @since 0.9
*/
Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept;
@@ -1281,13 +1351,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);
@@ -1331,9 +1408,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
@@ -1352,6 +1429,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.
*
@@ -1360,13 +1451,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);
};
@@ -1377,7 +1475,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
{
@@ -1422,7 +1520,7 @@ public:
*
* @see Text::font()
*
* @note Experimental API
* @since 0.15
*/
Result fill(uint8_t r, uint8_t g, uint8_t b) noexcept;
@@ -1434,9 +1532,9 @@ public:
* @param[in] f The unique pointer to the gradient fill.
*
* @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;
@@ -1452,9 +1550,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;
@@ -1475,13 +1573,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;
@@ -1495,9 +1593,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;
@@ -1506,18 +1604,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);
};
@@ -1616,8 +1716,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
@@ -1666,7 +1764,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
{
@@ -1680,6 +1778,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.
@@ -1689,14 +1788,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;
@@ -1752,7 +1851,7 @@ public:
*
* @return The version of the engine in the format major.minor.micro, or a @p nullptr in case of an internal error.
*
* @note Experimental API
* @since 0.15
*/
static const char* version(uint32_t* major, uint32_t* minor, uint32_t* micro) noexcept;
@@ -1857,6 +1956,7 @@ public:
* @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;
@@ -1895,7 +1995,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.
*

View File

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

View File

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

View File

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

View File

@@ -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) < 1e-3f) {
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,7 +114,7 @@ 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) +
@@ -60,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 ||
@@ -71,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;
@@ -108,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,9 +210,165 @@ Point operator*(const Point& pt, const Matrix& m)
return {tx, ty};
}
uint8_t mathLerp(const uint8_t &start, const uint8_t &end, float t)
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);
mathClamp(result, 0, 255);
tvg::clamp(result, 0, 255);
return static_cast<uint8_t>(result);
}
}

View File

@@ -29,47 +29,47 @@
#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 mathClamp(T& v, const T& min, const T& max)
static inline void clamp(T& v, const T& min, const T& max)
{
if (v < min) v = min;
else if (v > max) v = max;
@@ -79,27 +79,27 @@ static inline void mathClamp(T& v, const T& min, const T& 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;
@@ -113,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;
@@ -133,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);
@@ -160,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);
}
@@ -172,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;
@@ -192,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);
}
@@ -200,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);
}
@@ -240,32 +246,61 @@ static inline Point operator/(const Point& lhs, const float rhs)
}
static inline Point mathNormal(const Point& p1, const Point& p2)
{
auto dir = p2 - p1;
auto len = mathLength(dir);
if (mathZero(len)) return {};
auto unitDir = dir / len;
return {-unitDir.y, unitDir.x};
}
static inline void mathLog(const Point& pt)
static inline void 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 mathLerp(const uint8_t &start, const uint8_t &end, float t);
uint8_t lerp(const uint8_t &start, const uint8_t &end, float t);
}
#endif //_TVG_MATH_H_

View File

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

View File

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

View File

@@ -86,10 +86,10 @@ bool PngLoader::read()
if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
image->format = PNG_FORMAT_BGRA;
surface.cs = ColorSpace::ARGB8888;
surface.cs = ColorSpace::ARGB8888S;
} else {
image->format = PNG_FORMAT_RGBA;
surface.cs = ColorSpace::ABGR8888;
surface.cs = ColorSpace::ABGR8888S;
}
auto buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image))));

View File

@@ -134,7 +134,7 @@ bool WebpLoader::read()
}
Surface* WebpLoader::bitmap()
RenderSurface* WebpLoader::bitmap()
{
this->done();

View File

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

View File

@@ -125,7 +125,7 @@ bool JpgLoader::close()
}
Surface* JpgLoader::bitmap()
RenderSurface* JpgLoader::bitmap()
{
this->done();
return ImageLoader::bitmap();

View File

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

View File

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

View File

@@ -177,6 +177,7 @@ static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengt
else if (strstr(str, "%")) {
if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0f) * svgParse->global.h;
else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0f) * svgParse->global.w;
else if (type == SvgParserLengthType::Diagonal) parsedValue = (sqrtf(powf(svgParse->global.w, 2) + powf(svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
else //if other than it's radius
{
float max = svgParse->global.w;
@@ -593,15 +594,15 @@ static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* re
saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f;
brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f;
if (mathZero(saturation)) _red = _green = _blue = brightness;
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);
@@ -858,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;
@@ -882,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;
}
@@ -1066,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);
}
@@ -1614,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)}
};
@@ -2554,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;
@@ -2563,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 {
@@ -2586,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")) {
@@ -3341,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;

View File

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

View File

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

View File

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

View File

@@ -492,13 +492,13 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
key[0] = '\0';
val[0] = '\0';
if (next == nullptr && sep != nullptr) {
if (sep != nullptr && next == nullptr) {
memcpy(key, buf, sep - buf);
key[sep - buf] = '\0';
memcpy(val, sep + 1, end - sep - 1);
val[end - sep - 1] = '\0';
} else if (sep < next && sep != nullptr) {
} else if (sep != nullptr && sep < next) {
memcpy(key, buf, sep - buf);
key[sep - buf] = '\0';
@@ -522,8 +522,9 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
}
}
if (!next) break;
buf = next + 1;
} while (next != nullptr);
} while (true);
return true;
}

View File

@@ -113,7 +113,7 @@ struct SwSpan
uint8_t coverage;
};
struct SwRleData
struct SwRle
{
SwSpan *spans;
uint32_t alloc;
@@ -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
@@ -488,13 +488,14 @@ 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);
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);
@@ -541,13 +542,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);
void rleClip(SwRle* rle, const SwRle* clip);
void rleClip(SwRle* rle, const SwBBox* clip);
SwMpool* mpoolInit(uint32_t threads);
bool mpoolTerm(SwMpool* mpool);
@@ -567,9 +568,17 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
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);
bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params);
bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect);
bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, uint8_t opacity, bool direct);
bool effectDropShadowPrepare(RenderEffectDropShadow* effect);
#endif /* _TVG_SW_COMMON_H_ */

View File

@@ -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);
}
@@ -217,7 +217,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr
auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
if (len < FLOAT_EPSILON) {
if (mathZero(fill->linear.dx) && mathZero(fill->linear.dy)) {
if (tvg::zero(fill->linear.dx) && tvg::zero(fill->linear.dy)) {
fill->solid = true;
}
return true;
@@ -228,7 +228,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr
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) {
gradTransform = transform * gradTransform;
@@ -239,7 +239,7 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr
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;
@@ -261,7 +261,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
auto fy = P(radial)->fy;
auto fr = P(radial)->fr;
if (mathZero(r)) {
if (tvg::zero(r)) {
fill->solid = true;
return true;
}
@@ -295,7 +295,7 @@ 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 (isTransformation) gradTransform = transform * gradTransform;
else {
@@ -305,7 +305,7 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
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;
@@ -553,7 +553,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));
@@ -584,7 +584,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));
@@ -626,7 +626,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);
@@ -643,7 +643,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;
}
@@ -651,7 +651,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;
@@ -668,7 +668,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) {
@@ -715,7 +715,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);
@@ -755,7 +755,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) {
@@ -828,9 +828,9 @@ bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform,
fill->spread = fdata->spread();
if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
if (fdata->type() == Type::LinearGradient) {
if (!_prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform)) return false;
} else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
} else if (fdata->type() == Type::RadialGradient) {
if (!_prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform)) return false;
}

View File

@@ -29,7 +29,7 @@
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;
}
@@ -86,7 +86,7 @@ bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipReg
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;
}

View File

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

View File

@@ -0,0 +1,410 @@
/*
* 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];
};
static void _gaussianExtendRegion(RenderRegion& region, int extra, int8_t direction)
{
//bbox region expansion for feathering
if (direction != 2) {
region.x = -extra;
region.w = extra * 2;
}
if (direction != 1) {
region.y = -extra;
region.h = extra * 2;
}
}
static int _gaussianEdgeWrap(int end, int idx)
{
auto r = idx % end;
return (r < 0) ? end + r : r;
}
static int _gaussianEdgeExtend(int end, int idx)
{
if (idx < 0) return 0;
else if (idx >= end) return end - 1;
return idx;
}
static int _gaussianRemap(int end, int idx, int border)
{
if (border == 1) return _gaussianEdgeWrap(end, idx);
return _gaussianEdgeExtend(end, idx);
}
//TODO: SIMD OPTIMIZATION?
static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, int border, 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);
#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(w, x, border) + 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(w, r, border) + p) * 4;
auto lid = (_gaussianRemap(w, l, border) + 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];
dst[i++] = static_cast<uint8_t>(acc[0] * iarr + 0.5f);
dst[i++] = static_cast<uint8_t>(acc[1] * iarr + 0.5f);
dst[i++] = static_cast<uint8_t>(acc[2] * iarr + 0.5f);
dst[i++] = static_cast<uint8_t>(acc[3] * iarr + 0.5f);
}
}
}
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 effectGaussianBlurPrepare(RenderEffectGaussianBlur* params)
{
auto rd = (SwGaussianBlur*)malloc(sizeof(SwGaussianBlur));
auto extends = _gaussianInit(rd, params->sigma * params->sigma, params->quality);
//invalid
if (extends == 0) {
params->invalid = true;
free(rd);
return false;
}
_gaussianExtendRegion(params->extend, extends, params->direction);
params->rd = rd;
return true;
}
bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params)
{
if (cmp->image.channelSize != sizeof(uint32_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!");
return false;
}
auto& buffer = surface->compositor->image;
auto data = static_cast<SwGaussianBlur*>(params->rd);
auto& bbox = cmp->bbox;
auto w = (bbox.max.x - bbox.min.x);
auto h = (bbox.max.y - bbox.min.y);
auto stride = cmp->image.stride;
auto front = cmp->image.buf32;
auto back = buffer.buf32;
auto swapped = false;
TVGLOG("SW_ENGINE", "GaussianFilter region(%ld, %ld, %ld, %ld) params(%f %d %d), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->sigma, params->direction, params->border, data->level);
/* It is best to take advantage of the Gaussian blurs separable property
by dividing the process into two passes. horizontal and vertical.
We can expect fewer calculations. */
//horizontal
if (params->direction != 2) {
for (int i = 0; i < data->level; ++i) {
_gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, w, h, bbox, data->kernel[i], params->border, 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], params->border, 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);
#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(w, x) + p;
acc += A(src[id]);
}
//perform filtering
for (int x = 0; x < w; ++x, ++r, ++l) {
auto rid = _gaussianEdgeExtend(w, r) + p;
auto lid = _gaussianEdgeExtend(w, l) + p;
acc += A(src[rid]) - A(src[lid]);
dst[i++] = ALPHA_BLEND(color, static_cast<uint8_t>(acc * iarr + 0.5f));
}
}
}
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;
}
}
static void _dropShadowExtendRegion(RenderRegion& region, int extra, SwPoint& offset)
{
//bbox region expansion for feathering
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);
}
bool effectDropShadowPrepare(RenderEffectDropShadow* params)
{
auto rd = (SwDropShadow*)malloc(sizeof(SwDropShadow));
//compute box kernel sizes
auto extends = _gaussianInit(rd, params->sigma * params->sigma, params->quality);
//invalid
if (extends == 0 || params->color[3] == 0) {
params->invalid = true;
free(rd);
return false;
}
//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};
}
//bbox region expansion for feathering
_dropShadowExtendRegion(params->extend, extends, rd->offset);
params->rd = rd;
return 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, uint8_t opacity, bool direct)
{
if (cmp->image.channelSize != sizeof(uint32_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Drop Shadow!");
return false;
}
//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;
opacity = MULTIPLY(params->color[3], opacity);
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;
}

View File

@@ -490,7 +490,7 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin
/* Rle */
/************************************************************************/
static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
auto cbuffer = surface->compositor->image.buf8;
@@ -510,7 +510,7 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRleData* rle, SwMask
}
static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterDirectMaskedRle(SwSurface* surface, SwRle* rle, SwMask maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
auto span = rle->spans;
auto cbuffer = surface->compositor->image.buf8;
@@ -531,7 +531,7 @@ static bool _rasterDirectMaskedRle(SwSurface* surface, SwRleData* rle, SwMask ma
}
static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterMaskedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method);
@@ -545,7 +545,7 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
}
static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterMattedRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method);
@@ -568,10 +568,8 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
return true;
}
//8bit grayscale
if (surface->channelSize == sizeof(uint8_t)) {
} else if (surface->channelSize == sizeof(uint8_t)) {
uint8_t src;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
auto dst = &surface->buf8[span->y * surface->stride + span->x];
@@ -582,13 +580,12 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint
*dst = INTERPOLATE8(src, *dst, alpha(cmp));
}
}
return true;
}
return false;
return true;
}
static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterBlendingRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (surface->channelSize != sizeof(uint32_t)) return false;
@@ -612,7 +609,7 @@ static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t
}
static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
#if defined(THORVG_AVX_VECTOR_SUPPORT)
return avxRasterTranslucentRle(surface, rle, r, g, b, a);
@@ -624,7 +621,7 @@ static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint
}
static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b)
static bool _rasterSolidRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b)
{
auto span = rle->spans;
@@ -661,7 +658,7 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r,
}
static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
static bool _rasterRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (!rle) return false;
@@ -697,66 +694,9 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g,
auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \
if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image->w) continue; \
#if 0 //Enable it when GRAYSCALE image is supported
static bool _rasterCompositeScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto span = image->rle->spans;
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
SCALED_IMAGE_RANGE_Y(span->y)
auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
auto a = MULTIPLY(span->coverage, opacity);
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (a < 255) src = MULTIPLY(src, a);
*cmp = maskOp(src, *cmp, ~src);
}
}
return true;
}
static bool _rasterDirectScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto span = image->rle->spans;
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
SCALED_IMAGE_RANGE_Y(span->y)
auto cmp = &surface->compositor->image.buf8[span->y * surface->compositor->image.stride + span->x];
auto dst = &surface->buf8[span->y * surface->stride + span->x];
auto a = MULTIPLY(span->coverage, opacity);
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++cmp, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (a < 255) src = MULTIPLY(src, a);
src = maskOp(src, *cmp, 0); //not use alpha
*dst = src + MULTIPLY(*dst, ~src);
}
}
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
#endif
static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
#if 0 //Enable it when GRAYSCALE image is supported
TVGLOG("SW_ENGINE", "Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
//8bit masking channels composition
if (surface->channelSize != sizeof(uint8_t)) return false;
auto maskOp = _getMaskOp(surface->compositor->method);
if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity);
else return _rasterCompositeScaledMaskedRleImage(surface, image, itransform, region, maskOp, opacity);
#endif
TVGERR("SW_ENGINE", "Not Supported Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
return false;
}
@@ -784,7 +724,6 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
}
return true;
}
@@ -851,7 +790,7 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
Matrix itransform;
if (!mathInverse(&transform, &itransform)) return true;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity);
@@ -869,75 +808,6 @@ static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matr
/* RLE Direct Image */
/************************************************************************/
#if 0 //Enable it when GRAYSCALE image is supported
static bool _rasterCompositeDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity)
{
auto span = image->rle->spans;
auto cbuffer = surface->compositor->image.buf8;
auto ctride = surface->compositor->image.stride;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto cmp = &cbuffer[span->y * ctride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) {
*cmp = maskOp(*src, *cmp, ~*src);
}
} else {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) {
auto tmp = MULTIPLY(*src, alpha);
*cmp = maskOp(*src, *cmp, ~tmp);
}
}
}
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static bool _rasterDirectDirectMaskedRleImage(SwSurface* surface, const SwImage* image, SwMask maskOp, uint8_t opacity)
{
auto span = image->rle->spans;
auto cbuffer = surface->compositor->image.buf8;
auto ctride = surface->compositor->image.stride;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
auto src = image->buf8 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto cmp = &cbuffer[span->y * ctride + span->x];
auto dst = &surface->buf8[span->y * surface->stride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
auto tmp = maskOp(*src, *cmp, 0); //not use alpha
*dst = INTERPOLATE8(tmp, *dst, (255 - tmp));
}
} else {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp, ++dst) {
auto tmp = maskOp(MULTIPLY(*src, alpha), *cmp, 0); //not use alpha
*dst = INTERPOLATE8(tmp, *dst, (255 - tmp));
}
}
}
return true;
}
#endif
static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
#if 0 //Enable it when GRAYSCALE image is supported
TVGLOG("SW_ENGINE", "Direct Masked(%d) Rle Image", (int)surface->compositor->method);
//8bit masking channels composition
if (surface->channelSize != sizeof(uint8_t)) return false;
auto maskOp = _getMaskOp(surface->compositor->method);
if (_direct(surface->compositor->method)) _rasterDirectDirectMaskedRleImage(surface, image, maskOp, opacity);
else return _rasterCompositeDirectMaskedRleImage(surface, image, maskOp, opacity);
#endif
return false;
}
static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method);
@@ -999,21 +869,19 @@ static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
*dst = *img + ALPHA_BLEND(*dst, IA(*img));
}
} else {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
auto src = ALPHA_BLEND(*img, alpha);
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
}
rasterTranslucentPixel32(dst, img, span->len, alpha);
}
return true;
}
static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method);
return false;
}
static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
@@ -1037,67 +905,9 @@ static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t op
/*Scaled Image */
/************************************************************************/
#if 0 //Enable it when GRAYSCALE image is supported
static bool _rasterCompositeScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
int32_t miny = 0, maxy = 0;
for (auto y = region.min.y; y < region.max.y; ++y) {
SCALED_IMAGE_RANGE_Y(y)
auto cmp = cbuffer;
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) src = MULTIPLY(src, opacity);
*cmp = maskOp(src, *cmp, ~src);
}
cbuffer += cstride;
}
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static bool _rasterDirectScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x);
auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x);
int32_t miny = 0, maxy = 0;
for (auto y = region.min.y; y < region.max.y; ++y) {
SCALED_IMAGE_RANGE_Y(y)
auto cmp = cbuffer;
auto dst = dbuffer;
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf8, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) src = MULTIPLY(src, opacity);
src = maskOp(src, *cmp, 0); //not use alpha
*dst = src + MULTIPLY(*dst, ~src);
}
cbuffer += cstride;
dbuffer += surface->stride;
}
return true;
}
#endif
static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
TVGERR("SW_ENGINE", "Not supported ScaledMaskedImage!");
#if 0 //Enable it when GRAYSCALE image is supported
TVGLOG("SW_ENGINE", "Scaled Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
auto maskOp = _getMaskOp(surface->compositor->method);
if (_direct(surface->compositor->method)) return _rasterDirectScaledMaskedImage(surface, image, itransform, region, maskOp, opacity);
else return _rasterCompositeScaledMaskedImage(surface, image, itransform, region, maskOp, opacity);
#endif
TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!");
return false;
}
@@ -1202,7 +1012,7 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix&
{
Matrix itransform;
if (!mathInverse(&transform, &itransform)) return true;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity);
@@ -1220,78 +1030,9 @@ static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix&
/* Direct Image */
/************************************************************************/
#if 0 //Enable it when GRAYSCALE image is supported
static bool _rasterCompositeDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * cstride + region.min.x); //compositor buffer
auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
for (uint32_t y = 0; y < h; ++y) {
auto cmp = cbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) {
*cmp = maskOp(*src, *cmp, ~*src);
}
} else {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) {
auto tmp = MULTIPLY(*src, opacity);
*cmp = maskOp(tmp, *cmp, ~tmp);
}
}
cbuffer += cstride;
sbuffer += image->stride;
}
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
static bool _rasterDirectDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, SwMask maskOp, uint8_t opacity)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto cstride = surface->compositor->image.stride;
auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); //compositor buffer
auto dbuffer = surface->buf8 + (region.min.y * surface->stride + region.min.x); //destination buffer
auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
for (uint32_t y = 0; y < h; ++y) {
auto cmp = cbuffer;
auto dst = dbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
auto tmp = maskOp(*src, *cmp, 0); //not use alpha
*dst = tmp + MULTIPLY(*dst, ~tmp);
}
} else {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp, ++dst) {
auto tmp = maskOp(MULTIPLY(*src, opacity), *cmp, 0); //not use alpha
*dst = tmp + MULTIPLY(*dst, ~tmp);
}
}
cbuffer += cstride;
dbuffer += surface->stride;
sbuffer += image->stride;
}
return true;
}
#endif
static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
TVGERR("SW_ENGINE", "Not Supported: Direct Masked(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
#if 0 //Enable it when GRAYSCALE image is supported
auto maskOp = _getMaskOp(surface->compositor->method);
if (_direct(surface->compositor->method)) return _rasterDirectDirectMaskedImage(surface, image, region, maskOp, opacity);
else return _rasterCompositeDirectMaskedImage(surface, image, region, maskOp, opacity);
#endif
TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image");
return false;
}
@@ -1394,37 +1135,24 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S
//32bits channels
if (surface->channelSize == sizeof(uint32_t)) {
auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
for (auto y = region.min.y; y < region.max.y; ++y) {
auto dst = dbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) {
*dst = *src + ALPHA_BLEND(*dst, IA(*src));
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
auto tmp = ALPHA_BLEND(*src, opacity);
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
}
rasterTranslucentPixel32(dbuffer, sbuffer, region.max.x - region.min.x, opacity);
dbuffer += surface->stride;
sbuffer += image->stride;
}
//8bits grayscale
} else if (surface->channelSize == sizeof(uint8_t)) {
auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x];
for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image->stride) {
auto dst = dbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
*dst = *src + MULTIPLY(*dst, ~*src);
*dst = *src + MULTIPLY(*dst, IA(*src));
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
*dst = INTERPOLATE8(*src, *dst, opacity);
*dst = INTERPOLATE8(A(*src), *dst, opacity);
}
}
}
@@ -1602,13 +1330,23 @@ static bool _rasterBlendingGradientRect(SwSurface* surface, const SwBBox& region
template<typename fillMethod>
static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
{
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendPreNormal, 255);
buffer += surface->stride;
//32 bits
if (surface->channelSize == sizeof(uint32_t)) {
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendPreNormal, 255);
buffer += surface->stride;
}
//8 bits
} else if (surface->channelSize == sizeof(uint8_t)) {
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, _opMaskAdd, 255);
buffer += surface->stride;
}
}
return true;
}
@@ -1617,12 +1355,23 @@ static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& reg
template<typename fillMethod>
static bool _rasterSolidGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill)
{
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w, opBlendSrcOver, 255);
//32 bits
if (surface->channelSize == sizeof(uint32_t)) {
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendSrcOver, 255);
buffer += surface->stride;
}
//8 bits
} else if (surface->channelSize == sizeof(uint8_t)) {
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, _opMaskNone, 255);
buffer += surface->stride;
}
}
return true;
}
@@ -1663,7 +1412,7 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
/************************************************************************/
template<typename fillMethod>
static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp)
static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill, SwMask maskOp)
{
auto span = rle->spans;
auto cstride = surface->compositor->image.stride;
@@ -1678,7 +1427,7 @@ static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRleDat
template<typename fillMethod>
static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwMask maskOp)
static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill, SwMask maskOp)
{
auto span = rle->spans;
auto cstride = surface->compositor->image.stride;
@@ -1695,7 +1444,7 @@ static bool _rasterDirectGradientMaskedRle(SwSurface* surface, const SwRleData*
template<typename fillMethod>
static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
{
auto method = surface->compositor->method;
@@ -1710,7 +1459,7 @@ static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, c
template<typename fillMethod>
static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
static bool _rasterGradientMattedRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
{
TVGLOG("SW_ENGINE", "Matted(%d) Rle Linear Gradient", (int)surface->compositor->method);
@@ -1729,7 +1478,7 @@ static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, c
template<typename fillMethod>
static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
{
auto span = rle->spans;
@@ -1742,7 +1491,7 @@ static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRleData* rle,
template<typename fillMethod>
static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
{
auto span = rle->spans;
@@ -1757,7 +1506,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r
} else if (surface->channelSize == sizeof(uint8_t)) {
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
auto dst = &surface->buf8[span->y * surface->stride + span->x];
fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, 255);
fillMethod()(fill, dst, span->y, span->x, span->len, _opMaskAdd, span->coverage);
}
}
return true;
@@ -1765,7 +1514,7 @@ static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* r
template<typename fillMethod>
static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
static bool _rasterSolidGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
{
auto span = rle->spans;
@@ -1789,7 +1538,7 @@ static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, co
}
static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
static bool _rasterLinearGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
{
if (!rle) return false;
@@ -1806,7 +1555,7 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c
}
static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill)
static bool _rasterRadialGradientRle(SwSurface* surface, const SwRle* rle, const SwFill* fill)
{
if (!rle) return false;
@@ -1814,9 +1563,9 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
if (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill);
else return _rasterGradientMaskedRle<FillRadial>(surface, rle, fill);
} else if (_blending(surface)) {
_rasterBlendingGradientRle<FillRadial>(surface, rle, fill);
return _rasterBlendingGradientRle<FillRadial>(surface, rle, fill);
} else {
if (fill->translucent) _rasterTranslucentGradientRle<FillRadial>(surface, rle, fill);
if (fill->translucent) return _rasterTranslucentGradientRle<FillRadial>(surface, rle, fill);
else return _rasterSolidGradientRle<FillRadial>(surface, rle, fill);
}
return false;
@@ -1827,6 +1576,19 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
/* External Class Implementation */
/************************************************************************/
void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity)
{
//TODO: Support SIMD accelerations
cRasterTranslucentPixels(dst, src, len, opacity);
}
void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity)
{
//TODO: Support SIMD accelerations
cRasterPixels(dst, src, len, opacity);
}
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len)
{
@@ -1905,7 +1667,7 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_
}
void rasterUnpremultiply(Surface* surface)
void rasterUnpremultiply(RenderSurface* surface)
{
if (surface->channelSize != sizeof(uint32_t)) return;
@@ -1935,7 +1697,7 @@ void rasterUnpremultiply(Surface* surface)
}
void rasterPremultiply(Surface* surface)
void rasterPremultiply(RenderSurface* surface)
{
ScopedLock lock(surface->key);
if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return;
@@ -1965,13 +1727,13 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata,
return a > 0 ? rasterShape(surface, shape, color->r, color->g, color->b, a) : true;
}
auto id = fdata->identifier();
auto type = fdata->type();
if (shape->fastTrack) {
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
if (type == Type::LinearGradient) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
else if (type == Type::RadialGradient)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
} else {
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->rle, shape->fill);
else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->rle, shape->fill);
if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->rle, shape->fill);
else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->rle, shape->fill);
}
return false;
}
@@ -1986,9 +1748,9 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata,
return a > 0 ? rasterStroke(surface, shape, color->r, color->g, color->b, a) : true;
}
auto id = fdata->identifier();
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
auto type = fdata->type();
if (type == Type::LinearGradient) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
else if (type == Type::RadialGradient) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
return false;
}
@@ -2027,12 +1789,12 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, co
}
bool rasterConvertCS(Surface* surface, ColorSpace to)
bool rasterConvertCS(RenderSurface* surface, ColorSpace to)
{
ScopedLock lock(surface->key);
if (surface->cs == to) return true;
//TOOD: Support SIMD accelerations
//TODO: Support SIMD accelerations
auto from = surface->cs;
if (((from == ColorSpace::ABGR8888) || (from == ColorSpace::ABGR8888S)) && ((to == ColorSpace::ARGB8888) || (to == ColorSpace::ARGB8888S))) {
@@ -2045,3 +1807,39 @@ bool rasterConvertCS(Surface* surface, ColorSpace to)
}
return false;
}
//TODO: SIMD OPTIMIZATION?
void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped)
{
constexpr int BLOCK = 8; //experimental decision
if (flipped) {
src += ((bbox.min.x * stride) + bbox.min.y);
dst += ((bbox.min.y * stride) + bbox.min.x);
} else {
src += ((bbox.min.y * stride) + bbox.min.x);
dst += ((bbox.min.x * stride) + bbox.min.y);
}
#pragma omp parallel for
for (int x = 0; x < w; x += BLOCK) {
auto bx = std::min(w, x + BLOCK) - x;
auto in = &src[x];
auto out = &dst[x * stride];
for (int y = 0; y < h; y += BLOCK) {
auto p = &in[y * stride];
auto q = &out[y];
auto by = std::min(h, y + BLOCK) - y;
for (int xx = 0; xx < bx; ++xx) {
for (int yy = 0; yy < by; ++yy) {
*q = *p;
p += stride;
++q;
}
p += 1 - by * stride;
q += stride - by;
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -20,6 +20,17 @@
* SOFTWARE.
*/
struct Vertex
{
Point pt;
Point uv;
};
struct Polygon
{
Vertex vertex[3];
};
struct AALine
{
int32_t x[2];
@@ -53,10 +64,12 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
regionBottom = image->rle->spans[image->rle->size - 1].y;
}
if (yStart >= regionBottom) return false;
if (yStart < regionTop) yStart = regionTop;
if (yEnd > regionBottom) yEnd = regionBottom;
return yEnd > yStart;
return true;
}
@@ -673,7 +686,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;
@@ -689,8 +702,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

View File

@@ -20,6 +20,9 @@
* SOFTWARE.
*/
#ifdef THORVG_SW_OPENMP_SUPPORT
#include <omp.h>
#endif
#include <algorithm>
#include "tvgMath.h"
#include "tvgSwCommon.h"
@@ -38,7 +41,7 @@ struct SwTask : Task
{
SwSurface* surface = nullptr;
SwMpool* mpool = nullptr;
SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
SwBBox bbox; //Rendering Region
Matrix transform;
Array<RenderData> clips;
RenderUpdateFlag flags = RenderUpdateFlag::None;
@@ -65,9 +68,7 @@ struct SwTask : Task
}
virtual void dispose() = 0;
virtual bool clip(SwRleData* target) = 0;
virtual SwRleData* rle() = 0;
virtual bool clip(SwRle* target) = 0;
virtual ~SwTask() {}
};
@@ -92,38 +93,34 @@ 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;
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);
if (shape.fastTrack) rleClip(target, &bbox);
else if (shape.rle) rleClip(target, shape.rle);
else return false;
return true;
}
SwRleData* rle() override
{
if (!shape.rle && shape.fastTrack) {
shape.rle = rleRender(&shape.bbox);
}
return shape.rle;
}
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;
@@ -135,10 +132,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();
}
}
}
@@ -159,8 +157,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,9 +182,13 @@ struct SwShapeTask : SwTask
//Clip stroke rle
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
}
bbox = renderRegion; //sync
return;
err:
bbox.reset();
shapeReset(&shape);
shapeDelOutline(&shape, mpool, tid);
}
@@ -201,20 +203,14 @@ struct SwShapeTask : SwTask
struct SwImageTask : SwTask
{
SwImage image;
Surface* source; //Image source
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;
@@ -452,27 +448,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;
@@ -491,7 +478,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;
}
@@ -505,7 +502,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);
@@ -543,13 +540,50 @@ 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)
{
SwSurface* cmp = nullptr;
//Use cached data
for (auto p = compositors.begin(); p < compositors.end(); ++p) {
if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == channelSize) {
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 * surface->stride * surface->h);
cmp->compositor->image.w = surface->w;
cmp->compositor->image.h = surface->h;
cmp->compositor->image.stride = surface->stride;
cmp->compositor->image.direct = true;
cmp->compositor->valid = true;
cmp->channelSize = cmp->compositor->image.channelSize = channelSize;
cmp->w = cmp->compositor->image.w;
cmp->h = cmp->compositor->image.h;
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)
{
auto x = region.x;
auto y = region.y;
@@ -561,35 +595,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));
//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;
@@ -597,14 +612,6 @@ 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;
/* TODO: Currently, only blending might work.
Blending and composition must be handled together. */
@@ -618,7 +625,7 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
}
bool SwRenderer::endComposite(Compositor* cmp)
bool SwRenderer::endComposite(RenderCompositor* cmp)
{
if (!cmp) return false;
@@ -639,6 +646,40 @@ bool SwRenderer::endComposite(Compositor* cmp)
}
bool SwRenderer::prepare(RenderEffect* effect)
{
switch (effect->type) {
case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast<RenderEffectGaussianBlur*>(effect));
case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast<RenderEffectDropShadow*>(effect));
default: return false;
}
}
bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct)
{
if (effect->invalid) return false;
auto p = static_cast<SwCompositor*>(cmp);
switch (effect->type) {
case SceneEffect::GaussianBlur: {
return effectGaussianBlur(p, request(surface->channelSize), static_cast<const RenderEffectGaussianBlur*>(effect));
}
case SceneEffect::DropShadow: {
auto cmp1 = request(surface->channelSize);
cmp1->compositor->valid = false;
auto cmp2 = request(surface->channelSize);
SwSurface* surfaces[] = {cmp1, cmp2};
auto ret = effectDropShadow(p, surfaces, static_cast<const RenderEffectDropShadow*>(effect), opacity, direct);
cmp1->compositor->valid = true;
return ret;
}
default: return false;
}
}
ColorSpace SwRenderer::colorSpace()
{
if (surface) return surface->cs;
@@ -681,10 +722,10 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
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;
@@ -697,7 +738,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
}
RenderData SwRenderer::prepare(Surface* surface, RenderData data, const Matrix& 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);
@@ -748,6 +789,10 @@ bool SwRenderer::init(uint32_t threads)
int32_t SwRenderer::init()
{
#ifdef THORVG_SW_OPENMP_SUPPORT
omp_set_num_threads(TaskScheduler::threads());
#endif
return initEngineCnt;
}

View File

@@ -37,7 +37,7 @@ class SwRenderer : public RenderMethod
{
public:
RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
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;
@@ -48,18 +48,21 @@ 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) override;
bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override;
bool endComposite(RenderCompositor* cmp) override;
void clearCompositors();
bool prepare(RenderEffect* effect) override;
bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) override;
static SwRenderer* gen();
static bool init(uint32_t threads);
static int32_t init();
@@ -76,6 +79,7 @@ private:
SwRenderer();
~SwRenderer();
SwSurface* request(int channelSize);
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
};

View File

@@ -217,7 +217,7 @@ struct Cell
struct RleWorker
{
SwRleData* rle;
SwRle* rle;
SwPoint cellPos;
SwPoint cellMin;
@@ -235,6 +235,7 @@ struct RleWorker
SwPoint pos;
SwPoint bezStack[32 * 3 + 1];
SwPoint lineStack[32 + 1];
int levStack[32];
SwOutline* outline;
@@ -297,7 +298,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 +342,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 +362,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++;
}
@@ -513,98 +514,116 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
return;
}
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);
/* 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;
}
auto f1 = line[1] - SUBPIXELS(e1);
SwPoint f2;
//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);
}
//any other line
} else {
Area prod = diff.x * f1.y - diff.y * f1.x;
} while(e1 != e2);
/* 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);
/* 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;
}
_setCell(rw, e1);
} 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;
}
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;
}
@@ -690,31 +709,27 @@ 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]));
while (pt < limit) {
++pt;
++types;
//emit a single line_to
if (types[0] == SW_CURVE_TYPE_POINT) {
++pt;
++types;
_lineTo(rw, UPSCALE(*pt));
//types cubic
} else {
pt += 2;
types += 2;
if (pt <= limit) {
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
continue;
}
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
goto close;
pt += 3;
types += 3;
if (pt <= limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
else if (pt - 1 == limit) _cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
else goto close;
}
}
_lineTo(rw, start);
close:
_lineTo(rw, start);
first = last + 1;
}
}
@@ -731,7 +746,7 @@ static int _genRle(RleWorker& rw)
}
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 +755,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 +765,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 +798,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,7 +837,7 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRl
}
void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size)
{
free(rle->spans);
rle->spans = clippedSpans;
@@ -834,7 +849,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;
@@ -862,7 +877,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
@@ -953,12 +968,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;
@@ -975,14 +990,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);
@@ -990,7 +1005,7 @@ void rleFree(SwRleData* rle)
}
void rleClipPath(SwRleData *rle, const SwRleData *clip)
void rleClip(SwRle *rle, const SwRle *clip)
{
if (rle->size == 0 || clip->size == 0) return;
auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
@@ -999,11 +1014,11 @@ void rleClipPath(SwRleData *rle, const SwRleData *clip)
_replaceClipSpan(rle, spans, spansEnd - spans);
TVGLOG("SW_ENGINE", "Using ClipPath!");
TVGLOG("SW_ENGINE", "Using Path Clipping!");
}
void rleClipRect(SwRleData *rle, const SwBBox* clip)
void rleClip(SwRle *rle, const SwBBox* clip)
{
if (rle->size == 0) return;
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
@@ -1011,5 +1026,5 @@ void rleClipRect(SwRleData *rle, const SwBBox* clip)
_replaceClipSpan(rle, spans, spansEnd - spans);
TVGLOG("SW_ENGINE", "Using ClipRect!");
TVGLOG("SW_ENGINE", "Using Box Clipping!");
}

View File

@@ -22,7 +22,6 @@
#include "tvgSwCommon.h"
#include "tvgMath.h"
#include "tvgLines.h"
/************************************************************************/
/* Internal Class Implementation */
@@ -102,9 +101,9 @@ static bool _outlineClose(SwOutline& outline)
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);
@@ -163,10 +162,10 @@ 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)
{
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);
@@ -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,30 +296,30 @@ 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;
}
@@ -355,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;
@@ -499,7 +498,6 @@ bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& trans
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

View File

@@ -441,13 +441,23 @@ 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;
return;
}
//small size
if (firstArc) {
firstArc = false;
//process corner if necessary
@@ -662,7 +672,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 +725,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);
}
}
@@ -845,31 +855,25 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
//A contour cannot start with a cubic control point
if (type == SW_CURVE_TYPE_CUBIC) return false;
++types;
auto closed = outline.closed.data ? outline.closed.data[i]: false;
_beginSubPath(*stroke, start, closed);
while (pt < limit) {
++pt;
++types;
//emit a signel line_to
//emit a single line_to
if (types[0] == SW_CURVE_TYPE_POINT) {
++pt;
++types;
_lineTo(*stroke, *pt);
//types cubic
} else {
if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
pt += 2;
types += 2;
if (pt <= limit) {
_cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
continue;
}
_cubicTo(*stroke, pt[-2], pt[-1], start);
goto close;
pt += 3;
types += 3;
if (pt <= limit) _cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
else if (pt - 1 == limit) _cubicTo(*stroke, pt[-2], pt[-1], start);
else goto close;
}
}
close:

View File

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

View File

@@ -26,7 +26,7 @@
#include "tvgPaint.h"
enum Status : uint8_t {Synced = 0, Updating, Drawing, Damanged};
enum Status : uint8_t {Synced = 0, Updating, Drawing, Damaged};
struct Canvas::Impl
{
@@ -42,7 +42,7 @@ struct Canvas::Impl
~Impl()
{
//make it sure any deffered jobs
//make it sure any deferred jobs
renderer->sync();
renderer->clear();
@@ -61,7 +61,7 @@ struct Canvas::Impl
Result push(unique_ptr<Paint> paint)
{
//You can not push paints during rendering.
//You cannot push paints during rendering.
if (status == Status::Drawing) return Result::InsufficientCondition;
auto p = paint.release();
@@ -91,7 +91,7 @@ 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};
@@ -108,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;
@@ -124,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;
@@ -136,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.
@@ -147,7 +147,7 @@ struct Canvas::Impl
if (vport == val) return Result::Success;
renderer->viewport(val);
vport = val;
status = Status::Damanged;
status = Status::Damaged;
return Result::Success;
}
};

View File

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

View File

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

View File

@@ -55,7 +55,6 @@ struct Fill::Impl
uint32_t cnt = 0;
FillSpread spread;
DuplicateMethod<Fill>* dup = nullptr;
uint8_t id;
~Impl()
{

View File

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

View File

@@ -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;

View File

@@ -294,10 +294,10 @@ LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
{
*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 +317,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)) {
@@ -392,7 +392,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mim
}
}
}
//Unkown MimeType. Try with the candidates in the order
//Unknown MimeType. Try with the candidates in the order
for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
auto loader = _find(static_cast<FileType>(i));
if (loader) {

View File

@@ -32,11 +32,11 @@
/************************************************************************/
#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 = {}; \
}
@@ -91,8 +91,8 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat
//No rotation and no skewing, still can try out clipping the rect region.
auto tryClip = false;
if ((!mathRightAngle(pm) || mathSkewed(pm))) tryClip = true;
if ((!mathRightAngle(rm) || mathSkewed(rm))) tryClip = true;
if ((!rightAngle(pm) || skewed(pm))) tryClip = true;
if ((!rightAngle(rm) || skewed(rm))) tryClip = true;
if (tryClip) return _clipRect(renderer, pts, pm, rm, before);
@@ -102,8 +102,8 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat
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;
@@ -164,6 +164,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
ret->pImpl->opacity = opacity;
if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method);
if (clipper) ret->pImpl->clip(clipper->duplicate());
return ret;
}
@@ -172,7 +173,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
bool Paint::Impl::rotate(float degree)
{
if (tr.overriding) return false;
if (mathEqual(degree, tr.degree)) return true;
if (tvg::equal(degree, tr.degree)) return true;
tr.degree = degree;
renderFlag |= RenderUpdateFlag::Transform;
@@ -183,7 +184,7 @@ bool Paint::Impl::rotate(float degree)
bool Paint::Impl::scale(float factor)
{
if (tr.overriding) return false;
if (mathEqual(factor, tr.scale)) return true;
if (tvg::equal(factor, tr.scale)) return true;
tr.scale = factor;
renderFlag |= RenderUpdateFlag::Transform;
@@ -194,7 +195,7 @@ bool Paint::Impl::scale(float factor)
bool Paint::Impl::translate(float x, float y)
{
if (tr.overriding) return false;
if (mathEqual(x, tr.m.e13) && mathEqual(y, tr.m.e23)) return true;
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;
@@ -207,11 +208,9 @@ bool Paint::Impl::render(RenderMethod* renderer)
{
if (opacity == 0) return true;
Compositor* cmp = nullptr;
RenderCompositor* cmp = nullptr;
/* Note: only ClipPath is processed in update() step.
Create a composition image. */
if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
if (compData && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
RenderRegion region;
PAINT_METHOD(region, bounds(renderer));
@@ -248,43 +247,49 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
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;
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, pm, viewport)) == Result::Success) {
P(target)->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 = P(target)->update(renderer, pm, 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);
@@ -294,9 +299,9 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
tr.cm = pm * tr.m;
PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
/* 3. Composition Post Processing */
/* 4. Composition Post Processing */
if (compFastTrack == Result::Success) renderer->viewport(viewport);
else if (childClipper) clips.pop();
else if (this->clipper) clips.pop();
return rd;
}
@@ -308,7 +313,7 @@ bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transforme
const auto& m = this->transform(origin);
//Case: No transformed, quick return!
if (!transformed || mathIdentity(&m)) {
if (!transformed || identity(&m)) {
PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
return ret;
}
@@ -351,12 +356,18 @@ 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;
}
mathIdentity(&tr.m);
tvg::identity(&tr.m);
tr.degree = 0.0f;
tr.scale = 1.0f;
tr.overriding = false;
@@ -437,15 +448,27 @@ Paint* Paint::duplicate() const noexcept
}
Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
Result Paint::clip(std::unique_ptr<Paint> clipper) noexcept
{
if (method == CompositeMethod::ClipPath && target && target->identifier() != TVG_CLASS_ID_SHAPE) {
TVGERR("RENDERER", "ClipPath only allows the Shape!");
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) 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) noexcept
return Result::Success;
}
BlendMethod Paint::blend() const noexcept
{
return pImpl->blendMethod;
}

View File

@@ -49,6 +49,7 @@ namespace tvg
{
Paint* paint = nullptr;
Composite* compData = nullptr;
Paint* clipper = nullptr;
RenderMethod* renderer = nullptr;
struct {
Matrix m; //input matrix
@@ -67,8 +68,8 @@ namespace tvg
m.e31 = 0.0f;
m.e32 = 0.0f;
m.e33 = 1.0f;
mathScale(&m, scale, scale);
mathRotate(&m, degree);
tvg::scale(&m, scale, scale);
tvg::rotate(&m, degree);
}
} tr;
BlendMethod blendMethod;
@@ -76,7 +77,6 @@ namespace tvg
uint8_t ctxFlag;
uint8_t opacity;
uint8_t refCnt = 0; //reference count
uint8_t id; //TODO: deprecated, remove it
Impl(Paint* pnt) : paint(pnt)
{
@@ -89,6 +89,7 @@ namespace tvg
if (P(compData->target)->unref() == 0) delete(compData->target);
free(compData);
}
if (clipper && P(clipper)->unref() == 0) delete(clipper);
if (renderer && (renderer->unref() == 0)) delete(renderer);
}
@@ -106,7 +107,7 @@ namespace tvg
bool transform(const Matrix& m)
{
tr.m = m;
if (&tr.m != &m) tr.m = m;
tr.overriding = true;
renderFlag |= RenderUpdateFlag::Transform;
@@ -121,6 +122,20 @@ namespace tvg
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);
}
}
this->clipper = clp;
if (!clp) return;
P(clipper)->ref();
}
bool composite(Paint* source, Paint* target, CompositeMethod method)
{
//Invalid case

View File

@@ -20,6 +20,7 @@
* SOFTWARE.
*/
#include "tvgPaint.h"
#include "tvgPicture.h"
/************************************************************************/
@@ -73,11 +74,11 @@ bool Picture::Impl::needComposition(uint8_t opacity)
bool Picture::Impl::render(RenderMethod* renderer)
{
bool ret = false;
renderer->blend(picture->blend());
renderer->blend(PP(picture)->blendMethod);
if (surface) return renderer->renderImage(rd);
else if (paint) {
Compositor* cmp = nullptr;
RenderCompositor* cmp = nullptr;
if (needComp) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
renderer->beginComposite(cmp, CompositeMethod::None, 255);
@@ -134,7 +135,6 @@ Result Picture::Impl::load(ImageLoader* loader)
Picture::Picture() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
}
@@ -150,9 +150,15 @@ 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;
}

View File

@@ -60,7 +60,7 @@ 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;
Picture* picture = nullptr;

View File

@@ -24,6 +24,7 @@
#define _TVG_RENDER_H_
#include <math.h>
#include <cstdarg>
#include "tvgCommon.h"
#include "tvgArray.h"
#include "tvgLock.h"
@@ -36,9 +37,8 @@ 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};
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.
@@ -48,7 +48,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
@@ -62,11 +62,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;
@@ -80,21 +80,10 @@ struct Surface
};
struct Compositor
struct RenderCompositor
{
CompositeMethod method;
uint8_t opacity;
};
struct Vertex
{
Point pt;
Point uv;
};
struct Polygon
{
Vertex vertex[3];
uint8_t opacity;
};
struct RenderRegion
@@ -270,11 +259,66 @@ 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 invalid = 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] = std::min(va_arg(args, int), 255);
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;
}
};
class RenderMethod
{
private:
@@ -287,7 +331,7 @@ public:
virtual ~RenderMethod() {}
virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
virtual RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
virtual 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;
@@ -298,14 +342,17 @@ 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) = 0;
virtual bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
virtual bool endComposite(RenderCompositor* cmp) = 0;
virtual bool prepare(RenderEffect* effect) = 0;
virtual bool effect(RenderCompositor* cmp, const RenderEffect* effect, uint8_t opacity, bool direct) = 0;
};
static inline bool MASK_REGION_MERGING(CompositeMethod method)
@@ -374,7 +421,6 @@ static inline uint8_t MULTIPLY(uint8_t c, uint8_t a)
return (((c) * (a) + 0xff) >> 8);
}
}
#endif //_TVG_RENDER_H_

View File

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

View File

@@ -20,15 +20,32 @@
* SOFTWARE.
*/
#include <cstdarg>
#include "tvgScene.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
Result Scene::Impl::resetEffects()
{
if (effects) {
for (auto e = effects->begin(); e < effects->end(); ++e) {
delete(*e);
}
delete(effects);
effects = nullptr;
}
return Result::Success;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Scene::Scene() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_SCENE;
}
@@ -44,9 +61,15 @@ unique_ptr<Scene> Scene::gen() noexcept
}
uint32_t Scene::identifier() noexcept
TVG_DEPRECATED uint32_t Scene::identifier() noexcept
{
return TVG_CLASS_ID_SCENE;
return (uint32_t) Type::Scene;
}
Type Scene::type() const noexcept
{
return Type::Scene;
}
@@ -54,7 +77,11 @@ Result Scene::push(unique_ptr<Paint> paint) noexcept
{
auto p = paint.release();
if (!p) return Result::MemoryCorruption;
PP(p)->ref();
P(p)->ref();
//Relocated the paint to the current scene space
P(p)->renderFlag |= RenderUpdateFlag::Transform;
pImpl->paints.push_back(p);
return Result::Success;
@@ -79,3 +106,34 @@ 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;
}
default: break;
}
if (!re) return Result::InvalidArguments;
pImpl->effects->push(re);
return Result::Success;
}

View File

@@ -23,10 +23,9 @@
#ifndef _TVG_SCENE_H_
#define _TVG_SCENE_H_
#include <float.h>
#include "tvgMath.h"
#include "tvgPaint.h"
struct SceneIterator : Iterator
{
list<Paint*>* paints;
@@ -61,8 +60,10 @@ struct Scene::Impl
list<Paint*> paints;
RenderData rd = nullptr;
Scene* scene = nullptr;
uint8_t opacity; //for composition
bool needComp = false; //composite or not
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
Array<RenderEffect*>* effects = nullptr;
uint8_t opacity; //for composition
bool needComp = false; //composite or not
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);
}
@@ -83,12 +86,15 @@ struct Scene::Impl
{
if (opacity == 0 || paints.empty()) return false;
//post effects requires composition
if (effects) return true;
//Masking may require composition (even if opacity == 255)
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 (PP(scene)->blendMethod != BlendMethod::Normal) return true;
//Half translucent requires intermediate composition.
if (opacity == 255) return false;
@@ -96,31 +102,34 @@ struct Scene::Impl
//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 false;
return true;
}
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
{
this->vport = renderer->viewport();
if ((needComp = 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;
}
for (auto paint : paints) {
paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
}
return nullptr;
}
bool render(RenderMethod* renderer)
{
Compositor* cmp = nullptr;
RenderCompositor* cmp = nullptr;
auto ret = true;
renderer->blend(scene->blend());
renderer->blend(PP(scene)->blendMethod);
if (needComp) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
@@ -131,7 +140,16 @@ struct Scene::Impl
ret &= paint->pImpl->render(renderer);
}
if (cmp) renderer->endComposite(cmp);
if (cmp) {
//Apply post effects if any.
if (effects) {
auto direct = effects->count == 1 ? true : false;
for (auto e = effects->begin(); e < effects->end(); ++e) {
renderer->effect(cmp, *e, opacity, direct);
}
}
renderer->endComposite(cmp);
}
return ret;
}
@@ -155,7 +173,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->rd || renderer->prepare(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)
@@ -203,6 +237,8 @@ struct Scene::Impl
dup->paints.push_back(cdup);
}
if (effects) TVGERR("RENDERER", "TODO: Duplicate Effects?");
return scene;
}
@@ -218,6 +254,8 @@ struct Scene::Impl
{
return new SceneIterator(&paints);
}
Result resetEffects();
};
#endif //_TVG_SCENE_H_

View File

@@ -34,7 +34,6 @@
Shape :: Shape() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
}
@@ -52,7 +51,13 @@ unique_ptr<Shape> Shape::gen() noexcept
uint32_t Shape::identifier() noexcept
{
return TVG_CLASS_ID_SHAPE;
return (uint32_t) Type::Shape;
}
Type Shape::type() const noexcept
{
return Type::Shape;
}
@@ -151,14 +156,14 @@ Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
}
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;
@@ -409,12 +414,6 @@ Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
}
bool Shape::strokeTrim(float* begin, float* end) const noexcept
{
return pImpl->strokeTrim(begin, end);
}
Result Shape::fill(FillRule r) noexcept
{
pImpl->rs.rule = r;

View File

@@ -53,9 +53,9 @@ struct Shape::Impl
{
if (!rd) return false;
Compositor* cmp = nullptr;
RenderCompositor* cmp = nullptr;
renderer->blend(shape->blend());
renderer->blend(PP(shape)->blendMethod);
if (needComp) {
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
@@ -83,7 +83,7 @@ struct Shape::Impl
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;
@@ -106,7 +106,7 @@ struct Shape::Impl
if ((needComp = 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;
}
@@ -219,7 +219,7 @@ 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;
rs.stroke->trim.begin = begin;

View File

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

View File

@@ -37,7 +37,6 @@
Text::Text() : pImpl(new Impl(this))
{
Paint::pImpl->id = TVG_CLASS_ID_TEXT;
}
@@ -111,7 +110,7 @@ unique_ptr<Text> Text::gen() noexcept
}
uint32_t Text::identifier() noexcept
Type Text::type() const noexcept
{
return TVG_CLASS_ID_TEXT;
return Type::Text;
}

View File

@@ -90,7 +90,7 @@ struct Text::Impl
bool render(RenderMethod* renderer)
{
if (!loader) return true;
renderer->blend(paint->blend());
renderer->blend(PP(paint)->blendMethod);
return PP(shape)->render(renderer);
}
@@ -115,7 +115,7 @@ struct Text::Impl
auto fill = P(shape)->rs.fill;
if (fill && P(shape)->flag & RenderUpdateFlag::Gradient) {
auto scale = 1.0f / loader->scale;
if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
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;

View File

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

View File

@@ -1,6 +1,6 @@
#!/bin/bash -e
VERSION=0.14.10
VERSION=0.15.5
# Uncomment and set a git hash to use specific commit instead of tag.
#GIT_COMMIT=