thorvg/src/renderer/wg_engine/tvgWgPipelines.cpp
Sergii Liebodkin 45368c319e wg_engine: introduced composition ability
[issues 1479: Masking](#1479)

Supported composition methods:

    AlphaMask
    InvAlphaMask
    LumaMask
    InvLumaMask
    AddMask
    SubtractMask
    IntersectMask
    DifferenceMask

Usage example:

    //Solid Rectangle
    auto shape = tvg::Shape::gen();
    shape->appendRect(0, 0, 400, 400);
    shape->fill(0, 0, 255);

    //Mask
    auto mask = tvg::Shape::gen();
    mask->appendCircle(200, 200, 125, 125);
    mask->fill(255, 255, 255);        //AlphaMask RGB channels are unused.

    //Nested Mask
    auto nMask = tvg::Shape::gen();
    nMask->appendCircle(220, 220, 125, 125);
    nMask->fill(255, 255, 255);       //AlphaMask RGB channels are unused.

    mask->composite(std::move(nMask), tvg::CompositeMethod::AlphaMask);
    shape->composite(std::move(mask), tvg::CompositeMethod::AlphaMask);
    canvas->push(std::move(shape));

    //Star
    auto star = tvg::Shape::gen();
    star->fill(80, 80, 80);
    star->moveTo(599, 34);
    star->lineTo(653, 143);
    star->lineTo(774, 160);
    star->lineTo(687, 244);
    star->lineTo(707, 365);
    star->lineTo(599, 309);
    star->lineTo(497, 365);
    star->lineTo(512, 245);
    star->lineTo(426, 161);
    star->lineTo(546, 143);
    star->close();
    star->strokeWidth(30);
    star->strokeJoin(tvg::StrokeJoin::Miter);
    star->strokeFill(255, 255, 255);

    //Mask3
    auto mask3 = tvg::Shape::gen();
    mask3->appendCircle(600, 200, 125, 125);
    mask3->fill(255, 255, 255);       //AlphaMask RGB channels are unused.
    mask3->opacity(200);
    star->composite(std::move(mask3), tvg::CompositeMethod::AlphaMask);
    if (canvas->push(std::move(star)) != tvg::Result::Success) return;
2024-01-15 17:33:59 +09:00

393 lines
15 KiB
C++

/*
* Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgWgPipelines.h"
#include "tvgWgShaderSrc.h"
#define ARRAY_ELEMENTS_COUNT(arr) sizeof(arr)/sizeof(arr[0])
void WgPipelineFillShape::initialize(WGPUDevice device)
{
// vertex attributes settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
};
// bind groups
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupCanvas::getLayout(device),
WgBindGroupPaint::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Invert;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineFill;
auto shaderLabel = "The shader fill";
auto pipelineLabel = "The render pipeline fill shape";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineFillStroke::initialize(WGPUDevice device)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupCanvas::getLayout(device),
WgBindGroupPaint::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Replace;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineFill;
auto shaderLabel = "The shader fill";
auto pipelineLabel = "The render pipeline fill stroke";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineSolid::initialize(WGPUDevice device)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupCanvas::getLayout(device),
WgBindGroupPaint::getLayout(device),
WgBindGroupSolidColor::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_NotEqual;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineSolid;
auto shaderLabel = "The shader solid color";
auto pipelineLabel = "The render pipeline solid color";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineLinear::initialize(WGPUDevice device)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupCanvas::getLayout(device),
WgBindGroupPaint::getLayout(device),
WgBindGroupLinearGradient::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_NotEqual;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineLinear;
auto shaderLabel = "The shader linear gradient";
auto pipelineLabel = "The render pipeline linear gradient";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineRadial::initialize(WGPUDevice device)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupCanvas::getLayout(device),
WgBindGroupPaint::getLayout(device),
WgBindGroupRadialGradient::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_NotEqual;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineRadial;
auto shaderLabel = "The shader radial gradient";
auto pipelineLabel = "The render pipeline radial gradient";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineImage::initialize(WGPUDevice device)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexAttribute vertexAttributesTex = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 1 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2),
makeVertexBufferLayout(&vertexAttributesTex, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupCanvas::getLayout(device),
WgBindGroupPaint::getLayout(device),
WgBindGroupPicture::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineImage;
auto shaderLabel = "The shader image";
auto pipelineLabel = "The render pipeline image";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineBlit::initialize(WGPUDevice device)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexAttribute vertexAttributesTex = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 1 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2),
makeVertexBufferLayout(&vertexAttributesTex, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupBlit::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineBlit;
auto shaderLabel = "The shader blit";
auto pipelineLabel = "The render pipeline blit";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineBlitColor::initialize(WGPUDevice device)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexAttribute vertexAttributesTex = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 1 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2),
makeVertexBufferLayout(&vertexAttributesTex, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupBlit::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = cShaderSource_PipelineBlitColor;
auto shaderLabel = "The shader blit color";
auto pipelineLabel = "The render pipeline blit color";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
void WgPipelineComposition::initialize(WGPUDevice device, const char* shaderSrc)
{
// vertex and buffers settings
WGPUVertexAttribute vertexAttributesPos = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 0 };
WGPUVertexAttribute vertexAttributesTex = { WGPUVertexFormat_Float32x2, sizeof(float) * 0, 1 };
WGPUVertexBufferLayout vertexBufferLayouts[] = {
makeVertexBufferLayout(&vertexAttributesPos, 1, sizeof(float) * 2),
makeVertexBufferLayout(&vertexAttributesTex, 1, sizeof(float) * 2)
};
// bind groups and layouts
WGPUBindGroupLayout bindGroupLayouts[] = {
WgBindGroupBlit::getLayout(device),
WgBindGroupBlit::getLayout(device)
};
// stencil function
WGPUCompareFunction stencilFuncion = WGPUCompareFunction_Always;
WGPUStencilOperation stencilOperation = WGPUStencilOperation_Zero;
// sheder source and labels
auto shaderSource = shaderSrc;
auto shaderLabel = "The shader compose alpha mask";
auto pipelineLabel = "The render pipeline compose alpha mask";
// allocate all pipeline handles
allocate(device,
vertexBufferLayouts, ARRAY_ELEMENTS_COUNT(vertexBufferLayouts),
bindGroupLayouts, ARRAY_ELEMENTS_COUNT(bindGroupLayouts),
stencilFuncion, stencilOperation,
shaderSource, shaderLabel, pipelineLabel);
}
//************************************************************************
// pipelines
//************************************************************************
void WgPipelines::initialize(WGPUDevice device)
{
fillShape.initialize(device);
fillStroke.initialize(device);
solid.initialize(device);
linear.initialize(device);
radial.initialize(device);
image.initialize(device);
blit.initialize(device);
blitColor.initialize(device);
// composition pipelines
compAlphaMask.initialize(device, cShaderSource_PipelineCompAlphaMask);
compInvAlphaMask.initialize(device, cShaderSource_PipelineCompInvAlphaMask);
compLumaMask.initialize(device, cShaderSource_PipelineCompLumaMask);
compInvLumaMask.initialize(device, cShaderSource_PipelineCompInvLumaMask);
compAddMask.initialize(device, cShaderSource_PipelineCompAddMask);
compSubtractMask.initialize(device, cShaderSource_PipelineCompSubtractMask);
compIntersectMask.initialize(device, cShaderSource_PipelineCompIntersectMask);
compDifferenceMask.initialize(device, cShaderSource_PipelineCompDifferenceMask);
}
void WgPipelines::release()
{
WgBindGroupBlit::releaseLayout();
WgBindGroupPicture::releaseLayout();
WgBindGroupRadialGradient::releaseLayout();
WgBindGroupLinearGradient::releaseLayout();
WgBindGroupSolidColor::releaseLayout();
WgBindGroupCanvas::releaseLayout();
compDifferenceMask.release();
compIntersectMask.release();
compSubtractMask.release();
compAddMask.release();
compInvLumaMask.release();
compLumaMask.release();
compInvAlphaMask.release();
compAlphaMask.release();
blitColor.release();
blit.release();
image.release();
radial.release();
linear.release();
solid.release();
fillStroke.release();
fillShape.release();
}
WgPipelineComposition* WgPipelines::getCompositionPipeline(CompositeMethod method)
{
switch (method) {
case CompositeMethod::ClipPath:
case CompositeMethod::AlphaMask: return &compAlphaMask; break;
case CompositeMethod::InvAlphaMask: return &compInvAlphaMask; break;
case CompositeMethod::LumaMask: return &compLumaMask; break;
case CompositeMethod::InvLumaMask: return &compInvLumaMask; break;
case CompositeMethod::AddMask: return &compAddMask; break;
case CompositeMethod::SubtractMask: return &compSubtractMask; break;
case CompositeMethod::IntersectMask: return &compIntersectMask; break;
case CompositeMethod::DifferenceMask: return &compDifferenceMask; break;
default: return nullptr; break;
}
return nullptr;
}