mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
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:
parent
eea54f5fea
commit
c50d2fde5f
15 changed files with 926 additions and 108 deletions
60
inc/thorvg.h
60
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<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.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,13 +393,9 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
|||
|
||||
if (mathZero(inc)) {
|
||||
auto color = _fixedPixel(fill, static_cast<int32_t>(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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (!fill) return false;
|
||||
|
|
|
@ -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<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)
|
||||
{
|
||||
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
|
||||
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<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;
|
||||
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<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)
|
||||
{
|
||||
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<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), 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<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>
|
||||
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<FillLinear>(surface, region, fill);
|
||||
else return _rasterGradientMaskedRect<FillLinear>(surface, region, fill);
|
||||
} else if (_blending(surface)) {
|
||||
return _rasterBlendingGradientRect<FillLinear>(surface, region, fill);
|
||||
} else {
|
||||
if (fill->translucent) return _rasterTranslucentGradientRect<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 (_matting(surface)) return _rasterGradientMattedRect<FillRadial>(surface, region, fill);
|
||||
else return _rasterGradientMaskedRect<FillRadial>(surface, region, fill);
|
||||
} else if (_blending(surface)) {
|
||||
return _rasterBlendingGradientRect<FillRadial>(surface, region, fill);
|
||||
} else {
|
||||
if (fill->translucent) return _rasterTranslucentGradientRect<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>
|
||||
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<FillLinear>(surface, rle, fill);
|
||||
else return _rasterGradientMaskedRle<FillLinear>(surface, rle, fill);
|
||||
} else if (_blending(surface)) {
|
||||
return _rasterBlendingGradientRle<FillLinear>(surface, rle, fill);
|
||||
} else {
|
||||
if (fill->translucent) return _rasterTranslucentGradientRle<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 (_matting(surface)) return _rasterGradientMattedRle<FillRadial>(surface, rle, fill);
|
||||
else return _rasterGradientMaskedRle<FillRadial>(surface, rle, fill);
|
||||
} else if (_blending(surface)) {
|
||||
_rasterBlendingGradientRle<FillRadial>(surface, rle, fill);
|
||||
} else {
|
||||
if (fill->translucent) _rasterTranslucentGradientRle<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;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<uint32_t>(region.max.y - region.min.y);
|
||||
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 vIalpha = vdup_n_u8((uint8_t) ialpha);
|
||||
|
|
|
@ -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<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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<SwTask*>(data)->bounds();
|
||||
|
|
|
@ -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<SwSurface*> compositors; //render targets cache list
|
||||
SwMpool* mpool; //private memory pool
|
||||
RenderRegion vport; //viewport
|
||||
|
||||
bool sharedMpool = true; //memory-pool behavior policy
|
||||
|
||||
SwRenderer();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue