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;