From c50d2fde5fcf4ff60765e8903fd1218165a53765 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 19 Jun 2023 12:29:23 +0900 Subject: [PATCH] common sw_engine: support blending methods. The blending feature allows user 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. @APIs: - enum class BlendMethod::Normal, Add, Screen, Multiply, Overlay, Lighten, Difference, Exclusion, SrcOver, Darken, Lighten, ColorDodge, ColorBurn - BlendMethod Paint::blend() const noexcept; - Result Paint::blend(BlendMethod method) const noexcept; @Issue: https://github.com/thorvg/thorvg/issues/307 Co-authored-by: Peter Vullings Co-authored-by: Hermet Park --- inc/thorvg.h | 60 +++- src/lib/gl_engine/tvgGlRenderer.cpp | 7 + src/lib/gl_engine/tvgGlRenderer.h | 1 + src/lib/sw_engine/tvgSwCommon.h | 168 ++++++++++- src/lib/sw_engine/tvgSwFill.cpp | 113 +++++++- src/lib/sw_engine/tvgSwRaster.cpp | 388 +++++++++++++++++++++----- src/lib/sw_engine/tvgSwRasterC.h | 6 +- src/lib/sw_engine/tvgSwRasterNeon.h | 2 +- src/lib/sw_engine/tvgSwRasterTexmap.h | 214 +++++++++++++- src/lib/sw_engine/tvgSwRenderer.cpp | 53 ++++ src/lib/sw_engine/tvgSwRenderer.h | 2 +- src/lib/tvgPaint.cpp | 15 + src/lib/tvgPaint.h | 1 + src/lib/tvgRender.h | 1 + src/lib/tvgSceneImpl.h | 3 + 15 files changed, 926 insertions(+), 108 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index 6edd4ee1..ade17420 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -183,10 +183,38 @@ enum class CompositeMethod 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 * (1 - TA)) @BETA_API - SubtractMask, ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (1 - TA)) @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 * (1 - TA)) @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) }; @@ -316,7 +344,7 @@ public: * 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 */ @@ -344,6 +372,21 @@ public: */ 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. * @@ -404,6 +447,15 @@ public: */ 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. * diff --git a/src/lib/gl_engine/tvgGlRenderer.cpp b/src/lib/gl_engine/tvgGlRenderer.cpp index 7df3b16c..03448627 100644 --- a/src/lib/gl_engine/tvgGlRenderer.cpp +++ b/src/lib/gl_engine/tvgGlRenderer.cpp @@ -129,6 +129,13 @@ ColorSpace GlRenderer::colorSpace() } +bool GlRenderer::blend(TVG_UNUSED BlendMethod method) +{ + //TODO: + return false; +} + + bool GlRenderer::renderImage(TVG_UNUSED void* data) { //TODO: render requested images diff --git a/src/lib/gl_engine/tvgGlRenderer.h b/src/lib/gl_engine/tvgGlRenderer.h index 041f8f2c..aba83e3e 100644 --- a/src/lib/gl_engine/tvgGlRenderer.h +++ b/src/lib/gl_engine/tvgGlRenderer.h @@ -41,6 +41,7 @@ public: RenderRegion region(RenderData data) override; RenderRegion viewport() override; bool viewport(const RenderRegion& vp) override; + bool blend(BlendMethod method) override; ColorSpace colorSpace() override; bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h); diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 1e060346..95af89a1 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -239,7 +239,7 @@ struct SwImage bool scaled = false; //draw scaled image }; -typedef uint32_t(*SwBlender)(uint32_t s, uint32_t d, uint8_t sa); //src, dst, alpha +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 @@ -249,7 +249,9 @@ struct SwSurface : Surface { 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) { @@ -279,6 +281,11 @@ 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) + @@ -302,17 +309,32 @@ static inline SwCoord HALF_STROKE(float width) static inline uint8_t MULTIPLY(uint8_t c, uint8_t a) { - return ((c * a + 0xff) >> 8); + return (((c) * (a) + 0xff) >> 8); } -static inline uint8_t ALPHA(uint32_t c) +static inline uint8_t A(uint32_t c) { - return (c >> 24); + return ((c) >> 24); } -static inline uint8_t IALPHA(uint32_t c) +static inline uint8_t IA(uint32_t c) { - return (~c >> 24); + 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) @@ -323,12 +345,12 @@ static inline uint32_t opBlendInterp(uint32_t s, uint32_t d, uint8_t 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, IALPHA(t)); + 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, IALPHA(s)); + 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) @@ -336,6 +358,120 @@ static inline uint32_t opBlendSrcOver(uint32_t s, TVG_UNUSED uint32_t d, TVG_UNU 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); @@ -343,18 +479,18 @@ static inline uint32_t opMaskAdd(uint32_t s, uint32_t d, uint8_t a) static inline uint32_t opMaskSubtract(uint32_t s, uint32_t d, uint8_t a) { - return ALPHA_BLEND(d, MULTIPLY(IALPHA(s), 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, IALPHA(d)) + ALPHA_BLEND(d, IALPHA(t)); + 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(IALPHA(s), 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) @@ -364,20 +500,19 @@ static inline uint32_t opMaskPreAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a static inline uint32_t opMaskPreSubtract(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) { - return ALPHA_BLEND(d, IALPHA(s)); + 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, IALPHA(d)) + ALPHA_BLEND(d, IALPHA(s)); + 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, IALPHA(s))); + 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); @@ -427,8 +562,10 @@ void fillReset(SwFill* fill); void fillFree(SwFill* fill); //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); @@ -455,6 +592,7 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint 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 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/src/lib/sw_engine/tvgSwFill.cpp b/src/lib/sw_engine/tvgSwFill.cpp index b003b5b1..1c6eb4e4 100644 --- a/src/lib/sw_engine/tvgSwFill.cpp +++ b/src/lib/sw_engine/tvgSwFill.cpp @@ -279,6 +279,36 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 } +void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a) +{ + 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 @@ -363,12 +393,8 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 if (mathZero(inc)) { auto color = _fixedPixel(fill, static_cast(t * FIXPT_SIZE)); - if (op) { - for (uint32_t i = 0; i < len; ++i, ++dst) { - *dst = op(color, *dst, a); - } - } else { - rasterRGBA32(dst, color, 0, len); + for (uint32_t i = 0; i < len; ++i, ++dst) { + *dst = op(color, *dst, a); } return; } @@ -397,6 +423,81 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3 } +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/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 90a213b3..1cb6d2e5 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -48,6 +48,12 @@ struct FillLinear { fillLinear(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) + { + fillLinear(fill, dst, y, x, len, op, op2, a); + } + }; struct FillRadial @@ -61,19 +67,24 @@ struct FillRadial { 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 _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity = 255); -static inline uint8_t ALPHA(uint8_t* a) +static inline uint8_t _alpha(uint8_t* a) { return *a; } -static inline uint8_t IALPHA(uint8_t* a) +static inline uint8_t _ialpha(uint8_t* a) { return ~(*a); } @@ -116,7 +127,14 @@ 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); } +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 || (int)surface->compositor->method <= (int)CompositeMethod::ClipPath) return false; @@ -160,7 +178,7 @@ struct DifMaskOp { uint32_t operator()(uint32_t s, uint32_t d, uint8_t a) { - return ALPHA_BLEND(s, IALPHA(d)) + ALPHA_BLEND(d, a); + return ALPHA_BLEND(s, IA(d)) + ALPHA_BLEND(d, a); } }; @@ -178,7 +196,7 @@ struct SubMaskAOp { uint32_t operator()(uint32_t s, uint32_t d, uint8_t a) { - return ALPHA_BLEND(d, IALPHA(ALPHA_BLEND(s, a))); + return ALPHA_BLEND(d, IA(ALPHA_BLEND(s, a))); } }; @@ -188,7 +206,7 @@ struct DifMaskAOp uint32_t operator()(uint32_t s, uint32_t d, uint8_t a) { auto t = ALPHA_BLEND(s, a); - return ALPHA_BLEND(t, IALPHA(d)) + ALPHA_BLEND(d, IALPHA(t)); + return ALPHA_BLEND(t, IA(d)) + ALPHA_BLEND(d, IA(t)); } }; @@ -259,11 +277,6 @@ static uint32_t _interpDownScaler(const uint32_t *img, uint32_t stride, uint32_t } -void _rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len) -{ - cRasterPixels(dst, val, offset, len); -} - /************************************************************************/ /* Rect */ /************************************************************************/ @@ -378,6 +391,38 @@ static bool _rasterMattedRect(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); @@ -395,7 +440,7 @@ static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r //8bits grayscale if (surface->channelSize == sizeof(uint8_t)) { for (uint32_t y = 0; y < h; ++y) { - _rasterGrayscale8(surface->buf8, 255, region.min.y * surface->stride + region.min.x, w); + rasterGrayscale8(surface->buf8, 255, region.min.y * surface->stride + region.min.x, w); } return true; } @@ -408,18 +453,11 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin if (_compositing(surface)) { 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; } @@ -442,7 +480,7 @@ static void _rasterMaskedRleDup(SwSurface* surface, SwRleData* rle, uint8_t r, u auto cmp = &cbuffer[span->y * cstride + span->x]; if (span->coverage == 255) src = color; else src = ALPHA_BLEND(color, span->coverage); - auto ialpha = IALPHA(src); + auto ialpha = IA(src); for (auto x = 0; x < span->len; ++x, ++cmp) { *cmp = maskOp()(src, *cmp, ialpha); } @@ -465,7 +503,7 @@ static void _rasterMaskedRleInt(SwSurface* surface, SwRleData* rle, uint8_t r, u 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 = ALPHA(src); + auto alpha = A(src); for (uint32_t i = 0; i < span->len; ++i) { cmp[x + i] = ALPHA_BLEND(cmp[x + i], alpha); } @@ -542,6 +580,43 @@ static bool _rasterMattedRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint } +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); + } + } + } + return true; +} + + +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; @@ -564,7 +639,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) { - _rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len); + rasterGrayscale8(surface->buf8, span->coverage, span->y * surface->stride + span->x, span->len); } } return true; @@ -578,18 +653,11 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, if (_compositing(surface)) { 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; } @@ -666,14 +734,14 @@ static void _rasterScaledMaskedRleRGBAImageInt(SwSurface* surface, const SwImage 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, halfScale); - cmp[x + i] = ALPHA_BLEND(cmp[x + i], ALPHA(src)); + 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, halfScale); - cmp[x + i] = ALPHA_BLEND(cmp[x + i], ALPHA(ALPHA_BLEND(src, alpha))); + cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(ALPHA_BLEND(src, alpha))); } } x += span->len - 1; @@ -724,7 +792,7 @@ static bool _rasterScaledMattedRleRGBAImage(SwSurface* surface, const SwImage* i 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, halfScale), alpha(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, IALPHA(tmp)); + *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) { @@ -732,7 +800,7 @@ static bool _rasterScaledMattedRleRGBAImage(SwSurface* surface, const SwImage* i if ((uint32_t)sx >= image->w) continue; auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); auto tmp = ALPHA_BLEND(src, MULTIPLY(alpha(cmp), a)); - *dst = tmp + ALPHA_BLEND(*dst, IALPHA(tmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } } @@ -741,6 +809,46 @@ static bool _rasterScaledMattedRleRGBAImage(SwSurface* surface, const SwImage* i } +static bool _rasterScaledBlendingRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale) +{ + auto span = image->rle->spans; + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + + 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, halfScale); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, A(src)); + } + } 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 = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); + 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, halfScale), opacity); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, MULTIPLY(span->coverage, A(src))); + } + } + } + return true; +} + + static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale) { auto span = image->rle->spans; @@ -757,14 +865,14 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image, 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, halfScale); - *dst = src + ALPHA_BLEND(*dst, IALPHA(src)); + *dst = src + ALPHA_BLEND(*dst, IA(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, halfScale), alpha); - *dst = src + ALPHA_BLEND(*dst, IALPHA(src)); + *dst = src + ALPHA_BLEND(*dst, IA(src)); } } } @@ -785,6 +893,8 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const if (_compositing(surface)) { if (_matting(surface)) _rasterScaledMattedRleRGBAImage(surface, image, &itransform, region, opacity, halfScale); else _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, opacity, halfScale); + } else if (_blending(surface)) { + return _rasterScaledBlendingRleRGBAImage(surface, image, &itransform, region, opacity, halfScale); } else { return _rasterScaledRleRGBAImage(surface, image, &itransform, region, opacity, halfScale); } @@ -809,7 +919,7 @@ static void _rasterDirectMaskedRleRGBAImageDup(SwSurface* surface, const SwImage auto alpha = MULTIPLY(span->coverage, opacity); if (alpha == 255) { for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { - *cmp = maskOp()(*src, *cmp, IALPHA(*src)); + *cmp = maskOp()(*src, *cmp, IA(*src)); } } else { for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { @@ -835,12 +945,12 @@ static void _rasterDirectMaskedRleRGBAImageInt(SwSurface* surface, const SwImage 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], ALPHA(*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], ALPHA(t)); + cmp[x + i] = ALPHA_BLEND(cmp[x + i], A(t)); } } x += span->len; @@ -888,12 +998,41 @@ static bool _rasterDirectMattedRleRGBAImage(SwSurface* surface, const SwImage* i if (a == 255) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { auto tmp = ALPHA_BLEND(*img, alpha(cmp)); - *dst = tmp + ALPHA_BLEND(*dst, IALPHA(tmp)); + *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(a, alpha(cmp))); - *dst = tmp + ALPHA_BLEND(*dst, IALPHA(tmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); + } + } + } + return true; +} + + +static bool _rasterDirectBlendingRleRGBAImage(SwSurface* surface, const SwImage* image, uint32_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); + 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))); } } } @@ -910,11 +1049,13 @@ static bool _rasterDirectRleRGBAImage(SwSurface* surface, const SwImage* image, auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox); auto alpha = MULTIPLY(span->coverage, opacity); if (alpha == 255) { - *dst = *img + ALPHA_BLEND(*dst, IALPHA(*img)); + for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { + *dst = *img + ALPHA_BLEND(*dst, IA(*img)); + } } else { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { auto src = ALPHA_BLEND(*img, alpha); - *dst = src + ALPHA_BLEND(*dst, IALPHA(src)); + *dst = src + ALPHA_BLEND(*dst, IA(src)); } } } @@ -927,6 +1068,8 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32 if (_compositing(surface)) { if (_matting(surface)) return _rasterDirectMattedRleRGBAImage(surface, image, opacity); else return _rasterDirectMaskedRleRGBAImage(surface, image, opacity); + } else if (_blending(surface)) { + return _rasterDirectBlendingRleRGBAImage(surface, image, opacity); } else { return _rasterDirectRleRGBAImage(surface, image, opacity); } @@ -978,7 +1121,7 @@ static void _rasterScaledMaskedRGBAImageDup(SwSurface* surface, const SwImage* i 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, halfScale); - *cmp = maskOp()(src, *cmp, IALPHA(src)); + *cmp = maskOp()(src, *cmp, IA(src)); } } else { for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) { @@ -1015,14 +1158,14 @@ static void _rasterScaledMaskedRGBAImageInt(SwSurface* surface, const SwImage* 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, halfScale); - *tmp = ALPHA_BLEND(*tmp, ALPHA(src)); + *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, halfScale), opacity); - *tmp = ALPHA_BLEND(*tmp, ALPHA(src)); + *tmp = ALPHA_BLEND(*tmp, A(src)); } } x += w; @@ -1085,7 +1228,7 @@ static bool _rasterScaledMattedRGBAImage(SwSurface* surface, const SwImage* imag if ((uint32_t)sx >= image->w) continue; auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); auto temp = ALPHA_BLEND(src, alpha(cmp)); - *dst = temp + ALPHA_BLEND(*dst, IALPHA(temp)); + *dst = temp + ALPHA_BLEND(*dst, IA(temp)); } } else { for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { @@ -1093,7 +1236,7 @@ static bool _rasterScaledMattedRGBAImage(SwSurface* surface, const SwImage* imag if ((uint32_t)sx >= image->w) continue; auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); auto temp = ALPHA_BLEND(src, MULTIPLY(opacity, alpha(cmp))); - *dst = temp + ALPHA_BLEND(*dst, IALPHA(temp)); + *dst = temp + ALPHA_BLEND(*dst, IA(temp)); } } dbuffer += surface->stride; @@ -1103,6 +1246,37 @@ static bool _rasterScaledMattedRGBAImage(SwSurface* surface, const SwImage* imag } +static bool _rasterScaledBlendingRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale) +{ + auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); + auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler; + + 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 = 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, halfScale); + 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) { + 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, halfScale), opacity); + auto tmp = surface->blender(src, *dst, 255); + *dst = INTERPOLATE(tmp, *dst, A(src)); + } + } + } + return true; +} + + static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint32_t opacity, uint32_t halfScale) { auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x); @@ -1117,14 +1291,14 @@ static bool _rasterScaledRGBAImage(SwSurface* surface, const SwImage* image, con 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, halfScale); - *dst = src + ALPHA_BLEND(*dst, IALPHA(src)); + *dst = src + ALPHA_BLEND(*dst, IA(src)); } } 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(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), opacity); - *dst = src + ALPHA_BLEND(*dst, IALPHA(src)); + *dst = src + ALPHA_BLEND(*dst, IA(src)); } } } @@ -1145,6 +1319,8 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat if (_compositing(surface)) { if (_matting(surface)) return _rasterScaledMattedRGBAImage(surface, image, &itransform, region, opacity, halfScale); else return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, opacity, halfScale); + } else if (_blending(surface)) { + return _rasterScaledBlendingRGBAImage(surface, image, &itransform, region, opacity, halfScale); } else { return _rasterScaledRGBAImage(surface, image, &itransform, region, opacity, halfScale); } @@ -1171,7 +1347,7 @@ static void _rasterDirectMaskedRGBAImageDup(SwSurface* surface, const SwImage* i auto src = sbuffer; if (opacity == 255) { for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { - *cmp = maskOp()(*src, *cmp, IALPHA(*src)); + *cmp = maskOp()(*src, *cmp, IA(*src)); } } else { for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { @@ -1202,12 +1378,12 @@ static void _rasterDirectMaskedRGBAImageInt(SwSurface* surface, const SwImage* i 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, ALPHA(*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, ALPHA(t)); + *tmp = ALPHA_BLEND(*tmp, A(t)); } } x += w; @@ -1265,12 +1441,12 @@ static bool _rasterDirectMattedRGBAImage(SwSurface* surface, const SwImage* imag 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, IALPHA(tmp)); + *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, IALPHA(tmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } buffer += surface->stride; @@ -1281,6 +1457,33 @@ static bool _rasterDirectMattedRGBAImage(SwSurface* surface, const SwImage* imag } +static bool _rasterDirectBlendingRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_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); + + for (auto y = region.min.y; y < region.max.y; ++y) { + auto dst = dbuffer; + auto src = sbuffer; + if (opacity == 255) { + for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { + 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; + } + return true; +} + + static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity) { auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x]; @@ -1291,12 +1494,12 @@ static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, con auto src = sbuffer; if (opacity == 255) { for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { - *dst = *src + ALPHA_BLEND(*dst, IALPHA(*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, IALPHA(tmp)); + *dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); } } dbuffer += surface->stride; @@ -1312,6 +1515,8 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB if (_compositing(surface)) { if (_matting(surface)) return _rasterDirectMattedRGBAImage(surface, image, region, opacity); else return _rasterDirectMaskedRGBAImage(surface, image, region, opacity); + } else if (_blending(surface)) { + return _rasterDirectBlendingRGBAImage(surface, image, region, opacity); } else { return _rasterDirectRGBAImage(surface, image, region, opacity); } @@ -1429,6 +1634,25 @@ static bool _rasterGradientMattedRect(SwSurface* surface, const SwBBox& region, } +template +static bool _rasterBlendingGradientRect(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); + + 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 { + 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 true; +} + template static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) { @@ -1465,6 +1689,8 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, 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); @@ -1480,6 +1706,8 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, 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); @@ -1568,6 +1796,19 @@ static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, c } +template +static bool _rasterBlendingGradientRle(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]; + fillMethod()(fill, dst, span->y, span->x, span->len, opBlendPreNormal, surface->blender, span->coverage); + } + return true; +} + + template static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { @@ -1603,6 +1844,8 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c 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); @@ -1618,6 +1861,8 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c if (_compositing(surface)) { 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) _rasterTranslucentGradientRle(surface, rle, fill); else return _rasterSolidGradientRle(surface, rle, fill); @@ -1625,10 +1870,19 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c return false; } + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ + +void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len) +{ + //OPTIMIZE_ME: Support SIMD + cRasterPixels(dst, val, offset, len); +} + + void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) { #if defined(THORVG_AVX_VECTOR_SUPPORT) @@ -1644,8 +1898,8 @@ void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) bool rasterCompositor(SwSurface* surface) { //See CompositeMethod, Alpha:3, InvAlpha:4, Luma:5, InvLuma:6 - surface->alphas[0] = ALPHA; - surface->alphas[1] = IALPHA; + surface->alphas[0] = _alpha; + surface->alphas[1] = _ialpha; if (surface->cs == ColorSpace::ABGR8888 || surface->cs == ColorSpace::ABGR8888S) { surface->join = _abgrJoin; @@ -1682,11 +1936,11 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_ } else if (surface->channelSize == sizeof(uint8_t)) { //full clear if (w == surface->stride) { - _rasterGrayscale8(surface->buf8, 0x00, surface->stride * y, w * h); + rasterGrayscale8(surface->buf8, 0x00, surface->stride * y, w * h); //partial clear } else { for (uint32_t i = 0; i < h; i++) { - _rasterGrayscale8(surface->buf8, 0x00, (surface->stride * y + x) + (surface->stride * i), w); + rasterGrayscale8(surface->buf8, 0x00, (surface->stride * y + x) + (surface->stride * i), w); } } } diff --git a/src/lib/sw_engine/tvgSwRasterC.h b/src/lib/sw_engine/tvgSwRasterC.h index b429028c..6501bd83 100644 --- a/src/lib/sw_engine/tvgSwRasterC.h +++ b/src/lib/sw_engine/tvgSwRasterC.h @@ -74,7 +74,7 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rl 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 = IALPHA(src); + auto ialpha = IA(src); for (uint32_t x = 0; x < span->len; ++x, ++dst) { *dst = src + ALPHA_BLEND(*dst, ialpha); } @@ -103,9 +103,9 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi //32bits channels if (surface->channelSize == sizeof(uint32_t)) { - auto color = surface->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) { diff --git a/src/lib/sw_engine/tvgSwRasterNeon.h b/src/lib/sw_engine/tvgSwRasterNeon.h index e3e68437..14d6d030 100644 --- a/src/lib/sw_engine/tvgSwRasterNeon.h +++ b/src/lib/sw_engine/tvgSwRasterNeon.h @@ -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 = IALPHA(color); + auto ialpha = 255 - a; auto vColor = vdup_n_u32(color); auto vIalpha = vdup_n_u8((uint8_t) ialpha); diff --git a/src/lib/sw_engine/tvgSwRasterTexmap.h b/src/lib/sw_engine/tvgSwRasterTexmap.h index 2fee2a46..0da146d1 100644 --- a/src/lib/sw_engine/tvgSwRasterTexmap.h +++ b/src/lib/sw_engine/tvgSwRasterTexmap.h @@ -183,7 +183,7 @@ static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImag } px = INTERPOLATE(px, px2, ab); } - cmp[x] = ALPHA_BLEND(cmp[x], ALPHA(px)); + cmp[x] = ALPHA_BLEND(cmp[x], A(px)); //Step UV horizontally u += _dudx; @@ -219,7 +219,7 @@ static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImag } px = INTERPOLATE(px, px2, ab); } - cmp[x] = ALPHA_BLEND(cmp[x], MULTIPLY(ALPHA(px), opacity)); + cmp[x] = ALPHA_BLEND(cmp[x], MULTIPLY(A(px), opacity)); //Step UV horizontally u += _dudx; @@ -348,7 +348,7 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag } px = INTERPOLATE(px, px2, ab); } - *cmp = maskOp()(px, *cmp, IALPHA(px)); + *cmp = maskOp()(px, *cmp, IA(px)); ++cmp; //Step UV horizontally @@ -431,6 +431,183 @@ static void _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* } +static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) +{ + 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; + 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); + } + *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; + 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); + } + 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, uint8_t opacity, bool matting) { float _dudx = dudx, _dvdx = dvdx; @@ -549,7 +726,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, } else { src = px; } - *buf = src + ALPHA_BLEND(*buf, IALPHA(src)); + *buf = src + ALPHA_BLEND(*buf, IA(src)); ++buf; //Step UV horizontally @@ -599,7 +776,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, } else { src = ALPHA_BLEND(px, opacity); } - *buf = src + ALPHA_BLEND(*buf, IALPHA(src)); + *buf = src + ALPHA_BLEND(*buf, IA(src)); ++buf; //Step UV horizontally @@ -693,6 +870,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const 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) { @@ -721,8 +899,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const 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 _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); - + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); + } else { + _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); + } upper = true; } //Draw lower segment if possibly visible @@ -739,7 +920,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const 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 _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); + } else { + _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); + } } //Longer edge is on the right side } else { @@ -765,8 +950,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const 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 _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); - + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); + } else { + _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); + } upper = true; } //Draw lower segment if possibly visible @@ -786,7 +974,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const 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 _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); + } else if (blending) { + _rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); + } else { + _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); + } } } } diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 3f421765..30546093 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -499,6 +499,59 @@ bool SwRenderer::renderShape(RenderData data) } +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(); diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 0fdbecd4..4393740b 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -47,6 +47,7 @@ public: 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; @@ -70,7 +71,6 @@ private: Array compositors; //render targets cache list SwMpool* mpool; //private memory pool RenderRegion vport; //viewport - bool sharedMpool = true; //memory-pool behavior policy SwRenderer(); diff --git a/src/lib/tvgPaint.cpp b/src/lib/tvgPaint.cpp index 537e06d1..6580b53a 100644 --- a/src/lib/tvgPaint.cpp +++ b/src/lib/tvgPaint.cpp @@ -176,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); @@ -417,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; +} \ No newline at end of file diff --git a/src/lib/tvgPaint.h b/src/lib/tvgPaint.h index 30349870..c5d24b6b 100644 --- a/src/lib/tvgPaint.h +++ b/src/lib/tvgPaint.h @@ -63,6 +63,7 @@ namespace tvg StrategyMethod* smethod = nullptr; RenderTransform* rTransform = nullptr; Composite* compData = nullptr; + BlendMethod blendMethod = BlendMethod::Normal; //uint8_t uint8_t renderFlag = RenderUpdateFlag::None; uint8_t ctxFlag = ContextFlag::Invalid; uint8_t id; diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index b477e92c..e0fb43d8 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -250,6 +250,7 @@ public: 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; diff --git a/src/lib/tvgSceneImpl.h b/src/lib/tvgSceneImpl.h index 9ff45b21..8fa38bcc 100644 --- a/src/lib/tvgSceneImpl.h +++ b/src/lib/tvgSceneImpl.h @@ -100,6 +100,9 @@ struct Scene::Impl 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;