From 21c7debf14c889b2e97c0bb3ace32495eaec07f4 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 18 May 2023 14:06:53 +0900 Subject: [PATCH] composite: Support Inverse Luma Masking The CompositeMethod now includes the newly supported InvLumaMask option: The source pixels are converted to grayscale (luma values), and the complement of the target's pixels is alpha blended. As a result, only the part of the source where the grayscale is not covered by the target is visible. @APIs: CompositeMethod::InvLumaMask @Example: examples/InvLumaMasking.cpp @Issue: https://github.com/thorvg/thorvg/issues/404 --- inc/thorvg.h | 3 +- src/lib/sw_engine/tvgSwCommon.h | 1 + src/lib/sw_engine/tvgSwRaster.cpp | 48 +++++++++++++++++++++++++++++++ src/lib/tvgRender.h | 1 + 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index 994e686b..97990a8e 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -170,7 +170,8 @@ enum class CompositeMethod 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 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 ///< 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. @since 0.9 + LumaMask, ///< 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. @since 0.9 + InvLumaMask ///< The source pixels are converted to the grayscale (luma value) and the complement to the target's pixels are alpha blended. As a result, only the part of the source which grayscale is not covered by the target is visible. @BETA_API }; /** diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index 0e9029bf..b488eb4d 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -242,6 +242,7 @@ struct SwBlender { uint32_t (*join)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); uint8_t (*luma)(uint8_t* c); + uint8_t (*iluma)(uint8_t* c); }; struct SwCompositor; diff --git a/src/lib/sw_engine/tvgSwRaster.cpp b/src/lib/sw_engine/tvgSwRaster.cpp index 98bd7524..f158fa5c 100644 --- a/src/lib/sw_engine/tvgSwRaster.cpp +++ b/src/lib/sw_engine/tvgSwRaster.cpp @@ -76,6 +76,18 @@ static inline uint8_t _argbLuma(uint8_t* c) } +static inline uint8_t _abgrInvLuma(uint8_t* c) +{ + return ~_abgrLuma(c); +} + + +static inline uint8_t _argbInvLuma(uint8_t* c) +{ + return ~_argbLuma(c); +} + + 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); @@ -234,6 +246,8 @@ static bool _rasterRect(SwSurface* surface, const SwBBox& region, uint8_t r, uin return _rasterMaskedRect(surface, region, r, g, b, a, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterMaskedRect(surface, region, r, g, b, a, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterMaskedRect(surface, region, r, g, b, a, surface->blender.iluma); } } else { if (a == 255) { @@ -342,6 +356,8 @@ static bool _rasterRle(SwSurface* surface, SwRleData* rle, uint8_t r, uint8_t g, return _rasterMaskedRle(surface, rle, r, g, b, a, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterMaskedRle(surface, rle, r, g, b, a, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterMaskedRle(surface, rle, r, g, b, a, surface->blender.iluma); } } else { if (a == 255) { @@ -373,6 +389,8 @@ static bool _transformedRleRGBAImage(SwSurface* surface, const SwImage* image, c return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, surface->blender.iluma); } } else { return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity, nullptr); @@ -596,6 +614,8 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterScaledMaskedRleRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.iluma); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { @@ -604,6 +624,8 @@ static bool _scaledRleRGBAImage(SwSurface* surface, const SwImage* image, const 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.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterScaledMaskedTranslucentRleRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.iluma); } } } else { @@ -724,6 +746,8 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32 return _rasterDirectMaskedRleRGBAImage(surface, image, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterDirectMaskedRleRGBAImage(surface, image, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterDirectMaskedRleRGBAImage(surface, image, surface->blender.iluma); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { @@ -732,6 +756,8 @@ static bool _directRleRGBAImage(SwSurface* surface, const SwImage* image, uint32 return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterDirectMaskedTranslucentRleRGBAImage(surface, image, opacity, surface->blender.iluma); } } } else { @@ -755,6 +781,8 @@ static bool _transformedRGBAImage(SwSurface* surface, const SwImage* image, cons return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, surface->blender.iluma); } } else { return _rasterTexmapPolygon(surface, image, transform, ®ion, opacity, nullptr); @@ -771,6 +799,8 @@ static bool _transformedRGBAImageMesh(SwSurface* surface, const SwImage* image, return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, surface->blender.iluma); } } else { return _rasterTexmapPolygonMesh(surface, image, mesh, transform, region, opacity, nullptr); @@ -963,6 +993,8 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterScaledMaskedRGBAImage(surface, image, &itransform, region, halfScale, surface->blender.iluma); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { @@ -971,6 +1003,8 @@ static bool _scaledRGBAImage(SwSurface* surface, const SwImage* image, const Mat 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.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterScaledMaskedTranslucentRGBAImage(surface, image, &itransform, region, opacity, halfScale, surface->blender.iluma); } } } else { @@ -1089,6 +1123,8 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB return _rasterDirectMaskedRGBAImage(surface, image, region, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterDirectMaskedRGBAImage(surface, image, region, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterDirectMaskedRGBAImage(surface, image, region, surface->blender.iluma); } } else { if (surface->compositor->method == CompositeMethod::AlphaMask) { @@ -1097,6 +1133,8 @@ static bool _directRGBAImage(SwSurface* surface, const SwImage* image, const SwB return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterDirectMaskedTranslucentRGBAImage(surface, image, region, opacity, surface->blender.iluma); } } } else { @@ -1204,6 +1242,8 @@ static bool _rasterLinearGradientRect(SwSurface* surface, const SwBBox& region, return _rasterLinearGradientMaskedRect(surface, region, fill, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterLinearGradientMaskedRect(surface, region, fill, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterLinearGradientMaskedRect(surface, region, fill, surface->blender.iluma); } } else { if (fill->translucent) return _rasterTranslucentLinearGradientRect(surface, region, fill); @@ -1311,6 +1351,8 @@ static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, c return _rasterLinearGradientMaskedRle(surface, rle, fill, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterLinearGradientMaskedRle(surface, rle, fill, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterLinearGradientMaskedRle(surface, rle, fill, surface->blender.iluma); } } else { if (fill->translucent) return _rasterTranslucentLinearGradientRle(surface, rle, fill); @@ -1401,6 +1443,8 @@ static bool _rasterRadialGradientRect(SwSurface* surface, const SwBBox& region, return _rasterRadialGradientMaskedRect(surface, region, fill, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterRadialGradientMaskedRect(surface, region, fill, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterRadialGradientMaskedRect(surface, region, fill, surface->blender.iluma); } } else { if (fill->translucent) return _rasterTranslucentRadialGradientRect(surface, region, fill); @@ -1507,6 +1551,8 @@ static bool _rasterRadialGradientRle(SwSurface* surface, const SwRleData* rle, c return _rasterRadialGradientMaskedRle(surface, rle, fill, _ialpha); } else if (surface->compositor->method == CompositeMethod::LumaMask) { return _rasterRadialGradientMaskedRle(surface, rle, fill, surface->blender.luma); + } else if (surface->compositor->method == CompositeMethod::InvLumaMask) { + return _rasterRadialGradientMaskedRle(surface, rle, fill, surface->blender.iluma); } } else { if (fill->translucent) _rasterTranslucentRadialGradientRle(surface, rle, fill); @@ -1536,9 +1582,11 @@ bool rasterCompositor(SwSurface* surface) if (surface->cs == ColorSpace::ABGR8888 || surface->cs == ColorSpace::ABGR8888S) { surface->blender.join = _abgrJoin; surface->blender.luma = _abgrLuma; + surface->blender.iluma = _abgrInvLuma; } else if (surface->cs == ColorSpace::ARGB8888 || surface->cs == ColorSpace::ARGB8888S) { surface->blender.join = _argbJoin; surface->blender.luma = _argbLuma; + surface->blender.iluma = _argbInvLuma; } else { TVGERR("SW_ENGINE", "Unsupported Colorspace(%d) is expected!", surface->cs); return false; diff --git a/src/lib/tvgRender.h b/src/lib/tvgRender.h index 6270fa53..e4445c57 100644 --- a/src/lib/tvgRender.h +++ b/src/lib/tvgRender.h @@ -262,6 +262,7 @@ static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod& renderer, Composi case CompositeMethod::InvAlphaMask: return ColorSpace::Grayscale8; case CompositeMethod::LumaMask: + case CompositeMethod::InvLumaMask: return renderer.colorSpace(); default: TVGERR("COMMON", "Unsupported Composite Size! = %d", (int)method);