diff --git a/modules/svg/SCsub b/modules/svg/SCsub index ae3e1bdedbea..c4d7671fb30b 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -38,9 +38,6 @@ thirdparty_sources = [ "src/lib/tvgShape.cpp", "src/lib/tvgSwCanvas.cpp", "src/lib/tvgTaskScheduler.cpp", - "src/loaders/external_png/tvgPngLoader.cpp", - "src/loaders/jpg/tvgJpgd.cpp", - "src/loaders/jpg/tvgJpgLoader.cpp", "src/loaders/raw/tvgRawLoader.cpp", "src/loaders/svg/tvgSvgCssStyle.cpp", "src/loaders/svg/tvgSvgLoader.cpp", @@ -48,27 +45,23 @@ thirdparty_sources = [ "src/loaders/svg/tvgSvgSceneBuilder.cpp", "src/loaders/svg/tvgSvgUtil.cpp", "src/loaders/svg/tvgXmlParser.cpp", - "src/loaders/tvg/tvgTvgBinInterpreter.cpp", - "src/loaders/tvg/tvgTvgLoader.cpp", - "src/savers/tvg/tvgTvgSaver.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env_svg.Prepend(CPPPATH=[thirdparty_dir + "inc"]) +# Enable ThorVG static object linking. +env_svg.Append(CPPDEFINES=["TVG_STATIC"]) + env_thirdparty = env_svg.Clone() env_thirdparty.disable_warnings() env_thirdparty.Prepend( CPPPATH=[ thirdparty_dir + "src/lib", thirdparty_dir + "src/lib/sw_engine", - thirdparty_dir + "src/loaders/external_png", - thirdparty_dir + "src/loaders/jpg", thirdparty_dir + "src/loaders/raw", thirdparty_dir + "src/loaders/svg", - thirdparty_dir + "src/loaders/tvg", - thirdparty_dir + "src/savers/tvg", ] ) # Also requires libpng headers diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index 763915591482..a542bbc23413 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -107,7 +107,7 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref p_image, const ui // Note: memalloc here, be sure to memfree before any return. uint32_t *buffer = (uint32_t *)memalloc(sizeof(uint32_t) * width * height); - tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888_STRAIGHT); + tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888S); if (res != tvg::Result::Success) { memfree(buffer); ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't set target on ThorVG canvas."); diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp index 2f48f1564c84..b98911de10df 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.cpp +++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -271,7 +271,7 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) { } std::unique_ptr sw_canvas = tvg::SwCanvas::gen(); - res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888_STRAIGHT); + res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888S); if (res != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas."); } diff --git a/modules/text_server_fb/thorvg_svg_in_ot.cpp b/modules/text_server_fb/thorvg_svg_in_ot.cpp index 773b103c0137..785d6dbe4db8 100644 --- a/modules/text_server_fb/thorvg_svg_in_ot.cpp +++ b/modules/text_server_fb/thorvg_svg_in_ot.cpp @@ -256,7 +256,7 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) { } std::unique_ptr sw_canvas = tvg::SwCanvas::gen(); - res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888_STRAIGHT); + res = sw_canvas->target((uint32_t *)p_slot->bitmap.buffer, (int)p_slot->bitmap.width, (int)p_slot->bitmap.width, (int)p_slot->bitmap.rows, tvg::SwCanvas::ARGB8888S); if (res != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_Outline, "Failed to create SVG canvas."); } diff --git a/thirdparty/README.md b/thirdparty/README.md index 27705de9978b..505350297121 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -713,7 +713,7 @@ instead of `miniz.h` as an external dependency. ## thorvg - Upstream: https://github.com/thorvg/thorvg -- Version: 0.9.0 (a744006aa1edb918bacf0a415d0a57ca058e25f4, 2023) +- Version: 0.10.0 (b8c605583fd7de73209a93a1238e1ba72cce2e8f, 2023) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS index c5f8529da96c..da15a2a024c7 100644 --- a/thirdparty/thorvg/AUTHORS +++ b/thirdparty/thorvg/AUTHORS @@ -20,3 +20,4 @@ Vincenzo Pupillo EunSik Jeong Samsung Electronics Co., Ltd RafaƂ Mikrut +Martin Capitanio diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 89caf0161dbf..87125418fb08 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -1,17 +1,9 @@ #ifndef THORVG_CONFIG_H #define THORVG_CONFIG_H -#define THORVG_SW_RASTER_SUPPORT 1 +#define THORVG_SW_RASTER_SUPPORT -#define THORVG_SVG_LOADER_SUPPORT 1 +#define THORVG_SVG_LOADER_SUPPORT -#define THORVG_PNG_LOADER_SUPPORT 1 - -#define THORVG_TVG_LOADER_SUPPORT 1 - -#define THORVG_TVG_SAVER_SUPPORT 1 - -#define THORVG_JPG_LOADER_SUPPORT 1 - -#define THORVG_VERSION_STRING "0.9.0" +#define THORVG_VERSION_STRING "0.10.0" #endif diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h index bde045f76cd9..897296fa9d61 100644 --- a/thirdparty/thorvg/inc/thorvg.h +++ b/thirdparty/thorvg/inc/thorvg.h @@ -18,43 +18,48 @@ #include #include #include +#include #ifdef TVG_API #undef TVG_API #endif -#if defined(_WIN32) && !defined(__clang__) - #if TVG_BUILD - #if TVG_EXPORT +#ifndef TVG_STATIC + #ifdef _WIN32 + #if TVG_BUILD #define TVG_API __declspec(dllexport) #else - #define TVG_API + #define TVG_API __declspec(dllimport) #endif + #elif (defined(__SUNPRO_C) || defined(__SUNPRO_CC)) + #define TVG_API __global #else - #define TVG_API - #endif - #define TVG_DEPRECATED __declspec(deprecated) -#else - #if TVG_BUILD - #if TVG_EXPORT - #define TVG_API __attribute__ ((visibility ("default"))) + #if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__INTEL_COMPILER) + #define TVG_API __attribute__ ((visibility("default"))) #else #define TVG_API #endif - #else - #define TVG_API #endif - #define TVG_DEPRECATED __attribute__ ((__deprecated__)) +#else + #define TVG_API +#endif + +#ifdef TVG_DEPRECATED + #undef TVG_DEPRECATED #endif -#ifdef __cplusplus -extern "C" { +#ifdef _WIN32 + #define TVG_DEPRECATED __declspec(deprecated) +#elif __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) + #define TVG_DEPRECATED __attribute__ ((__deprecated__)) +#else + #define TVG_DEPRECATED #endif #define _TVG_DECLARE_PRIVATE(A) \ -protected: \ struct Impl; \ Impl* pImpl; \ +protected: \ A(const A&) = delete; \ const A& operator=(const A&) = delete; \ A() @@ -63,23 +68,14 @@ protected: \ A() = delete; \ ~A() = delete -#define _TVG_DECLARE_ACCESSOR() \ - friend Canvas; \ - friend Scene; \ - friend Picture; \ - friend Accessor; \ - friend IteratorAccessor - +#define _TVG_DECLARE_ACCESSOR(A) \ + friend A namespace tvg { class RenderMethod; -class IteratorAccessor; -class Scene; -class Picture; -class Canvas; -class Accessor; +class Animation; /** * @defgroup ThorVG ThorVG @@ -102,6 +98,7 @@ enum class Result Unknown ///< The value returned in all other cases. }; + /** * @brief Enumeration specifying the values of the path commands accepted by TVG. * @@ -116,6 +113,7 @@ enum class PathCommand CubicTo ///< Draws a cubic Bezier curve from the current point to the given point using two given control points and sets a new value of the current point. This command expects 3 points: the 1st control-point, the 2nd control-point, the end-point of the curve. }; + /** * @brief Enumeration determining the ending type of a stroke in the open sub-paths. */ @@ -126,6 +124,7 @@ enum class StrokeCap Butt ///< The stroke ends exactly at each of the two end-points of a sub-path. For zero length sub-paths no stroke is rendered. }; + /** * @brief Enumeration determining the style used at the corners of joined stroked path segments. */ @@ -136,6 +135,7 @@ enum class StrokeJoin Miter ///< The outer corner of the joined path segments is spiked. The spike is created by extension beyond the join point of the outer edges of the stroke until they intersect. In case the extension goes beyond the limit, the join style is converted to the Bevel style. }; + /** * @brief Enumeration specifying how to fill the area outside the gradient bounds. */ @@ -146,6 +146,7 @@ enum class FillSpread Repeat ///< The gradient pattern is repeated continuously beyond the gradient area until the expected region is filled. }; + /** * @brief Enumeration specifying the algorithm used to establish which parts of the shape are treated as the inside of the shape. */ @@ -155,18 +156,57 @@ enum class FillRule EvenOdd ///< A line from the point to a location outside the shape is drawn and its intersections with the path segments of the shape are counted. If the number of intersections is an odd number, the point is inside the shape. }; + /** * @brief Enumeration indicating the method used in the composition of two objects - the target and the source. + * + * Notation: S(Source), T(Target), SA(Source Alpha), TA(Target Alpha) + * + * @see Paint::composite() */ 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. - AlphaMask, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which alpha intersects with the target is visible. - InvAlphaMask, ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which alpha is not covered by the target is visible. - LumaMask ///< The source pixels are converted to the grayscale (luma value) and alpha blended with the target. As a result, only the part of the source, which intersects with the target is visible. @since 0.9 + 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. + 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 + InvLumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the complement to the compositing target's pixels. @BETA_API + AddMask, ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) @BETA_API + SubtractMask, ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) @BETA_API + IntersectMask, ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) @BETA_API + DifferenceMask ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) @BETA_API }; + +/** + * @brief Enumeration indicates the method used for blending paint. Please refer to the respective formulas for each method. + * + * Notation: S(source paint as the top layer), D(destination as the bottom layer), Sa(source paint alpha), Da(destination alpha) + * + * @see Paint::blend() + * + * @BETA_API + */ +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) + 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) +}; + + /** * @brief Enumeration specifying the engine type used for the graphics backend. For multiple backends bitwise operation is allowed. */ @@ -293,7 +333,7 @@ class TVG_API Paint * The values of the matrix can be set by the transform() API, as well by the translate(), * scale() and rotate(). In case no transformation was applied, the identity matrix is returned. * - * @retval The augmented transformation matrix. + * @return The augmented transformation matrix. * * @since 0.4 */ @@ -321,6 +361,21 @@ class TVG_API Paint */ Result composite(std::unique_ptr target, CompositeMethod method) noexcept; + /** + * @brief Sets the blending method for the paint object. + * + * The blending feature allows you to combine colors to create visually appealing effects, including transparency, lighting, shading, and color mixing, among others. + * its process involves the combination of colors or images from the source paint object with the destination (the lower layer image) using blending operations. + * The blending operation is determined by the chosen @p BlendMethod, which specifies how the colors or images are combined. + * + * @param[in] method The blending method to be set. + * + * @return Result::Success when the blending method is successfully set. + * + * @BETA_API + */ + Result blend(BlendMethod method) const noexcept; + /** * @brief Gets the bounding box of the paint object before any transformation. * @@ -333,6 +388,7 @@ class TVG_API Paint * * @note The bounding box doesn't indicate the final rendered region. It's the smallest rectangle that encloses the object. * @see Paint::bounds(float* x, float* y, float* w, float* h, bool transformed); + * @deprecated Use bounds(float* x, float* y, float* w, float* h, bool transformed) instead */ TVG_DEPRECATED Result bounds(float* x, float* y, float* w, float* h) const noexcept; @@ -380,6 +436,15 @@ class TVG_API Paint */ CompositeMethod composite(const Paint** target) const noexcept; + /** + * @brief Gets the blending method of the object. + * + * @return The blending method + * + * @BETA_API + */ + BlendMethod blend() const noexcept; + /** * @brief Return the unique id value of the paint instance. * @@ -389,7 +454,6 @@ class TVG_API Paint */ uint32_t identifier() const noexcept; - _TVG_DECLARE_ACCESSOR(); _TVG_DECLARE_PRIVATE(Paint); }; @@ -525,14 +589,25 @@ class TVG_API Canvas * * @return Result::Success when succeed. */ - Result reserve(uint32_t n) noexcept; + TVG_DEPRECATED Result reserve(uint32_t n) noexcept; + + /** + * @brief Returns the list of the paints that currently held by the Canvas. + * + * 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(). + * @see Canvas::sync() + * + * @BETA_API + */ + std::list& paints() noexcept; /** * @brief Passes drawing elements to the Canvas using Paint objects. * * Only pushed paints in the canvas will be drawing targets. * They are retained by the canvas until you call Canvas::clear(). - * If you know the number of the pushed objects in advance, please call Canvas::reserve(). * * @param[in] paint A Paint object to be drawn. * @@ -541,7 +616,7 @@ class TVG_API Canvas * @retval Result::InsufficientCondition An internal error. * * @note The rendering order of the paints is the same as the order as they were pushed into the canvas. Consider sorting the paints before pushing them if you intend to use layering. - * @see Canvas::reserve() + * @see Canvas::paints() * @see Canvas::clear() */ virtual Result push(std::unique_ptr paint) noexcept; @@ -555,6 +630,8 @@ class TVG_API Canvas * @return Result::Success when succeed, Result::InsufficientCondition otherwise. * * @warning If you don't free the paints they become dangled. They are supposed to be reused, otherwise you are responsible for their lives. Thus please use the @p free argument only when you know how it works, otherwise it's not recommended. + * @see Canvas::push() + * @see Canvas::paints() */ virtual Result clear(bool free = true) noexcept; @@ -829,7 +906,7 @@ class TVG_API Shape final : public Paint * * @note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse. */ - Result appendRect(float x, float y, float w, float h, float rx, float ry) noexcept; + Result appendRect(float x, float y, float w, float h, float rx = 0, float ry = 0) noexcept; /** * @brief Appends an ellipse to the path. @@ -905,7 +982,7 @@ class TVG_API Shape final : public Paint * * @return Result::Success when succeed, Result::FailedAllocation otherwise. */ - Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept; + Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; /** * @brief Sets the gradient fill of the stroke for all of the figures from the path. @@ -953,6 +1030,18 @@ class TVG_API Shape final : public Paint */ Result stroke(StrokeJoin join) noexcept; + + /** + * @brief Sets the stroke miterlimit. + * + * @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join, when the @c StrokeJoin::Miter join style is set. The default value is 4. + * + * @return Result::Success when succeed, Result::NonSupport unsupported value, Result::FailedAllocation otherwise. + * + * @BETA_API + */ + Result strokeMiterlimit(float miterlimit) noexcept; + /** * @brief Sets the solid color for all of the figures from the path. * @@ -968,7 +1057,7 @@ class TVG_API Shape final : public Paint * @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) noexcept; + Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; /** * @brief Sets the gradient fill for all of the figures from the path. @@ -999,7 +1088,8 @@ class TVG_API Shape final : public Paint * @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option). * * @return Result::Success when succeed, Result::FailedAllocation otherwise. - * @BETA_API + * + * @since 0.10 */ Result order(bool strokeFirst) noexcept; @@ -1039,7 +1129,7 @@ class TVG_API Shape final : public Paint * * @return Result::Success when succeed. */ - Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept; + Result fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept; /** * @brief Gets the fill rule value. @@ -1065,7 +1155,7 @@ class TVG_API Shape final : public Paint * * @return Result::Success when succeed, Result::InsufficientCondition otherwise. */ - Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept; + Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept; /** * @brief Gets the pointer to the gradient fill of the stroke. @@ -1097,6 +1187,15 @@ class TVG_API Shape final : public Paint */ StrokeJoin strokeJoin() const noexcept; + /** + * @brief Gets the stroke miterlimit. + * + * @return The stroke miterlimit value when succeed, 4 if no stroke was set. + * + * @BETA_API + */ + float strokeMiterlimit() const noexcept; + /** * @brief Creates a new Shape object. * @@ -1120,10 +1219,11 @@ class TVG_API Shape final : public Paint /** * @class Picture * - * @brief A class representing an image read in one of the supported formats: raw, svg, png, jpg and etc. + * @brief A class representing an image read in one of the supported formats: raw, svg, png, jpg, lottie(json) and etc. * Besides the methods inherited from the Paint, it provides methods to load & draw images on the canvas. * * @note Supported formats are depended on the available TVG loaders. + * @note See Animation class if the picture data is animatable. */ class TVG_API Picture final : public Paint { @@ -1240,8 +1340,8 @@ class TVG_API Picture final : public Paint * @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh. * @param[in] triangleCnt The number of Polygons(triangles) provided, or 0 to remove the mesh. * - * @return Result::Success When succeed. - * @return Result::Unknown If fails + * @retval Result::Success When succeed. + * @retval Result::Unknown If fails * * @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect. * @warning Please do not use it, this API is not official one. It could be modified in the next version. @@ -1264,15 +1364,6 @@ class TVG_API Picture final : public Paint */ uint32_t mesh(const Polygon** triangles) const noexcept; - /** - * @brief Gets the position and the size of the loaded SVG picture. - * - * @warning Please do not use it, this API is not official one. It could be modified in the next version. - * - * @BETA_API - */ - Result viewbox(float* x, float* y, float* w, float* h) const noexcept; - /** * @brief Creates a new Picture object. * @@ -1289,6 +1380,7 @@ class TVG_API Picture final : public Paint */ static uint32_t identifier() noexcept; + _TVG_DECLARE_ACCESSOR(Animation); _TVG_DECLARE_PRIVATE(Picture); }; @@ -1314,14 +1406,14 @@ class TVG_API Scene final : public Paint * * Only the paints pushed into the scene will be the drawn targets. * The paints are retained by the scene until Scene::clear() is called. - * If you know the number of the pushed objects in advance, please call Scene::reserve(). * * @param[in] paint A Paint object to be drawn. * * @return Result::Success when succeed, Result::MemoryCorruption otherwise. * * @note The rendering order of the paints is the same as the order as they were pushed. Consider sorting the paints before pushing them if you intend to use layering. - * @see Scene::reserve() + * @see Scene::paints() + * @see Scene::clear() */ Result push(std::unique_ptr paint) noexcept; @@ -1335,7 +1427,21 @@ class TVG_API Scene final : public Paint * * @return Result::Success when succeed, Result::FailedAllocation otherwise. */ - Result reserve(uint32_t size) noexcept; + TVG_DEPRECATED Result reserve(uint32_t size) noexcept; + + /** + * @brief Returns the list of the paints that currently held by the Scene. + * + * 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(). + * @see Canvas::sync() + * @see Scene::push() + * @see Scene::clear() + * + * @BETA_API + */ + std::list& paints() noexcept; /** * @brief Sets the total number of the paints pushed into the scene to be zero. @@ -1386,10 +1492,10 @@ class TVG_API SwCanvas final : public Canvas */ enum Colorspace { - 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. - ABGR8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. - ARGB8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. + ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. (a << 24 | b << 16 | g << 8 | r) + ARGB8888, ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. (a << 24 | r << 16 | g << 8 | b) + ABGR8888S, ///< @BETA_API The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. + ARGB8888S, ///< @BETA_API The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. }; /** @@ -1544,6 +1650,101 @@ class TVG_API Initializer final }; +/** + * @class Animation + * + * @brief The Animation class enables manipulation of animatable images. + * + * This class supports the display and control of animation frames. + * + * @BETA_API + */ + +class TVG_API Animation +{ +public: + ~Animation(); + + /** + * @brief Specifies the current frame in the animation. + * + * @param[in] no The index of the animation frame to be displayed. The index should be less than the totalFrame(). + * + * @retval Result::Success Successfully set the frame. + * @retval Result::InsufficientCondition No animatable data loaded from the Picture. + * @retval Result::NonSupport The Picture data does not support animations. + * + * @see totalFrame() + * + * @BETA_API + */ + Result frame(uint32_t no) noexcept; + + /** + * @brief Retrieves a picture instance associated with this animation instance. + * + * This function provides access to the picture instance that can be used to load animation formats, such as Lottie(json). + * After setting up the picture, it can be pushed to the designated canvas, enabling control over animation frames + * with this Animation instance. + * + * @return A picture instance that is tied to this animation. + * + * @warning The picture instance is owned by Animation. It should not be deleted manually. + * + * @BETA_API + */ + Picture* picture() const noexcept; + + /** + * @brief Retrieves the current frame number of the animation. + * + * @return The current frame number of the animation, between 0 and totalFrame() - 1. + * + * @note If the Picture is not properly configured, this function will return 0. + * + * @see Animation::frame(uint32_t no) + * @see Animation::totalFrame() + * + * @BETA_API + */ + uint32_t curFrame() const noexcept; + + /** + * @brief Retrieves the total number of frames in the animation. + * + * @return The total number of frames in the animation. + * + * @note Frame numbering starts from 0. + * @note If the Picture is not properly configured, this function will return 0. + * + * @BETA_API + */ + uint32_t totalFrame() const noexcept; + + /** + * @brief Retrieves the duration of the animation in seconds. + * + * @return The duration of the animation in seconds. + * + * @note If the Picture is not properly configured, this function will return 0. + * + * @BETA_API + */ + float duration() const noexcept; + + /** + * @brief Creates a new Animation object. + * + * @return A new Animation object. + * + * @BETA_API + */ + static std::unique_ptr gen() noexcept; + + _TVG_DECLARE_PRIVATE(Animation); +}; + + /** * @class Saver * @@ -1629,7 +1830,7 @@ class TVG_API Saver final * * @warning We strongly warn you not to change the paints of a scene unless you really know the design-structure. * - * @BETA_API + * @since 0.10 */ class TVG_API Accessor final { @@ -1645,8 +1846,6 @@ class TVG_API Accessor final * @return Return the given @p picture instance. * * @note The bitmap based picture might not have the scene-tree. - * - * @BETA_API */ std::unique_ptr set(std::unique_ptr picture, std::function func) noexcept; @@ -1654,20 +1853,39 @@ class TVG_API Accessor final * @brief Creates a new Accessor object. * * @return A new Accessor object. - * - * @BETA_API */ static std::unique_ptr gen() noexcept; _TVG_DECLARE_PRIVATE(Accessor); }; -/** @}*/ -} //namespace +/** + * @brief The cast() function is a utility function used to cast a 'Paint' to type 'T'. + * + * @BETA_API + */ +template +std::unique_ptr cast(Paint* paint) +{ + return std::unique_ptr(static_cast(paint)); +} -#ifdef __cplusplus + +/** + * @brief The cast() function is a utility function used to cast a 'Fill' to type 'T'. + * + * @BETA_API + */ +template +std::unique_ptr cast(Fill* fill) +{ + return std::unique_ptr(static_cast(fill)); } -#endif + + +/** @}*/ + +} //namespace #endif //_THORVG_H_ diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h index 0e9029bf7372..3d68b56fb88e 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwCommon.h @@ -99,15 +99,11 @@ struct SwSize struct SwOutline { - SwPoint* pts; //the outline's points - uint32_t ptsCnt; //number of points in the glyph - uint32_t reservedPtsCnt; - uint32_t* cntrs; //the contour end points - uint16_t cntrsCnt; //number of contours in glyph - uint16_t reservedCntrsCnt; - uint8_t* types; //curve type - bool* closed; //opened or closed path? - FillRule fillRule; + Array pts; //the outline's points + Array cntrs; //the contour end points + Array types; //curve type + Array closed; //opened or closed path? + FillRule fillRule; }; struct SwSpan @@ -180,6 +176,7 @@ struct SwStroke SwPoint ptStartSubPath; SwFixed subPathLineLength; SwFixed width; + SwFixed miterlimit; StrokeCap cap; StrokeJoin join; @@ -238,18 +235,25 @@ struct SwImage bool scaled = false; //draw scaled image }; -struct SwBlender -{ - uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); - uint8_t (*luma)(uint8_t* c); -}; +typedef uint32_t(*SwBlender)(uint32_t s, uint32_t d, uint8_t a); //src, dst, alpha +typedef uint32_t(*SwJoin)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); //color channel join +typedef uint8_t(*SwAlpha)(uint8_t*); //blending alpha struct SwCompositor; struct SwSurface : Surface { - SwBlender blender; //mandatory + 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) + + SwAlpha alpha(CompositeMethod method) + { + auto idx = (int)(method) - 2; //0: None, 1: ClipPath + return alphas[idx > 3 ? 0 : idx]; //CompositeMethod has only four Matting methods. + } }; struct SwCompositor : Compositor @@ -273,15 +277,25 @@ static inline SwCoord TO_SWCOORD(float val) return SwCoord(val * 64.0f); } +static inline uint32_t JOIN(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) +{ + return (c0 << 24 | c1 << 16 | c2 << 8 | c3); +} + static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a) { return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) + ((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff)); } -static inline uint32_t INTERPOLATE(uint32_t a, uint32_t c0, uint32_t c1) +static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a) +{ + return (((((((s >> 8) & 0xff00ff) - ((d >> 8) & 0xff00ff)) * a) + (d & 0xff00ff00)) & 0xff00ff00) + ((((((s & 0xff00ff) - (d & 0xff00ff)) * a) >> 8) + (d & 0xff00ff)) & 0xff00ff)); +} + +static inline uint8_t INTERPOLATE8(uint8_t s, uint8_t d, uint8_t a) { - return (((((((c0 >> 8) & 0xff00ff) - ((c1 >> 8) & 0xff00ff)) * a) + (c1 & 0xff00ff00)) & 0xff00ff00) + ((((((c0 & 0xff00ff) - (c1 & 0xff00ff)) * a) >> 8) + (c1 & 0xff00ff)) & 0xff00ff)); + return ((s * a + 0xff) >> 8) + ((d * ~a + 0xff) >> 8); } static inline SwCoord HALF_STROKE(float width) @@ -289,6 +303,207 @@ static inline SwCoord HALF_STROKE(float width) return TO_SWCOORD(width * 0.5f); } +static inline uint8_t A(uint32_t c) +{ + return ((c) >> 24); +} + +static inline uint8_t IA(uint32_t c) +{ + return (~(c) >> 24); +} + +static inline uint8_t C1(uint32_t c) +{ + return ((c) >> 16); +} + +static inline uint8_t C2(uint32_t c) +{ + return ((c) >> 8); +} + +static inline uint8_t C3(uint32_t c) +{ + return (c); +} + +static inline uint32_t opBlendInterp(uint32_t s, uint32_t d, uint8_t a) +{ + return INTERPOLATE(s, d, a); +} + +static inline uint32_t opBlendNormal(uint32_t s, uint32_t d, uint8_t a) +{ + auto t = ALPHA_BLEND(s, a); + return t + ALPHA_BLEND(d, IA(t)); +} + +static inline uint32_t opBlendPreNormal(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + return s + ALPHA_BLEND(d, IA(s)); +} + +static inline uint32_t opBlendSrcOver(uint32_t s, TVG_UNUSED uint32_t d, TVG_UNUSED uint8_t a) +{ + return s; +} + +//TODO: BlendMethod could remove the alpha parameter. +static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + //if (s > d) => s - d + //else => d - s + auto c1 = (C1(s) > C1(d)) ? (C1(s) - C1(d)) : (C1(d) - C1(s)); + auto c2 = (C2(s) > C2(d)) ? (C2(s) - C2(d)) : (C2(d) - C2(s)); + auto c3 = (C3(s) > C3(d)) ? (C3(s) - C3(d)) : (C3(d) - C3(s)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + //A + B - 2AB + auto c1 = min(255, C1(s) + C1(d) - min(255, (C1(s) * C1(d)) << 1)); + auto c2 = min(255, C2(s) + C2(d) - min(255, (C2(s) * C2(d)) << 1)); + auto c3 = min(255, C3(s) + C3(d) - min(255, (C3(s) * C3(d)) << 1)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // s + d + auto c1 = min(C1(s) + C1(d), 255); + auto c2 = min(C2(s) + C2(d), 255); + auto c3 = min(C3(s) + C3(d), 255); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // s + d - s * d + auto c1 = C1(s) + C1(d) - MULTIPLY(C1(s), C1(d)); + auto c2 = C2(s) + C2(d) - MULTIPLY(C2(s), C2(d)); + auto c3 = C3(s) + C3(d) - MULTIPLY(C3(s), C3(d)); + return JOIN(255, c1, c2, c3); +} + + +static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // s * d + auto c1 = MULTIPLY(C1(s), C1(d)); + auto c2 = MULTIPLY(C2(s), C2(d)); + auto c3 = MULTIPLY(C3(s), C3(d)); + return JOIN(255, c1, c2, c3); +} + + +static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // if (2 * d < da) => 2 * s * d, + // else => 1 - 2 * (1 - s) * (1 - d) + auto c1 = (C1(d) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d)))); + auto c2 = (C2(d) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d)))); + auto c3 = (C3(d) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d)))); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendDarken(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // min(s, d) + auto c1 = min(C1(s), C1(d)); + auto c2 = min(C2(s), C2(d)); + auto c3 = min(C3(s), C3(d)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // max(s, d) + auto c1 = max(C1(s), C1(d)); + auto c2 = max(C2(s), C2(d)); + auto c3 = max(C3(s), C3(d)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendColorDodge(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // d / (1 - s) + auto is = 0xffffffff - s; + auto c1 = (C1(is) > 0) ? (C1(d) / C1(is)) : C1(d); + auto c2 = (C2(is) > 0) ? (C2(d) / C2(is)) : C2(d); + auto c3 = (C3(is) > 0) ? (C3(d) / C3(is)) : C3(d); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + // 1 - (1 - d) / s + auto id = 0xffffffff - d; + auto c1 = 255 - ((C1(s) > 0) ? (C1(id) / C1(s)) : C1(id)); + auto c2 = 255 - ((C2(s) > 0) ? (C2(id) / C2(s)) : C2(id)); + auto c3 = 255 - ((C3(s) > 0) ? (C3(id) / C3(s)) : C3(id)); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + auto c1 = (C1(s) < 128) ? min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d)))); + auto c2 = (C2(s) < 128) ? min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d)))); + auto c3 = (C3(s) < 128) ? min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d)))); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + //(255 - 2 * s) * (d * d) + (2 * s * b) + auto c1 = min(255, MULTIPLY(255 - min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d))); + auto c2 = min(255, MULTIPLY(255 - min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d))); + auto c3 = min(255, MULTIPLY(255 - min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d))); + return JOIN(255, c1, c2, c3); +} + +static inline uint32_t opMaskAdd(uint32_t s, uint32_t d, uint8_t a) +{ + return opBlendNormal(s, d, a); +} + +static inline uint32_t opMaskSubtract(uint32_t s, uint32_t d, uint8_t a) +{ + return ALPHA_BLEND(d, MULTIPLY(IA(s), a)); +} + +static inline uint32_t opMaskDifference(uint32_t s, uint32_t d, uint8_t a) +{ + auto t = ALPHA_BLEND(s, a); + return ALPHA_BLEND(t, IA(d)) + ALPHA_BLEND(d, IA(t)); +} + +static inline uint32_t opMaskIntersect(uint32_t s, uint32_t d, uint8_t a) +{ + return ALPHA_BLEND(d, MULTIPLY(IA(s), a)); +} + +static inline uint32_t opMaskPreAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + return opBlendPreNormal(s, d, a); +} + +static inline uint32_t opMaskPreSubtract(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + return ALPHA_BLEND(d, IA(s)); +} + +static inline uint32_t opMaskPreDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, IA(s)); +} + +static inline uint32_t opMaskPreIntersect(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +{ + return ALPHA_BLEND(d, MULTIPLY(a, IA(s))); +} + int64_t mathMultiply(int64_t a, int64_t b); int64_t mathDivide(int64_t a, int64_t b); int64_t mathMulDiv(int64_t a, int64_t b, int64_t c); @@ -315,8 +530,8 @@ void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* t bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid); void shapeFree(SwShape* shape); void shapeDelStroke(SwShape* shape); -bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable); -bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable); +bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable); +bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable); void shapeResetFill(SwShape* shape); void shapeResetStrokeFill(SwShape* shape); void shapeDelFill(SwShape* shape); @@ -333,11 +548,16 @@ void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); void imageReset(SwImage* image); void imageFree(SwImage* image); -bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable); +bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable); void fillReset(SwFill* fill); void fillFree(SwFill* fill); -void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len); -void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len); +//OPTIMIZE_ME: Skip the function pointer access +void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver. +void fillLinear(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 fillLinear(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); //masking ver. +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver. +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); //masking ver. SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias); SwRleData* rleRender(const SwBBox* bbox); @@ -358,11 +578,12 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx); bool rasterCompositor(SwSurface* surface); bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id); bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); -bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint32_t opacity); +bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity); bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a); bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id); bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h); -void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); +void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len); +void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len); void rasterUnpremultiply(Surface* surface); void rasterPremultiply(Surface* surface); bool rasterConvertCS(Surface* surface, ColorSpace to); diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp index 694bc352314c..1c6eb4e42897 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwFill.cpp @@ -33,7 +33,7 @@ #define FIXPT_SIZE (1<ctable) { fill->ctable = static_cast(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t))); @@ -46,13 +46,13 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* auto pColors = colors; - auto a = (pColors->a * opacity) / 255; + auto a = MULTIPLY(pColors->a, opacity); if (a < 255) fill->translucent = true; auto r = pColors->r; auto g = pColors->g; auto b = pColors->b; - auto rgba = surface->blender.join(r, g, b, a); + auto rgba = surface->join(r, g, b, a); auto inc = 1.0f / static_cast(GRADIENT_STOP_SIZE); auto pos = 1.5f * inc; @@ -70,17 +70,17 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* auto curr = colors + j; auto next = curr + 1; auto delta = 1.0f / (next->offset - curr->offset); - auto a2 = (next->a * opacity) / 255; + auto a2 = MULTIPLY(next->a, opacity); if (!fill->translucent && a2 < 255) fill->translucent = true; - auto rgba2 = surface->blender.join(next->r, next->g, next->b, a2); + auto rgba2 = surface->join(next->r, next->g, next->b, a2); while (pos < next->offset && i < GRADIENT_STOP_SIZE) { auto t = (pos - curr->offset) * delta; auto dist = static_cast(255 * t); auto dist2 = 255 - dist; - auto color = INTERPOLATE(dist2, rgba, rgba2); + auto color = INTERPOLATE(rgba, rgba2, dist2); fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24)); ++i; @@ -233,7 +233,7 @@ static inline uint32_t _pixel(const SwFill* fill, float pos) /* External Class Implementation */ /************************************************************************/ -void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len) +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) { auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX; auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY; @@ -244,16 +244,146 @@ void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative; auto det = rx * rx + ry * ry; - for (uint32_t i = 0 ; i < len ; ++i) { - *dst = _pixel(fill, sqrtf(det)); - ++dst; + if (opacity == 255) { + for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, alpha(cmp)); + det += detFirstDerivative; + detFirstDerivative += detSecondDerivative; + } + } else { + for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(_pixel(fill, sqrtf(det)), *dst, MULTIPLY(opacity, alpha(cmp))); + det += detFirstDerivative; + detFirstDerivative += detSecondDerivative; + } + } +} + + +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) +{ + auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX; + auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY; + + // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx + auto detSecondDerivative = fill->radial.detSecDeriv; + // detFirstDerivative = d(det)/dx + auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative; + auto det = rx * rx + ry * ry; + + for (uint32_t i = 0 ; i < len ; ++i, ++dst) { + *dst = op(_pixel(fill, sqrtf(det)), *dst, a); det += detFirstDerivative; detFirstDerivative += detSecondDerivative; } } -void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len) +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) +{ + auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX; + auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY; + + // detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx + auto detSecondDerivative = fill->radial.detSecDeriv; + // detFirstDerivative = d(det)/dx + auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative; + auto det = rx * rx + ry * ry; + + if (a == 255) { + for (uint32_t i = 0 ; i < len ; ++i, ++dst) { + auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255); + *dst = op2(tmp, *dst, 255); + det += detFirstDerivative; + detFirstDerivative += detSecondDerivative; + } + } else { + for (uint32_t i = 0 ; i < len ; ++i, ++dst) { + auto tmp = op(_pixel(fill, sqrtf(det)), *dst, 255); + auto tmp2 = op2(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, a); + det += detFirstDerivative; + detFirstDerivative += detSecondDerivative; + } + } +} + + +void fillLinear(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) +{ + //Rotation + float rx = x + 0.5f; + float ry = y + 0.5f; + 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 (opacity == 255) { + if (mathZero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(color, *dst, alpha(cmp)); + } + return; + } + + auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); + auto vMin = -vMax; + auto v = t + (inc * len); + + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) { + *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, alpha(cmp)); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, alpha(cmp)); + ++dst; + t += inc; + cmp += csize; + } + } + } else { + if (mathZero(inc)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) { + *dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity)); + } + return; + } + + auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); + auto vMin = -vMax; + auto v = t + (inc * len); + + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst, cmp += csize) { + *dst = opBlendNormal(_fixedPixel(fill, t2), *dst, MULTIPLY(alpha(cmp), opacity)); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + *dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, MULTIPLY(opacity, alpha(cmp))); + ++dst; + t += inc; + cmp += csize; + } + } + } +} + + +void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) { //Rotation float rx = x + 0.5f; @@ -263,7 +393,9 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, if (mathZero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); - rasterRGBA32(dst, color, 0, len); + for (uint32_t i = 0; i < len; ++i, ++dst) { + *dst = op(color, *dst, a); + } return; } @@ -275,16 +407,15 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, if (v < vMax && v > vMin) { auto t2 = static_cast(t * FIXPT_SIZE); auto inc2 = static_cast(inc * FIXPT_SIZE); - for (uint32_t j = 0; j < len; ++j) { - *dst = _fixedPixel(fill, t2); - ++dst; + for (uint32_t j = 0; j < len; ++j, ++dst) { + *dst = op(_fixedPixel(fill, t2), *dst, a); t2 += inc2; } //we have to fallback to float math } else { uint32_t counter = 0; while (counter++ < len) { - *dst = _pixel(fill, t / GRADIENT_STOP_SIZE); + *dst = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, a); ++dst; t += inc; } @@ -292,7 +423,82 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, } -bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable) +void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) +{ + //Rotation + float rx = x + 0.5f; + float ry = y + 0.5f; + 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)) { + auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); + if (a == 255) { + for (uint32_t i = 0; i < len; ++i, ++dst) { + auto tmp = op(color, *dst, a); + *dst = op2(tmp, *dst, 255); + } + } else { + for (uint32_t i = 0; i < len; ++i, ++dst) { + auto tmp = op(color, *dst, a); + auto tmp2 = op2(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, a); + } + } + return; + } + + auto vMax = static_cast(INT32_MAX >> (FIXPT_BITS + 1)); + auto vMin = -vMax; + auto v = t + (inc * len); + + if (a == 255) { + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst) { + auto tmp = op(_fixedPixel(fill, t2), *dst, 255); + *dst = op2(tmp, *dst, 255); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255); + *dst = op2(tmp, *dst, 255); + ++dst; + t += inc; + } + } + } else { + //we can use fixed point math + if (v < vMax && v > vMin) { + auto t2 = static_cast(t * FIXPT_SIZE); + auto inc2 = static_cast(inc * FIXPT_SIZE); + for (uint32_t j = 0; j < len; ++j, ++dst) { + auto tmp = op(_fixedPixel(fill, t2), *dst, 255); + auto tmp2 = op2(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, a); + t2 += inc2; + } + //we have to fallback to float math + } else { + uint32_t counter = 0; + while (counter++ < len) { + auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255); + auto tmp2 = op2(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, a); + ++dst; + t += inc; + } + } + } +} + + +bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) { if (!fill) return false; diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp index 9e215dbefd1a..4829a8c81d52 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwImage.cpp @@ -39,18 +39,10 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr image->outline = mpoolReqOutline(mpool, tid); auto outline = image->outline; - if (outline->reservedPtsCnt < 5) { - outline->reservedPtsCnt = 5; - outline->pts = static_cast(realloc(outline->pts, outline->reservedPtsCnt * sizeof(SwPoint))); - outline->types = static_cast(realloc(outline->types, outline->reservedPtsCnt * sizeof(uint8_t))); - } - - if (outline->reservedCntrsCnt < 1) { - outline->reservedCntrsCnt = 1; - outline->cntrs = static_cast(realloc(outline->cntrs, outline->reservedCntrsCnt * sizeof(uint32_t))); - outline->closed = static_cast(realloc(outline->closed, outline->reservedCntrsCnt * sizeof(bool))); - outline->closed[0] = true; - } + outline->pts.reserve(5); + outline->types.reserve(5); + outline->cntrs.reserve(1); + outline->closed.reserve(1); Point to[4]; if (mesh->triangleCnt > 0) { @@ -97,17 +89,14 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr } for (int i = 0; i < 4; i++) { - outline->pts[outline->ptsCnt] = mathTransform(&to[i], transform); - outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT; - ++outline->ptsCnt; + outline->pts.push(mathTransform(&to[i], transform)); + outline->types.push(SW_CURVE_TYPE_POINT); } - outline->pts[outline->ptsCnt] = outline->pts[0]; - outline->types[outline->ptsCnt] = SW_CURVE_TYPE_POINT; - ++outline->ptsCnt; - - outline->cntrs[outline->cntrsCnt] = outline->ptsCnt - 1; - ++outline->cntrsCnt; + outline->pts.push(outline->pts.data[0]); + outline->types.push(SW_CURVE_TYPE_POINT); + outline->cntrs.push(outline->pts.count - 1); + outline->closed.push(true); image->outline = outline; diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp index 5a4f58d9a65c..dbcfa754f31e 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMath.cpp @@ -465,9 +465,9 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S { if (!outline) return false; - auto pt = outline->pts; + auto pt = outline->pts.data; - if (outline->ptsCnt == 0 || outline->cntrsCnt <= 0) { + if (outline->pts.empty() || outline->cntrs.empty()) { renderRegion.reset(); return false; } @@ -477,9 +477,7 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S auto yMin = pt->y; auto yMax = pt->y; - ++pt; - - for (uint32_t i = 1; i < outline->ptsCnt; ++i, ++pt) { + for (++pt; pt < outline->pts.end(); ++pt) { if (xMin > pt->x) xMin = pt->x; if (xMax < pt->x) xMax = pt->x; if (yMin > pt->y) yMin = pt->y; diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp index 05ff9ddf0bad..936d9cbdeded 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwMemPool.cpp @@ -40,8 +40,10 @@ SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx) void mpoolRetOutline(SwMpool* mpool, unsigned idx) { - mpool->outline[idx].cntrsCnt = 0; - mpool->outline[idx].ptsCnt = 0; + mpool->outline[idx].pts.clear(); + mpool->outline[idx].cntrs.clear(); + mpool->outline[idx].types.clear(); + mpool->outline[idx].closed.clear(); } @@ -53,8 +55,10 @@ SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx) void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx) { - mpool->strokeOutline[idx].cntrsCnt = 0; - mpool->strokeOutline[idx].ptsCnt = 0; + mpool->strokeOutline[idx].pts.clear(); + mpool->strokeOutline[idx].cntrs.clear(); + mpool->strokeOutline[idx].types.clear(); + mpool->strokeOutline[idx].closed.clear(); } @@ -93,42 +97,19 @@ bool mpoolClear(SwMpool* mpool) SwOutline* p; for (unsigned i = 0; i < mpool->allocSize; ++i) { - //Outline p = &mpool->outline[i]; - - free(p->cntrs); - p->cntrs = nullptr; - - free(p->pts); - p->pts = nullptr; - - free(p->types); - p->types = nullptr; - - free(p->closed); - p->closed = nullptr; - - p->cntrsCnt = p->reservedCntrsCnt = 0; - p->ptsCnt = p->reservedPtsCnt = 0; + p->pts.reset(); + p->cntrs.reset(); + p->types.reset(); + p->closed.reset(); //StrokeOutline p = &mpool->strokeOutline[i]; - - free(p->cntrs); - p->cntrs = nullptr; - - free(p->pts); - p->pts = nullptr; - - free(p->types); - p->types = nullptr; - - free(p->closed); - p->closed = nullptr; - - p->cntrsCnt = p->reservedCntrsCnt = 0; - p->ptsCnt = p->reservedPtsCnt = 0; + p->pts.reset(); + p->cntrs.reset(); + p->types.reset(); + p->closed.reset(); } return true; diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp index 1f10afd9b369..bd32bf0b237c 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRaster.cpp @@ -37,23 +37,45 @@ /************************************************************************/ constexpr auto DOWN_SCALE_TOLERANCE = 0.5f; -template -static inline T _multiply(T c, T a) +struct FillLinear { - return ((c * a + 0xff) >> 8); -} + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) + { + fillLinear(fill, dst, y, x, len, op, a); + } + void operator()(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) + { + fillLinear(fill, dst, y, x, len, cmp, alpha, csize, opacity); + } -static inline uint32_t _alpha(uint32_t c) -{ - return (c >> 24); -} + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) + { + fillLinear(fill, dst, y, x, len, op, op2, a); + } +}; -static inline uint32_t _ialpha(uint32_t c) +struct FillRadial { - return (~c >> 24); -} + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a) + { + fillRadial(fill, dst, y, x, len, op, a); + } + + void operator()(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) + { + fillRadial(fill, dst, y, x, len, cmp, alpha, csize, opacity); + } + + void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) + { + fillRadial(fill, dst, y, x, len, op, op2, a); + } +}; + + +static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity = 255); static inline uint8_t _alpha(uint8_t* a) @@ -82,6 +104,18 @@ static inline uint8_t _argbLuma(uint8_t* c) } +static inline uint8_t _abgrInvLuma(uint8_t* c) +{ + return ~_abgrLuma(c); +} + + +static inline uint8_t _argbInvLuma(uint8_t* c) +{ + return ~_argbLuma(c); +} + + static inline uint32_t _abgrJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return (a << 24 | b << 16 | g << 8 | r); @@ -93,29 +127,111 @@ static inline uint32_t _argbJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a) return (a << 24 | r << 16 | g << 8 | b); } - -#include "tvgSwRasterTexmap.h" -#include "tvgSwRasterC.h" -#include "tvgSwRasterAvx.h" -#include "tvgSwRasterNeon.h" +static inline bool _blending(const SwSurface* surface) +{ + return (surface->blender) ? true : false; +} +/* OPTIMIZE_ME: Probably, we can separate masking(8bits) / composition(32bits) + This would help to enhance the performance by avoiding the unnecessary matting from the composition */ static inline bool _compositing(const SwSurface* surface) { - if (!surface->compositor || surface->compositor->method == CompositeMethod::None) return false; + if (!surface->compositor || (int)surface->compositor->method <= (int)CompositeMethod::ClipPath) return false; return true; } -static inline uint32_t _halfScale(float scale) +static inline bool _matting(const SwSurface* surface) +{ + if ((int)surface->compositor->method < (int)CompositeMethod::AddMask) return true; + else return false; +} + + +static inline bool _masking(const SwSurface* surface) +{ + if ((int)surface->compositor->method >= (int)CompositeMethod::AddMask) return true; + else return false; +} + + +static inline uint32_t _opMaskAdd(uint32_t s, uint32_t d, uint8_t a) +{ + return s + ALPHA_BLEND(d, a); +} + + +static inline uint32_t _opMaskSubtract(TVG_UNUSED uint32_t s, uint32_t d, uint8_t a) +{ + return ALPHA_BLEND(d, a); +} + + +static inline uint32_t _opMaskDifference(uint32_t s, uint32_t d, uint8_t a) +{ + return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, a); +} + + +static inline uint32_t _opAMaskAdd(uint32_t s, uint32_t d, uint8_t a) +{ + return INTERPOLATE(s, d, a); +} + + +static inline uint32_t _opAMaskSubtract(TVG_UNUSED uint32_t s, uint32_t d, uint8_t a) +{ + return ALPHA_BLEND(d, IA(ALPHA_BLEND(s, a))); +} + + +static inline uint32_t _opAMaskDifference(uint32_t s, uint32_t d, uint8_t a) +{ + auto t = ALPHA_BLEND(s, a); + return ALPHA_BLEND(t, IA(d)) + ALPHA_BLEND(d, IA(t)); +} + + +static inline SwBlender _getMaskOp(CompositeMethod method) +{ + switch (method) { + case CompositeMethod::AddMask: return _opMaskAdd; + case CompositeMethod::SubtractMask: return _opMaskSubtract; + case CompositeMethod::DifferenceMask: return _opMaskDifference; + default: return nullptr; + } +} + + +static inline SwBlender _getAMaskOp(CompositeMethod method) +{ + switch (method) { + case CompositeMethod::AddMask: return _opAMaskAdd; + case CompositeMethod::SubtractMask: return _opAMaskSubtract; + case CompositeMethod::DifferenceMask: return _opAMaskDifference; + default: return nullptr; + } +} + + +#include "tvgSwRasterTexmap.h" +#include "tvgSwRasterC.h" +#include "tvgSwRasterAvx.h" +#include "tvgSwRasterNeon.h" + + +static inline uint32_t _sampleSize(float scale) { - auto halfScale = static_cast(0.5f / scale); - if (halfScale == 0) halfScale = 1; - return halfScale; + auto sampleSize = static_cast(0.5f / scale); + if (sampleSize == 0) sampleSize = 1; + return sampleSize; } + //Bilinear Interpolation -static uint32_t _interpUpScaler(const uint32_t *img, uint32_t w, uint32_t h, float sx, float sy) +//OPTIMIZE_ME: Skip the function pointer access +static uint32_t _interpUpScaler(const uint32_t *img, TVG_UNUSED uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, TVG_UNUSED uint32_t n, TVG_UNUSED uint32_t n2) { auto rx = (uint32_t)(sx); auto ry = (uint32_t)(sy); @@ -132,15 +248,17 @@ static uint32_t _interpUpScaler(const uint32_t *img, uint32_t w, uint32_t h, flo auto c3 = img[rx2 + ry2 * w]; auto c4 = img[rx + ry2 * w]; - return INTERPOLATE(dy, INTERPOLATE(dx, c3, c4), INTERPOLATE(dx, c2, c1)); + return INTERPOLATE(INTERPOLATE(c3, c4, dx), INTERPOLATE(c2, c1, dx), dy); } //2n x 2n Mean Kernel -static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, uint32_t rx, uint32_t ry, uint32_t n) +//OPTIMIZE_ME: Skip the function pointer access +static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t w, uint32_t h, float sx, float sy, uint32_t n, uint32_t n2) { + uint32_t rx = lroundf(sx); + uint32_t ry = lroundf(sy); uint32_t c[4] = {0, 0, 0, 0}; - auto n2 = n * n; auto src = img + rx - n + (ry - n) * stride; for (auto y = ry - n; y < ry + n; ++y) { @@ -162,34 +280,104 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t } -void _rasterGrayscale8(uint8_t *dst, uint32_t val, uint32_t offset, int32_t len) -{ - cRasterPixels(dst, val, offset, len); -} - /************************************************************************/ /* Rect */ /************************************************************************/ -static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t(*blender)(uint8_t*)) +static void _rasterMaskedRectDup(SwSurface* surface, const SwBBox& region, SwBlender opMask, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + auto cbuffer = surface->compositor->image.buf32 + (region.min.y * surface->compositor->image.stride + region.min.x); //compositor buffer + auto cstride = surface->compositor->image.stride; + auto color = surface->join(r, g, b, a); + auto ialpha = 255 - a; + + for (uint32_t y = 0; y < h; ++y) { + auto cmp = cbuffer; + for (uint32_t x = 0; x < w; ++x, ++cmp) { + *cmp = opMask(color, *cmp, ialpha); + } + cbuffer += cstride; + } +} + + +static void _rasterMaskedRectInt(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + auto cstride = surface->compositor->image.stride; + + for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) { + auto cmp = surface->compositor->image.buf32 + (y * cstride + surface->compositor->bbox.min.x); + if (y == region.min.y) { + for (auto y2 = y; y2 < region.max.y; ++y2) { + auto tmp = cmp; + auto x = surface->compositor->bbox.min.x; + while (x < surface->compositor->bbox.max.x) { + if (x == region.min.x) { + for (uint32_t i = 0; i < w; ++i, ++tmp) { + *tmp = ALPHA_BLEND(*tmp, a); + } + x += w; + } else { + *tmp = 0; + ++tmp; + ++x; + } + } + cmp += cstride; + } + y += (h - 1); + } else { + rasterPixel32(cmp, 0x00000000, 0, w); + cmp += cstride; + } + } +} + + +static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + //32bit channels composition + if (surface->channelSize != sizeof(uint32_t)) return false; + + TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.max.y, region.min.y); + + if (surface->compositor->method == CompositeMethod::IntersectMask) { + _rasterMaskedRectInt(surface, region, r, g, b, a); + } else if (auto opMask = _getMaskOp(surface->compositor->method)) { + //Other Masking operations: Add, Subtract, Difference ... + _rasterMaskedRectDup(surface, region, opMask, r, g, b, a); + } else { + return false; + } + + //Masking Composition + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { auto w = static_cast(region.max.x - region.min.x); auto h = static_cast(region.max.y - region.min.y); auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8 + ((region.min.y * surface->compositor->image.stride + region.min.x) * csize); //compositor buffer + auto alpha = surface->alpha(surface->compositor->method); - TVGLOG("SW_ENGINE", "Masked Rect [Region: %lu %lu %u %u]", region.min.x, region.min.y, w, h); - + TVGLOG("SW_ENGINE", "Matted(%d) Rect [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h); + //32bits channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->blender.join(r, g, b, a); + auto color = surface->join(r, g, b, a); auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; - auto cmp = &cbuffer[y * surface->stride * csize]; + auto cmp = &cbuffer[y * surface->compositor->image.stride * csize]; for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) { - auto tmp = ALPHA_BLEND(color, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + *dst = INTERPOLATE(color, *dst, alpha(cmp)); } } //8bits grayscale @@ -197,10 +385,9 @@ static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; - auto cmp = &cbuffer[y * surface->stride * csize]; + auto cmp = &cbuffer[y * surface->compositor->image.stride * csize]; for (uint32_t x = 0; x < w; ++x, ++dst, cmp += csize) { - auto tmp = _multiply(a, blender(cmp)); - *dst = tmp + _multiply(*dst, _ialpha(tmp)); + *dst = INTERPOLATE8(a, *dst, alpha(cmp)); } } } @@ -208,6 +395,38 @@ static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint8_t } +static bool _rasterBlendingRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (surface->channelSize != sizeof(uint32_t)) return false; + + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + auto color = surface->join(r, g, b, a); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto ialpha = 255 - a; + + for (uint32_t y = 0; y < h; ++y) { + auto dst = &buffer[y * surface->stride]; + for (uint32_t x = 0; x < w; ++x, ++dst) { + *dst = surface->blender(color, *dst, ialpha); + } + } + return true; +} + + +static bool _rasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ +#if defined(THORVG_AVX_VECTOR_SUPPORT) + return avxRasterTranslucentRect(surface, region, r, g, b, a); +#elif defined(THORVG_NEON_VECTOR_SUPPORT) + return neonRasterTranslucentRect(surface, region, r, g, b, a); +#else + return cRasterTranslucentRect(surface, region, r, g, b, a); +#endif +} + + static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b) { auto w = static_cast(region.max.x - region.min.x); @@ -215,44 +434,34 @@ static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r //32bits channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->blender.join(r, g, b, 255); + auto color = surface->join(r, g, b, 255); auto buffer = surface->buf32 + (region.min.y * surface->stride); for (uint32_t y = 0; y < h; ++y) { - rasterRGBA32(buffer + y * surface->stride, color, region.min.x, w); + rasterPixel32(buffer + y * surface->stride, color, region.min.x, w); } + return true; + } //8bits grayscale - } else if (surface->channelSize == sizeof(uint8_t)) { - auto buffer = surface->buf8 + (region.min.y * surface->stride); + if (surface->channelSize == sizeof(uint8_t)) { for (uint32_t y = 0; y < h; ++y) { - _rasterGrayscale8(buffer + y * surface->stride, 255, region.min.x, w); + rasterGrayscale8(surface->buf8, 255, region.min.y * surface->stride + region.min.x, w); } + return true; } - return true; + return false; } static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (_compositing(surface)) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterMaskedRect(surface, region, r, g, b, a, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterMaskedRect(surface, region, r, g, b, a, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterMaskedRect(surface, region, r, g, b, a, surface->blender.luma); - } + if (_matting(surface)) return _rasterMattedRect(surface, region, r, g, b, a); + else return _rasterMaskedRect(surface, region, r, g, b, a); + } else if (_blending(surface)) { + return _rasterBlendingRect(surface, region, r, g, b, a); } else { - if (a == 255) { - return _rasterSolidRect(surface, region, r, g, b); - } else { -#if defined(THORVG_AVX_VECTOR_SUPPORT) - return avxRasterTranslucentRect(surface, region, r, g, b, a); -#elif defined(THORVG_NEON_VECTOR_SUPPORT) - return neonRasterTranslucentRect(surface, region, r, g, b, a); -#else - return cRasterTranslucentRect(surface, region, r, g, b, a); -#endif - } + if (a == 255) return _rasterSolidRect(surface, region, r, g, b); + else return _rasterTranslucentRect(surface, region, r, g, b, a); } return false; } @@ -262,38 +471,138 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin /* Rle */ /************************************************************************/ -static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t(*blender)(uint8_t*)) +static void _rasterMaskedRleDup(SwSurface* surface, SwRleData* rle, SwBlender maskOp, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - TVGLOG("SW_ENGINE", "Masked Rle"); + auto span = rle->spans; + auto cbuffer = surface->compositor->image.buf32; + auto cstride = surface->compositor->image.stride; + auto color = surface->join(r, g, b, a); + uint32_t src; + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto cmp = &cbuffer[span->y * cstride + span->x]; + if (span->coverage == 255) src = color; + else src = ALPHA_BLEND(color, span->coverage); + auto ialpha = IA(src); + for (auto x = 0; x < span->len; ++x, ++cmp) { + *cmp = maskOp(src, *cmp, ialpha); + } + } +} + + +static void _rasterMaskedRleInt(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ auto span = rle->spans; + auto cbuffer = surface->compositor->image.buf32; + auto cstride = surface->compositor->image.stride; + auto color = surface->join(r, g, b, a); uint32_t src; + + for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) { + auto cmp = &cbuffer[y * cstride]; + auto x = surface->compositor->bbox.min.x; + while (x < surface->compositor->bbox.max.x) { + if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) { + if (span->coverage == 255) src = color; + else src = ALPHA_BLEND(color, span->coverage); + auto alpha = A(src); + for (uint32_t i = 0; i < span->len; ++i) { + cmp[x + i] = ALPHA_BLEND(cmp[x + i], alpha); + } + x += span->len; + ++span; + } else { + cmp[x] = 0; + ++x; + } + } + } +} + + +static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + TVGLOG("SW_ENGINE", "Masked(%d) Rle", (int)surface->compositor->method); + + //32bit channels composition + if (surface->channelSize != sizeof(uint32_t)) return false; + + if (surface->compositor->method == CompositeMethod::IntersectMask) { + _rasterMaskedRleInt(surface, rle, r, g, b, a); + } else if (auto opMask = _getMaskOp(surface->compositor->method)) { + //Other Masking operations: Add, Subtract, Difference ... + _rasterMaskedRleDup(surface, rle, opMask, r, g, b, a); + } else { + return false; + } + + //Masking Composition + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + TVGLOG("SW_ENGINE", "Matted(%d) Rle", (int)surface->compositor->method); + + auto span = rle->spans; auto cbuffer = surface->compositor->image.buf8; auto csize = surface->compositor->image.channelSize; + auto alpha = surface->alpha(surface->compositor->method); //32bit channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->blender.join(r, g, b, a); + uint32_t src; + auto color = surface->join(r, g, b, a); for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; if (span->coverage == 255) src = color; else src = ALPHA_BLEND(color, span->coverage); for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) { - auto tmp = ALPHA_BLEND(src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + auto tmp = ALPHA_BLEND(src, alpha(cmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } + return true; + } //8bit grayscale - } else if (surface->channelSize == sizeof(uint8_t)) { + 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]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; if (span->coverage == 255) src = a; - else src = _multiply(a, span->coverage); + else src = MULTIPLY(a, span->coverage); for (uint32_t x = 0; x < span->len; ++x, ++dst, cmp += csize) { - auto tmp = _multiply(src, blender(cmp)); - *dst = tmp + _multiply(*dst, _ialpha(tmp)); + *dst = INTERPOLATE8(src, *dst, alpha(cmp)); + } + } + return true; + } + return false; +} + + +static bool _rasterBlendingRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if (surface->channelSize != sizeof(uint32_t)) return false; + + auto span = rle->spans; + auto color = surface->join(r, g, b, a); + auto ialpha = 255 - a; + + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + if (span->coverage == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + *dst = surface->blender(color, *dst, ialpha); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst) { + auto tmp = surface->blender(color, *dst, ialpha); + *dst = INTERPOLATE(tmp, *dst, span->coverage); } } } @@ -301,16 +610,28 @@ static bool _rasterMaskedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint } +static bool _rasterTranslucentRle(SwSurface* surface, const SwRleData* 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); +#elif defined(THORVG_NEON_VECTOR_SUPPORT) + return neonRasterTranslucentRle(surface, rle, r, g, b, a); +#else + return cRasterTranslucentRle(surface, rle, r, g, b, a); +#endif +} + + static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b) { auto span = rle->spans; //32bit channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->blender.join(r, g, b, 255); + auto color = surface->join(r, g, b, 255); for (uint32_t i = 0; i < rle->size; ++i, ++span) { if (span->coverage == 255) { - rasterRGBA32(surface->buf32 + span->y * surface->stride, color, span->x, span->len); + rasterPixel32(surface->buf32 + span->y * surface->stride, color, span->x, span->len); } else { auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto src = ALPHA_BLEND(color, span->coverage); @@ -323,14 +644,7 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { for (uint32_t i = 0; i < rle->size; ++i, ++span) { - if (span->coverage == 255) { - _rasterGrayscale8(surface->buf8 + span->y * surface->stride, 255, span->x, span->len); - } else { - auto dst = &surface->buf8[span->y * surface->stride + span->x]; - for (uint32_t x = 0; x < span->len; ++x, ++dst) { - *dst = span->coverage; - } - } + rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len); } } return true; @@ -342,188 +656,204 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, if (!rle) return false; if (_compositing(surface)) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterMaskedRle(surface, rle, r, g, b, a, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterMaskedRle(surface, rle, r, g, b, a, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterMaskedRle(surface, rle, r, g, b, a, surface->blender.luma); - } + if (_matting(surface)) return _rasterMattedRle(surface, rle, r, g, b, a); + else return _rasterMaskedRle(surface, rle, r, g, b, a); + } else if (_blending(surface)) { + return _rasterBlendingRle(surface, rle, r, g, b, a); } else { - if (a == 255) { - return _rasterSolidRle(surface, rle, r, g, b); - } else { -#if defined(THORVG_AVX_VECTOR_SUPPORT) - return avxRasterTranslucentRle(surface, rle, r, g, b, a); -#elif defined(THORVG_NEON_VECTOR_SUPPORT) - return neonRasterTranslucentRle(surface, rle, r, g, b, a); -#else - return cRasterTranslucentRle(surface, rle, r, g, b, a); -#endif - } + if (a == 255) return _rasterSolidRle(surface, rle, r, g, b); + else return _rasterTranslucentRle(surface, rle, r, g, b, a); } return false; } /************************************************************************/ -/* RLE Transformed RGBA Image */ +/* RLE Transformed Image */ /************************************************************************/ -static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, uint32_t opacity) +static bool _transformedRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, uint8_t opacity) { - if (_compositing(surface)) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, surface->blender.luma); - } - } else { - return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, nullptr); + auto ret = _rasterTexmapPolygon(surface, image, transform, nullptr, opacity); + + //Masking Composition + if (_compositing(surface) && _masking(surface)) { + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox); } - return false; + + return ret; + } + /************************************************************************/ -/* RLE Scaled RGBA Image */ +/* RLE Scaled Image */ /************************************************************************/ -static bool _rasterScaledMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint8_t(*blender)(uint8_t*)) +static void _rasterScaledMaskedRleImageDup(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity) { - TVGLOG("SW_ENGINE", "Scaled Masked Translucent Rle Image"); - + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; auto span = image->rle->spans; - auto csize = surface->compositor->image.channelSize; - //Center (Down-Scaled) - if (image->scale < DOWN_SCALE_TOLERANCE) { - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; - auto alpha = _multiply(span->coverage, opacity); - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha); - auto tmp = ALPHA_BLEND(src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto sy = span->y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto cmp = &surface->compositor->image.buf32[span->y * surface->compositor->image.stride + span->x]; + auto a = MULTIPLY(span->coverage, opacity); + if (a == 255) { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++cmp) { + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + *cmp = maskOp(src, *cmp, 255); } - } - //Center (Up-Scaled) - } else { - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = span->y * itransform->e22 + itransform->e23; - if ((uint32_t)sy >= image->h) continue; - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; - auto alpha = _multiply(span->coverage, opacity); - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { + } else { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++cmp) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), alpha); - auto tmp = ALPHA_BLEND(src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + *cmp = amaskOp(src, *cmp, a); } } } - return true; } -static bool _rasterScaledMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint8_t(*blender)(uint8_t*)) +static void _rasterScaledMaskedRleImageInt(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { - TVGLOG("SW_ENGINE", "Scaled Masked Rle Image"); - + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; auto span = image->rle->spans; - auto csize = surface->compositor->image.channelSize; - - //Center (Down-Scaled) - if (image->scale < DOWN_SCALE_TOLERANCE) { - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; - if (span->coverage == 255) { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto tmp = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + auto cbuffer = surface->compositor->image.buf32; + auto cstride = surface->compositor->image.stride; + + for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) { + auto cmp = &cbuffer[y * cstride]; + for (auto x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) { + if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) { + auto sy = span->y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t i = 0; i < span->len; ++i) { + auto sx = (x + i) * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(src)); + } + } else { + for (uint32_t i = 0; i < span->len; ++i) { + auto sx = (x + i) * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(ALPHA_BLEND(src, alpha))); + } } + x += span->len - 1; + ++span; } else { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage); - auto tmp = ALPHA_BLEND(src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } + cmp[x] = 0; } } - //Center (Up-Scaled) - } else { - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = span->y * itransform->e22 + itransform->e23; - if ((uint32_t)sy >= image->h) continue; - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; - if (span->coverage == 255) { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { - auto sx = x * itransform->e11 + itransform->e13; - if ((uint32_t)sx >= image->w) continue; - auto tmp = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } - } else { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { - auto sx = x * itransform->e11 + itransform->e13; - if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), span->coverage); - auto tmp = ALPHA_BLEND(src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } + } +} + + +static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ + TVGLOG("SW_ENGINE", "Scaled Masked(%d) Rle Image", (int)surface->compositor->method); + + if (surface->compositor->method == CompositeMethod::IntersectMask) { + _rasterScaledMaskedRleImageInt(surface, image, itransform, region, opacity); + } else if (auto opMask = _getMaskOp(surface->compositor->method)) { + //Other Masking operations: Add, Subtract, Difference ... + _rasterScaledMaskedRleImageDup(surface, image, itransform, region, opMask, _getAMaskOp(surface->compositor->method), opacity); + } else { + return false; + } + //Masking Composition + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ + TVGLOG("SW_ENGINE", "Scaled Matted(%d) Rle Image", (int)surface->compositor->method); + + auto span = image->rle->spans; + auto csize = surface->compositor->image.channelSize; + auto alpha = surface->alpha(surface->compositor->method); + + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; + + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto sy = span->y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize]; + auto a = MULTIPLY(span->coverage, opacity); + if (a == 255) { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto tmp = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), alpha(cmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } else { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst, cmp += csize) { + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + auto tmp = ALPHA_BLEND(src, MULTIPLY(alpha(cmp), a)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } } + return true; } -static bool _rasterScaledTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale) +static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { auto span = image->rle->spans; + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; - //Center (Down-Scaled) - if (image->scale < DOWN_SCALE_TOLERANCE) { - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - auto alpha = _multiply(span->coverage, opacity); + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto sy = span->y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, A(src)); } - } - //Center (Up-Scaled) - } else { - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = span->y * itransform->e22 + itransform->e23; - if ((uint32_t)sy >= image->h) continue; - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - auto alpha = _multiply(span->coverage, opacity); + } else if (opacity == 255) { for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), alpha); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src))); + } + } else { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src))); } } } @@ -531,52 +861,31 @@ static bool _rasterScaledTranslucentRleRGBAImage(SwSurface* surface, const SwIma } -static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale) +static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { auto span = image->rle->spans; + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; - //Center (Down-Scaled) - if (image->scale < DOWN_SCALE_TOLERANCE) { - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = (uint32_t)(span->y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - if (span->coverage == 255) { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto src = _interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); - } - } else { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), span->coverage); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); - } + for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { + auto sy = span->y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + *dst = src + ALPHA_BLEND(*dst, IA(src)); } - } - //Center (Up-Scaled) - } else { - for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto sy = span->y * itransform->e22 + itransform->e23; - if ((uint32_t)sy >= image->h) continue; - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - if (span->coverage == 255) { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = x * itransform->e11 + itransform->e13; - if ((uint32_t)sx >= image->w) continue; - auto src = _interpUpScaler(image->buf32, image->w, image->h, sx, sy); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); - } - } else { - for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { - auto sx = x * itransform->e11 + itransform->e13; - if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), span->coverage); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); - } + } else { + for (uint32_t x = static_cast(span->x); x < static_cast(span->x) + span->len; ++x, ++dst) { + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), alpha); + *dst = src + ALPHA_BLEND(*dst, IA(src)); } } } @@ -584,7 +893,7 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, } -static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity) +static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) { Matrix itransform; @@ -592,88 +901,120 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const if (!mathInverse(transform, &itransform)) return false; } else mathIdentity(&itransform); - auto halfScale = _halfScale(image->scale); - if (_compositing(surface)) { - if (opacity == 255) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.luma); - } - } else { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.luma); - } - } + if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity); + else return _rasterScaledMaskedRleImage(surface, image, &itransform, region, opacity); + } else if (_blending(surface)) { + return _rasterScaledBlendingRleImage(surface, image, &itransform, region, opacity); } else { - if (opacity == 255) return _rasterScaledRleRGBAImage(surface, image, &itransform, region, opacity, halfScale); - else return _rasterScaledTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale); + return _rasterScaledRleImage(surface, image, &itransform, region, opacity); } return false; } /************************************************************************/ -/* RLE Direct RGBA Image */ +/* RLE Direct Image */ /************************************************************************/ -static bool _rasterDirectMaskedTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity, uint8_t(*blender)(uint8_t*)) +static void _rasterDirectMaskedRleImageDup(SwSurface* surface, const SwImage* image, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity) { - TVGLOG("SW_ENGINE", "Direct Masked Rle Image"); - auto span = image->rle->spans; - auto csize = surface->compositor->image.channelSize; - auto cbuffer = surface->compositor->image.buf8; + auto cbuffer = surface->compositor->image.buf32; + auto ctride = surface->compositor->image.stride; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; - auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); - auto alpha = _multiply(span->coverage, opacity); + auto src = image->buf32 + (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, ++dst, ++img, cmp += csize) { - auto tmp = ALPHA_BLEND(*img, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { + *cmp = maskOp(*src, *cmp, IA(*src)); } } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { - auto tmp = ALPHA_BLEND(*img, _multiply(alpha, blender(cmp))); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { + *cmp = amaskOp(*src, *cmp, alpha); } } } - return true; } -static bool _rasterDirectMaskedRleRGBAImage(SwSurface* surface, const SwImage* image, uint8_t(*blender)(uint8_t*)) +static void _rasterDirectMaskedRleImageInt(SwSurface* surface, const SwImage* image, uint8_t opacity) { - TVGLOG("SW_ENGINE", "Direct Masked Rle Image"); + auto span = image->rle->spans; + auto cbuffer = surface->compositor->image.buf32; + auto ctride = surface->compositor->image.stride; + + for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) { + auto cmp = &cbuffer[y * ctride]; + auto x = surface->compositor->bbox.min.x; + while (x < surface->compositor->bbox.max.x) { + if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) { + auto alpha = MULTIPLY(span->coverage, opacity); + auto src = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); + if (alpha == 255) { + for (uint32_t i = 0; i < span->len; ++i, ++src) { + cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(*src)); + } + } else { + for (uint32_t i = 0; i < span->len; ++i, ++src) { + auto t = ALPHA_BLEND(*src, alpha); + cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(t)); + } + } + x += span->len; + ++span; + } else { + cmp[x] = 0; + ++x; + } + } + } +} + + +static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +{ + TVGLOG("SW_ENGINE", "Direct Masked(%d) Rle Image", (int)surface->compositor->method); + + if (surface->compositor->method == CompositeMethod::IntersectMask) { + _rasterDirectMaskedRleImageInt(surface, image, opacity); + } else if (auto opMask = _getMaskOp(surface->compositor->method)) { + //Other Masking operations: Add, Subtract, Difference ... + _rasterDirectMaskedRleImageDup(surface, image, opMask, _getAMaskOp(surface->compositor->method), opacity); + } else { + return false; + } + + //Masking Composition + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) +{ + TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method); auto span = image->rle->spans; auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8; + auto alpha = surface->alpha(surface->compositor->method); for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); - if (span->coverage == 255) { + auto a = MULTIPLY(span->coverage, opacity); + if (a == 255) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { - auto tmp = ALPHA_BLEND(*img, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + auto tmp = ALPHA_BLEND(*img, alpha(cmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } else { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { - auto tmp = ALPHA_BLEND(*img, _multiply(span->coverage, blender(cmp))); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + auto tmp = ALPHA_BLEND(*img, MULTIPLY(a, alpha(cmp))); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } } @@ -681,38 +1022,51 @@ static bool _rasterDirectMaskedRleRGBAImage(SwSurface* surface, const SwImage* i } -static bool _rasterDirectTranslucentRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity) +static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) { auto span = image->rle->spans; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { 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); - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { - auto src = ALPHA_BLEND(*img, alpha); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto alpha = MULTIPLY(span->coverage, opacity); + if (alpha == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + *dst = surface->blender(*img, *dst, IA(*img)); + } + } else if (opacity == 255) { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + auto tmp = surface->blender(*img, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(*img))); + } + } else { + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + auto src = ALPHA_BLEND(*img, opacity); + auto tmp = surface->blender(src, *dst, IA(src)); + *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src))); + } } } return true; } -static bool _rasterDirectRleRGBAImage(SwSurface* surface, const SwImage* image) +static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) { auto span = image->rle->spans; for (uint32_t i = 0; i < image->rle->size; ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); - if (span->coverage == 255) { + 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, _ialpha(*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, span->coverage); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto src = ALPHA_BLEND(*img, alpha); + *dst = src + ALPHA_BLEND(*dst, IA(src)); } } } @@ -720,195 +1074,221 @@ static bool _rasterDirectRleRGBAImage(SwSurface* surface, const SwImage* image) } -static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_t opacity) +static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity) { if (_compositing(surface)) { - if (opacity == 255) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterDirectMaskedRleRGBAImage(surface, image, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterDirectMaskedRleRGBAImage(surface, image, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterDirectMaskedRleRGBAImage(surface, image, surface->blender.luma); - } - } else { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, surface->blender.luma); - } - } + if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity); + else return _rasterDirectMaskedRleImage(surface, image, opacity); + } else if (_blending(surface)) { + return _rasterDirectBlendingRleImage(surface, image, opacity); } else { - if (opacity == 255) return _rasterDirectRleRGBAImage(surface, image); - else return _rasterDirectTranslucentRleRGBAImage(surface, image, opacity); + return _rasterDirectRleImage(surface, image, opacity); } return false; } /************************************************************************/ -/* Transformed RGBA Image */ +/* Transformed Image */ /************************************************************************/ -static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity) +static bool _transformedImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) { - if (_compositing(surface)) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, surface->blender.luma); - } - } else { - return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, nullptr); + auto ret = _rasterTexmapPolygon(surface, image, transform, ®ion, opacity); + + //Masking Composition + if (_compositing(surface) && _masking(surface)) { + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox); } - return false; + + return ret; } -static bool _transformedRGBAImageMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint32_t opacity) + +static bool _transformedImageMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity) { - if (_compositing(surface)) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, surface->blender.luma); - } - } else { - return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, nullptr); - } - return false; + //TODO: Not completed for all cases. + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity); } /************************************************************************/ -/*Scaled RGBA Image */ +/*Scaled Image */ /************************************************************************/ -static bool _rasterScaledMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale, uint8_t(*blender)(uint8_t*)) +static void _rasterScaledMaskedImageDup(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity) { - TVGLOG("SW_ENGINE", "Scaled Masked Image"); + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); - auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); - auto csize = surface->compositor->image.channelSize; - auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; - - // Down-Scaled - if (image->scale < DOWN_SCALE_TOLERANCE) { - for (auto y = region.min.y; y < region.max.y; ++y) { - auto sy = (uint32_t)(y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; - auto dst = dbuffer; - auto cmp = cbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto alpha = _multiply(opacity, blender(cmp)); - auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + for (auto y = region.min.y; y < region.max.y; ++y) { + auto sy = y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto cmp = cbuffer; + if (opacity == 255) { + for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) { + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + *cmp = maskOp(src, *cmp, IA(src)); } - dbuffer += surface->stride; - cbuffer += surface->compositor->image.stride * csize; - } - // Up-Scaled - } else { - for (auto y = region.min.y; y < region.max.y; ++y) { - auto sy = y * itransform->e22 + itransform->e23; - if ((uint32_t)sy >= image->h) continue; - auto dst = dbuffer; - auto cmp = cbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { + } else { + for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto alpha = _multiply(opacity, blender(cmp)); - auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), alpha); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + *cmp = amaskOp(src, *cmp, opacity); } - dbuffer += surface->stride; - cbuffer += surface->compositor->image.stride * csize; } + cbuffer += cstride; + } +} + +static void _rasterScaledMaskedImageInt(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf32 + (surface->compositor->bbox.min.y * cstride + surface->compositor->bbox.min.x); + + for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) { + if (y == region.min.y) { + auto cbuffer2 = cbuffer; + for (auto y2 = y; y2 < region.max.y; ++y2) { + auto sy = y2 * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto tmp = cbuffer2; + auto x = surface->compositor->bbox.min.x; + while (x < surface->compositor->bbox.max.x) { + if (x == region.min.x) { + if (opacity == 255) { + for (uint32_t i = 0; i < w; ++i, ++tmp) { + auto sx = (x + i) * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + *tmp = ALPHA_BLEND(*tmp, A(src)); + } + } else { + for (uint32_t i = 0; i < w; ++i, ++tmp) { + auto sx = (x + i) * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity); + *tmp = ALPHA_BLEND(*tmp, A(src)); + } + } + x += w; + } else { + *tmp = 0; + ++tmp; + ++x; + } + } + cbuffer2 += cstride; + } + y += (h - 1); + } else { + auto tmp = cbuffer; + for (auto x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x, ++tmp) { + *tmp = 0; + } + } + cbuffer += cstride; } - return true; } -static bool _rasterScaledMaskedRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale, uint8_t (*blender)(uint8_t*)) +static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { - TVGLOG("SW_ENGINE", "Scaled Masked Image"); + 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); + if (surface->compositor->method == CompositeMethod::IntersectMask) { + _rasterScaledMaskedImageInt(surface, image, itransform, region, opacity); + } else if (auto opMask = _getMaskOp(surface->compositor->method)) { + //Other Masking operations: Add, Subtract, Difference ... + _rasterScaledMaskedImageDup(surface, image, itransform, region, opMask, _getAMaskOp(surface->compositor->method), opacity); + } else { + return false; + } + + //Masking Composition + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) +{ auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; + auto alpha = surface->alpha(surface->compositor->method); + + TVGLOG("SW_ENGINE", "Scaled Matted(%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 scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; - // Down-Scaled - if (image->scale < DOWN_SCALE_TOLERANCE) { - for (auto y = region.min.y; y < region.max.y; ++y) { - auto sy = (uint32_t)(y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; - auto dst = dbuffer; - auto cmp = cbuffer; + for (auto y = region.min.y; y < region.max.y; ++y) { + auto sy = y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto dst = dbuffer; + auto cmp = cbuffer; + if (opacity == 255) { for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), blender(cmp)); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + auto temp = ALPHA_BLEND(src, alpha(cmp)); + *dst = temp + ALPHA_BLEND(*dst, IA(temp)); } - dbuffer += surface->stride; - cbuffer += surface->compositor->image.stride * csize; - } - // Up-Scaled - } else { - for (auto y = region.min.y; y < region.max.y; ++y) { - auto sy = y * itransform->e22 + itransform->e23; - if ((uint32_t)sy >= image->h) continue; - auto dst = dbuffer; - auto cmp = cbuffer; + } else { for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), blender(cmp)); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + auto temp = ALPHA_BLEND(src, MULTIPLY(opacity, alpha(cmp))); + *dst = temp + ALPHA_BLEND(*dst, IA(temp)); } - dbuffer += surface->stride; - cbuffer += surface->compositor->image.stride * csize; } + dbuffer += surface->stride; + cbuffer += surface->compositor->image.stride * csize; } return true; } -static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale) +static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; - // Down-Scaled - if (image->scale < DOWN_SCALE_TOLERANCE) { - for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { - auto sy = (uint32_t)(y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; - auto dst = dbuffer; + for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { + auto sy = y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto dst = dbuffer; + if (opacity == 255) { for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), opacity); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, A(src)); } - } - // Up-Scaled - } else { - for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { - auto sy = fabsf(y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; - auto dst = dbuffer; + } else { for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = ALPHA_BLEND(_interpUpScaler(image->buf32, image->w, image->h, sx, sy), opacity); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, A(src)); } } } @@ -916,34 +1296,30 @@ static bool _rasterScaledTranslucentRGBAImage(SwSurface* surface, const SwImage* } -static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t halfScale) +static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity) { auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + auto sampleSize = _sampleSize(image->scale); + auto sampleSize2 = sampleSize * sampleSize; - // Down-Scaled - if (image->scale < DOWN_SCALE_TOLERANCE) { - for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { - auto sy = (uint32_t)(y * itransform->e22 + itransform->e23); - if (sy >= image->h) continue; - auto dst = dbuffer; + for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { + auto sy = y * itransform->e22 + itransform->e23; + if ((uint32_t)sy >= image->h) continue; + auto dst = dbuffer; + if (opacity == 255) { for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { - auto sx = (uint32_t)(x * itransform->e11 + itransform->e13); - if (sx >= image->w) continue; - auto src = _interpDownScaler(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto sx = x * itransform->e11 + itransform->e13; + if ((uint32_t)sx >= image->w) continue; + auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2); + *dst = src + ALPHA_BLEND(*dst, IA(src)); } - } - // Up-Scaled - } else { - for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) { - auto sy = y * itransform->e22 + itransform->e23; - if ((uint32_t)sy >= image->h) continue; - auto dst = dbuffer; + } else { for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { auto sx = x * itransform->e11 + itransform->e13; if ((uint32_t)sx >= image->w) continue; - auto src = _interpUpScaler(image->buf32, image->w, image->h, sx, sy); - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, sampleSize, sampleSize2), opacity); + *dst = src + ALPHA_BLEND(*dst, IA(src)); } } } @@ -951,7 +1327,7 @@ static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, con } -static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity) +static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) { Matrix itransform; @@ -959,85 +1335,137 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat if (!mathInverse(transform, &itransform)) return false; } else mathIdentity(&itransform); - auto halfScale = _halfScale(image->scale); - if (_compositing(surface)) { - if (opacity == 255) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.luma); - } - } else { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.luma); - } - } + if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity); + else return _rasterScaledMaskedImage(surface, image, &itransform, region, opacity); + } else if (_blending(surface)) { + return _rasterScaledBlendingImage(surface, image, &itransform, region, opacity); } else { - if (opacity == 255) return _rasterScaledRGBAImage(surface, image, &itransform, region, halfScale); - else return _rasterScaledTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale); + return _rasterScaledImage(surface, image, &itransform, region, opacity); } return false; } /************************************************************************/ -/* Direct RGBA Image */ +/* Direct Image */ /************************************************************************/ -static bool _rasterDirectMaskedRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t (*blender)(uint8_t*)) +static void _rasterDirectMaskedImageDup(SwSurface* surface, const SwImage* image, const SwBBox& region, SwBlender maskOp, SwBlender amaskOp, uint8_t opacity) { - TVGLOG("SW_ENGINE", "Direct Masked Image"); - - auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - auto h2 = static_cast(region.max.y - region.min.y); - auto w2 = static_cast(region.max.x - region.min.x); - auto csize = surface->compositor->image.channelSize; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(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 sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); - auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer - for (uint32_t y = 0; y < h2; ++y) { - auto dst = buffer; + for (uint32_t y = 0; y < h; ++y) { auto cmp = cbuffer; auto src = sbuffer; - for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, cmp += csize) { - auto tmp = ALPHA_BLEND(*src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + if (opacity == 255) { + for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { + *cmp = maskOp(*src, *cmp, IA(*src)); + } + } else { + for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { + *cmp = amaskOp(*src, *cmp, opacity); + } } - buffer += surface->stride; - cbuffer += surface->compositor->image.stride * csize; + cbuffer += cstride; sbuffer += image->stride; } - return true; } -static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity, uint8_t (*blender)(uint8_t*)) +static void _rasterDirectMaskedImageInt(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +{ + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf32 + (surface->compositor->bbox.min.y * cstride + surface->compositor->bbox.min.x); + + for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) { + if (y == region.min.y) { + auto cbuffer2 = cbuffer; + for (auto y2 = y; y2 < region.max.y; ++y2) { + auto tmp = cbuffer2; + auto x = surface->compositor->bbox.min.x; + while (x < surface->compositor->bbox.max.x) { + if (x == region.min.x) { + auto src = &image->buf32[(y2 + image->oy) * image->stride + (x + image->ox)]; + if (opacity == 255) { + for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) { + *tmp = ALPHA_BLEND(*tmp, A(*src)); + } + } else { + for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) { + auto t = ALPHA_BLEND(*src, opacity); + *tmp = ALPHA_BLEND(*tmp, A(t)); + } + } + x += w; + } else { + *tmp = 0; + ++tmp; + ++x; + } + } + cbuffer2 += cstride; + } + y += (h - 1); + } else { + rasterPixel32(cbuffer, 0x00000000, 0, surface->compositor->bbox.max.x - surface->compositor->bbox.min.x); + } + cbuffer += cstride; + } +} + + +static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) { - TVGLOG("SW_ENGINE", "Direct Masked Translucent Image"); + TVGLOG("SW_ENGINE", "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 (surface->compositor->method == CompositeMethod::IntersectMask) { + _rasterDirectMaskedImageInt(surface, image, region, opacity); + } else if (auto opMask = _getMaskOp(surface->compositor->method)) { + //Other Masking operations: Add, Subtract, Difference ... + _rasterDirectMaskedImageDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), opacity); + } else { + return false; + } + //Masking Composition + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox); +} + + +static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) +{ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - auto h2 = static_cast(region.max.y - region.min.y); - auto w2 = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); auto csize = surface->compositor->image.channelSize; + auto alpha = surface->alpha(surface->compositor->method); + + TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h); auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer - for (uint32_t y = 0; y < h2; ++y) { + for (uint32_t y = 0; y < h; ++y) { auto dst = buffer; auto cmp = cbuffer; auto src = sbuffer; - for (uint32_t x = 0; x < w2; ++x, ++dst, ++src, cmp += csize) { - auto tmp = ALPHA_BLEND(*src, _multiply(opacity, blender(cmp))); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + if (opacity == 255) { + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, alpha(cmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } else { + for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { + auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp))); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } } buffer += surface->stride; cbuffer += surface->compositor->image.stride * csize; @@ -1047,7 +1475,7 @@ static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const Sw } -static bool _rasterDirectTranslucentRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity) +static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) { auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x]; auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); @@ -1055,9 +1483,17 @@ static bool _rasterDirectTranslucentRGBAImage(SwSurface* surface, const SwImage* for (auto y = region.min.y; y < region.max.y; ++y) { auto dst = dbuffer; auto src = sbuffer; - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { - auto tmp = ALPHA_BLEND(*src, opacity); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); + if (opacity == 255) { + for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { + auto tmp = surface->blender(*src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, A(*src)); + } + } else { + for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { + auto tmp = ALPHA_BLEND(*src, opacity); + auto tmp2 = surface->blender(tmp, *dst, 255); + *dst = INTERPOLATE(tmp2, *dst, A(tmp)); + } } dbuffer += surface->stride; sbuffer += image->stride; @@ -1066,7 +1502,7 @@ static bool _rasterDirectTranslucentRGBAImage(SwSurface* surface, const SwImage* } -static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region) +static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) { auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x]; auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); @@ -1074,8 +1510,15 @@ static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, con for (auto y = region.min.y; y < region.max.y; ++y) { auto dst = dbuffer; auto src = sbuffer; - for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { - *dst = *src + ALPHA_BLEND(*dst, _ialpha(*src)); + 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)); + } } dbuffer += surface->stride; sbuffer += image->stride; @@ -1085,466 +1528,405 @@ static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, con //Blenders for the following scenarios: [Composition / Non-Composition] * [Opaque / Translucent] -static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity) +static bool _directImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity) { if (_compositing(surface)) { - if (opacity == 255) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterDirectMaskedRGBAImage(surface, image, region, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterDirectMaskedRGBAImage(surface, image, region, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterDirectMaskedRGBAImage(surface, image, region, surface->blender.luma); - } - } else { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, surface->blender.luma); - } - } + if (_matting(surface)) return _rasterDirectMattedImage(surface, image, region, opacity); + else return _rasterDirectMaskedImage(surface, image, region, opacity); + } else if (_blending(surface)) { + return _rasterDirectBlendingImage(surface, image, region, opacity); } else { - if (opacity == 255) return _rasterDirectRGBAImage(surface, image, region); - else return _rasterDirectTranslucentRGBAImage(surface, image, region, opacity); + return _rasterDirectImage(surface, image, region, opacity); } return false; } //Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed] -static bool _rasterRGBAImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& region, uint32_t opacity) +static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& region, uint8_t opacity) { //RLE Image if (image->rle) { - if (image->direct) return _directRleRGBAImage(surface, image, opacity); - else if (image->scaled) return _scaledRleRGBAImage(surface, image, transform, region, opacity); - else return _transformedRleRGBAImage(surface, image, transform, opacity); + if (image->direct) return _directRleImage(surface, image, opacity); + else if (image->scaled) return _scaledRleImage(surface, image, transform, region, opacity); + else return _transformedRleImage(surface, image, transform, opacity); //Whole Image } else { - if (image->direct) return _directRGBAImage(surface, image, region, opacity); - else if (image->scaled) return _scaledRGBAImage(surface, image, transform, region, opacity); - else return _transformedRGBAImage(surface, image, transform, region, opacity); + if (image->direct) return _directImage(surface, image, region, opacity); + else if (image->scaled) return _scaledImage(surface, image, transform, region, opacity); + else return _transformedImage(surface, image, transform, region, opacity); } } /************************************************************************/ -/* Rect Linear Gradient */ +/* Rect Gradient */ /************************************************************************/ -static bool _rasterLinearGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint8_t (*blender)(uint8_t*)) +template +static void _rasterGradientMaskedRectDup(SwSurface* surface, const SwBBox& region, const SwFill* fill, SwBlender maskOp) { - if (fill->linear.len < FLT_EPSILON) return false; - - auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); - auto csize = surface->compositor->image.channelSize; - auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; - - auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); - if (!sbuffer) return false; + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf32 + (region.min.y * cstride + region.min.x); for (uint32_t y = 0; y < h; ++y) { - fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w); - auto dst = buffer; - auto cmp = cbuffer; - auto src = sbuffer; - for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { - auto tmp = ALPHA_BLEND(*src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } - buffer += surface->stride; - cbuffer += surface->stride * csize; + fillMethod()(fill, cbuffer, region.min.y + y, region.min.x, w, maskOp, 255); + cbuffer += surface->stride; } - return true; } -static bool _rasterTranslucentLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +template +static void _rasterGradientMaskedRectInt(SwSurface* surface, const SwBBox& region, const SwFill* fill) { - if (fill->linear.len < FLT_EPSILON) return false; - - auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); - - auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); - if (!sbuffer) return false; - - for (uint32_t y = 0; y < h; ++y) { - auto dst = buffer; - fillFetchLinear(fill, sbuffer, region.min.y + y, region.min.x, w); - for (uint32_t x = 0; x < w; ++x, ++dst) { - *dst = sbuffer[x] + ALPHA_BLEND(*dst, _ialpha(sbuffer[x])); + auto cstride = surface->compositor->image.stride; + + for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) { + auto cmp = surface->compositor->image.buf32 + (y * cstride + surface->compositor->bbox.min.x); + if (y == region.min.y) { + for (auto y2 = y; y2 < region.max.y; ++y2) { + auto tmp = cmp; + auto x = surface->compositor->bbox.min.x; + while (x < surface->compositor->bbox.max.x) { + if (x == region.min.x) { + fillMethod()(fill, tmp, y2, x, w, opMaskPreIntersect, 255); + x += w; + tmp += w; + } else { + *tmp = 0; + ++tmp; + ++x; + } + } + cmp += cstride; + } + y += (h - 1); + } else { + rasterPixel32(cmp, 0x00000000, 0, surface->compositor->bbox.max.x -surface->compositor->bbox.min.x); + cmp += cstride; } - buffer += surface->stride; } - return true; } -static bool _rasterSolidLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +template +static bool _rasterGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { - if (fill->linear.len < FLT_EPSILON) return false; + auto method = surface->compositor->method; + + TVGLOG("SW_ENGINE", "Masked(%d) Gradient [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 (method == CompositeMethod::AddMask) _rasterGradientMaskedRectDup(surface, region, fill, opMaskPreAdd); + else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRectDup(surface, region, fill, opMaskPreSubtract); + else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRectDup(surface, region, fill, opMaskPreDifference); + else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRectInt(surface, region, fill); + else return false; + + //Masking Composition + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255); +} + +template +static bool _rasterGradientMattedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +{ auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - auto w = static_cast(region.max.x - region.min.x); auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); + auto csize = surface->compositor->image.channelSize; + auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; + auto alpha = surface->alpha(surface->compositor->method); + + TVGLOG("SW_ENGINE", "Matted(%d) Gradient [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h); for (uint32_t y = 0; y < h; ++y) { - fillFetchLinear(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w); + fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, cbuffer, alpha, csize, 255); + buffer += surface->stride; + cbuffer += surface->stride * csize; } return true; } -static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +template +static bool _rasterBlendingGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { - if (_compositing(surface)) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterLinearGradientMaskedRect(surface, region, fill, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterLinearGradientMaskedRect(surface, region, fill, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterLinearGradientMaskedRect(surface, region, fill, surface->blender.luma); + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); + + if (fill->translucent) { + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w, opBlendPreNormal, surface->blender, 255); } } else { - if (fill->translucent) return _rasterTranslucentLinearGradientRect(surface, region, fill); - else _rasterSolidLinearGradientRect(surface, region, fill); + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w, opBlendSrcOver, surface->blender, 255); + } } - return false; + return true; } - -/************************************************************************/ -/* Rle Linear Gradient */ -/************************************************************************/ - -static bool _rasterLinearGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint8_t (*blender)(uint8_t*)) +template +static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { - if (fill->linear.len < FLT_EPSILON) return false; - - auto span = rle->spans; - auto csize = surface->compositor->image.channelSize; - auto cbuffer = surface->compositor->image.buf8; - auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); - if (!buffer) return false; + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto h = static_cast(region.max.y - region.min.y); + auto w = static_cast(region.max.x - region.min.x); - for (uint32_t i = 0; i < rle->size; ++i, ++span) { - fillFetchLinear(fill, buffer, span->y, span->x, span->len); - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; - auto src = buffer; - if (span->coverage == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) { - auto tmp = ALPHA_BLEND(*src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } - } else { - auto ialpha = 255 - span->coverage; - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) { - auto tmp = ALPHA_BLEND(*src, blender(cmp)); - tmp = ALPHA_BLEND(tmp, span->coverage) + ALPHA_BLEND(*dst, ialpha); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } - } + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, opBlendPreNormal, 255); + buffer += surface->stride; } return true; } -static bool _rasterTranslucentLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +template +static bool _rasterSolidGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { - if (fill->linear.len < FLT_EPSILON) return false; - - auto span = rle->spans; - auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); - if (!buffer) return false; + auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; + auto w = static_cast(region.max.x - region.min.x); + auto h = static_cast(region.max.y - region.min.y); - for (uint32_t i = 0; i < rle->size; ++i, ++span) { - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - fillFetchLinear(fill, buffer, span->y, span->x, span->len); - if (span->coverage == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst) { - *dst = buffer[x] + ALPHA_BLEND(*dst, _ialpha(buffer[x])); - } - } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst) { - auto tmp = ALPHA_BLEND(buffer[x], span->coverage); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } - } + for (uint32_t y = 0; y < h; ++y) { + fillMethod()(fill, buffer + y * surface->stride, region.min.y + y, region.min.x, w, opBlendSrcOver, 255); } return true; } -static bool _rasterSolidLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { if (fill->linear.len < FLT_EPSILON) return false; - auto buf = static_cast(alloca(surface->w * sizeof(uint32_t))); - if (!buf) return false; - - auto span = rle->spans; - - for (uint32_t i = 0; i < rle->size; ++i, ++span) { - if (span->coverage == 255) { - fillFetchLinear(fill, surface->buf32 + span->y * surface->stride + span->x, span->y, span->x, span->len); - } else { - fillFetchLinear(fill, buf, span->y, span->x, span->len); - auto dst = &surface->buf32[span->y * surface->stride + span->x]; - for (uint32_t x = 0; x < span->len; ++x) { - dst[x] = INTERPOLATE(span->coverage, buf[x], dst[x]); - } - } + if (_compositing(surface)) { + if (_matting(surface)) return _rasterGradientMattedRect(surface, region, fill); + else return _rasterGradientMaskedRect(surface, region, fill); + } else if (_blending(surface)) { + return _rasterBlendingGradientRect(surface, region, fill); + } else { + if (fill->translucent) return _rasterTranslucentGradientRect(surface, region, fill); + else _rasterSolidGradientRect(surface, region, fill); } - return true; + return false; } -static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { - if (!rle) return false; + if (fill->radial.a < FLT_EPSILON) return false; if (_compositing(surface)) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterLinearGradientMaskedRle(surface, rle, fill, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterLinearGradientMaskedRle(surface, rle, fill, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterLinearGradientMaskedRle(surface, rle, fill, surface->blender.luma); - } + if (_matting(surface)) return _rasterGradientMattedRect(surface, region, fill); + else return _rasterGradientMaskedRect(surface, region, fill); + } else if (_blending(surface)) { + return _rasterBlendingGradientRect(surface, region, fill); } else { - if (fill->translucent) return _rasterTranslucentLinearGradientRle(surface, rle, fill); - else return _rasterSolidLinearGradientRle(surface, rle, fill); + if (fill->translucent) return _rasterTranslucentGradientRect(surface, region, fill); + else _rasterSolidGradientRect(surface, region, fill); } return false; } + /************************************************************************/ -/* Rect Radial Gradient */ +/* Rle Gradient */ /************************************************************************/ -static bool _rasterRadialGradientMaskedRect(SwSurface* surface, const SwBBox& region, const SwFill* fill, uint8_t(*blender)(uint8_t*)) +template +static void _rasterGradientMaskedRleDup(SwSurface* surface, const SwRleData* rle, const SwFill* fill, SwBlender maskOp) { - if (fill->radial.a < FLT_EPSILON) return false; - - auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - auto h = static_cast(region.max.y - region.min.y); - auto w = static_cast(region.max.x - region.min.x); - auto csize = surface->compositor->image.channelSize; - auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; - - auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); - if (!sbuffer) return false; + auto span = rle->spans; + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf32; - for (uint32_t y = 0; y < h; ++y) { - fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w); - auto dst = buffer; - auto cmp = cbuffer; - auto src = sbuffer; - for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { - auto tmp = ALPHA_BLEND(*src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } - buffer += surface->stride; - cbuffer += surface->stride * csize; + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto cmp = &cbuffer[span->y * cstride + span->x]; + fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage); } - return true; } -static bool _rasterTranslucentRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +template +static void _rasterGradientMaskedRleInt(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { - if (fill->radial.a < FLT_EPSILON) return false; - - auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - auto h = static_cast(region.max.y - region.min.y); - auto w = static_cast(region.max.x - region.min.x); - - auto sbuffer = static_cast(alloca(w * sizeof(uint32_t))); - if (!sbuffer) return false; - - for (uint32_t y = 0; y < h; ++y) { - auto dst = buffer; - fillFetchRadial(fill, sbuffer, region.min.y + y, region.min.x, w); - for (uint32_t x = 0; x < w; ++x, ++dst) { - *dst = sbuffer[x] + ALPHA_BLEND(*dst, _ialpha(sbuffer[x])); + auto span = rle->spans; + auto cstride = surface->compositor->image.stride; + auto cbuffer = surface->compositor->image.buf32; + + for (auto y = surface->compositor->bbox.min.y; y < surface->compositor->bbox.max.y; ++y) { + auto cmp = &cbuffer[y * cstride]; + auto x = surface->compositor->bbox.min.x; + while (x < surface->compositor->bbox.max.x) { + if (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) { + fillMethod()(fill, cmp, span->y, span->x, span->len, opMaskIntersect, span->coverage); + x += span->len; + ++span; + } else { + cmp[x] = 0; + ++x; + } } - buffer += surface->stride; } - return true; } -static bool _rasterSolidRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) +template +static bool _rasterGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { - if (fill->radial.a < FLT_EPSILON) return false; - - auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - auto h = static_cast(region.max.y - region.min.y); - auto w = static_cast(region.max.x - region.min.x); + TVGLOG("SW_ENGINE", "Masked(%d) Rle Linear Gradient", (int)surface->compositor->method); - for (uint32_t y = 0; y < h; ++y) { - auto dst = &buffer[y * surface->stride]; - fillFetchRadial(fill, dst, region.min.y + y, region.min.x, w); - } - return true; -} + auto method = surface->compositor->method; + if (method == CompositeMethod::AddMask) _rasterGradientMaskedRleDup(surface, rle, fill, opMaskAdd); + else if (method == CompositeMethod::SubtractMask) _rasterGradientMaskedRleDup(surface, rle, fill, opMaskSubtract); + else if (method == CompositeMethod::DifferenceMask) _rasterGradientMaskedRleDup(surface, rle, fill, opMaskDifference); + else if (method == CompositeMethod::IntersectMask) _rasterGradientMaskedRleInt(surface, rle, fill); + else return false; -static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) -{ - if (_compositing(surface)) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterRadialGradientMaskedRect(surface, region, fill, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterRadialGradientMaskedRect(surface, region, fill, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterRadialGradientMaskedRect(surface, region, fill, surface->blender.luma); - } - } else { - if (fill->translucent) return _rasterTranslucentRadialGradientRect(surface, region, fill); - else return _rasterSolidRadialGradientRect(surface, region, fill); - } - return false; + //Masking Composition + return _rasterDirectImage(surface, &surface->compositor->image, surface->compositor->bbox, 255); } -/************************************************************************/ -/* RLE Radial Gradient */ -/************************************************************************/ - -static bool _rasterRadialGradientMaskedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill, uint8_t(*blender)(uint8_t*)) +template +static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { - if (fill->radial.a < FLT_EPSILON) return false; + TVGLOG("SW_ENGINE", "Matted(%d) Rle Linear Gradient", (int)surface->compositor->method); auto span = rle->spans; auto csize = surface->compositor->image.channelSize; auto cbuffer = surface->compositor->image.buf8; - auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); - if (!buffer) return false; + auto alpha = surface->alpha(surface->compositor->method); for (uint32_t i = 0; i < rle->size; ++i, ++span) { - fillFetchRadial(fill, buffer, span->y, span->x, span->len); auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize]; - auto src = buffer; - if (span->coverage == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) { - auto tmp = ALPHA_BLEND(*src, blender(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } - } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst, ++src, cmp += csize) { - auto tmp = INTERPOLATE(span->coverage, ALPHA_BLEND(*src, blender(cmp)), *dst); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } - } + fillMethod()(fill, dst, span->y, span->x, span->len, cmp, alpha, csize, span->coverage); } return true; } -static bool _rasterTranslucentRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +template +static bool _rasterBlendingGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { - if (fill->radial.a < FLT_EPSILON) return false; - auto span = rle->spans; - auto buffer = static_cast(alloca(surface->w * sizeof(uint32_t))); - if (!buffer) return false; for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; - fillFetchRadial(fill, buffer, span->y, span->x, span->len); - if (span->coverage == 255) { - for (uint32_t x = 0; x < span->len; ++x, ++dst) { - *dst = buffer[x] + ALPHA_BLEND(*dst, _ialpha(buffer[x])); - } - } else { - for (uint32_t x = 0; x < span->len; ++x, ++dst) { - auto tmp = ALPHA_BLEND(buffer[x], span->coverage); - *dst = tmp + ALPHA_BLEND(*dst, _ialpha(tmp)); - } - } + fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, surface->blender, span->coverage); } return true; } -static bool _rasterSolidRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +template +static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { - if (fill->radial.a < FLT_EPSILON) return false; + auto span = rle->spans; - auto buf = static_cast(alloca(surface->w * sizeof(uint32_t))); - if (!buf) return false; + for (uint32_t i = 0; i < rle->size; ++i, ++span) { + auto dst = &surface->buf32[span->y * surface->stride + span->x]; + if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, 255); + else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendNormal, span->coverage); + } + return true; +} + +template +static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ auto span = rle->spans; for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; - if (span->coverage == 255) { - fillFetchRadial(fill, dst, span->y, span->x, span->len); - } else { - fillFetchRadial(fill, buf, span->y, span->x, span->len); - auto ialpha = 255 - span->coverage; - for (uint32_t x = 0; x < span->len; ++x, ++dst) { - *dst = ALPHA_BLEND(buf[x], span->coverage) + ALPHA_BLEND(*dst, ialpha); - } - } + if (span->coverage == 255) fillMethod()(fill, dst, span->y, span->x, span->len, opBlendSrcOver, 255); + else fillMethod()(fill, dst, span->y, span->x, span->len, opBlendInterp, span->coverage); } return true; } +static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) +{ + if (!rle || fill->linear.len < FLT_EPSILON) return false; + + if (_compositing(surface)) { + if (_matting(surface)) return _rasterGradientMattedRle(surface, rle, fill); + else return _rasterGradientMaskedRle(surface, rle, fill); + } else if (_blending(surface)) { + return _rasterBlendingGradientRle(surface, rle, fill); + } else { + if (fill->translucent) return _rasterTranslucentGradientRle(surface, rle, fill); + else return _rasterSolidGradientRle(surface, rle, fill); + } + return false; +} + + static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { - if (!rle) return false; + if (!rle || fill->radial.a < FLT_EPSILON) return false; if (_compositing(surface)) { - if (surface->compositor->method == CompositeMethod::AlphaMask) { - return _rasterRadialGradientMaskedRle(surface, rle, fill, _alpha); - } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { - return _rasterRadialGradientMaskedRle(surface, rle, fill, _ialpha); - } else if (surface->compositor->method == CompositeMethod::LumaMask) { - return _rasterRadialGradientMaskedRle(surface, rle, fill, surface->blender.luma); - } + if (_matting(surface)) return _rasterGradientMattedRle(surface, rle, fill); + else return _rasterGradientMaskedRle(surface, rle, fill); + } else if (_blending(surface)) { + _rasterBlendingGradientRle(surface, rle, fill); } else { - if (fill->translucent) _rasterTranslucentRadialGradientRle(surface, rle, fill); - else return _rasterSolidRadialGradientRle(surface, rle, fill); + if (fill->translucent) _rasterTranslucentGradientRle(surface, rle, fill); + else return _rasterSolidGradientRle(surface, rle, fill); } return false; } + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) + +void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len) +{ + //OPTIMIZE_ME: Support SIMD + cRasterPixels(dst, val, offset, len); +} + + +void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) { #if defined(THORVG_AVX_VECTOR_SUPPORT) - avxRasterRGBA32(dst, val, offset, len); + avxRasterPixel32(dst, val, offset, len); #elif defined(THORVG_NEON_VECTOR_SUPPORT) - neonRasterRGBA32(dst, val, offset, len); + neonRasterPixel32(dst, val, offset, len); #else - cRasterPixels(dst, val, offset, len); + cRasterPixels(dst, val, offset, len); #endif } bool rasterCompositor(SwSurface* surface) { + //See CompositeMethod, Alpha:3, InvAlpha:4, Luma:5, InvLuma:6 + surface->alphas[0] = _alpha; + surface->alphas[1] = _ialpha; + if (surface->cs == ColorSpace::ABGR8888 || surface->cs == ColorSpace::ABGR8888S) { - surface->blender.join = _abgrJoin; - surface->blender.luma = _abgrLuma; + surface->join = _abgrJoin; + surface->alphas[2] = _abgrLuma; + surface->alphas[3] = _abgrInvLuma; } else if (surface->cs == ColorSpace::ARGB8888 || surface->cs == ColorSpace::ARGB8888S) { - surface->blender.join = _argbJoin; - surface->blender.luma = _argbLuma; + surface->join = _argbJoin; + surface->alphas[2] = _argbLuma; + surface->alphas[3] = _argbInvLuma; } else { TVGERR("SW_ENGINE", "Unsupported Colorspace(%d) is expected!", surface->cs); return false; @@ -1557,24 +1939,26 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_ { if (!surface || !surface->buf32 || surface->stride == 0 || surface->w == 0 || surface->h == 0) return false; - //full clear + //32 bits if (surface->channelSize == sizeof(uint32_t)) { + //full clear if (w == surface->stride) { - rasterRGBA32(surface->buf32 + (surface->stride * y), 0x00000000, 0, w * h); + rasterPixel32(surface->buf32, 0x00000000, surface->stride * y, w * h); + //partial clear } else { - auto buffer = surface->buf32 + (surface->stride * y + x); for (uint32_t i = 0; i < h; i++) { - rasterRGBA32(buffer + (surface->stride * i), 0x00000000, 0, w); + rasterPixel32(surface->buf32, 0x00000000, (surface->stride * y + x) + (surface->stride * i), w); } } - //partial clear + //8 bits } else if (surface->channelSize == sizeof(uint8_t)) { + //full clear if (w == surface->stride) { - _rasterGrayscale8(surface->buf8 + (surface->stride * y), 0x00, 0, w * h); + rasterGrayscale8(surface->buf8, 0x00, surface->stride * y, w * h); + //partial clear } else { - auto buffer = surface->buf8 + (surface->stride * y + x); for (uint32_t i = 0; i < h; i++) { - _rasterGrayscale8(buffer + (surface->stride * i), 0x00, 0, w); + rasterGrayscale8(surface->buf8, 0x00, (surface->stride * y + x) + (surface->stride * i), w); } } } @@ -1671,9 +2055,9 @@ bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id) bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (a < 255) { - r = _multiply(r, a); - g = _multiply(g, a); - b = _multiply(b, a); + r = MULTIPLY(r, a); + g = MULTIPLY(g, a); + b = MULTIPLY(b, a); } if (shape->fastTrack) return _rasterRect(surface, shape->bbox, r, g, b, a); @@ -1684,16 +2068,16 @@ bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8 bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (a < 255) { - r = _multiply(r, a); - g = _multiply(g, a); - b = _multiply(b, a); + r = MULTIPLY(r, a); + g = MULTIPLY(g, a); + b = MULTIPLY(b, a); } return _rasterRle(surface, shape->strokeRle, r, g, b, a); } -bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint32_t opacity) +bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity) { if (surface->channelSize == sizeof(uint8_t)) { TVGERR("SW_ENGINE", "Not supported grayscale image!"); @@ -1707,8 +2091,8 @@ bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, con //TODO: case: _rasterRGBImageMesh() //TODO: case: _rasterGrayscaleImageMesh() //TODO: case: _rasterAlphaImageMesh() - if (mesh && mesh->triangleCnt > 0) return _transformedRGBAImageMesh(surface, image, mesh, transform, &bbox, opacity); - else return _rasterRGBAImage(surface, image, transform, bbox, opacity); + if (mesh && mesh->triangleCnt > 0) return _transformedImageMesh(surface, image, mesh, transform, &bbox, opacity); + else return _rasterImage(surface, image, transform, bbox, opacity); } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h index cf658a6abb78..090fa29a7aa4 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterAvx.h @@ -62,7 +62,7 @@ static inline __m128i ALPHA_BLEND(__m128i c, __m128i a) } -static void avxRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) +static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) { //1. calculate how many iterations we need to cover the length uint32_t iterations = len / N_32BITS_IN_256REG; @@ -89,12 +89,12 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u return false; } - auto color = surface->blender.join(r, g, b, a); + auto color = surface->join(r, g, b, a); auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); - auto ialpha = 255 - static_cast(_alpha(color)); + uint32_t ialpha = 255 - a; auto avxColor = _mm_set1_epi32(color); auto avxIalpha = _mm_set1_epi8(ialpha); @@ -138,7 +138,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, ui return false; } - auto color = surface->blender.join(r, g, b, a); + auto color = surface->join(r, g, b, a); auto span = rle->spans; uint32_t src; @@ -148,7 +148,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, ui if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); else src = color; - auto ialpha = 255 - static_cast(_alpha(src)); + auto ialpha = IA(src); //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required) auto notAligned = ((uintptr_t)dst & 0xf) / 4; diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h index ad2fc57f24cd..eb377e78e304 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterC.h @@ -21,9 +21,41 @@ */ template -static void inline cRasterPixels(PIXEL_T* dst, uint32_t val, uint32_t offset, int32_t len) +static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len) { dst += offset; + + //fix the misaligned memory + auto alignOffset = (long long) dst % 8; + if (alignOffset > 0) { + if (sizeof(PIXEL_T) == 4) alignOffset /= 4; + else if (sizeof(PIXEL_T) == 1) alignOffset = 8 - alignOffset; + while (alignOffset > 0 && len > 0) { + *dst++ = val; + --len; + --alignOffset; + } + } + + //64bits faster clear + if ((sizeof(PIXEL_T) == 4)) { + auto val64 = (uint64_t(val) << 32) | uint64_t(val); + while (len > 1) { + *reinterpret_cast(dst) = val64; + len -= 2; + dst += 2; + } + } else if (sizeof(PIXEL_T) == 1) { + auto val32 = (uint32_t(val) << 24) | (uint32_t(val) << 16) | (uint32_t(val) << 8) | uint32_t(val); + auto val64 = (uint64_t(val32) << 32) | val32; + while (len > 7) { + *reinterpret_cast(dst) = val64; + len -= 8; + dst += 8; + } + } + + //leftovers while (len--) *dst++ = val; } @@ -34,14 +66,15 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rl //32bit channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->blender.join(r, g, b, a); + auto color = surface->join(r, g, b, a); uint32_t src; for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf32[span->y * surface->stride + span->x]; if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); else src = color; + auto ialpha = IA(src); for (uint32_t x = 0; x < span->len; ++x, ++dst) { - *dst = src + ALPHA_BLEND(*dst, _ialpha(src)); + *dst = src + ALPHA_BLEND(*dst, ialpha); } } //8bit grayscale @@ -49,10 +82,11 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rl uint8_t src; for (uint32_t i = 0; i < rle->size; ++i, ++span) { auto dst = &surface->buf8[span->y * surface->stride + span->x]; - if (span->coverage < 255) src = _multiply(span->coverage, a); + if (span->coverage < 255) src = MULTIPLY(span->coverage, a); else src = a; + auto ialpha = ~a; for (uint32_t x = 0; x < span->len; ++x, ++dst) { - *dst = src + _multiply(*dst, ~src); + *dst = src + MULTIPLY(*dst, ialpha); } } } @@ -67,9 +101,9 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi //32bits channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->blender.join(r, g, b, a); + auto color = surface->join(r, g, b, 255); auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; - auto ialpha = _ialpha(color); + auto ialpha = 255 - a; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < w; ++x, ++dst) { @@ -79,10 +113,11 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi //8bit grayscale } else if (surface->channelSize == sizeof(uint8_t)) { auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x; + auto ialpha = ~a; for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; for (uint32_t x = 0; x < w; ++x, ++dst) { - *dst = a + _multiply(*dst, ~a); + *dst = a + MULTIPLY(*dst, ialpha); } } } @@ -94,13 +129,27 @@ static bool inline cRasterABGRtoARGB(Surface* surface) { TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h); - auto buffer = surface->buf32; - for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { - auto dst = buffer; - for (uint32_t x = 0; x < surface->w; ++x, ++dst) { - auto c = *dst; - //flip Blue, Red channels - *dst = (c & 0xff000000) + ((c & 0x00ff0000) >> 16) + (c & 0x0000ff00) + ((c & 0x000000ff) << 16); + //64bits faster converting + if (surface->w % 2 == 0) { + auto buffer = reinterpret_cast(surface->buf32); + for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride / 2) { + auto dst = buffer; + for (uint32_t x = 0; x < surface->w / 2; ++x, ++dst) { + auto c = *dst; + //flip Blue, Red channels + *dst = (c & 0xff000000ff000000) + ((c & 0x00ff000000ff0000) >> 16) + (c & 0x0000ff000000ff00) + ((c & 0x000000ff000000ff) << 16); + } + } + //default converting + } else { + auto buffer = surface->buf32; + for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) { + auto dst = buffer; + for (uint32_t x = 0; x < surface->w; ++x, ++dst) { + auto c = *dst; + //flip Blue, Red channels + *dst = (c & 0xff000000) + ((c & 0x00ff0000) >> 16) + (c & 0x0000ff00) + ((c & 0x000000ff) << 16); + } } } return true; diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h index 33c3d181b57b..ba77ed53cf62 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterNeon.h @@ -31,7 +31,7 @@ static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a) } -static void neonRasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) +static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) { uint32_t iterations = len / 4; uint32_t neonFilled = iterations * 4; @@ -67,7 +67,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, u else src = color; auto dst = &surface->buf32[span->y * surface->stride + span->x]; - auto ialpha = 255 - _alpha(src); + auto ialpha = IALPHA(src); if ((((uint32_t) dst) & 0x7) != 0) { //fill not aligned byte @@ -105,7 +105,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x; auto h = static_cast(region.max.y - region.min.y); auto w = static_cast(region.max.x - region.min.x); - auto ialpha = 255 - _alpha(color); + auto ialpha = 255 - a; auto vColor = vdup_n_u32(color); auto vIalpha = vdup_n_u8((uint8_t) ialpha); diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h index 52585162fcec..4b30c52ea398 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmap.h @@ -41,6 +41,7 @@ static inline void _swap(float& a, float& b, float& tmp) b = tmp; } + //Careful! Shared resource, No support threading static float dudx, dvdx; static float dxdya, dxdyb, dudya, dvdya; @@ -69,40 +70,744 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, uint8_t(*blender)(uint8_t*), AASpans* aaSpans) +static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag) +{ + float _dudx = dudx, _dvdx = dvdx; + float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; + float _xa = xa, _xb = xb, _ua = ua, _va = va; + auto sbuf = image->buf32; + int32_t sw = static_cast(image->stride); + int32_t sh = image->h; + int32_t x1, x2, ar, ab, iru, irv, px, ay; + int32_t vv = 0, uu = 0; + int32_t minx = INT32_MAX, maxx = INT32_MIN; + float dx, u, v, iptr; + auto cbuffer = surface->compositor->image.buf32; + SwSpan* span = nullptr; //used only when rle based. + + if (!_arrange(image, region, yStart, yEnd)) return; + + //Clear out of the Polygon vertical ranges + auto size = surface->compositor->bbox.max.x - surface->compositor->bbox.min.x; + if (dirFlag == 1) { //left top case. + for(int y = surface->compositor->bbox.min.y; y < yStart; ++y) { + rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size); + } + } + if (dirFlag == 4) { //right bottom case. + for(int y = yEnd; y < surface->compositor->bbox.max.y; ++y) { + rasterPixel32(surface->compositor->image.buf32 + y * surface->compositor->image.stride, 0, surface->compositor->bbox.min.x, size); + } + } + + //Loop through all lines in the segment + uint32_t spanIdx = 0; + + if (region) { + minx = region->min.x; + maxx = region->max.x; + } else { + span = image->rle->spans; + while (span->y < yStart) { + ++span; + ++spanIdx; + } + } + + for (int32_t y = yStart; y < yEnd; ++y) { + auto cmp = &cbuffer[y * surface->compositor->image.stride]; + x1 = (int32_t)_xa; + x2 = (int32_t)_xb; + + if (!region) { + minx = INT32_MAX; + maxx = INT32_MIN; + //one single row, could be consisted of multiple spans. + while (span->y == y && spanIdx < image->rle->size) { + if (minx > span->x) minx = span->x; + if (maxx < span->x + span->len) maxx = span->x + span->len; + ++span; + ++spanIdx; + } + } + + if (x1 < minx) x1 = minx; + if (x2 > maxx) x2 = maxx; + + //Anti-Aliasing frames + //FIXME: this aa must be applied before masking op + ay = y - aaSpans->yStart; + if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; + if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + + //Range allowed + if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { + for (int32_t x = surface->compositor->bbox.min.x; x < surface->compositor->bbox.max.x; ++x) { + //Range allowed + if (x >= x1 && x < x2) { + //Perform subtexel pre-stepping on UV + dx = 1 - (_xa - x1); + u = _ua + dx * _dudx; + v = _va + dx * _dvdx; + if ((uint32_t)v >= image->h) { + cmp[x] = 0; + } else { + if (opacity == 255) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + cmp[x] = ALPHA_BLEND(cmp[x], A(px)); + + //Step UV horizontally + u += _dudx; + v += _dvdx; + } else { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + cmp[x] = ALPHA_BLEND(cmp[x], MULTIPLY(A(px), opacity)); + + //Step UV horizontally + u += _dudx; + v += _dvdx; + } + } + } else { + //Clear out of polygon horizontal range + if (x < x1 && (dirFlag == 1 || dirFlag == 2)) cmp[x] = 0; + else if (x >= x2 && (dirFlag == 3 || dirFlag == 4)) cmp[x] = 0; + } + } + } + //Step along both edges + _xa += _dxdya; + _xb += _dxdyb; + _ua += _dudya; + _va += _dvdya; + } + xa = _xa; + xb = _xb; + ua = _ua; + va = _va; +} + + +static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImage* image, const SwBBox* region, SwBlender maskOp, SwBlender amaskOp, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) { -#define TEXMAP_TRANSLUCENT -#define TEXMAP_MASKING - #include "tvgSwRasterTexmapInternal.h" -#undef TEXMAP_MASKING -#undef TEXMAP_TRANSLUCENT + float _dudx = dudx, _dvdx = dvdx; + float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; + float _xa = xa, _xb = xb, _ua = ua, _va = va; + auto sbuf = image->buf32; + int32_t sw = static_cast(image->stride); + int32_t sh = image->h; + int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; + int32_t vv = 0, uu = 0; + int32_t minx = INT32_MAX, maxx = INT32_MIN; + float dx, u, v, iptr; + SwSpan* span = nullptr; //used only when rle based. + + if (!_arrange(image, region, yStart, yEnd)) return; + + //Loop through all lines in the segment + uint32_t spanIdx = 0; + + if (region) { + minx = region->min.x; + maxx = region->max.x; + } else { + span = image->rle->spans; + while (span->y < yStart) { + ++span; + ++spanIdx; + } + } + + y = yStart; + + while (y < yEnd) { + x1 = (int32_t)_xa; + x2 = (int32_t)_xb; + + if (!region) { + minx = INT32_MAX; + maxx = INT32_MIN; + //one single row, could be consisted of multiple spans. + while (span->y == y && spanIdx < image->rle->size) { + if (minx > span->x) minx = span->x; + if (maxx < span->x + span->len) maxx = span->x + span->len; + ++span; + ++spanIdx; + } + } + if (x1 < minx) x1 = minx; + if (x2 > maxx) x2 = maxx; + + //Anti-Aliasing frames + ay = y - aaSpans->yStart; + if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; + if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + + //Range allowed + if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { + + //Perform subtexel pre-stepping on UV + dx = 1 - (_xa - x1); + u = _ua + dx * _dudx; + v = _va + dx * _dvdx; + + x = x1; + + auto cmp = &surface->compositor->image.buf32[y * surface->compositor->image.stride + x1]; + + if (opacity == 255) { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + *cmp = maskOp(px, *cmp, IA(px)); + ++cmp; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } else { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + *cmp = amaskOp(px, *cmp, opacity); + ++cmp; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } + } + + //Step along both edges + _xa += _dxdya; + _xb += _dxdyb; + _ua += _dudya; + _va += _dvdya; + + if (!region && spanIdx >= image->rle->size) break; + + ++y; + } + xa = _xa; + xb = _xb; + ua = _ua; + va = _va; } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint8_t(*blender)(uint8_t*), AASpans* aaSpans) +static void _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0) { -#define TEXMAP_MASKING - #include "tvgSwRasterTexmapInternal.h" -#undef TEXMAP_MASKING + if (surface->compositor->method == CompositeMethod::IntersectMask) { + _rasterMaskedPolygonImageSegmentInt(surface, image, region, yStart, yEnd, aaSpans, opacity, dirFlag); + } else if (auto opMask = _getMaskOp(surface->compositor->method)) { + //Other Masking operations: Add, Subtract, Difference ... + _rasterMaskedPolygonImageSegmentDup(surface, image, region, opMask, _getAMaskOp(surface->compositor->method), yStart, yEnd, aaSpans, opacity); + } } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, uint32_t opacity, AASpans* aaSpans) +static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) { -#define TEXMAP_TRANSLUCENT - #include "tvgSwRasterTexmapInternal.h" -#undef TEXMAP_TRANSLUCENT + float _dudx = dudx, _dvdx = dvdx; + float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; + float _xa = xa, _xb = xb, _ua = ua, _va = va; + auto sbuf = image->buf32; + auto dbuf = surface->buf32; + int32_t sw = static_cast(image->stride); + int32_t sh = image->h; + int32_t dw = surface->stride; + int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; + int32_t vv = 0, uu = 0; + int32_t minx = INT32_MAX, maxx = INT32_MIN; + float dx, u, v, iptr; + uint32_t* buf; + SwSpan* span = nullptr; //used only when rle based. + + if (!_arrange(image, region, yStart, yEnd)) return; + + //Loop through all lines in the segment + uint32_t spanIdx = 0; + + if (region) { + minx = region->min.x; + maxx = region->max.x; + } else { + span = image->rle->spans; + while (span->y < yStart) { + ++span; + ++spanIdx; + } + } + + y = yStart; + + while (y < yEnd) { + x1 = (int32_t)_xa; + x2 = (int32_t)_xb; + + if (!region) { + minx = INT32_MAX; + maxx = INT32_MIN; + //one single row, could be consisted of multiple spans. + while (span->y == y && spanIdx < image->rle->size) { + if (minx > span->x) minx = span->x; + if (maxx < span->x + span->len) maxx = span->x + span->len; + ++span; + ++spanIdx; + } + } + if (x1 < minx) x1 = minx; + if (x2 > maxx) x2 = maxx; + + //Anti-Aliasing frames + ay = y - aaSpans->yStart; + if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; + if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + + //Range allowed + if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { + + //Perform subtexel pre-stepping on UV + dx = 1 - (_xa - x1); + u = _ua + dx * _dudx; + v = _va + dx * _dvdx; + + buf = dbuf + ((y * dw) + x1); + + x = x1; + + if (opacity == 255) { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + *buf = surface->blender(px, *buf, IA(px)); + ++buf; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } else { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + auto src = ALPHA_BLEND(px, opacity); + *buf = surface->blender(src, *buf, IA(src)); + ++buf; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } + } + + //Step along both edges + _xa += _dxdya; + _xb += _dxdyb; + _ua += _dudya; + _va += _dvdya; + + if (!region && spanIdx >= image->rle->size) break; + + ++y; + } + xa = _xa; + xb = _xb; + ua = _ua; + va = _va; } -static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans) +static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting) { - #include "tvgSwRasterTexmapInternal.h" + float _dudx = dudx, _dvdx = dvdx; + float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; + float _xa = xa, _xb = xb, _ua = ua, _va = va; + auto sbuf = image->buf32; + auto dbuf = surface->buf32; + int32_t sw = static_cast(image->stride); + int32_t sh = image->h; + int32_t dw = surface->stride; + int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; + int32_t vv = 0, uu = 0; + int32_t minx = INT32_MAX, maxx = INT32_MIN; + float dx, u, v, iptr; + uint32_t* buf; + SwSpan* span = nullptr; //used only when rle based. + + //for matting(composition) + auto csize = matting ? surface->compositor->image.channelSize: 0; + auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr; + uint8_t* cmp = nullptr; + + if (!_arrange(image, region, yStart, yEnd)) return; + + //Loop through all lines in the segment + uint32_t spanIdx = 0; + + if (region) { + minx = region->min.x; + maxx = region->max.x; + } else { + span = image->rle->spans; + while (span->y < yStart) { + ++span; + ++spanIdx; + } + } + + y = yStart; + + while (y < yEnd) { + x1 = (int32_t)_xa; + x2 = (int32_t)_xb; + + if (!region) { + minx = INT32_MAX; + maxx = INT32_MIN; + //one single row, could be consisted of multiple spans. + while (span->y == y && spanIdx < image->rle->size) { + if (minx > span->x) minx = span->x; + if (maxx < span->x + span->len) maxx = span->x + span->len; + ++span; + ++spanIdx; + } + } + if (x1 < minx) x1 = minx; + if (x2 > maxx) x2 = maxx; + + //Anti-Aliasing frames + ay = y - aaSpans->yStart; + if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; + if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; + + //Range allowed + if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { + + //Perform subtexel pre-stepping on UV + dx = 1 - (_xa - x1); + u = _ua + dx * _dudx; + v = _va + dx * _dvdx; + + buf = dbuf + ((y * dw) + x1); + + x = x1; + + if (matting) cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize]; + + if (opacity == 255) { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + if (uu >= sw) continue; + vv = (int) v; + if (vv >= sh) continue; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + uint32_t src; + if (matting) { + src = ALPHA_BLEND(px, alpha(cmp)); + cmp += csize; + } else { + src = px; + } + *buf = src + ALPHA_BLEND(*buf, IA(src)); + ++buf; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } else { + //Draw horizontal line + while (x++ < x2) { + uu = (int) u; + vv = (int) v; + + ar = (int)(255 * (1 - modff(u, &iptr))); + ab = (int)(255 * (1 - modff(v, &iptr))); + iru = uu + 1; + irv = vv + 1; + + if (vv >= sh) continue; + + px = *(sbuf + (vv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* right pixel */ + int px2 = *(sbuf + (vv * sw) + iru); + px = INTERPOLATE(px, px2, ar); + } + /* vertical interpolate */ + if (irv < sh) { + /* bottom pixel */ + int px2 = *(sbuf + (irv * sw) + uu); + + /* horizontal interpolate */ + if (iru < sw) { + /* bottom right pixel */ + int px3 = *(sbuf + (irv * sw) + iru); + px2 = INTERPOLATE(px2, px3, ar); + } + px = INTERPOLATE(px, px2, ab); + } + uint32_t src; + if (matting) { + src = ALPHA_BLEND(px, MULTIPLY(opacity, alpha(cmp))); + cmp += csize; + } else { + src = ALPHA_BLEND(px, opacity); + } + *buf = src + ALPHA_BLEND(*buf, IA(src)); + ++buf; + + //Step UV horizontally + u += _dudx; + v += _dvdx; + //range over? + if ((uint32_t)v >= image->h) break; + } + } + } + + //Step along both edges + _xa += _dxdya; + _xb += _dxdyb; + _ua += _dudya; + _va += _dvdya; + + if (!region && spanIdx >= image->rle->size) break; + + ++y; + } + xa = _xa; + xb = _xb; + ua = _ua; + va = _va; } /* This mapping algorithm is based on Mikael Kalms's. */ -static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, uint32_t opacity, Polygon& polygon, uint8_t(*blender)(uint8_t*), AASpans* aaSpans) +static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, Polygon& polygon, AASpans* aaSpans, uint8_t opacity) { float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x}; float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y}; @@ -165,6 +870,8 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const if (mathEqual(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 + auto blending = _blending(surface); //Blending required //Longer edge is on the left side if (!side) { @@ -190,14 +897,14 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const dxdyb = dxdy[0]; xb = x[0] + dy * dxdyb + (off_y * dxdyb); - if (blender) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blender, aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blender, aaSpans); + if (compositing) { + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); } else { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans); + _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); } - upper = true; } //Draw lower segment if possibly visible @@ -211,12 +918,13 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const // Set right edge X-slope and perform subpixel pre-stepping dxdyb = dxdy[2]; xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb); - if (blender) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blender, aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blender, aaSpans); + if (compositing) { + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); } else { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans); + _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); } } //Longer edge is on the right side @@ -240,14 +948,14 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const ua = u[0] + dy * dudya + (off_y * dudya); va = v[0] + dy * dvdya + (off_y * dvdya); - if (blender) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], blender, aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, blender, aaSpans); + if (compositing) { + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); } else { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], opacity, aaSpans); + _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); } - upper = true; } //Draw lower segment if possibly visible @@ -264,12 +972,13 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const ua = u[1] + dy * dudya + (off_y * dudya); va = v[1] + dy * dvdya + (off_y * dvdya); - if (blender) { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], blender, aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, blender, aaSpans); + if (compositing) { + if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); + else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); } else { - if (opacity == 255) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans); - else _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], opacity, aaSpans); + _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); } } } @@ -508,7 +1217,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) pos = 1; while (pos <= line->length[0]) { - *dst = INTERPOLATE((line->coverage[0] * pos), *dst, pixel); + *dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos); ++dst; ++pos; } @@ -520,7 +1229,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) pos = width; while ((int32_t)(width - line->length[1]) < pos) { - *dst = INTERPOLATE(255 - (line->coverage[1] * (line->length[1] - (width - pos))), *dst, pixel); + *dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * (line->length[1] - (width - pos)))); --dst; --pos; } @@ -545,7 +1254,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans) | / | 3 -- 2 */ -static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint8_t(*blender)(uint8_t*)) +static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity) { //Exceptions: No dedicated drawing area? if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; @@ -576,14 +1285,14 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const polygon.vertex[1] = vertices[1]; polygon.vertex[2] = vertices[3]; - _rasterPolygonImage(surface, image, region, opacity, polygon, blender, aaSpans); + _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); //Draw the second polygon polygon.vertex[0] = vertices[1]; polygon.vertex[1] = vertices[2]; polygon.vertex[2] = vertices[3]; - _rasterPolygonImage(surface, image, region, opacity, polygon, blender, aaSpans); + _rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); return _apply(surface, aaSpans); } @@ -602,7 +1311,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Should provide two Polygons, one for each triangle. // TODO: region? */ -static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint32_t opacity, uint8_t(*blender)(uint8_t*)) +static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity) { //Exceptions: No dedicated drawing area? if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false; @@ -636,7 +1345,7 @@ static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, c auto aaSpans = _AASpans(ys, ye, image, region); if (aaSpans) { for (uint32_t i = 0; i < mesh->triangleCnt; i++) { - _rasterPolygonImage(surface, image, region, opacity, transformedTris[i], blender, aaSpans); + _rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity); } // Apply to surface (note: frees the AA spans) _apply(surface, aaSpans); diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h deleted file mode 100644 index 51685fe6e8cc..000000000000 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRasterTexmapInternal.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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. - */ - -{ - float _dudx = dudx, _dvdx = dvdx; - float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; - float _xa = xa, _xb = xb, _ua = ua, _va = va; - auto sbuf = image->buf32; - auto dbuf = surface->buf32; - int32_t sw = static_cast(image->stride); - int32_t sh = image->h; - int32_t dw = surface->stride; - int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; - int32_t vv = 0, uu = 0; - int32_t minx = INT32_MAX, maxx = INT32_MIN; - float dx, u, v, iptr; - uint32_t* buf; - SwSpan* span = nullptr; //used only when rle based. - -#ifdef TEXMAP_MASKING - uint8_t* cmp; - auto csize = surface->compositor->image.channelSize; -#endif - - if (!_arrange(image, region, yStart, yEnd)) return; - - //Loop through all lines in the segment - uint32_t spanIdx = 0; - - if (region) { - minx = region->min.x; - maxx = region->max.x; - } else { - span = image->rle->spans; - while (span->y < yStart) { - ++span; - ++spanIdx; - } - } - - y = yStart; - - while (y < yEnd) { - x1 = (int32_t)_xa; - x2 = (int32_t)_xb; - - if (!region) { - minx = INT32_MAX; - maxx = INT32_MIN; - //one single row, could be consisted of multiple spans. - while (span->y == y && spanIdx < image->rle->size) { - if (minx > span->x) minx = span->x; - if (maxx < span->x + span->len) maxx = span->x + span->len; - ++span; - ++spanIdx; - } - } - if (x1 < minx) x1 = minx; - if (x2 > maxx) x2 = maxx; - - //Anti-Aliasing frames - ay = y - aaSpans->yStart; - if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; - if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; - - //Range exception - if ((x2 - x1) < 1 || (x1 >= maxx) || (x2 <= minx)) goto next; - - //Perform subtexel pre-stepping on UV - dx = 1 - (_xa - x1); - u = _ua + dx * _dudx; - v = _va + dx * _dvdx; - - buf = dbuf + ((y * dw) + x1); - - x = x1; - -#ifdef TEXMAP_MASKING - cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize]; -#endif - //Draw horizontal line - while (x++ < x2) { - uu = (int) u; - vv = (int) v; - - ar = (int)(255 * (1 - modff(u, &iptr))); - ab = (int)(255 * (1 - modff(v, &iptr))); - iru = uu + 1; - irv = vv + 1; - - if (vv >= sh) continue; - - px = *(sbuf + (vv * sw) + uu); - - /* horizontal interpolate */ - if (iru < sw) { - /* right pixel */ - int px2 = *(sbuf + (vv * sw) + iru); - px = INTERPOLATE(ar, px, px2); - } - /* vertical interpolate */ - if (irv < sh) { - /* bottom pixel */ - int px2 = *(sbuf + (irv * sw) + uu); - - /* horizontal interpolate */ - if (iru < sw) { - /* bottom right pixel */ - int px3 = *(sbuf + (irv * sw) + iru); - px2 = INTERPOLATE(ar, px2, px3); - } - px = INTERPOLATE(ab, px, px2); - } -#if defined(TEXMAP_MASKING) && defined(TEXMAP_TRANSLUCENT) - auto src = ALPHA_BLEND(px, _multiply(opacity, blender(cmp))); -#elif defined(TEXMAP_MASKING) - auto src = ALPHA_BLEND(px, blender(cmp)); -#elif defined(TEXMAP_TRANSLUCENT) - auto src = ALPHA_BLEND(px, opacity); -#else - auto src = px; -#endif - *buf = src + ALPHA_BLEND(*buf, _ialpha(src)); - ++buf; -#ifdef TEXMAP_MASKING - cmp += csize; -#endif - //Step UV horizontally - u += _dudx; - v += _dvdx; - //range over? - if ((uint32_t)v >= image->h) break; - } -next: - //Step along both edges - _xa += _dxdya; - _xb += _dxdyb; - _ua += _dudya; - _va += _dvdya; - - if (!region && spanIdx >= image->rle->size) break; - - ++y; - } - xa = _xa; - xb = _xb; - ua = _ua; - va = _va; -} diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp index 6223bc87221a..1115a41d6498 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.cpp @@ -35,13 +35,13 @@ static uint32_t threadsCnt = 0; struct SwTask : Task { - Matrix* transform = nullptr; SwSurface* surface = nullptr; SwMpool* mpool = nullptr; - RenderUpdateFlag flags = RenderUpdateFlag::None; - Array clips; - uint32_t opacity; SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region + Matrix* transform = nullptr; + Array clips; + RenderUpdateFlag flags = RenderUpdateFlag::None; + uint8_t opacity; bool pushed = false; //Pushed into task list? bool disposed = false; //Disposed task? @@ -106,7 +106,7 @@ struct SwShapeTask : SwTask if (HALF_STROKE(rshape->strokeWidth()) > 0) { rshape->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); - visibleStroke = rshape->strokeFill() || (static_cast(strokeAlpha * opacity / 255) > 0); + visibleStroke = rshape->strokeFill() || (MULTIPLY(strokeAlpha, opacity) > 0); } //This checks also for the case, if the invisible shape turned to visible by alpha. @@ -117,7 +117,7 @@ struct SwShapeTask : SwTask if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) { uint8_t alpha = 0; rshape->fillColor(nullptr, nullptr, nullptr, &alpha); - alpha = static_cast(static_cast(alpha) * opacity / 255); + alpha = MULTIPLY(alpha, opacity); visibleFill = (alpha > 0 || rshape->fill); if (visibleFill || visibleStroke || clipper) { shapeReset(&shape); @@ -125,10 +125,6 @@ struct SwShapeTask : SwTask } } - //Decide Stroking Composition - if (visibleStroke && visibleFill && opacity < 255) cmpStroking = true; - else cmpStroking = false; - //Fill if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) { if (visibleFill || clipper) { @@ -143,7 +139,7 @@ struct SwShapeTask : SwTask if (auto fill = rshape->fill) { auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false; if (ctable) shapeResetFill(&shape); - if (!shapeGenFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err; + if (!shapeGenFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err; } else { shapeDelFill(&shape); } @@ -158,7 +154,7 @@ struct SwShapeTask : SwTask if (auto fill = rshape->strokeFill()) { auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; if (ctable) shapeResetStrokeFill(&shape); - if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, cmpStroking ? 255 : opacity, ctable)) goto err; + if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err; } else { shapeDelStrokeFill(&shape); } @@ -171,7 +167,7 @@ struct SwShapeTask : SwTask shapeDelOutline(&shape, mpool, tid); //Clip Path - for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { + for (auto clip = clips.data; clip < clips.end(); ++clip) { auto clipper = static_cast(*clip); //Clip shape rle if (shape.rle && !clipper->clip(shape.rle)) goto err; @@ -232,7 +228,7 @@ struct SwSceneTask : SwTask rleMerge(sceneRle, clipper1->rle(), clipper2->rle()); //Unify the remained clippers - for (auto rd = scene.data + 2; rd < (scene.data + scene.count); ++rd) { + for (auto rd = scene.data + 2; rd < scene.end(); ++rd) { auto clipper = static_cast(*rd); rleMerge(sceneRle, sceneRle, clipper->rle()); } @@ -294,7 +290,7 @@ struct SwImageTask : SwTask if (image.rle) { //Clear current task memorypool here if the clippers would use the same memory pool imageDelOutline(&image, mpool, tid); - for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { + for (auto clip = clips.data; clip < clips.end(); ++clip) { auto clipper = static_cast(*clip); if (!clipper->clip(image.rle)) goto err; } @@ -326,26 +322,26 @@ static void _termEngine() } -static void _renderFill(SwShapeTask* task, SwSurface* surface, uint32_t opacity) +static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity) { uint8_t r, g, b, a; if (auto fill = task->rshape->fill) { rasterGradientShape(surface, &task->shape, fill->identifier()); } else { task->rshape->fillColor(&r, &g, &b, &a); - a = static_cast((opacity * (uint32_t) a) / 255); + a = MULTIPLY(opacity, a); if (a > 0) rasterShape(surface, &task->shape, r, g, b, a); } } -static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint32_t opacity) +static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity) { uint8_t r, g, b, a; if (auto strokeFill = task->rshape->strokeFill()) { rasterGradientStroke(surface, &task->shape, strokeFill->identifier()); } else { if (task->rshape->strokeColor(&r, &g, &b, &a)) { - a = static_cast((opacity * (uint32_t) a) / 255); + a = MULTIPLY(opacity, a); if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); } } @@ -359,7 +355,7 @@ SwRenderer::~SwRenderer() { clearCompositors(); - if (surface) delete(surface); + delete(surface); if (!sharedMpool) mpoolTerm(mpool); @@ -371,7 +367,7 @@ SwRenderer::~SwRenderer() bool SwRenderer::clear() { - for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) { + for (auto task = tasks.data; task < tasks.end(); ++task) { if ((*task)->disposed) { delete(*task); } else { @@ -444,7 +440,7 @@ bool SwRenderer::preRender() void SwRenderer::clearCompositors() { //Free Composite Caches - for (auto comp = compositors.data; comp < (compositors.data + compositors.count); ++comp) { + for (auto comp = compositors.data; comp < compositors.end(); ++comp) { free((*comp)->compositor->image.data); delete((*comp)->compositor); delete(*comp); @@ -460,8 +456,9 @@ bool SwRenderer::postRender() rasterUnpremultiply(surface); } - for (auto task = tasks.data; task < (tasks.data + tasks.count); ++task) { - (*task)->pushed = false; + for (auto task = tasks.data; task < tasks.end(); ++task) { + if ((*task)->disposed) delete(*task); + else (*task)->pushed = false; } tasks.clear(); @@ -490,41 +487,79 @@ bool SwRenderer::renderShape(RenderData data) if (task->opacity == 0) return true; - uint32_t opacity; - Compositor* cmp = nullptr; - - //Do Stroking Composition - if (task->cmpStroking) { - opacity = 255; - cmp = target(task->bounds(), colorSpace()); - beginComposite(cmp, CompositeMethod::None, task->opacity); - //No Stroking Composition - } else { - opacity = task->opacity; - } - //Main raster stage if (task->rshape->stroke && task->rshape->stroke->strokeFirst) { - _renderStroke(task, surface, opacity); - _renderFill(task, surface, opacity); + _renderStroke(task, surface, task->opacity); + _renderFill(task, surface, task->opacity); } else { - _renderFill(task, surface, opacity); - _renderStroke(task, surface, opacity); + _renderFill(task, surface, task->opacity); + _renderStroke(task, surface, task->opacity); } - if (task->cmpStroking) endComposite(cmp); - return true; } +bool SwRenderer::blend(BlendMethod method) +{ + if (surface->blendMethod == method) return true; + surface->blendMethod = method; + + switch (method) { + case BlendMethod::Add: + surface->blender = opBlendAdd; + break; + case BlendMethod::Screen: + surface->blender = opBlendScreen; + break; + case BlendMethod::Multiply: + surface->blender = opBlendMultiply; + 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; + case BlendMethod::Lighten: + surface->blender = opBlendLighten; + break; + case BlendMethod::ColorDodge: + surface->blender = opBlendColorDodge; + break; + case BlendMethod::ColorBurn: + surface->blender = opBlendColorBurn; + break; + case BlendMethod::HardLight: + surface->blender = opBlendHardLight; + break; + case BlendMethod::SoftLight: + surface->blender = opBlendSoftLight; + break; + default: + surface->blender = nullptr; + break; + } + return false; +} + + RenderRegion SwRenderer::region(RenderData data) { return static_cast(data)->bounds(); } -bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) +bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) { if (!cmp) return false; auto p = static_cast(cmp); @@ -579,7 +614,7 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) auto reqChannelSize = CHANNEL_SIZE(cs); //Use cached data - for (auto p = compositors.data; p < (compositors.data + compositors.count); ++p) { + for (auto p = compositors.data; p < compositors.end(); ++p) { if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) { cmp = *p; break; @@ -674,7 +709,7 @@ bool SwRenderer::dispose(RenderData data) } -void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array& clips, RenderUpdateFlag flags) +void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags) { if (!surface) return task; if (flags == RenderUpdateFlag::None) return task; @@ -685,7 +720,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, //TODO: Failed threading them. It would be better if it's possible. //See: https://github.com/thorvg/thorvg/issues/1409 //Guarantee composition targets get ready. - for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) { + for (auto clip = clips.data; clip < clips.end(); ++clip) { static_cast(*clip)->done(); } @@ -719,7 +754,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, } -RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) +RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) { //prepare task auto task = static_cast(data); @@ -728,11 +763,11 @@ RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD task->source = surface; task->mesh = mesh; } - return prepareCommon(task, transform, opacity, clips, flags); + return prepareCommon(task, transform, clips, opacity, flags); } -RenderData SwRenderer::prepare(const Array& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) +RenderData SwRenderer::prepare(const Array& scene, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) { //prepare task auto task = static_cast(data); @@ -742,14 +777,14 @@ RenderData SwRenderer::prepare(const Array& scene, RenderData data, //TODO: Failed threading them. It would be better if it's possible. //See: https://github.com/thorvg/thorvg/issues/1409 //Guarantee composition targets get ready. - for (auto task = scene.data; task < (scene.data + scene.count); ++task) { + for (auto task = scene.data; task < scene.end(); ++task) { static_cast(*task)->done(); } - return prepareCommon(task, transform, opacity, clips, flags); + return prepareCommon(task, transform, clips, opacity, flags); } -RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) +RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) { //prepare task auto task = static_cast(data); @@ -759,7 +794,7 @@ RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const } task->clipper = clipper; - return prepareCommon(task, transform, opacity, clips, flags); + return prepareCommon(task, transform, clips, opacity, flags); } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h index 36614fa1c134..4393740bd992 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRenderer.h @@ -36,9 +36,9 @@ namespace tvg class SwRenderer : public RenderMethod { public: - RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) override; - RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; - RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) override; + RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override; + RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; + RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) override; bool preRender() override; bool renderShape(RenderData data) override; bool renderImage(RenderData data) override; @@ -47,6 +47,7 @@ class SwRenderer : public RenderMethod RenderRegion region(RenderData data) override; RenderRegion viewport() override; bool viewport(const RenderRegion& vp) override; + bool blend(BlendMethod method) override; ColorSpace colorSpace() override; bool clear() override; @@ -55,7 +56,7 @@ class SwRenderer : public RenderMethod bool mempool(bool shared); Compositor* target(const RenderRegion& region, ColorSpace cs) override; - bool beginComposite(Compositor* cmp, CompositeMethod method, uint32_t opacity) override; + bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override; bool endComposite(Compositor* cmp) override; void clearCompositors(); @@ -70,13 +71,12 @@ class SwRenderer : public RenderMethod Array compositors; //render targets cache list SwMpool* mpool; //private memory pool RenderRegion vport; //viewport - bool sharedMpool = true; //memory-pool behavior policy SwRenderer(); ~SwRenderer(); - RenderData prepareCommon(SwTask* task, const RenderTransform* transform, uint32_t opacity, const Array& clips, RenderUpdateFlag flags); + RenderData prepareCommon(SwTask* task, const RenderTransform* transform, const Array& clips, uint8_t opacity, RenderUpdateFlag flags); }; } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp index 50dec208b225..fe84983a767a 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwRle.cpp @@ -708,22 +708,19 @@ static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, } -static bool _decomposeOutline(RleWorker& rw) +static void _decomposeOutline(RleWorker& rw) { auto outline = rw.outline; auto first = 0; //index of first point in contour - for (uint32_t n = 0; n < outline->cntrsCnt; ++n) { - auto last = outline->cntrs[n]; - auto limit = outline->pts + last; - auto start = UPSCALE(outline->pts[first]); - auto pt = outline->pts + first; - auto types = outline->types + first; + for (auto cntr = outline->cntrs.data; cntr < outline->cntrs.end(); ++cntr) { + auto last = *cntr; + auto limit = outline->pts.data + last; + auto start = UPSCALE(outline->pts.data[first]); + auto pt = outline->pts.data + first; + auto types = outline->types.data + first; - /* A contour cannot start with a cubic control point! */ - if (types[0] == SW_CURVE_TYPE_CUBIC) goto invalid_outline; - - _moveTo(rw, UPSCALE(outline->pts[first])); + _moveTo(rw, UPSCALE(outline->pts.data[first])); while (pt < limit) { ++pt; @@ -734,9 +731,6 @@ static bool _decomposeOutline(RleWorker& rw) _lineTo(rw, UPSCALE(*pt)); //types cubic } else { - if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) - goto invalid_outline; - pt += 2; types += 2; @@ -752,22 +746,15 @@ static bool _decomposeOutline(RleWorker& rw) close: first = last + 1; } - - return true; - -invalid_outline: - TVGERR("SW_ENGINE", "Invalid Outline!"); - return false; } static int _genRle(RleWorker& rw) { if (setjmp(rw.jmpBuf) == 0) { - auto ret = _decomposeOutline(rw); + _decomposeOutline(rw); if (!rw.invalid) _recordCell(rw); - if (ret) return 0; //success - else return 1; //fail + return 0; } return -1; //lack of cell memory } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp index 7462a7b7eafa..14dd68b906f6 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwShape.cpp @@ -61,91 +61,39 @@ static void _lineSplitAt(const Line& cur, float at, Line& left, Line& right) } -static bool _growOutlineContour(SwOutline& outline, uint32_t n) -{ - if (outline.reservedCntrsCnt >= outline.cntrsCnt + n) return false; - outline.reservedCntrsCnt = outline.cntrsCnt + n; - outline.cntrs = static_cast(realloc(outline.cntrs, outline.reservedCntrsCnt * sizeof(uint32_t))); - return true; -} - - -static void _reserveOutlineClose(SwOutline& outline) -{ - //Dash outlines are always opened. - //Only normal outlines use this information, it sholud be same to their contour counts. - if (outline.closed) free(outline.closed); - outline.closed = static_cast(calloc(outline.reservedCntrsCnt, sizeof(bool))); -} - - -static void _resetOutlineClose(SwOutline& outline) -{ - memset(outline.closed, 0x0, outline.reservedCntrsCnt * sizeof(bool)); -} - - -static void _growOutlinePoint(SwOutline& outline, uint32_t n) -{ - if (outline.reservedPtsCnt >= outline.ptsCnt + n) return; - outline.reservedPtsCnt = outline.ptsCnt + n; - outline.pts = static_cast(realloc(outline.pts, outline.reservedPtsCnt * sizeof(SwPoint))); - outline.types = static_cast(realloc(outline.types, outline.reservedPtsCnt * sizeof(uint8_t))); -} - - static void _outlineEnd(SwOutline& outline) { - if (outline.ptsCnt == 0) return; - - _growOutlineContour(outline, 1); - outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1; - ++outline.cntrsCnt; + if (outline.pts.empty()) return; + outline.cntrs.push(outline.pts.count - 1); } static void _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform) { - _growOutlinePoint(outline, 1); - - outline.pts[outline.ptsCnt] = mathTransform(to, transform); - outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT; - - if (outline.ptsCnt > 0) { - _growOutlineContour(outline, 1); - outline.cntrs[outline.cntrsCnt] = outline.ptsCnt - 1; - ++outline.cntrsCnt; - } + if (outline.pts.count > 0) outline.cntrs.push(outline.pts.count - 1); - ++outline.ptsCnt; + outline.pts.push(mathTransform(to, transform)); + outline.types.push(SW_CURVE_TYPE_POINT); } static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform) { - _growOutlinePoint(outline, 1); - - outline.pts[outline.ptsCnt] = mathTransform(to, transform); - outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT; - ++outline.ptsCnt; + outline.pts.push(mathTransform(to, transform)); + outline.types.push(SW_CURVE_TYPE_POINT); } static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform) { - _growOutlinePoint(outline, 3); - - outline.pts[outline.ptsCnt] = mathTransform(ctrl1, transform); - outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC; - ++outline.ptsCnt; + outline.pts.push(mathTransform(ctrl1, transform)); + outline.types.push(SW_CURVE_TYPE_CUBIC); - outline.pts[outline.ptsCnt] = mathTransform(ctrl2, transform); - outline.types[outline.ptsCnt] = SW_CURVE_TYPE_CUBIC; - ++outline.ptsCnt; + outline.pts.push(mathTransform(ctrl2, transform)); + outline.types.push(SW_CURVE_TYPE_CUBIC); - outline.pts[outline.ptsCnt] = mathTransform(to, transform); - outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT; - ++outline.ptsCnt; + outline.pts.push(mathTransform(to, transform)); + outline.types.push(SW_CURVE_TYPE_POINT); } @@ -153,30 +101,21 @@ static void _outlineClose(SwOutline& outline) { uint32_t i = 0; - if (outline.cntrsCnt > 0) { - i = outline.cntrs[outline.cntrsCnt - 1] + 1; - } else { - i = 0; //First Path - } + if (outline.cntrs.count > 0) i = outline.cntrs.last() + 1; + else i = 0; //First Path //Make sure there is at least one point in the current path - if (outline.ptsCnt == i) return; + if (outline.pts.count == i) return; //Close the path - _growOutlinePoint(outline, 1); - - outline.pts[outline.ptsCnt] = outline.pts[i]; - outline.types[outline.ptsCnt] = SW_CURVE_TYPE_POINT; - ++outline.ptsCnt; - outline.closed[outline.cntrsCnt] = true; + outline.pts.push(outline.pts.data[i]); + outline.types.push(SW_CURVE_TYPE_POINT); + outline.closed.push(true); } static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform) { - _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1); - _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1); - Line cur = {dash.ptCur, *to}; auto len = _lineLength(cur.pt1, cur.pt2); @@ -220,9 +159,6 @@ 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) { - _growOutlinePoint(*dash.outline, dash.outline->ptsCnt >> 1); - _growOutlineContour(*dash.outline, dash.outline->cntrsCnt >> 1); - Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to}; auto len = bezLength(cur); @@ -269,11 +205,11 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform) { - const PathCommand* cmds = rshape->path.cmds; - auto cmdCnt = rshape->path.cmdCnt; + const PathCommand* cmds = rshape->path.cmds.data; + auto cmdCnt = rshape->path.cmds.count; - const Point* pts = rshape->path.pts; - auto ptsCnt = rshape->path.ptsCnt; + const Point* pts = rshape->path.pts.data; + auto ptsCnt = rshape->path.pts.count; //No actual shape data if (cmdCnt == 0 || ptsCnt == 0) return nullptr; @@ -323,8 +259,9 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans ++outlineCntrsCnt; //for end //No idea exact count.... Reserve Approximitely 20x... - _growOutlinePoint(*dash.outline, outlinePtsCnt * 20); - _growOutlineContour(*dash.outline, outlineCntrsCnt * 20); + dash.outline->pts.grow(20 * outlinePtsCnt); + dash.outline->types.grow(20 * outlinePtsCnt); + dash.outline->cntrs.grow(20 * outlineCntrsCnt); while (cmdCnt-- > 0) { switch (*cmds) { @@ -364,12 +301,12 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans static bool _axisAlignedRect(const SwOutline* outline) { //Fast Track: axis-aligned rectangle? - if (outline->ptsCnt != 5) return false; + if (outline->pts.count != 5) return false; - auto pt1 = outline->pts + 0; - auto pt2 = outline->pts + 1; - auto pt3 = outline->pts + 2; - auto pt4 = outline->pts + 3; + auto pt1 = outline->pts.data + 0; + auto pt2 = outline->pts.data + 1; + auto pt3 = outline->pts.data + 2; + auto pt4 = outline->pts.data + 3; auto a = SwPoint{pt1->x, pt3->y}; auto b = SwPoint{pt3->x, pt1->y}; @@ -380,14 +317,13 @@ static bool _axisAlignedRect(const SwOutline* outline) } - static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite) { - const PathCommand* cmds = rshape->path.cmds; - auto cmdCnt = rshape->path.cmdCnt; + const PathCommand* cmds = rshape->path.cmds.data; + auto cmdCnt = rshape->path.cmds.count; - const Point* pts = rshape->path.pts; - auto ptsCnt = rshape->path.ptsCnt; + const Point* pts = rshape->path.pts.data; + auto ptsCnt = rshape->path.pts.count; //No actual shape data if (cmdCnt == 0 || ptsCnt == 0) return false; @@ -431,13 +367,15 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* shape->outline = mpoolReqOutline(mpool, tid); auto outline = shape->outline; - _growOutlinePoint(*outline, outlinePtsCnt); + outline->pts.grow(outlinePtsCnt); + outline->types.grow(outlinePtsCnt); + outline->cntrs.grow(outlineCntrsCnt); - if (_growOutlineContour(*outline, outlineCntrsCnt)) { - _reserveOutlineClose(*outline); - } else { - _resetOutlineClose(*outline); - } + //Dash outlines are always opened. + //Only normal outlines use this information, it sholud be same to their contour counts. + outline->closed.reserve(outline->cntrs.reserved); + + memset(outline->closed.data, 0x0, sizeof(bool) * outline->closed.reserved); //Generate Outlines while (cmdCnt-- > 0) { @@ -605,10 +543,10 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* fail: if (freeOutline) { - if (shapeOutline->cntrs) free(shapeOutline->cntrs); - if (shapeOutline->pts) free(shapeOutline->pts); - if (shapeOutline->types) free(shapeOutline->types); - if (shapeOutline->closed) free(shapeOutline->closed); + free(shapeOutline->cntrs.data); + free(shapeOutline->pts.data); + free(shapeOutline->types.data); + free(shapeOutline->closed.data); free(shapeOutline); } mpoolRetStrokeOutline(mpool, tid); @@ -617,13 +555,13 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* } -bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable) +bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) { return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable); } -bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable) +bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable) { return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable); } diff --git a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp index 2f41d5f0217a..38626cb42895 100644 --- a/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp +++ b/thirdparty/thorvg/src/lib/sw_engine/tvgSwStroke.cpp @@ -129,7 +129,6 @@ static void _borderCubicTo(SwStrokeBorder* border, const SwPoint& ctrl1, const S tag[2] = SW_STROKE_TAG_POINT; border->ptsCnt += 3; - border->movable = false; } @@ -193,7 +192,6 @@ static void _borderLineTo(SwStrokeBorder* border, const SwPoint& to, bool movabl //move last point border->pts[border->ptsCnt - 1] = to; } else { - //don't add zero-length line_to if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return; @@ -233,8 +231,6 @@ static void _arcTo(SwStroke& stroke, int32_t side) static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength) { - constexpr SwFixed MITER_LIMIT = 4 * (1 << 16); - auto border = stroke.borders + side; if (stroke.join == StrokeJoin::Round) { @@ -257,7 +253,7 @@ static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength) } thcos = mathCos(theta); - auto sigma = mathMultiply(MITER_LIMIT, thcos); + auto sigma = mathMultiply(stroke.miterlimit, thcos); //is miter limit exceeded? if (sigma < 0x10000L) bevel = true; @@ -432,8 +428,7 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to) static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to) { - /* if all control points are coincident, this is a no-op; - avoid creating a spurious corner */ + //if all control points are coincident, this is a no-op; avoid creating a spurious corner if ((stroke.center - ctrl1).small() && (ctrl1 - ctrl2).small() && (ctrl2 - to).small()) { stroke.center = to; return; @@ -499,8 +494,7 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl auto border = stroke.borders; int32_t side = 0; - while (side <= 1) - { + while (side < 2) { auto rotate = SIDE_TO_ROTATE(side); //compute control points @@ -521,7 +515,6 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl _end += arc[0]; if (stroke.handleWideStrokes) { - /* determine whether the border radius is greater than the radius of curvature of the original arc */ auto _start = border->pts[border->ptsCnt - 1]; @@ -556,8 +549,6 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl ++border; continue; } - - //else fall through } _borderCubicTo(border, _ctrl1, _ctrl2, _end); ++side; @@ -653,7 +644,6 @@ static void _addReverseLeft(SwStroke& stroke, bool opened) if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END) dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END); } - --srcPt; --srcTag; ++dstPt; @@ -779,36 +769,27 @@ static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrs static void _exportBorderOutline(const SwStroke& stroke, SwOutline* outline, uint32_t side) { auto border = stroke.borders + side; + if (border->ptsCnt == 0) return; - if (border->ptsCnt == 0) return; //invalid border - - memcpy(outline->pts + outline->ptsCnt, border->pts, border->ptsCnt * sizeof(SwPoint)); + memcpy(outline->pts.data + outline->pts.count, border->pts, border->ptsCnt * sizeof(SwPoint)); auto cnt = border->ptsCnt; auto src = border->tags; - auto tags = outline->types + outline->ptsCnt; - auto cntrs = outline->cntrs + outline->cntrsCnt; - auto idx = outline->ptsCnt; + auto tags = outline->types.data + outline->types.count; + auto idx = outline->pts.count; while (cnt > 0) { - if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT; else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC; - else { - //LOG: What type of stroke outline?? - } - - if (*src & SW_STROKE_TAG_END) { - *cntrs = idx; - ++cntrs; - ++outline->cntrsCnt; - } + else TVGERR("SW_ENGINE", "Invalid stroke tag was given! = %d", *src); + if (*src & SW_STROKE_TAG_END) outline->cntrs.push(idx); ++src; ++tags; ++idx; --cnt; } - outline->ptsCnt = outline->ptsCnt + border->ptsCnt; + outline->pts.count += border->ptsCnt; + outline->types.count += border->ptsCnt; } @@ -844,6 +825,7 @@ void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* tran stroke->width = HALF_STROKE(rshape->strokeWidth()); stroke->cap = rshape->strokeCap(); + stroke->miterlimit = static_cast(rshape->strokeMiterlimit()) << 16; //Save line join: it can be temporarily changed when stroking curves... stroke->joinSaved = stroke->join = rshape->strokeJoin(); @@ -858,10 +840,11 @@ void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* tran bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline) { uint32_t first = 0; + uint32_t i = 0; - for (uint32_t i = 0; i < outline.cntrsCnt; ++i) { - auto last = outline.cntrs[i]; //index of last point in contour - auto limit = outline.pts + last; + for (auto cntr = outline.cntrs.data; cntr < outline.cntrs.end(); ++cntr, ++i) { + auto last = *cntr; //index of last point in contour + auto limit = outline.pts.data + last; //Skip empty points if (last <= first) { @@ -869,15 +852,15 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline) continue; } - auto start = outline.pts[first]; - auto pt = outline.pts + first; - auto types = outline.types + first; + auto start = outline.pts.data[first]; + auto pt = outline.pts.data + first; + auto types = outline.types.data + first; auto type = types[0]; //A contour cannot start with a cubic control point if (type == SW_CURVE_TYPE_CUBIC) return false; - auto closed = outline.closed ? outline.closed[i]: false; + auto closed = outline.closed.data ? outline.closed.data[i]: false; _beginSubPath(*stroke, start, closed); @@ -903,7 +886,6 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline) goto close; } } - close: if (!stroke->firstPt) _endSubPath(*stroke); first = last + 1; @@ -923,15 +905,9 @@ SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid) auto cntrsCnt = count2 + count4; auto outline = mpoolReqStrokeOutline(mpool, tid); - if (outline->reservedPtsCnt < ptsCnt) { - outline->pts = static_cast(realloc(outline->pts, sizeof(SwPoint) * ptsCnt)); - outline->types = static_cast(realloc(outline->types, sizeof(uint8_t) * ptsCnt)); - outline->reservedPtsCnt = ptsCnt; - } - if (outline->reservedCntrsCnt < cntrsCnt) { - outline->cntrs = static_cast(realloc(outline->cntrs, sizeof(uint32_t) * cntrsCnt)); - outline->reservedCntrsCnt = cntrsCnt; - } + outline->pts.reserve(ptsCnt); + outline->types.reserve(ptsCnt); + outline->cntrs.reserve(cntrsCnt); _exportBorderOutline(*stroke, outline, 0); //left _exportBorderOutline(*stroke, outline, 1); //right diff --git a/thirdparty/thorvg/src/lib/tvgAnimation.cpp b/thirdparty/thorvg/src/lib/tvgAnimation.cpp new file mode 100644 index 000000000000..f671daa727cb --- /dev/null +++ b/thirdparty/thorvg/src/lib/tvgAnimation.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2023 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 "tvgAnimationImpl.h" +#include "tvgCommon.h" +#include "tvgFrameModule.h" +#include "tvgPictureImpl.h" + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +struct Animation::Impl +{ + //TODO: Memory Safety + Picture picture; +}; + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +Animation::~Animation() +{ + +} + + +Animation::Animation() : pImpl(new Impl) +{ + pImpl->picture.pImpl->animated = true; +} + + +Result Animation::frame(uint32_t no) noexcept +{ + auto loader = pImpl->picture.pImpl->loader.get(); + + if (!loader) return Result::InsufficientCondition; + if (!loader->animatable()) return Result::NonSupport; + + if (static_cast(loader)->frame(no)) return Result::Success; + return Result::InsufficientCondition; +} + + +Picture* Animation::picture() const noexcept +{ + return &pImpl->picture; +} + + +uint32_t Animation::curFrame() const noexcept +{ + auto loader = pImpl->picture.pImpl->loader.get(); + + if (!loader) return 0; + if (!loader->animatable()) return 0; + + return static_cast(loader)->curFrame(); +} + + +uint32_t Animation::totalFrame() const noexcept +{ + auto loader = pImpl->picture.pImpl->loader.get(); + + if (!loader) return 0; + if (!loader->animatable()) return 0; + + return static_cast(loader)->totalFrame(); +} + + +float Animation::duration() const noexcept +{ + auto loader = pImpl->picture.pImpl->loader.get(); + + if (!loader) return 0; + if (!loader->animatable()) return 0; + + return static_cast(loader)->duration(); +} + + +unique_ptr Animation::gen() noexcept +{ + return unique_ptr(new Animation); +} diff --git a/thirdparty/thorvg/src/lib/tvgArray.h b/thirdparty/thorvg/src/lib/tvgArray.h index 8d67753ae3e3..0e8aef30714e 100644 --- a/thirdparty/thorvg/src/lib/tvgArray.h +++ b/thirdparty/thorvg/src/lib/tvgArray.h @@ -35,30 +35,35 @@ struct Array uint32_t count = 0; uint32_t reserved = 0; + Array(){} + + Array(const Array& rhs) + { + reset(); + *this = rhs; + } + void push(T element) { if (count + 1 > reserved) { - reserved = (count + 1) * 2; - auto p = data; + reserved = count + (count + 2) / 2; data = static_cast(realloc(data, sizeof(T) * reserved)); - if (!data) { - data = p; - return; - } } data[count++] = element; } + void push(Array& rhs) + { + grow(rhs.count); + memcpy(data + count, rhs.data, rhs.count * sizeof(T)); + count += rhs.count; + } + bool reserve(uint32_t size) { if (size > reserved) { reserved = size; - auto p = data; data = static_cast(realloc(data, sizeof(T) * reserved)); - if (!data) { - data = p; - return false; - } } return true; } @@ -68,11 +73,21 @@ struct Array return reserve(count + size); } - T* ptr() + T* end() const { return data + count; } + T& last() + { + return data[count - 1]; + } + + T& first() + { + return data[0]; + } + void pop() { if (count > 0) --count; @@ -80,10 +95,8 @@ struct Array void reset() { - if (data) { - free(data); - data = nullptr; - } + free(data); + data = nullptr; count = reserved = 0; } @@ -92,16 +105,21 @@ struct Array count = 0; } + bool empty() const + { + return count == 0; + } + void operator=(const Array& rhs) { reserve(rhs.count); - if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * reserved); + if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * rhs.count); count = rhs.count; } ~Array() { - if (data) free(data); + free(data); } }; diff --git a/thirdparty/thorvg/src/lib/tvgBezier.cpp b/thirdparty/thorvg/src/lib/tvgBezier.cpp index 616225223589..87511a06c7df 100644 --- a/thirdparty/thorvg/src/lib/tvgBezier.cpp +++ b/thirdparty/thorvg/src/lib/tvgBezier.cpp @@ -20,10 +20,11 @@ * SOFTWARE. */ -#include -#include +#include "tvgMath.h" #include "tvgBezier.h" +#define BEZIER_EPSILON 1e-4f + /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ @@ -107,29 +108,25 @@ void bezSplitLeft(Bezier& cur, float at, Bezier& left) } -float bezAt(const Bezier& bz, float at) +float bezAt(const Bezier& bz, float at, float length) { - auto len = bezLength(bz); 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 >= len) return 1.0f; + if (at >= length) return length; while (true) { auto right = bz; Bezier left; bezSplitLeft(right, t, left); - len = bezLength(left); - - if (fabsf(len - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) { + length = bezLength(left); + if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) { break; } - - if (len < at) { + if (length < at) { smallest = t; t = (t + biggest) * 0.5f; } else { @@ -144,8 +141,53 @@ float bezAt(const Bezier& bz, float at) void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right) { right = cur; - auto t = bezAt(right, at); + 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 atan2(pt.x, pt.y) * 180.0f / 3.141592f; +} + + } diff --git a/thirdparty/thorvg/src/lib/tvgBezier.h b/thirdparty/thorvg/src/lib/tvgBezier.h index aa309b098e05..539a63bdd32d 100644 --- a/thirdparty/thorvg/src/lib/tvgBezier.h +++ b/thirdparty/thorvg/src/lib/tvgBezier.h @@ -28,8 +28,6 @@ namespace tvg { -#define BEZIER_EPSILON 1e-4f - struct Bezier { Point start; @@ -41,8 +39,10 @@ struct Bezier 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 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); } diff --git a/thirdparty/thorvg/src/lib/tvgBinaryDesc.h b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h index 3b44db2829f1..3b9dbe891be5 100644 --- a/thirdparty/thorvg/src/lib/tvgBinaryDesc.h +++ b/thirdparty/thorvg/src/lib/tvgBinaryDesc.h @@ -26,9 +26,6 @@ /* TODO: Need to consider whether uin8_t is enough size for extension... Rather than optimal data, we can use enough size and data compress? */ -/* Data types, do not change data types once Tvg Format is officially released, - That would occur the abi break. */ - using TvgBinByte = uint8_t; using TvgBinCounter = uint32_t; using TvgBinTag = TvgBinByte; @@ -39,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 "000400" //Major 00, Minor 04, Micro 00 +#define TVG_HEADER_VERSION "001000" //Major 00, Minor 10, 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 @@ -63,8 +60,9 @@ using TvgBinFlag = TvgBinByte; #define TVG_TAG_PAINT_CMP_METHOD (TvgBinTag)0x20 +//TODO: Keep this for the compatibility, Remove in TVG 1.0 release //Scene -#define TVG_TAG_SCENE_RESERVEDCNT (TvgBinTag)0x30 + #define TVG_TAG_SCENE_RESERVEDCNT (TvgBinTag)0x30 //Shape @@ -73,14 +71,17 @@ using TvgBinFlag = TvgBinByte; #define TVG_TAG_SHAPE_FILL (TvgBinTag)0x42 #define TVG_TAG_SHAPE_COLOR (TvgBinTag)0x43 #define TVG_TAG_SHAPE_FILLRULE (TvgBinTag)0x44 -#define TVG_TAG_SHAPE_STROKE_CAP (TvgBinTag)0x50 -#define TVG_TAG_SHAPE_STROKE_JOIN (TvgBinTag)0x51 + //Stroke +#define TVG_TAG_SHAPE_STROKE_CAP (TvgBinTag)0x50 +#define TVG_TAG_SHAPE_STROKE_JOIN (TvgBinTag)0x51 #define TVG_TAG_SHAPE_STROKE_WIDTH (TvgBinTag)0x52 #define TVG_TAG_SHAPE_STROKE_COLOR (TvgBinTag)0x53 #define TVG_TAG_SHAPE_STROKE_FILL (TvgBinTag)0x54 #define TVG_TAG_SHAPE_STROKE_DASHPTRN (TvgBinTag)0x55 +#define TVG_TAG_SHAPE_STROKE_MITERLIMIT (TvgBinTag)0x56 +#define TVG_TAG_SHAPE_STROKE_ORDER (TvgBinTag)0x57 //Fill diff --git a/thirdparty/thorvg/src/lib/tvgCanvas.cpp b/thirdparty/thorvg/src/lib/tvgCanvas.cpp index 95c66b94df97..1dee37032688 100644 --- a/thirdparty/thorvg/src/lib/tvgCanvas.cpp +++ b/thirdparty/thorvg/src/lib/tvgCanvas.cpp @@ -37,16 +37,21 @@ Canvas::~Canvas() } -Result Canvas::reserve(uint32_t n) noexcept +Result Canvas::reserve(TVG_UNUSED uint32_t n) noexcept { - if (!pImpl->paints.reserve(n)) return Result::FailedAllocation; - return Result::Success; + return Result::NonSupport; +} + + +list& Canvas::paints() noexcept +{ + return pImpl->paints; } Result Canvas::push(unique_ptr paint) noexcept { - return pImpl->push(move(paint)); + return pImpl->push(std::move(paint)); } diff --git a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h index 6f5760f53caa..9a3018e7a61e 100644 --- a/thirdparty/thorvg/src/lib/tvgCanvasImpl.h +++ b/thirdparty/thorvg/src/lib/tvgCanvasImpl.h @@ -31,7 +31,7 @@ struct Canvas::Impl { - Array paints; + list paints; RenderMethod* renderer; bool refresh = false; //if all paints should be updated by force. bool drawing = false; //on drawing condition? @@ -53,7 +53,7 @@ struct Canvas::Impl auto p = paint.release(); if (!p) return Result::MemoryCorruption; - paints.push(p); + paints.push_back(p); return update(p, true); } @@ -64,9 +64,9 @@ struct Canvas::Impl if (!renderer || !renderer->clear()) return Result::InsufficientCondition; //Free paints - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - (*paint)->pImpl->dispose(*renderer); - if (free) delete(*paint); + for (auto paint : paints) { + paint->pImpl->dispose(*renderer); + if (free) delete(paint); } paints.clear(); @@ -83,7 +83,7 @@ struct Canvas::Impl Result update(Paint* paint, bool force) { - if (paints.count == 0 || drawing || !renderer) return Result::InsufficientCondition; + if (paints.empty() || drawing || !renderer) return Result::InsufficientCondition; Array clips; auto flag = RenderUpdateFlag::None; @@ -92,17 +92,17 @@ struct Canvas::Impl //Update single paint node if (paint) { //Optimize Me: Can we skip the searching? - for (auto paint2 = paints.data; paint2 < (paints.data + paints.count); ++paint2) { - if ((*paint2) == paint) { - paint->pImpl->update(*renderer, nullptr, 255, clips, flag); + for (auto paint2 : paints) { + if (paint2 == paint) { + paint->pImpl->update(*renderer, nullptr, clips, 255, flag); return Result::Success; } } return Result::InvalidArguments; //Update all retained paint nodes } else { - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - (*paint)->pImpl->update(*renderer, nullptr, 255, clips, flag); + for (auto paint : paints) { + paint->pImpl->update(*renderer, nullptr, clips, 255, flag); } } @@ -113,11 +113,11 @@ struct Canvas::Impl Result draw() { - if (drawing || paints.count == 0 || !renderer || !renderer->preRender()) return Result::InsufficientCondition; + if (drawing || paints.empty() || !renderer || !renderer->preRender()) return Result::InsufficientCondition; bool rendered = false; - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - if ((*paint)->pImpl->render(*renderer)) rendered = true; + for (auto paint : paints) { + if (paint->pImpl->render(*renderer)) rendered = true; } if (!rendered || !renderer->postRender()) return Result::InsufficientCondition; diff --git a/thirdparty/thorvg/src/lib/tvgCommon.h b/thirdparty/thorvg/src/lib/tvgCommon.h index 1731647c00c9..23011ffed577 100644 --- a/thirdparty/thorvg/src/lib/tvgCommon.h +++ b/thirdparty/thorvg/src/lib/tvgCommon.h @@ -62,7 +62,9 @@ using namespace tvg; #define TVG_CLASS_ID_LINEAR 4 #define TVG_CLASS_ID_RADIAL 5 -enum class FileType { Tvg = 0, Svg, Raw, Png, Jpg, Unknown }; +enum class FileType { Tvg = 0, Svg, Lottie, Raw, Png, Jpg, Webp, Unknown }; + +using Size = Point; #ifdef THORVG_LOG_ENABLED constexpr auto ErrorColor = "\033[31m"; //red diff --git a/thirdparty/thorvg/src/lib/tvgFill.h b/thirdparty/thorvg/src/lib/tvgFill.h index e90991c9df25..20603b733364 100644 --- a/thirdparty/thorvg/src/lib/tvgFill.h +++ b/thirdparty/thorvg/src/lib/tvgFill.h @@ -55,11 +55,11 @@ struct Fill::Impl uint32_t cnt = 0; FillSpread spread; DuplicateMethod* dup = nullptr; - uint32_t id; + uint8_t id; ~Impl() { - if (dup) delete(dup); + delete(dup); free(colorStops); free(transform); } diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h b/thirdparty/thorvg/src/lib/tvgFrameModule.h similarity index 63% rename from thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h rename to thirdparty/thorvg/src/lib/tvgFrameModule.h index 5354e1bdd609..857c6caeb950 100644 --- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h +++ b/thirdparty/thorvg/src/lib/tvgFrameModule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2023 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 @@ -20,28 +20,28 @@ * SOFTWARE. */ -#ifndef _TVG_PNG_LOADER_H_ -#define _TVG_PNG_LOADER_H_ +#ifndef _TVG_FRAME_MODULE_H_ +#define _TVG_FRAME_MODULE_H_ -#include +#include "tvgLoadModule.h" -class PngLoader : public LoadModule +namespace tvg +{ + +class FrameModule: public LoadModule { public: - PngLoader(); - ~PngLoader(); + virtual ~FrameModule() {} - using LoadModule::open; - bool open(const string& path) override; - bool open(const char* data, uint32_t size, bool copy) override; - bool read() override; - bool close() override; + virtual bool frame(uint32_t frameNo) = 0; //set the current frame number - unique_ptr bitmap() override; + virtual uint32_t totalFrame() = 0; //return the total frame count + virtual uint32_t curFrame() = 0; //return the current frame number + virtual float duration() = 0; //return the animation duration in seconds -private: - png_imagep image = nullptr; - uint32_t* content = nullptr; + virtual bool animatable() override { return true; } }; -#endif //_TVG_PNG_LOADER_H_ +} + +#endif //_TVG_FRAME_MODULE_H_ diff --git a/thirdparty/thorvg/src/lib/tvgLoadModule.h b/thirdparty/thorvg/src/lib/tvgLoadModule.h index ee7c0c119304..29ceba3fcc59 100644 --- a/thirdparty/thorvg/src/lib/tvgLoadModule.h +++ b/thirdparty/thorvg/src/lib/tvgLoadModule.h @@ -31,11 +31,6 @@ namespace tvg class LoadModule { public: - //default view box, if any. - float vx = 0; - float vy = 0; - float vw = 0; - float vh = 0; float w = 0, h = 0; //default image size ColorSpace cs = ColorSpace::Unsupported; //must be clarified at open() @@ -48,8 +43,12 @@ class LoadModule //Override this if the vector-format has own resizing policy. virtual bool resize(Paint* paint, float w, float h) { return false; } + virtual bool animatable() { return false; } //true if this loader supports animation. + virtual void sync() {}; //finish immediately if any async update jobs. + virtual bool read() = 0; virtual bool close() = 0; + virtual unique_ptr bitmap() { return nullptr; } virtual unique_ptr paint() { return nullptr; } }; diff --git a/thirdparty/thorvg/src/lib/tvgLoader.cpp b/thirdparty/thorvg/src/lib/tvgLoader.cpp index 2bbe177844aa..16b604c89eba 100644 --- a/thirdparty/thorvg/src/lib/tvgLoader.cpp +++ b/thirdparty/thorvg/src/lib/tvgLoader.cpp @@ -38,6 +38,14 @@ #include "tvgJpgLoader.h" #endif +#ifdef THORVG_WEBP_LOADER_SUPPORT + #include "tvgWebpLoader.h" +#endif + +#ifdef THORVG_LOTTIE_LOADER_SUPPORT + #include "tvgLottieLoader.h" +#endif + #include "tvgRawLoader.h" /************************************************************************/ @@ -56,6 +64,12 @@ static LoadModule* _find(FileType type) case FileType::Svg: { #ifdef THORVG_SVG_LOADER_SUPPORT return new SvgLoader; +#endif + break; + } + case FileType::Lottie: { +#ifdef THORVG_LOTTIE_LOADER_SUPPORT + return new LottieLoader; #endif break; } @@ -72,6 +86,12 @@ static LoadModule* _find(FileType type) case FileType::Jpg: { #ifdef THORVG_JPG_LOADER_SUPPORT return new JpgLoader; +#endif + break; + } + case FileType::Webp: { +#ifdef THORVG_WEBP_LOADER_SUPPORT + return new WebpLoader; #endif break; } @@ -91,6 +111,10 @@ static LoadModule* _find(FileType type) format = "SVG"; break; } + case FileType::Lottie: { + format = "lottie(json)"; + break; + } case FileType::Raw: { format = "RAW"; break; @@ -103,6 +127,10 @@ static LoadModule* _find(FileType type) format = "JPG"; break; } + case FileType::Webp: { + format = "WEBP"; + break; + } default: { format = "???"; break; @@ -119,8 +147,11 @@ static LoadModule* _findByPath(const string& path) auto ext = path.substr(path.find_last_of(".") + 1); if (!ext.compare("tvg")) return _find(FileType::Tvg); if (!ext.compare("svg")) return _find(FileType::Svg); + if (!ext.compare("json")) return _find(FileType::Lottie); + if (!ext.compare("lottie")) return _find(FileType::Lottie); if (!ext.compare("png")) return _find(FileType::Png); if (!ext.compare("jpg")) return _find(FileType::Jpg); + if (!ext.compare("webp")) return _find(FileType::Webp); return nullptr; } @@ -133,9 +164,11 @@ static LoadModule* _findByType(const string& mimeType) if (mimeType == "tvg") type = FileType::Tvg; else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg; + else if (mimeType == "lottie" || mimeType == "json") type = FileType::Lottie; else if (mimeType == "raw") type = FileType::Raw; else if (mimeType == "png") type = FileType::Png; else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg; + else if (mimeType == "webp") type = FileType::Webp; else { TVGLOG("LOADER", "Given mimetype is unknown = \"%s\".", mimeType.c_str()); return nullptr; diff --git a/thirdparty/thorvg/src/lib/tvgMath.h b/thirdparty/thorvg/src/lib/tvgMath.h index 9ab8291b7551..afe1849825d9 100644 --- a/thirdparty/thorvg/src/lib/tvgMath.h +++ b/thirdparty/thorvg/src/lib/tvgMath.h @@ -45,6 +45,15 @@ static inline bool mathEqual(float a, float b) return (fabsf(a - b) < FLT_EPSILON); } +static inline bool mathEqual(const Matrix& a, const Matrix& b) +{ + if (!mathEqual(a.e11, b.e11) || !mathEqual(a.e12, b.e12) || !mathEqual(a.e13, b.e13) || + !mathEqual(a.e21, b.e21) || !mathEqual(a.e22, b.e22) || !mathEqual(a.e23, b.e23) || + !mathEqual(a.e31, b.e31) || !mathEqual(a.e32, b.e32) || !mathEqual(a.e33, b.e33)) { + return false; + } + return true; +} static inline bool mathRightAngle(const Matrix* m) { @@ -109,17 +118,17 @@ static inline void mathIdentity(Matrix* m) } -static inline void mathScale(Matrix* m, float scale) +static inline void mathScale(Matrix* m, float sx, float sy) { - m->e11 = scale; - m->e22 = scale; + m->e11 *= sx; + m->e22 *= sy; } static inline void mathTranslate(Matrix* m, float x, float y) { - m->e13 = x; - m->e23 = y; + m->e13 += x; + m->e23 += y; } @@ -165,4 +174,29 @@ static inline Matrix mathMultiply(const Matrix* lhs, const Matrix* rhs) } +static inline Point operator-(const Point& lhs, const Point& rhs) +{ + return {lhs.x - rhs.x, lhs.y - rhs.y}; +} + + +static inline Point operator+(const Point& lhs, const Point& rhs) +{ + return {lhs.x + rhs.x, lhs.y + rhs.y}; +} + + +static inline Point operator*(const Point& lhs, float rhs) +{ + return {lhs.x * rhs, lhs.y * rhs}; +} + + +template +static inline T mathLerp(const T &start, const T &end, float t) +{ + return static_cast(start + (end - start) * t); +} + + #endif //_TVG_MATH_H_ diff --git a/thirdparty/thorvg/src/lib/tvgPaint.cpp b/thirdparty/thorvg/src/lib/tvgPaint.cpp index c7030aaccfe4..57da269cd791 100644 --- a/thirdparty/thorvg/src/lib/tvgPaint.cpp +++ b/thirdparty/thorvg/src/lib/tvgPaint.cpp @@ -166,6 +166,7 @@ bool Paint::Impl::render(RenderMethod& renderer) Create a composition image. */ if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) { auto region = smethod->bounds(renderer); + if (MASK_OPERATION(compData->method)) region.add(compData->target->pImpl->smethod->bounds(renderer)); if (region.w == 0 || region.h == 0) return true; cmp = renderer.target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method)); if (renderer.beginComposite(cmp, CompositeMethod::None, 255)) { @@ -175,6 +176,7 @@ bool Paint::Impl::render(RenderMethod& renderer) if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity); + renderer.blend(blendMethod); auto ret = smethod->render(renderer); if (cmp) renderer.endComposite(cmp); @@ -183,7 +185,7 @@ bool Paint::Impl::render(RenderMethod& renderer) } -RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, uint32_t pFlag, bool clipper) +RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pTransform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) { if (renderFlag & RenderUpdateFlag::Transform) { if (!rTransform) return nullptr; @@ -209,11 +211,18 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT auto tryFastTrack = false; if (target->identifier() == TVG_CLASS_ID_SHAPE) { if (method == CompositeMethod::ClipPath) tryFastTrack = true; + //OPTIMIZE HERE: Actually, this condition AlphaMask is useless. We can skip it? else if (method == CompositeMethod::AlphaMask) { auto shape = static_cast(target); uint8_t a; shape->fillColor(nullptr, nullptr, nullptr, &a); if (a == 255 && shape->opacity() == 255 && !shape->fill()) tryFastTrack = true; + //OPTIMIZE HERE: Actually, this condition InvAlphaMask is useless. We can skip it? + } else if (method == CompositeMethod::InvAlphaMask) { + auto shape = static_cast(target); + uint8_t a; + shape->fillColor(nullptr, nullptr, nullptr, &a); + if ((a == 0 || shape->opacity() == 0) && !shape->fill()) tryFastTrack = true; } if (tryFastTrack) { RenderRegion viewport2; @@ -227,7 +236,7 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT } if (!compFastTrack) { childClipper = compData->method == CompositeMethod::ClipPath ? true : false; - trd = target->pImpl->update(renderer, pTransform, 255, clips, pFlag, childClipper); + trd = target->pImpl->update(renderer, pTransform, clips, 255, pFlag, childClipper); if (childClipper) clips.push(trd); } } @@ -236,14 +245,14 @@ RenderData Paint::Impl::update(RenderMethod& renderer, const RenderTransform* pT RenderData rd = nullptr; auto newFlag = static_cast(pFlag | renderFlag); renderFlag = RenderUpdateFlag::None; - opacity = (opacity * this->opacity) / 255; + opacity = MULTIPLY(opacity, this->opacity); if (rTransform && pTransform) { RenderTransform outTransform(pTransform, rTransform); - rd = smethod->update(renderer, &outTransform, opacity, clips, newFlag, clipper); + rd = smethod->update(renderer, &outTransform, clips, opacity, newFlag, clipper); } else { auto outTransform = pTransform ? pTransform : rTransform; - rd = smethod->update(renderer, outTransform, opacity, clips, newFlag, clipper); + rd = smethod->update(renderer, outTransform, clips, opacity, newFlag, clipper); } /* 3. Composition Post Processing */ @@ -371,7 +380,7 @@ Result Paint::composite(std::unique_ptr target, CompositeMethod method) n { auto p = target.release(); if (pImpl->composite(this, p, method)) return Result::Success; - if (p) delete(p); + delete(p); return Result::InvalidArguments; } @@ -409,3 +418,17 @@ uint32_t Paint::identifier() const noexcept { return pImpl->id; } + + +Result Paint::blend(BlendMethod method) const noexcept +{ + pImpl->blendMethod = method; + + return Result::Success; +} + + +BlendMethod Paint::blend() const noexcept +{ + return pImpl->blendMethod; +} diff --git a/thirdparty/thorvg/src/lib/tvgPaint.h b/thirdparty/thorvg/src/lib/tvgPaint.h index d70490c64940..c00238a070eb 100644 --- a/thirdparty/thorvg/src/lib/tvgPaint.h +++ b/thirdparty/thorvg/src/lib/tvgPaint.h @@ -28,7 +28,7 @@ namespace tvg { - enum ContextFlag {Invalid = 0, FastTrack = 1}; + enum ContextFlag : uint8_t {Invalid = 0, FastTrack = 1}; struct Iterator { @@ -43,7 +43,7 @@ namespace tvg virtual ~StrategyMethod() {} virtual bool dispose(RenderMethod& renderer) = 0; - virtual void* update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) = 0; //Return engine data if it has. + virtual void* update(RenderMethod& renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) = 0; //Return engine data if it has. virtual bool render(RenderMethod& renderer) = 0; virtual bool bounds(float* x, float* y, float* w, float* h) = 0; virtual RenderRegion bounds(RenderMethod& renderer) const = 0; @@ -63,9 +63,10 @@ namespace tvg StrategyMethod* smethod = nullptr; RenderTransform* rTransform = nullptr; Composite* compData = nullptr; - uint32_t renderFlag = RenderUpdateFlag::None; - uint32_t ctxFlag = ContextFlag::Invalid; - uint32_t id; + BlendMethod blendMethod = BlendMethod::Normal; //uint8_t + uint8_t renderFlag = RenderUpdateFlag::None; + uint8_t ctxFlag = ContextFlag::Invalid; + uint8_t id; uint8_t opacity = 255; ~Impl() @@ -74,8 +75,8 @@ namespace tvg delete(compData->target); free(compData); } - if (smethod) delete(smethod); - if (rTransform) delete(rTransform); + delete(smethod); + delete(rTransform); } void method(StrategyMethod* method) @@ -147,7 +148,7 @@ namespace tvg bool scale(float factor); bool translate(float x, float y); bool bounds(float* x, float* y, float* w, float* h, bool transformed); - RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, uint32_t pFlag, bool clipper = false); + RenderData update(RenderMethod& renderer, const RenderTransform* pTransform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false); bool render(RenderMethod& renderer); Paint* duplicate(); }; @@ -176,9 +177,9 @@ namespace tvg return inst->dispose(renderer); } - RenderData update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag renderFlag, bool clipper) override + RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag renderFlag, bool clipper) override { - return inst->update(renderer, transform, opacity, clips, renderFlag, clipper); + return inst->update(renderer, transform, clips, opacity, renderFlag, clipper); } bool render(RenderMethod& renderer) override diff --git a/thirdparty/thorvg/src/lib/tvgPicture.cpp b/thirdparty/thorvg/src/lib/tvgPicture.cpp index ad9db962452d..fd324a947ca3 100644 --- a/thirdparty/thorvg/src/lib/tvgPicture.cpp +++ b/thirdparty/thorvg/src/lib/tvgPicture.cpp @@ -26,7 +26,7 @@ /* External Class Implementation */ /************************************************************************/ -Picture::Picture() : pImpl(new Impl) +Picture::Picture() : pImpl(new Impl(this)) { Paint::pImpl->id = TVG_CLASS_ID_PICTURE; Paint::pImpl->method(new PaintMethod(pImpl)); @@ -81,13 +81,6 @@ Result Picture::load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept } -Result Picture::viewbox(float* x, float* y, float* w, float* h) const noexcept -{ - if (pImpl->viewbox(x, y, w, h)) return Result::Success; - return Result::InsufficientCondition; -} - - Result Picture::size(float w, float h) noexcept { if (pImpl->size(w, h)) return Result::Success; diff --git a/thirdparty/thorvg/src/lib/tvgPictureImpl.h b/thirdparty/thorvg/src/lib/tvgPictureImpl.h index 6af7a732e55c..d445c72c10e3 100644 --- a/thirdparty/thorvg/src/lib/tvgPictureImpl.h +++ b/thirdparty/thorvg/src/lib/tvgPictureImpl.h @@ -67,11 +67,18 @@ struct Picture::Impl RenderData rd = nullptr; //engine data float w = 0, h = 0; RenderMesh rm; //mesh data + Picture* picture = nullptr; bool resizing = false; + bool needComp = false; //need composition + bool animated = false; //picture is belonged to Animation + + Impl(Picture* p) : picture(p) + { + } ~Impl() { - if (paint) delete(paint); + delete(paint); delete(surface); } @@ -85,7 +92,7 @@ struct Picture::Impl return ret; } - uint32_t load() + RenderUpdateFlag load() { if (loader) { if (!paint) { @@ -102,7 +109,8 @@ struct Picture::Impl } if (paint) return RenderUpdateFlag::None; } - } + } else loader->sync(); + if (!surface) { if ((surface = loader->bitmap().release())) { loader->close(); @@ -127,38 +135,52 @@ struct Picture::Impl else return RenderTransform(pTransform, &tmp); } - RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) + bool needComposition(uint8_t opacity) + { + //In this case, paint(scene) would try composition itself. + if (opacity < 255) return false; + + //Composition test + const Paint* target; + auto method = picture->composite(&target); + if (!target || method == tvg::CompositeMethod::ClipPath) return false; + if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false; + + return true; + } + + RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) { auto flag = load(); if (surface) { auto transform = resizeTransform(pTransform); - rd = renderer.prepare(surface, &rm, rd, &transform, opacity, clips, static_cast(pFlag | flag)); + rd = renderer.prepare(surface, &rm, rd, &transform, clips, opacity, static_cast(pFlag | flag)); } else if (paint) { if (resizing) { loader->resize(paint, w, h); resizing = false; } - rd = paint->pImpl->update(renderer, pTransform, opacity, clips, static_cast(pFlag | flag), clipper); + needComp = needComposition(opacity) ? true : false; + rd = paint->pImpl->update(renderer, pTransform, clips, opacity, static_cast(pFlag | flag), clipper); } return rd; } bool render(RenderMethod &renderer) { + bool ret = false; if (surface) return renderer.renderImage(rd); - else if (paint) return paint->pImpl->render(renderer); - return false; - } - - bool viewbox(float* x, float* y, float* w, float* h) - { - if (!loader) return false; - if (x) *x = loader->vx; - if (y) *y = loader->vy; - if (w) *w = loader->vw; - if (h) *h = loader->vh; - return true; + else if (paint) { + Compositor* cmp = nullptr; + if (needComp) { + cmp = renderer.target(bounds(renderer), renderer.colorSpace()); + renderer.beginComposite(cmp, CompositeMethod::None, 255); + } + ret = paint->pImpl->render(renderer); + if (cmp) renderer.endComposite(cmp); + } + return ret; } bool size(float w, float h) diff --git a/thirdparty/thorvg/src/lib/tvgRender.cpp b/thirdparty/thorvg/src/lib/tvgRender.cpp index fb9270afe6cd..ec4db13f6ea3 100644 --- a/thirdparty/thorvg/src/lib/tvgRender.cpp +++ b/thirdparty/thorvg/src/lib/tvgRender.cpp @@ -53,7 +53,7 @@ bool RenderTransform::update() mathIdentity(&m); - mathScale(&m, scale); + mathScale(&m, scale, scale); if (!mathZero(degree)) mathRotate(&m, degree); diff --git a/thirdparty/thorvg/src/lib/tvgRender.h b/thirdparty/thorvg/src/lib/tvgRender.h index 6270fa53168a..9d7bafb613b2 100644 --- a/thirdparty/thorvg/src/lib/tvgRender.h +++ b/thirdparty/thorvg/src/lib/tvgRender.h @@ -32,7 +32,7 @@ namespace tvg using RenderData = void*; using pixel_t = uint32_t; -enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255}; +enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 255}; struct Surface; @@ -65,7 +65,7 @@ struct Surface struct Compositor { CompositeMethod method; - uint32_t opacity; + uint8_t opacity; }; struct RenderMesh @@ -98,6 +98,20 @@ struct RenderRegion if (w < 0) w = 0; if (h < 0) h = 0; } + + void add(const RenderRegion& rhs) + { + if (rhs.x < x) { + w += (x - rhs.x); + x = rhs.x; + } + if (rhs.y < y) { + h += (y - rhs.y); + y = rhs.y; + } + if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x; + if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y; + } }; struct RenderTransform @@ -125,12 +139,13 @@ struct RenderStroke uint32_t dashCnt = 0; StrokeCap cap = StrokeCap::Square; StrokeJoin join = StrokeJoin::Bevel; + float miterlimit = 4.0f; bool strokeFirst = false; ~RenderStroke() { free(dashPattern); - if (fill) delete(fill); + delete(fill); } }; @@ -138,13 +153,8 @@ struct RenderShape { struct { - PathCommand* cmds = nullptr; - uint32_t cmdCnt = 0; - uint32_t reservedCmdCnt = 0; - - Point *pts = nullptr; - uint32_t ptsCnt = 0; - uint32_t reservedPtsCnt = 0; + Array cmds; + Array pts; } path; Fill *fill = nullptr; @@ -154,11 +164,8 @@ struct RenderShape ~RenderShape() { - free(path.cmds); - free(path.pts); - - if (fill) delete(fill); - if (stroke) delete(stroke); + delete(fill); + delete(stroke); } void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const @@ -211,15 +218,22 @@ struct RenderShape if (!stroke) return StrokeJoin::Bevel; return stroke->join; } + + float strokeMiterlimit() const + { + if (!stroke) return 4.0f; + + return stroke->miterlimit;; + } }; class RenderMethod { public: virtual ~RenderMethod() {} - virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags, bool clipper) = 0; - virtual RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; - virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flags) = 0; + virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0; + virtual RenderData prepare(const Array& scene, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; + virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flags) = 0; virtual bool preRender() = 0; virtual bool renderShape(RenderData data) = 0; virtual bool renderImage(RenderData data) = 0; @@ -228,16 +242,36 @@ class RenderMethod virtual RenderRegion region(RenderData data) = 0; virtual RenderRegion viewport() = 0; virtual bool viewport(const RenderRegion& vp) = 0; + virtual bool blend(BlendMethod method) = 0; virtual ColorSpace colorSpace() = 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, uint32_t opacity) = 0; + virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) = 0; virtual bool endComposite(Compositor* cmp) = 0; }; +static inline bool MASK_OPERATION(CompositeMethod method) +{ + switch(method) { + case CompositeMethod::AlphaMask: + case CompositeMethod::InvAlphaMask: + case CompositeMethod::LumaMask: + case CompositeMethod::InvLumaMask: + return false; + case CompositeMethod::AddMask: + case CompositeMethod::SubtractMask: + case CompositeMethod::IntersectMask: + case CompositeMethod::DifferenceMask: + return true; + default: + TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method); + return false; + } +} + static inline uint8_t CHANNEL_SIZE(ColorSpace cs) { switch(cs) { @@ -262,6 +296,11 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi case CompositeMethod::InvAlphaMask: return ColorSpace::Grayscale8; case CompositeMethod::LumaMask: + case CompositeMethod::InvLumaMask: + case CompositeMethod::AddMask: + case CompositeMethod::SubtractMask: + case CompositeMethod::IntersectMask: + case CompositeMethod::DifferenceMask: return renderer.colorSpace(); default: TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method); @@ -269,6 +308,12 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi } } +static inline uint8_t MULTIPLY(uint8_t c, uint8_t a) +{ + return (((c) * (a) + 0xff) >> 8); +} + + } #endif //_TVG_RENDER_H_ diff --git a/thirdparty/thorvg/src/lib/tvgSaver.cpp b/thirdparty/thorvg/src/lib/tvgSaver.cpp index 85b5a37855a2..3b4dfddcc234 100644 --- a/thirdparty/thorvg/src/lib/tvgSaver.cpp +++ b/thirdparty/thorvg/src/lib/tvgSaver.cpp @@ -36,7 +36,7 @@ struct Saver::Impl SaveModule* saveModule = nullptr; ~Impl() { - if (saveModule) delete(saveModule); + delete(saveModule); } }; diff --git a/thirdparty/thorvg/src/lib/tvgScene.cpp b/thirdparty/thorvg/src/lib/tvgScene.cpp index feb45a9d221e..d2b7503e28d3 100644 --- a/thirdparty/thorvg/src/lib/tvgScene.cpp +++ b/thirdparty/thorvg/src/lib/tvgScene.cpp @@ -55,17 +55,15 @@ Result Scene::push(unique_ptr paint) noexcept { auto p = paint.release(); if (!p) return Result::MemoryCorruption; - pImpl->paints.push(p); + pImpl->paints.push_back(p); return Result::Success; } -Result Scene::reserve(uint32_t size) noexcept +Result Scene::reserve(TVG_UNUSED uint32_t size) noexcept { - if (!pImpl->paints.reserve(size)) return Result::FailedAllocation; - - return Result::Success; + return Result::NonSupport; } @@ -75,3 +73,9 @@ Result Scene::clear(bool free) noexcept return Result::Success; } + + +list& Scene::paints() noexcept +{ + return pImpl->paints; +} diff --git a/thirdparty/thorvg/src/lib/tvgSceneImpl.h b/thirdparty/thorvg/src/lib/tvgSceneImpl.h index 9ce4d0b37da3..8fa38bcc8574 100644 --- a/thirdparty/thorvg/src/lib/tvgSceneImpl.h +++ b/thirdparty/thorvg/src/lib/tvgSceneImpl.h @@ -32,37 +32,41 @@ struct SceneIterator : Iterator { - Array* paints; - uint32_t idx = 0; + list* paints; + list::iterator itr; - SceneIterator(Array* p) : paints(p) + SceneIterator(list* p) : paints(p) { + begin(); } const Paint* next() override { - if (idx >= paints->count) return nullptr; - return paints->data[idx++]; + if (itr == paints->end()) return nullptr; + auto paint = *itr; + ++itr; + return paint; } uint32_t count() override { - return paints->count; + return paints->size(); } void begin() override { - idx = 0; + itr = paints->begin(); } }; struct Scene::Impl { - Array paints; - uint8_t opacity; //for composition + list paints; RenderMethod* renderer = nullptr; //keep it for explicit clear RenderData rd = nullptr; Scene* scene = nullptr; + uint8_t opacity; //for composition + bool needComp; //composite or not Impl(Scene* s) : scene(s) { @@ -70,15 +74,15 @@ struct Scene::Impl ~Impl() { - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - delete(*paint); + for (auto paint : paints) { + delete(paint); } } bool dispose(RenderMethod& renderer) { - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - (*paint)->pImpl->dispose(renderer); + for (auto paint : paints) { + paint->pImpl->dispose(renderer); } auto ret = renderer.dispose(rd); @@ -88,43 +92,50 @@ struct Scene::Impl return ret; } - bool needComposition(uint32_t opacity) + bool needComposition(uint8_t opacity) { - if (opacity == 0 || paints.count == 0) return false; + if (opacity == 0 || paints.empty()) return false; //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; + //Half translucent requires intermediate composition. if (opacity == 255) return false; //If scene has several children or only scene, it may require composition. - if (paints.count > 1) return true; - if (paints.count == 1 && (*paints.data)->identifier() == TVG_CLASS_ID_SCENE) return true; - return false; + //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; + + return true; } - RenderData update(RenderMethod &renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag flag, bool clipper) + RenderData update(RenderMethod &renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper) { - /* Overriding opacity value. If this scene is half-translucent, - It must do intermeidate composition with that opacity value. */ - this->opacity = static_cast(opacity); - if (needComposition(opacity)) opacity = 255; + if ((needComp = needComposition(opacity))) { + /* Overriding opacity value. If this scene is half-translucent, + It must do intermeidate composition with that opacity value. */ + this->opacity = opacity; + opacity = 255; + } this->renderer = &renderer; if (clipper) { Array rds; - rds.reserve(paints.count); - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - rds.push((*paint)->pImpl->update(renderer, transform, opacity, clips, flag, true)); + rds.reserve(paints.size()); + for (auto paint : paints) { + rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true)); } - rd = renderer.prepare(rds, rd, transform, opacity, clips, flag); + rd = renderer.prepare(rds, rd, transform, clips, opacity, flag); return rd; } else { - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - (*paint)->pImpl->update(renderer, transform, opacity, clips, flag, false); + for (auto paint : paints) { + paint->pImpl->update(renderer, transform, clips, opacity, flag, false); } return nullptr; } @@ -134,13 +145,13 @@ struct Scene::Impl { Compositor* cmp = nullptr; - if (needComposition(opacity)) { + if (needComp) { cmp = renderer.target(bounds(renderer), renderer.colorSpace()); renderer.beginComposite(cmp, CompositeMethod::None, opacity); } - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - if (!(*paint)->pImpl->render(renderer)) return false; + for (auto paint : paints) { + if (!paint->pImpl->render(renderer)) return false; } if (cmp) renderer.endComposite(cmp); @@ -150,15 +161,15 @@ struct Scene::Impl RenderRegion bounds(RenderMethod& renderer) const { - if (paints.count == 0) return {0, 0, 0, 0}; + if (paints.empty()) return {0, 0, 0, 0}; int32_t x1 = INT32_MAX; int32_t y1 = INT32_MAX; int32_t x2 = 0; int32_t y2 = 0; - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - auto region = (*paint)->pImpl->bounds(renderer); + for (auto paint : paints) { + auto region = paint->pImpl->bounds(renderer); //Merge regions if (region.x < x1) x1 = region.x; @@ -172,20 +183,20 @@ struct Scene::Impl bool bounds(float* px, float* py, float* pw, float* ph) { - if (paints.count == 0) return false; + if (paints.empty()) return false; auto x1 = FLT_MAX; auto y1 = FLT_MAX; auto x2 = -FLT_MAX; auto y2 = -FLT_MAX; - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { + for (auto paint : paints) { auto x = FLT_MAX; auto y = FLT_MAX; auto w = 0.0f; auto h = 0.0f; - if ((*paint)->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue; + if (paint->bounds(&x, &y, &w, &h, true) != tvg::Result::Success) continue; //Merge regions if (x < x1) x1 = x; @@ -208,10 +219,8 @@ struct Scene::Impl auto dup = ret.get()->pImpl; - dup->paints.reserve(paints.count); - - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - dup->paints.push((*paint)->duplicate()); + for (auto paint : paints) { + dup->paints.push_back(paint->duplicate()); } return ret.release(); @@ -221,9 +230,9 @@ struct Scene::Impl { auto dispose = renderer ? true : false; - for (auto paint = paints.data; paint < (paints.data + paints.count); ++paint) { - if (dispose) (*paint)->pImpl->dispose(*renderer); - if (free) delete(*paint); + for (auto paint : paints) { + if (dispose) paint->pImpl->dispose(*renderer); + if (free) delete(paint); } paints.clear(); renderer = nullptr; diff --git a/thirdparty/thorvg/src/lib/tvgShape.cpp b/thirdparty/thorvg/src/lib/tvgShape.cpp index a8354375c9a3..f32feb213374 100644 --- a/thirdparty/thorvg/src/lib/tvgShape.cpp +++ b/thirdparty/thorvg/src/lib/tvgShape.cpp @@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f; /* External Class Implementation */ /************************************************************************/ -Shape :: Shape() : pImpl(new Impl()) +Shape :: Shape() : pImpl(new Impl(this)) { Paint::pImpl->id = TVG_CLASS_ID_SHAPE; Paint::pImpl->method(new PaintMethod(pImpl)); @@ -59,7 +59,10 @@ uint32_t Shape::identifier() noexcept Result Shape::reset() noexcept { - pImpl->reset(); + pImpl->rs.path.cmds.clear(); + pImpl->rs.path.pts.clear(); + + pImpl->flag = RenderUpdateFlag::Path; return Result::Success; } @@ -69,9 +72,8 @@ uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept { if (!cmds) return 0; - *cmds = pImpl->rs.path.cmds; - - return pImpl->rs.path.cmdCnt; + *cmds = pImpl->rs.path.cmds.data; + return pImpl->rs.path.cmds.count; } @@ -79,9 +81,8 @@ uint32_t Shape::pathCoords(const Point** pts) const noexcept { if (!pts) return 0; - *pts = pImpl->rs.path.pts; - - return pImpl->rs.path.ptsCnt; + *pts = pImpl->rs.path.pts.data; + return pImpl->rs.path.pts.count; } @@ -242,21 +243,22 @@ Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) } -//TODO: kill alpha at TVG 1.0, because we also have opacity Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept { - pImpl->rs.color[0] = r; - pImpl->rs.color[1] = g; - pImpl->rs.color[2] = b; - pImpl->rs.color[3] = a; - pImpl->flag |= RenderUpdateFlag::Color; - if (pImpl->rs.fill) { delete(pImpl->rs.fill); pImpl->rs.fill = nullptr; pImpl->flag |= RenderUpdateFlag::Gradient; } + if (r == pImpl->rs.color[0] && g == pImpl->rs.color[1] && b == pImpl->rs.color[2] && a == pImpl->rs.color[3]) return Result::Success; + + pImpl->rs.color[0] = r; + pImpl->rs.color[1] = g; + pImpl->rs.color[2] = b; + pImpl->rs.color[3] = a; + pImpl->flag |= RenderUpdateFlag::Color; + return Result::Success; } @@ -328,7 +330,7 @@ Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const Result Shape::stroke(unique_ptr f) noexcept { - return pImpl->strokeFill(move(f)); + return pImpl->strokeFill(std::move(f)); } @@ -374,6 +376,17 @@ Result Shape::stroke(StrokeJoin join) noexcept return Result::Success; } +Result Shape::strokeMiterlimit(float miterlimit) noexcept +{ + // https://www.w3.org/TR/SVG2/painting.html#LineJoin + // - A negative value for stroke-miterlimit must be treated as an illegal value. + if (miterlimit < 0.0f) return Result::NonSupport; + // TODO Find out a reasonable max value. + if (!pImpl->strokeMiterlimit(miterlimit)) return Result::FailedAllocation; + + return Result::Success; +} + StrokeCap Shape::strokeCap() const noexcept { @@ -386,6 +399,11 @@ StrokeJoin Shape::strokeJoin() const noexcept return pImpl->rs.strokeJoin(); } +float Shape::strokeMiterlimit() const noexcept +{ + return pImpl->rs.strokeMiterlimit(); +} + Result Shape::fill(FillRule r) noexcept { diff --git a/thirdparty/thorvg/src/lib/tvgShapeImpl.h b/thirdparty/thorvg/src/lib/tvgShapeImpl.h index da288756fbbf..b05f85bf1cf4 100644 --- a/thirdparty/thorvg/src/lib/tvgShapeImpl.h +++ b/thirdparty/thorvg/src/lib/tvgShapeImpl.h @@ -24,6 +24,7 @@ #define _TVG_SHAPE_IMPL_H_ #include +#include "tvgMath.h" #include "tvgPaint.h" /************************************************************************/ @@ -34,7 +35,14 @@ struct Shape::Impl { RenderShape rs; //shape data RenderData rd = nullptr; //engine data - uint32_t flag = RenderUpdateFlag::None; + Shape* shape; + uint8_t flag = RenderUpdateFlag::None; + uint8_t opacity; //for composition + bool needComp; //composite or not + + Impl(Shape* s) : shape(s) + { + } bool dispose(RenderMethod& renderer) { @@ -45,12 +53,48 @@ struct Shape::Impl bool render(RenderMethod& renderer) { - return renderer.renderShape(rd); + Compositor* cmp = nullptr; + bool ret; + + if (needComp) { + cmp = renderer.target(bounds(renderer), renderer.colorSpace()); + renderer.beginComposite(cmp, CompositeMethod::None, opacity); + } + ret = renderer.renderShape(rd); + if (cmp) renderer.endComposite(cmp); + return ret; } - RenderData update(RenderMethod& renderer, const RenderTransform* transform, uint32_t opacity, Array& clips, RenderUpdateFlag pFlag, bool clipper) + bool needComposition(uint8_t opacity) { - rd = renderer.prepare(rs, rd, transform, opacity, clips, static_cast(pFlag | flag), clipper); + if (opacity == 0) return false; + + //Shape composition is only necessary when stroking & fill are valid. + if (!rs.stroke || rs.stroke->width < FLT_EPSILON || rs.stroke->color[3] == 0) return false; + if (!rs.fill && rs.color[3] == 0) return false; + + //translucent fill & stroke + if (opacity < 255) return true; + + //Composition test + const Paint* target; + auto method = shape->composite(&target); + if (!target || method == tvg::CompositeMethod::ClipPath) return false; + if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false; + + return true; + } + + RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + { + if ((needComp = needComposition(opacity))) { + /* Overriding opacity value. If this scene is half-translucent, + It must do intermeidate composition with that opacity value. */ + this->opacity = opacity; + opacity = 255; + } + + rd = renderer.prepare(rs, rd, transform, clips, opacity, static_cast(pFlag | flag), clipper); flag = RenderUpdateFlag::None; return rd; } @@ -63,15 +107,16 @@ struct Shape::Impl bool bounds(float* x, float* y, float* w, float* h) { //Path bounding size - if (rs.path.ptsCnt > 0 ) { - Point min = { rs.path.pts[0].x, rs.path.pts[0].y }; - Point max = { rs.path.pts[0].x, rs.path.pts[0].y }; - - for (uint32_t i = 1; i < rs.path.ptsCnt; ++i) { - if (rs.path.pts[i].x < min.x) min.x = rs.path.pts[i].x; - if (rs.path.pts[i].y < min.y) min.y = rs.path.pts[i].y; - if (rs.path.pts[i].x > max.x) max.x = rs.path.pts[i].x; - if (rs.path.pts[i].y > max.y) max.y = rs.path.pts[i].y; + if (rs.path.pts.count > 0 ) { + auto pts = rs.path.pts.data; + Point min = { pts->x, pts->y }; + Point max = { pts->x, pts->y }; + + for (auto pts2 = pts + 1; pts2 < rs.path.pts.end(); ++pts2) { + if (pts2->x < min.x) min.x = pts2->x; + if (pts2->y < min.y) min.y = pts2->y; + if (pts2->x > max.x) max.x = pts2->x; + if (pts2->y > max.y) max.y = pts2->y; } if (x) *x = min.x; @@ -87,88 +132,67 @@ struct Shape::Impl if (w) *w += rs.stroke->width; if (h) *h += rs.stroke->width; } - return rs.path.ptsCnt > 0 ? true : false; + return rs.path.pts.count > 0 ? true : false; } void reserveCmd(uint32_t cmdCnt) { - if (cmdCnt <= rs.path.reservedCmdCnt) return; - rs.path.reservedCmdCnt = cmdCnt; - rs.path.cmds = static_cast(realloc(rs.path.cmds, sizeof(PathCommand) * rs.path.reservedCmdCnt)); + rs.path.cmds.reserve(cmdCnt); } void reservePts(uint32_t ptsCnt) { - if (ptsCnt <= rs.path.reservedPtsCnt) return; - rs.path.reservedPtsCnt = ptsCnt; - rs.path.pts = static_cast(realloc(rs.path.pts, sizeof(Point) * rs.path.reservedPtsCnt)); + rs.path.pts.reserve(ptsCnt); } void grow(uint32_t cmdCnt, uint32_t ptsCnt) { - reserveCmd(rs.path.cmdCnt + cmdCnt); - reservePts(rs.path.ptsCnt + ptsCnt); - } - - void reset() - { - rs.path.cmdCnt = 0; - rs.path.ptsCnt = 0; - - flag = RenderUpdateFlag::Path; + rs.path.cmds.grow(cmdCnt); + rs.path.pts.grow(ptsCnt); } void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) { - memcpy(rs.path.cmds + rs.path.cmdCnt, cmds, sizeof(PathCommand) * cmdCnt); - memcpy(rs.path.pts + rs.path.ptsCnt, pts, sizeof(Point) * ptsCnt); - rs.path.cmdCnt += cmdCnt; - rs.path.ptsCnt += ptsCnt; + memcpy(rs.path.cmds.end(), cmds, sizeof(PathCommand) * cmdCnt); + memcpy(rs.path.pts.end(), pts, sizeof(Point) * ptsCnt); + rs.path.cmds.count += cmdCnt; + rs.path.pts.count += ptsCnt; flag |= RenderUpdateFlag::Path; } void moveTo(float x, float y) { - if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); - if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2); - - rs.path.cmds[rs.path.cmdCnt++] = PathCommand::MoveTo; - rs.path.pts[rs.path.ptsCnt++] = {x, y}; + rs.path.cmds.push(PathCommand::MoveTo); + rs.path.pts.push({x, y}); flag |= RenderUpdateFlag::Path; } void lineTo(float x, float y) { - if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); - if (rs.path.ptsCnt + 2 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 2) * 2); - - rs.path.cmds[rs.path.cmdCnt++] = PathCommand::LineTo; - rs.path.pts[rs.path.ptsCnt++] = {x, y}; + rs.path.cmds.push(PathCommand::LineTo); + rs.path.pts.push({x, y}); flag |= RenderUpdateFlag::Path; } void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) { - if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); - if (rs.path.ptsCnt + 3 > rs.path.reservedPtsCnt) reservePts((rs.path.ptsCnt + 3) * 2); - - rs.path.cmds[rs.path.cmdCnt++] = PathCommand::CubicTo; - rs.path.pts[rs.path.ptsCnt++] = {cx1, cy1}; - rs.path.pts[rs.path.ptsCnt++] = {cx2, cy2}; - rs.path.pts[rs.path.ptsCnt++] = {x, y}; + rs.path.cmds.push(PathCommand::CubicTo); + rs.path.pts.push({cx1, cy1}); + rs.path.pts.push({cx2, cy2}); + rs.path.pts.push({x, y}); flag |= RenderUpdateFlag::Path; } void close() { - if (rs.path.cmdCnt > 0 && rs.path.cmds[rs.path.cmdCnt - 1] == PathCommand::Close) return; + //Don't close multiple times. + if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return; - if (rs.path.cmdCnt + 1 > rs.path.reservedCmdCnt) reserveCmd((rs.path.cmdCnt + 1) * 2); - rs.path.cmds[rs.path.cmdCnt++] = PathCommand::Close; + rs.path.cmds.push(PathCommand::Close); flag |= RenderUpdateFlag::Path; } @@ -202,6 +226,15 @@ struct Shape::Impl return true; } + bool strokeMiterlimit(float miterlimit) + { + if (!rs.stroke) rs.stroke = new RenderStroke(); + rs.stroke->miterlimit = miterlimit; + flag |= RenderUpdateFlag::Stroke; + + return true; + } + bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (!rs.stroke) rs.stroke = new RenderStroke(); @@ -262,6 +295,12 @@ struct Shape::Impl return true; } + bool strokeFirst() + { + if (!rs.stroke) return true; + return rs.stroke->strokeFirst; + } + bool strokeFirst(bool strokeFirst) { if (!rs.stroke) rs.stroke = new RenderStroke(); @@ -271,6 +310,11 @@ struct Shape::Impl return true; } + void update(RenderUpdateFlag flag) + { + this->flag |= flag; + } + Paint* duplicate() { auto ret = Shape::gen(); @@ -283,19 +327,11 @@ struct Shape::Impl dup->flag = RenderUpdateFlag::Color; //Path - if (rs.path.cmdCnt > 0 && rs.path.ptsCnt > 0) { - dup->rs.path.cmdCnt = rs.path.cmdCnt; - dup->rs.path.reservedCmdCnt = rs.path.reservedCmdCnt; - dup->rs.path.ptsCnt = rs.path.ptsCnt; - dup->rs.path.reservedPtsCnt = rs.path.reservedPtsCnt; - - dup->rs.path.cmds = static_cast(malloc(sizeof(PathCommand) * dup->rs.path.reservedCmdCnt)); - if (dup->rs.path.cmds) memcpy(dup->rs.path.cmds, rs.path.cmds, sizeof(PathCommand) * dup->rs.path.cmdCnt); - - dup->rs.path.pts = static_cast(malloc(sizeof(Point) * dup->rs.path.reservedPtsCnt)); - if (dup->rs.path.pts) memcpy(dup->rs.path.pts, rs.path.pts, sizeof(Point) * dup->rs.path.ptsCnt); + if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) { + dup->rs.path.cmds = rs.path.cmds; + dup->rs.path.pts = rs.path.pts; + dup->flag |= RenderUpdateFlag::Path; } - dup->flag |= RenderUpdateFlag::Path; //Stroke if (rs.stroke) { diff --git a/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp index 626bd51a2e80..7e6bc40c5b3b 100644 --- a/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp +++ b/thirdparty/thorvg/src/lib/tvgSwCanvas.cpp @@ -67,7 +67,7 @@ Result SwCanvas::mempool(MempoolPolicy policy) noexcept if (!renderer) return Result::MemoryCorruption; //It can't change the policy during the running. - if (Canvas::pImpl->paints.count > 0) return Result::InsufficientCondition; + if (!Canvas::pImpl->paints.empty()) return Result::InsufficientCondition; if (policy == MempoolPolicy::Individual) renderer->mempool(false); else renderer->mempool(true); diff --git a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h index ce2016f56108..7ada963b776b 100644 --- a/thirdparty/thorvg/src/lib/tvgTaskScheduler.h +++ b/thirdparty/thorvg/src/lib/tvgTaskScheduler.h @@ -85,3 +85,4 @@ struct Task } #endif //_TVG_TASK_SCHEDULER_H_ + diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp deleted file mode 100644 index b0a9fdd57995..000000000000 --- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2020 - 2023 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 "tvgLoader.h" -#include "tvgPngLoader.h" - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -PngLoader::PngLoader() -{ - image = static_cast(calloc(1, sizeof(png_image))); - image->version = PNG_IMAGE_VERSION; - image->opaque = NULL; -} - -PngLoader::~PngLoader() -{ - if (content) { - free((void*)content); - content = nullptr; - } - free(image); -} - -bool PngLoader::open(const string& path) -{ - image->opaque = NULL; - - if (!png_image_begin_read_from_file(image, path.c_str())) return false; - - w = (float)image->width; - h = (float)image->height; - cs = ColorSpace::ARGB8888; - - return true; -} - -bool PngLoader::open(const char* data, uint32_t size, bool copy) -{ - image->opaque = NULL; - - if (!png_image_begin_read_from_memory(image, data, size)) return false; - - w = (float)image->width; - h = (float)image->height; - cs = ColorSpace::ARGB8888; - - return true; -} - - -bool PngLoader::read() -{ - png_bytep buffer; - image->format = PNG_FORMAT_BGRA; - buffer = static_cast(malloc(PNG_IMAGE_SIZE((*image)))); - if (!buffer) { - //out of memory, only time when libpng doesnt free its data - png_image_free(image); - return false; - } - if (!png_image_finish_read(image, NULL, buffer, 0, NULL)) { - free(buffer); - return false; - } - content = reinterpret_cast(buffer); - - return true; -} - -bool PngLoader::close() -{ - png_image_free(image); - return true; -} - -unique_ptr PngLoader::bitmap() -{ - if (!content) return nullptr; - - //TODO: It's better to keep this surface instance in the loader side - auto surface = new Surface; - surface->buf32 = content; - surface->stride = w; - surface->w = w; - surface->h = h; - surface->cs = cs; - surface->channelSize = sizeof(uint32_t); - surface->owner = true; - surface->premultiplied = false; - - return unique_ptr(surface); -} - diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp deleted file mode 100644 index 6edda86cc13e..000000000000 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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 -#include "tvgLoader.h" -#include "tvgJpgLoader.h" - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - -void JpgLoader::clear() -{ - jpgdDelete(decoder); - if (freeData) free(data); - decoder = nullptr; - data = nullptr; - freeData = false; -} - - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - - -JpgLoader::~JpgLoader() -{ - jpgdDelete(decoder); - if (freeData) free(data); - free(image); -} - - -bool JpgLoader::open(const string& path) -{ - clear(); - - int width, height; - decoder = jpgdHeader(path.c_str(), &width, &height); - if (!decoder) return false; - - w = static_cast(width); - h = static_cast(height); - cs = ColorSpace::ARGB8888; - - return true; -} - - -bool JpgLoader::open(const char* data, uint32_t size, bool copy) -{ - clear(); - - if (copy) { - this->data = (char *) malloc(size); - if (!this->data) return false; - memcpy((char *)this->data, data, size); - freeData = true; - } else { - this->data = (char *) data; - freeData = false; - } - - int width, height; - decoder = jpgdHeader(this->data, size, &width, &height); - if (!decoder) return false; - - w = static_cast(width); - h = static_cast(height); - cs = ColorSpace::ARGB8888; - - return true; -} - - - -bool JpgLoader::read() -{ - if (!decoder || w <= 0 || h <= 0) return false; - - TaskScheduler::request(this); - - return true; -} - - -bool JpgLoader::close() -{ - this->done(); - clear(); - return true; -} - - -unique_ptr JpgLoader::bitmap() -{ - this->done(); - - if (!image) return nullptr; - - //TODO: It's better to keep this surface instance in the loader side - auto surface = new Surface; - surface->buf8 = image; - surface->stride = static_cast(w); - surface->w = static_cast(w); - surface->h = static_cast(h); - surface->cs = cs; - surface->channelSize = sizeof(uint32_t); - surface->premultiplied = true; - surface->owner = true; - - return unique_ptr(surface); -} - - -void JpgLoader::run(unsigned tid) -{ - if (image) { - free(image); - image = nullptr; - } - image = jpgdDecompress(decoder); -} diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h deleted file mode 100644 index 6d2febe94f01..000000000000 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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_JPG_LOADER_H_ -#define _TVG_JPG_LOADER_H_ - -#include "tvgTaskScheduler.h" -#include "tvgJpgd.h" - -class JpgLoader : public LoadModule, public Task -{ -private: - jpeg_decoder* decoder = nullptr; - char* data = nullptr; - unsigned char *image = nullptr; - bool freeData = false; - - void clear(); - -public: - ~JpgLoader(); - - using LoadModule::open; - bool open(const string& path) override; - bool open(const char* data, uint32_t size, bool copy) override; - bool read() override; - bool close() override; - - unique_ptr bitmap() override; - void run(unsigned tid) override; -}; - -#endif //_TVG_JPG_LOADER_H_ diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp deleted file mode 100644 index 6ea2efb0545a..000000000000 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp +++ /dev/null @@ -1,3029 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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. - */ - -// jpgd.cpp - C++ class for JPEG decompression. -// Public domain, Rich Geldreich -// Alex Evans: Linear memory allocator (taken from jpge.h). -// v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings (all looked harmless) -// -// Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2. -// -// Chroma upsampling quality: H2V2 is upsampled in the frequency domain, H2V1 and H1V2 are upsampled using point sampling. -// Chroma upsampling reference: "Fast Scheme for Image Size Change in the Compressed Domain" -// http://vision.ai.uiuc.edu/~dugad/research/dct/index.html - -#include -#include -#include -#include -#include -#include "tvgJpgd.h" - -#ifdef _MSC_VER - #pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable - #define JPGD_NORETURN __declspec(noreturn) -#elif defined(__GNUC__) - #define JPGD_NORETURN __attribute__ ((noreturn)) -#else - #define JPGD_NORETURN -#endif - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - - -// Set to 1 to enable freq. domain chroma upsampling on images using H2V2 subsampling (0=faster nearest neighbor sampling). -// This is slower, but results in higher quality on images with highly saturated colors. -#define JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING 1 - -#define JPGD_ASSERT(x) -#define JPGD_MAX(a,b) (((a)>(b)) ? (a) : (b)) -#define JPGD_MIN(a,b) (((a)<(b)) ? (a) : (b)) - -typedef int16_t jpgd_quant_t; -typedef int16_t jpgd_block_t; - -// Success/failure error codes. -enum jpgd_status -{ - JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1, - 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_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, - JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM -}; - -enum -{ - JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4, - JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384 -}; - -// Input stream interface. -// Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available. -// The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set. -// It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer. -// Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding. -struct jpeg_decoder_stream -{ - jpeg_decoder_stream() { } - virtual ~jpeg_decoder_stream() { } - - // The read() method is called when the internal input buffer is empty. - // Parameters: - // pBuf - input buffer - // max_bytes_to_read - maximum bytes that can be written to pBuf - // pEOF_flag - set this to true if at end of stream (no more bytes remaining) - // Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0). - // Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full. - virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag) = 0; -}; - - -// stdio FILE stream class. -class jpeg_decoder_file_stream : public jpeg_decoder_stream -{ - jpeg_decoder_file_stream(const jpeg_decoder_file_stream &); - jpeg_decoder_file_stream &operator =(const jpeg_decoder_file_stream &); - - FILE *m_pFile = nullptr; - bool m_eof_flag = false; - bool m_error_flag = false; - -public: - jpeg_decoder_file_stream() {} - virtual ~jpeg_decoder_file_stream(); - bool open(const char *Pfilename); - void close(); - virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag); - }; - - -// Memory stream class. -class jpeg_decoder_mem_stream : public jpeg_decoder_stream -{ - const uint8_t *m_pSrc_data; - uint32_t m_ofs, m_size; - -public: - jpeg_decoder_mem_stream() : m_pSrc_data(nullptr), m_ofs(0), m_size(0) {} - jpeg_decoder_mem_stream(const uint8_t *pSrc_data, uint32_t size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) {} - virtual ~jpeg_decoder_mem_stream() {} - bool open(const uint8_t *pSrc_data, uint32_t size); - void close() { m_pSrc_data = nullptr; m_ofs = 0; m_size = 0; } - virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag); -}; - - -class jpeg_decoder -{ -public: - // Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc. - // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline. - jpeg_decoder(jpeg_decoder_stream *pStream); - ~jpeg_decoder(); - - // Call this method after constructing the object to begin decompression. - // If JPGD_SUCCESS is returned you may then call decode() on each scanline. - int begin_decoding(); - // Returns the next scan line. - // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1). - // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4). - // Returns JPGD_SUCCESS if a scan line has been returned. - // Returns JPGD_DONE if all scan lines have been returned. - // Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info. - int decode(const void** pScan_line, uint32_t* pScan_line_len); - inline jpgd_status get_error_code() const { return m_error_code; } - inline int get_width() const { return m_image_x_size; } - inline int get_height() const { return m_image_y_size; } - inline int get_num_components() const { return m_comps_in_frame; } - inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; } - inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); } - // Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file). - inline int get_total_bytes_read() const { return m_total_bytes_read; } - -private: - jpeg_decoder(const jpeg_decoder &); - jpeg_decoder &operator =(const jpeg_decoder &); - - typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int); - - struct huff_tables - { - bool ac_table; - uint32_t look_up[256]; - uint32_t look_up2[256]; - uint8_t code_size[256]; - uint32_t tree[512]; - }; - - struct coeff_buf - { - uint8_t *pData; - int block_num_x, block_num_y; - int block_len_x, block_len_y; - int block_size; - }; - - struct mem_block - { - mem_block *m_pNext; - size_t m_used_count; - size_t m_size; - char m_data[1]; - }; - - jmp_buf m_jmp_state; - mem_block *m_pMem_blocks; - int m_image_x_size; - int m_image_y_size; - jpeg_decoder_stream *m_pStream; - int m_progressive_flag; - uint8_t m_huff_ac[JPGD_MAX_HUFF_TABLES]; - uint8_t* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size - uint8_t* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size - jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables - int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported) - int m_comps_in_frame; // # of components in frame - int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor - int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor - int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector - int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID - int m_comp_h_blocks[JPGD_MAX_COMPONENTS]; - int m_comp_v_blocks[JPGD_MAX_COMPONENTS]; - int m_comps_in_scan; // # of components in scan - int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan - int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector - int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector - int m_spectral_start; // spectral selection start - int m_spectral_end; // spectral selection end - int m_successive_low; // successive approximation low - int m_successive_high; // successive approximation high - int m_max_mcu_x_size; // MCU's max. X size in pixels - int m_max_mcu_y_size; // MCU's max. Y size in pixels - int m_blocks_per_mcu; - int m_max_blocks_per_row; - int m_mcus_per_row, m_mcus_per_col; - int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU]; - int m_total_lines_left; // total # lines left in image - int m_mcu_lines_left; // total # lines left in this MCU - int m_real_dest_bytes_per_scan_line; - int m_dest_bytes_per_scan_line; // rounded up - int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y) - huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES]; - coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS]; - coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS]; - int m_eob_run; - int m_block_y_mcu[JPGD_MAX_COMPONENTS]; - uint8_t* m_pIn_buf_ofs; - int m_in_buf_left; - int m_tem_flag; - bool m_eof_flag; - uint8_t m_in_buf_pad_start[128]; - uint8_t m_in_buf[JPGD_IN_BUF_SIZE + 128]; - uint8_t m_in_buf_pad_end[128]; - int m_bits_left; - uint32_t m_bit_buf; - int m_restart_interval; - int m_restarts_left; - int m_next_restart_num; - int m_max_mcus_per_row; - int m_max_blocks_per_mcu; - int m_expanded_blocks_per_mcu; - int m_expanded_blocks_per_row; - int m_expanded_blocks_per_component; - bool m_freq_domain_chroma_upsample; - int m_max_mcus_per_col; - uint32_t m_last_dc_val[JPGD_MAX_COMPONENTS]; - jpgd_block_t* m_pMCU_coefficients; - int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU]; - uint8_t* m_pSample_buf; - int m_crr[256]; - int m_cbb[256]; - int m_crg[256]; - int m_cbg[256]; - uint8_t* m_pScan_line_0; - uint8_t* m_pScan_line_1; - jpgd_status m_error_code; - bool m_ready_flag; - int m_total_bytes_read; - - void free_all_blocks(); - JPGD_NORETURN void stop_decoding(jpgd_status status); - void *alloc(size_t n, bool zero = false); - void word_clear(void *p, uint16_t c, uint32_t n); - void prep_in_buffer(); - void read_dht_marker(); - void read_dqt_marker(); - void read_sof_marker(); - void skip_variable_marker(); - void read_dri_marker(); - void read_sos_marker(); - int next_marker(); - int process_markers(); - void locate_soi_marker(); - void locate_sof_marker(); - int locate_sos_marker(); - void init(jpeg_decoder_stream * pStream); - void create_look_ups(); - void fix_in_buffer(); - void transform_mcu(int mcu_row); - void transform_mcu_expand(int mcu_row); - coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y); - inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y); - void load_next_row(); - void decode_next_row(); - void make_huff_table(int index, huff_tables *pH); - void check_quant_tables(); - void check_huff_tables(); - void calc_mcu_block_order(); - int init_scan(); - void init_frame(); - void process_restart(); - void decode_scan(pDecode_block_func decode_block_func); - void init_progressive(); - void init_sequential(); - void decode_start(); - void decode_init(jpeg_decoder_stream * pStream); - void H2V2Convert(); - void H2V1Convert(); - void H1V2Convert(); - void H1V1Convert(); - void gray_convert(); - void expanded_convert(); - void find_eoi(); - inline uint32_t get_char(); - inline uint32_t get_char(bool *pPadding_flag); - inline void stuff_char(uint8_t q); - inline uint8_t get_octet(); - inline uint32_t get_bits(int num_bits); - inline uint32_t get_bits_no_markers(int numbits); - inline int huff_decode(huff_tables *pH); - inline int huff_decode(huff_tables *pH, int& extrabits); - static inline uint8_t clamp(int i); - static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y); - static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y); - static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y); - static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y); -}; - - -// DCT coefficients are stored in this sequence. -static int g_ZAG[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 }; - -enum JPEG_MARKER -{ - M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8, - M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC, - M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7, - M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF, - M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0 -}; - -enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 }; - -#define CONST_BITS 13 -#define PASS1_BITS 2 -#define SCALEDONE ((int32_t)1) -#define DESCALE(x,n) (((x) + (SCALEDONE << ((n)-1))) >> (n)) -#define DESCALE_ZEROSHIFT(x,n) (((x) + (128 << (n)) + (SCALEDONE << ((n)-1))) >> (n)) -#define MULTIPLY(var, cnst) ((var) * (cnst)) -#define CLAMP(i) ((static_cast(i) > 255) ? (((~i) >> 31) & 0xFF) : (i)) - -#define FIX_0_298631336 ((int32_t)2446) /* FIX(0.298631336) */ -#define FIX_0_390180644 ((int32_t)3196) /* FIX(0.390180644) */ -#define FIX_0_541196100 ((int32_t)4433) /* FIX(0.541196100) */ -#define FIX_0_765366865 ((int32_t)6270) /* FIX(0.765366865) */ -#define FIX_0_899976223 ((int32_t)7373) /* FIX(0.899976223) */ -#define FIX_1_175875602 ((int32_t)9633) /* FIX(1.175875602) */ -#define FIX_1_501321110 ((int32_t)12299) /* FIX(1.501321110) */ -#define FIX_1_847759065 ((int32_t)15137) /* FIX(1.847759065) */ -#define FIX_1_961570560 ((int32_t)16069) /* FIX(1.961570560) */ -#define FIX_2_053119869 ((int32_t)16819) /* FIX(2.053119869) */ -#define FIX_2_562915447 ((int32_t)20995) /* FIX(2.562915447) */ -#define FIX_3_072711026 ((int32_t)25172) /* FIX(3.072711026) */ - - -// Compiler creates a fast path 1D IDCT for X non-zero columns -template -struct Row -{ - static void idct(int* pTemp, const jpgd_block_t* pSrc) - { - // ACCESS_COL() will be optimized at compile time to either an array access, or 0. - #define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0) - - const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6); - const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100); - const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); - const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); - - const int tmp0 = static_cast(ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS; - const int tmp1 = static_cast(ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS; - - const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; - - const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1); - - const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3; - const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602); - - const int az1 = MULTIPLY(bz1, - FIX_0_899976223); - const int az2 = MULTIPLY(bz2, - FIX_2_562915447); - const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5; - const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5; - - const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3; - const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4; - const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3; - const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4; - - pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS-PASS1_BITS); - pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS-PASS1_BITS); - pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS-PASS1_BITS); - pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS-PASS1_BITS); - pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS-PASS1_BITS); - pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS-PASS1_BITS); - pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS-PASS1_BITS); - pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS-PASS1_BITS); - } -}; - - -template <> -struct Row<0> -{ - static void idct(int* pTemp, const jpgd_block_t* pSrc) - { -#ifdef _MSC_VER - pTemp; pSrc; -#endif - } -}; - - -template <> -struct Row<1> -{ - static void idct(int* pTemp, const jpgd_block_t* pSrc) - { - const int dcval = (pSrc[0] << PASS1_BITS); - - pTemp[0] = dcval; - pTemp[1] = dcval; - pTemp[2] = dcval; - pTemp[3] = dcval; - pTemp[4] = dcval; - pTemp[5] = dcval; - pTemp[6] = dcval; - pTemp[7] = dcval; - } -}; - - -// Compiler creates a fast path 1D IDCT for X non-zero rows -template -struct Col -{ - static void idct(uint8_t* pDst_ptr, const int* pTemp) - { - // ACCESS_ROW() will be optimized at compile time to either an array access, or 0. - #define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0) - - const int z2 = ACCESS_ROW(2); - const int z3 = ACCESS_ROW(6); - - const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100); - const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); - const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); - - const int tmp0 = static_cast(ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS; - const int tmp1 = static_cast(ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS; - - const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2; - - const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1); - - const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3; - const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602); - - const int az1 = MULTIPLY(bz1, - FIX_0_899976223); - const int az2 = MULTIPLY(bz2, - FIX_2_562915447); - const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5; - const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5; - - const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3; - const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4; - const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3; - const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4; - - int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*0] = (uint8_t)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*7] = (uint8_t)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*1] = (uint8_t)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*6] = (uint8_t)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*2] = (uint8_t)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*5] = (uint8_t)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*3] = (uint8_t)CLAMP(i); - - i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS+PASS1_BITS+3); - pDst_ptr[8*4] = (uint8_t)CLAMP(i); - } -}; - - -template <> -struct Col<1> -{ - static void idct(uint8_t* pDst_ptr, const int* pTemp) - { - int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS+3); - const uint8_t dcval_clamped = (uint8_t)CLAMP(dcval); - pDst_ptr[0*8] = dcval_clamped; - pDst_ptr[1*8] = dcval_clamped; - pDst_ptr[2*8] = dcval_clamped; - pDst_ptr[3*8] = dcval_clamped; - pDst_ptr[4*8] = dcval_clamped; - pDst_ptr[5*8] = dcval_clamped; - pDst_ptr[6*8] = dcval_clamped; - pDst_ptr[7*8] = dcval_clamped; - } -}; - - -static const uint8_t s_idct_row_table[] = { - 1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0, - 4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0, - 6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0, - 6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0, - 8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2, - 8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2, - 8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4, - 8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8, -}; - - -static const uint8_t s_idct_col_table[] = { 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }; - - -void idct(const jpgd_block_t* pSrc_ptr, uint8_t* pDst_ptr, int block_max_zag) -{ - JPGD_ASSERT(block_max_zag >= 1); - JPGD_ASSERT(block_max_zag <= 64); - - if (block_max_zag <= 1) { - int k = ((pSrc_ptr[0] + 4) >> 3) + 128; - k = CLAMP(k); - k = k | (k<<8); - k = k | (k<<16); - for (int i = 8; i > 0; i--) { - *(int*)&pDst_ptr[0] = k; - *(int*)&pDst_ptr[4] = k; - pDst_ptr += 8; - } - return; - } - - int temp[64]; - const jpgd_block_t* pSrc = pSrc_ptr; - int* pTemp = temp; - const uint8_t* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8]; - int i; - for (i = 8; i > 0; i--, pRow_tab++) { - switch (*pRow_tab) { - case 0: Row<0>::idct(pTemp, pSrc); break; - case 1: Row<1>::idct(pTemp, pSrc); break; - case 2: Row<2>::idct(pTemp, pSrc); break; - case 3: Row<3>::idct(pTemp, pSrc); break; - case 4: Row<4>::idct(pTemp, pSrc); break; - case 5: Row<5>::idct(pTemp, pSrc); break; - case 6: Row<6>::idct(pTemp, pSrc); break; - case 7: Row<7>::idct(pTemp, pSrc); break; - case 8: Row<8>::idct(pTemp, pSrc); break; - } - pSrc += 8; - pTemp += 8; - } - - pTemp = temp; - - const int nonzero_rows = s_idct_col_table[block_max_zag - 1]; - for (i = 8; i > 0; i--) { - switch (nonzero_rows) { - case 1: Col<1>::idct(pDst_ptr, pTemp); break; - case 2: Col<2>::idct(pDst_ptr, pTemp); break; - case 3: Col<3>::idct(pDst_ptr, pTemp); break; - case 4: Col<4>::idct(pDst_ptr, pTemp); break; - case 5: Col<5>::idct(pDst_ptr, pTemp); break; - case 6: Col<6>::idct(pDst_ptr, pTemp); break; - case 7: Col<7>::idct(pDst_ptr, pTemp); break; - case 8: Col<8>::idct(pDst_ptr, pTemp); break; - } - pTemp++; - pDst_ptr++; - } -} - - -void idct_4x4(const jpgd_block_t* pSrc_ptr, uint8_t* pDst_ptr) -{ - int temp[64]; - int* pTemp = temp; - const jpgd_block_t* pSrc = pSrc_ptr; - - for (int i = 4; i > 0; i--) { - Row<4>::idct(pTemp, pSrc); - pSrc += 8; - pTemp += 8; - } - - pTemp = temp; - - for (int i = 8; i > 0; i--) { - Col<4>::idct(pDst_ptr, pTemp); - pTemp++; - pDst_ptr++; - } -} - - -// Retrieve one character from the input stream. -inline uint32_t jpeg_decoder::get_char() -{ - // Any bytes remaining in buffer? - if (!m_in_buf_left) { - // Try to get more bytes. - prep_in_buffer(); - // Still nothing to get? - if (!m_in_buf_left) { - // Pad the end of the stream with 0xFF 0xD9 (EOI marker) - int t = m_tem_flag; - m_tem_flag ^= 1; - if (t) return 0xD9; - else return 0xFF; - } - } - uint32_t c = *m_pIn_buf_ofs++; - m_in_buf_left--; - return c; -} - - -// Same as previous method, except can indicate if the character is a pad character or not. -inline uint32_t jpeg_decoder::get_char(bool *pPadding_flag) -{ - if (!m_in_buf_left) { - prep_in_buffer(); - if (!m_in_buf_left) { - *pPadding_flag = true; - int t = m_tem_flag; - m_tem_flag ^= 1; - if (t) return 0xD9; - else return 0xFF; - } - } - *pPadding_flag = false; - uint32_t c = *m_pIn_buf_ofs++; - m_in_buf_left--; - - return c; -} - - -// Inserts a previously retrieved character back into the input buffer. -inline void jpeg_decoder::stuff_char(uint8_t q) -{ - *(--m_pIn_buf_ofs) = q; - m_in_buf_left++; -} - - -// Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered. -inline uint8_t jpeg_decoder::get_octet() -{ - bool padding_flag; - int c = get_char(&padding_flag); - - if (c == 0xFF) { - if (padding_flag) return 0xFF; - - c = get_char(&padding_flag); - if (padding_flag) { - stuff_char(0xFF); - return 0xFF; - } - if (c == 0x00) return 0xFF; - else { - stuff_char(static_cast(c)); - stuff_char(0xFF); - return 0xFF; - } - } - return static_cast(c); -} - - -// Retrieves a variable number of bits from the input stream. Does not recognize markers. -inline uint32_t jpeg_decoder::get_bits(int num_bits) -{ - if (!num_bits) return 0; - - uint32_t i = m_bit_buf >> (32 - num_bits); - - if ((m_bits_left -= num_bits) <= 0) { - m_bit_buf <<= (num_bits += m_bits_left); - uint32_t c1 = get_char(); - uint32_t c2 = get_char(); - m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2; - m_bit_buf <<= -m_bits_left; - m_bits_left += 16; - JPGD_ASSERT(m_bits_left >= 0); - } - else m_bit_buf <<= num_bits; - - return i; -} - - -// Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered. -inline uint32_t jpeg_decoder::get_bits_no_markers(int num_bits) -{ - if (!num_bits)return 0; - - uint32_t i = m_bit_buf >> (32 - num_bits); - - if ((m_bits_left -= num_bits) <= 0) { - m_bit_buf <<= (num_bits += m_bits_left); - if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF)) { - uint32_t c1 = get_octet(); - uint32_t c2 = get_octet(); - m_bit_buf |= (c1 << 8) | c2; - } else { - m_bit_buf |= ((uint32_t)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1]; - m_in_buf_left -= 2; - m_pIn_buf_ofs += 2; - } - m_bit_buf <<= -m_bits_left; - m_bits_left += 16; - JPGD_ASSERT(m_bits_left >= 0); - } else m_bit_buf <<= num_bits; - - return i; -} - - -// Decodes a Huffman encoded symbol. -inline int jpeg_decoder::huff_decode(huff_tables *pH) -{ - int symbol; - - // Check first 8-bits: do we have a complete symbol? - if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0) { - // Decode more bits, use a tree traversal to find symbol. - int ofs = 23; - do { - symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))]; - ofs--; - } while (symbol < 0); - get_bits_no_markers(8 + (23 - ofs)); - } else get_bits_no_markers(pH->code_size[symbol]); - - return symbol; -} - - -// Decodes a Huffman encoded symbol. -inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits) -{ - int symbol; - - // Check first 8-bits: do we have a complete symbol? - if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0) { - // Use a tree traversal to find symbol. - int ofs = 23; - do { - symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))]; - ofs--; - } while (symbol < 0); - - get_bits_no_markers(8 + (23 - ofs)); - extra_bits = get_bits_no_markers(symbol & 0xF); - } else { - JPGD_ASSERT(((symbol >> 8) & 31) == pH->code_size[symbol & 255] + ((symbol & 0x8000) ? (symbol & 15) : 0)); - - if (symbol & 0x8000) { - get_bits_no_markers((symbol >> 8) & 31); - extra_bits = symbol >> 16; - } else { - int code_size = (symbol >> 8) & 31; - int num_extra_bits = symbol & 0xF; - int bits = code_size + num_extra_bits; - if (bits <= (m_bits_left + 16)) extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1); - else { - get_bits_no_markers(code_size); - extra_bits = get_bits_no_markers(num_extra_bits); - } - } - symbol &= 0xFF; - } - return symbol; -} - - -// Tables and macro used to fully decode the DPCM differences. -static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; -static const unsigned int s_extend_offset[16] = { 0, ((~0u)<<1) + 1, ((~0u)<<2) + 1, ((~0u)<<3) + 1, ((~0u)<<4) + 1, ((~0u)<<5) + 1, ((~0u)<<6) + 1, ((~0u)<<7) + 1, ((~0u)<<8) + 1, ((~0u)<<9) + 1, ((~0u)<<10) + 1, ((~0u)<<11) + 1, ((~0u)<<12) + 1, ((~0u)<<13) + 1, ((~0u)<<14) + 1, ((~0u)<<15) + 1 }; - -// The logical AND's in this macro are to shut up static code analysis (aren't really necessary - couldn't find another way to do this) -#define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x)) - - -// Clamps a value between 0-255. -inline uint8_t jpeg_decoder::clamp(int i) -{ - if (static_cast(i) > 255) i = (((~i) >> 31) & 0xFF); - return static_cast(i); -} - - -namespace DCT_Upsample -{ - struct Matrix44 - { - typedef int Element_Type; - enum { NUM_ROWS = 4, NUM_COLS = 4 }; - - Element_Type v[NUM_ROWS][NUM_COLS]; - - inline int rows() const { return NUM_ROWS; } - inline int cols() const { return NUM_COLS; } - inline const Element_Type & at(int r, int c) const { return v[r][c]; } - inline Element_Type & at(int r, int c) { return v[r][c]; } - - inline Matrix44() {} - - inline Matrix44& operator += (const Matrix44& a) - { - for (int r = 0; r < NUM_ROWS; r++) { - at(r, 0) += a.at(r, 0); - at(r, 1) += a.at(r, 1); - at(r, 2) += a.at(r, 2); - at(r, 3) += a.at(r, 3); - } - return *this; - } - - inline Matrix44& operator -= (const Matrix44& a) - { - for (int r = 0; r < NUM_ROWS; r++) { - at(r, 0) -= a.at(r, 0); - at(r, 1) -= a.at(r, 1); - at(r, 2) -= a.at(r, 2); - at(r, 3) -= a.at(r, 3); - } - return *this; - } - - friend inline Matrix44 operator + (const Matrix44& a, const Matrix44& b) - { - Matrix44 ret; - for (int r = 0; r < NUM_ROWS; r++) { - ret.at(r, 0) = a.at(r, 0) + b.at(r, 0); - ret.at(r, 1) = a.at(r, 1) + b.at(r, 1); - ret.at(r, 2) = a.at(r, 2) + b.at(r, 2); - ret.at(r, 3) = a.at(r, 3) + b.at(r, 3); - } - return ret; - } - - friend inline Matrix44 operator - (const Matrix44& a, const Matrix44& b) - { - Matrix44 ret; - for (int r = 0; r < NUM_ROWS; r++) { - ret.at(r, 0) = a.at(r, 0) - b.at(r, 0); - ret.at(r, 1) = a.at(r, 1) - b.at(r, 1); - ret.at(r, 2) = a.at(r, 2) - b.at(r, 2); - ret.at(r, 3) = a.at(r, 3) - b.at(r, 3); - } - return ret; - } - - static inline void add_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b) - { - for (int r = 0; r < 4; r++) { - pDst[0*8 + r] = static_cast(a.at(r, 0) + b.at(r, 0)); - pDst[1*8 + r] = static_cast(a.at(r, 1) + b.at(r, 1)); - pDst[2*8 + r] = static_cast(a.at(r, 2) + b.at(r, 2)); - pDst[3*8 + r] = static_cast(a.at(r, 3) + b.at(r, 3)); - } - } - - static inline void sub_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b) - { - for (int r = 0; r < 4; r++) { - pDst[0*8 + r] = static_cast(a.at(r, 0) - b.at(r, 0)); - pDst[1*8 + r] = static_cast(a.at(r, 1) - b.at(r, 1)); - pDst[2*8 + r] = static_cast(a.at(r, 2) - b.at(r, 2)); - pDst[3*8 + r] = static_cast(a.at(r, 3) - b.at(r, 3)); - } - } - }; - - const int FRACT_BITS = 10; - const int SCALE = 1 << FRACT_BITS; - - typedef int Temp_Type; - #define D(i) (((i) + (SCALE >> 1)) >> FRACT_BITS) - #define F(i) ((int)((i) * SCALE + .5f)) - - // Any decent C++ compiler will optimize this at compile time to a 0, or an array access. - #define AT(c, r) ((((c)>=NUM_COLS)||((r)>=NUM_ROWS)) ? 0 : pSrc[(c)+(r)*8]) - - // NUM_ROWS/NUM_COLS = # of non-zero rows/cols in input matrix - template - struct P_Q - { - static void calc(Matrix44& P, Matrix44& Q, const jpgd_block_t* pSrc) - { - // 4x8 = 4x8 times 8x8, matrix 0 is constant - const Temp_Type X000 = AT(0, 0); - const Temp_Type X001 = AT(0, 1); - const Temp_Type X002 = AT(0, 2); - const Temp_Type X003 = AT(0, 3); - const Temp_Type X004 = AT(0, 4); - const Temp_Type X005 = AT(0, 5); - const Temp_Type X006 = AT(0, 6); - const Temp_Type X007 = AT(0, 7); - const Temp_Type X010 = D(F(0.415735f) * AT(1, 0) + F(0.791065f) * AT(3, 0) + F(-0.352443f) * AT(5, 0) + F(0.277785f) * AT(7, 0)); - const Temp_Type X011 = D(F(0.415735f) * AT(1, 1) + F(0.791065f) * AT(3, 1) + F(-0.352443f) * AT(5, 1) + F(0.277785f) * AT(7, 1)); - const Temp_Type X012 = D(F(0.415735f) * AT(1, 2) + F(0.791065f) * AT(3, 2) + F(-0.352443f) * AT(5, 2) + F(0.277785f) * AT(7, 2)); - const Temp_Type X013 = D(F(0.415735f) * AT(1, 3) + F(0.791065f) * AT(3, 3) + F(-0.352443f) * AT(5, 3) + F(0.277785f) * AT(7, 3)); - const Temp_Type X014 = D(F(0.415735f) * AT(1, 4) + F(0.791065f) * AT(3, 4) + F(-0.352443f) * AT(5, 4) + F(0.277785f) * AT(7, 4)); - const Temp_Type X015 = D(F(0.415735f) * AT(1, 5) + F(0.791065f) * AT(3, 5) + F(-0.352443f) * AT(5, 5) + F(0.277785f) * AT(7, 5)); - const Temp_Type X016 = D(F(0.415735f) * AT(1, 6) + F(0.791065f) * AT(3, 6) + F(-0.352443f) * AT(5, 6) + F(0.277785f) * AT(7, 6)); - const Temp_Type X017 = D(F(0.415735f) * AT(1, 7) + F(0.791065f) * AT(3, 7) + F(-0.352443f) * AT(5, 7) + F(0.277785f) * AT(7, 7)); - const Temp_Type X020 = AT(4, 0); - const Temp_Type X021 = AT(4, 1); - const Temp_Type X022 = AT(4, 2); - const Temp_Type X023 = AT(4, 3); - const Temp_Type X024 = AT(4, 4); - const Temp_Type X025 = AT(4, 5); - const Temp_Type X026 = AT(4, 6); - const Temp_Type X027 = AT(4, 7); - const Temp_Type X030 = D(F(0.022887f) * AT(1, 0) + F(-0.097545f) * AT(3, 0) + F(0.490393f) * AT(5, 0) + F(0.865723f) * AT(7, 0)); - const Temp_Type X031 = D(F(0.022887f) * AT(1, 1) + F(-0.097545f) * AT(3, 1) + F(0.490393f) * AT(5, 1) + F(0.865723f) * AT(7, 1)); - const Temp_Type X032 = D(F(0.022887f) * AT(1, 2) + F(-0.097545f) * AT(3, 2) + F(0.490393f) * AT(5, 2) + F(0.865723f) * AT(7, 2)); - const Temp_Type X033 = D(F(0.022887f) * AT(1, 3) + F(-0.097545f) * AT(3, 3) + F(0.490393f) * AT(5, 3) + F(0.865723f) * AT(7, 3)); - const Temp_Type X034 = D(F(0.022887f) * AT(1, 4) + F(-0.097545f) * AT(3, 4) + F(0.490393f) * AT(5, 4) + F(0.865723f) * AT(7, 4)); - const Temp_Type X035 = D(F(0.022887f) * AT(1, 5) + F(-0.097545f) * AT(3, 5) + F(0.490393f) * AT(5, 5) + F(0.865723f) * AT(7, 5)); - const Temp_Type X036 = D(F(0.022887f) * AT(1, 6) + F(-0.097545f) * AT(3, 6) + F(0.490393f) * AT(5, 6) + F(0.865723f) * AT(7, 6)); - const Temp_Type X037 = D(F(0.022887f) * AT(1, 7) + F(-0.097545f) * AT(3, 7) + F(0.490393f) * AT(5, 7) + F(0.865723f) * AT(7, 7)); - - // 4x4 = 4x8 times 8x4, matrix 1 is constant - P.at(0, 0) = X000; - P.at(0, 1) = D(X001 * F(0.415735f) + X003 * F(0.791065f) + X005 * F(-0.352443f) + X007 * F(0.277785f)); - P.at(0, 2) = X004; - P.at(0, 3) = D(X001 * F(0.022887f) + X003 * F(-0.097545f) + X005 * F(0.490393f) + X007 * F(0.865723f)); - P.at(1, 0) = X010; - P.at(1, 1) = D(X011 * F(0.415735f) + X013 * F(0.791065f) + X015 * F(-0.352443f) + X017 * F(0.277785f)); - P.at(1, 2) = X014; - P.at(1, 3) = D(X011 * F(0.022887f) + X013 * F(-0.097545f) + X015 * F(0.490393f) + X017 * F(0.865723f)); - P.at(2, 0) = X020; - P.at(2, 1) = D(X021 * F(0.415735f) + X023 * F(0.791065f) + X025 * F(-0.352443f) + X027 * F(0.277785f)); - P.at(2, 2) = X024; - P.at(2, 3) = D(X021 * F(0.022887f) + X023 * F(-0.097545f) + X025 * F(0.490393f) + X027 * F(0.865723f)); - P.at(3, 0) = X030; - P.at(3, 1) = D(X031 * F(0.415735f) + X033 * F(0.791065f) + X035 * F(-0.352443f) + X037 * F(0.277785f)); - P.at(3, 2) = X034; - P.at(3, 3) = D(X031 * F(0.022887f) + X033 * F(-0.097545f) + X035 * F(0.490393f) + X037 * F(0.865723f)); - // 40 muls 24 adds - - // 4x4 = 4x8 times 8x4, matrix 1 is constant - Q.at(0, 0) = D(X001 * F(0.906127f) + X003 * F(-0.318190f) + X005 * F(0.212608f) + X007 * F(-0.180240f)); - Q.at(0, 1) = X002; - Q.at(0, 2) = D(X001 * F(-0.074658f) + X003 * F(0.513280f) + X005 * F(0.768178f) + X007 * F(-0.375330f)); - Q.at(0, 3) = X006; - Q.at(1, 0) = D(X011 * F(0.906127f) + X013 * F(-0.318190f) + X015 * F(0.212608f) + X017 * F(-0.180240f)); - Q.at(1, 1) = X012; - Q.at(1, 2) = D(X011 * F(-0.074658f) + X013 * F(0.513280f) + X015 * F(0.768178f) + X017 * F(-0.375330f)); - Q.at(1, 3) = X016; - Q.at(2, 0) = D(X021 * F(0.906127f) + X023 * F(-0.318190f) + X025 * F(0.212608f) + X027 * F(-0.180240f)); - Q.at(2, 1) = X022; - Q.at(2, 2) = D(X021 * F(-0.074658f) + X023 * F(0.513280f) + X025 * F(0.768178f) + X027 * F(-0.375330f)); - Q.at(2, 3) = X026; - Q.at(3, 0) = D(X031 * F(0.906127f) + X033 * F(-0.318190f) + X035 * F(0.212608f) + X037 * F(-0.180240f)); - Q.at(3, 1) = X032; - Q.at(3, 2) = D(X031 * F(-0.074658f) + X033 * F(0.513280f) + X035 * F(0.768178f) + X037 * F(-0.375330f)); - Q.at(3, 3) = X036; - // 40 muls 24 adds - } - }; - - - template - struct R_S - { - static void calc(Matrix44& R, Matrix44& S, const jpgd_block_t* pSrc) - { - // 4x8 = 4x8 times 8x8, matrix 0 is constant - const Temp_Type X100 = D(F(0.906127f) * AT(1, 0) + F(-0.318190f) * AT(3, 0) + F(0.212608f) * AT(5, 0) + F(-0.180240f) * AT(7, 0)); - const Temp_Type X101 = D(F(0.906127f) * AT(1, 1) + F(-0.318190f) * AT(3, 1) + F(0.212608f) * AT(5, 1) + F(-0.180240f) * AT(7, 1)); - const Temp_Type X102 = D(F(0.906127f) * AT(1, 2) + F(-0.318190f) * AT(3, 2) + F(0.212608f) * AT(5, 2) + F(-0.180240f) * AT(7, 2)); - const Temp_Type X103 = D(F(0.906127f) * AT(1, 3) + F(-0.318190f) * AT(3, 3) + F(0.212608f) * AT(5, 3) + F(-0.180240f) * AT(7, 3)); - const Temp_Type X104 = D(F(0.906127f) * AT(1, 4) + F(-0.318190f) * AT(3, 4) + F(0.212608f) * AT(5, 4) + F(-0.180240f) * AT(7, 4)); - const Temp_Type X105 = D(F(0.906127f) * AT(1, 5) + F(-0.318190f) * AT(3, 5) + F(0.212608f) * AT(5, 5) + F(-0.180240f) * AT(7, 5)); - const Temp_Type X106 = D(F(0.906127f) * AT(1, 6) + F(-0.318190f) * AT(3, 6) + F(0.212608f) * AT(5, 6) + F(-0.180240f) * AT(7, 6)); - const Temp_Type X107 = D(F(0.906127f) * AT(1, 7) + F(-0.318190f) * AT(3, 7) + F(0.212608f) * AT(5, 7) + F(-0.180240f) * AT(7, 7)); - const Temp_Type X110 = AT(2, 0); - const Temp_Type X111 = AT(2, 1); - const Temp_Type X112 = AT(2, 2); - const Temp_Type X113 = AT(2, 3); - const Temp_Type X114 = AT(2, 4); - const Temp_Type X115 = AT(2, 5); - const Temp_Type X116 = AT(2, 6); - const Temp_Type X117 = AT(2, 7); - const Temp_Type X120 = D(F(-0.074658f) * AT(1, 0) + F(0.513280f) * AT(3, 0) + F(0.768178f) * AT(5, 0) + F(-0.375330f) * AT(7, 0)); - const Temp_Type X121 = D(F(-0.074658f) * AT(1, 1) + F(0.513280f) * AT(3, 1) + F(0.768178f) * AT(5, 1) + F(-0.375330f) * AT(7, 1)); - const Temp_Type X122 = D(F(-0.074658f) * AT(1, 2) + F(0.513280f) * AT(3, 2) + F(0.768178f) * AT(5, 2) + F(-0.375330f) * AT(7, 2)); - const Temp_Type X123 = D(F(-0.074658f) * AT(1, 3) + F(0.513280f) * AT(3, 3) + F(0.768178f) * AT(5, 3) + F(-0.375330f) * AT(7, 3)); - const Temp_Type X124 = D(F(-0.074658f) * AT(1, 4) + F(0.513280f) * AT(3, 4) + F(0.768178f) * AT(5, 4) + F(-0.375330f) * AT(7, 4)); - const Temp_Type X125 = D(F(-0.074658f) * AT(1, 5) + F(0.513280f) * AT(3, 5) + F(0.768178f) * AT(5, 5) + F(-0.375330f) * AT(7, 5)); - const Temp_Type X126 = D(F(-0.074658f) * AT(1, 6) + F(0.513280f) * AT(3, 6) + F(0.768178f) * AT(5, 6) + F(-0.375330f) * AT(7, 6)); - const Temp_Type X127 = D(F(-0.074658f) * AT(1, 7) + F(0.513280f) * AT(3, 7) + F(0.768178f) * AT(5, 7) + F(-0.375330f) * AT(7, 7)); - const Temp_Type X130 = AT(6, 0); - const Temp_Type X131 = AT(6, 1); - const Temp_Type X132 = AT(6, 2); - const Temp_Type X133 = AT(6, 3); - const Temp_Type X134 = AT(6, 4); - const Temp_Type X135 = AT(6, 5); - const Temp_Type X136 = AT(6, 6); - const Temp_Type X137 = AT(6, 7); - // 80 muls 48 adds - - // 4x4 = 4x8 times 8x4, matrix 1 is constant - R.at(0, 0) = X100; - R.at(0, 1) = D(X101 * F(0.415735f) + X103 * F(0.791065f) + X105 * F(-0.352443f) + X107 * F(0.277785f)); - R.at(0, 2) = X104; - R.at(0, 3) = D(X101 * F(0.022887f) + X103 * F(-0.097545f) + X105 * F(0.490393f) + X107 * F(0.865723f)); - R.at(1, 0) = X110; - R.at(1, 1) = D(X111 * F(0.415735f) + X113 * F(0.791065f) + X115 * F(-0.352443f) + X117 * F(0.277785f)); - R.at(1, 2) = X114; - R.at(1, 3) = D(X111 * F(0.022887f) + X113 * F(-0.097545f) + X115 * F(0.490393f) + X117 * F(0.865723f)); - R.at(2, 0) = X120; - R.at(2, 1) = D(X121 * F(0.415735f) + X123 * F(0.791065f) + X125 * F(-0.352443f) + X127 * F(0.277785f)); - R.at(2, 2) = X124; - R.at(2, 3) = D(X121 * F(0.022887f) + X123 * F(-0.097545f) + X125 * F(0.490393f) + X127 * F(0.865723f)); - R.at(3, 0) = X130; - R.at(3, 1) = D(X131 * F(0.415735f) + X133 * F(0.791065f) + X135 * F(-0.352443f) + X137 * F(0.277785f)); - R.at(3, 2) = X134; - R.at(3, 3) = D(X131 * F(0.022887f) + X133 * F(-0.097545f) + X135 * F(0.490393f) + X137 * F(0.865723f)); - // 40 muls 24 adds - // 4x4 = 4x8 times 8x4, matrix 1 is constant - S.at(0, 0) = D(X101 * F(0.906127f) + X103 * F(-0.318190f) + X105 * F(0.212608f) + X107 * F(-0.180240f)); - S.at(0, 1) = X102; - S.at(0, 2) = D(X101 * F(-0.074658f) + X103 * F(0.513280f) + X105 * F(0.768178f) + X107 * F(-0.375330f)); - S.at(0, 3) = X106; - S.at(1, 0) = D(X111 * F(0.906127f) + X113 * F(-0.318190f) + X115 * F(0.212608f) + X117 * F(-0.180240f)); - S.at(1, 1) = X112; - S.at(1, 2) = D(X111 * F(-0.074658f) + X113 * F(0.513280f) + X115 * F(0.768178f) + X117 * F(-0.375330f)); - S.at(1, 3) = X116; - S.at(2, 0) = D(X121 * F(0.906127f) + X123 * F(-0.318190f) + X125 * F(0.212608f) + X127 * F(-0.180240f)); - S.at(2, 1) = X122; - S.at(2, 2) = D(X121 * F(-0.074658f) + X123 * F(0.513280f) + X125 * F(0.768178f) + X127 * F(-0.375330f)); - S.at(2, 3) = X126; - S.at(3, 0) = D(X131 * F(0.906127f) + X133 * F(-0.318190f) + X135 * F(0.212608f) + X137 * F(-0.180240f)); - S.at(3, 1) = X132; - S.at(3, 2) = D(X131 * F(-0.074658f) + X133 * F(0.513280f) + X135 * F(0.768178f) + X137 * F(-0.375330f)); - S.at(3, 3) = X136; - // 40 muls 24 adds - } - }; -} // end namespace DCT_Upsample - - -// Unconditionally frees all allocated m_blocks. -void jpeg_decoder::free_all_blocks() -{ - delete(m_pStream); - m_pStream = nullptr; - - for (mem_block *b = m_pMem_blocks; b; ) { - mem_block *n = b->m_pNext; - free(b); - b = n; - } - m_pMem_blocks = nullptr; -} - - -// This method handles all errors. It will never return. -// It could easily be changed to use C++ exceptions. -JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status) -{ - m_error_code = status; - free_all_blocks(); - longjmp(m_jmp_state, status); -} - - -void *jpeg_decoder::alloc(size_t nSize, bool zero) -{ - nSize = (JPGD_MAX(nSize, 1) + 3) & ~3; - char *rv = nullptr; - for (mem_block *b = m_pMem_blocks; b; b = b->m_pNext) { - if ((b->m_used_count + nSize) <= b->m_size) { - rv = b->m_data + b->m_used_count; - b->m_used_count += nSize; - break; - } - } - if (!rv) { - int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047); - mem_block *b = (mem_block*)malloc(sizeof(mem_block) + capacity); - if (!b) stop_decoding(JPGD_NOTENOUGHMEM); - b->m_pNext = m_pMem_blocks; m_pMem_blocks = b; - b->m_used_count = nSize; - b->m_size = capacity; - rv = b->m_data; - } - if (zero) memset(rv, 0, nSize); - return rv; -} - - -void jpeg_decoder::word_clear(void *p, uint16_t c, uint32_t n) -{ - uint8_t *pD = (uint8_t*)p; - const uint8_t l = c & 0xFF, h = (c >> 8) & 0xFF; - while (n) { - pD[0] = l; pD[1] = h; pD += 2; - n--; - } -} - - -// Refill the input buffer. -// This method will sit in a loop until (A) the buffer is full or (B) -// the stream's read() method reports and end of file condition. -void jpeg_decoder::prep_in_buffer() -{ - m_in_buf_left = 0; - m_pIn_buf_ofs = m_in_buf; - - if (m_eof_flag) return; - - do { - int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag); - if (bytes_read == -1) stop_decoding(JPGD_STREAM_READ); - m_in_buf_left += bytes_read; - } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag)); - - m_total_bytes_read += m_in_buf_left; - - // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid). - // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.) - word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64); -} - - -// Read a Huffman code table. -void jpeg_decoder::read_dht_marker() -{ - int i, index, count; - uint8_t huff_num[17]; - uint8_t huff_val[256]; - uint32_t num_left = get_bits(16); - - if (num_left < 2) stop_decoding(JPGD_BAD_DHT_MARKER); - num_left -= 2; - - while (num_left) { - index = get_bits(8); - huff_num[0] = 0; - count = 0; - - for (i = 1; i <= 16; i++) { - huff_num[i] = static_cast(get_bits(8)); - count += huff_num[i]; - } - - if (count > 255) stop_decoding(JPGD_BAD_DHT_COUNTS); - - for (i = 0; i < count; i++) - huff_val[i] = static_cast(get_bits(8)); - - i = 1 + 16 + count; - - if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DHT_MARKER); - num_left -= i; - - if ((index & 0x10) > 0x10) stop_decoding(JPGD_BAD_DHT_INDEX); - index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1); - if (index >= JPGD_MAX_HUFF_TABLES) stop_decoding(JPGD_BAD_DHT_INDEX); - - if (!m_huff_num[index]) m_huff_num[index] = (uint8_t *)alloc(17); - if (!m_huff_val[index]) m_huff_val[index] = (uint8_t *)alloc(256); - - m_huff_ac[index] = (index & 0x10) != 0; - memcpy(m_huff_num[index], huff_num, 17); - memcpy(m_huff_val[index], huff_val, 256); - } -} - - -// Read a quantization table. -void jpeg_decoder::read_dqt_marker() -{ - int n, i, prec; - uint32_t temp; - uint32_t num_left = get_bits(16); - if (num_left < 2) stop_decoding(JPGD_BAD_DQT_MARKER); - num_left -= 2; - - while (num_left) { - n = get_bits(8); - prec = n >> 4; - n &= 0x0F; - - if (n >= JPGD_MAX_QUANT_TABLES) stop_decoding(JPGD_BAD_DQT_TABLE); - - if (!m_quant[n]) m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t)); - - // read quantization entries, in zag order - for (i = 0; i < 64; i++) { - temp = get_bits(8); - if (prec) temp = (temp << 8) + get_bits(8); - m_quant[n][i] = static_cast(temp); - } - i = 64 + 1; - if (prec) i += 64; - if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DQT_LENGTH); - num_left -= i; - } -} - - -// Read the start of frame (SOF) marker. -void jpeg_decoder::read_sof_marker() -{ - int i; - uint32_t num_left = get_bits(16); - - if (get_bits(8) != 8) stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */ - - m_image_y_size = get_bits(16); - if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) stop_decoding(JPGD_BAD_HEIGHT); - - m_image_x_size = get_bits(16); - if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) stop_decoding(JPGD_BAD_WIDTH); - - m_comps_in_frame = get_bits(8); - if (m_comps_in_frame > JPGD_MAX_COMPONENTS) stop_decoding(JPGD_TOO_MANY_COMPONENTS); - - if (num_left != (uint32_t)(m_comps_in_frame * 3 + 8)) stop_decoding(JPGD_BAD_SOF_LENGTH); - - for (i = 0; i < m_comps_in_frame; i++) { - m_comp_ident[i] = get_bits(8); - m_comp_h_samp[i] = get_bits(4); - m_comp_v_samp[i] = get_bits(4); - m_comp_quant[i] = get_bits(8); - } -} - - -// Used to skip unrecognized markers. -void jpeg_decoder::skip_variable_marker() -{ - uint32_t num_left = get_bits(16); - if (num_left < 2) stop_decoding(JPGD_BAD_VARIABLE_MARKER); - num_left -= 2; - - while (num_left) { - get_bits(8); - num_left--; - } -} - - -// Read a define restart interval (DRI) marker. -void jpeg_decoder::read_dri_marker() -{ - if (get_bits(16) != 4) stop_decoding(JPGD_BAD_DRI_LENGTH); - m_restart_interval = get_bits(16); -} - - -// Read a start of scan (SOS) marker. -void jpeg_decoder::read_sos_marker() -{ - int i, ci, c, cc; - uint32_t num_left = get_bits(16); - int n = get_bits(8); - - m_comps_in_scan = n; - num_left -= 3; - - if ( (num_left != (uint32_t)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) stop_decoding(JPGD_BAD_SOS_LENGTH); - - for (i = 0; i < n; i++) { - cc = get_bits(8); - c = get_bits(8); - num_left -= 2; - - for (ci = 0; ci < m_comps_in_frame; ci++) - if (cc == m_comp_ident[ci]) break; - - if (ci >= m_comps_in_frame) stop_decoding(JPGD_BAD_SOS_COMP_ID); - - m_comp_list[i] = ci; - m_comp_dc_tab[ci] = (c >> 4) & 15; - m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1); - } - m_spectral_start = get_bits(8); - m_spectral_end = get_bits(8); - m_successive_high = get_bits(4); - m_successive_low = get_bits(4); - - if (!m_progressive_flag) { - m_spectral_start = 0; - m_spectral_end = 63; - } - num_left -= 3; - - while (num_left) { /* read past whatever is num_left */ - get_bits(8); - num_left--; - } -} - - -// Finds the next marker. -int jpeg_decoder::next_marker() -{ - uint32_t c, bytes = 0; - - do { - do { - bytes++; - c = get_bits(8); - } while (c != 0xFF); - - do { - c = get_bits(8); - } while (c == 0xFF); - } while (c == 0); - - // If bytes > 0 here, there where extra bytes before the marker (not good). - return c; -} - - -// Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is -// encountered. -int jpeg_decoder::process_markers() -{ - int c; - - for ( ; ; ) { - c = next_marker(); - switch (c) { - case M_SOF0: - case M_SOF1: - case M_SOF2: - case M_SOF3: - case M_SOF5: - case M_SOF6: - case M_SOF7: - // case M_JPG: - case M_SOF9: - case M_SOF10: - case M_SOF11: - case M_SOF13: - case M_SOF14: - case M_SOF15: - case M_SOI: - case M_EOI: - case M_SOS: return c; - case M_DHT: { - read_dht_marker(); - break; - } - // No arithmitic support - dumb patents! - case M_DAC: { - stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT); - break; - } - case M_DQT: { - read_dqt_marker(); - break; - } - case M_DRI: { - read_dri_marker(); - break; - } - //case M_APP0: /* no need to read the JFIF marker */ - case M_JPG: - case M_RST0: /* no parameters */ - case M_RST1: - case M_RST2: - case M_RST3: - case M_RST4: - case M_RST5: - case M_RST6: - case M_RST7: - case M_TEM: { - stop_decoding(JPGD_UNEXPECTED_MARKER); - break; - } - default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ - skip_variable_marker(); - break; - } - } - } -} - - -// Finds the start of image (SOI) marker. -// This code is rather defensive: it only checks the first 512 bytes to avoid -// false positives. -void jpeg_decoder::locate_soi_marker() -{ - uint32_t lastchar = get_bits(8); - uint32_t thischar = get_bits(8); - - /* ok if it's a normal JPEG file without a special header */ - if ((lastchar == 0xFF) && (thischar == M_SOI)) return; - - uint32_t bytesleft = 4096; //512; - - while (true) { - if (--bytesleft == 0) stop_decoding(JPGD_NOT_JPEG); - - lastchar = thischar; - thischar = get_bits(8); - - if (lastchar == 0xFF) { - if (thischar == M_SOI) break; - else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end - } - } - - // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad. - thischar = (m_bit_buf >> 24) & 0xFF; - if (thischar != 0xFF) stop_decoding(JPGD_NOT_JPEG); -} - - -// Find a start of frame (SOF) marker. -void jpeg_decoder::locate_sof_marker() -{ - locate_soi_marker(); - int c = process_markers(); - - switch (c) { - case M_SOF2: m_progressive_flag = true; - case M_SOF0: /* baseline DCT */ - case M_SOF1: { /* extended sequential DCT */ - read_sof_marker(); - break; - } - case M_SOF9: { /* Arithmitic coding */ - stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT); - break; - } - default: { - stop_decoding(JPGD_UNSUPPORTED_MARKER); - break; - } - } -} - - -// Find a start of scan (SOS) marker. -int jpeg_decoder::locate_sos_marker() -{ - int c = process_markers(); - if (c == M_EOI) return false; - else if (c != M_SOS) stop_decoding(JPGD_UNEXPECTED_MARKER); - read_sos_marker(); - return true; -} - - -// Reset everything to default/uninitialized state. -void jpeg_decoder::init(jpeg_decoder_stream *pStream) -{ - m_pMem_blocks = nullptr; - m_error_code = JPGD_SUCCESS; - m_ready_flag = false; - m_image_x_size = m_image_y_size = 0; - m_pStream = pStream; - m_progressive_flag = false; - - memset(m_huff_ac, 0, sizeof(m_huff_ac)); - memset(m_huff_num, 0, sizeof(m_huff_num)); - memset(m_huff_val, 0, sizeof(m_huff_val)); - memset(m_quant, 0, sizeof(m_quant)); - - m_scan_type = 0; - m_comps_in_frame = 0; - - memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp)); - memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp)); - memset(m_comp_quant, 0, sizeof(m_comp_quant)); - memset(m_comp_ident, 0, sizeof(m_comp_ident)); - memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks)); - memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks)); - - m_comps_in_scan = 0; - memset(m_comp_list, 0, sizeof(m_comp_list)); - memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab)); - memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab)); - - m_spectral_start = 0; - m_spectral_end = 0; - m_successive_low = 0; - m_successive_high = 0; - m_max_mcu_x_size = 0; - m_max_mcu_y_size = 0; - m_blocks_per_mcu = 0; - m_max_blocks_per_row = 0; - m_mcus_per_row = 0; - m_mcus_per_col = 0; - m_expanded_blocks_per_component = 0; - m_expanded_blocks_per_mcu = 0; - m_expanded_blocks_per_row = 0; - m_freq_domain_chroma_upsample = false; - - memset(m_mcu_org, 0, sizeof(m_mcu_org)); - - m_total_lines_left = 0; - m_mcu_lines_left = 0; - m_real_dest_bytes_per_scan_line = 0; - m_dest_bytes_per_scan_line = 0; - m_dest_bytes_per_pixel = 0; - - memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs)); - - memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs)); - memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs)); - memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); - - m_eob_run = 0; - - memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); - - m_pIn_buf_ofs = m_in_buf; - m_in_buf_left = 0; - m_eof_flag = false; - m_tem_flag = 0; - - memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start)); - memset(m_in_buf, 0, sizeof(m_in_buf)); - memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end)); - - m_restart_interval = 0; - m_restarts_left = 0; - m_next_restart_num = 0; - - m_max_mcus_per_row = 0; - m_max_blocks_per_mcu = 0; - m_max_mcus_per_col = 0; - - memset(m_last_dc_val, 0, sizeof(m_last_dc_val)); - m_pMCU_coefficients = nullptr; - m_pSample_buf = nullptr; - - m_total_bytes_read = 0; - - m_pScan_line_0 = nullptr; - m_pScan_line_1 = nullptr; - - // Ready the input buffer. - prep_in_buffer(); - - // Prime the bit buffer. - m_bits_left = 16; - m_bit_buf = 0; - - get_bits(16); - get_bits(16); - - for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++) { - m_mcu_block_max_zag[i] = 64; - } -} - -#define SCALEBITS 16 -#define ONE_HALF ((int) 1 << (SCALEBITS-1)) -#define FIX(x) ((int) ((x) * (1L<> SCALEBITS; - m_cbb[i] = ( FIX(1.77200f) * k + ONE_HALF) >> SCALEBITS; - m_crg[i] = (-FIX(0.71414f)) * k; - m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF; - } -} - - -// This method throws back into the stream any bytes that where read -// into the bit buffer during initial marker scanning. -void jpeg_decoder::fix_in_buffer() -{ - // In case any 0xFF's where pulled into the buffer during marker scanning. - JPGD_ASSERT((m_bits_left & 7) == 0); - - if (m_bits_left == 16) stuff_char( (uint8_t)(m_bit_buf & 0xFF)); - if (m_bits_left >= 8) stuff_char( (uint8_t)((m_bit_buf >> 8) & 0xFF)); - - stuff_char((uint8_t)((m_bit_buf >> 16) & 0xFF)); - stuff_char((uint8_t)((m_bit_buf >> 24) & 0xFF)); - - m_bits_left = 16; - get_bits_no_markers(16); - get_bits_no_markers(16); -} - - -void jpeg_decoder::transform_mcu(int mcu_row) -{ - jpgd_block_t* pSrc_ptr = m_pMCU_coefficients; - uint8_t* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64; - - for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) { - idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]); - pSrc_ptr += 64; - pDst_ptr += 64; - } -} - - -static const uint8_t s_max_rc[64] = -{ - 17, 18, 34, 50, 50, 51, 52, 52, 52, 68, 84, 84, 84, 84, 85, 86, 86, 86, 86, 86, - 102, 118, 118, 118, 118, 118, 118, 119, 120, 120, 120, 120, 120, 120, 120, 136, - 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, - 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136 -}; - - -void jpeg_decoder::transform_mcu_expand(int mcu_row) -{ - jpgd_block_t* pSrc_ptr = m_pMCU_coefficients; - uint8_t* pDst_ptr = m_pSample_buf + mcu_row * m_expanded_blocks_per_mcu * 64; - - // Y IDCT - int mcu_block; - for (mcu_block = 0; mcu_block < m_expanded_blocks_per_component; mcu_block++) { - idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]); - pSrc_ptr += 64; - pDst_ptr += 64; - } - - // Chroma IDCT, with upsampling - jpgd_block_t temp_block[64]; - - for (int i = 0; i < 2; i++) { - DCT_Upsample::Matrix44 P, Q, R, S; - JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] >= 1); - JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] <= 64); - - int max_zag = m_mcu_block_max_zag[mcu_block++] - 1; - if (max_zag <= 0) max_zag = 0; // should never happen, only here to shut up static analysis - - switch (s_max_rc[max_zag]) { - case 1*16+1: - DCT_Upsample::P_Q<1, 1>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<1, 1>::calc(R, S, pSrc_ptr); - break; - case 1*16+2: - DCT_Upsample::P_Q<1, 2>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<1, 2>::calc(R, S, pSrc_ptr); - break; - case 2*16+2: - DCT_Upsample::P_Q<2, 2>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<2, 2>::calc(R, S, pSrc_ptr); - break; - case 3*16+2: - DCT_Upsample::P_Q<3, 2>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<3, 2>::calc(R, S, pSrc_ptr); - break; - case 3*16+3: - DCT_Upsample::P_Q<3, 3>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<3, 3>::calc(R, S, pSrc_ptr); - break; - case 3*16+4: - DCT_Upsample::P_Q<3, 4>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<3, 4>::calc(R, S, pSrc_ptr); - break; - case 4*16+4: - DCT_Upsample::P_Q<4, 4>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<4, 4>::calc(R, S, pSrc_ptr); - break; - case 5*16+4: - DCT_Upsample::P_Q<5, 4>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<5, 4>::calc(R, S, pSrc_ptr); - break; - case 5*16+5: - DCT_Upsample::P_Q<5, 5>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<5, 5>::calc(R, S, pSrc_ptr); - break; - case 5*16+6: - DCT_Upsample::P_Q<5, 6>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<5, 6>::calc(R, S, pSrc_ptr); - break; - case 6*16+6: - DCT_Upsample::P_Q<6, 6>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<6, 6>::calc(R, S, pSrc_ptr); - break; - case 7*16+6: - DCT_Upsample::P_Q<7, 6>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<7, 6>::calc(R, S, pSrc_ptr); - break; - case 7*16+7: - DCT_Upsample::P_Q<7, 7>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<7, 7>::calc(R, S, pSrc_ptr); - break; - case 7*16+8: - DCT_Upsample::P_Q<7, 8>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<7, 8>::calc(R, S, pSrc_ptr); - break; - case 8*16+8: - DCT_Upsample::P_Q<8, 8>::calc(P, Q, pSrc_ptr); - DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr); - break; - default: - JPGD_ASSERT(false); - } - DCT_Upsample::Matrix44 a(P + Q); P -= Q; - DCT_Upsample::Matrix44& b = P; - DCT_Upsample::Matrix44 c(R + S); R -= S; - DCT_Upsample::Matrix44& d = R; - - DCT_Upsample::Matrix44::add_and_store(temp_block, a, c); - idct_4x4(temp_block, pDst_ptr); - pDst_ptr += 64; - - DCT_Upsample::Matrix44::sub_and_store(temp_block, a, c); - idct_4x4(temp_block, pDst_ptr); - pDst_ptr += 64; - - DCT_Upsample::Matrix44::add_and_store(temp_block, b, d); - idct_4x4(temp_block, pDst_ptr); - pDst_ptr += 64; - - DCT_Upsample::Matrix44::sub_and_store(temp_block, b, d); - idct_4x4(temp_block, pDst_ptr); - pDst_ptr += 64; - pSrc_ptr += 64; - } -} - - -// Loads and dequantizes the next row of (already decoded) coefficients. -// Progressive images only. -void jpeg_decoder::load_next_row() -{ - int i; - jpgd_block_t *p; - jpgd_quant_t *q; - int mcu_row, mcu_block, row_block = 0; - int component_num, component_id; - int block_x_mcu[JPGD_MAX_COMPONENTS]; - - memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int)); - - for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) { - int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; - - for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) { - component_id = m_mcu_org[mcu_block]; - q = m_quant[m_comp_quant[component_id]]; - p = m_pMCU_coefficients + 64 * mcu_block; - - jpgd_block_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); - jpgd_block_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); - p[0] = pDC[0]; - memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t)); - - for (i = 63; i > 0; i--) { - if (p[g_ZAG[i]]) break; - } - - m_mcu_block_max_zag[mcu_block] = i + 1; - - for ( ; i >= 0; i--) { - if (p[g_ZAG[i]]) { - p[g_ZAG[i]] = static_cast(p[g_ZAG[i]] * q[i]); - } - } - - row_block++; - - if (m_comps_in_scan == 1) block_x_mcu[component_id]++; - else { - if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) block_x_mcu_ofs = 0; - if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) { - block_y_mcu_ofs = 0; - block_x_mcu[component_id] += m_comp_h_samp[component_id]; - } - } - } - if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row); - else transform_mcu(mcu_row); - } - if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++; - else { - for (component_num = 0; component_num < m_comps_in_scan; component_num++) { - component_id = m_comp_list[component_num]; - m_block_y_mcu[component_id] += m_comp_v_samp[component_id]; - } - } -} - - -// Restart interval processing. -void jpeg_decoder::process_restart() -{ - int i; - int c = 0; - - // Align to a byte boundry - // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers! - //get_bits_no_markers(m_bits_left & 7); - - // Let's scan a little bit to find the marker, but not _too_ far. - // 1536 is a "fudge factor" that determines how much to scan. - for (i = 1536; i > 0; i--) { - if (get_char() == 0xFF) break; - } - if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER); - - for ( ; i > 0; i--) { - if ((c = get_char()) != 0xFF) break; - } - if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER); - - // Is it the expected marker? If not, something bad happened. - if (c != (m_next_restart_num + M_RST0)) stop_decoding(JPGD_BAD_RESTART_MARKER); - - // Reset each component's DC prediction values. - memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t)); - - m_eob_run = 0; - m_restarts_left = m_restart_interval; - m_next_restart_num = (m_next_restart_num + 1) & 7; - - // Get the bit buffer going again... - m_bits_left = 16; - get_bits_no_markers(16); - get_bits_no_markers(16); -} - - -static inline int dequantize_ac(int c, int q) -{ - c *= q; - return c; -} - -// Decodes and dequantizes the next row of coefficients. -void jpeg_decoder::decode_next_row() -{ - int row_block = 0; - - for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) { - if ((m_restart_interval) && (m_restarts_left == 0)) process_restart(); - - jpgd_block_t* p = m_pMCU_coefficients; - - for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64) { - int component_id = m_mcu_org[mcu_block]; - jpgd_quant_t* q = m_quant[m_comp_quant[component_id]]; - - int r, s; - s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r); - s = JPGD_HUFF_EXTEND(r, s); - - m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]); - - p[0] = static_cast(s * q[0]); - - int prev_num_set = m_mcu_block_max_zag[mcu_block]; - huff_tables *pH = m_pHuff_tabs[m_comp_ac_tab[component_id]]; - int k; - for (k = 1; k < 64; k++) { - int extra_bits; - s = huff_decode(pH, extra_bits); - r = s >> 4; - s &= 15; - - if (s) { - if (r) { - if ((k + r) > 63) stop_decoding(JPGD_DECODE_ERROR); - if (k < prev_num_set) { - int n = JPGD_MIN(r, prev_num_set - k); - int kt = k; - while (n--) p[g_ZAG[kt++]] = 0; - } - k += r; - } - s = JPGD_HUFF_EXTEND(extra_bits, s); - JPGD_ASSERT(k < 64); - p[g_ZAG[k]] = static_cast(dequantize_ac(s, q[k])); //s * q[k]; - } else { - if (r == 15) { - if ((k + 16) > 64) stop_decoding(JPGD_DECODE_ERROR); - if (k < prev_num_set) { - int n = JPGD_MIN(16, prev_num_set - k); - int kt = k; - while (n--) { - JPGD_ASSERT(kt <= 63); - p[g_ZAG[kt++]] = 0; - } - } - k += 16 - 1; // - 1 because the loop counter is k - JPGD_ASSERT(p[g_ZAG[k]] == 0); - } else break; - } - } - - if (k < prev_num_set) { - int kt = k; - while (kt < prev_num_set) p[g_ZAG[kt++]] = 0; - } - - m_mcu_block_max_zag[mcu_block] = k; - row_block++; - } - if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row); - else transform_mcu(mcu_row); - m_restarts_left--; - } -} - - -// YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB -void jpeg_decoder::H1V1Convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8_t *d = m_pScan_line_0; - uint8_t *s = m_pSample_buf + row * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) { - for (int j = 0; j < 8; j++) { - int y = s[j]; - int cb = s[64+j]; - int cr = s[128+j]; - - d[0] = clamp(y + m_crr[cr]); - d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16)); - d[2] = clamp(y + m_cbb[cb]); - d[3] = 255; - d += 4; - } - s += 64*3; - } -} - - -// YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB -void jpeg_decoder::H2V1Convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8_t *d0 = m_pScan_line_0; - uint8_t *y = m_pSample_buf + row * 8; - uint8_t *c = m_pSample_buf + 2*64 + row * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) { - for (int l = 0; l < 2; l++) { - for (int j = 0; j < 4; j++) { - int cb = c[0]; - int cr = c[64]; - - int rc = m_crr[cr]; - int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); - int bc = m_cbb[cb]; - - int yy = y[j<<1]; - d0[0] = clamp(yy+rc); - d0[1] = clamp(yy+gc); - d0[2] = clamp(yy+bc); - d0[3] = 255; - - yy = y[(j<<1)+1]; - d0[4] = clamp(yy+rc); - d0[5] = clamp(yy+gc); - d0[6] = clamp(yy+bc); - d0[7] = 255; - d0 += 8; - c++; - } - y += 64; - } - y += 64*4 - 64*2; - c += 64*4 - 8; - } -} - - -// YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB -void jpeg_decoder::H1V2Convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8_t *d0 = m_pScan_line_0; - uint8_t *d1 = m_pScan_line_1; - uint8_t *y; - uint8_t *c; - - if (row < 8) y = m_pSample_buf + row * 8; - else y = m_pSample_buf + 64*1 + (row & 7) * 8; - - c = m_pSample_buf + 64*2 + (row >> 1) * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) { - for (int j = 0; j < 8; j++) { - int cb = c[0+j]; - int cr = c[64+j]; - - int rc = m_crr[cr]; - int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); - int bc = m_cbb[cb]; - - int yy = y[j]; - d0[0] = clamp(yy+rc); - d0[1] = clamp(yy+gc); - d0[2] = clamp(yy+bc); - d0[3] = 255; - - yy = y[8+j]; - d1[0] = clamp(yy+rc); - d1[1] = clamp(yy+gc); - d1[2] = clamp(yy+bc); - d1[3] = 255; - - d0 += 4; - d1 += 4; - } - y += 64*4; - c += 64*4; - } -} - - -// YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB -void jpeg_decoder::H2V2Convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8_t *d0 = m_pScan_line_0; - uint8_t *d1 = m_pScan_line_1; - uint8_t *y; - uint8_t *c; - - if (row < 8) y = m_pSample_buf + row * 8; - else y = m_pSample_buf + 64*2 + (row & 7) * 8; - - c = m_pSample_buf + 64*4 + (row >> 1) * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) { - for (int l = 0; l < 2; l++) { - for (int j = 0; j < 8; j += 2) { - int cb = c[0]; - int cr = c[64]; - - int rc = m_crr[cr]; - int gc = ((m_crg[cr] + m_cbg[cb]) >> 16); - int bc = m_cbb[cb]; - - int yy = y[j]; - d0[0] = clamp(yy+rc); - d0[1] = clamp(yy+gc); - d0[2] = clamp(yy+bc); - d0[3] = 255; - - yy = y[j+1]; - d0[4] = clamp(yy+rc); - d0[5] = clamp(yy+gc); - d0[6] = clamp(yy+bc); - d0[7] = 255; - - yy = y[j+8]; - d1[0] = clamp(yy+rc); - d1[1] = clamp(yy+gc); - d1[2] = clamp(yy+bc); - d1[3] = 255; - - yy = y[j+8+1]; - d1[4] = clamp(yy+rc); - d1[5] = clamp(yy+gc); - d1[6] = clamp(yy+bc); - d1[7] = 255; - - d0 += 8; - d1 += 8; - - c++; - } - y += 64; - } - y += 64*6 - 64*2; - c += 64*6 - 8; - } -} - - -// Y (1 block per MCU) to 8-bit grayscale -void jpeg_decoder::gray_convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8_t *d = m_pScan_line_0; - uint8_t *s = m_pSample_buf + row * 8; - - for (int i = m_max_mcus_per_row; i > 0; i--) { - *(uint32_t *)d = *(uint32_t *)s; - *(uint32_t *)(&d[4]) = *(uint32_t *)(&s[4]); - s += 64; - d += 8; - } -} - - -void jpeg_decoder::expanded_convert() -{ - int row = m_max_mcu_y_size - m_mcu_lines_left; - uint8_t* Py = m_pSample_buf + (row / 8) * 64 * m_comp_h_samp[0] + (row & 7) * 8; - uint8_t* d = m_pScan_line_0; - - for (int i = m_max_mcus_per_row; i > 0; i--) { - for (int k = 0; k < m_max_mcu_x_size; k += 8) { - const int Y_ofs = k * 8; - const int Cb_ofs = Y_ofs + 64 * m_expanded_blocks_per_component; - const int Cr_ofs = Y_ofs + 64 * m_expanded_blocks_per_component * 2; - for (int j = 0; j < 8; j++) { - int y = Py[Y_ofs + j]; - int cb = Py[Cb_ofs + j]; - int cr = Py[Cr_ofs + j]; - - d[0] = clamp(y + m_crr[cr]); - d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16)); - d[2] = clamp(y + m_cbb[cb]); - d[3] = 255; - - d += 4; - } - } - Py += 64 * m_expanded_blocks_per_mcu; - } -} - - -// Find end of image (EOI) marker, so we can return to the user the exact size of the input stream. -void jpeg_decoder::find_eoi() -{ - if (!m_progressive_flag) { - // Attempt to read the EOI marker. - //get_bits_no_markers(m_bits_left & 7); - - // Prime the bit buffer - m_bits_left = 16; - get_bits(16); - get_bits(16); - - // The next marker _should_ be EOI - process_markers(); - } - m_total_bytes_read -= m_in_buf_left; -} - - -int jpeg_decoder::decode(const void** pScan_line, uint32_t* pScan_line_len) -{ - if ((m_error_code) || (!m_ready_flag)) return JPGD_FAILED; - if (m_total_lines_left == 0) return JPGD_DONE; - if (m_mcu_lines_left == 0) { - if (setjmp(m_jmp_state)) return JPGD_FAILED; - if (m_progressive_flag) load_next_row(); - else decode_next_row(); - // Find the EOI marker if that was the last row. - if (m_total_lines_left <= m_max_mcu_y_size) find_eoi(); - m_mcu_lines_left = m_max_mcu_y_size; - } - - if (m_freq_domain_chroma_upsample) { - expanded_convert(); - *pScan_line = m_pScan_line_0; - } else { - switch (m_scan_type) { - case JPGD_YH2V2: { - if ((m_mcu_lines_left & 1) == 0) { - H2V2Convert(); - *pScan_line = m_pScan_line_0; - } - else *pScan_line = m_pScan_line_1; - break; - } - case JPGD_YH2V1: { - H2V1Convert(); - *pScan_line = m_pScan_line_0; - break; - } - case JPGD_YH1V2: { - if ((m_mcu_lines_left & 1) == 0) { - H1V2Convert(); - *pScan_line = m_pScan_line_0; - } else *pScan_line = m_pScan_line_1; - break; - } - case JPGD_YH1V1: { - H1V1Convert(); - *pScan_line = m_pScan_line_0; - break; - } - case JPGD_GRAYSCALE: { - gray_convert(); - *pScan_line = m_pScan_line_0; - break; - } - } - } - - *pScan_line_len = m_real_dest_bytes_per_scan_line; - m_mcu_lines_left--; - m_total_lines_left--; - - return JPGD_SUCCESS; -} - - -// Creates the tables needed for efficient Huffman decoding. -void jpeg_decoder::make_huff_table(int index, huff_tables *pH) -{ - int p, i, l, si; - uint8_t huffsize[257]; - uint32_t huffcode[257]; - uint32_t code; - uint32_t subtree; - int code_size; - int lastp; - int nextfreeentry; - int currententry; - - pH->ac_table = m_huff_ac[index] != 0; - p = 0; - - for (l = 1; l <= 16; l++) { - for (i = 1; i <= m_huff_num[index][l]; i++) { - huffsize[p++] = static_cast(l); - } - } - - huffsize[p] = 0; - lastp = p; - code = 0; - si = huffsize[0]; - p = 0; - - while (huffsize[p]) { - while (huffsize[p] == si) { - huffcode[p++] = code; - code++; - } - code <<= 1; - si++; - } - - memset(pH->look_up, 0, sizeof(pH->look_up)); - memset(pH->look_up2, 0, sizeof(pH->look_up2)); - memset(pH->tree, 0, sizeof(pH->tree)); - memset(pH->code_size, 0, sizeof(pH->code_size)); - - nextfreeentry = -1; - p = 0; - - while (p < lastp) { - i = m_huff_val[index][p]; - code = huffcode[p]; - code_size = huffsize[p]; - pH->code_size[i] = static_cast(code_size); - - if (code_size <= 8) { - code <<= (8 - code_size); - for (l = 1 << (8 - code_size); l > 0; l--) { - JPGD_ASSERT(i < 256); - pH->look_up[code] = i; - bool has_extrabits = false; - int extra_bits = 0; - int num_extra_bits = i & 15; - int bits_to_fetch = code_size; - - if (num_extra_bits) { - int total_codesize = code_size + num_extra_bits; - if (total_codesize <= 8) { - has_extrabits = true; - extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize)); - JPGD_ASSERT(extra_bits <= 0x7FFF); - bits_to_fetch += num_extra_bits; - } - } - if (!has_extrabits) pH->look_up2[code] = i | (bits_to_fetch << 8); - else pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8); - code++; - } - } else { - subtree = (code >> (code_size - 8)) & 0xFF; - currententry = pH->look_up[subtree]; - - if (currententry == 0) { - pH->look_up[subtree] = currententry = nextfreeentry; - pH->look_up2[subtree] = currententry = nextfreeentry; - nextfreeentry -= 2; - } - - code <<= (16 - (code_size - 8)); - - for (l = code_size; l > 9; l--) { - if ((code & 0x8000) == 0) currententry--; - if (pH->tree[-currententry - 1] == 0) { - pH->tree[-currententry - 1] = nextfreeentry; - currententry = nextfreeentry; - nextfreeentry -= 2; - } else currententry = pH->tree[-currententry - 1]; - code <<= 1; - } - if ((code & 0x8000) == 0) currententry--; - pH->tree[-currententry - 1] = i; - } - p++; - } -} - - -// Verifies the quantization tables needed for this scan are available. -void jpeg_decoder::check_quant_tables() -{ - for (int i = 0; i < m_comps_in_scan; i++) { - if (m_quant[m_comp_quant[m_comp_list[i]]] == nullptr) stop_decoding(JPGD_UNDEFINED_QUANT_TABLE); - } -} - - -// Verifies that all the Huffman tables needed for this scan are available. -void jpeg_decoder::check_huff_tables() -{ - for (int i = 0; i < m_comps_in_scan; i++) { - if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE); - if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE); - } - - for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++) { - if (m_huff_num[i]) { - if (!m_pHuff_tabs[i]) m_pHuff_tabs[i] = (huff_tables *)alloc(sizeof(huff_tables)); - make_huff_table(i, m_pHuff_tabs[i]); - } - } -} - - -// Determines the component order inside each MCU. -// Also calcs how many MCU's are on each row, etc. -void jpeg_decoder::calc_mcu_block_order() -{ - int component_num, component_id; - int max_h_samp = 0, max_v_samp = 0; - - for (component_id = 0; component_id < m_comps_in_frame; component_id++) { - if (m_comp_h_samp[component_id] > max_h_samp) { - max_h_samp = m_comp_h_samp[component_id]; - } - if (m_comp_v_samp[component_id] > max_v_samp) { - max_v_samp = m_comp_v_samp[component_id]; - } - } - - for (component_id = 0; component_id < m_comps_in_frame; component_id++) { - m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8; - m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8; - } - - if (m_comps_in_scan == 1) { - m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]]; - m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]]; - } else { - m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp; - m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp; - } - - if (m_comps_in_scan == 1) { - m_mcu_org[0] = m_comp_list[0]; - m_blocks_per_mcu = 1; - } else { - m_blocks_per_mcu = 0; - - for (component_num = 0; component_num < m_comps_in_scan; component_num++) { - int num_blocks; - component_id = m_comp_list[component_num]; - num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id]; - while (num_blocks--) m_mcu_org[m_blocks_per_mcu++] = component_id; - } - } -} - - -// Starts a new scan. -int jpeg_decoder::init_scan() -{ - if (!locate_sos_marker()) return false; - - calc_mcu_block_order(); - check_huff_tables(); - check_quant_tables(); - - memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t)); - - m_eob_run = 0; - - if (m_restart_interval) { - m_restarts_left = m_restart_interval; - m_next_restart_num = 0; - } - fix_in_buffer(); - return true; -} - - -// Starts a frame. Determines if the number of components or sampling factors -// are supported. -void jpeg_decoder::init_frame() -{ - int i; - - if (m_comps_in_frame == 1) { - if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); - m_scan_type = JPGD_GRAYSCALE; - m_max_blocks_per_mcu = 1; - m_max_mcu_x_size = 8; - m_max_mcu_y_size = 8; - } else if (m_comps_in_frame == 3) { - if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1))) - stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); - - if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) { - m_scan_type = JPGD_YH1V1; - m_max_blocks_per_mcu = 3; - m_max_mcu_x_size = 8; - m_max_mcu_y_size = 8; - } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1)) { - m_scan_type = JPGD_YH2V1; - m_max_blocks_per_mcu = 4; - m_max_mcu_x_size = 16; - m_max_mcu_y_size = 8; - } else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2)) { - m_scan_type = JPGD_YH1V2; - m_max_blocks_per_mcu = 4; - m_max_mcu_x_size = 8; - m_max_mcu_y_size = 16; - } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2)) { - m_scan_type = JPGD_YH2V2; - m_max_blocks_per_mcu = 6; - m_max_mcu_x_size = 16; - m_max_mcu_y_size = 16; - } else stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); - } else stop_decoding(JPGD_UNSUPPORTED_COLORSPACE); - - m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size; - m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size; - - // These values are for the *destination* pixels: after conversion. - if (m_scan_type == JPGD_GRAYSCALE) m_dest_bytes_per_pixel = 1; - else m_dest_bytes_per_pixel = 4; - - m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel; - m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel); - - // Initialize two scan line buffers. - m_pScan_line_0 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true); - if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2)) { - m_pScan_line_1 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true); - } - - m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu; - - // Should never happen - if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) stop_decoding(JPGD_ASSERTION_ERROR); - - // Allocate the coefficient buffer, enough for one MCU - m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t)); - - for (i = 0; i < m_max_blocks_per_mcu; i++) { - m_mcu_block_max_zag[i] = 64; - } - - m_expanded_blocks_per_component = m_comp_h_samp[0] * m_comp_v_samp[0]; - m_expanded_blocks_per_mcu = m_expanded_blocks_per_component * m_comps_in_frame; - m_expanded_blocks_per_row = m_max_mcus_per_row * m_expanded_blocks_per_mcu; - // Freq. domain chroma upsampling is only supported for H2V2 subsampling factor (the most common one I've seen). - m_freq_domain_chroma_upsample = false; -#if JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING - m_freq_domain_chroma_upsample = (m_expanded_blocks_per_mcu == 4*3); -#endif - - if (m_freq_domain_chroma_upsample) - m_pSample_buf = (uint8_t *)alloc(m_expanded_blocks_per_row * 64); - else - m_pSample_buf = (uint8_t *)alloc(m_max_blocks_per_row * 64); - - m_total_lines_left = m_image_y_size; - m_mcu_lines_left = 0; - create_look_ups(); -} - - -// The coeff_buf series of methods originally stored the coefficients -// into a "virtual" file which was located in EMS, XMS, or a disk file. A cache -// was used to make this process more efficient. Now, we can store the entire -// thing in RAM. -jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y) -{ - coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf)); - cb->block_num_x = block_num_x; - cb->block_num_y = block_num_y; - cb->block_len_x = block_len_x; - cb->block_len_y = block_len_y; - cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_t); - cb->pData = (uint8_t *)alloc(cb->block_size * block_num_x * block_num_y, true); - return cb; -} - - -inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, int block_y) -{ - JPGD_ASSERT((block_x < cb->block_num_x) && (block_y < cb->block_num_y)); - return (jpgd_block_t *)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x)); -} - - -// The following methods decode the various types of m_blocks encountered -// in progressively encoded images. -void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y) -{ - int s, r; - jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y); - - if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0) { - r = pD->get_bits_no_markers(s); - s = JPGD_HUFF_EXTEND(r, s); - } - pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]); - p[0] = static_cast(static_cast(s) << pD->m_successive_low); -} - - -void jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y) -{ - if (pD->get_bits_no_markers(1)) { - jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y); - p[0] |= (1 << pD->m_successive_low); - } -} - - -void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y) -{ - int k, s, r; - - if (pD->m_eob_run) { - pD->m_eob_run--; - return; - } - jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); - - for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++) { - s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]); - r = s >> 4; - s &= 15; - if (s) { - if ((k += r) > 63) pD->stop_decoding(JPGD_DECODE_ERROR); - r = pD->get_bits_no_markers(s); - s = JPGD_HUFF_EXTEND(r, s); - p[g_ZAG[k]] = static_cast(static_cast(s) << pD->m_successive_low); - } else { - if (r == 15) { - if ((k += 15) > 63) pD->stop_decoding(JPGD_DECODE_ERROR); - } else { - pD->m_eob_run = 1 << r; - if (r) pD->m_eob_run += pD->get_bits_no_markers(r); - pD->m_eob_run--; - break; - } - } - } -} - - -void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y) -{ - int s, k, r; - int p1 = 1 << pD->m_successive_low; - int m1 = static_cast(-1) << pD->m_successive_low; - jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y); - - JPGD_ASSERT(pD->m_spectral_end <= 63); - - k = pD->m_spectral_start; - - if (pD->m_eob_run == 0) { - for ( ; k <= pD->m_spectral_end; k++) { - s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]); - r = s >> 4; - s &= 15; - if (s) { - if (s != 1) pD->stop_decoding(JPGD_DECODE_ERROR); - if (pD->get_bits_no_markers(1)) s = p1; - else s = m1; - } else { - if (r != 15) { - pD->m_eob_run = 1 << r; - if (r) pD->m_eob_run += pD->get_bits_no_markers(r); - break; - } - } - - do { - jpgd_block_t *this_coef = p + g_ZAG[k & 63]; - - if (*this_coef != 0) { - if (pD->get_bits_no_markers(1)) { - if ((*this_coef & p1) == 0) { - if (*this_coef >= 0) *this_coef = static_cast(*this_coef + p1); - else *this_coef = static_cast(*this_coef + m1); - } - } - } else { - if (--r < 0) break; - } - k++; - } while (k <= pD->m_spectral_end); - - if ((s) && (k < 64)) { - p[g_ZAG[k]] = static_cast(s); - } - } - } - - if (pD->m_eob_run > 0) { - for ( ; k <= pD->m_spectral_end; k++) { - jpgd_block_t *this_coef = p + g_ZAG[k & 63]; // logical AND to shut up static code analysis - - if (*this_coef != 0) { - if (pD->get_bits_no_markers(1)) { - if ((*this_coef & p1) == 0) { - if (*this_coef >= 0) *this_coef = static_cast(*this_coef + p1); - else *this_coef = static_cast(*this_coef + m1); - } - } - } - } - pD->m_eob_run--; - } -} - - -// Decode a scan in a progressively encoded image. -void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func) -{ - int mcu_row, mcu_col, mcu_block; - int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS]; - - memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu)); - - for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++) { - int component_num, component_id; - memset(block_x_mcu, 0, sizeof(block_x_mcu)); - - for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) { - int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; - - if ((m_restart_interval) && (m_restarts_left == 0)) process_restart(); - - for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) { - component_id = m_mcu_org[mcu_block]; - decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs); - - if (m_comps_in_scan == 1) block_x_mcu[component_id]++; - else { - if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) { - block_x_mcu_ofs = 0; - - if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) { - block_y_mcu_ofs = 0; - block_x_mcu[component_id] += m_comp_h_samp[component_id]; - } - } - } - } - m_restarts_left--; - } - - if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++; - else { - for (component_num = 0; component_num < m_comps_in_scan; component_num++) { - component_id = m_comp_list[component_num]; - m_block_y_mcu[component_id] += m_comp_v_samp[component_id]; - } - } - } -} - - -// Decode a progressively encoded image. -void jpeg_decoder::init_progressive() -{ - int i; - - if (m_comps_in_frame == 4) stop_decoding(JPGD_UNSUPPORTED_COLORSPACE); - - // Allocate the coefficient buffers. - for (i = 0; i < m_comps_in_frame; i++) { - m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1); - m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8); - } - - while (true) { - int dc_only_scan, refinement_scan; - pDecode_block_func decode_block_func; - - if (!init_scan()) break; - - dc_only_scan = (m_spectral_start == 0); - refinement_scan = (m_successive_high != 0); - - if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) stop_decoding(JPGD_BAD_SOS_SPECTRAL); - - if (dc_only_scan) { - if (m_spectral_end) stop_decoding(JPGD_BAD_SOS_SPECTRAL); - } else if (m_comps_in_scan != 1) { /* AC scans can only contain one component */ - stop_decoding(JPGD_BAD_SOS_SPECTRAL); - } - - if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) stop_decoding(JPGD_BAD_SOS_SUCCESSIVE); - - if (dc_only_scan) { - if (refinement_scan) decode_block_func = decode_block_dc_refine; - else decode_block_func = decode_block_dc_first; - } else { - if (refinement_scan) decode_block_func = decode_block_ac_refine; - else decode_block_func = decode_block_ac_first; - } - decode_scan(decode_block_func); - m_bits_left = 16; - get_bits(16); - get_bits(16); - } - - m_comps_in_scan = m_comps_in_frame; - - for (i = 0; i < m_comps_in_frame; i++) { - m_comp_list[i] = i; - } - - calc_mcu_block_order(); -} - - -void jpeg_decoder::init_sequential() -{ - if (!init_scan()) stop_decoding(JPGD_UNEXPECTED_MARKER); -} - - -void jpeg_decoder::decode_start() -{ - init_frame(); - if (m_progressive_flag) init_progressive(); - else init_sequential(); -} - - -void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream) -{ - init(pStream); - locate_sof_marker(); -} - - -jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream) -{ - if (setjmp(m_jmp_state)) return; - decode_init(pStream); -} - - -int jpeg_decoder::begin_decoding() -{ - if (m_ready_flag) return JPGD_SUCCESS; - if (m_error_code) return JPGD_FAILED; - if (setjmp(m_jmp_state)) return JPGD_FAILED; - - decode_start(); - m_ready_flag = true; - - return JPGD_SUCCESS; -} - - -jpeg_decoder::~jpeg_decoder() -{ - free_all_blocks(); -} - - -void jpeg_decoder_file_stream::close() -{ - if (m_pFile) { - fclose(m_pFile); - m_pFile = nullptr; - } - m_eof_flag = false; - m_error_flag = false; -} - - -jpeg_decoder_file_stream::~jpeg_decoder_file_stream() -{ - close(); -} - - -bool jpeg_decoder_file_stream::open(const char *Pfilename) -{ - close(); - - m_eof_flag = false; - m_error_flag = false; - -#if defined(_MSC_VER) - m_pFile = nullptr; - fopen_s(&m_pFile, Pfilename, "rb"); -#else - m_pFile = fopen(Pfilename, "rb"); -#endif - return m_pFile != nullptr; -} - - -int jpeg_decoder_file_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag) -{ - if (!m_pFile) return -1; - - if (m_eof_flag) { - *pEOF_flag = true; - return 0; - } - - if (m_error_flag) return -1; - - int bytes_read = static_cast(fread(pBuf, 1, max_bytes_to_read, m_pFile)); - if (bytes_read < max_bytes_to_read) { - if (ferror(m_pFile)) { - m_error_flag = true; - return -1; - } - m_eof_flag = true; - *pEOF_flag = true; - } - return bytes_read; -} - - -bool jpeg_decoder_mem_stream::open(const uint8_t *pSrc_data, uint32_t size) -{ - close(); - m_pSrc_data = pSrc_data; - m_ofs = 0; - m_size = size; - return true; -} - - -int jpeg_decoder_mem_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag) -{ - *pEOF_flag = false; - if (!m_pSrc_data) return -1; - - uint32_t bytes_remaining = m_size - m_ofs; - if ((uint32_t)max_bytes_to_read > bytes_remaining) { - max_bytes_to_read = bytes_remaining; - *pEOF_flag = true; - } - memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read); - m_ofs += max_bytes_to_read; - - return max_bytes_to_read; -} - - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - - -jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height) -{ - auto decoder = new jpeg_decoder(new jpeg_decoder_mem_stream((const uint8_t*)data, size)); - if (decoder->get_error_code() != JPGD_SUCCESS) { - delete(decoder); - return nullptr; - } - - if (width) *width = decoder->get_width(); - if (height) *height = decoder->get_height(); - - return decoder; -} - - -jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height) -{ - auto fileStream = new jpeg_decoder_file_stream(); - if (!fileStream->open(filename)) return nullptr; - - auto decoder = new jpeg_decoder(fileStream); - if (decoder->get_error_code() != JPGD_SUCCESS) { - delete(decoder); - return nullptr; - } - - if (width) *width = decoder->get_width(); - if (height) *height = decoder->get_height(); - - return decoder; -} - - -void jpgdDelete(jpeg_decoder* decoder) -{ - delete(decoder); -} - - -unsigned char* jpgdDecompress(jpeg_decoder* decoder) -{ - if (!decoder) return nullptr; - - int req_comps = 4; //TODO: fixed 4 channel components now? - if ((req_comps != 1) && (req_comps != 3) && (req_comps != 4)) return nullptr; - - auto image_width = decoder->get_width(); - auto image_height = decoder->get_height(); - //auto actual_comps = decoder->get_num_components(); - - if (decoder->begin_decoding() != JPGD_SUCCESS) return nullptr; - - const int dst_bpl = image_width * req_comps; - uint8_t *pImage_data = (uint8_t*)malloc(dst_bpl * image_height); - if (!pImage_data) return nullptr; - - for (int y = 0; y < image_height; y++) { - const uint8_t* pScan_line; - uint32_t scan_line_len; - if (decoder->decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) { - free(pImage_data); - return nullptr; - } - - uint8_t *pDst = pImage_data + y * dst_bpl; - - //Return as BGRA - if ((req_comps == 4) && (decoder->get_num_components() == 3)) { - for (int x = 0; x < image_width; x++) { - pDst[0] = pScan_line[x*4+2]; - pDst[1] = pScan_line[x*4+1]; - pDst[2] = pScan_line[x*4+0]; - pDst[3] = 255; - pDst += 4; - } - } else if (((req_comps == 1) && (decoder->get_num_components() == 1)) || ((req_comps == 4) && (decoder->get_num_components() == 3))) { - memcpy(pDst, pScan_line, dst_bpl); - } else if (decoder->get_num_components() == 1) { - if (req_comps == 3) { - for (int x = 0; x < image_width; x++) { - uint8_t luma = pScan_line[x]; - pDst[0] = luma; - pDst[1] = luma; - pDst[2] = luma; - pDst += 3; - } - } else { - for (int x = 0; x < image_width; x++) { - uint8_t luma = pScan_line[x]; - pDst[0] = luma; - pDst[1] = luma; - pDst[2] = luma; - pDst[3] = 255; - pDst += 4; - } - } - } else if (decoder->get_num_components() == 3) { - if (req_comps == 1) { - const int YR = 19595, YG = 38470, YB = 7471; - for (int x = 0; x < image_width; x++) { - int r = pScan_line[x*4+0]; - int g = pScan_line[x*4+1]; - int b = pScan_line[x*4+2]; - *pDst++ = static_cast((r * YR + g * YG + b * YB + 32768) >> 16); - } - } else { - for (int x = 0; x < image_width; x++) { - pDst[0] = pScan_line[x*4+0]; - pDst[1] = pScan_line[x*4+1]; - pDst[2] = pScan_line[x*4+2]; - pDst += 3; - } - } - } - } - return pImage_data; -} diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h deleted file mode 100644 index 030fdc294666..000000000000 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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. - */ - -// jpgd.h - C++ class for JPEG decompression. -// Public domain, Rich Geldreich -#ifndef _TVG_JPGD_H_ -#define _TVG_JPGD_H_ - -class jpeg_decoder; - -jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height); -jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height); -unsigned char* jpgdDecompress(jpeg_decoder* decoder); -void jpgdDelete(jpeg_decoder* decoder); - -#endif //_TVG_JPGD_H_ diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp index 694e6d1ebfdb..6fdb64120422 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp @@ -28,17 +28,30 @@ /* Internal Class Implementation */ /************************************************************************/ +static bool _isImportanceApplicable(SvgStyleFlags &toFlagsImportance, SvgStyleFlags fromFlagsImportance, SvgStyleFlags flag) +{ + if (!(toFlagsImportance & flag) && (fromFlagsImportance & flag)) { + return true; + } + return false; +} + static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) { if (from == nullptr) return; //Copy the properties of 'from' only if they were explicitly set (not the default ones). - if (from->curColorSet && !(to->flags & SvgStyleFlags::Color)) { + if ((from->curColorSet && !(to->flags & SvgStyleFlags::Color)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Color)) { to->color = from->color; to->curColorSet = true; to->flags = (to->flags | SvgStyleFlags::Color); + if (from->flagsImportance & SvgStyleFlags::Color) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color); + } } //Fill - if ((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) { + if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Fill)) { to->fill.paint.color = from->fill.paint.color; to->fill.paint.none = from->fill.paint.none; to->fill.paint.curColor = from->fill.paint.curColor; @@ -48,19 +61,31 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) } to->fill.flags = (to->fill.flags | SvgFillFlags::Paint); to->flags = (to->flags | SvgStyleFlags::Fill); + if (from->flagsImportance & SvgStyleFlags::Fill) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Fill); + } } - if ((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) { + if (((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillOpacity)) { to->fill.opacity = from->fill.opacity; to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity); to->flags = (to->flags | SvgStyleFlags::FillOpacity); + if (from->flagsImportance & SvgStyleFlags::FillOpacity) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillOpacity); + } } - if ((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) { + if (((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillRule)) { to->fill.fillRule = from->fill.fillRule; to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule); to->flags = (to->flags | SvgStyleFlags::FillRule); + if (from->flagsImportance & SvgStyleFlags::FillRule) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillRule); + } } //Stroke - if ((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) { + if (((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Stroke)) { to->stroke.paint.color = from->stroke.paint.color; to->stroke.paint.none = from->stroke.paint.none; to->stroke.paint.curColor = from->stroke.paint.curColor; @@ -70,18 +95,30 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) } to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint); to->flags = (to->flags | SvgStyleFlags::Stroke); + if (from->flagsImportance & SvgStyleFlags::Stroke) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Stroke); + } } - if ((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) { + if (((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeOpacity)) { to->stroke.opacity = from->stroke.opacity; to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity); to->flags = (to->flags | SvgStyleFlags::StrokeOpacity); + if (from->flagsImportance & SvgStyleFlags::StrokeOpacity) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeOpacity); + } } - if ((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) { + if (((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeWidth)) { to->stroke.width = from->stroke.width; to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width); to->flags = (to->flags | SvgStyleFlags::StrokeWidth); + if (from->flagsImportance & SvgStyleFlags::StrokeWidth) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeWidth); + } } - if ((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) { + if (((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeDashArray)) { if (from->stroke.dash.array.count > 0) { to->stroke.dash.array.clear(); to->stroke.dash.array.reserve(from->stroke.dash.array.count); @@ -90,23 +127,38 @@ static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from) } to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash); to->flags = (to->flags | SvgStyleFlags::StrokeDashArray); + if (from->flagsImportance & SvgStyleFlags::StrokeDashArray) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeDashArray); + } } } - if ((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) { + if (((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineCap)) { to->stroke.cap = from->stroke.cap; to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap); to->flags = (to->flags | SvgStyleFlags::StrokeLineCap); + if (from->flagsImportance & SvgStyleFlags::StrokeLineCap) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineCap); + } } - if ((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) { + if (((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineJoin)) { to->stroke.join = from->stroke.join; to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join); to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin); + if (from->flagsImportance & SvgStyleFlags::StrokeLineJoin) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineJoin); + } } //Opacity //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity' - if (from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) { + if ((from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) || + _isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Opacity)) { to->opacity = from->opacity; to->flags = (to->flags | SvgStyleFlags::Opacity); + if (from->flagsImportance & SvgStyleFlags::Opacity) { + to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Opacity); + } } } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp index f9f08cdb8112..998c98e83fe2 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -392,15 +392,8 @@ static char* _idFromUrl(const char* url) int i = 0; while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i; - - //custom strndup() for portability - int len = strlen(url); - if (i < len) len = i; - - auto ret = (char*) malloc(len + 1); - if (!ret) return 0; - ret[len] = '\0'; - return (char*) memcpy(ret, url, len); + + return svgUtilStrndup(url, i); } @@ -983,6 +976,22 @@ static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node->style->stroke.join = _toLineJoin(value); } +static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value) +{ + char* end = nullptr; + const float miterlimit = svgUtilStrtof(value, &end); + + // https://www.w3.org/TR/SVG2/painting.html#LineJoin + // - A negative value for stroke-miterlimit must be treated as an illegal value. + if (miterlimit < 0.0f) { + TVGERR("SVG", "A stroke-miterlimit change (%f <- %f) with a negative value is omitted.", + node->style->stroke.miterlimit, miterlimit); + return; + } + + node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Miterlimit); + node->style->stroke.miterlimit = miterlimit; +} static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value) { @@ -1099,6 +1108,7 @@ static constexpr struct STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke), STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth), STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin), + STYLE_DEF(stroke-miterlimit, StrokeMiterlimit, SvgStyleFlags::StrokeMiterlimit), STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap), STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity), STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray), @@ -1125,12 +1135,27 @@ static bool _parseStyleAttr(void* data, const char* key, const char* value, bool sz = strlen(key); for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) { if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) { + bool importance = false; + if (auto ptr = strstr(value, "!important")) { + size_t size = ptr - value; + while (size > 0 && isspace(value[size - 1])) { + size--; + } + value = svgUtilStrndup(value, size); + importance = true; + } if (style) { - styleTags[i].tagHandler(loader, node, value); - node->style->flags = (node->style->flags | styleTags[i].flag); + if (importance || !(node->style->flagsImportance & styleTags[i].flag)) { + styleTags[i].tagHandler(loader, node, value); + node->style->flags = (node->style->flags | styleTags[i].flag); + } } else if (!(node->style->flags & styleTags[i].flag)) { styleTags[i].tagHandler(loader, node, value); } + if (importance) { + node->style->flagsImportance = (node->style->flags | styleTags[i].flag); + free(const_cast(value)); + } return true; } } @@ -1307,6 +1332,7 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type) node->style->stroke.cap = StrokeCap::Butt; //Default line join is miter node->style->stroke.join = StrokeJoin::Miter; + node->style->stroke.miterlimit = 4.0f; node->style->stroke.scale = 1.0; node->style->paintOrder = _toPaintOrder("fill stroke"); @@ -1593,39 +1619,11 @@ static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const } -static bool _attrParsePolygonPoints(const char* str, float** points, int* ptCount) +static bool _attrParsePolygonPoints(const char* str, SvgPolygonNode* polygon) { - float tmp[50]; - int tmpCount = 0; - int count = 0; float num; - float *pointArray = nullptr, *tmpArray; - - while (_parseNumber(&str, &num)) { - tmp[tmpCount++] = num; - if (tmpCount == 50) { - tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float)); - if (!tmpArray) goto error_alloc; - pointArray = tmpArray; - memcpy(&pointArray[count], tmp, tmpCount * sizeof(float)); - count += tmpCount; - tmpCount = 0; - } - } - - if (tmpCount > 0) { - tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float)); - if (!tmpArray) goto error_alloc; - pointArray = tmpArray; - memcpy(&pointArray[count], tmp, tmpCount * sizeof(float)); - count += tmpCount; - } - *ptCount = count; - *points = pointArray; + while (_parseNumber(&str, &num)) polygon->pts.push(num); return true; - -error_alloc: - return false; } @@ -1642,7 +1640,7 @@ static bool _attrParsePolygonNode(void* data, const char* key, const char* value else polygon = &(node->node.polyline); if (!strcmp(key, "points")) { - return _attrParsePolygonPoints(value, &polygon->points, &polygon->pointsCount); + return _attrParsePolygonPoints(value, polygon); } else if (!strcmp(key, "style")) { return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader); } else if (!strcmp(key, "clip-path")) { @@ -1903,6 +1901,7 @@ static SvgNode* _getDefsNode(SvgNode* node) } if (node->type == SvgNodeType::Doc) return node->node.doc.defs; + if (node->type == SvgNodeType::Defs) return node; return nullptr; } @@ -2680,7 +2679,7 @@ static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgSty } } - if (to->stops.count == 0) _cloneGradStops(to->stops, from->stops); + if (to->stops.empty()) _cloneGradStops(to->stops, from->stops); } @@ -2784,6 +2783,9 @@ static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* paren if (!(child->stroke.flags & SvgStrokeFlags::Join)) { child->stroke.join = parent->stroke.join; } + if (!(child->stroke.flags & SvgStrokeFlags::Miterlimit)) { + child->stroke.miterlimit = parent->stroke.miterlimit; + } } @@ -2847,6 +2849,10 @@ static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from) if (from->stroke.flags & SvgStrokeFlags::Join) { to->stroke.join = from->stroke.join; } + + if (from->stroke.flags & SvgStrokeFlags::Miterlimit) { + to->stroke.miterlimit = from->stroke.miterlimit; + } } @@ -2910,16 +2916,14 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) break; } case SvgNodeType::Polygon: { - if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) { - to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float)); - memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float)); + if ((to->node.polygon.pts.count = from->node.polygon.pts.count)) { + to->node.polygon.pts = from->node.polygon.pts; } break; } case SvgNodeType::Polyline: { - if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) { - to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float)); - memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float)); + if ((to->node.polyline.pts.count = from->node.polyline.pts.count)) { + to->node.polyline.pts = from->node.polyline.pts; } break; } @@ -2934,6 +2938,16 @@ static void _copyAttr(SvgNode* to, const SvgNode* from) } break; } + case SvgNodeType::Use: { + to->node.use.x = from->node.use.x; + to->node.use.y = from->node.use.y; + to->node.use.w = from->node.use.w; + to->node.use.h = from->node.use.h; + to->node.use.isWidthSet = from->node.use.isWidthSet; + to->node.use.isHeightSet = from->node.use.isHeightSet; + to->node.use.symbol = from->node.use.symbol; + break; + } default: { break; } @@ -3200,7 +3214,7 @@ static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node) } case SvgNodeType::Polygon: case SvgNodeType::Polyline: { - if (node->node.polygon.pointsCount < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type); + if (node->node.polygon.pts.count < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type); break; } case SvgNodeType::Circle: { @@ -3356,11 +3370,11 @@ static void _freeNode(SvgNode* node) break; } case SvgNodeType::Polygon: { - free(node->node.polygon.points); + free(node->node.polygon.pts.data); break; } case SvgNodeType::Polyline: { - free(node->node.polyline.points); + free(node->node.polyline.pts.data); break; } case SvgNodeType::Doc: { @@ -3497,6 +3511,7 @@ void SvgLoader::run(unsigned tid) if (defs) _updateComposite(loaderData.doc, defs); _updateStyle(loaderData.doc, nullptr); + if (defs) _updateStyle(defs, nullptr); if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients); if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients); @@ -3683,6 +3698,5 @@ bool SvgLoader::close() unique_ptr SvgLoader::paint() { this->done(); - if (root) return move(root); - else return nullptr; + return std::move(root); } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h index 5c74184ec8e7..8331bf4c16d4 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h @@ -48,12 +48,17 @@ class SvgLoader : public LoadModule, public Task bool resize(Paint* paint, float w, float h) override; bool read() override; bool close() override; + unique_ptr paint() override; private: SvgViewFlag viewFlag = SvgViewFlag::None; AspectRatioAlign align = AspectRatioAlign::XMidYMid; AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet; + float vx = 0; + float vy = 0; + float vw = 0; + float vh = 0; bool header(); void clear(); diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h index dec9fadebeef..961438ff42f1 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h @@ -100,6 +100,7 @@ enum class SvgStrokeFlags Cap = 0x20, Join = 0x40, Dash = 0x80, + Miterlimit = 0x100 }; constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b) @@ -137,7 +138,8 @@ enum class SvgStyleFlags Mask = 0x2000, MaskType = 0x4000, Display = 0x8000, - PaintOrder = 0x10000 + PaintOrder = 0x10000, + StrokeMiterlimit = 0x20000 }; constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b) @@ -351,8 +353,7 @@ struct SvgPathNode struct SvgPolygonNode { - int pointsCount; - float* points; + Array pts; }; struct SvgClipNode @@ -466,6 +467,7 @@ struct SvgStyleStroke float centered; StrokeCap cap; StrokeJoin join; + float miterlimit; SvgDash dash; int dashCount; }; @@ -482,6 +484,7 @@ struct SvgStyleProperty char* cssClass; bool paintOrder; //true if default (fill, stroke), false otherwise SvgStyleFlags flags; + SvgStyleFlags flagsImportance; //indicates the importance of the flag - if set, higher priority is applied (https://drafts.csswg.org/css-cascade-4/#importance) }; struct SvgNode @@ -539,7 +542,7 @@ struct SvgNodeIdPair struct SvgLoaderData { - Array stack = {nullptr, 0, 0}; + Array stack; SvgNode* doc = nullptr; SvgNode* def = nullptr; SvgNode* cssStyle = nullptr; diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp index c5e5df893ebf..e5beef8093d1 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -263,7 +263,7 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox comp->transform(m); } - if (valid) paint->composite(move(comp), CompositeMethod::ClipPath); + if (valid) paint->composite(std::move(comp), CompositeMethod::ClipPath); node->style->clipPath.applying = false; } @@ -285,9 +285,9 @@ static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox if (node->transform) comp->transform(*node->transform); if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) { - paint->composite(move(comp), CompositeMethod::LumaMask); + paint->composite(std::move(comp), CompositeMethod::LumaMask); } else { - paint->composite(move(comp), CompositeMethod::AlphaMask); + paint->composite(std::move(comp), CompositeMethod::AlphaMask); } } @@ -313,10 +313,10 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri if (style->fill.paint.gradient->type == SvgGradientType::Linear) { auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity); - vg->fill(move(linear)); + vg->fill(std::move(linear)); } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) { auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity); - vg->fill(move(radial)); + vg->fill(std::move(radial)); } } else if (style->fill.paint.url) { //TODO: Apply the color pointed by url @@ -342,6 +342,7 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri vg->stroke(style->stroke.width); vg->stroke(style->stroke.cap); vg->stroke(style->stroke.join); + vg->strokeMiterlimit(style->stroke.miterlimit); if (style->stroke.dash.array.count > 0) { vg->stroke(style->stroke.dash.array.data, style->stroke.dash.array.count); } @@ -355,10 +356,10 @@ static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const stri if (style->stroke.paint.gradient->type == SvgGradientType::Linear) { auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity); - vg->stroke(move(linear)); + vg->stroke(std::move(linear)); } else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) { auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity); - vg->stroke(move(radial)); + vg->stroke(std::move(radial)); } } else if (style->stroke.paint.url) { //TODO: Apply the color pointed by url @@ -401,19 +402,21 @@ static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const str break; } case SvgNodeType::Polygon: { - if (node->node.polygon.pointsCount < 2) break; - shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]); - for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) { - shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]); + if (node->node.polygon.pts.count < 2) break; + auto pts = node->node.polygon.pts.data; + shape->moveTo(pts[0], pts[1]); + for (pts += 2; pts < node->node.polygon.pts.end(); pts += 2) { + shape->lineTo(pts[0], pts[1]); } shape->close(); break; } case SvgNodeType::Polyline: { - if (node->node.polygon.pointsCount < 2) break; - shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]); - for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) { - shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]); + if (node->node.polyline.pts.count < 2) break; + auto pts = node->node.polyline.pts.data; + shape->moveTo(pts[0], pts[1]); + for (pts += 2; pts < node->node.polyline.pts.end(); pts += 2) { + shape->lineTo(pts[0], pts[1]); } break; } @@ -676,7 +679,7 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c scene->transform(mSceneTransform); if (node->node.use.symbol->node.symbol.overflowVisible) { - finalScene = move(scene); + finalScene = std::move(scene); } else { auto viewBoxClip = Shape::gen(); viewBoxClip->appendRect(0, 0, width, height, 0, 0); @@ -689,17 +692,17 @@ static unique_ptr _useBuildHelper(const SvgNode* node, const Box& vBox, c viewBoxClip->transform(mClipTransform); auto compositeLayer = Scene::gen(); - compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath); - compositeLayer->push(move(scene)); + compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath); + compositeLayer->push(std::move(scene)); auto root = Scene::gen(); - root->push(move(compositeLayer)); + root->push(std::move(compositeLayer)); - finalScene = move(root); + finalScene = std::move(root); } } else { if (!mathIdentity((const Matrix*)(&mUseTransform))) scene->transform(mUseTransform); - finalScene = move(scene); + finalScene = std::move(scene); } return finalScene; @@ -731,7 +734,7 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, } else if ((*child)->type == SvgNodeType::Image) { auto image = _imageBuildHelper(*child, vBox, svgPath); if (image) { - scene->push(move(image)); + scene->push(std::move(image)); if (isMaskWhite) *isMaskWhite = false; } } else if ((*child)->type != SvgNodeType::Mask) { @@ -739,13 +742,13 @@ static unique_ptr _sceneBuildHelper(const SvgNode* node, const Box& vBox, if (shape) { if (isMaskWhite) { uint8_t r, g, b; - shape->fillColor(&r, &g, &b, nullptr); + shape->fillColor(&r, &g, &b); if (shape->fill() || r < 255 || g < 255 || b < 255 || shape->strokeFill() || - (shape->strokeColor(&r, &g, &b, nullptr) == Result::Success && (r < 255 || g < 255 || b < 255))) { + (shape->strokeColor(&r, &g, &b) == Result::Success && (r < 255 || g < 255 || b < 255))) { *isMaskWhite = false; } } - scene->push(move(shape)); + scene->push(std::move(shape)); } } } @@ -801,14 +804,14 @@ unique_ptr svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, fl auto viewBoxClip = Shape::gen(); viewBoxClip->appendRect(0, 0, w, h, 0, 0); - viewBoxClip->fill(0, 0, 0, 255); + viewBoxClip->fill(0, 0, 0); auto compositeLayer = Scene::gen(); - compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath); - compositeLayer->push(move(docNode)); + compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath); + compositeLayer->push(std::move(docNode)); auto root = Scene::gen(); - root->push(move(compositeLayer)); + root->push(std::move(compositeLayer)); loaderData.doc->node.doc.vx = vBox.x; loaderData.doc->node.doc.vy = vBox.y; diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp index 12d20c9731ae..2aaeb2b25dfd 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp @@ -275,3 +275,16 @@ string svgUtilBase64Decode(const char *src) } return decoded; } + + +char* svgUtilStrndup(const char* str, size_t n) +{ + auto len = strlen(str); + if (len < n) n = len; + + auto ret = (char*)malloc(n + 1); + if (!ret) return nullptr; + ret[n] = '\0'; + + return (char*)memcpy(ret, str, n); +} diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h index 6f94367aebc6..48be4649bc33 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h @@ -30,4 +30,6 @@ float svgUtilStrtof(const char *nPtr, char **endPtr); string svgUtilURLDecode(const char *src); string svgUtilBase64Decode(const char *src); +char* svgUtilStrndup(const char* str, size_t n); + #endif //_TVG_SVG_UTIL_H_ diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp index 77467e071bf7..faacfcd223fc 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp @@ -33,6 +33,7 @@ #endif #include "tvgXmlParser.h" +#include "tvgSvgUtil.h" /************************************************************************/ /* Internal Class Implementation */ @@ -238,14 +239,6 @@ static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &to } -static char* _strndup(const char* src, unsigned len) -{ - auto ret = (char*)malloc(len + 1); - if (!ret) return nullptr; - ret[len] = '\0'; - return (char*)memcpy(ret, src, len); -} - /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -564,10 +557,10 @@ const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char } if (p == itr) *tag = strdup("all"); - else *tag = _strndup(itr, p - itr); + else *tag = svgUtilStrndup(itr, p - itr); if (p == itrEnd) *name = nullptr; - else *name = _strndup(p + 1, itrEnd - p - 1); + else *name = svgUtilStrndup(p + 1, itrEnd - p - 1); return (nextElement ? nextElement + 1 : nullptr); } diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp deleted file mode 100644 index 2b89d6bb5930..000000000000 --- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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 - -#ifdef _WIN32 - #include -#elif defined(__linux__) - #include -#else - #include -#endif - -#include "tvgTvgCommon.h" - - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - -struct TvgBinBlock -{ - TvgBinTag type; - TvgBinCounter length; - const char* data; - const char* end; -}; - -static Paint* _parsePaint(TvgBinBlock baseBlock); - - -static TvgBinBlock _readBlock(const char *ptr) -{ - TvgBinBlock block; - block.type = *ptr; - READ_UI32(&block.length, ptr + SIZE(TvgBinTag)); - block.data = ptr + SIZE(TvgBinTag) + SIZE(TvgBinCounter); - block.end = block.data + block.length; - return block; -} - - -static bool _parseCmpTarget(const char *ptr, const char *end, Paint *paint) -{ - auto block = _readBlock(ptr); - if (block.end > end) return false; - - if (block.type != TVG_TAG_PAINT_CMP_METHOD) return false; - if (block.length != SIZE(TvgBinFlag)) return false; - - auto cmpMethod = static_cast(*block.data); - - ptr = block.end; - - auto cmpBlock = _readBlock(ptr); - if (cmpBlock.end > end) return false; - - paint->composite(unique_ptr(_parsePaint(cmpBlock)), cmpMethod); - - return true; -} - - -static bool _parsePaintProperty(TvgBinBlock block, Paint *paint) -{ - switch (block.type) { - case TVG_TAG_PAINT_OPACITY: { - if (block.length != SIZE(uint8_t)) return false; - paint->opacity(*block.data); - return true; - } - case TVG_TAG_PAINT_TRANSFORM: { - if (block.length != SIZE(Matrix)) return false; - Matrix matrix; - memcpy(&matrix, block.data, SIZE(Matrix)); - paint->transform(matrix); - return true; - } - case TVG_TAG_PAINT_CMP_TARGET: { - if (block.length < SIZE(TvgBinTag) + SIZE(TvgBinCounter)) return false; - return _parseCmpTarget(block.data, block.end, paint); - } - } - return false; -} - - -static bool _parseScene(TvgBinBlock block, Paint *paint) -{ - auto scene = static_cast(paint); - - //Case1: scene reserve count - if (block.type == TVG_TAG_SCENE_RESERVEDCNT) { - if (block.length != SIZE(uint32_t)) return false; - uint32_t reservedCnt; - READ_UI32(&reservedCnt, block.data); - scene->reserve(reservedCnt); - return true; - } - - //Case2: Base Paint Properties - if (_parsePaintProperty(block, scene)) return true; - - //Case3: A Child paint - if (auto paint = _parsePaint(block)) { - scene->push(unique_ptr(paint)); - return true; - } - - return false; -} - - -static bool _parseShapePath(const char *ptr, const char *end, Shape *shape) -{ - uint32_t cmdCnt, ptsCnt; - - READ_UI32(&cmdCnt, ptr); - ptr += SIZE(cmdCnt); - - READ_UI32(&ptsCnt, ptr); - ptr += SIZE(ptsCnt); - - auto cmds = (TvgBinFlag*) ptr; - ptr += SIZE(TvgBinFlag) * cmdCnt; - - auto pts = (Point*) ptr; - ptr += SIZE(Point) * ptsCnt; - - if (ptr > end) return false; - - /* Recover to PathCommand(4 bytes) from TvgBinFlag(1 byte) */ - PathCommand* inCmds = (PathCommand*)alloca(sizeof(PathCommand) * cmdCnt); - for (uint32_t i = 0; i < cmdCnt; ++i) { - inCmds[i] = static_cast(cmds[i]); - } - - shape->appendPath(inCmds, cmdCnt, pts, ptsCnt); - - return true; -} - - -static unique_ptr _parseShapeFill(const char *ptr, const char *end) -{ - unique_ptr fillGrad; - - while (ptr < end) { - auto block = _readBlock(ptr); - if (block.end > end) return nullptr; - - switch (block.type) { - case TVG_TAG_FILL_RADIAL_GRADIENT: { - if (block.length != 3 * SIZE(float)) return nullptr; - - auto ptr = block.data; - float x, y, radius; - - READ_FLOAT(&x, ptr); - ptr += SIZE(float); - READ_FLOAT(&y, ptr); - ptr += SIZE(float); - READ_FLOAT(&radius, ptr); - - auto fillGradRadial = RadialGradient::gen(); - fillGradRadial->radial(x, y, radius); - fillGrad = move(fillGradRadial); - break; - } - case TVG_TAG_FILL_LINEAR_GRADIENT: { - if (block.length != 4 * SIZE(float)) return nullptr; - - auto ptr = block.data; - float x1, y1, x2, y2; - - READ_FLOAT(&x1, ptr); - ptr += SIZE(float); - READ_FLOAT(&y1, ptr); - ptr += SIZE(float); - READ_FLOAT(&x2, ptr); - ptr += SIZE(float); - READ_FLOAT(&y2, ptr); - - auto fillGradLinear = LinearGradient::gen(); - fillGradLinear->linear(x1, y1, x2, y2); - fillGrad = move(fillGradLinear); - break; - } - case TVG_TAG_FILL_FILLSPREAD: { - if (!fillGrad) return nullptr; - if (block.length != SIZE(TvgBinFlag)) return nullptr; - fillGrad->spread((FillSpread) *block.data); - break; - } - case TVG_TAG_FILL_COLORSTOPS: { - if (!fillGrad) return nullptr; - if (block.length == 0 || block.length & 0x07) return nullptr; - uint32_t stopsCnt = block.length >> 3; // 8 bytes per ColorStop - if (stopsCnt > 1023) return nullptr; - Fill::ColorStop* stops = (Fill::ColorStop*)alloca(sizeof(Fill::ColorStop) * stopsCnt); - auto p = block.data; - for (uint32_t i = 0; i < stopsCnt; i++, p += 8) { - READ_FLOAT(&stops[i].offset, p); - stops[i].r = p[4]; - stops[i].g = p[5]; - stops[i].b = p[6]; - stops[i].a = p[7]; - } - fillGrad->colorStops(stops, stopsCnt); - break; - } - case TVG_TAG_FILL_TRANSFORM: { - if (!fillGrad || block.length != SIZE(Matrix)) return nullptr; - Matrix gradTransform; - memcpy(&gradTransform, block.data, SIZE(Matrix)); - fillGrad->transform(gradTransform); - break; - } - default: { - TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of the fill properties, %d bytes skipped", block.type, block.type, block.length); - break; - } - } - ptr = block.end; - } - return fillGrad; -} - - -static bool _parseShapeStrokeDashPattern(const char *ptr, const char *end, Shape *shape) -{ - uint32_t dashPatternCnt; - READ_UI32(&dashPatternCnt, ptr); - ptr += SIZE(uint32_t); - if (dashPatternCnt > 0) { - float* dashPattern = static_cast(malloc(sizeof(float) * dashPatternCnt)); - if (!dashPattern) return false; - memcpy(dashPattern, ptr, sizeof(float) * dashPatternCnt); - ptr += SIZE(float) * dashPatternCnt; - - if (ptr > end) { - free(dashPattern); - return false; - } - - shape->stroke(dashPattern, dashPatternCnt); - free(dashPattern); - } - return true; -} - - -static bool _parseShapeStroke(const char *ptr, const char *end, Shape *shape) -{ - while (ptr < end) { - auto block = _readBlock(ptr); - if (block.end > end) return false; - - switch (block.type) { - case TVG_TAG_SHAPE_STROKE_CAP: { - if (block.length != SIZE(TvgBinFlag)) return false; - shape->stroke((StrokeCap) *block.data); - break; - } - case TVG_TAG_SHAPE_STROKE_JOIN: { - if (block.length != SIZE(TvgBinFlag)) return false; - shape->stroke((StrokeJoin) *block.data); - break; - } - case TVG_TAG_SHAPE_STROKE_WIDTH: { - if (block.length != SIZE(float)) return false; - float width; - READ_FLOAT(&width, block.data); - shape->stroke(width); - break; - } - case TVG_TAG_SHAPE_STROKE_COLOR: { - if (block.length != 4) return false; - shape->stroke(block.data[0], block.data[1], block.data[2], block.data[3]); - break; - } - case TVG_TAG_SHAPE_STROKE_FILL: { - auto fill = _parseShapeFill(block.data, block.end); - if (!fill) return false; - shape->stroke(move(move(fill))); - break; - } - case TVG_TAG_SHAPE_STROKE_DASHPTRN: { - if (!_parseShapeStrokeDashPattern(block.data, block.end, shape)) return false; - break; - } - default: { - TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of stroke properties, %d bytes skipped", block.type, block.type, block.length); - break; - } - } - ptr = block.end; - } - return true; -} - - -static bool _parseShape(TvgBinBlock block, Paint* paint) -{ - auto shape = static_cast(paint); - - //Case1: Shape specific properties - switch (block.type) { - case TVG_TAG_SHAPE_PATH: { - return _parseShapePath(block.data, block.end, shape); - } - case TVG_TAG_SHAPE_STROKE: { - return _parseShapeStroke(block.data, block.end, shape); - } - case TVG_TAG_SHAPE_FILL: { - auto fill = _parseShapeFill(block.data, block.end); - if (!fill) return false; - shape->fill(move(fill)); - return true; - } - case TVG_TAG_SHAPE_COLOR: { - if (block.length != 4) return false; - shape->fill(block.data[0], block.data[1], block.data[2], block.data[3]); - return true; - } - case TVG_TAG_SHAPE_FILLRULE: { - if (block.length != SIZE(TvgBinFlag)) return false; - shape->fill((FillRule)*block.data); - return true; - } - } - - //Case2: Base Paint Properties - return _parsePaintProperty(block, shape); -} - - -static bool _parsePicture(TvgBinBlock block, Paint* paint) -{ - auto picture = static_cast(paint); - - switch (block.type) { - case TVG_TAG_PICTURE_RAW_IMAGE: { - if (block.length < 2 * SIZE(uint32_t)) return false; - - auto ptr = block.data; - uint32_t w, h; - - READ_UI32(&w, ptr); - ptr += SIZE(uint32_t); - READ_UI32(&h, ptr); - ptr += SIZE(uint32_t); - - auto size = w * h * SIZE(uint32_t); - if (block.length != 2 * SIZE(uint32_t) + size) return false; - - picture->load((uint32_t*) ptr, w, h, true); - - return true; - } - case TVG_TAG_PICTURE_MESH: { - if (block.length < 1 * SIZE(uint32_t)) return false; - - auto ptr = block.data; - uint32_t meshCnt; - READ_UI32(&meshCnt, ptr); - ptr += SIZE(uint32_t); - - auto size = meshCnt * SIZE(Polygon); - if (block.length != SIZE(uint32_t) + size) return false; - - picture->mesh((Polygon*) ptr, meshCnt); - - return true; - } - //Base Paint Properties - default: { - if (_parsePaintProperty(block, picture)) return true; - } - } - - //Vector Picture won't be requested since Saver replaces it with the Scene - return false; -} - - -static Paint* _parsePaint(TvgBinBlock baseBlock) -{ - bool (*parser)(TvgBinBlock, Paint*); - Paint *paint; - - //1. Decide the type of paint. - switch (baseBlock.type) { - case TVG_TAG_CLASS_SCENE: { - paint = Scene::gen().release(); - parser = _parseScene; - break; - } - case TVG_TAG_CLASS_SHAPE: { - paint = Shape::gen().release(); - parser = _parseShape; - break; - } - case TVG_TAG_CLASS_PICTURE: { - paint = Picture::gen().release(); - parser = _parsePicture; - break; - } - default: { - TVGERR("TVG", "Invalid Paint Type %d (0x%x)", baseBlock.type, baseBlock.type); - return nullptr; - } - } - - auto ptr = baseBlock.data; - - //2. Read Subsequent properties of the current paint. - while (ptr < baseBlock.end) { - auto block = _readBlock(ptr); - if (block.end > baseBlock.end) return paint; - if (!parser(block, paint)) { - TVGERR("TVG", "Encountered the wrong paint properties... Paint Class %d (0x%x)", baseBlock.type, baseBlock.type); - return paint; - } - ptr = block.end; - } - return paint; -} - - - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -unique_ptr TvgBinInterpreter::run(const char *ptr, const char* end) -{ - auto scene = Scene::gen(); - if (!scene) return nullptr; - - while (ptr < end) { - auto block = _readBlock(ptr); - if (block.end > end) { - TVGERR("TVG", "Corrupted tvg file."); - return nullptr; - } - scene->push(unique_ptr(_parsePaint(block))); - ptr = block.end; - } - - return scene; -} diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h deleted file mode 100644 index 29fa1025b058..000000000000 --- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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_TVG_COMMON_H_ -#define _TVG_TVG_COMMON_H_ - -#include "tvgCommon.h" -#include "tvgBinaryDesc.h" - -#define SIZE(A) sizeof(A) -#define READ_UI32(dst, src) memcpy(dst, (src), sizeof(uint32_t)) -#define READ_FLOAT(dst, src) memcpy(dst, (src), sizeof(float)) - - -/* Interface for Tvg Binary Interpreter */ -class TvgBinInterpreterBase -{ -public: - virtual ~TvgBinInterpreterBase() {} - - /* ptr: points the tvg binary body (after header) - end: end of the tvg binary data */ - virtual unique_ptr run(const char* ptr, const char* end) = 0; -}; - - -/* Version 0 */ -class TvgBinInterpreter : public TvgBinInterpreterBase -{ -public: - unique_ptr run(const char* ptr, const char* end) override; -}; - - -#endif //_TVG_TVG_COMMON_H_ diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp deleted file mode 100644 index 82c578e6d421..000000000000 --- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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 -#include -#include "tvgLoader.h" -#include "tvgTvgLoader.h" -#include "tvgLzw.h" - - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - - -void TvgLoader::clear() -{ - if (copy) free((char*)data); - ptr = data = nullptr; - size = 0; - copy = false; - - if (interpreter) { - delete(interpreter); - interpreter = nullptr; - } -} - - -/* WARNING: Header format shall not change! */ -bool TvgLoader::readHeader() -{ - if (!ptr) return false; - - //Make sure the size is large enough to hold the header - if (size < TVG_HEADER_SIZE) return false; - - //1. Signature - if (memcmp(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH)) return false; - ptr += TVG_HEADER_SIGNATURE_LENGTH; - - //2. Version - char version[TVG_HEADER_VERSION_LENGTH + 1]; - memcpy(version, ptr, TVG_HEADER_VERSION_LENGTH); - version[TVG_HEADER_VERSION_LENGTH - 1] = '\0'; - ptr += TVG_HEADER_VERSION_LENGTH; - this->version = atoi(version); - if (this->version > THORVG_VERSION_NUMBER()) { - TVGLOG("TVG", "This TVG file expects a higher version(%d) of ThorVG symbol(%d)", this->version, THORVG_VERSION_NUMBER()); - } - - //3. View Size - READ_FLOAT(&w, ptr); - ptr += SIZE(float); - READ_FLOAT(&h, ptr); - ptr += SIZE(float); - - //4. Reserved - if (*ptr & TVG_HEAD_FLAG_COMPRESSED) compressed = true; - ptr += TVG_HEADER_RESERVED_LENGTH; - - //5. Compressed Size if any - if (compressed) { - auto p = ptr; - - //TVG_HEADER_UNCOMPRESSED_SIZE - memcpy(&uncompressedSize, p, sizeof(uint32_t)); - p += SIZE(uint32_t); - - //TVG_HEADER_COMPRESSED_SIZE - memcpy(&compressedSize, p, sizeof(uint32_t)); - p += SIZE(uint32_t); - - //TVG_HEADER_COMPRESSED_SIZE_BITS - memcpy(&compressedSizeBits, p, sizeof(uint32_t)); - } - - ptr += TVG_HEADER_COMPRESS_SIZE; - - //Decide the proper Tvg Binary Interpreter based on the current file version - if (this->version >= 0) interpreter = new TvgBinInterpreter; - - return true; -} - - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -TvgLoader::~TvgLoader() -{ - close(); -} - - -bool TvgLoader::open(const string &path) -{ - clear(); - - ifstream f; - f.open(path, ifstream::in | ifstream::binary | ifstream::ate); - - if (!f.is_open()) return false; - - size = f.tellg(); - f.seekg(0, ifstream::beg); - - copy = true; - data = (char*)malloc(size); - if (!data) { - clear(); - f.close(); - return false; - } - - if (!f.read((char*)data, size)) - { - clear(); - f.close(); - return false; - } - - f.close(); - - ptr = data; - - return readHeader(); -} - - -bool TvgLoader::open(const char *data, uint32_t size, bool copy) -{ - clear(); - - if (copy) { - this->data = (char*)malloc(size); - if (!this->data) return false; - memcpy((char*)this->data, data, size); - } else this->data = data; - - this->ptr = this->data; - this->size = size; - this->copy = copy; - - return readHeader(); -} - - -bool TvgLoader::resize(Paint* paint, float w, float h) -{ - if (!paint) return false; - - auto sx = w / this->w; - auto sy = h / this->h; - - //Scale - auto scale = sx < sy ? sx : sy; - paint->scale(scale); - - //Align - float tx = 0, ty = 0; - auto sw = this->w * scale; - auto sh = this->h * scale; - if (sw > sh) ty -= (h - sh) * 0.5f; - else tx -= (w - sw) * 0.5f; - paint->translate(-tx, -ty); - - return true; -} - - -bool TvgLoader::read() -{ - if (!ptr || size == 0) return false; - - TaskScheduler::request(this); - - return true; -} - - -bool TvgLoader::close() -{ - this->done(); - clear(); - return true; -} - - -void TvgLoader::run(unsigned tid) -{ - if (root) root.reset(); - - auto data = const_cast(ptr); - - if (compressed) { - data = (char*) lzwDecode((uint8_t*) data, compressedSize, compressedSizeBits, uncompressedSize); - root = interpreter->run(data, data + uncompressedSize); - free(data); - } else { - root = interpreter->run(data, this->data + size); - } - - if (!root) clear(); -} - - -unique_ptr TvgLoader::paint() -{ - this->done(); - if (root) return move(root); - return nullptr; -} diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h deleted file mode 100644 index b98dff83b0c1..000000000000 --- a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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_TVG_LOADER_H_ -#define _TVG_TVG_LOADER_H_ - -#include "tvgTaskScheduler.h" -#include "tvgTvgCommon.h" - - -class TvgLoader : public LoadModule, public Task -{ -public: - const char* data = nullptr; - const char* ptr = nullptr; - uint32_t size = 0; - uint16_t version = 0; - unique_ptr root = nullptr; - TvgBinInterpreterBase* interpreter = nullptr; - uint32_t uncompressedSize = 0; - uint32_t compressedSize = 0; - uint32_t compressedSizeBits = 0; - bool copy = false; - bool compressed = false; - - ~TvgLoader(); - - using LoadModule::open; - bool open(const string &path) override; - bool open(const char *data, uint32_t size, bool copy) override; - bool read() override; - bool close() override; - bool resize(Paint* paint, float w, float h) override; - unique_ptr paint() override; - -private: - bool readHeader(); - void run(unsigned tid) override; - void clear(); -}; - -#endif //_TVG_TVG_LOADER_H_ diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp deleted file mode 100644 index 10985701409e..000000000000 --- a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.cpp +++ /dev/null @@ -1,792 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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 "tvgSaveModule.h" -#include "tvgTvgSaver.h" -#include "tvgLzw.h" - -#include - -#ifdef _WIN32 - #include -#elif defined(__linux__) - #include -#else - #include -#endif - -static FILE* _fopen(const char* filename, const char* mode) -{ -#if defined(_MSC_VER) && defined(__clang__) - FILE *fp; - auto err = fopen_s(&fp, filename, mode); - if (err != 0) return nullptr; - return fp; -#else - auto fp = fopen(filename, mode); - if (!fp) return nullptr; - return fp; -#endif -} - -#define SIZE(A) sizeof(A) - -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ - -static inline TvgBinCounter SERIAL_DONE(TvgBinCounter cnt) -{ - return SIZE(TvgBinTag) + SIZE(TvgBinCounter) + cnt; -} - - -/* if the properties are identical, we can merge the shapes. */ -static bool _merge(Shape* from, Shape* to) -{ - uint8_t r, g, b, a; - uint8_t r2, g2, b2, a2; - - //fill - if (from->fill() || to->fill()) return false; - - r = g = b = a = r2 = g2 = b2 = a2 = 0; - - from->fillColor(&r, &g, &b, &a); - to->fillColor(&r2, &g2, &b2, &a2); - - if (r != r2 || g != g2 || b != b2 || a != a2) return false; - - //composition - if (from->composite(nullptr) != CompositeMethod::None) return false; - if (to->composite(nullptr) != CompositeMethod::None) return false; - - //opacity - if (from->opacity() != to->opacity()) return false; - - //transform - auto t1 = from->transform(); - auto t2 = to->transform(); - - if (!mathEqual(t1.e11, t2.e11) || !mathEqual(t1.e12, t2.e12) || !mathEqual(t1.e13, t2.e13) || - !mathEqual(t1.e21, t2.e21) || !mathEqual(t1.e22, t2.e22) || !mathEqual(t1.e23, t2.e23) || - !mathEqual(t1.e31, t2.e31) || !mathEqual(t1.e32, t2.e32) || !mathEqual(t1.e33, t2.e33)) { - return false; - } - - //stroke - r = g = b = a = r2 = g2 = b2 = a2 = 0; - - from->strokeColor(&r, &g, &b, &a); - to->strokeColor(&r2, &g2, &b2, &a2); - - if (r != r2 || g != g2 || b != b2 || a != a2) return false; - - if (fabs(from->strokeWidth() - to->strokeWidth()) > FLT_EPSILON) return false; - - //OPTIMIZE: Yet we can't merge outlining shapes unless we can support merging shapes feature. - if (from->strokeWidth() > 0 || to->strokeWidth() > 0) return false; - - if (from->strokeCap() != to->strokeCap()) return false; - if (from->strokeJoin() != to->strokeJoin()) return false; - if (from->strokeDash(nullptr) > 0 || to->strokeDash(nullptr) > 0) return false; - if (from->strokeFill() || to->strokeFill()) return false; - - //fill rule - if (from->fillRule() != to->fillRule()) return false; - - //Good, identical shapes, we can merge them. - const PathCommand* cmds = nullptr; - auto cmdCnt = from->pathCommands(&cmds); - - const Point* pts = nullptr; - auto ptsCnt = from->pathCoords(&pts); - - to->appendPath(cmds, cmdCnt, pts, ptsCnt); - - return true; -} - - -bool TvgSaver::saveEncoding(const std::string& path) -{ - if (!compress) return flushTo(path); - - //Try encoding - auto uncompressed = buffer.data + headerSize; - auto uncompressedSize = buffer.count - headerSize; - - uint32_t compressedSize, compressedSizeBits; - - auto compressed = lzwEncode(uncompressed, uncompressedSize, &compressedSize, &compressedSizeBits); - - //Failed compression. - if (!compressed) return flushTo(path); - - //Optimization is ineffective. - if (compressedSize >= uncompressedSize) { - free(compressed); - return flushTo(path); - } - - TVGLOG("TVG_SAVER", "%s, compressed: %d -> %d, saved rate: %3.2f%%", path.c_str(), uncompressedSize, compressedSize, (1 - ((float) compressedSize / (float) uncompressedSize)) * 100); - - //Update compress size in the header. - uncompressed -= (TVG_HEADER_COMPRESS_SIZE + TVG_HEADER_RESERVED_LENGTH); - - //Compression Flag - *uncompressed |= TVG_HEAD_FLAG_COMPRESSED; - uncompressed += TVG_HEADER_RESERVED_LENGTH; - - //Uncompressed Size - memcpy(uncompressed, &uncompressedSize, TVG_HEADER_UNCOMPRESSED_SIZE); - uncompressed += TVG_HEADER_UNCOMPRESSED_SIZE; - - //Comprssed Size - memcpy(uncompressed, &compressedSize, TVG_HEADER_COMPRESSED_SIZE); - uncompressed += TVG_HEADER_COMPRESSED_SIZE; - - //Compressed Size Bits - memcpy(uncompressed, &compressedSizeBits, TVG_HEADER_COMPRESSED_SIZE_BITS); - - //Good optimization, flush to file. - auto fp = _fopen(path.c_str(), "wb+"); - if (!fp) goto fail; - - //write header - if (fwrite(buffer.data, SIZE(uint8_t), headerSize, fp) == 0) goto fail; - - //write compressed data - if (fwrite(compressed, SIZE(uint8_t), compressedSize, fp) == 0) goto fail; - - fclose(fp); - free(compressed); - - return true; - -fail: - if (fp) fclose(fp); - if (compressed) free(compressed); - return false; -} - - -bool TvgSaver::flushTo(const std::string& path) -{ - auto fp = _fopen(path.c_str(), "wb+"); - if (!fp) return false; - - if (fwrite(buffer.data, SIZE(uint8_t), buffer.count, fp) == 0) { - fclose(fp); - return false; - } - fclose(fp); - - return true; -} - - -/* WARNING: Header format shall not changed! */ -bool TvgSaver::writeHeader() -{ - headerSize = TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + SIZE(vsize) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE; - - buffer.grow(headerSize); - - //1. Signature - auto ptr = buffer.ptr(); - memcpy(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH); - ptr += TVG_HEADER_SIGNATURE_LENGTH; - - //2. Version - memcpy(ptr, TVG_HEADER_VERSION, TVG_HEADER_VERSION_LENGTH); - ptr += TVG_HEADER_VERSION_LENGTH; - - buffer.count += (TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH); - - //3. View Size - writeData(vsize, SIZE(vsize)); - ptr += SIZE(vsize); - - //4. Reserved data + Compress size - memset(ptr, 0x00, TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE); - buffer.count += (TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE); - - return true; -} - - -void TvgSaver::writeTag(TvgBinTag tag) -{ - buffer.grow(SIZE(TvgBinTag)); - memcpy(buffer.ptr(), &tag, SIZE(TvgBinTag)); - buffer.count += SIZE(TvgBinTag); -} - - -void TvgSaver::writeCount(TvgBinCounter cnt) -{ - buffer.grow(SIZE(TvgBinCounter)); - memcpy(buffer.ptr(), &cnt, SIZE(TvgBinCounter)); - buffer.count += SIZE(TvgBinCounter); -} - - -void TvgSaver::writeReservedCount(TvgBinCounter cnt) -{ - memcpy(buffer.ptr() - cnt - SIZE(TvgBinCounter), &cnt, SIZE(TvgBinCounter)); -} - - -void TvgSaver::reserveCount() -{ - buffer.grow(SIZE(TvgBinCounter)); - buffer.count += SIZE(TvgBinCounter); -} - - -TvgBinCounter TvgSaver::writeData(const void* data, TvgBinCounter cnt) -{ - buffer.grow(cnt); - memcpy(buffer.ptr(), data, cnt); - buffer.count += cnt; - - return cnt; -} - - -TvgBinCounter TvgSaver::writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data) -{ - auto growCnt = SERIAL_DONE(cnt); - - buffer.grow(growCnt); - - auto ptr = buffer.ptr(); - - *ptr = tag; - ++ptr; - - memcpy(ptr, &cnt, SIZE(TvgBinCounter)); - ptr += SIZE(TvgBinCounter); - - memcpy(ptr, data, cnt); - ptr += cnt; - - buffer.count += growCnt; - - return growCnt; -} - - -TvgBinCounter TvgSaver::writeTransform(const Matrix* transform, TvgBinTag tag) -{ - if (!mathIdentity(transform)) return writeTagProperty(tag, SIZE(Matrix), transform); - return 0; -} - - -TvgBinCounter TvgSaver::serializePaint(const Paint* paint, const Matrix* pTransform) -{ - TvgBinCounter cnt = 0; - - //opacity - auto opacity = paint->opacity(); - if (opacity < 255) { - cnt += writeTagProperty(TVG_TAG_PAINT_OPACITY, SIZE(opacity), &opacity); - } - - //composite - const Paint* cmpTarget = nullptr; - auto cmpMethod = paint->composite(&cmpTarget); - if (cmpMethod != CompositeMethod::None && cmpTarget) { - cnt += serializeComposite(cmpTarget, cmpMethod, pTransform); - } - - return cnt; -} - - -/* Propagate parents properties to the child so that we can skip saving the parent. */ -TvgBinCounter TvgSaver::serializeChild(const Paint* parent, const Paint* child, const Matrix* transform) -{ - const Paint* compTarget = nullptr; - auto compMethod = parent->composite(&compTarget); - - /* If the parent & the only child have composition, we can't skip the parent... - Or if the parent has the transform and composition, we can't skip the parent... */ - if (compMethod != CompositeMethod::None) { - if (transform || child->composite(nullptr) != CompositeMethod::None) return 0; - } - - //propagate opacity - uint32_t opacity = parent->opacity(); - - if (opacity < 255) { - uint32_t tmp = (child->opacity() * opacity); - if (tmp > 0) tmp /= 255; - const_cast(child)->opacity(tmp); - } - - //propagate composition - if (compTarget) const_cast(child)->composite(unique_ptr(compTarget->duplicate()), compMethod); - - return serialize(child, transform); -} - - -TvgBinCounter TvgSaver::serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform) -{ - auto it = IteratorAccessor::iterator(scene); - if (it->count() == 0) { - delete(it); - return 0; - } - - //Case - Only Child: Skip saving this scene. - if (it->count() == 1) { - auto cnt = serializeChild(scene, it->next(), cTransform); - if (cnt > 0) { - delete(it); - return cnt; - } - } - - it->begin(); - - //Case - Delegator Scene: This scene is just a delegator, we can skip this: - if (scene->composite(nullptr) == CompositeMethod::None && scene->opacity() == 255) { - auto ret = serializeChildren(it, cTransform, false); - delete(it); - return ret; - } - - //Case - Serialize Scene & its children - writeTag(TVG_TAG_CLASS_SCENE); - reserveCount(); - - auto cnt = serializeChildren(it, cTransform, true) + serializePaint(scene, pTransform); - - delete(it); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); -} - - -TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform) -{ - const Fill::ColorStop* stops = nullptr; - auto stopsCnt = fill->colorStops(&stops); - if (!stops || stopsCnt == 0) return 0; - - writeTag(tag); - reserveCount(); - - TvgBinCounter cnt = 0; - - //radial fill - if (fill->identifier() == TVG_CLASS_ID_RADIAL) { - float args[3]; - static_cast(fill)->radial(args, args + 1, args + 2); - cnt += writeTagProperty(TVG_TAG_FILL_RADIAL_GRADIENT, SIZE(args), args); - //linear fill - } else { - float args[4]; - static_cast(fill)->linear(args, args + 1, args + 2, args + 3); - cnt += writeTagProperty(TVG_TAG_FILL_LINEAR_GRADIENT, SIZE(args), args); - } - - if (auto flag = static_cast(fill->spread())) - cnt += writeTagProperty(TVG_TAG_FILL_FILLSPREAD, SIZE(TvgBinFlag), &flag); - cnt += writeTagProperty(TVG_TAG_FILL_COLORSTOPS, stopsCnt * SIZE(Fill::ColorStop), stops); - - auto gTransform = fill->transform(); - if (pTransform) gTransform = mathMultiply(pTransform, &gTransform); - - cnt += writeTransform(&gTransform, TVG_TAG_FILL_TRANSFORM); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); -} - - -TvgBinCounter TvgSaver::serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform) -{ - writeTag(TVG_TAG_SHAPE_STROKE); - reserveCount(); - - //width - auto width = shape->strokeWidth(); - if (preTransform) width *= sqrtf(powf(pTransform->e11, 2.0f) + powf(pTransform->e21, 2.0f)); //we know x/y scaling factors are same. - auto cnt = writeTagProperty(TVG_TAG_SHAPE_STROKE_WIDTH, SIZE(width), &width); - - //cap - if (auto flag = static_cast(shape->strokeCap())) - cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_CAP, SIZE(TvgBinFlag), &flag); - - //join - if (auto flag = static_cast(shape->strokeJoin())) - cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_JOIN, SIZE(TvgBinFlag), &flag); - - //fill - if (auto fill = shape->strokeFill()) { - cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL, (preTransform ? pTransform : nullptr)); - } else { - uint8_t color[4] = {0, 0, 0, 0}; - shape->strokeColor(color, color + 1, color + 2, color + 3); - cnt += writeTagProperty(TVG_TAG_SHAPE_STROKE_COLOR, SIZE(color), &color); - } - - //dash - const float* dashPattern = nullptr; - auto dashCnt = shape->strokeDash(&dashPattern); - if (dashPattern && dashCnt > 0) { - TvgBinCounter dashCntSize = SIZE(dashCnt); - TvgBinCounter dashPtrnSize = dashCnt * SIZE(dashPattern[0]); - - writeTag(TVG_TAG_SHAPE_STROKE_DASHPTRN); - writeCount(dashCntSize + dashPtrnSize); - cnt += writeData(&dashCnt, dashCntSize); - cnt += writeData(dashPattern, dashPtrnSize); - cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); - } - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); -} - - -TvgBinCounter TvgSaver::serializePath(const Shape* shape, const Matrix* transform, bool preTransform) -{ - const PathCommand* cmds = nullptr; - auto cmdCnt = shape->pathCommands(&cmds); - const Point* pts = nullptr; - auto ptsCnt = shape->pathCoords(&pts); - - if (!cmds || !pts || cmdCnt == 0 || ptsCnt == 0) return 0; - - writeTag(TVG_TAG_SHAPE_PATH); - reserveCount(); - - /* Reduce the binary size. - Convert PathCommand(4 bytes) to TvgBinFlag(1 byte) */ - TvgBinFlag* outCmds = (TvgBinFlag*)alloca(SIZE(TvgBinFlag) * cmdCnt); - for (uint32_t i = 0; i < cmdCnt; ++i) { - outCmds[i] = static_cast(cmds[i]); - } - - auto cnt = writeData(&cmdCnt, SIZE(cmdCnt)); - cnt += writeData(&ptsCnt, SIZE(ptsCnt)); - cnt += writeData(outCmds, SIZE(TvgBinFlag) * cmdCnt); - - //transform? - if (preTransform) { - if (!mathEqual(transform->e11, 1.0f) || !mathZero(transform->e12) || !mathZero(transform->e13) || - !mathZero(transform->e21) || !mathEqual(transform->e22, 1.0f) || !mathZero(transform->e23) || - !mathZero(transform->e31) || !mathZero(transform->e32) || !mathEqual(transform->e33, 1.0f)) { - auto p = const_cast(pts); - for (uint32_t i = 0; i < ptsCnt; ++i) mathMultiply(p++, transform); - } - } - - cnt += writeData(pts, ptsCnt * SIZE(pts[0])); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); -} - - -TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform) -{ - writeTag(TVG_TAG_CLASS_SHAPE); - reserveCount(); - TvgBinCounter cnt = 0; - - //fill rule - if (auto flag = static_cast(shape->fillRule())) { - cnt = writeTagProperty(TVG_TAG_SHAPE_FILLRULE, SIZE(TvgBinFlag), &flag); - } - - //the pre-transformation can't be applied in the case when the stroke is dashed or irregulary scaled - bool preTransform = true; - - //stroke - if (shape->strokeWidth() > 0) { - uint8_t color[4] = {0, 0, 0, 0}; - shape->strokeColor(color, color + 1, color + 2, color + 3); - auto fill = shape->strokeFill(); - if (fill || color[3] > 0) { - if (!mathEqual(cTransform->e11, cTransform->e22) || (mathZero(cTransform->e11) && !mathEqual(cTransform->e12, cTransform->e21)) || shape->strokeDash(nullptr) > 0) preTransform = false; - cnt += serializeStroke(shape, cTransform, preTransform); - } - } - - //fill - if (auto fill = shape->fill()) { - cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL, (preTransform ? cTransform : nullptr)); - } else { - uint8_t color[4] = {0, 0, 0, 0}; - shape->fillColor(color, color + 1, color + 2, color + 3); - if (color[3] > 0) cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, SIZE(color), color); - } - - cnt += serializePath(shape, cTransform, preTransform); - - if (!preTransform) cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM); - cnt += serializePaint(shape, pTransform); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); -} - - -/* Picture has either a vector scene or a bitmap. */ -TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform) -{ - auto it = IteratorAccessor::iterator(picture); - - //Case - Vector Scene: - if (it->count() == 1) { - auto cnt = serializeChild(picture, it->next(), cTransform); - //Only child, Skip to save Picture... - if (cnt > 0) { - delete(it); - return cnt; - /* Unfortunately, we can't skip the Picture because it might have a compositor, - Serialize Scene(instead of the Picture) & its scene. */ - } else { - writeTag(TVG_TAG_CLASS_SCENE); - reserveCount(); - auto cnt = serializeChildren(it, cTransform, true) + serializePaint(picture, pTransform); - writeReservedCount(cnt); - delete(it); - return SERIAL_DONE(cnt); - } - } - delete(it); - - //Case - Bitmap Image: - uint32_t w, h; - auto pixels = picture->data(&w, &h); - if (!pixels) return 0; - - writeTag(TVG_TAG_CLASS_PICTURE); - reserveCount(); - - TvgBinCounter cnt = 0; - TvgBinCounter sizeCnt = SIZE(w); - TvgBinCounter imgSize = w * h * SIZE(pixels[0]); - - writeTag(TVG_TAG_PICTURE_RAW_IMAGE); - writeCount(2 * sizeCnt + imgSize); - - cnt += writeData(&w, sizeCnt); - cnt += writeData(&h, sizeCnt); - cnt += writeData(pixels, imgSize); - cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); - - //mesh: currently only available in bitmap image. - const Polygon* triangles = nullptr; - auto triangleCnt = picture->mesh(&triangles); - if (triangles && triangleCnt > 0) { - TvgBinCounter triangleCntSize = SIZE(triangleCnt); - TvgBinCounter trianglesSize = triangleCnt * SIZE(triangles[0]); - - writeTag(TVG_TAG_PICTURE_MESH); - writeCount(triangleCntSize + trianglesSize); - cnt += writeData(&triangleCnt, triangleCntSize); - cnt += writeData(triangles, trianglesSize); - cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter); - } - - //Bitmap picture needs the transform info. - cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM); - - cnt += serializePaint(picture, pTransform); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); -} - - -TvgBinCounter TvgSaver::serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform) -{ - writeTag(TVG_TAG_PAINT_CMP_TARGET); - reserveCount(); - - auto flag = static_cast(cmpMethod); - auto cnt = writeTagProperty(TVG_TAG_PAINT_CMP_METHOD, SIZE(TvgBinFlag), &flag); - - cnt += serialize(cmpTarget, pTransform, true); - - writeReservedCount(cnt); - - return SERIAL_DONE(cnt); -} - - -TvgBinCounter TvgSaver::serializeChildren(Iterator* it, const Matrix* pTransform, bool reserved) -{ - TvgBinCounter cnt = 0; - - //Merging shapes. the result is written in the children. - Array children; - children.reserve(it->count()); - children.push(it->next()); - - while (auto child = it->next()) { - if (child->identifier() == TVG_CLASS_ID_SHAPE) { - //only dosable if the previous child is a shape. - auto target = children.ptr() - 1; - if ((*target)->identifier() == TVG_CLASS_ID_SHAPE) { - if (_merge((Shape*)child, (Shape*)*target)) { - continue; - } - } - } - children.push(child); - } - - //The children of a reserved scene - if (reserved && children.count > 1) { - cnt += writeTagProperty(TVG_TAG_SCENE_RESERVEDCNT, SIZE(children.count), &children.count); - } - - //Serialize merged children. - auto child = children.data; - for (uint32_t i = 0; i < children.count; ++i, ++child) { - cnt += serialize(*child, pTransform); - } - - return cnt; -} - - -TvgBinCounter TvgSaver::serialize(const Paint* paint, const Matrix* pTransform, bool compTarget) -{ - if (!paint) return 0; - - //Invisible paint, no point to save it if the paint is not the composition target... - if (!compTarget && paint->opacity() == 0) return 0; - - auto transform = const_cast(paint)->transform(); - if (pTransform) transform = mathMultiply(pTransform, &transform); - - switch (paint->identifier()) { - case TVG_CLASS_ID_SHAPE: return serializeShape(static_cast(paint), pTransform, &transform); - case TVG_CLASS_ID_SCENE: return serializeScene(static_cast(paint), pTransform, &transform); - case TVG_CLASS_ID_PICTURE: return serializePicture(static_cast(paint), pTransform, &transform); - } - - return 0; -} - - -void TvgSaver::run(unsigned tid) -{ - if (!writeHeader()) return; - - //Serialize Root Paint, without its transform. - Matrix transform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; - - if (paint->opacity() > 0) { - switch (paint->identifier()) { - case TVG_CLASS_ID_SHAPE: { - serializeShape(static_cast(paint), nullptr, &transform); - break; - } - case TVG_CLASS_ID_SCENE: { - serializeScene(static_cast(paint), nullptr, &transform); - break; - } - case TVG_CLASS_ID_PICTURE: { - serializePicture(static_cast(paint), nullptr, &transform); - break; - } - } - } - - if (!saveEncoding(path)) return; -} - - -/************************************************************************/ -/* External Class Implementation */ -/************************************************************************/ - -TvgSaver::~TvgSaver() -{ - close(); -} - - -bool TvgSaver::close() -{ - this->done(); - - if (paint) { - delete(paint); - paint = nullptr; - } - if (path) { - free(path); - path = nullptr; - } - buffer.reset(); - return true; -} - - -bool TvgSaver::save(Paint* paint, const string& path, bool compress) -{ - close(); - - float x, y; - x = y = 0; - paint->bounds(&x, &y, &vsize[0], &vsize[1], false); - - //cut off the negative space - if (x < 0) vsize[0] += x; - if (y < 0) vsize[1] += y; - - if (vsize[0] < FLT_EPSILON || vsize[1] < FLT_EPSILON) { - TVGLOG("TVG_SAVER", "Saving paint(%p) has zero view size.", paint); - return false; - } - - this->path = strdup(path.c_str()); - if (!this->path) return false; - - this->paint = paint; - this->compress = compress; - - TaskScheduler::request(this); - - return true; -} diff --git a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h b/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h deleted file mode 100644 index 0824475923cd..000000000000 --- a/thirdparty/thorvg/src/savers/tvg/tvgTvgSaver.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 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_TVGSAVER_H_ -#define _TVG_TVGSAVER_H_ - -#include "tvgArray.h" -#include "tvgBinaryDesc.h" -#include "tvgTaskScheduler.h" - -namespace tvg -{ - -class TvgSaver : public SaveModule, public Task -{ -private: - Array buffer; - Paint* paint = nullptr; - char *path = nullptr; - uint32_t headerSize; - float vsize[2] = {0.0f, 0.0f}; - bool compress; - - bool flushTo(const std::string& path); - bool saveEncoding(const std::string& path); - void reserveCount(); - - bool writeHeader(); - bool writeViewSize(); - void writeTag(TvgBinTag tag); - void writeCount(TvgBinCounter cnt); - void writeReservedCount(TvgBinCounter cnt); - TvgBinCounter writeData(const void* data, TvgBinCounter cnt); - TvgBinCounter writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data); - TvgBinCounter writeTransform(const Matrix* transform, TvgBinTag tag); - - TvgBinCounter serialize(const Paint* paint, const Matrix* pTransform, bool compTarget = false); - TvgBinCounter serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform); - TvgBinCounter serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform); - TvgBinCounter serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform); - TvgBinCounter serializePaint(const Paint* paint, const Matrix* pTransform); - TvgBinCounter serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform); - TvgBinCounter serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform); - TvgBinCounter serializePath(const Shape* shape, const Matrix* transform, bool preTransform); - TvgBinCounter serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform); - TvgBinCounter serializeChildren(Iterator* it, const Matrix* transform, bool reserved); - TvgBinCounter serializeChild(const Paint* parent, const Paint* child, const Matrix* pTransform); - -public: - ~TvgSaver(); - - bool save(Paint* paint, const string& path, bool compress) override; - bool close() override; - void run(unsigned tid) override; -}; - -} - -#endif //_TVG_SAVE_MODULE_H_ diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index 4eb7bf47edc4..f57824872b19 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,31 +1,47 @@ -VERSION=0.9.0 -rm -rf AUTHORS inc LICENSE src *.zip -curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.zip -bsdtar --strip-components=1 -xvf *.zip -rm *.zip -rm -rf .github docs pc res test tools tvgcompat .git* *.md *.txt wasm_build.sh CODEOWNERS -find . -type f -name 'meson.build' -delete -rm -rf src/bin src/bindings src/examples src/wasm -rm -rf src/lib/gl_engine src/loaders/external_jpg src/loaders/png -cat << EOF > inc/config.h -#ifndef THORVG_CONFIG_H -#define THORVG_CONFIG_H +#!/bin/bash -e -#define THORVG_SW_RASTER_SUPPORT 1 +VERSION=0.10.0 -#define THORVG_SVG_LOADER_SUPPORT 1 +rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/ -#define THORVG_PNG_LOADER_SUPPORT 1 +mkdir tmp/ && pushd tmp/ + +curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.tar.gz +tar --strip-components=1 -xvf *.tar.gz +rm *.tar.gz +find . -type f -name 'meson.build' -delete -#define THORVG_TVG_LOADER_SUPPORT 1 +# Fix newline at end of file. +for source in $(find ./ -type f \( -iname \*.h -o -iname \*.cpp \)); do + sed -i -e '$a\' $source +done -#define THORVG_TVG_SAVER_SUPPORT 1 +cp -v AUTHORS LICENSE .. +cp -rv inc ../ -#define THORVG_JPG_LOADER_SUPPORT 1 +cat << EOF > ../inc/config.h +#ifndef THORVG_CONFIG_H +#define THORVG_CONFIG_H + +#define THORVG_SW_RASTER_SUPPORT + +#define THORVG_SVG_LOADER_SUPPORT #define THORVG_VERSION_STRING "$VERSION" #endif EOF -for source in $(find ./ -type f \( -iname \*.h -o -iname \*.cpp \)); do - sed -i -e '$a\' $source -done + +mkdir ../src +cp -rv src/lib ../src/ +# Only sw_engine is enabled. +rm -rfv ../src/lib/gl_engine + +# Only svg loader is enabled. +mkdir ../src/loaders +cp -rv src/loaders/svg src/loaders/raw ../src/loaders/ + +# Future versions +# cp -rv src/utils ../src + +popd +rm -rf tmp/