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 <peter@projectitis.com>
Co-authored-by: Hermet Park <hermetpark@lottiefiles.com>
This commit is contained in:
Hermet Park 2023-06-19 12:29:23 +09:00 committed by Hermet Park
parent eea54f5fea
commit c50d2fde5f
15 changed files with 926 additions and 108 deletions

View file

@ -183,10 +183,38 @@ enum class CompositeMethod
InvAlphaMask, ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value. InvAlphaMask, ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value.
LumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9 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 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 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 * (1 - 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 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(), * 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. * 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 * @since 0.4
*/ */
@ -344,6 +372,21 @@ public:
*/ */
Result composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept; Result composite(std::unique_ptr<Paint> 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. * @brief Gets the bounding box of the paint object before any transformation.
* *
@ -404,6 +447,15 @@ public:
*/ */
CompositeMethod composite(const Paint** target) const noexcept; 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. * @brief Return the unique id value of the paint instance.
* *

View file

@ -129,6 +129,13 @@ ColorSpace GlRenderer::colorSpace()
} }
bool GlRenderer::blend(TVG_UNUSED BlendMethod method)
{
//TODO:
return false;
}
bool GlRenderer::renderImage(TVG_UNUSED void* data) bool GlRenderer::renderImage(TVG_UNUSED void* data)
{ {
//TODO: render requested images //TODO: render requested images

View file

@ -41,6 +41,7 @@ public:
RenderRegion region(RenderData data) override; RenderRegion region(RenderData data) override;
RenderRegion viewport() override; RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override; bool viewport(const RenderRegion& vp) override;
bool blend(BlendMethod method) override;
ColorSpace colorSpace() override; ColorSpace colorSpace() override;
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h); bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h);

View file

@ -239,7 +239,7 @@ struct SwImage
bool scaled = false; //draw scaled image 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 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 typedef uint8_t(*SwAlpha)(uint8_t*); //blending alpha
@ -249,7 +249,9 @@ struct SwSurface : Surface
{ {
SwJoin join; SwJoin join;
SwAlpha alphas[4]; //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5 SwAlpha alphas[4]; //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5
SwBlender blender = nullptr; //blender (optional)
SwCompositor* compositor = nullptr; //compositor (optional) SwCompositor* compositor = nullptr; //compositor (optional)
BlendMethod blendMethod; //blending method (uint8_t)
SwAlpha alpha(CompositeMethod method) SwAlpha alpha(CompositeMethod method)
{ {
@ -279,6 +281,11 @@ static inline SwCoord TO_SWCOORD(float val)
return SwCoord(val * 64.0f); 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) static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
{ {
return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) + 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) 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) 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) static inline uint32_t opBlendNormal(uint32_t s, uint32_t d, uint8_t a)
{ {
auto t = ALPHA_BLEND(s, 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) 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) 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; 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) static inline uint32_t opMaskAdd(uint32_t s, uint32_t d, uint8_t a)
{ {
return opBlendNormal(s, d, 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) 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) static inline uint32_t opMaskDifference(uint32_t s, uint32_t d, uint8_t a)
{ {
auto t = ALPHA_BLEND(s, 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) 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) 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) 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) 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) 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 mathMultiply(int64_t a, int64_t b);
int64_t mathDivide(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); int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
@ -427,8 +562,10 @@ void fillReset(SwFill* fill);
void fillFree(SwFill* fill); void fillFree(SwFill* fill);
//OPTIMIZE_ME: Skip the function pointer access //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, 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 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, 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. void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //masking ver.
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias); SwRleData* rleRender(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 rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h); 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 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 rasterUnpremultiply(Surface* surface);
void rasterPremultiply(Surface* surface); void rasterPremultiply(Surface* surface);
bool rasterConvertCS(Surface* surface, ColorSpace to); bool rasterConvertCS(Surface* surface, ColorSpace to);

View file

