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