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() const {
63  if (color_source == nullptr) {
64  auto contents = std::make_shared<SolidColorContents>();
65  contents->SetColor(color);
66  return contents;
67  }
68 
69  switch (color_source->type()) {
70  case flutter::DlColorSourceType::kLinearGradient: {
71  const flutter::DlLinearGradientColorSource* linear =
72  color_source->asLinearGradient();
73  FML_DCHECK(linear);
74  auto start_point = linear->start_point();
75  auto end_point = linear->end_point();
76  std::vector<Color> colors;
77  std::vector<float> stops;
78  ConvertStops(linear, colors, stops);
79 
80  auto tile_mode = static_cast<Entity::TileMode>(linear->tile_mode());
81  auto effect_transform = linear->matrix();
82 
83  auto contents = std::make_shared<LinearGradientContents>();
84  contents->SetOpacityFactor(color.alpha);
85  contents->SetColors(std::move(colors));
86  contents->SetStops(std::move(stops));
87  contents->SetEndPoints(start_point, end_point);
88  contents->SetTileMode(tile_mode);
89  contents->SetEffectTransform(effect_transform);
90 
91  std::array<Point, 2> bounds{start_point, end_point};
92  auto intrinsic_size = Rect::MakePointBounds(bounds.begin(), bounds.end());
93  if (intrinsic_size.has_value()) {
94  contents->SetColorSourceSize(intrinsic_size->GetSize().Max({1, 1}));
95  }
96  return contents;
97  }
98  case flutter::DlColorSourceType::kRadialGradient: {
99  const flutter::DlRadialGradientColorSource* radialGradient =
100  color_source->asRadialGradient();
101  FML_DCHECK(radialGradient);
102  auto center = radialGradient->center();
103  auto radius = radialGradient->radius();
104  std::vector<Color> colors;
105  std::vector<float> stops;
106  ConvertStops(radialGradient, colors, stops);
107 
108  auto tile_mode =
109  static_cast<Entity::TileMode>(radialGradient->tile_mode());
110  auto effect_transform = radialGradient->matrix();
111 
112  auto contents = std::make_shared<RadialGradientContents>();
113  contents->SetOpacityFactor(color.alpha);
114  contents->SetColors(std::move(colors));
115  contents->SetStops(std::move(stops));
116  contents->SetCenterAndRadius(center, radius);
117  contents->SetTileMode(tile_mode);
118  contents->SetEffectTransform(effect_transform);
119 
120  auto radius_pt = Point(radius, radius);
121  std::array<Point, 2> bounds{center + radius_pt, center - radius_pt};
122  auto intrinsic_size = Rect::MakePointBounds(bounds.begin(), bounds.end());
123  if (intrinsic_size.has_value()) {
124  contents->SetColorSourceSize(intrinsic_size->GetSize().Max({1, 1}));
125  }
126  return contents;
127  }
128  case flutter::DlColorSourceType::kConicalGradient: {
129  const flutter::DlConicalGradientColorSource* conical_gradient =
130  color_source->asConicalGradient();
131  FML_DCHECK(conical_gradient);
132  Point center = conical_gradient->end_center();
133  DlScalar radius = conical_gradient->end_radius();
134  Point focus_center = conical_gradient->start_center();
135  DlScalar focus_radius = conical_gradient->start_radius();
136  std::vector<Color> colors;
137  std::vector<float> stops;
138  ConvertStops(conical_gradient, colors, stops);
139 
140  auto tile_mode =
141  static_cast<Entity::TileMode>(conical_gradient->tile_mode());
142  auto effect_transform = conical_gradient->matrix();
143 
144  std::shared_ptr<ConicalGradientContents> contents =
145  std::make_shared<ConicalGradientContents>();
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>();
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>();
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>();
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>();
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>();
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  auto mask = std::make_shared<SolidColorContents>();
373  mask->SetColor(Color::White());
374  std::optional<Rect> coverage = texture_contents->GetCoverage({});
375  Geometry* geometry = nullptr;
376  if (coverage) {
377  texture_contents->SetDestinationRect(
378  coverage.value().Expand(expand_amount, expand_amount));
379  *rect_geom = FillRectGeometry(coverage.value());
380  geometry = rect_geom;
381  }
382  mask->SetGeometry(geometry);
383  auto descriptor = texture_contents->GetSamplerDescriptor();
384  texture_contents->SetSamplerDescriptor(descriptor);
385  std::shared_ptr<FilterContents> blurred_mask =
386  FilterContents::MakeGaussianBlur(FilterInput::Make(mask), sigma, sigma,
387  Entity::TileMode::kDecal, style,
388  geometry);
389 
390  return ColorFilterContents::MakeBlend(
391  BlendMode::kSrcIn,
392  {FilterInput::Make(blurred_mask), FilterInput::Make(texture_contents)});
393 }
394 
395 std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
396  std::shared_ptr<ColorSourceContents> color_source_contents,
397  const flutter::DlColorFilter* color_filter,
398  bool invert_colors,
399  FillRectGeometry* rect_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 (color_source_contents->IsSolidColor()) {
403  return FilterContents::MakeGaussianBlur(
404  FilterInput::Make(color_source_contents), sigma, sigma,
405  Entity::TileMode::kDecal, style, color_source_contents->GetGeometry());
406  }
407 
408  /// 1. Create an opaque white mask of the original geometry.
409 
410  auto mask = std::make_shared<SolidColorContents>();
411  mask->SetColor(Color::White());
412  mask->SetGeometry(color_source_contents->GetGeometry());
413 
414  /// 2. Blur the mask.
415 
416  auto blurred_mask = FilterContents::MakeGaussianBlur(
417  FilterInput::Make(mask), sigma, sigma, Entity::TileMode::kDecal, style,
418  color_source_contents->GetGeometry());
419 
420  /// 3. Replace the geometry of the original color source with a rectangle that
421  /// covers the full region of the blurred mask. Note that geometry is in
422  /// local bounds.
423 
424  auto expanded_local_bounds = blurred_mask->GetCoverage({});
425  if (!expanded_local_bounds.has_value()) {
426  expanded_local_bounds = Rect();
427  }
428  *rect_geom = FillRectGeometry(expanded_local_bounds.value());
429  color_source_contents->SetGeometry(rect_geom);
430  std::shared_ptr<Contents> color_contents = color_source_contents;
431 
432  /// 4. Apply the user set color filter on the GPU, if applicable.
433  if (color_filter) {
434  color_contents =
435  WrapWithGPUColorFilter(color_filter, FilterInput::Make(color_contents),
436  ColorFilterContents::AbsorbOpacity::kYes);
437  }
438  if (invert_colors) {
439  color_contents =
440  WrapWithInvertColors(FilterInput::Make(color_contents),
441  ColorFilterContents::AbsorbOpacity::kYes);
442  }
443 
444  /// 5. Composite the color source with the blurred mask.
445 
446  return ColorFilterContents::MakeBlend(
447  BlendMode::kSrcIn,
448  {FilterInput::Make(blurred_mask), FilterInput::Make(color_contents)});
449 }
450 
451 std::shared_ptr<FilterContents> Paint::MaskBlurDescriptor::CreateMaskBlur(
452  const FilterInput::Ref& input,
453  bool is_solid_color,
454  const Matrix& ctm) const {
455  Vector2 blur_sigma(sigma.sigma, sigma.sigma);
456  if (!respect_ctm) {
457  blur_sigma /=
458  Vector2(ctm.GetBasisX().GetLength(), ctm.GetBasisY().GetLength());
459  }
460  if (is_solid_color) {
461  return FilterContents::MakeGaussianBlur(input, Sigma(blur_sigma.x),
462  Sigma(blur_sigma.y),
463  Entity::TileMode::kDecal, style);
464  }
465  return FilterContents::MakeBorderMaskBlur(input, Sigma(blur_sigma.x),
466  Sigma(blur_sigma.y), style);
467 }
468 
469 bool Paint::HasColorFilter() const {
470  return color_filter || invert_colors;
471 }
472 
473 } // 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:331
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:792
TPoint< Scalar > Point
Definition: point.h:327
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:337
constexpr Vector3 GetBasisX() const
Definition: matrix.h:335
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:77
const flutter::DlColorSource * color_source
Definition: paint.h:76
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:78
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:83
std::optional< MaskBlurDescriptor > mask_blur_descriptor
Definition: paint.h:85
Color color
Definition: paint.h:75
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
std::shared_ptr< ColorSourceContents > CreateContents() const
Definition: paint.cc:62
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