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