Flutter Impeller
blend_filter_contents.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <array>
8 #include <memory>
9 #include <optional>
10 
11 #include "impeller/base/strings.h"
12 #include "impeller/core/formats.h"
19 #include "impeller/entity/entity.h"
25 
26 namespace impeller {
27 
28 std::optional<BlendMode> InvertPorterDuffBlend(BlendMode blend_mode) {
29  switch (blend_mode) {
30  case BlendMode::kClear:
31  return BlendMode::kClear;
32  case BlendMode::kSource:
35  return BlendMode::kSource;
43  return BlendMode::kSourceIn;
47  return BlendMode::kSourceOut;
52  case BlendMode::kXor:
53  return BlendMode::kXor;
54  case BlendMode::kPlus:
55  return BlendMode::kPlus;
57  return BlendMode::kModulate;
58  default:
59  return std::nullopt;
60  }
61 }
62 
65 }
66 
68 
69 using PipelineProc = std::shared_ptr<Pipeline<PipelineDescriptor>> (
71 
72 template <typename TPipeline>
73 static std::optional<Entity> AdvancedBlend(
74  const FilterInput::Vector& inputs,
75  const ContentContext& renderer,
76  const Entity& entity,
77  const Rect& coverage,
78  BlendMode blend_mode,
79  std::optional<Color> foreground_color,
81  PipelineProc pipeline_proc,
82  std::optional<Scalar> alpha) {
83  using VS = typename TPipeline::VertexShader;
84  using FS = typename TPipeline::FragmentShader;
85 
86  //----------------------------------------------------------------------------
87  /// Handle inputs.
88  ///
89 
90  const size_t total_inputs =
91  inputs.size() + (foreground_color.has_value() ? 1 : 0);
92  if (total_inputs < 2) {
93  return std::nullopt;
94  }
95 
96  auto dst_snapshot =
97  inputs[0]->GetSnapshot("AdvancedBlend(Dst)", renderer, entity);
98  if (!dst_snapshot.has_value()) {
99  return std::nullopt;
100  }
101  auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
102  if (!maybe_dst_uvs.has_value()) {
103  return std::nullopt;
104  }
105  auto dst_uvs = maybe_dst_uvs.value();
106 
107  std::optional<Snapshot> src_snapshot;
108  std::array<Point, 4> src_uvs;
109  if (!foreground_color.has_value()) {
110  src_snapshot =
111  inputs[1]->GetSnapshot("AdvancedBlend(Src)", renderer, entity);
112  if (!src_snapshot.has_value()) {
113  if (!dst_snapshot.has_value()) {
114  return std::nullopt;
115  }
116  return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(),
117  entity.GetStencilDepth());
118  }
119  auto maybe_src_uvs = src_snapshot->GetCoverageUVs(coverage);
120  if (!maybe_src_uvs.has_value()) {
121  if (!dst_snapshot.has_value()) {
122  return std::nullopt;
123  }
124  return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(),
125  entity.GetStencilDepth());
126  }
127  src_uvs = maybe_src_uvs.value();
128  }
129 
130  Rect subpass_coverage = coverage;
131  if (entity.GetContents()) {
132  auto coverage_hint = entity.GetContents()->GetCoverageHint();
133 
134  if (coverage_hint.has_value()) {
135  auto maybe_subpass_coverage =
136  subpass_coverage.Intersection(*coverage_hint);
137  if (!maybe_subpass_coverage.has_value()) {
138  return std::nullopt; // Nothing to render.
139  }
140 
141  subpass_coverage = *maybe_subpass_coverage;
142  }
143  }
144 
145  //----------------------------------------------------------------------------
146  /// Render to texture.
147  ///
148 
149  ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
150  RenderPass& pass) {
151  auto& host_buffer = pass.GetTransientsBuffer();
152 
153  auto size = pass.GetRenderTargetSize();
155  vtx_builder.AddVertices({
156  {Point(0, 0), dst_uvs[0], src_uvs[0]},
157  {Point(size.width, 0), dst_uvs[1], src_uvs[1]},
158  {Point(size.width, size.height), dst_uvs[3], src_uvs[3]},
159  {Point(0, 0), dst_uvs[0], src_uvs[0]},
160  {Point(size.width, size.height), dst_uvs[3], src_uvs[3]},
161  {Point(0, size.height), dst_uvs[2], src_uvs[2]},
162  });
163  auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
164 
165  auto options = OptionsFromPass(pass);
166  options.blend_mode = BlendMode::kSource;
167  std::shared_ptr<Pipeline<PipelineDescriptor>> pipeline =
168  std::invoke(pipeline_proc, renderer, options);
169 
170  Command cmd;
171  DEBUG_COMMAND_INFO(cmd, SPrintF("Advanced Blend Filter (%s)",
172  BlendModeToString(blend_mode)));
173  cmd.BindVertices(vtx_buffer);
174  cmd.pipeline = std::move(pipeline);
175 
176  typename FS::BlendInfo blend_info;
177  typename VS::FrameInfo frame_info;
178 
179  auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
181  dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
182  dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
183  }
184  auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
185  dst_sampler_descriptor);
186  FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
187  frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
188  blend_info.dst_input_alpha =
190  ? dst_snapshot->opacity
191  : 1.0;
192 
193  if (foreground_color.has_value()) {
194  blend_info.color_factor = 1;
195  blend_info.color = foreground_color.value();
196  // This texture will not be sampled from due to the color factor. But
197  // this is present so that validation doesn't trip on a missing
198  // binding.
199  FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler);
200  } else {
201  auto src_sampler_descriptor = src_snapshot->sampler_descriptor;
203  src_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
204  src_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
205  }
206  auto src_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
207  src_sampler_descriptor);
208  blend_info.color_factor = 0;
209  blend_info.src_input_alpha = src_snapshot->opacity;
210  FS::BindTextureSamplerSrc(cmd, src_snapshot->texture, src_sampler);
211  frame_info.src_y_coord_scale = src_snapshot->texture->GetYCoordScale();
212  }
213  auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
214  FS::BindBlendInfo(cmd, blend_uniform);
215 
216  frame_info.mvp =
218  Matrix::MakeTranslation(coverage.origin - subpass_coverage.origin);
219 
220  auto uniform_view = host_buffer.EmplaceUniform(frame_info);
221  VS::BindFrameInfo(cmd, uniform_view);
222  pass.AddCommand(std::move(cmd));
223 
224  return true;
225  };
226 
227  auto out_texture = renderer.MakeSubpass(
228  "Advanced Blend Filter", ISize(subpass_coverage.size), callback);
229  if (!out_texture) {
230  return std::nullopt;
231  }
232 
233  return Entity::FromSnapshot(
234  Snapshot{
235  .texture = out_texture,
236  .transform = Matrix::MakeTranslation(subpass_coverage.origin),
237  // Since we absorbed the transform of the inputs and used the
238  // respective snapshot sampling modes when blending, pass on
239  // the default NN clamp sampler.
240  .sampler_descriptor = {},
241  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
242  ? 1.0f
243  : dst_snapshot->opacity) *
244  alpha.value_or(1.0)},
245  entity.GetBlendMode(), entity.GetStencilDepth());
246 }
247 
248 std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend(
249  const std::shared_ptr<FilterInput>& input,
250  const ContentContext& renderer,
251  const Entity& entity,
252  const Rect& coverage,
253  Color foreground_color,
254  BlendMode blend_mode,
255  std::optional<Scalar> alpha,
256  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
257  auto dst_snapshot =
258  input->GetSnapshot("ForegroundAdvancedBlend", renderer, entity);
259  if (!dst_snapshot.has_value()) {
260  return std::nullopt;
261  }
262 
263  RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
264  blend_mode, alpha, absorb_opacity](
265  const ContentContext& renderer,
266  const Entity& entity, RenderPass& pass) -> bool {
269 
270  auto& host_buffer = pass.GetTransientsBuffer();
271 
272  auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
273  if (!maybe_dst_uvs.has_value()) {
274  return false;
275  }
276  auto dst_uvs = maybe_dst_uvs.value();
277 
278  auto size = coverage.size;
279  auto origin = coverage.origin;
280  VertexBufferBuilder<VS::PerVertexData> vtx_builder;
281  vtx_builder.AddVertices({
282  {origin, dst_uvs[0], dst_uvs[0]},
283  {Point(origin.x + size.width, origin.y), dst_uvs[1], dst_uvs[1]},
284  {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
285  dst_uvs[3]},
286  {origin, dst_uvs[0], dst_uvs[0]},
287  {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
288  dst_uvs[3]},
289  {Point(origin.x, origin.y + size.height), dst_uvs[2], dst_uvs[2]},
290  });
291  auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
292 
293  Command cmd;
294  DEBUG_COMMAND_INFO(cmd, SPrintF("Foreground Advanced Blend Filter (%s)",
295  BlendModeToString(blend_mode)));
296  cmd.BindVertices(vtx_buffer);
297  cmd.stencil_reference = entity.GetStencilDepth();
298  auto options = OptionsFromPass(pass);
299 
300  switch (blend_mode) {
301  case BlendMode::kScreen:
302  cmd.pipeline = renderer.GetBlendScreenPipeline(options);
303  break;
304  case BlendMode::kOverlay:
305  cmd.pipeline = renderer.GetBlendOverlayPipeline(options);
306  break;
307  case BlendMode::kDarken:
308  cmd.pipeline = renderer.GetBlendDarkenPipeline(options);
309  break;
310  case BlendMode::kLighten:
311  cmd.pipeline = renderer.GetBlendLightenPipeline(options);
312  break;
314  cmd.pipeline = renderer.GetBlendColorDodgePipeline(options);
315  break;
317  cmd.pipeline = renderer.GetBlendColorBurnPipeline(options);
318  break;
320  cmd.pipeline = renderer.GetBlendHardLightPipeline(options);
321  break;
323  cmd.pipeline = renderer.GetBlendSoftLightPipeline(options);
324  break;
326  cmd.pipeline = renderer.GetBlendDifferencePipeline(options);
327  break;
329  cmd.pipeline = renderer.GetBlendExclusionPipeline(options);
330  break;
332  cmd.pipeline = renderer.GetBlendMultiplyPipeline(options);
333  break;
334  case BlendMode::kHue:
335  cmd.pipeline = renderer.GetBlendHuePipeline(options);
336  break;
338  cmd.pipeline = renderer.GetBlendSaturationPipeline(options);
339  break;
340  case BlendMode::kColor:
341  cmd.pipeline = renderer.GetBlendColorPipeline(options);
342  break;
344  cmd.pipeline = renderer.GetBlendLuminosityPipeline(options);
345  break;
346  default:
347  return false;
348  }
349 
350  FS::BlendInfo blend_info;
351  VS::FrameInfo frame_info;
352 
353  auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
354  if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
355  dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
356  dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
357  }
358  auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
359  dst_sampler_descriptor);
360  FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
361  frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale();
362  blend_info.dst_input_alpha =
364  ? dst_snapshot->opacity * alpha.value_or(1.0)
365  : 1.0;
366 
367  blend_info.color_factor = 1;
368  blend_info.color = foreground_color;
369  // This texture will not be sampled from due to the color factor. But
370  // this is present so that validation doesn't trip on a missing
371  // binding.
372  FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler);
373 
374  auto blend_uniform = host_buffer.EmplaceUniform(blend_info);
375  FS::BindBlendInfo(cmd, blend_uniform);
376 
377  frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
378  entity.GetTransformation();
379 
380  auto uniform_view = host_buffer.EmplaceUniform(frame_info);
381  VS::BindFrameInfo(cmd, uniform_view);
382 
383  return pass.AddCommand(std::move(cmd));
384  };
385  CoverageProc coverage_proc =
386  [coverage](const Entity& entity) -> std::optional<Rect> {
387  return coverage.TransformBounds(entity.GetTransformation());
388  };
389 
390  auto contents = AnonymousContents::Make(render_proc, coverage_proc);
391 
392  Entity sub_entity;
393  sub_entity.SetContents(std::move(contents));
394  sub_entity.SetStencilDepth(entity.GetStencilDepth());
395 
396  return sub_entity;
397 }
398 
399 std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend(
400  const std::shared_ptr<FilterInput>& input,
401  const ContentContext& renderer,
402  const Entity& entity,
403  const Rect& coverage,
404  Color foreground_color,
405  BlendMode blend_mode,
406  std::optional<Scalar> alpha,
407  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
408  if (blend_mode == BlendMode::kClear) {
409  return std::nullopt;
410  }
411 
412  if (blend_mode == BlendMode::kSource) {
413  auto contents = std::make_shared<SolidColorContents>();
414  contents->SetGeometry(Geometry::MakeRect(coverage));
415  contents->SetColor(foreground_color);
416 
417  Entity foreground_entity;
418  foreground_entity.SetBlendMode(entity.GetBlendMode());
419  foreground_entity.SetStencilDepth(entity.GetStencilDepth());
420  foreground_entity.SetContents(std::move(contents));
421  return foreground_entity;
422  }
423 
424  auto dst_snapshot =
425  input->GetSnapshot("ForegroundPorterDuffBlend", renderer, entity);
426  if (!dst_snapshot.has_value()) {
427  return std::nullopt;
428  }
429 
430  if (blend_mode == BlendMode::kDestination) {
431  return Entity::FromSnapshot(dst_snapshot, entity.GetBlendMode(),
432  entity.GetStencilDepth());
433  }
434 
435  RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
436  blend_mode, absorb_opacity, alpha](
437  const ContentContext& renderer,
438  const Entity& entity, RenderPass& pass) -> bool {
441 
442  auto& host_buffer = pass.GetTransientsBuffer();
443 
444  auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage);
445  if (!maybe_dst_uvs.has_value()) {
446  return false;
447  }
448  auto dst_uvs = maybe_dst_uvs.value();
449 
450  auto size = coverage.size;
451  auto origin = coverage.origin;
452  auto color = foreground_color.Premultiply();
453  VertexBufferBuilder<VS::PerVertexData> vtx_builder;
454  vtx_builder.AddVertices({
455  {origin, dst_uvs[0], color},
456  {Point(origin.x + size.width, origin.y), dst_uvs[1], color},
457  {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
458  color},
459  {origin, dst_uvs[0], color},
460  {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3],
461  color},
462  {Point(origin.x, origin.y + size.height), dst_uvs[2], color},
463  });
464  auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
465 
466  Command cmd;
467  DEBUG_COMMAND_INFO(cmd, SPrintF("Foreground PorterDuff Blend Filter (%s)",
468  BlendModeToString(blend_mode)));
469  cmd.BindVertices(vtx_buffer);
470  cmd.stencil_reference = entity.GetStencilDepth();
471  auto options = OptionsFromPass(pass);
472  cmd.pipeline = renderer.GetPorterDuffBlendPipeline(options);
473 
474  FS::FragInfo frag_info;
475  VS::FrameInfo frame_info;
476 
477  auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor;
478  if (renderer.GetDeviceCapabilities().SupportsDecalSamplerAddressMode()) {
479  dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal;
480  dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal;
481  }
482  auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
483  dst_sampler_descriptor);
484  FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler);
485  frame_info.texture_sampler_y_coord_scale =
486  dst_snapshot->texture->GetYCoordScale();
487 
488  frag_info.input_alpha =
490  ? dst_snapshot->opacity * alpha.value_or(1.0)
491  : 1.0;
492  frag_info.output_alpha = 1.0;
493 
494  auto blend_coefficients =
495  kPorterDuffCoefficients[static_cast<int>(blend_mode)];
496  frag_info.src_coeff = blend_coefficients[0];
497  frag_info.src_coeff_dst_alpha = blend_coefficients[1];
498  frag_info.dst_coeff = blend_coefficients[2];
499  frag_info.dst_coeff_src_alpha = blend_coefficients[3];
500  frag_info.dst_coeff_src_color = blend_coefficients[4];
501 
502  FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
503 
504  frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
505  entity.GetTransformation();
506 
507  auto uniform_view = host_buffer.EmplaceUniform(frame_info);
508  VS::BindFrameInfo(cmd, uniform_view);
509 
510  return pass.AddCommand(std::move(cmd));
511  };
512 
513  CoverageProc coverage_proc =
514  [coverage](const Entity& entity) -> std::optional<Rect> {
515  return coverage.TransformBounds(entity.GetTransformation());
516  };
517 
518  auto contents = AnonymousContents::Make(render_proc, coverage_proc);
519 
520  Entity sub_entity;
521  sub_entity.SetContents(std::move(contents));
522  sub_entity.SetStencilDepth(entity.GetStencilDepth());
523 
524  return sub_entity;
525 }
526 
527 static std::optional<Entity> PipelineBlend(
528  const FilterInput::Vector& inputs,
529  const ContentContext& renderer,
530  const Entity& entity,
531  const Rect& coverage,
532  BlendMode blend_mode,
533  std::optional<Color> foreground_color,
534  ColorFilterContents::AbsorbOpacity absorb_opacity,
535  std::optional<Scalar> alpha) {
536  using VS = BlendPipeline::VertexShader;
538 
539  auto dst_snapshot =
540  inputs[0]->GetSnapshot("PipelineBlend(Dst)", renderer, entity);
541  if (!dst_snapshot.has_value()) {
542  return std::nullopt; // Nothing to render.
543  }
544 
545  Rect subpass_coverage = coverage;
546  if (entity.GetContents()) {
547  auto coverage_hint = entity.GetContents()->GetCoverageHint();
548 
549  if (coverage_hint.has_value()) {
550  auto maybe_subpass_coverage =
551  subpass_coverage.Intersection(*coverage_hint);
552  if (!maybe_subpass_coverage.has_value()) {
553  return std::nullopt; // Nothing to render.
554  }
555 
556  subpass_coverage = *maybe_subpass_coverage;
557  }
558  }
559 
560  ContentContext::SubpassCallback callback = [&](const ContentContext& renderer,
561  RenderPass& pass) {
562  auto& host_buffer = pass.GetTransientsBuffer();
563 
564  Command cmd;
565  DEBUG_COMMAND_INFO(cmd, SPrintF("Pipeline Blend Filter (%s)",
566  BlendModeToString(blend_mode)));
567  auto options = OptionsFromPass(pass);
568 
569  auto add_blend_command = [&](std::optional<Snapshot> input) {
570  if (!input.has_value()) {
571  return false;
572  }
573  auto input_coverage = input->GetCoverage();
574  if (!input_coverage.has_value()) {
575  return false;
576  }
577 
578  auto sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler(
579  input->sampler_descriptor);
580  FS::BindTextureSamplerSrc(cmd, input->texture, sampler);
581 
582  auto size = input->texture->GetSize();
584  vtx_builder.AddVertices({
585  {Point(0, 0), Point(0, 0)},
586  {Point(size.width, 0), Point(1, 0)},
587  {Point(size.width, size.height), Point(1, 1)},
588  {Point(0, 0), Point(0, 0)},
589  {Point(size.width, size.height), Point(1, 1)},
590  {Point(0, size.height), Point(0, 1)},
591  });
592  auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer);
593  cmd.BindVertices(vtx_buffer);
594 
595  VS::FrameInfo frame_info;
596  frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
597  Matrix::MakeTranslation(-subpass_coverage.origin) *
598  input->transform;
599  frame_info.texture_sampler_y_coord_scale =
600  input->texture->GetYCoordScale();
601 
602  FS::FragInfo frag_info;
603  frag_info.input_alpha =
605  ? input->opacity
606  : 1.0;
607  FS::BindFragInfo(cmd, host_buffer.EmplaceUniform(frag_info));
608  VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info));
609 
610  pass.AddCommand(std::move(cmd));
611  return true;
612  };
613 
614  // Draw the first texture using kSource.
615  options.blend_mode = BlendMode::kSource;
616  cmd.pipeline = renderer.GetBlendPipeline(options);
617  if (!add_blend_command(dst_snapshot)) {
618  return true;
619  }
620 
621  // Write subsequent textures using the selected blend mode.
622 
623  if (inputs.size() >= 2) {
624  options.blend_mode = blend_mode;
625  cmd.pipeline = renderer.GetBlendPipeline(options);
626 
627  for (auto texture_i = inputs.begin() + 1; texture_i < inputs.end();
628  texture_i++) {
629  auto src_input = texture_i->get()->GetSnapshot("PipelineBlend(Src)",
630  renderer, entity);
631  if (!add_blend_command(src_input)) {
632  return true;
633  }
634  }
635  }
636 
637  // If a foreground color is set, blend it in.
638 
639  if (foreground_color.has_value()) {
640  auto contents = std::make_shared<SolidColorContents>();
641  contents->SetGeometry(
642  Geometry::MakeRect(Rect::MakeSize(pass.GetRenderTargetSize())));
643  contents->SetColor(foreground_color.value());
644 
645  Entity foreground_entity;
646  foreground_entity.SetBlendMode(blend_mode);
647  foreground_entity.SetContents(contents);
648  if (!foreground_entity.Render(renderer, pass)) {
649  return false;
650  }
651  }
652 
653  return true;
654  };
655 
656  auto out_texture = renderer.MakeSubpass(
657  "Pipeline Blend Filter", ISize(subpass_coverage.size), callback);
658 
659  if (!out_texture) {
660  return std::nullopt;
661  }
662 
663  return Entity::FromSnapshot(
664  Snapshot{
665  .texture = out_texture,
666  .transform = Matrix::MakeTranslation(subpass_coverage.origin),
667  // Since we absorbed the transform of the inputs and used the
668  // respective snapshot sampling modes when blending, pass on
669  // the default NN clamp sampler.
670  .sampler_descriptor = {},
671  .opacity = (absorb_opacity == ColorFilterContents::AbsorbOpacity::kYes
672  ? 1.0f
673  : dst_snapshot->opacity) *
674  alpha.value_or(1.0)},
675  entity.GetBlendMode(), entity.GetStencilDepth());
676 }
677 
678 #define BLEND_CASE(mode) \
679  case BlendMode::k##mode: \
680  advanced_blend_proc_ = \
681  [](const FilterInput::Vector& inputs, const ContentContext& renderer, \
682  const Entity& entity, const Rect& coverage, BlendMode blend_mode, \
683  std::optional<Color> fg_color, \
684  ColorFilterContents::AbsorbOpacity absorb_opacity, \
685  std::optional<Scalar> alpha) { \
686  PipelineProc p = &ContentContext::GetBlend##mode##Pipeline; \
687  return AdvancedBlend<Blend##mode##Pipeline>( \
688  inputs, renderer, entity, coverage, blend_mode, fg_color, \
689  absorb_opacity, p, alpha); \
690  }; \
691  break;
692 
694  if (blend_mode > Entity::kLastAdvancedBlendMode) {
695  VALIDATION_LOG << "Invalid blend mode " << static_cast<int>(blend_mode)
696  << " assigned to BlendFilterContents.";
697  }
698 
699  blend_mode_ = blend_mode;
700 
701  if (blend_mode > Entity::kLastPipelineBlendMode) {
702  switch (blend_mode) {
703  BLEND_CASE(Screen)
704  BLEND_CASE(Overlay)
705  BLEND_CASE(Darken)
706  BLEND_CASE(Lighten)
707  BLEND_CASE(ColorDodge)
708  BLEND_CASE(ColorBurn)
709  BLEND_CASE(HardLight)
710  BLEND_CASE(SoftLight)
711  BLEND_CASE(Difference)
712  BLEND_CASE(Exclusion)
713  BLEND_CASE(Multiply)
714  BLEND_CASE(Hue)
718  default:
719  FML_UNREACHABLE();
720  }
721  }
722 }
723 
724 void BlendFilterContents::SetForegroundColor(std::optional<Color> color) {
725  foreground_color_ = color;
726 }
727 
728 std::optional<Entity> BlendFilterContents::RenderFilter(
729  const FilterInput::Vector& inputs,
730  const ContentContext& renderer,
731  const Entity& entity,
732  const Matrix& effect_transform,
733  const Rect& coverage,
734  const std::optional<Rect>& coverage_hint) const {
735  if (inputs.empty()) {
736  return std::nullopt;
737  }
738 
739  if (inputs.size() == 1 && !foreground_color_.has_value()) {
740  // Nothing to blend.
741  return PipelineBlend(inputs, renderer, entity, coverage, BlendMode::kSource,
742  std::nullopt, GetAbsorbOpacity(), GetAlpha());
743  }
744 
745  if (blend_mode_ <= Entity::kLastPipelineBlendMode) {
746  if (inputs.size() == 1 && foreground_color_.has_value() &&
748  return CreateForegroundPorterDuffBlend(
749  inputs[0], renderer, entity, coverage, foreground_color_.value(),
750  blend_mode_, GetAlpha(), GetAbsorbOpacity());
751  }
752  return PipelineBlend(inputs, renderer, entity, coverage, blend_mode_,
753  foreground_color_, GetAbsorbOpacity(), GetAlpha());
754  }
755 
756  if (blend_mode_ <= Entity::kLastAdvancedBlendMode) {
757  if (inputs.size() == 1 && foreground_color_.has_value() &&
759  return CreateForegroundAdvancedBlend(
760  inputs[0], renderer, entity, coverage, foreground_color_.value(),
761  blend_mode_, GetAlpha(), GetAbsorbOpacity());
762  }
763  return advanced_blend_proc_(inputs, renderer, entity, coverage, blend_mode_,
764  foreground_color_, GetAbsorbOpacity(),
765  GetAlpha());
766  }
767 
768  FML_UNREACHABLE();
769 }
770 
771 } // namespace impeller
impeller::BlendMode
BlendMode
Definition: color.h:57
impeller::OptionsFromPass
ContentContextOptions OptionsFromPass(const RenderPass &pass)
Definition: contents.cc:20
impeller::Command
An object used to specify work to the GPU along with references to resources the GPU will used when d...
Definition: command.h:99
BLEND_CASE
#define BLEND_CASE(mode)
Definition: blend_filter_contents.cc:678
DEBUG_COMMAND_INFO
#define DEBUG_COMMAND_INFO(obj, arg)
Definition: command.h:31
impeller::Entity::kLastPipelineBlendMode
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:23
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
contents.h
impeller::BlendFilterContents::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: blend_filter_contents.cc:693
impeller::Entity::GetStencilDepth
uint32_t GetStencilDepth() const
Definition: entity.cc:89
impeller::TRect::size
TSize< Type > size
Definition: rect.h:24
impeller::ContentContext::GetBlendPipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > GetBlendPipeline(ContentContextOptions opts) const
Definition: content_context.h:422
impeller::Entity::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:97
impeller::BlendFilterContents::SetForegroundColor
void SetForegroundColor(std::optional< Color > color)
Sets a source color which is blended after all of the inputs have been blended.
Definition: blend_filter_contents.cc:724
impeller::Entity::FromSnapshot
static std::optional< Entity > FromSnapshot(const std::optional< Snapshot > &snapshot, BlendMode blend_mode=BlendMode::kSourceOver, uint32_t stencil_depth=0)
Create an entity that can be used to render a given snapshot.
Definition: entity.cc:21
sampler_library.h
impeller::BlendMode::kMultiply
@ kMultiply
entity.h
solid_color_contents.h
impeller::Color
Definition: color.h:122
formats.h
impeller::InvertPorterDuffBlend
std::optional< BlendMode > InvertPorterDuffBlend(BlendMode blend_mode)
Definition: blend_filter_contents.cc:28
impeller::BlendMode::kSourceIn
@ kSourceIn
impeller::Geometry::MakeRect
static std::unique_ptr< Geometry > MakeRect(Rect rect)
Definition: geometry.cc:142
impeller::RenderPipelineT::FragmentShader
FragmentShader_ FragmentShader
Definition: pipeline.h:91
impeller::BlendMode::kSaturation
@ kSaturation
impeller::VertexBufferBuilder::AddVertices
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
Definition: vertex_buffer_builder.h:64
impeller::BlendMode::kHue
@ kHue
impeller::BlendMode::kDestinationIn
@ kDestinationIn
impeller::BlendMode::kDarken
@ kDarken
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:94
impeller::BlendMode::kColor
@ kColor
impeller::BlendMode::kLuminosity
@ kLuminosity
impeller::AdvancedBlend
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)
Definition: blend_filter_contents.cc:73
impeller::BlendMode::kXor
@ kXor
impeller::Entity::SetContents
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:77
path_builder.h
impeller::BlendMode::kOverlay
@ kOverlay
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::Entity
Definition: entity.h:21
impeller::ContentContext::MakeSubpass
std::shared_ptr< Texture > MakeSubpass(const std::string &label, ISize texture_size, const SubpassCallback &subpass_callback, bool msaa_enabled=true) const
Creates a new texture of size texture_size and calls subpass_callback with a RenderPass for drawing t...
Definition: content_context.cc:373
impeller::Point
TPoint< Scalar > Point
Definition: point.h:306
impeller::BlendMode::kLighten
@ kLighten
impeller::TRect::Intersection
constexpr std::optional< TRect< T > > Intersection(const TRect &o) const
Definition: rect.h:219
render_pass.h
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Entity::Render
bool Render(const ContentContext &renderer, RenderPass &parent_pass) const
Definition: entity.cc:155
impeller::BlendMode::kHardLight
@ kHardLight
impeller::BlendMode::kPlus
@ kPlus
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::BlendMode::kDestination
@ kDestination
impeller::ContentContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: content_context.cc:440
impeller::VertexBufferBuilder
Definition: vertex_buffer_builder.h:23
impeller::Entity::kLastAdvancedBlendMode
static constexpr BlendMode kLastAdvancedBlendMode
Definition: entity.h:24
impeller::ColorFilterContents::GetAbsorbOpacity
AbsorbOpacity GetAbsorbOpacity() const
Definition: color_filter_contents.cc:89
impeller::Snapshot
Represents a texture and its intended draw transform/sampler configuration.
Definition: snapshot.h:24
impeller::BlendMode::kDestinationATop
@ kDestinationATop
impeller::kPorterDuffCoefficients
constexpr std::array< std::array< Scalar, 5 >, 15 > kPorterDuffCoefficients
Definition: blend_filter_contents.h:14
impeller::Entity::GetContents
const std::shared_ptr< Contents > & GetContents() const
Definition: entity.cc:81
impeller::PipelineProc
std::shared_ptr< Pipeline< PipelineDescriptor > >(ContentContext::*)(ContentContextOptions) const PipelineProc
Definition: blend_filter_contents.cc:70
impeller::TRect::origin
TPoint< Type > origin
Definition: rect.h:23
filter_input.h
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:306
impeller::Luminosity
static constexpr Scalar Luminosity(Vector3 color)
Definition: color.cc:140
strings.h
impeller::BlendMode::kDestinationOut
@ kDestinationOut
color_filter_contents.h
impeller::BlendFilterContents::~BlendFilterContents
~BlendFilterContents() override
impeller::BlendMode::kSoftLight
@ kSoftLight
anonymous_contents.h
impeller::Entity::GetBlendMode
BlendMode GetBlendMode() const
Definition: entity.cc:101
impeller::VertexBufferBuilder::CreateVertexBuffer
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
Definition: vertex_buffer_builder.h:78
impeller::ContentContext::SubpassCallback
std::function< bool(const ContentContext &, RenderPass &)> SubpassCallback
Definition: content_context.h:703
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:136
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:27
impeller::BlendMode::kDifference
@ kDifference
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:60
content_context.h
impeller::Contents::RenderProc
std::function< bool(const ContentContext &renderer, const Entity &entity, RenderPass &pass)> RenderProc
Definition: contents.h:48
impeller::ContentContext::GetDeviceCapabilities
const Capabilities & GetDeviceCapabilities() const
Definition: content_context.cc:444
impeller::BlendMode::kExclusion
@ kExclusion
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:52
snapshot.h
impeller::BlendMode::kDestinationOver
@ kDestinationOver
impeller::BlendMode::kClear
@ kClear
impeller::PipelineBlend
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)
Definition: blend_filter_contents.cc:527
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:448
color.h
blend_filter_contents.h
impeller::BlendMode::kColorDodge
@ kColorDodge
impeller::Contents::CoverageProc
std::function< std::optional< Rect >(const Entity &entity)> CoverageProc
Definition: contents.h:49
impeller::Command::BindVertices
bool BindVertices(const VertexBuffer &buffer)
Specify the vertex and index buffer to use for this command.
Definition: command.cc:15
impeller::BlendMode::kSourceATop
@ kSourceATop
impeller::Snapshot::texture
std::shared_ptr< Texture > texture
Definition: snapshot.h:25
impeller::Command::pipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > pipeline
Definition: command.h:103
impeller::BlendMode::kModulate
@ kModulate
impeller::ColorFilterContents::AbsorbOpacity::kYes
@ kYes
impeller::FilterInput::Vector
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:32
impeller::ColorFilterContents::GetAlpha
std::optional< Scalar > GetAlpha() const
Definition: color_filter_contents.cc:98
impeller::Capabilities::SupportsDecalSamplerAddressMode
virtual bool SupportsDecalSamplerAddressMode() const =0
Whether the context backend supports SamplerAddressMode::Decal.
impeller::RenderPipelineT::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:90
impeller::ContentContextOptions
Definition: content_context.h:302
impeller::ColorFilterContents::AbsorbOpacity
AbsorbOpacity
Definition: color_filter_contents.h:13
impeller::BlendMode::kScreen
@ kScreen
impeller
Definition: aiks_context.cc:10
impeller::ContentContext
Definition: content_context.h:344
impeller::BlendMode::kSourceOut
@ kSourceOut
impeller::BlendFilterContents::BlendFilterContents
BlendFilterContents()
Definition: blend_filter_contents.cc:63
impeller::TRect
Definition: rect.h:20
impeller::BlendMode::kSource
@ kSource
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:36
impeller::AnonymousContents::Make
static std::shared_ptr< Contents > Make(RenderProc render_proc, CoverageProc coverage_proc)
Definition: anonymous_contents.cc:11
impeller::Saturation
static constexpr Scalar Saturation(Vector3 color)
Definition: color.cc:166
impeller::SamplerAddressMode::kDecal
@ kDecal
decal sampling mode is only supported on devices that pass the Capabilities.SupportsDecalSamplerAddre...