Centralize canvas blend gate planning

This commit is contained in:
2026-06-03 18:20:01 +02:00
parent a89f5e6cf2
commit 1369a9048e
8 changed files with 327 additions and 86 deletions

View File

@@ -40,6 +40,36 @@ namespace {
return false;
}
[[nodiscard]] bool paint_blend_mode_from_persisted_index(int value, pp::paint::BlendMode& out) noexcept
{
switch (value) {
case 0: out = pp::paint::BlendMode::normal; return true;
case 1: out = pp::paint::BlendMode::multiply; return true;
case 2: out = pp::paint::BlendMode::screen; return true;
case 3: out = pp::paint::BlendMode::color_dodge; return true;
case 4: out = pp::paint::BlendMode::overlay; return true;
default: return false;
}
}
[[nodiscard]] bool stroke_blend_mode_from_persisted_index(int value, pp::paint::StrokeBlendMode& out) noexcept
{
switch (value) {
case 0: out = pp::paint::StrokeBlendMode::normal; return true;
case 1: out = pp::paint::StrokeBlendMode::multiply; return true;
case 2: out = pp::paint::StrokeBlendMode::subtract; return true;
case 3: out = pp::paint::StrokeBlendMode::darken; return true;
case 4: out = pp::paint::StrokeBlendMode::overlay; return true;
case 5: out = pp::paint::StrokeBlendMode::color_dodge; return true;
case 6: out = pp::paint::StrokeBlendMode::color_burn; return true;
case 7: out = pp::paint::StrokeBlendMode::linear_burn; return true;
case 8: out = pp::paint::StrokeBlendMode::hard_mix; return true;
case 9: out = pp::paint::StrokeBlendMode::linear_height; return true;
case 10: out = pp::paint::StrokeBlendMode::height; return true;
default: return false;
}
}
[[nodiscard]] pp::foundation::Result<std::size_t> expected_pixel_count(pp::renderer::Extent2D extent) noexcept
{
const auto extent_status = pp::renderer::validate_extent(extent);
@@ -77,6 +107,22 @@ namespace {
return StrokeCompositePath::fixed_function_blend;
}
void apply_stroke_plan(CanvasBlendGatePlan& gate, const StrokeCompositePlan& stroke) noexcept
{
gate.path = stroke.path;
gate.reads_destination_color = stroke.reads_destination_color;
gate.requires_auxiliary_texture = stroke.requires_auxiliary_texture;
gate.requires_texture_copy = stroke.requires_texture_copy;
gate.requires_render_target_blit = stroke.requires_render_target_blit;
}
void mark_shader_blend_fallback(CanvasBlendGatePlan& gate) noexcept
{
gate.shader_blend = true;
gate.complex_blend = true;
gate.compatibility_fallback = true;
}
}
pp::foundation::Status composite_layer(
@@ -170,6 +216,82 @@ pp::foundation::Result<StrokeCompositePlan> plan_stroke_composite(
return pp::foundation::Result<StrokeCompositePlan>::success(plan);
}
pp::foundation::Result<CanvasBlendGatePlan> plan_canvas_blend_gate(
pp::renderer::RenderDeviceFeatures features,
CanvasBlendGateRequest request) noexcept
{
CanvasBlendGatePlan gate;
for (std::size_t i = 0; i < request.layer_blend_modes.size(); ++i) {
pp::paint::BlendMode layer_blend = pp::paint::BlendMode::normal;
if (!paint_blend_mode_from_persisted_index(request.layer_blend_modes[i], layer_blend)) {
if (request.layer_blend_modes[i] != 0) {
gate.first_complex_layer_index = static_cast<int>(i);
mark_shader_blend_fallback(gate);
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
}
continue;
}
if (layer_blend == pp::paint::BlendMode::normal) {
continue;
}
gate.shader_blend = true;
gate.complex_blend = true;
gate.first_complex_layer_index = static_cast<int>(i);
const auto stroke = plan_stroke_composite(
features,
StrokeCompositeRequest {
.extent = request.extent,
.layer_blend_mode = layer_blend,
});
if (stroke) {
apply_stroke_plan(gate, stroke.value());
} else {
gate.compatibility_fallback = true;
}
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
}
pp::paint::StrokeBlendMode stroke_blend = pp::paint::StrokeBlendMode::normal;
if (request.has_stroke_blend_mode) {
if (!stroke_blend_mode_from_persisted_index(request.stroke_blend_mode, stroke_blend)) {
if (request.stroke_blend_mode != 0) {
gate.stroke_complex = true;
mark_shader_blend_fallback(gate);
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
}
} else if (stroke_blend != pp::paint::StrokeBlendMode::normal) {
gate.stroke_complex = true;
}
}
gate.dual_brush_complex = request.dual_brush_blend;
gate.pattern_complex = request.pattern_blend;
if (!gate.stroke_complex && !gate.dual_brush_complex && !gate.pattern_complex) {
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
}
gate.shader_blend = true;
gate.complex_blend = true;
const auto stroke = plan_stroke_composite(
features,
StrokeCompositeRequest {
.extent = request.extent,
.stroke_blend_mode = stroke_blend,
.dual_brush_blend = request.dual_brush_blend,
.pattern_blend = request.pattern_blend,
});
if (stroke) {
apply_stroke_plan(gate, stroke.value());
} else {
gate.compatibility_fallback = true;
}
return pp::foundation::Result<CanvasBlendGatePlan>::success(gate);
}
const char* stroke_composite_path_name(StrokeCompositePath path) noexcept
{
switch (path) {