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