From 371139e220952ff41d9ccedde4af816ba76f331d Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 9 Sep 2024 18:16:22 +0900 Subject: [PATCH] renderer: blending refactoring++ - reordered the blending types to align with lottie spec. - removed source over. --- examples/Blending.cpp | 7 ----- inc/thorvg.h | 16 ++++++----- src/loaders/lottie/tvgLottieParser.cpp | 31 +--------------------- src/loaders/lottie/tvgLottieParser.h | 1 - src/renderer/sw_engine/tvgSwRenderer.cpp | 29 ++++++++++---------- src/renderer/wg_engine/tvgWgCompositor.cpp | 1 - src/renderer/wg_engine/tvgWgPipelines.cpp | 27 ++++++++++--------- src/renderer/wg_engine/tvgWgPipelines.h | 16 +++++------ src/renderer/wg_engine/tvgWgShaderSrc.cpp | 7 ----- test/testPaint.cpp | 4 --- test/testSwEngine.cpp | 1 - 11 files changed, 49 insertions(+), 91 deletions(-) diff --git a/examples/Blending.cpp b/examples/Blending.cpp index 8d5c3942..c7ecb1a0 100644 --- a/examples/Blending.cpp +++ b/examples/Blending.cpp @@ -108,13 +108,6 @@ struct UserExample : tvgexam::Example shape7->fill(0, 0, 255); canvas->push(std::move(shape7)); - //SrcOver - auto shape8 = tvg::Shape::gen(); - shape8->appendRect(550, 600, 150, 150); - shape8->blend(tvg::BlendMethod::SrcOver); - shape8->fill(10, 255, 155, 50); - canvas->push(std::move(shape8)); - //Darken auto shape9 = tvg::Shape::gen(); shape9->appendRect(600, 650, 350, 250); diff --git a/inc/thorvg.h b/inc/thorvg.h index e72cdecc..85f864fb 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -183,19 +183,23 @@ enum class CompositeMethod 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) + Screen, ///< The values of the pixels in the two layers are inverted, multiplied, and then inverted again. (S + D) - (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) + 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) + 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) + Hue, ///< Reserved. Not supported. + Saturation, ///< Reserved. Not supported. + Color, ///< Reserved. Not supported. + Luminosity, ///< Reserved. Not supported. + Add, ///< Simply adds pixel values of one layer with the other. (S + D) + HardMix ///< Reserved. Not supported. }; diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index 9b5523ec..6bdfd6a6 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -77,35 +77,6 @@ CompositeMethod LottieParser::getMaskMethod(bool inversed) } -BlendMethod LottieParser::getBlendMethod() -{ - switch (getInt()) { - case 0: return BlendMethod::Normal; - case 1: return BlendMethod::Multiply; - case 2: return BlendMethod::Screen; - case 3: return BlendMethod::Overlay; - case 4: return BlendMethod::Darken; - case 5: return BlendMethod::Lighten; - case 6: return BlendMethod::ColorDodge; - case 7: return BlendMethod::ColorBurn; - case 8: return BlendMethod::HardLight; - case 9: return BlendMethod::SoftLight; - case 10: return BlendMethod::Difference; - case 11: return BlendMethod::Exclusion; - //case 12: return BlendMethod::Hue: - //case 13: return BlendMethod::Saturation: - //case 14: return BlendMethod::Color: - //case 15: return BlendMethod::Luminosity: - case 16: return BlendMethod::Add; - //case 17: return BlendMethod::HardMix: - default: { - TVGERR("LOTTIE", "Non-Supported Blend Mode"); - return BlendMethod::Normal; - } - } -} - - RGB24 LottieParser::getColor(const char *str) { RGB24 color = {0, 0, 0}; @@ -1293,7 +1264,7 @@ LottieLayer* LottieParser::parseLayer(LottieLayer* precomp) else if (KEY_AS("ip")) layer->inFrame = getFloat(); else if (KEY_AS("op")) layer->outFrame = getFloat(); else if (KEY_AS("st")) layer->startFrame = getFloat(); - else if (KEY_AS("bm")) layer->blendMethod = getBlendMethod(); + else if (KEY_AS("bm")) layer->blendMethod = (BlendMethod) getInt(); else if (KEY_AS("parent")) layer->pidx = getInt(); else if (KEY_AS("tm")) parseTimeRemap(layer); else if (KEY_AS("w") || KEY_AS("sw")) getLayerSize(layer->w); diff --git a/src/loaders/lottie/tvgLottieParser.h b/src/loaders/lottie/tvgLottieParser.h index 61191e18..861a3920 100644 --- a/src/loaders/lottie/tvgLottieParser.h +++ b/src/loaders/lottie/tvgLottieParser.h @@ -43,7 +43,6 @@ public: const char* dirName = nullptr; //base resource directory private: - BlendMethod getBlendMethod(); RGB24 getColor(const char *str); CompositeMethod getMatteType(); FillRule getFillRule(); diff --git a/src/renderer/sw_engine/tvgSwRenderer.cpp b/src/renderer/sw_engine/tvgSwRenderer.cpp index 5f0edac0..2241fe07 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -452,27 +452,18 @@ bool SwRenderer::blend(BlendMethod method) surface->blendMethod = method; switch (method) { - case BlendMethod::Add: - surface->blender = opBlendAdd; - break; - case BlendMethod::Screen: - surface->blender = opBlendScreen; + case BlendMethod::Normal: + surface->blender = nullptr; break; case BlendMethod::Multiply: surface->blender = opBlendMultiply; break; + case BlendMethod::Screen: + surface->blender = opBlendScreen; + 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; @@ -491,7 +482,17 @@ bool SwRenderer::blend(BlendMethod method) case BlendMethod::SoftLight: surface->blender = opBlendSoftLight; break; + case BlendMethod::Difference: + surface->blender = opBlendDifference; + break; + case BlendMethod::Exclusion: + surface->blender = opBlendExclusion; + break; + case BlendMethod::Add: + surface->blender = opBlendAdd; + break; default: + TVGLOG("SW_ENGINE", "Non supported blending option = %d", (int) method); surface->blender = nullptr; break; } diff --git a/src/renderer/wg_engine/tvgWgCompositor.cpp b/src/renderer/wg_engine/tvgWgCompositor.cpp index d30c188e..bf68d636 100755 --- a/src/renderer/wg_engine/tvgWgCompositor.cpp +++ b/src/renderer/wg_engine/tvgWgCompositor.cpp @@ -82,7 +82,6 @@ void WgCompositor::release(WgContext& context) WgPipelineBlendType WgCompositor::blendMethodToBlendType(BlendMethod blendMethod) { if (blendMethod == BlendMethod::Normal) return WgPipelineBlendType::Normal; - if (blendMethod == BlendMethod::SrcOver) return WgPipelineBlendType::SrcOver; return WgPipelineBlendType::Custom; } diff --git a/src/renderer/wg_engine/tvgWgPipelines.cpp b/src/renderer/wg_engine/tvgWgPipelines.cpp index 504cfab1..061587b0 100755 --- a/src/renderer/wg_engine/tvgWgPipelines.cpp +++ b/src/renderer/wg_engine/tvgWgPipelines.cpp @@ -159,7 +159,6 @@ void WgPipelines::initialize(WgContext& context) .alpha = { .operation = WGPUBlendOperation_Add, .srcFactor = WGPUBlendFactor_One, .dstFactor = WGPUBlendFactor_OneMinusSrcAlpha } }; const WGPUBlendState blendStates[] { - blendStateSrc, // WgPipelineBlendType::SrcOver blendStateNrm, // WgPipelineBlendType::Normal blendStateSrc // WgPipelineBlendType::Custom (same as SrcOver) }; @@ -268,7 +267,7 @@ void WgPipelines::initialize(WgContext& context) primitiveState, multisampleState, blendStateSrc); // render pipeline solid - for (uint32_t i = 0; i < 3; i++) { + for (uint32_t i = 0; i < 2; i++) { solid[i] = createRenderPipeline( context.device, "The render pipeline solid", shaderSolid, "vs_main", "fs_main", layoutSolid, @@ -280,7 +279,7 @@ void WgPipelines::initialize(WgContext& context) } // render pipeline radial - for (uint32_t i = 0; i < 3; i++) { + for (uint32_t i = 0; i < 2; i++) { radial[i] = createRenderPipeline( context.device, "The render pipeline radial", shaderRadial, "vs_main", "fs_main", layoutGradient, @@ -292,7 +291,7 @@ void WgPipelines::initialize(WgContext& context) } // render pipeline linear - for (uint32_t i = 0; i < 3; i++) { + for (uint32_t i = 0; i < 2; i++) { linear[i] = createRenderPipeline( context.device, "The render pipeline linear", shaderLinear, "vs_main", "fs_main", layoutGradient, @@ -304,7 +303,7 @@ void WgPipelines::initialize(WgContext& context) } // render pipeline image - for (uint32_t i = 0; i < 3; i++) { + for (uint32_t i = 0; i < 2; i++) { image[i] = createRenderPipeline( context.device, "The render pipeline image", shaderImage, "vs_main", "fs_main", layoutImage, @@ -389,19 +388,23 @@ void WgPipelines::initialize(WgContext& context) // compute shader blend names const char* shaderBlendNames[] { "cs_main_Normal", - "cs_main_Add", - "cs_main_Screen", "cs_main_Multiply", + "cs_main_Screen", "cs_main_Overlay", - "cs_main_Difference", - "cs_main_Exclusion", - "cs_main_SrcOver", "cs_main_Darken", "cs_main_Lighten", "cs_main_ColorDodge", "cs_main_ColorBurn", "cs_main_HardLight", - "cs_main_SoftLight" + "cs_main_SoftLight", + "cs_main_Difference", + "cs_main_Exclusion", + "cs_main_Normal", //TODO: a padding for reserved Hue. + "cs_main_Normal", //TODO: a padding for reserved Saturation. + "cs_main_Normal", //TODO: a padding for reserved Color. + "cs_main_Normal", //TODO: a padding for reserved Luminosity. + "cs_main_Add", + "cs_main_Normal" //TODO: a padding for reserved Hardmix. }; // create blend shaders @@ -429,7 +432,7 @@ void WgPipelines::releaseGraphicHandles(WgContext& context) size_t pipesSceneCompCnt = sizeof(sceneComp) / sizeof(sceneComp[0]); for (uint32_t i = 0; i < pipesSceneCompCnt; i++) releaseRenderPipeline(sceneComp[i]); - for (uint32_t i = 0; i < 3; i++) { + for (uint32_t i = 0; i < 2; i++) { releaseRenderPipeline(image[i]); releaseRenderPipeline(linear[i]); releaseRenderPipeline(radial[i]); diff --git a/src/renderer/wg_engine/tvgWgPipelines.h b/src/renderer/wg_engine/tvgWgPipelines.h index 8a02a2f6..0a1fee6d 100755 --- a/src/renderer/wg_engine/tvgWgPipelines.h +++ b/src/renderer/wg_engine/tvgWgPipelines.h @@ -25,7 +25,7 @@ #include "tvgWgBindGroups.h" -enum class WgPipelineBlendType { SrcOver = 0, Normal, Custom }; +enum class WgPipelineBlendType { Normal, Custom }; class WgPipelines { private: @@ -61,19 +61,19 @@ public: WGPURenderPipeline winding{}; WGPURenderPipeline evenodd{}; WGPURenderPipeline direct{}; - WGPURenderPipeline solid[3]{}; - WGPURenderPipeline radial[3]{}; - WGPURenderPipeline linear[3]{}; - WGPURenderPipeline image[3]{}; + WGPURenderPipeline solid[2]{}; + WGPURenderPipeline radial[2]{}; + WGPURenderPipeline linear[2]{}; + WGPURenderPipeline image[2]{}; WGPURenderPipeline sceneComp[12]; WGPURenderPipeline sceneBlend; WGPURenderPipeline blit{}; WGPURenderPipeline clipPath{}; // compute pipeline WGPUComputePipeline mergeMasks; - WGPUComputePipeline blendSolid[14]; - WGPUComputePipeline blendGradient[14]; - WGPUComputePipeline blendImage[14]; + WGPUComputePipeline blendSolid[18]; + WGPUComputePipeline blendGradient[18]; + WGPUComputePipeline blendImage[18]; private: void releaseGraphicHandles(WgContext& context); void releaseComputeHandles(WgContext& context); diff --git a/src/renderer/wg_engine/tvgWgShaderSrc.cpp b/src/renderer/wg_engine/tvgWgShaderSrc.cpp index a3114a6c..199aa1b6 100755 --- a/src/renderer/wg_engine/tvgWgShaderSrc.cpp +++ b/src/renderer/wg_engine/tvgWgShaderSrc.cpp @@ -505,13 +505,6 @@ fn cs_main_Exclusion(@builtin(global_invocation_id) id: vec3u) { textureStore(imageTgt, id.xy, postProcess(d, vec4f(Rc, 1.0))); }; -@compute @workgroup_size(8, 8) -fn cs_main_SrcOver(@builtin(global_invocation_id) id: vec3u) { - let d: FragData = getFragData(id.xy); - if (d.skip) { return; } - textureStore(imageTgt, id.xy, postProcess(d, vec4f(d.Sc, d.Sa))); -}; - @compute @workgroup_size(8, 8) fn cs_main_Darken(@builtin(global_invocation_id) id: vec3u) { let d: FragData = getFragData(id.xy); diff --git a/test/testPaint.cpp b/test/testPaint.cpp index a7942a86..12d0f31d 100644 --- a/test/testPaint.cpp +++ b/test/testPaint.cpp @@ -289,10 +289,6 @@ TEST_CASE("Blending", "[tvgPaint]") REQUIRE(shape->blend(BlendMethod::Exclusion) == Result::Success); REQUIRE(shape->blend() == BlendMethod::Exclusion); - //SrcOver - REQUIRE(shape->blend(BlendMethod::SrcOver) == Result::Success); - REQUIRE(shape->blend() == BlendMethod::SrcOver); - //Darken REQUIRE(shape->blend(BlendMethod::Darken) == Result::Success); REQUIRE(shape->blend() == BlendMethod::Darken); diff --git a/test/testSwEngine.cpp b/test/testSwEngine.cpp index 925581ab..d92ebb8c 100644 --- a/test/testSwEngine.cpp +++ b/test/testSwEngine.cpp @@ -1239,7 +1239,6 @@ TEST_CASE("Blending Shapes", "[tvgSwEngine]") BlendMethod::Overlay, BlendMethod::Difference, BlendMethod::Exclusion, - BlendMethod::SrcOver, BlendMethod::Darken, BlendMethod::Lighten, BlendMethod::ColorDodge,