Flutter Impeller
canvas.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 
5 #include "impeller/aiks/canvas.h"
6 
7 #include <optional>
8 #include <utility>
9 
10 #include "flutter/fml/logging.h"
11 #include "flutter/fml/trace_event.h"
25 
26 namespace impeller {
27 
28 namespace {
29 
30 static std::shared_ptr<Contents> CreateContentsForGeometryWithFilters(
31  const Paint& paint,
32  std::shared_ptr<Geometry> geometry) {
33  std::shared_ptr<ColorSourceContents> contents =
34  paint.color_source.GetContents(paint);
35 
36  // Attempt to apply the color filter on the CPU first.
37  // Note: This is not just an optimization; some color sources rely on
38  // CPU-applied color filters to behave properly.
39  bool needs_color_filter = paint.HasColorFilter();
40  if (needs_color_filter) {
41  auto color_filter = paint.GetColorFilter();
42  if (contents->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) {
43  needs_color_filter = false;
44  }
45  }
46 
47  bool can_apply_mask_filter = geometry->CanApplyMaskFilter();
48  contents->SetGeometry(std::move(geometry));
49 
50  if (can_apply_mask_filter && paint.mask_blur_descriptor.has_value()) {
51  // If there's a mask blur and we need to apply the color filter on the GPU,
52  // we need to be careful to only apply the color filter to the source
53  // colors. CreateMaskBlur is able to handle this case.
54  return paint.mask_blur_descriptor->CreateMaskBlur(
55  contents, needs_color_filter ? paint.GetColorFilter() : nullptr);
56  }
57 
58  std::shared_ptr<Contents> contents_copy = std::move(contents);
59  // Image input types will directly set their color filter,
60  // if any. See `TiledTextureContents.SetColorFilter`.
61  if (needs_color_filter &&
62  paint.color_source.GetType() != ColorSource::Type::kImage) {
63  std::shared_ptr<ColorFilter> color_filter = paint.GetColorFilter();
64  contents_copy = color_filter->WrapWithGPUColorFilter(
65  FilterInput::Make(std::move(contents_copy)),
67  }
68 
69  if (paint.image_filter) {
70  std::shared_ptr<FilterContents> filter = paint.image_filter->WrapInput(
71  FilterInput::Make(std::move(contents_copy)));
72  filter->SetRenderingMode(Entity::RenderingMode::kDirect);
73  return filter;
74  }
75 
76  return contents_copy;
77 }
78 
79 static std::shared_ptr<Contents> CreatePathContentsWithFilters(
80  const Paint& paint,
81  const Path& path) {
82  std::shared_ptr<Geometry> geometry;
83  switch (paint.style) {
85  geometry = Geometry::MakeFillPath(path);
86  break;
88  geometry =
89  Geometry::MakeStrokePath(path, paint.stroke_width, paint.stroke_miter,
90  paint.stroke_cap, paint.stroke_join);
91  break;
92  }
93 
94  return CreateContentsForGeometryWithFilters(paint, std::move(geometry));
95 }
96 
97 static std::shared_ptr<Contents> CreateCoverContentsWithFilters(
98  const Paint& paint) {
99  return CreateContentsForGeometryWithFilters(paint, Geometry::MakeCover());
100 }
101 
102 } // namespace
103 
105  Initialize(std::nullopt);
106 }
107 
108 Canvas::Canvas(Rect cull_rect) {
109  Initialize(cull_rect);
110 }
111 
112 Canvas::Canvas(IRect cull_rect) {
113  Initialize(Rect::MakeLTRB(cull_rect.GetLeft(), cull_rect.GetTop(),
114  cull_rect.GetRight(), cull_rect.GetBottom()));
115 }
116 
117 Canvas::~Canvas() = default;
118 
119 void Canvas::Initialize(std::optional<Rect> cull_rect) {
120  initial_cull_rect_ = cull_rect;
121  base_pass_ = std::make_unique<EntityPass>();
122  base_pass_->SetNewClipDepth(++current_depth_);
123  current_pass_ = base_pass_.get();
124  transform_stack_.emplace_back(CanvasStackEntry{.cull_rect = cull_rect});
125  FML_DCHECK(GetSaveCount() == 1u);
126  FML_DCHECK(base_pass_->GetSubpassesDepth() == 1u);
127 }
128 
129 void Canvas::Reset() {
130  base_pass_ = nullptr;
131  current_pass_ = nullptr;
132  current_depth_ = 0u;
133  transform_stack_ = {};
134 }
135 
136 void Canvas::Save() {
137  Save(false);
138 }
139 
140 namespace {
141 class MipCountVisitor : public ImageFilterVisitor {
142  public:
143  virtual void Visit(const BlurImageFilter& filter) {
144  required_mip_count_ = FilterContents::kBlurFilterRequiredMipCount;
145  }
146  virtual void Visit(const LocalMatrixImageFilter& filter) {
147  required_mip_count_ = 1;
148  }
149  virtual void Visit(const DilateImageFilter& filter) {
150  required_mip_count_ = 1;
151  }
152  virtual void Visit(const ErodeImageFilter& filter) {
153  required_mip_count_ = 1;
154  }
155  virtual void Visit(const MatrixImageFilter& filter) {
156  required_mip_count_ = 1;
157  }
158  virtual void Visit(const ComposeImageFilter& filter) {
159  required_mip_count_ = 1;
160  }
161  virtual void Visit(const ColorImageFilter& filter) {
162  required_mip_count_ = 1;
163  }
164  int32_t GetRequiredMipCount() const { return required_mip_count_; }
165 
166  private:
167  int32_t required_mip_count_ = -1;
168 };
169 } // namespace
170 
171 void Canvas::Save(bool create_subpass,
172  BlendMode blend_mode,
173  const std::shared_ptr<ImageFilter>& backdrop_filter) {
174  auto entry = CanvasStackEntry{};
175  entry.transform = transform_stack_.back().transform;
176  entry.cull_rect = transform_stack_.back().cull_rect;
177  entry.clip_depth = transform_stack_.back().clip_depth;
178  if (create_subpass) {
179  entry.rendering_mode = Entity::RenderingMode::kSubpass;
180  auto subpass = std::make_unique<EntityPass>();
181  subpass->SetEnableOffscreenCheckerboard(
183  if (backdrop_filter) {
184  EntityPass::BackdropFilterProc backdrop_filter_proc =
185  [backdrop_filter = backdrop_filter->Clone()](
186  const FilterInput::Ref& input, const Matrix& effect_transform,
187  Entity::RenderingMode rendering_mode) {
188  auto filter = backdrop_filter->WrapInput(input);
189  filter->SetEffectTransform(effect_transform);
190  filter->SetRenderingMode(rendering_mode);
191  return filter;
192  };
193  subpass->SetBackdropFilter(backdrop_filter_proc);
194  MipCountVisitor mip_count_visitor;
195  backdrop_filter->Visit(mip_count_visitor);
196  current_pass_->SetRequiredMipCount(
197  std::max(current_pass_->GetRequiredMipCount(),
198  mip_count_visitor.GetRequiredMipCount()));
199  }
200  subpass->SetBlendMode(blend_mode);
201  current_pass_ = GetCurrentPass().AddSubpass(std::move(subpass));
202  current_pass_->SetTransform(transform_stack_.back().transform);
203  current_pass_->SetClipDepth(transform_stack_.back().clip_depth);
204  }
205  transform_stack_.emplace_back(entry);
206 }
207 
209  FML_DCHECK(transform_stack_.size() > 0);
210  if (transform_stack_.size() == 1) {
211  return false;
212  }
213  size_t num_clips = transform_stack_.back().num_clips;
214  current_pass_->PopClips(num_clips, current_depth_);
215 
216  if (transform_stack_.back().rendering_mode ==
218  current_pass_->SetNewClipDepth(++current_depth_);
219  current_pass_ = GetCurrentPass().GetSuperpass();
220  FML_DCHECK(current_pass_);
221  }
222 
223  transform_stack_.pop_back();
224  if (num_clips > 0) {
225  RestoreClip();
226  }
227 
228  return true;
229 }
230 
231 void Canvas::Concat(const Matrix& transform) {
232  transform_stack_.back().transform = GetCurrentTransform() * transform;
233 }
234 
235 void Canvas::PreConcat(const Matrix& transform) {
236  transform_stack_.back().transform = transform * GetCurrentTransform();
237 }
238 
240  transform_stack_.back().transform = {};
241 }
242 
243 void Canvas::Transform(const Matrix& transform) {
244  Concat(transform);
245 }
246 
248  return transform_stack_.back().transform;
249 }
250 
251 const std::optional<Rect> Canvas::GetCurrentLocalCullingBounds() const {
252  auto cull_rect = transform_stack_.back().cull_rect;
253  if (cull_rect.has_value()) {
254  Matrix inverse = transform_stack_.back().transform.Invert();
255  cull_rect = cull_rect.value().TransformBounds(inverse);
256  }
257  return cull_rect;
258 }
259 
262 }
263 
266 }
267 
270 }
271 
272 void Canvas::Skew(Scalar sx, Scalar sy) {
273  Concat(Matrix::MakeSkew(sx, sy));
274 }
275 
276 void Canvas::Rotate(Radians radians) {
277  Concat(Matrix::MakeRotationZ(radians));
278 }
279 
280 size_t Canvas::GetSaveCount() const {
281  return transform_stack_.size();
282 }
283 
284 void Canvas::RestoreToCount(size_t count) {
285  while (GetSaveCount() > count) {
286  if (!Restore()) {
287  return;
288  }
289  }
290 }
291 
292 void Canvas::DrawPath(const Path& path, const Paint& paint) {
293  Entity entity;
295  entity.SetClipDepth(GetClipDepth());
296  entity.SetBlendMode(paint.blend_mode);
297  entity.SetContents(CreatePathContentsWithFilters(paint, path));
298 
299  AddEntityToCurrentPass(std::move(entity));
300 }
301 
302 void Canvas::DrawPaint(const Paint& paint) {
303  Entity entity;
305  entity.SetClipDepth(GetClipDepth());
306  entity.SetBlendMode(paint.blend_mode);
307  entity.SetContents(CreateCoverContentsWithFilters(paint));
308 
309  AddEntityToCurrentPass(std::move(entity));
310 }
311 
312 bool Canvas::AttemptDrawBlurredRRect(const Rect& rect,
313  Size corner_radii,
314  const Paint& paint) {
316  paint.style != Paint::Style::kFill) {
317  return false;
318  }
319 
320  if (!paint.mask_blur_descriptor.has_value()) {
321  return false;
322  }
323 
324  // A blur sigma that is not positive enough should not result in a blur.
325  if (paint.mask_blur_descriptor->sigma.sigma <= kEhCloseEnough) {
326  return false;
327  }
328 
329  // For symmetrically mask blurred solid RRects, absorb the mask blur and use
330  // a faster SDF approximation.
331 
332  Color rrect_color =
333  paint.HasColorFilter()
334  // Absorb the color filter, if any.
335  ? paint.GetColorFilter()->GetCPUColorFilterProc()(paint.color)
336  : paint.color;
337 
338  Paint rrect_paint = {.mask_blur_descriptor = paint.mask_blur_descriptor};
339 
340  // In some cases, we need to render the mask blur to a separate layer.
341  //
342  // 1. If the blur style is normal, we'll be drawing using one draw call and
343  // no clips. And so we can just wrap the RRect contents with the
344  // ImageFilter, which will get applied to the result as per usual.
345  //
346  // 2. If the blur style is solid, we combine the non-blurred RRect with the
347  // blurred RRect via two separate draw calls, and so we need to defer any
348  // fancy blending, translucency, or image filtering until after these two
349  // draws have been combined in a separate layer.
350  //
351  // 3. If the blur style is outer or inner, we apply the blur style via a
352  // clip. The ImageFilter needs to be applied to the mask blurred result.
353  // And so if there's an ImageFilter, we need to defer applying it until
354  // after the clipped RRect blur has been drawn to a separate texture.
355  // However, since there's only one draw call that produces color, we
356  // don't need to worry about the blend mode or translucency (unlike with
357  // BlurStyle::kSolid).
358  //
359  if ((paint.mask_blur_descriptor->style !=
361  paint.image_filter) ||
363  (!rrect_color.IsOpaque() ||
364  paint.blend_mode != BlendMode::kSourceOver))) {
365  // Defer the alpha, blend mode, and image filter to a separate layer.
366  SaveLayer({.color = Color::White().WithAlpha(rrect_color.alpha),
367  .blend_mode = paint.blend_mode,
368  .image_filter = paint.image_filter});
369  rrect_paint.color = rrect_color.WithAlpha(1);
370  } else {
371  rrect_paint.color = rrect_color;
372  rrect_paint.blend_mode = paint.blend_mode;
373  rrect_paint.image_filter = paint.image_filter;
374  Save();
375  }
376 
377  auto draw_blurred_rrect = [this, &rect, &corner_radii, &rrect_paint]() {
378  auto contents = std::make_shared<SolidRRectBlurContents>();
379 
380  contents->SetColor(rrect_paint.color);
381  contents->SetSigma(rrect_paint.mask_blur_descriptor->sigma);
382  contents->SetRRect(rect, corner_radii);
383 
384  Entity blurred_rrect_entity;
385  blurred_rrect_entity.SetTransform(GetCurrentTransform());
386  blurred_rrect_entity.SetClipDepth(GetClipDepth());
387  blurred_rrect_entity.SetBlendMode(rrect_paint.blend_mode);
388 
389  rrect_paint.mask_blur_descriptor = std::nullopt;
390  blurred_rrect_entity.SetContents(
391  rrect_paint.WithFilters(std::move(contents)));
392  AddEntityToCurrentPass(std::move(blurred_rrect_entity));
393  };
394 
395  switch (rrect_paint.mask_blur_descriptor->style) {
397  draw_blurred_rrect();
398  break;
399  }
401  // First, draw the blurred RRect.
402  draw_blurred_rrect();
403  // Then, draw the non-blurred RRect on top.
404  Entity entity;
405  entity.SetTransform(GetCurrentTransform());
406  entity.SetClipDepth(GetClipDepth());
407  entity.SetBlendMode(rrect_paint.blend_mode);
408  entity.SetContents(CreateContentsForGeometryWithFilters(
409  rrect_paint, Geometry::MakeRoundRect(rect, corner_radii)));
410  AddEntityToCurrentPass(std::move(entity));
411  break;
412  }
414  ClipRRect(rect, corner_radii, Entity::ClipOperation::kDifference);
415  draw_blurred_rrect();
416  break;
417  }
419  ClipRRect(rect, corner_radii, Entity::ClipOperation::kIntersect);
420  draw_blurred_rrect();
421  break;
422  }
423  }
424 
425  Restore();
426 
427  return true;
428 }
429 
430 void Canvas::DrawLine(const Point& p0, const Point& p1, const Paint& paint) {
431  Entity entity;
432  entity.SetTransform(GetCurrentTransform());
433  entity.SetClipDepth(GetClipDepth());
434  entity.SetBlendMode(paint.blend_mode);
435  entity.SetContents(CreateContentsForGeometryWithFilters(
436  paint, Geometry::MakeLine(p0, p1, paint.stroke_width, paint.stroke_cap)));
437 
438  AddEntityToCurrentPass(std::move(entity));
439 }
440 
441 void Canvas::DrawRect(const Rect& rect, const Paint& paint) {
442  if (paint.style == Paint::Style::kStroke) {
443  DrawPath(PathBuilder{}.AddRect(rect).TakePath(), paint);
444  return;
445  }
446 
447  if (AttemptDrawBlurredRRect(rect, {}, paint)) {
448  return;
449  }
450 
451  Entity entity;
452  entity.SetTransform(GetCurrentTransform());
453  entity.SetClipDepth(GetClipDepth());
454  entity.SetBlendMode(paint.blend_mode);
455  entity.SetContents(
456  CreateContentsForGeometryWithFilters(paint, Geometry::MakeRect(rect)));
457 
458  AddEntityToCurrentPass(std::move(entity));
459 }
460 
461 void Canvas::DrawOval(const Rect& rect, const Paint& paint) {
462  if (rect.IsSquare()) {
463  // Circles have slightly less overhead and can do stroking
464  DrawCircle(rect.GetCenter(), rect.GetWidth() * 0.5f, paint);
465  return;
466  }
467 
468  if (paint.style == Paint::Style::kStroke) {
469  // No stroked ellipses yet
470  DrawPath(PathBuilder{}.AddOval(rect).TakePath(), paint);
471  return;
472  }
473 
474  if (AttemptDrawBlurredRRect(rect, rect.GetSize() * 0.5f, paint)) {
475  return;
476  }
477 
478  Entity entity;
479  entity.SetTransform(GetCurrentTransform());
480  entity.SetClipDepth(GetClipDepth());
481  entity.SetBlendMode(paint.blend_mode);
482  entity.SetContents(
483  CreateContentsForGeometryWithFilters(paint, Geometry::MakeOval(rect)));
484 
485  AddEntityToCurrentPass(std::move(entity));
486 }
487 
488 void Canvas::DrawRRect(const Rect& rect,
489  const Size& corner_radii,
490  const Paint& paint) {
491  if (AttemptDrawBlurredRRect(rect, corner_radii, paint)) {
492  return;
493  }
494 
495  if (paint.style == Paint::Style::kFill) {
496  Entity entity;
497  entity.SetTransform(GetCurrentTransform());
498  entity.SetClipDepth(GetClipDepth());
499  entity.SetBlendMode(paint.blend_mode);
500  entity.SetContents(CreateContentsForGeometryWithFilters(
501  paint, Geometry::MakeRoundRect(rect, corner_radii)));
502 
503  AddEntityToCurrentPass(std::move(entity));
504  return;
505  }
506 
507  auto path = PathBuilder{}
508  .SetConvexity(Convexity::kConvex)
509  .AddRoundedRect(rect, corner_radii)
510  .SetBounds(rect)
511  .TakePath();
512  DrawPath(path, paint);
513 }
514 
515 void Canvas::DrawCircle(const Point& center,
516  Scalar radius,
517  const Paint& paint) {
518  Size half_size(radius, radius);
519  if (AttemptDrawBlurredRRect(
520  Rect::MakeOriginSize(center - half_size, half_size * 2),
521  {radius, radius}, paint)) {
522  return;
523  }
524 
525  Entity entity;
526  entity.SetTransform(GetCurrentTransform());
527  entity.SetClipDepth(GetClipDepth());
528  entity.SetBlendMode(paint.blend_mode);
529  auto geometry =
530  paint.style == Paint::Style::kStroke
531  ? Geometry::MakeStrokedCircle(center, radius, paint.stroke_width)
532  : Geometry::MakeCircle(center, radius);
533  entity.SetContents(
534  CreateContentsForGeometryWithFilters(paint, std::move(geometry)));
535 
536  AddEntityToCurrentPass(std::move(entity));
537 }
538 
539 void Canvas::ClipPath(const Path& path, Entity::ClipOperation clip_op) {
540  auto bounds = path.GetBoundingBox();
541  ClipGeometry(Geometry::MakeFillPath(path), clip_op);
542  if (clip_op == Entity::ClipOperation::kIntersect) {
543  if (bounds.has_value()) {
544  IntersectCulling(bounds.value());
545  }
546  }
547 }
548 
549 void Canvas::ClipRect(const Rect& rect, Entity::ClipOperation clip_op) {
550  auto geometry = Geometry::MakeRect(rect);
551  auto& cull_rect = transform_stack_.back().cull_rect;
552  if (clip_op == Entity::ClipOperation::kIntersect && //
553  cull_rect.has_value() && //
554  geometry->CoversArea(transform_stack_.back().transform, *cull_rect) //
555  ) {
556  return; // This clip will do nothing, so skip it.
557  }
558 
559  ClipGeometry(geometry, clip_op);
560  switch (clip_op) {
561  case Entity::ClipOperation::kIntersect:
562  IntersectCulling(rect);
563  break;
564  case Entity::ClipOperation::kDifference:
565  SubtractCulling(rect);
566  break;
567  }
568 }
569 
570 void Canvas::ClipOval(const Rect& bounds, Entity::ClipOperation clip_op) {
571  auto geometry = Geometry::MakeOval(bounds);
572  auto& cull_rect = transform_stack_.back().cull_rect;
573  if (clip_op == Entity::ClipOperation::kIntersect && //
574  cull_rect.has_value() && //
575  geometry->CoversArea(transform_stack_.back().transform, *cull_rect) //
576  ) {
577  return; // This clip will do nothing, so skip it.
578  }
579 
580  ClipGeometry(geometry, clip_op);
581  switch (clip_op) {
582  case Entity::ClipOperation::kIntersect:
583  IntersectCulling(bounds);
584  break;
585  case Entity::ClipOperation::kDifference:
586  break;
587  }
588 }
589 
590 void Canvas::ClipRRect(const Rect& rect,
591  const Size& corner_radii,
592  Entity::ClipOperation clip_op) {
593  // Does the rounded rect have a flat part on the top/bottom or left/right?
594  bool flat_on_TB = corner_radii.width * 2 < rect.GetWidth();
595  bool flat_on_LR = corner_radii.height * 2 < rect.GetHeight();
596  auto geometry = Geometry::MakeRoundRect(rect, corner_radii);
597  auto& cull_rect = transform_stack_.back().cull_rect;
598  if (clip_op == Entity::ClipOperation::kIntersect && //
599  cull_rect.has_value() && //
600  geometry->CoversArea(transform_stack_.back().transform, *cull_rect) //
601  ) {
602  return; // This clip will do nothing, so skip it.
603  }
604 
605  ClipGeometry(geometry, clip_op);
606  switch (clip_op) {
607  case Entity::ClipOperation::kIntersect:
608  IntersectCulling(rect);
609  break;
610  case Entity::ClipOperation::kDifference:
611  if (corner_radii.IsEmpty()) {
612  SubtractCulling(rect);
613  } else {
614  // We subtract the inner "tall" and "wide" rectangle pieces
615  // that fit inside the corners which cover the greatest area
616  // without involving the curved corners
617  // Since this is a subtract operation, we can subtract each
618  // rectangle piece individually without fear of interference.
619  if (flat_on_TB) {
620  SubtractCulling(rect.Expand(Size{-corner_radii.width, 0.0}));
621  }
622  if (flat_on_LR) {
623  SubtractCulling(rect.Expand(Size{0.0, -corner_radii.height}));
624  }
625  }
626  break;
627  }
628 }
629 
630 void Canvas::ClipGeometry(const std::shared_ptr<Geometry>& geometry,
631  Entity::ClipOperation clip_op) {
632  auto contents = std::make_shared<ClipContents>();
633  contents->SetGeometry(geometry);
634  contents->SetClipOperation(clip_op);
635 
636  Entity entity;
637  entity.SetTransform(GetCurrentTransform());
638  entity.SetContents(std::move(contents));
639  entity.SetClipDepth(GetClipDepth());
640 
641  GetCurrentPass().PushClip(std::move(entity));
642 
643  ++transform_stack_.back().clip_depth;
644  ++transform_stack_.back().num_clips;
645 }
646 
647 void Canvas::IntersectCulling(Rect clip_rect) {
648  clip_rect = clip_rect.TransformBounds(GetCurrentTransform());
649  std::optional<Rect>& cull_rect = transform_stack_.back().cull_rect;
650  if (cull_rect.has_value()) {
651  cull_rect = cull_rect
652  .value() //
653  .Intersection(clip_rect) //
654  .value_or(Rect{});
655  } else {
656  cull_rect = clip_rect;
657  }
658 }
659 
660 void Canvas::SubtractCulling(Rect clip_rect) {
661  std::optional<Rect>& cull_rect = transform_stack_.back().cull_rect;
662  if (cull_rect.has_value()) {
663  clip_rect = clip_rect.TransformBounds(GetCurrentTransform());
664  cull_rect = cull_rect
665  .value() //
666  .Cutout(clip_rect) //
667  .value_or(Rect{});
668  }
669  // else (no cull) diff (any clip) is non-rectangular
670 }
671 
672 void Canvas::RestoreClip() {
673  Entity entity;
674  entity.SetTransform(GetCurrentTransform());
675  // This path is empty because ClipRestoreContents just generates a quad that
676  // takes up the full render target.
677  entity.SetContents(std::make_shared<ClipRestoreContents>());
678  entity.SetClipDepth(GetClipDepth());
679 
680  AddEntityToCurrentPass(std::move(entity));
681 }
682 
683 void Canvas::DrawPoints(std::vector<Point> points,
684  Scalar radius,
685  const Paint& paint,
686  PointStyle point_style) {
687  if (radius <= 0) {
688  return;
689  }
690 
691  Entity entity;
692  entity.SetTransform(GetCurrentTransform());
693  entity.SetClipDepth(GetClipDepth());
694  entity.SetBlendMode(paint.blend_mode);
695  entity.SetContents(CreateContentsForGeometryWithFilters(
696  paint,
697  Geometry::MakePointField(std::move(points), radius,
698  /*round=*/point_style == PointStyle::kRound)));
699 
700  AddEntityToCurrentPass(std::move(entity));
701 }
702 
703 void Canvas::DrawImage(const std::shared_ptr<Image>& image,
704  Point offset,
705  const Paint& paint,
706  SamplerDescriptor sampler) {
707  if (!image) {
708  return;
709  }
710 
711  const auto source = Rect::MakeSize(image->GetSize());
712  const auto dest = source.Shift(offset);
713 
714  DrawImageRect(image, source, dest, paint, std::move(sampler));
715 }
716 
717 void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
718  Rect source,
719  Rect dest,
720  const Paint& paint,
721  SamplerDescriptor sampler,
722  SourceRectConstraint src_rect_constraint) {
723  if (!image || source.IsEmpty() || dest.IsEmpty()) {
724  return;
725  }
726 
727  auto size = image->GetSize();
728 
729  if (size.IsEmpty()) {
730  return;
731  }
732 
733  auto texture_contents = TextureContents::MakeRect(dest);
734  texture_contents->SetTexture(image->GetTexture());
735  texture_contents->SetSourceRect(source);
736  texture_contents->SetStrictSourceRect(src_rect_constraint ==
737  SourceRectConstraint::kStrict);
738  texture_contents->SetSamplerDescriptor(std::move(sampler));
739  texture_contents->SetOpacity(paint.color.alpha);
740  texture_contents->SetDeferApplyingOpacity(paint.HasColorFilter());
741 
742  std::shared_ptr<Contents> contents = texture_contents;
743  if (paint.mask_blur_descriptor.has_value()) {
744  contents = paint.mask_blur_descriptor->CreateMaskBlur(texture_contents);
745  }
746 
747  Entity entity;
748  entity.SetBlendMode(paint.blend_mode);
749  entity.SetClipDepth(GetClipDepth());
750  entity.SetContents(paint.WithFilters(contents));
751  entity.SetTransform(GetCurrentTransform());
752 
753  AddEntityToCurrentPass(std::move(entity));
754 }
755 
756 Picture Canvas::EndRecordingAsPicture() {
757  // Assign clip depths to any outstanding clip entities.
758  while (current_pass_ != nullptr) {
759  current_pass_->PopAllClips(current_depth_);
760  current_pass_ = current_pass_->GetSuperpass();
761  }
762 
763  Picture picture;
764  picture.pass = std::move(base_pass_);
765 
766  Reset();
767  Initialize(initial_cull_rect_);
768 
769  return picture;
770 }
771 
772 EntityPass& Canvas::GetCurrentPass() {
773  FML_DCHECK(current_pass_ != nullptr);
774  return *current_pass_;
775 }
776 
777 size_t Canvas::GetClipDepth() const {
778  return transform_stack_.back().clip_depth;
779 }
780 
781 void Canvas::AddEntityToCurrentPass(Entity entity) {
782  entity.SetNewClipDepth(++current_depth_);
783  GetCurrentPass().AddEntity(std::move(entity));
784 }
785 
786 void Canvas::SaveLayer(const Paint& paint,
787  std::optional<Rect> bounds,
788  const std::shared_ptr<ImageFilter>& backdrop_filter,
789  ContentBoundsPromise bounds_promise) {
790  TRACE_EVENT0("flutter", "Canvas::saveLayer");
791  Save(true, paint.blend_mode, backdrop_filter);
792 
793  // The DisplayList bounds/rtree doesn't account for filters applied to parent
794  // layers, and so sub-DisplayLists are getting culled as if no filters are
795  // applied.
796  // See also: https://github.com/flutter/flutter/issues/139294
797  if (paint.image_filter) {
798  transform_stack_.back().cull_rect = std::nullopt;
799  }
800 
801  auto& new_layer_pass = GetCurrentPass();
802  if (bounds) {
803  new_layer_pass.SetBoundsLimit(bounds, bounds_promise);
804  }
805 
806  if (paint.image_filter) {
807  MipCountVisitor mip_count_visitor;
808  paint.image_filter->Visit(mip_count_visitor);
809  new_layer_pass.SetRequiredMipCount(mip_count_visitor.GetRequiredMipCount());
810  }
811 
812  // Only apply opacity peephole on default blending.
813  if (paint.blend_mode == BlendMode::kSourceOver) {
814  new_layer_pass.SetDelegate(
815  std::make_shared<OpacityPeepholePassDelegate>(paint));
816  } else {
817  new_layer_pass.SetDelegate(std::make_shared<PaintPassDelegate>(paint));
818  }
819 }
820 
821 void Canvas::DrawTextFrame(const std::shared_ptr<TextFrame>& text_frame,
822  Point position,
823  const Paint& paint) {
824  Entity entity;
825  entity.SetClipDepth(GetClipDepth());
826  entity.SetBlendMode(paint.blend_mode);
827 
828  auto text_contents = std::make_shared<TextContents>();
829  text_contents->SetTextFrame(text_frame);
830  text_contents->SetColor(paint.color);
831  text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
832 
833  entity.SetTransform(GetCurrentTransform() *
834  Matrix::MakeTranslation(position));
835 
836  // TODO(bdero): This mask blur application is a hack. It will always wind up
837  // doing a gaussian blur that affects the color source itself
838  // instead of just the mask. The color filter text support
839  // needs to be reworked in order to interact correctly with
840  // mask filters.
841  // https://github.com/flutter/flutter/issues/133297
842  entity.SetContents(
843  paint.WithFilters(paint.WithMaskBlur(std::move(text_contents), true)));
844 
845  AddEntityToCurrentPass(std::move(entity));
846 }
847 
849  const std::shared_ptr<VerticesGeometry>& vertices,
850  const Paint& paint) {
851  // If there are no vertex color or texture coordinates. Or if there
852  // are vertex coordinates then only if the contents are an image or
853  // a solid color.
854  if (vertices->HasVertexColors()) {
855  return false;
856  }
857  if (vertices->HasTextureCoordinates() &&
858  (paint.color_source.GetType() == ColorSource::Type::kImage ||
860  return true;
861  }
862  return !vertices->HasTextureCoordinates();
863 }
864 
865 void Canvas::DrawVertices(const std::shared_ptr<VerticesGeometry>& vertices,
866  BlendMode blend_mode,
867  const Paint& paint) {
868  // Override the blend mode with kDestination in order to match the behavior
869  // of Skia's SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER flag, which
870  // is enabled when the Flutter engine builds Skia.
872  blend_mode = BlendMode::kDestination;
873  }
874 
875  Entity entity;
876  entity.SetTransform(GetCurrentTransform());
877  entity.SetClipDepth(GetClipDepth());
878  entity.SetBlendMode(paint.blend_mode);
879 
880  // If there are no vertex color or texture coordinates. Or if there
881  // are vertex coordinates then only if the contents are an image.
882  if (UseColorSourceContents(vertices, paint)) {
883  entity.SetContents(CreateContentsForGeometryWithFilters(paint, vertices));
884  AddEntityToCurrentPass(std::move(entity));
885  return;
886  }
887 
888  auto src_paint = paint;
889  src_paint.color = paint.color.WithAlpha(1.0);
890 
891  std::shared_ptr<Contents> src_contents =
892  src_paint.CreateContentsForGeometry(vertices);
893  if (vertices->HasTextureCoordinates()) {
894  // If the color source has an intrinsic size, then we use that to
895  // create the src contents as a simplification. Otherwise we use
896  // the extent of the texture coordinates to determine how large
897  // the src contents should be. If neither has a value we fall back
898  // to using the geometry coverage data.
899  Rect src_coverage;
900  auto size = src_contents->GetColorSourceSize();
901  if (size.has_value()) {
902  src_coverage = Rect::MakeXYWH(0, 0, size->width, size->height);
903  } else {
904  auto cvg = vertices->GetCoverage(Matrix{});
905  FML_CHECK(cvg.has_value());
906  src_coverage =
907  // Covered by FML_CHECK.
908  // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
909  vertices->GetTextureCoordinateCoverge().value_or(cvg.value());
910  }
911  src_contents =
912  src_paint.CreateContentsForGeometry(Geometry::MakeRect(src_coverage));
913  }
914 
915  auto contents = std::make_shared<VerticesContents>();
916  contents->SetAlpha(paint.color.alpha);
917  contents->SetBlendMode(blend_mode);
918  contents->SetGeometry(vertices);
919  contents->SetSourceContents(std::move(src_contents));
920  entity.SetContents(paint.WithFilters(std::move(contents)));
921 
922  AddEntityToCurrentPass(std::move(entity));
923 }
924 
925 void Canvas::DrawAtlas(const std::shared_ptr<Image>& atlas,
926  std::vector<Matrix> transforms,
927  std::vector<Rect> texture_coordinates,
928  std::vector<Color> colors,
929  BlendMode blend_mode,
930  SamplerDescriptor sampler,
931  std::optional<Rect> cull_rect,
932  const Paint& paint) {
933  if (!atlas) {
934  return;
935  }
936 
937  std::shared_ptr<AtlasContents> contents = std::make_shared<AtlasContents>();
938  contents->SetColors(std::move(colors));
939  contents->SetTransforms(std::move(transforms));
940  contents->SetTextureCoordinates(std::move(texture_coordinates));
941  contents->SetTexture(atlas->GetTexture());
942  contents->SetSamplerDescriptor(std::move(sampler));
943  contents->SetBlendMode(blend_mode);
944  contents->SetCullRect(cull_rect);
945  contents->SetAlpha(paint.color.alpha);
946 
947  Entity entity;
948  entity.SetTransform(GetCurrentTransform());
949  entity.SetClipDepth(GetClipDepth());
950  entity.SetBlendMode(paint.blend_mode);
951  entity.SetContents(paint.WithFilters(contents));
952 
953  AddEntityToCurrentPass(std::move(entity));
954 }
955 
956 } // namespace impeller
text_contents.h
impeller::Matrix::MakeSkew
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:117
impeller::Paint::stroke_cap
Cap stroke_cap
Definition: paint.h:60
impeller::Entity::ClipOperation::kIntersect
@ kIntersect
impeller::Entity::SetClipDepth
void SetClipDepth(uint32_t clip_depth)
Definition: entity.cc:101
impeller::Picture::pass
std::unique_ptr< EntityPass > pass
Definition: picture.h:21
impeller::Paint::WithMaskBlur
std::shared_ptr< Contents > WithMaskBlur(std::shared_ptr< Contents > input, bool is_solid_color) const
Definition: paint.cc:79
impeller::EntityPass::BackdropFilterProc
std::function< std::shared_ptr< FilterContents >(FilterInput::Ref, const Matrix &effect_transform, Entity::RenderingMode rendering_mode)> BackdropFilterProc
Definition: entity_pass.h:61
impeller::Entity::ClipOperation::kDifference
@ kDifference
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Canvas::RestoreToCount
void RestoreToCount(size_t count)
Definition: canvas.cc:284
image_filter.h
impeller::Entity::SetBlendMode
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:130
texture_contents.h
impeller::BlurImageFilter
Definition: image_filter.h:96
impeller::Geometry::MakeStrokePath
static std::shared_ptr< Geometry > MakeStrokePath(const Path &path, Scalar stroke_width=0.0, Scalar miter_limit=4.0, Cap stroke_cap=Cap::kButt, Join stroke_join=Join::kMiter)
Definition: geometry.cc:187
impeller::Geometry::MakeRoundRect
static std::shared_ptr< Geometry > MakeRoundRect(const Rect &rect, const Size &radii)
Definition: geometry.cc:230
impeller::Paint::Style::kStroke
@ kStroke
impeller::CanvasStackEntry
Definition: canvas.h:31
impeller::Paint
Definition: paint.h:23
impeller::EntityPass::SetNewClipDepth
void SetNewClipDepth(size_t clip_depth)
Definition: entity_pass.cc:1170
impeller::CanvasStackEntry::cull_rect
std::optional< Rect > cull_rect
Definition: canvas.h:34
impeller::Canvas::Skew
void Skew(Scalar sx, Scalar sy)
Definition: canvas.cc:272
impeller::BlendMode
BlendMode
Definition: color.h:59
impeller::PathBuilder::SetBounds
PathBuilder & SetBounds(Rect bounds)
Set the bounding box that will be used by Path.GetBoundingBox in place of performing the computation.
Definition: path_builder.cc:453
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
impeller::FilterInput::Make
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
Definition: filter_input.cc:19
impeller::PointStyle
PointStyle
Definition: canvas.h:41
impeller::Paint::color
Color color
Definition: paint.h:55
paint_pass_delegate.h
impeller::EntityPass::AddSubpass
EntityPass * AddSubpass(std::unique_ptr< EntityPass > pass)
Appends a given pass as a subpass.
Definition: entity_pass.cc:272
impeller::PathBuilder
Definition: path_builder.h:14
impeller::FilterInput::Ref
std::shared_ptr< FilterInput > Ref
Definition: filter_input.h:32
impeller::Canvas::ResetTransform
void ResetTransform()
Definition: canvas.cc:239
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:339
impeller::Color::alpha
Scalar alpha
Definition: color.h:143
impeller::ContentBoundsPromise
ContentBoundsPromise
Definition: entity_pass.h:28
impeller::Canvas::DebugOptions::offscreen_texture_checkerboard
bool offscreen_texture_checkerboard
Definition: canvas.h:65
impeller::PathBuilder::AddRoundedRect
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
Definition: path_builder.cc:145
impeller::TRect::GetCenter
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:350
impeller::Canvas::debug_options
struct impeller::Canvas::DebugOptions debug_options
impeller::FilterContents::BlurStyle::kNormal
@ kNormal
Blurred inside and outside.
impeller::PathBuilder::SetConvexity
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:80
impeller::TRect::GetHeight
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:314
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Paint::color_source
ColorSource color_source
Definition: paint.h:56
impeller::Canvas::GetCurrentTransform
const Matrix & GetCurrentTransform() const
Definition: canvas.cc:247
impeller::EntityPass::SetClipDepth
void SetClipDepth(size_t clip_depth)
Definition: entity_pass.cc:1162
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:112
impeller::Canvas::Concat
void Concat(const Matrix &transform)
Definition: canvas.cc:231
impeller::UseColorSourceContents
static bool UseColorSourceContents(const std::shared_ptr< VerticesGeometry > &vertices, const Paint &paint)
Definition: canvas.cc:848
impeller::TRect::IsEmpty
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: rect.h:264
impeller::Entity::SetContents
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:93
vertices_contents.h
path_builder.h
impeller::SamplerDescriptor
Definition: sampler_descriptor.h:15
impeller::kColor
@ kColor
Definition: geometry.h:51
impeller::Entity
Definition: entity.h:21
impeller::Picture
Definition: picture.h:20
impeller::Geometry::MakeFillPath
static std::shared_ptr< Geometry > MakeFillPath(const Path &path, std::optional< Rect > inner_rect=std::nullopt)
Definition: geometry.cc:175
impeller::TSize
Definition: size.h:19
filter_contents.h
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:318
impeller::Canvas::Scale
void Scale(const Vector2 &scale)
Definition: canvas.cc:264
impeller::EntityPass::GetSuperpass
EntityPass * GetSuperpass() const
Definition: entity_pass.cc:268
color_source_contents.h
impeller::FilterContents::BlurStyle::kSolid
@ kSolid
Solid inside, blurred outside.
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:51
impeller::Canvas::Save
void Save()
Definition: canvas.cc:136
impeller::EntityPass
Definition: entity_pass.h:43
impeller::Paint::GetColorFilter
std::shared_ptr< ColorFilter > GetColorFilter() const
Definition: paint.cc:213
impeller::Paint::style
Style style
Definition: paint.h:63
impeller::TRect::GetWidth
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:308
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:270
impeller::Paint::Style::kFill
@ kFill
impeller::EntityPass::GetRequiredMipCount
int32_t GetRequiredMipCount() const
Definition: entity_pass.h:186
impeller::TRect::IsSquare
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
Definition: rect.h:271
geometry.h
impeller::Color::White
static constexpr Color White()
Definition: color.h:256
impeller::SourceRectConstraint
SourceRectConstraint
Controls the behavior of the source rectangle given to DrawImageRect.
Definition: canvas.h:50
impeller::Canvas::Restore
bool Restore()
Definition: canvas.cc:208
impeller::Radians
Definition: scalar.h:38
impeller::Entity::RenderingMode::kDirect
@ kDirect
canvas.h
clip_contents.h
impeller::Canvas::DrawPath
void DrawPath(const Path &path, const Paint &paint)
Definition: canvas.cc:292
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:22
impeller::Canvas::DrawPaint
void DrawPaint(const Paint &paint)
Definition: canvas.cc:302
impeller::EntityPass::SetTransform
void SetTransform(Matrix transform)
Definition: entity_pass.cc:1158
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:661
impeller::Canvas::GetSaveCount
size_t GetSaveCount() const
Definition: canvas.cc:280
impeller::FilterContents::BlurStyle::kInner
@ kInner
Blurred inside, nothing outside.
impeller::FilterContents::kBlurFilterRequiredMipCount
static const int32_t kBlurFilterRequiredMipCount
Definition: filter_contents.h:24
impeller::Canvas::Canvas
Canvas()
Definition: canvas.cc:104
impeller::TSize::width
Type width
Definition: size.h:22
impeller::Entity::RenderingMode::kSubpass
@ kSubpass
impeller::Canvas::PreConcat
void PreConcat(const Matrix &transform)
Definition: canvas.cc:235
atlas_contents.h
impeller::ColorSource::Type::kImage
@ kImage
impeller::FilterContents::BlurStyle::kOuter
@ kOuter
Nothing inside, blurred outside.
impeller::Entity::SetTransform
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:65
impeller::TRect::GetSize
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:294
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:322
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:209
impeller::Canvas::Rotate
void Rotate(Radians radians)
Definition: canvas.cc:276
constants.h
impeller::EntityPass::PopClips
void PopClips(size_t num_clips, uint64_t depth)
Definition: entity_pass.cc:123
impeller::TPoint< Scalar >
impeller::Canvas::Transform
void Transform(const Matrix &transform)
Definition: canvas.cc:243
impeller::Canvas::GetCurrentLocalCullingBounds
const std::optional< Rect > GetCurrentLocalCullingBounds() const
Definition: canvas.cc:251
scale
const Scalar scale
Definition: stroke_path_geometry.cc:297
impeller::Entity::ClipOperation
ClipOperation
Definition: entity.h:61
impeller::TRect::GetBottom
constexpr auto GetBottom() const
Definition: rect.h:324
impeller::Paint::HasColorFilter
bool HasColorFilter() const
Whether this paint has a color filter that can apply opacity.
Definition: paint.cc:227
impeller::TSize::height
Type height
Definition: size.h:23
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::PathBuilder::AddOval
PathBuilder & AddOval(const Rect &rect)
Definition: path_builder.cc:371
impeller::Entity::RenderingMode
RenderingMode
Definition: entity.h:28
impeller::TSize::IsEmpty
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: size.h:105
offset
Point offset
Definition: stroke_path_geometry.cc:300
impeller::ColorFilterContents::AbsorbOpacity::kYes
@ kYes
impeller::ImageFilterVisitor
Definition: image_filter.h:27
impeller
Definition: aiks_blur_unittests.cc:20
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::Paint::mask_blur_descriptor
std::optional< MaskBlurDescriptor > mask_blur_descriptor
Definition: paint.h:69
solid_rrect_blur_contents.h
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:320
impeller::Paint::stroke_width
Scalar stroke_width
Definition: paint.h:59
impeller::ColorSource::Type::kColor
@ kColor
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:56
impeller::EntityPass::SetRequiredMipCount
void SetRequiredMipCount(int32_t mip_count)
Definition: entity_pass.h:188
impeller::ColorSource::GetType
Type GetType() const
Definition: color_source.cc:230
impeller::TRect< Scalar >
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Canvas::~Canvas
~Canvas()
impeller::Vector3
Definition: vector.h:20
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Paint::blend_mode
BlendMode blend_mode
Definition: paint.h:64
impeller::TRect::Expand
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition: rect.h:547
impeller::Geometry::MakeCover
static std::shared_ptr< Geometry > MakeCover()
Definition: geometry.cc:200
impeller::Paint::image_filter
std::shared_ptr< ImageFilter > image_filter
Definition: paint.h:67
impeller::Canvas::Translate
void Translate(const Vector3 &offset)
Definition: canvas.cc:260