Flutter Impeller
paint.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 <memory>
8 
9 #include "flutter/display_list/effects/dl_color_filter.h"
10 #include "flutter/display_list/effects/dl_color_sources.h"
11 #include "flutter/display_list/geometry/dl_path.h"
12 #include "fml/logging.h"
28 
29 namespace impeller {
30 
33 using DlRect = flutter::DlRect;
35 using DlPath = flutter::DlPath;
36 
37 std::shared_ptr<ColorSourceContents> Paint::CreateContents() const {
38  if (color_source == nullptr) {
39  auto contents = std::make_shared<SolidColorContents>();
40  contents->SetColor(color);
41  return contents;
42  }
43 
44  switch (color_source->type()) {
45  case flutter::DlColorSourceType::kLinearGradient: {
46  const flutter::DlLinearGradientColorSource* linear =
47  color_source->asLinearGradient();
48  FML_DCHECK(linear);
49  auto start_point = linear->start_point();
50  auto end_point = linear->end_point();
51  std::vector<Color> colors;
52  std::vector<float> stops;
53  skia_conversions::ConvertStops(linear, colors, stops);
54 
55  auto tile_mode = static_cast<Entity::TileMode>(linear->tile_mode());
56  auto effect_transform = linear->matrix();
57 
58  auto contents = std::make_shared<LinearGradientContents>();
59  contents->SetOpacityFactor(color.alpha);
60  contents->SetColors(std::move(colors));
61  contents->SetStops(std::move(stops));
62  contents->SetEndPoints(start_point, end_point);
63  contents->SetTileMode(tile_mode);
64  contents->SetEffectTransform(effect_transform);
65 
66  std::array<Point, 2> bounds{start_point, end_point};
67  auto intrinsic_size = Rect::MakePointBounds(bounds.begin(), bounds.end());
68  if (intrinsic_size.has_value()) {
69  contents->SetColorSourceSize(intrinsic_size->GetSize().Max({1, 1}));
70  }
71  return contents;
72  }
73  case flutter::DlColorSourceType::kRadialGradient: {
74  const flutter::DlRadialGradientColorSource* radialGradient =
75  color_source->asRadialGradient();
76  FML_DCHECK(radialGradient);
77  auto center = radialGradient->center();
78  auto radius = radialGradient->radius();
79  std::vector<Color> colors;
80  std::vector<float> stops;
81  skia_conversions::ConvertStops(radialGradient, colors, stops);
82 
83  auto tile_mode =
84  static_cast<Entity::TileMode>(radialGradient->tile_mode());
85  auto effect_transform = radialGradient->matrix();
86 
87  auto contents = std::make_shared<RadialGradientContents>();
88  contents->SetOpacityFactor(color.alpha);
89  contents->SetColors(std::move(colors));
90  contents->SetStops(std::move(stops));
91  contents->SetCenterAndRadius(center, radius);
92  contents->SetTileMode(tile_mode);
93  contents->SetEffectTransform(effect_transform);
94 
95  auto radius_pt = Point(radius, radius);
96  std::array<Point, 2> bounds{center + radius_pt, center - radius_pt};
97  auto intrinsic_size = Rect::MakePointBounds(bounds.begin(), bounds.end());
98  if (intrinsic_size.has_value()) {
99  contents->SetColorSourceSize(intrinsic_size->GetSize().Max({1, 1}));
100  }
101  return contents;
102  }
103  case flutter::DlColorSourceType::kConicalGradient: {
104  const flutter::DlConicalGradientColorSource* conical_gradient =
105  color_source->asConicalGradient();
106  FML_DCHECK(conical_gradient);
107  Point center = conical_gradient->end_center();
108  DlScalar radius = conical_gradient->end_radius();
109  Point focus_center = conical_gradient->start_center();
110  DlScalar focus_radius = conical_gradient->start_radius();
111  std::vector<Color> colors;
112  std::vector<float> stops;
113  skia_conversions::ConvertStops(conical_gradient, colors, stops);
114 
115  auto tile_mode =
116  static_cast<Entity::TileMode>(conical_gradient->tile_mode());
117  auto effect_transform = conical_gradient->matrix();
118 
119  std::shared_ptr<ConicalGradientContents> contents =
120  std::make_shared<ConicalGradientContents>();
121  contents->SetOpacityFactor(color.alpha);
122  contents->SetColors(std::move(colors));
123  contents->SetStops(std::move(stops));
124  contents->SetCenterAndRadius(center, radius);
125  contents->SetTileMode(tile_mode);
126  contents->SetEffectTransform(effect_transform);
127  contents->SetFocus(focus_center, focus_radius);
128 
129  auto radius_pt = Point(radius, radius);
130  std::array<Point, 2> bounds{center + radius_pt, center - radius_pt};
131  auto intrinsic_size = Rect::MakePointBounds(bounds.begin(), bounds.end());
132  if (intrinsic_size.has_value()) {
133  contents->SetColorSourceSize(intrinsic_size->GetSize().Max({1, 1}));
134  }
135  return contents;
136  }
137  case flutter::DlColorSourceType::kSweepGradient: {
138  const flutter::DlSweepGradientColorSource* sweepGradient =
139  color_source->asSweepGradient();
140  FML_DCHECK(sweepGradient);
141 
142  auto center = sweepGradient->center();
143  auto start_angle = Degrees(sweepGradient->start());
144  auto end_angle = Degrees(sweepGradient->end());
145  std::vector<Color> colors;
146  std::vector<float> stops;
147  skia_conversions::ConvertStops(sweepGradient, colors, stops);
148 
149  auto tile_mode =
150  static_cast<Entity::TileMode>(sweepGradient->tile_mode());
151  auto effect_transform = sweepGradient->matrix();
152 
153  auto contents = std::make_shared<SweepGradientContents>();
154  contents->SetOpacityFactor(color.alpha);
155  contents->SetCenterAndAngles(center, start_angle, end_angle);
156  contents->SetColors(std::move(colors));
157  contents->SetStops(std::move(stops));
158  contents->SetTileMode(tile_mode);
159  contents->SetEffectTransform(effect_transform);
160 
161  return contents;
162  }
163  case flutter::DlColorSourceType::kImage: {
164  const flutter::DlImageColorSource* image_color_source =
165  color_source->asImage();
166  FML_DCHECK(image_color_source &&
167  image_color_source->image()->impeller_texture());
168  auto texture = image_color_source->image()->impeller_texture();
169  auto x_tile_mode = static_cast<Entity::TileMode>(
170  image_color_source->horizontal_tile_mode());
171  auto y_tile_mode = static_cast<Entity::TileMode>(
172  image_color_source->vertical_tile_mode());
173  auto sampler_descriptor =
174  skia_conversions::ToSamplerDescriptor(image_color_source->sampling());
175  auto effect_transform = image_color_source->matrix();
176 
177  auto contents = std::make_shared<TiledTextureContents>();
178  contents->SetOpacityFactor(color.alpha);
179  contents->SetTexture(texture);
180  contents->SetTileModes(x_tile_mode, y_tile_mode);
181  contents->SetSamplerDescriptor(sampler_descriptor);
182  contents->SetEffectTransform(effect_transform);
183  if (color_filter || invert_colors) {
186  invert_colors = invert_colors](const FilterInput::Ref& input) {
187  if (invert_colors && color_filter) {
188  std::shared_ptr<FilterContents> color_filter_output =
190  color_filter, input,
192  return WrapWithInvertColors(
193  FilterInput::Make(color_filter_output),
195  }
196  if (color_filter) {
197  return WrapWithGPUColorFilter(
198  color_filter, input,
200  }
201  return WrapWithInvertColors(
203  };
204  contents->SetColorFilter(filter_proc);
205  }
206  contents->SetColorSourceSize(Size::Ceil(texture->GetSize()));
207  return contents;
208  }
209  case flutter::DlColorSourceType::kRuntimeEffect: {
210  const flutter::DlRuntimeEffectColorSource* runtime_effect_color_source =
211  color_source->asRuntimeEffect();
212  auto runtime_stage =
213  runtime_effect_color_source->runtime_effect()->runtime_stage();
214  auto uniform_data = runtime_effect_color_source->uniform_data();
215  auto samplers = runtime_effect_color_source->samplers();
216 
217  std::vector<RuntimeEffectContents::TextureInput> texture_inputs;
218 
219  for (auto& sampler : samplers) {
220  if (sampler == nullptr) {
221  return nullptr;
222  }
223  auto* image = sampler->asImage();
224  if (!sampler->asImage()) {
225  return nullptr;
226  }
227  FML_DCHECK(image->image()->impeller_texture());
228  texture_inputs.push_back({
229  .sampler_descriptor =
230  skia_conversions::ToSamplerDescriptor(image->sampling()),
231  .texture = image->image()->impeller_texture(),
232  });
233  }
234 
235  auto contents = std::make_shared<RuntimeEffectContents>();
236  contents->SetOpacityFactor(color.alpha);
237  contents->SetRuntimeStage(std::move(runtime_stage));
238  contents->SetUniformData(std::move(uniform_data));
239  contents->SetTextureInputs(std::move(texture_inputs));
240  return contents;
241  }
242  }
243  FML_UNREACHABLE();
244 }
245 
246 std::shared_ptr<Contents> Paint::WithFilters(
247  std::shared_ptr<Contents> input) const {
248  input = WithColorFilter(input, ColorFilterContents::AbsorbOpacity::kYes);
249  auto image_filter =
251  if (image_filter) {
252  input = image_filter;
253  }
254  return input;
255 }
256 
257 std::shared_ptr<Contents> Paint::WithFiltersForSubpassTarget(
258  std::shared_ptr<Contents> input,
259  const Matrix& effect_transform) const {
260  auto image_filter =
261  WithImageFilter(input, effect_transform,
263  if (image_filter) {
264  input = image_filter;
265  }
266  input = WithColorFilter(input, ColorFilterContents::AbsorbOpacity::kYes);
267  return input;
268 }
269 
270 std::shared_ptr<Contents> Paint::WithMaskBlur(std::shared_ptr<Contents> input,
271  bool is_solid_color,
272  const Matrix& ctm) const {
273  if (mask_blur_descriptor.has_value()) {
274  input = mask_blur_descriptor->CreateMaskBlur(FilterInput::Make(input),
275  is_solid_color, ctm);
276  }
277  return input;
278 }
279 
280 std::shared_ptr<FilterContents> Paint::WithImageFilter(
281  const FilterInput::Variant& input,
282  const Matrix& effect_transform,
283  Entity::RenderingMode rendering_mode) const {
284  if (!image_filter) {
285  return nullptr;
286  }
287  auto filter = WrapInput(image_filter, FilterInput::Make(input));
288  filter->SetRenderingMode(rendering_mode);
289  filter->SetEffectTransform(effect_transform);
290  return filter;
291 }
292 
293 std::shared_ptr<Contents> Paint::WithColorFilter(
294  std::shared_ptr<Contents> input,
295  ColorFilterContents::AbsorbOpacity absorb_opacity) const {
296  // Image input types will directly set their color filter,
297  // if any. See `TiledTextureContents.SetColorFilter`.
298  if (color_source &&
299  color_source->type() == flutter::DlColorSourceType::kImage) {
300  return input;
301  }
302 
303  if (!color_filter && !invert_colors) {
304  return input;
305  }
306 
307  // Attempt to apply the color filter on the CPU first.
308  // Note: This is not just an optimization; some color sources rely on
309  // CPU-applied color filters to behave properly.
310  if (input->ApplyColorFilter([&](Color color) -> Color {
311  if (color_filter) {
312  color = GetCPUColorFilterProc(color_filter)(color);
313  }
314  if (invert_colors) {
316  }
317  return color;
318  })) {
319  return input;
320  }
321 
322  if (color_filter) {
323  input = WrapWithGPUColorFilter(color_filter, FilterInput::Make(input),
324  absorb_opacity);
325  }
326  if (invert_colors) {
327  input = WrapWithInvertColors(FilterInput::Make(input), absorb_opacity);
328  }
329 
330  return input;
331 }
332 
333 std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
334  std::shared_ptr<TextureContents> texture_contents,
335  RectGeometry* rect_geom) const {
336  Scalar expand_amount = GaussianBlurFilterContents::CalculateBlurRadius(
337  GaussianBlurFilterContents::ScaleSigma(sigma.sigma));
338  texture_contents->SetSourceRect(
339  texture_contents->GetSourceRect().Expand(expand_amount, expand_amount));
340  auto mask = std::make_shared<SolidColorContents>();
341  mask->SetColor(Color::White());
342  std::optional<Rect> coverage = texture_contents->GetCoverage({});
343  Geometry* geometry = nullptr;
344  if (coverage) {
345  texture_contents->SetDestinationRect(
346  coverage.value().Expand(expand_amount, expand_amount));
347  *rect_geom = RectGeometry(coverage.value());
348  geometry = rect_geom;
349  }
350  mask->SetGeometry(geometry);
351  auto descriptor = texture_contents->GetSamplerDescriptor();
352  texture_contents->SetSamplerDescriptor(descriptor);
353  std::shared_ptr<FilterContents> blurred_mask =
354  FilterContents::MakeGaussianBlur(FilterInput::Make(mask), sigma, sigma,
355  Entity::TileMode::kDecal, style,
356  geometry);
357 
358  return ColorFilterContents::MakeBlend(
359  BlendMode::kSourceIn,
360  {FilterInput::Make(blurred_mask), FilterInput::Make(texture_contents)});
361 }
362 
363 std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
364  std::shared_ptr<ColorSourceContents> color_source_contents,
365  const flutter::DlColorFilter* color_filter,
366  bool invert_colors,
367  RectGeometry* rect_geom) const {
368  // If it's a solid color then we can just get away with doing one Gaussian
369  // blur. The color filter will always be applied on the CPU.
370  if (color_source_contents->IsSolidColor()) {
371  return FilterContents::MakeGaussianBlur(
372  FilterInput::Make(color_source_contents), sigma, sigma,
373  Entity::TileMode::kDecal, style, color_source_contents->GetGeometry());
374  }
375 
376  /// 1. Create an opaque white mask of the original geometry.
377 
378  auto mask = std::make_shared<SolidColorContents>();
379  mask->SetColor(Color::White());
380  mask->SetGeometry(color_source_contents->GetGeometry());
381 
382  /// 2. Blur the mask.
383 
384  auto blurred_mask = FilterContents::MakeGaussianBlur(
385  FilterInput::Make(mask), sigma, sigma, Entity::TileMode::kDecal, style,
386  color_source_contents->GetGeometry());
387 
388  /// 3. Replace the geometry of the original color source with a rectangle that
389  /// covers the full region of the blurred mask. Note that geometry is in
390  /// local bounds.
391 
392  auto expanded_local_bounds = blurred_mask->GetCoverage({});
393  if (!expanded_local_bounds.has_value()) {
394  expanded_local_bounds = Rect();
395  }
396  *rect_geom = RectGeometry(expanded_local_bounds.value());
397  color_source_contents->SetGeometry(rect_geom);
398  std::shared_ptr<Contents> color_contents = color_source_contents;
399 
400  /// 4. Apply the user set color filter on the GPU, if applicable.
401  if (color_filter) {
402  color_contents =
403  WrapWithGPUColorFilter(color_filter, FilterInput::Make(color_contents),
404  ColorFilterContents::AbsorbOpacity::kYes);
405  }
406  if (invert_colors) {
407  color_contents =
408  WrapWithInvertColors(FilterInput::Make(color_contents),
409  ColorFilterContents::AbsorbOpacity::kYes);
410  }
411 
412  /// 5. Composite the color source with the blurred mask.
413 
414  return ColorFilterContents::MakeBlend(
415  BlendMode::kSourceIn,
416  {FilterInput::Make(blurred_mask), FilterInput::Make(color_contents)});
417 }
418 
419 std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
420  const FilterInput::Ref& input,
421  bool is_solid_color,
422  const Matrix& ctm) const {
423  Vector2 blur_sigma(sigma.sigma, sigma.sigma);
424  if (!respect_ctm) {
425  blur_sigma /=
426  Vector2(ctm.GetBasisX().GetLength(), ctm.GetBasisY().GetLength());
427  }
428  if (is_solid_color) {
429  return FilterContents::MakeGaussianBlur(input, Sigma(blur_sigma.x),
430  Sigma(blur_sigma.y),
431  Entity::TileMode::kDecal, style);
432  }
433  return FilterContents::MakeBorderMaskBlur(input, Sigma(blur_sigma.x),
434  Sigma(blur_sigma.y), style);
435 }
436 
437 bool Paint::HasColorFilter() const {
438  return color_filter || invert_colors;
439 }
440 
441 } // namespace impeller
std::shared_ptr< FilterInput > Ref
Definition: filter_input.h:32
std::variant< std::shared_ptr< FilterContents >, std::shared_ptr< Contents >, std::shared_ptr< Texture >, Rect > Variant
Definition: filter_input.h:37
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
Definition: filter_input.cc:19
std::function< std::shared_ptr< ColorFilterContents >(FilterInput::Ref)> ColorFilterProc
void ConvertStops(const flutter::DlGradientColorSourceBase *gradient, std::vector< Color > &colors, std::vector< float > &stops)
Convert display list colors + stops into impeller colors and stops, taking care to ensure that the st...
impeller::SamplerDescriptor ToSamplerDescriptor(const flutter::DlImageSampling options)
std::shared_ptr< ColorFilterContents > WrapWithGPUColorFilter(const flutter::DlColorFilter *filter, const std::shared_ptr< FilterInput > &input, ColorFilterContents::AbsorbOpacity absorb_opacity)
Definition: color_filter.cc:24
Point Vector2
Definition: point.h:331
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
float Scalar
Definition: scalar.h:18
flutter::DlIRect DlIRect
Definition: dl_dispatcher.h:26
std::shared_ptr< FilterContents > WrapInput(const flutter::DlImageFilter *filter, const FilterInput::Ref &input)
Generate a new FilterContents using this filter's configuration.
Definition: image_filter.cc:17
std::shared_ptr< ColorFilterContents > WrapWithInvertColors(const std::shared_ptr< FilterInput > &input, ColorFilterContents::AbsorbOpacity absorb_opacity)
Definition: color_filter.cc:16
TRect< Scalar > Rect
Definition: rect.h:792
TPoint< Scalar > Point
Definition: point.h:327
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
flutter::DlPath DlPath
Definition: dl_dispatcher.h:28
static constexpr const ColorMatrix kColorInversion
A color matrix which inverts colors.
Definition: color_filter.h:16
flutter::DlScalar DlScalar
Definition: dl_dispatcher.h:23
Scalar alpha
Definition: color.h:142
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:301
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
constexpr Vector3 GetBasisY() const
Definition: matrix.h:324
constexpr Vector3 GetBasisX() const
Definition: matrix.h:322
std::shared_ptr< Contents > WithFilters(std::shared_ptr< Contents > input) const
Wrap this paint's configured filters to the given contents.
Definition: paint.cc:246
const flutter::DlColorFilter * color_filter
Definition: paint.h:76
const flutter::DlColorSource * color_source
Definition: paint.h:75
const flutter::DlImageFilter * image_filter
Definition: paint.h:77
std::shared_ptr< Contents > WithMaskBlur(std::shared_ptr< Contents > input, bool is_solid_color, const Matrix &ctm) const
Definition: paint.cc:270
bool invert_colors
Definition: paint.h:85
std::optional< MaskBlurDescriptor > mask_blur_descriptor
Definition: paint.h:87
Color color
Definition: paint.h:74
std::shared_ptr< Contents > WithFiltersForSubpassTarget(std::shared_ptr< Contents > input, const Matrix &effect_transform=Matrix()) const
Wrap this paint's configured filters to the given contents of subpass target.
Definition: paint.cc:257
std::shared_ptr< FilterContents > WithImageFilter(const FilterInput::Variant &input, const Matrix &effect_transform, Entity::RenderingMode rendering_mode) const
Definition: paint.cc:280
std::shared_ptr< ColorSourceContents > CreateContents() const
Definition: paint.cc:37
In filters that use Gaussian distributions, "sigma" is a size of one standard deviation in terms of t...
Definition: sigma.h:32
constexpr static std::optional< TRect > MakePointBounds(const U &value)
Definition: rect.h:165
constexpr TSize Ceil() const
Definition: size.h:114
constexpr Scalar GetLength() const
Definition: vector.h:47