@ -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) 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 //Rotation
@ -363,13 +393,9 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
if (mathZero(inc)) { if (mathZero(inc)) {
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)); auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
if (op) {
for (uint32_t i = 0; i < len; ++i, ++dst) { for (uint32_t i = 0; i < len; ++i, ++dst) {
*dst = op(color, *dst, a); *dst = op(color, *dst, a);
} }
} else {
rasterRGBA32(dst, color, 0, len);
}
return; 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<int32_t>(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<float>(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<int32_t>(t * FIXPT_SIZE);
auto inc2 = static_cast<int32_t>(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<int32_t>(t * FIXPT_SIZE);
auto inc2 = static_cast<int32_t>(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) bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
{ {
if (!fill) return false; if (!fill) return false;

View file

@ -48,6 +48,12 @@ struct FillLinear
{ {
fillLinear(fill, dst, y, x, len, cmp, alpha, csize, opacity); 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 struct FillRadial
@ -61,19 +67,24 @@ struct FillRadial
{ {
fillRadial(fill, dst, y, x, len, cmp, alpha, csize, opacity); fillRadial(fill, dst, y, x, len, cmp, alpha, csize, opacity);
} }
void operator()(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
{
fillRadial(fill, dst, y, x, len, op, op2, a);
}
}; };
static bool _rasterDirectRGBAImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint32_t opacity = 255); 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; return *a;
} }
static inline uint8_t IALPHA(uint8_t* a) static inline uint8_t _ialpha(uint8_t* a)
{ {
return ~(*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); 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) static inline bool _compositing(const SwSurface* surface)
{ {
if (!surface->compositor || (int)surface->compositor->method <= (int)CompositeMethod::ClipPath) return false; 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) 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) 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) uint32_t operator()(uint32_t s, uint32_t d, uint8_t a)
{ {
auto t = ALPHA_BLEND(s, 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 */ /* 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<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(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) static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b)
{ {
auto w = static_cast<uint32_t>(region.max.x - region.min.x); auto w = static_cast<uint32_t>(region.max.x - region.min.x);
@ -395,7 +440,7 @@ static bool _rasterSolidRect(SwSurface* surface, const SwBBox& region, uint8_t r
//8bits grayscale //8bits grayscale
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
for (uint32_t y = 0; y < h; ++y) { 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; return true;
} }
@ -408,18 +453,11 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterMattedRect(surface, region, r, g, b, a); if (_matting(surface)) return _rasterMattedRect(surface, region, r, g, b, a);
else return _rasterMaskedRect(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 { } else {
if (a == 255) { if (a == 255) return _rasterSolidRect(surface, region, r, g, b);
return _rasterSolidRect(surface, region, r, g, b); else return _rasterTranslucentRect(surface, region, r, g, b, a);
} 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
}
} }
return false; 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]; auto cmp = &cbuffer[span->y * cstride + span->x];
if (span->coverage == 255) src = color; if (span->coverage == 255) src = color;
else src = ALPHA_BLEND(color, span->coverage); else src = ALPHA_BLEND(color, span->coverage);
auto ialpha = IALPHA(src); auto ialpha = IA(src);
for (auto x = 0; x < span->len; ++x, ++cmp) { for (auto x = 0; x < span->len; ++x, ++cmp) {
*cmp = maskOp()(src, *cmp, ialpha); *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 (y == span->y && x == span->x && x + span->len <= surface->compositor->bbox.max.x) {
if (span->coverage == 255) src = color; if (span->coverage == 255) src = color;
else src = ALPHA_BLEND(color, span->coverage); else src = ALPHA_BLEND(color, span->coverage);
auto alpha = ALPHA(src); auto alpha = A(src);
for (uint32_t i = 0; i < span->len; ++i) { for (uint32_t i = 0; i < span->len; ++i) {
cmp[x + i] = ALPHA_BLEND(cmp[x + i], alpha); 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) static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b)
{ {
auto span = rle->spans; auto span = rle->spans;
@ -564,7 +639,7 @@ static bool _rasterSolidRle(SwSurface* surface, const SwRleData* rle, uint8_t r,
//8bit grayscale //8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
for (uint32_t i = 0; i < rle->size; ++i, ++span) { 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; return true;
@ -578,18 +653,11 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g,
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterMattedRle(surface, rle, r, g, b, a); if (_matting(surface)) return _rasterMattedRle(surface, rle, r, g, b, a);
else return _rasterMaskedRle(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 { } else {
if (a == 255) { if (a == 255) return _rasterSolidRle(surface, rle, r, g, b);
return _rasterSolidRle(surface, rle, r, g, b); else return _rasterTranslucentRle(surface, rle, r, g, b, a);
} 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
}
} }
return false; return false;
} }
@ -666,14 +734,14 @@ static void _rasterScaledMaskedRleRGBAImageInt(SwSurface* surface, const SwImage
auto sx = (x + i) * itransform->e11 + itransform->e13; auto sx = (x + i) * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); 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 { } else {
for (uint32_t i = 0; i < span->len; ++i) { for (uint32_t i = 0; i < span->len; ++i) {
auto sx = (x + i) * itransform->e11 + itransform->e13; auto sx = (x + i) * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); 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; x += span->len - 1;
@ -724,7 +792,7 @@ static bool _rasterScaledMattedRleRGBAImage(SwSurface* surface, const SwImage* i
auto sx = x * itransform->e11 + itransform->e13; auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; 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)); 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 { } else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) { for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(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; if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale);
auto tmp = ALPHA_BLEND(src, MULTIPLY(alpha(cmp), a)); 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<uint32_t>(span->x); x < static_cast<uint32_t>(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<uint32_t>(span->x); x < static_cast<uint32_t>(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<uint32_t>(span->x); x < static_cast<uint32_t>(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) 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; auto span = image->rle->spans;
@ -757,14 +865,14 @@ static bool _rasterScaledRleRGBAImage(SwSurface* surface, const SwImage* image,
auto sx = x * itransform->e11 + itransform->e13; auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); 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 { } else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) { for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13; auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; if ((uint32_t)sx >= image->w) continue;
auto src = ALPHA_BLEND(scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale), alpha); 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 (_compositing(surface)) {
if (_matting(surface)) _rasterScaledMattedRleRGBAImage(surface, image, &itransform, region, opacity, halfScale); if (_matting(surface)) _rasterScaledMattedRleRGBAImage(surface, image, &itransform, region, opacity, halfScale);
else _rasterScaledMaskedRleRGBAImage(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 { } else {
return _rasterScaledRleRGBAImage(surface, image, &itransform, region, opacity, halfScale); 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); auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) { if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) {
*cmp = maskOp()(*src, *cmp, IALPHA(*src)); *cmp = maskOp()(*src, *cmp, IA(*src));
} }
} else { } else {
for (uint32_t x = 0; x < span->len; ++x, ++src, ++cmp) { 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); auto src = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
if (alpha == 255) { if (alpha == 255) {
for (uint32_t i = 0; i < span->len; ++i, ++src) { 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 { } else {
for (uint32_t i = 0; i < span->len; ++i, ++src) { for (uint32_t i = 0; i < span->len; ++i, ++src) {
auto t = ALPHA_BLEND(*src, alpha); 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; x += span->len;
@ -888,12 +998,41 @@ static bool _rasterDirectMattedRleRGBAImage(SwSurface* surface, const SwImage* i
if (a == 255) { if (a == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) {
auto tmp = ALPHA_BLEND(*img, alpha(cmp)); auto tmp = ALPHA_BLEND(*img, alpha(cmp));
*dst = tmp + ALPHA_BLEND(*dst, IALPHA(tmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
} }
} else { } else {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) {
auto tmp = ALPHA_BLEND(*img, MULTIPLY(a, alpha(cmp))); 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 img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto alpha = MULTIPLY(span->coverage, opacity); auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) { 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 { } else {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) { for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
auto src = ALPHA_BLEND(*img, alpha); 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 (_compositing(surface)) {
if (_matting(surface)) return _rasterDirectMattedRleRGBAImage(surface, image, opacity); if (_matting(surface)) return _rasterDirectMattedRleRGBAImage(surface, image, opacity);
else return _rasterDirectMaskedRleRGBAImage(surface, image, opacity); else return _rasterDirectMaskedRleRGBAImage(surface, image, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingRleRGBAImage(surface, image, opacity);
} else { } else {
return _rasterDirectRleRGBAImage(surface, image, opacity); return _rasterDirectRleRGBAImage(surface, image, opacity);
} }
@ -978,7 +1121,7 @@ static void _rasterScaledMaskedRGBAImageDup(SwSurface* surface, const SwImage* i
auto sx = x * itransform->e11 + itransform->e13; auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); 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 { } else {
for (auto x = region.min.x; x < region.max.x; ++x, ++cmp) { 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; auto sx = (x + i) * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); 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 { } else {
for (uint32_t i = 0; i < w; ++i, ++tmp) { for (uint32_t i = 0; i < w; ++i, ++tmp) {
auto sx = (x + i) * itransform->e11 + itransform->e13; auto sx = (x + i) * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; 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 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; x += w;
@ -1085,7 +1228,7 @@ static bool _rasterScaledMattedRGBAImage(SwSurface* surface, const SwImage* imag
if ((uint32_t)sx >= image->w) continue; if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale);
auto temp = ALPHA_BLEND(src, alpha(cmp)); auto temp = ALPHA_BLEND(src, alpha(cmp));
*dst = temp + ALPHA_BLEND(*dst, IALPHA(temp)); *dst = temp + ALPHA_BLEND(*dst, IA(temp));
} }
} else { } else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) { 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; if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale);
auto temp = ALPHA_BLEND(src, MULTIPLY(opacity, alpha(cmp))); 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; 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) 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); 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; auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; if ((uint32_t)sx >= image->w) continue;
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, halfScale); 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 { } else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) { for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
auto sx = x * itransform->e11 + itransform->e13; auto sx = x * itransform->e11 + itransform->e13;
if ((uint32_t)sx >= image->w) continue; 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 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 (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedRGBAImage(surface, image, &itransform, region, opacity, halfScale); if (_matting(surface)) return _rasterScaledMattedRGBAImage(surface, image, &itransform, region, opacity, halfScale);
else return _rasterScaledMaskedRGBAImage(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 { } else {
return _rasterScaledRGBAImage(surface, image, &itransform, region, opacity, halfScale); return _rasterScaledRGBAImage(surface, image, &itransform, region, opacity, halfScale);
} }
@ -1171,7 +1347,7 @@ static void _rasterDirectMaskedRGBAImageDup(SwSurface* surface, const SwImage* i
auto src = sbuffer; auto src = sbuffer;
if (opacity == 255) { if (opacity == 255) {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) {
*cmp = maskOp()(*src, *cmp, IALPHA(*src)); *cmp = maskOp()(*src, *cmp, IA(*src));
} }
} else { } else {
for (uint32_t x = 0; x < w; ++x, ++src, ++cmp) { 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)]; auto src = &image->buf32[(y2 + image->oy) * image->stride + (x + image->ox)];
if (opacity == 255) { if (opacity == 255) {
for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) { for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) {
*tmp = ALPHA_BLEND(*tmp, ALPHA(*src)); *tmp = ALPHA_BLEND(*tmp, A(*src));
} }
} else { } else {
for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) { for (uint32_t i = 0; i < w; ++i, ++tmp, ++src) {
auto t = ALPHA_BLEND(*src, opacity); auto t = ALPHA_BLEND(*src, opacity);
*tmp = ALPHA_BLEND(*tmp, ALPHA(t)); *tmp = ALPHA_BLEND(*tmp, A(t));
} }
} }
x += w; x += w;
@ -1265,12 +1441,12 @@ static bool _rasterDirectMattedRGBAImage(SwSurface* surface, const SwImage* imag
if (opacity == 255) { if (opacity == 255) {
for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
auto tmp = ALPHA_BLEND(*src, alpha(cmp)); auto tmp = ALPHA_BLEND(*src, alpha(cmp));
*dst = tmp + ALPHA_BLEND(*dst, IALPHA(tmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
} }
} else { } else {
for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) { for (uint32_t x = 0; x < w; ++x, ++dst, ++src, cmp += csize) {
auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp))); 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; 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) 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]; 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; auto src = sbuffer;
if (opacity == 255) { if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { 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 { } else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
auto tmp = ALPHA_BLEND(*src, opacity); auto tmp = ALPHA_BLEND(*src, opacity);
*dst = tmp + ALPHA_BLEND(*dst, IALPHA(tmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
} }
} }
dbuffer += surface->stride; dbuffer += surface->stride;
@ -1312,6 +1515,8 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterDirectMattedRGBAImage(surface, image, region, opacity); if (_matting(surface)) return _rasterDirectMattedRGBAImage(surface, image, region, opacity);
else return _rasterDirectMaskedRGBAImage(surface, image, region, opacity); else return _rasterDirectMaskedRGBAImage(surface, image, region, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingRGBAImage(surface, image, region, opacity);
} else { } else {
return _rasterDirectRGBAImage(surface, image, region, opacity); return _rasterDirectRGBAImage(surface, image, region, opacity);
} }
@ -1429,6 +1634,25 @@ static bool _rasterGradientMattedRect(SwSurface* surface, const SwBBox& region,
} }
template<typename fillMethod>
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<uint32_t>(region.max.x - region.min.x);
auto h = static_cast<uint32_t>(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<typename fillMethod> template<typename fillMethod>
static bool _rasterTranslucentGradientRect(SwSurface* surface, const SwBBox& region, const SwFill* fill) 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 (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRect<FillLinear>(surface, region, fill); if (_matting(surface)) return _rasterGradientMattedRect<FillLinear>(surface, region, fill);
else return _rasterGradientMaskedRect<FillLinear>(surface, region, fill); else return _rasterGradientMaskedRect<FillLinear>(surface, region, fill);
} else if (_blending(surface)) {
return _rasterBlendingGradientRect<FillLinear>(surface, region, fill);
} else { } else {
if (fill->translucent) return _rasterTranslucentGradientRect<FillLinear>(surface, region, fill); if (fill->translucent) return _rasterTranslucentGradientRect<FillLinear>(surface, region, fill);
else _rasterSolidGradientRect<FillLinear>(surface, region, fill); else _rasterSolidGradientRect<FillLinear>(surface, region, fill);
@ -1480,6 +1706,8 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region,
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRect<FillRadial>(surface, region, fill); if (_matting(surface)) return _rasterGradientMattedRect<FillRadial>(surface, region, fill);
else return _rasterGradientMaskedRect<FillRadial>(surface, region, fill); else return _rasterGradientMaskedRect<FillRadial>(surface, region, fill);
} else if (_blending(surface)) {
return _rasterBlendingGradientRect<FillRadial>(surface, region, fill);
} else { } else {
if (fill->translucent) return _rasterTranslucentGradientRect<FillRadial>(surface, region, fill); if (fill->translucent) return _rasterTranslucentGradientRect<FillRadial>(surface, region, fill);
else _rasterSolidGradientRect<FillRadial>(surface, region, fill); else _rasterSolidGradientRect<FillRadial>(surface, region, fill);
@ -1568,6 +1796,19 @@ static bool _rasterGradientMattedRle(SwSurface* surface, const SwRleData* rle, c
} }
template<typename fillMethod>
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<typename fillMethod> template<typename fillMethod>
static bool _rasterTranslucentGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) 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 (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRle<FillLinear>(surface, rle, fill); if (_matting(surface)) return _rasterGradientMattedRle<FillLinear>(surface, rle, fill);
else return _rasterGradientMaskedRle<FillLinear>(surface, rle, fill); else return _rasterGradientMaskedRle<FillLinear>(surface, rle, fill);
} else if (_blending(surface)) {
return _rasterBlendingGradientRle<FillLinear>(surface, rle, fill);
} else { } else {
if (fill->translucent) return _rasterTranslucentGradientRle<FillLinear>(surface, rle, fill); if (fill->translucent) return _rasterTranslucentGradientRle<FillLinear>(surface, rle, fill);
else return _rasterSolidGradientRle<FillLinear>(surface, rle, fill); else return _rasterSolidGradientRle<FillLinear>(surface, rle, fill);
@ -1618,6 +1861,8 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill); if (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill);
else return _rasterGradientMaskedRle<FillRadial>(surface, rle, fill); else return _rasterGradientMaskedRle<FillRadial>(surface, rle, fill);
} else if (_blending(surface)) {
_rasterBlendingGradientRle<FillRadial>(surface, rle, fill);
} else { } else {
if (fill->translucent) _rasterTranslucentGradientRle<FillRadial>(surface, rle, fill); if (fill->translucent) _rasterTranslucentGradientRle<FillRadial>(surface, rle, fill);
else return _rasterSolidGradientRle<FillRadial>(surface, rle, fill); else return _rasterSolidGradientRle<FillRadial>(surface, rle, fill);
@ -1625,10 +1870,19 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c
return false; return false;
} }
/************************************************************************/ /************************************************************************/
/* External Class Implementation */ /* 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) void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
{ {
#if defined(THORVG_AVX_VECTOR_SUPPORT) #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) bool rasterCompositor(SwSurface* surface)
{ {
//See CompositeMethod, Alpha:3, InvAlpha:4, Luma:5, InvLuma:6 //See CompositeMethod, Alpha:3, InvAlpha:4, Luma:5, InvLuma:6
surface->alphas[0] = ALPHA; surface->alphas[0] = _alpha;
surface->alphas[1] = IALPHA; surface->alphas[1] = _ialpha;
if (surface->cs == ColorSpace::ABGR8888 || surface->cs == ColorSpace::ABGR8888S) { if (surface->cs == ColorSpace::ABGR8888 || surface->cs == ColorSpace::ABGR8888S) {
surface->join = _abgrJoin; 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)) { } else if (surface->channelSize == sizeof(uint8_t)) {
//full clear //full clear
if (w == surface->stride) { if (w == surface->stride) {
_rasterGrayscale8(surface->buf8, 0x00, surface->stride * y, w * h); rasterGrayscale8(surface->buf8, 0x00, surface->stride * y, w * h);
//partial clear //partial clear
} else { } else {
for (uint32_t i = 0; i < h; i++) { 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);
} }
} }
} }

View file

@ -74,7 +74,7 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rl
auto dst = &surface->buf32[span->y * surface->stride + span->x]; auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color; else src = color;
auto ialpha = IALPHA(src); auto ialpha = IA(src);
for (uint32_t x = 0; x < span->len; ++x, ++dst) { for (uint32_t x = 0; x < span->len; ++x, ++dst) {
*dst = src + ALPHA_BLEND(*dst, ialpha); *dst = src + ALPHA_BLEND(*dst, ialpha);
} }
@ -103,9 +103,9 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi
//32bits channels //32bits channels
if (surface->channelSize == sizeof(uint32_t)) { 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 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) { for (uint32_t y = 0; y < h; ++y) {
auto dst = &buffer[y * surface->stride]; auto dst = &buffer[y * surface->stride];
for (uint32_t x = 0; x < w; ++x, ++dst) { for (uint32_t x = 0; x < w; ++x, ++dst) {

View file

@ -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 buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto h = static_cast<uint32_t>(region.max.y - region.min.y); auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x); auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto ialpha = IALPHA(color); auto ialpha = 255 - a;
auto vColor = vdup_n_u32(color); auto vColor = vdup_n_u32(color);
auto vIalpha = vdup_n_u8((uint8_t) ialpha); auto vIalpha = vdup_n_u8((uint8_t) ialpha);

View file

@ -183,7 +183,7 @@ static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImag
} }
px = INTERPOLATE(px, px2, ab); px = INTERPOLATE(px, px2, ab);
} }
cmp[x] = ALPHA_BLEND(cmp[x], ALPHA(px)); cmp[x] = ALPHA_BLEND(cmp[x], A(px));
//Step UV horizontally //Step UV horizontally
u += _dudx; u += _dudx;
@ -219,7 +219,7 @@ static void _rasterMaskedPolygonImageSegmentInt(SwSurface* surface, const SwImag
} }
px = INTERPOLATE(px, px2, ab); 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 //Step UV horizontally
u += _dudx; u += _dudx;
@ -348,7 +348,7 @@ static void _rasterMaskedPolygonImageSegmentDup(SwSurface* surface, const SwImag
} }
px = INTERPOLATE(px, px2, ab); px = INTERPOLATE(px, px2, ab);
} }
*cmp = maskOp()(px, *cmp, IALPHA(px)); *cmp = maskOp()(px, *cmp, IA(px));
++cmp; ++cmp;
//Step UV horizontally //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<int32_t>(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) 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; float _dudx = dudx, _dvdx = dvdx;
@ -549,7 +726,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
} else { } else {
src = px; src = px;
} }
*buf = src + ALPHA_BLEND(*buf, IALPHA(src)); *buf = src + ALPHA_BLEND(*buf, IA(src));
++buf; ++buf;
//Step UV horizontally //Step UV horizontally
@ -599,7 +776,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
} else { } else {
src = ALPHA_BLEND(px, opacity); src = ALPHA_BLEND(px, opacity);
} }
*buf = src + ALPHA_BLEND(*buf, IALPHA(src)); *buf = src + ALPHA_BLEND(*buf, IA(src));
++buf; ++buf;
//Step UV horizontally //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 regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
auto compositing = _compositing(surface); //Composition required auto compositing = _compositing(surface); //Composition required
auto blending = _blending(surface); //Blending required
//Longer edge is on the left side //Longer edge is on the left side
if (!side) { if (!side) {
@ -721,8 +899,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); 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 _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; upper = true;
} }
//Draw lower segment if possibly visible //Draw lower segment if possibly visible
@ -739,7 +920,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); 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 _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 //Longer edge is on the right side
} else { } else {
@ -765,8 +950,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); 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 _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; upper = true;
} }
//Draw lower segment if possibly visible //Draw lower segment if possibly visible
@ -786,7 +974,11 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); 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 _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);
}
} }
} }
} }

View file

@ -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) RenderRegion SwRenderer::region(RenderData data)
{ {
return static_cast<SwTask*>(data)->bounds(); return static_cast<SwTask*>(data)->bounds();

View file

@ -47,6 +47,7 @@ public:
RenderRegion region(RenderData data) override; RenderRegion region(RenderData data) override;
RenderRegion viewport() override; RenderRegion viewport() override;
bool viewport(const RenderRegion& vp) override; bool viewport(const RenderRegion& vp) override;
bool blend(BlendMethod method) override;
ColorSpace colorSpace() override; ColorSpace colorSpace() override;
bool clear() override; bool clear() override;
@ -70,7 +71,6 @@ private:
Array<SwSurface*> compositors; //render targets cache list Array<SwSurface*> compositors; //render targets cache list
SwMpool* mpool; //private memory pool SwMpool* mpool; //private memory pool
RenderRegion vport; //viewport RenderRegion vport; //viewport
bool sharedMpool = true; //memory-pool behavior policy bool sharedMpool = true; //memory-pool behavior policy
SwRenderer(); SwRenderer();

View file

@ -176,6 +176,7 @@ bool Paint::Impl::render(RenderMethod& renderer)
if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity); if (cmp) renderer.beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
renderer.blend(blendMethod);
auto ret = smethod->render(renderer); auto ret = smethod->render(renderer);
if (cmp) renderer.endComposite(cmp); if (cmp) renderer.endComposite(cmp);
@ -417,3 +418,17 @@ uint32_t Paint::identifier() const noexcept
{ {
return pImpl->id; return pImpl->id;
} }
Result Paint::blend(BlendMethod method) const noexcept
{
pImpl->blendMethod = method;
return Result::Success;
}
BlendMethod Paint::blend() const noexcept
{
return pImpl->blendMethod;
}

View file

@ -63,6 +63,7 @@ namespace tvg
StrategyMethod* smethod = nullptr; StrategyMethod* smethod = nullptr;
RenderTransform* rTransform = nullptr; RenderTransform* rTransform = nullptr;
Composite* compData = nullptr; Composite* compData = nullptr;
BlendMethod blendMethod = BlendMethod::Normal; //uint8_t
uint8_t renderFlag = RenderUpdateFlag::None; uint8_t renderFlag = RenderUpdateFlag::None;
uint8_t ctxFlag = ContextFlag::Invalid; uint8_t ctxFlag = ContextFlag::Invalid;
uint8_t id; uint8_t id;

View file

@ -250,6 +250,7 @@ public:
virtual RenderRegion region(RenderData data) = 0; virtual RenderRegion region(RenderData data) = 0;
virtual RenderRegion viewport() = 0; virtual RenderRegion viewport() = 0;
virtual bool viewport(const RenderRegion& vp) = 0; virtual bool viewport(const RenderRegion& vp) = 0;
virtual bool blend(BlendMethod method) = 0;
virtual ColorSpace colorSpace() = 0; virtual ColorSpace colorSpace() = 0;
virtual bool clear() = 0; virtual bool clear() = 0;

View file

@ -100,6 +100,9 @@ struct Scene::Impl
auto compMethod = scene->composite(nullptr); auto compMethod = scene->composite(nullptr);
if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true; 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. //Half translucent requires intermediate composition.
if (opacity == 255) return false; if (opacity == 255) return false;