renderer: blending refactoring++

- reordered the blending types to align with lottie spec.
- removed source over.
This commit is contained in:
Hermet Park 2024-09-09 18:16:22 +09:00
parent a7aac95b03
commit 371139e220
11 changed files with 49 additions and 91 deletions

View file

@ -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);

View file

@ -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.
};

View file

@ -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);

View file

@ -43,7 +43,6 @@ public:
const char* dirName = nullptr; //base resource directory
private:
BlendMethod getBlendMethod();
RGB24 getColor(const char *str);
CompositeMethod getMatteType();
FillRule getFillRule();

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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]);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -1239,7 +1239,6 @@ TEST_CASE("Blending Shapes", "[tvgSwEngine]")
BlendMethod::Overlay,
BlendMethod::Difference,
BlendMethod::Exclusion,
BlendMethod::SrcOver,
BlendMethod::Darken,
BlendMethod::Lighten,
BlendMethod::ColorDodge,