blob: 3497c5bdd8e809fa7815cd6efe817a1e7dfc1392 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/entity/contents/filters/blend_filter_contents.h"
#include <array>
#include <memory>
#include <optional>
#include "impeller/base/strings.h"
#include "impeller/core/formats.h"
#include "impeller/entity/contents/anonymous_contents.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/contents.h"
#include "impeller/entity/contents/filters/color_filter_contents.h"
#include "impeller/entity/contents/filters/inputs/filter_input.h"
#include "impeller/entity/contents/solid_color_contents.h"
#include "impeller/entity/entity.h"
#include "impeller/geometry/color.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/snapshot.h"
namespace impeller {
std::optional<BlendMode> InvertPorterDuffBlend(BlendMode blend_mode) {
switch (blend_mode) {
case BlendMode::kClear:
return BlendMode::kClear;
case BlendMode::kSource:
return BlendMode::kDestination;
case BlendMode::kDestination:
return BlendMode::kSource;
case BlendMode::kSourceOver:
return BlendMode::kDestinationOver;
case BlendMode::kDestinationOver:
return BlendMode::kSourceOver;
case BlendMode::kSourceIn:
return BlendMode::kDestinationIn;
case BlendMode::kDestinationIn:
return BlendMode::kSourceIn;
case BlendMode::kSourceOut:
return BlendMode::kDestinationOut;
case BlendMode::kDestinationOut:
return BlendMode::kSourceOut;
case BlendMode::kSourceATop:
return BlendMode::kDestinationATop;
case BlendMode::kDestinationATop:
return BlendMode::kSourceATop;
case BlendMode::kXor:
return BlendMode::kXor;
case BlendMode::kPlus:
return BlendMode::kPlus;
case BlendMode::kModulate:
return BlendMode::kModulate;
default:
return std::nullopt;
}
}
BlendFilterContents::BlendFilterContents() {
SetBlendMode(BlendMode::kSourceOver);
}
BlendFilterContents::~BlendFilterContents() = default;
using PipelineProc = std::shared_ptr<Pipeline<PipelineDescriptor>> (
ContentContext::*)(ContentContextOptions) const;
template <typename TPipeline>
static std::optional<Entity> AdvancedBlend(
const FilterInput::Vector& inputs,
const ContentContext& renderer,
const Entity& entity,
const Rect& coverage,
BlendMode blend_mode,
std::optional<Color> foreground_color,
ColorFilterContents::AbsorbOpacity absorb_opacity,
PipelineProc pipeline_proc,
std::optional<Scalar> alpha) {
using VS = typename TPipeline::VertexShader;
using FS = typename TPipeline::FragmentShader;
//----------------------------------------------------------------------------
/// Handle inputs.
///
const size_t total_inputs =
inputs.size() + (foreground_color.has_value() ? 1 : 0);
if (total_inputs < 2) {
return std::nullopt;
}
auto dst_snapshot =
inputs[0]->GetSnapshot("AdvancedBlend(Dst)", renderer, entity);
if (!dst_snapshot.has_value()) {
return std::nullopt;
}
auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
if (!maybe_dst_uvs.has_value()) {
return std::nullopt;
}
auto dst_uvs = maybe_dst_uvs.value();
std::optional<Snapshot> src_snapshot;
std::array<Point, 4> src_uvs;
if (!foreground_color.has_value()) {
src_snapshot =
inputs[1]->GetSnapshot("AdvancedBlend(Src)", renderer, entity);
if (!src_snapshot.has_value()) {
if (!dst_snapshot.has_value()) {
return std::nullopt;
}
return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
}
auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage);
if (!maybe_src_uvs.has_value()) {
if (!dst_snapshot.has_value()) {
return std::nullopt;
}
return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
}
src_uvs = maybe_src_uvs.value();
}
Rect subpass_coverage = coverage;
if (entity.GetContents()) {
auto coverage_hint = entity.GetContents()->GetCoverageHint();
if (coverage_hint.has_value()) {
auto maybe_subpass_coverage =
subpass_coverage.Intersection(*coverage_hint);
if (!maybe_subpass_coverage.has_value()) {
return std::nullopt; // Nothing to render.
}
subpass_coverage = *maybe_subpass_coverage;
}
}
//----------------------------------------------------------------------------
/// Render to texture.
///
ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
RenderPass& pass) {
auto& host_buffer = renderer.GetTransientsBuffer();
auto size = pass.GetRenderTargetSize();
VertexBufferBuilder<typename VS::PerVertexData> vtx_builder;
vtx_builder.AddVertices({
{Point(0, 0), dst_uvs[0], src_uvs[0]},
{Point(size.width, 0), dst_uvs[1], src_uvs[1]},
{Point(0, size.height), dst_uvs[2], src_uvs[2]},
{Point(size.width, size.height), dst_uvs[3], src_uvs[3]},
});
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
auto options = OptionsFromPass(pass);
options.primitive_type = PrimitiveType::kTriangleStrip;
options.blend_mode = BlendMode::kSource;
std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
std::invoke(pipeline_proc, renderer, options);
#ifdef IMPELLER_DEBUG
pass.SetCommandLabel(
SPrintF("Advanced Blend Filter (%s)", BlendModeToString(blend_mode)));
#endif // IMPELLER_DEBUG
pass.SetVertexBuffer(std::move(vtx_buffer));
pass.SetPipeline(pipeline);
typename FS::BlendInfo blend_info;
typename VS::FrameInfo frame_info;
auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
}
const std::unique_ptr<const Sampler>& dst_sampler =
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
dst_sampler_descriptor);
FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
blend_info.dst_input_alpha =
absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
? dst_snapshot->opacity
: 1.0;
if (foreground_color.has_value()) {
blend_info.color_factor = 1;
blend_info.color = foreground_color.value();
// This texture will not be sampled from due to the color factor. But
// this is present so that validation doesn't trip on a missing
// binding.
FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
} else {
auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
}
const std::unique_ptr<const Sampler>& src_sampler =
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
src_sampler_descriptor);
blend_info.color_factor = 0;
blend_info.src_input_alpha = src_snapshot->opacity;
FS::BindTextureSamplerSrc(pass, src_snapshot->texture, src_sampler);
frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale();
}
auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
FS::BindBlendInfo(pass, blend_uniform);
frame_info.mvp = pass.GetOrthographicTransform() *
Matrix::MakeTranslation(coverage.GetOrigin() -
subpass_coverage.GetOrigin());
auto uniform_view = host_buffer.EmplaceUniform(frame_info);
VS::BindFrameInfo(pass, uniform_view);
return pass.Draw().ok();
};
std::shared_ptr<CommandBuffer> command_buffer =
renderer.GetContext()->CreateCommandBuffer();
if (!command_buffer) {
return std::nullopt;
}
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
"Advanced Blend Filter", ISize(subpass_coverage.GetSize()),
command_buffer, callback);
if (!render_target.ok()) {
return std::nullopt;
}
if (!renderer.GetContext()
->GetCommandQueue()
->Submit(/*buffers=*/{std::move(command_buffer)})
.ok()) {
return std::nullopt;
}
return Entity::FromSnapshot(
Snapshot{
.texture = render_target.value().GetRenderTargetTexture(),
.transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
// Since we absorbed the transform of the inputs and used the
// respective snapshot sampling modes when blending, pass on
// the default NN clamp sampler.
.sampler_descriptor = {},
.opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
? 1.0f
: dst_snapshot->opacity) *
alpha.value_or(1.0)},
entity.GetBlendMode());
}
std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
const std::shared_ptr<FilterInput>& input,
const ContentContext& renderer,
const Entity& entity,
const Rect& coverage,
Color foreground_color,
BlendMode blend_mode,
std::optional<Scalar> alpha,
ColorFilterContents::AbsorbOpacity absorb_opacity) const {
auto dst_snapshot =
input->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
if (!dst_snapshot.has_value()) {
return std::nullopt;
}
RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode, alpha,
absorb_opacity](const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) -> bool {
using VS = BlendScreenPipeline::VertexShader;
using FS = BlendScreenPipeline::FragmentShader;
auto& host_buffer = renderer.GetTransientsBuffer();
auto size = dst_snapshot->texture->GetSize();
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
vtx_builder.AddVertices({
{{0, 0}, {0, 0}, {0, 0}},
{Point(size.width, 0), {1, 0}, {1, 0}},
{Point(0, size.height), {0, 1}, {0, 1}},
{Point(size.width, size.height), {1, 1}, {1, 1}},
});
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
#ifdef IMPELLER_DEBUG
pass.SetCommandLabel(SPrintF("Foreground Advanced Blend Filter (%s)",
BlendModeToString(blend_mode)));
#endif // IMPELLER_DEBUG
pass.SetVertexBuffer(std::move(vtx_buffer));
auto options = OptionsFromPass(pass);
options.primitive_type = PrimitiveType::kTriangleStrip;
switch (blend_mode) {
case BlendMode::kScreen:
pass.SetPipeline(renderer.GetBlendScreenPipeline(options));
break;
case BlendMode::kOverlay:
pass.SetPipeline(renderer.GetBlendOverlayPipeline(options));
break;
case BlendMode::kDarken:
pass.SetPipeline(renderer.GetBlendDarkenPipeline(options));
break;
case BlendMode::kLighten:
pass.SetPipeline(renderer.GetBlendLightenPipeline(options));
break;
case BlendMode::kColorDodge:
pass.SetPipeline(renderer.GetBlendColorDodgePipeline(options));
break;
case BlendMode::kColorBurn:
pass.SetPipeline(renderer.GetBlendColorBurnPipeline(options));
break;
case BlendMode::kHardLight:
pass.SetPipeline(renderer.GetBlendHardLightPipeline(options));
break;
case BlendMode::kSoftLight:
pass.SetPipeline(renderer.GetBlendSoftLightPipeline(options));
break;
case BlendMode::kDifference:
pass.SetPipeline(renderer.GetBlendDifferencePipeline(options));
break;
case BlendMode::kExclusion:
pass.SetPipeline(renderer.GetBlendExclusionPipeline(options));
break;
case BlendMode::kMultiply:
pass.SetPipeline(renderer.GetBlendMultiplyPipeline(options));
break;
case BlendMode::kHue:
pass.SetPipeline(renderer.GetBlendHuePipeline(options));
break;
case BlendMode::kSaturation:
pass.SetPipeline(renderer.GetBlendSaturationPipeline(options));
break;
case BlendMode::kColor:
pass.SetPipeline(renderer.GetBlendColorPipeline(options));
break;
case BlendMode::kLuminosity:
pass.SetPipeline(renderer.GetBlendLuminosityPipeline(options));
break;
default:
return false;
}
FS::BlendInfo blend_info;
VS::FrameInfo frame_info;
auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
}
const std::unique_ptr<const Sampler>& dst_sampler =
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
dst_sampler_descriptor);
FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
frame_info.mvp = pass.GetOrthographicTransform() * dst_snapshot->transform;
blend_info.dst_input_alpha =
absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
? dst_snapshot->opacity * alpha.value_or(1.0)
: 1.0;
blend_info.color_factor = 1;
blend_info.color = foreground_color;
// This texture will not be sampled from due to the color factor. But
// this is present so that validation doesn't trip on a missing
// binding.
FS::BindTextureSamplerSrc(pass, dst_snapshot->texture, dst_sampler);
auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
FS::BindBlendInfo(pass, blend_uniform);
auto uniform_view = host_buffer.EmplaceUniform(frame_info);
VS::BindFrameInfo(pass, uniform_view);
return pass.Draw().ok();
};
CoverageProc coverage_proc =
[coverage](const Entity& entity) -> std::optional<Rect> {
return coverage.TransformBounds(entity.GetTransform());
};
auto contents = AnonymousContents::Make(render_proc, coverage_proc);
Entity sub_entity;
sub_entity.SetContents(std::move(contents));
return sub_entity;
}
std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
const std::shared_ptr<FilterInput>& input,
const ContentContext& renderer,
const Entity& entity,
const Rect& coverage,
Color foreground_color,
BlendMode blend_mode,
std::optional<Scalar> alpha,
ColorFilterContents::AbsorbOpacity absorb_opacity) const {
if (blend_mode == BlendMode::kClear) {
return std::nullopt;
}
auto dst_snapshot =
input->GetSnapshot("ForegroundPorterDuffBlend", renderer, entity);
if (!dst_snapshot.has_value()) {
return std::nullopt;
}
if (blend_mode == BlendMode::kDestination) {
return Entity::FromSnapshot(dst_snapshot.value(), entity.GetBlendMode());
}
RenderProc render_proc = [foreground_color, dst_snapshot, blend_mode,
absorb_opacity, alpha](
const ContentContext& renderer,
const Entity& entity, RenderPass& pass) -> bool {
using VS = PorterDuffBlendPipeline::VertexShader;
using FS = PorterDuffBlendPipeline::FragmentShader;
auto& host_buffer = renderer.GetTransientsBuffer();
auto size = dst_snapshot->texture->GetSize();
auto color = foreground_color.Premultiply();
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
vtx_builder.AddVertices({
{{0, 0}, {0, 0}, color},
{Point(size.width, 0), {1, 0}, color},
{Point(0, size.height), {0, 1}, color},
{Point(size.width, size.height), {1, 1}, color},
});
auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
#ifdef IMPELLER_DEBUG
pass.SetCommandLabel(SPrintF("Foreground PorterDuff Blend Filter (%s)",
BlendModeToString(blend_mode)));
#endif // IMPELLER_DEBUG
pass.SetVertexBuffer(std::move(vtx_buffer));
auto options = OptionsFromPass(pass);
options.primitive_type = PrimitiveType::kTriangleStrip;
pass.SetPipeline(renderer.GetPorterDuffBlendPipeline(options));
FS::FragInfo frag_info;
VS::FrameInfo frame_info;
frame_info.mvp = pass.GetOrthographicTransform() * dst_snapshot->transform;
auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
}
const std::unique_ptr<const Sampler>& dst_sampler =
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
dst_sampler_descriptor);
FS::BindTextureSamplerDst(pass, dst_snapshot->texture, dst_sampler);
frame_info.texture_sampler_y_coord_scale =
dst_snapshot->texture->GetYCoordScale();
frag_info.input_alpha =
absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
? dst_snapshot->opacity * alpha.value_or(1.0)
: 1.0;
frag_info.output_alpha = 1.0;
auto blend_coefficients =
kPorterDuffCoefficients[static_cast<int>(blend_mode)];
frag_info.src_coeff = blend_coefficients[0];
frag_info.src_coeff_dst_alpha = blend_coefficients[1];
frag_info.dst_coeff = blend_coefficients[2];
frag_info.dst_coeff_src_alpha = blend_coefficients[3];
frag_info.dst_coeff_src_color = blend_coefficients[4];
FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
return pass.Draw().ok();
};
CoverageProc coverage_proc =
[coverage](const Entity& entity) -> std::optional<Rect> {
return coverage.TransformBounds(entity.GetTransform());
};
auto contents = AnonymousContents::Make(render_proc, coverage_proc);
Entity sub_entity;
sub_entity.SetContents(std::move(contents));
return sub_entity;
}
static std::optional<Entity> PipelineBlend(
const FilterInput::Vector& inputs,
const ContentContext& renderer,
const Entity& entity,
const Rect& coverage,
BlendMode blend_mode,
std::optional<Color> foreground_color,
ColorFilterContents::AbsorbOpacity absorb_opacity,
std::optional<Scalar> alpha) {
using VS = TexturePipeline::VertexShader;
using FS = TexturePipeline::FragmentShader;
auto dst_snapshot =
inputs[0]->GetSnapshot("PipelineBlend(Dst)", renderer, entity);
if (!dst_snapshot.has_value()) {
return std::nullopt; // Nothing to render.
}
Rect subpass_coverage = coverage;
if (entity.GetContents()) {
auto coverage_hint = entity.GetContents()->GetCoverageHint();
if (coverage_hint.has_value()) {
auto maybe_subpass_coverage =
subpass_coverage.Intersection(*coverage_hint);
if (!maybe_subpass_coverage.has_value()) {
return std::nullopt; // Nothing to render.
}
subpass_coverage = *maybe_subpass_coverage;
}
}
ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
RenderPass& pass) {
auto& host_buffer = renderer.GetTransientsBuffer();
#ifdef IMPELLER_DEBUG
pass.SetCommandLabel(
SPrintF("Pipeline Blend Filter (%s)", BlendModeToString(blend_mode)));
#endif // IMPELLER_DEBUG
auto options = OptionsFromPass(pass);
options.primitive_type = PrimitiveType::kTriangleStrip;
auto add_blend_command = [&](std::optional<Snapshot> input) {
if (!input.has_value()) {
return false;
}
auto input_coverage = input->GetCoverage();
if (!input_coverage.has_value()) {
return false;
}
const std::unique_ptr<const Sampler>& sampler =
renderer.GetContext()->GetSamplerLibrary()->GetSampler(
input->sampler_descriptor);
FS::BindTextureSampler(pass, input->texture, sampler);
auto size = input->texture->GetSize();
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
vtx_builder.AddVertices({
{Point(0, 0), Point(0, 0)},
{Point(size.width, 0), Point(1, 0)},
{Point(0, size.height), Point(0, 1)},
{Point(size.width, size.height), Point(1, 1)},
});
pass.SetVertexBuffer(vtx_builder.CreateVertexBuffer(host_buffer));
VS::FrameInfo frame_info;
frame_info.mvp = pass.GetOrthographicTransform() *
Matrix::MakeTranslation(-subpass_coverage.GetOrigin()) *
input->transform;
frame_info.texture_sampler_y_coord_scale =
input->texture->GetYCoordScale();
FS::FragInfo frag_info;
frag_info.alpha =
absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
? input->opacity
: 1.0;
FS::BindFragInfo(pass, host_buffer.EmplaceUniform(frag_info));
VS::BindFrameInfo(pass, host_buffer.EmplaceUniform(frame_info));
return pass.Draw().ok();
};
// Draw the first texture using kSource.
options.blend_mode = BlendMode::kSource;
pass.SetPipeline(renderer.GetTexturePipeline(options));
if (!add_blend_command(dst_snapshot)) {
return true;
}
// Write subsequent textures using the selected blend mode.
if (inputs.size() >= 2) {
options.blend_mode = blend_mode;
pass.SetPipeline(renderer.GetTexturePipeline(options));
for (auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
texture_i++) {
auto src_input = texture_i->get()->GetSnapshot("PipelineBlend(Src)",
renderer, entity);
if (!add_blend_command(src_input)) {
return true;
}
}
}
// If a foreground color is set, blend it in.
if (foreground_color.has_value()) {
auto contents = std::make_shared<SolidColorContents>();
contents->SetGeometry(
Geometry::MakeRect(Rect::MakeSize(pass.GetRenderTargetSize())));
contents->SetColor(foreground_color.value());
Entity foreground_entity;
foreground_entity.SetBlendMode(blend_mode);
foreground_entity.SetContents(contents);
if (!foreground_entity.Render(renderer, pass)) {
return false;
}
}
return true;
};
std::shared_ptr<CommandBuffer> command_buffer =
renderer.GetContext()->CreateCommandBuffer();
if (!command_buffer) {
return std::nullopt;
}
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
"Pipeline Blend Filter", ISize(subpass_coverage.GetSize()),
command_buffer, callback);
if (!render_target.ok()) {
return std::nullopt;
}
if (!renderer.GetContext()
->GetCommandQueue()
->Submit(/*buffers=*/{std::move(command_buffer)})
.ok()) {
return std::nullopt;
}
return Entity::FromSnapshot(
Snapshot{
.texture = render_target.value().GetRenderTargetTexture(),
.transform = Matrix::MakeTranslation(subpass_coverage.GetOrigin()),
// Since we absorbed the transform of the inputs and used the
// respective snapshot sampling modes when blending, pass on
// the default NN clamp sampler.
.sampler_descriptor = {},
.opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
? 1.0f
: dst_snapshot->opacity) *
alpha.value_or(1.0)},
entity.GetBlendMode());
}
#define BLEND_CASE(mode) \
case BlendMode::k##mode: \
advanced_blend_proc_ = \
[](const FilterInput::Vector& inputs, const ContentContext& renderer, \
const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
std::optional<Color> fg_color, \
ColorFilterContents::AbsorbOpacity absorb_opacity, \
std::optional<Scalar> alpha) { \
PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
return AdvancedBlend<Blend##mode##Pipeline>( \
inputs, renderer, entity, coverage, blend_mode, fg_color, \
absorb_opacity, p, alpha); \
}; \
break;
void BlendFilterContents::SetBlendMode(BlendMode blend_mode) {
if (blend_mode > Entity::kLastAdvancedBlendMode) {
VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
<< " assigned to BlendFilterContents.";
}
blend_mode_ = blend_mode;
if (blend_mode > Entity::kLastPipelineBlendMode) {
switch (blend_mode) {
BLEND_CASE(Screen)
BLEND_CASE(Overlay)
BLEND_CASE(Darken)
BLEND_CASE(Lighten)
BLEND_CASE(ColorDodge)
BLEND_CASE(ColorBurn)
BLEND_CASE(HardLight)
BLEND_CASE(SoftLight)
BLEND_CASE(Difference)
BLEND_CASE(Exclusion)
BLEND_CASE(Multiply)
BLEND_CASE(Hue)
BLEND_CASE(Saturation)
BLEND_CASE(Color)
BLEND_CASE(Luminosity)
default:
FML_UNREACHABLE();
}
}
}
void BlendFilterContents::SetForegroundColor(std::optional<Color> color) {
foreground_color_ = color;
}
std::optional<Entity> BlendFilterContents::RenderFilter(
const FilterInput::Vector& inputs,
const ContentContext& renderer,
const Entity& entity,
const Matrix& effect_transform,
const Rect& coverage,
const std::optional<Rect>& coverage_hint) const {
if (inputs.empty()) {
return std::nullopt;
}
if (inputs.size() == 1 && !foreground_color_.has_value()) {
// Nothing to blend.
return PipelineBlend(inputs, renderer, entity, coverage, BlendMode::kSource,
std::nullopt, GetAbsorbOpacity(), GetAlpha());
}
if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
if (inputs.size() == 1 && foreground_color_.has_value()) {
return CreateForegroundPorterDuffBlend(
inputs[0], renderer, entity, coverage, foreground_color_.value(),
blend_mode_, GetAlpha(), GetAbsorbOpacity());
}
return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
foreground_color_, GetAbsorbOpacity(), GetAlpha());
}
if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
if (inputs.size() == 1 && foreground_color_.has_value()) {
return CreateForegroundAdvancedBlend(
inputs[0], renderer, entity, coverage, foreground_color_.value(),
blend_mode_, GetAlpha(), GetAbsorbOpacity());
}
return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,
foreground_color_, GetAbsorbOpacity(),
GetAlpha());
}
FML_UNREACHABLE();
}
} // namespace impeller