From 07dfa5dc05a12605f7e60ac3870f8071a736465c Mon Sep 17 00:00:00 2001 From: Michal Maciola Date: Thu, 30 Dec 2021 09:28:27 +0100 Subject: [PATCH] compositeMethod: introduced LumaMask Introduced CompositeMethod::LumaMask that converts the source pixels to the grayscale (luma value) before alpha blending. Thanks to it, mask works more like typical mask in graphics editor software. Grayscale is calculated with weighted method: (0.0721*B + 0.7154*G + 0.2125*R) * A Introduced surface->blender.lumaValue function Change-Id: Ia0258e2f374aa27acaef9b2f4161af5b04400422 --- inc/thorvg.h | 5 ++-- src/lib/sw_engine/tvgSwCommon.h | 1 + src/lib/sw_engine/tvgSwRaster.cpp | 54 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index e542d36..b08356d 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -145,8 +145,9 @@ enum class TVG_EXPORT CompositeMethod { None = 0, ///< No composition is applied. ClipPath, ///< The intersection of the source and the target is determined and only the resulting pixels from the source are rendered. - AlphaMask, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which intersects with the target is visible. - InvAlphaMask ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which is not covered by the target is visible. + AlphaMask, ///< The pixels of the source and the target are alpha blended. As a result, only the part of the source, which alpha intersects with the target is visible. + InvAlphaMask, ///< The pixels of the source and the complement to the target's pixels are alpha blended. As a result, only the part of the source which alpha is not covered by the target is visible. + LumaMask ///< @BETA_API The source pixels are converted to the grayscale (luma value) and alpha blended with the target. As a result, only the part of the source, which intersects with the target is visible. }; /** diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index e0ffc1f..4d940eb 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -235,6 +235,7 @@ struct SwImage struct SwBlender { uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + uint32_t (*lumaValue)(uint32_t c); }; struct SwCompositor; diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 56bc2f7..6b5e00a 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -47,6 +47,18 @@ static inline uint32_t _ialpha(uint32_t c) } +static inline uint32_t _abgrLumaValue(uint32_t c) +{ + return ((((c&0xff)*54) + (((c>>8)&0xff)*183) + (((c>>16)&0xff)*19))) >> 8; //0.2125*R + 0.7154*G + 0.0721*B +} + + +static inline uint32_t _argbLumaValue(uint32_t c) +{ + return ((((c&0xff)*19) + (((c>>8)&0xff)*183) + (((c>>16)&0xff)*54))) >> 8; //0.0721*B + 0.7154*G + 0.2125*R +} + + static inline uint32_t _abgrJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return (a << 24 | b << 16 | g << 8 | r); @@ -139,7 +151,7 @@ static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, uint32_t auto w = static_cast(region.max.x - region.min.x); auto h = static_cast(region.max.y - region.min.y); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer + auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer for (uint32_t y = 0; y < h; ++y) { auto dst = &buffer[y * surface->stride]; @@ -173,6 +185,8 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint32_t color return _rasterMaskedRect(surface, region, color, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterMaskedRect(surface, region, color, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterMaskedRect(surface, region, color, surface->blender.lumaValue); } } else { if (opacity == 255) { @@ -246,6 +260,8 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint32_t color, uint8 return _rasterMaskedRle(surface, rle, color, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterMaskedRle(surface, rle, color, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterMaskedRle(surface, rle, color, surface->blender.lumaValue); } } else { if (opacity == 255) { @@ -275,6 +291,8 @@ static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, c return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, surface->blender.lumaValue); } } else { return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, nullptr); @@ -494,12 +512,16 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.lumaValue); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.lumaValue); } } } else { @@ -616,12 +638,16 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32 return _rasterDirectMaskedRleRGBAImage(surface, image, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedRleRGBAImage(surface, image, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterDirectMaskedRleRGBAImage(surface, image, surface->blender.lumaValue); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, surface->blender.lumaValue); } } } else { @@ -643,6 +669,8 @@ static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, cons return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, surface->blender.lumaValue); } } else { return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, nullptr); @@ -832,12 +860,16 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.lumaValue); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.lumaValue); } } } else { @@ -861,7 +893,7 @@ static bool _rasterDirectMaskedRGBAImage(SwSurface* surface, const SwImage* imag auto w2 = static_cast(region.max.x - region.min.x); auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer + auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer for (uint32_t y = 0; y < h2; ++y) { auto dst = buffer; @@ -888,7 +920,7 @@ static bool _rasterDirectMaskedTranslucentRGBAImage(SwSurface* surface, const Sw auto w2 = static_cast(region.max.x - region.min.x); auto sbuffer = image->data + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox); - auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer + auto cbuffer = surface->compositor->image.data + (region.min.y * surface->compositor->image.stride) + region.min.x; //compositor buffer for (uint32_t y = 0; y < h2; ++y) { auto dst = buffer; @@ -952,12 +984,16 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB return _rasterDirectMaskedRGBAImage(surface, image, region, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedRGBAImage(surface, image, region, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterDirectMaskedRGBAImage(surface, image, region, surface->blender.lumaValue); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, surface->blender.lumaValue); } } } else { @@ -1062,6 +1098,8 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, return _rasterLinearGradientMaskedRect(surface, region, fill, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterLinearGradientMaskedRect(surface, region, fill, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterLinearGradientMaskedRect(surface, region, fill, surface->blender.lumaValue); } } else { if (fill->translucent) return _rasterTranslucentLinearGradientRect(surface, region, fill); @@ -1166,6 +1204,8 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c return _rasterLinearGradientMaskedRle(surface, rle, fill, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterLinearGradientMaskedRle(surface, rle, fill, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterLinearGradientMaskedRle(surface, rle, fill, surface->blender.lumaValue); } } else { if (fill->translucent) return _rasterTranslucentLinearGradientRle(surface, rle, fill); @@ -1253,6 +1293,8 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, return _rasterRadialGradientMaskedRect(surface, region, fill, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterRadialGradientMaskedRect(surface, region, fill, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterRadialGradientMaskedRect(surface, region, fill, surface->blender.lumaValue); } } else { if (fill->translucent) return _rasterTranslucentRadialGradientRect(surface, region, fill); @@ -1356,6 +1398,8 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c return _rasterRadialGradientMaskedRle(surface, rle, fill, _alpha); } else if (surface->compositor->method == CompositeMethod::InvAlphaMask) { return _rasterRadialGradientMaskedRle(surface, rle, fill, _ialpha); + } else if (surface->compositor->method == CompositeMethod::LumaMask) { + return _rasterRadialGradientMaskedRle(surface, rle, fill, surface->blender.lumaValue); } } else { if (fill->translucent) _rasterTranslucentRadialGradientRle(surface, rle, fill); @@ -1385,8 +1429,10 @@ bool rasterCompositor(SwSurface* surface) { if (surface->cs == SwCanvas::ABGR8888 || surface->cs == SwCanvas::ABGR8888_STRAIGHT) { surface->blender.join = _abgrJoin; + surface->blender.lumaValue = _abgrLumaValue; } else if (surface->cs == SwCanvas::ARGB8888 || surface->cs == SwCanvas::ARGB8888_STRAIGHT) { surface->blender.join = _argbJoin; + surface->blender.lumaValue = _argbLumaValue; } else { //What Color Space ??? return false; @@ -1500,4 +1546,4 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co //TODO: case: _rasterGrayscaleImage() //TODO: case: _rasterAlphaImage() return _rasterRGBAImage(surface, image, transform, bbox, opacity); -} \ No newline at end of file +} -- 2.7.4