mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +00:00

[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;
393 lines
15 KiB
C++
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;
|
|
}
|