Flutter Engine
The Flutter Engine
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
6
7#include <memory>
8#include <optional>
9#include <utility>
10
11#include "flutter/fml/logging.h"
12#include "flutter/fml/trace_event.h"
29
30namespace impeller {
31
32namespace {
33
34static std::shared_ptr<Contents> CreateContentsForGeometryWithFilters(
35 const Paint& paint,
36 std::shared_ptr<Geometry> geometry) {
37 std::shared_ptr<ColorSourceContents> contents =
39
40 // Attempt to apply the color filter on the CPU first.
41 // Note: This is not just an optimization; some color sources rely on
42 // CPU-applied color filters to behave properly.
43 bool needs_color_filter = paint.HasColorFilter();
44 if (needs_color_filter) {
46 if (contents->ApplyColorFilter(color_filter->GetCPUColorFilterProc())) {
47 needs_color_filter = false;
48 }
49 }
50
51 bool can_apply_mask_filter = geometry->CanApplyMaskFilter();
52 contents->SetGeometry(std::move(geometry));
53
54 if (can_apply_mask_filter && paint.mask_blur_descriptor.has_value()) {
55 // If there's a mask blur and we need to apply the color filter on the GPU,
56 // we need to be careful to only apply the color filter to the source
57 // colors. CreateMaskBlur is able to handle this case.
58 return paint.mask_blur_descriptor->CreateMaskBlur(
59 contents, needs_color_filter ? paint.GetColorFilter() : nullptr);
60 }
61
62 std::shared_ptr<Contents> contents_copy = std::move(contents);
63 // Image input types will directly set their color filter,
64 // if any. See `TiledTextureContents.SetColorFilter`.
65 if (needs_color_filter &&
67 std::shared_ptr<ColorFilter> color_filter = paint.GetColorFilter();
68 contents_copy = color_filter->WrapWithGPUColorFilter(
69 FilterInput::Make(std::move(contents_copy)),
71 }
72
73 if (paint.image_filter) {
74 std::shared_ptr<FilterContents> filter = paint.image_filter->WrapInput(
75 FilterInput::Make(std::move(contents_copy)));
76 filter->SetRenderingMode(Entity::RenderingMode::kDirect);
77 return filter;
78 }
79
80 return contents_copy;
81}
82
83struct GetTextureColorSourceDataVisitor {
84 GetTextureColorSourceDataVisitor() {}
85
86 std::optional<ImageData> operator()(const LinearGradientData& data) {
87 return std::nullopt;
88 }
89
90 std::optional<ImageData> operator()(const RadialGradientData& data) {
91 return std::nullopt;
92 }
93
94 std::optional<ImageData> operator()(const ConicalGradientData& data) {
95 return std::nullopt;
96 }
97
98 std::optional<ImageData> operator()(const SweepGradientData& data) {
99 return std::nullopt;
100 }
101
102 std::optional<ImageData> operator()(const ImageData& data) { return data; }
103
104 std::optional<ImageData> operator()(const RuntimeEffectData& data) {
105 return std::nullopt;
106 }
107
108 std::optional<ImageData> operator()(const std::monostate& data) {
109 return std::nullopt;
110 }
111
112#if IMPELLER_ENABLE_3D
113 std::optional<ImageData> operator()(const SceneData& data) {
114 return std::nullopt;
115 }
116#endif // IMPELLER_ENABLE_3D
117};
118
119static std::optional<ImageData> GetImageColorSourceData(
120 const ColorSource& color_source) {
121 return std::visit(GetTextureColorSourceDataVisitor{}, color_source.GetData());
122}
123
124static std::shared_ptr<Contents> CreatePathContentsWithFilters(
125 const Paint& paint,
126 const Path& path) {
127 std::shared_ptr<Geometry> geometry;
128 switch (paint.style) {
130 geometry = Geometry::MakeFillPath(path);
131 break;
133 geometry =
136 break;
137 }
138
139 return CreateContentsForGeometryWithFilters(paint, std::move(geometry));
140}
141
142static std::shared_ptr<Contents> CreateCoverContentsWithFilters(
143 const Paint& paint) {
144 return CreateContentsForGeometryWithFilters(paint, Geometry::MakeCover());
145}
146
147} // namespace
148
150 Initialize(std::nullopt);
151}
152
154 Initialize(cull_rect);
155}
156
158 Initialize(Rect::MakeLTRB(cull_rect.GetLeft(), cull_rect.GetTop(),
159 cull_rect.GetRight(), cull_rect.GetBottom()));
160}
161
162Canvas::~Canvas() = default;
163
164void Canvas::Initialize(std::optional<Rect> cull_rect) {
165 initial_cull_rect_ = cull_rect;
166 base_pass_ = std::make_unique<EntityPass>();
167 base_pass_->SetClipDepth(++current_depth_);
168 current_pass_ = base_pass_.get();
170 .cull_rect = cull_rect,
171 .clip_depth = kMaxDepth,
172 });
173 FML_DCHECK(GetSaveCount() == 1u);
174 FML_DCHECK(base_pass_->GetSubpassesDepth() == 1u);
175}
176
178 base_pass_ = nullptr;
179 current_pass_ = nullptr;
180 current_depth_ = 0u;
181 transform_stack_ = {};
182}
183
184void Canvas::Save(uint32_t total_content_depth) {
185 Save(false, total_content_depth);
186}
187
188namespace {
189class MipCountVisitor : public ImageFilterVisitor {
190 public:
191 virtual void Visit(const BlurImageFilter& filter) {
193 }
194 virtual void Visit(const LocalMatrixImageFilter& filter) {
195 required_mip_count_ = 1;
196 }
197 virtual void Visit(const DilateImageFilter& filter) {
198 required_mip_count_ = 1;
199 }
200 virtual void Visit(const ErodeImageFilter& filter) {
201 required_mip_count_ = 1;
202 }
203 virtual void Visit(const MatrixImageFilter& filter) {
204 required_mip_count_ = 1;
205 }
206 virtual void Visit(const ComposeImageFilter& filter) {
207 required_mip_count_ = 1;
208 }
209 virtual void Visit(const ColorImageFilter& filter) {
210 required_mip_count_ = 1;
211 }
212 int32_t GetRequiredMipCount() const { return required_mip_count_; }
213
214 private:
215 int32_t required_mip_count_ = -1;
216};
217} // namespace
218
219void Canvas::Save(bool create_subpass,
220 uint32_t total_content_depth,
221 BlendMode blend_mode,
222 const std::shared_ptr<ImageFilter>& backdrop_filter) {
223 auto entry = CanvasStackEntry{};
224 entry.transform = transform_stack_.back().transform;
225 entry.cull_rect = transform_stack_.back().cull_rect;
226 entry.clip_height = transform_stack_.back().clip_height;
227 entry.distributed_opacity = transform_stack_.back().distributed_opacity;
228 if (create_subpass) {
229 entry.rendering_mode =
231 auto subpass = std::make_unique<EntityPass>();
232 if (backdrop_filter) {
233 EntityPass::BackdropFilterProc backdrop_filter_proc =
234 [backdrop_filter = backdrop_filter->Clone()](
235 const FilterInput::Ref& input, const Matrix& effect_transform,
236 Entity::RenderingMode rendering_mode) {
237 auto filter = backdrop_filter->WrapInput(input);
238 filter->SetEffectTransform(effect_transform);
239 filter->SetRenderingMode(rendering_mode);
240 return filter;
241 };
242 subpass->SetBackdropFilter(backdrop_filter_proc);
243 MipCountVisitor mip_count_visitor;
244 backdrop_filter->Visit(mip_count_visitor);
245 current_pass_->SetRequiredMipCount(
246 std::max(current_pass_->GetRequiredMipCount(),
247 mip_count_visitor.GetRequiredMipCount()));
248 }
249 subpass->SetBlendMode(blend_mode);
250 current_pass_ = GetCurrentPass().AddSubpass(std::move(subpass));
251 current_pass_->SetTransform(transform_stack_.back().transform);
252 current_pass_->SetClipHeight(transform_stack_.back().clip_height);
253 }
254 transform_stack_.emplace_back(entry);
255}
256
258 FML_DCHECK(transform_stack_.size() > 0);
259 if (transform_stack_.size() == 1) {
260 return false;
261 }
262 size_t num_clips = transform_stack_.back().num_clips;
263 current_pass_->PopClips(num_clips, current_depth_);
264
265 if (transform_stack_.back().rendering_mode ==
267 transform_stack_.back().rendering_mode ==
269 current_pass_->SetClipDepth(++current_depth_);
270 current_pass_ = GetCurrentPass().GetSuperpass();
271 FML_DCHECK(current_pass_);
272 }
273
274 transform_stack_.pop_back();
275 if (num_clips > 0) {
276 RestoreClip();
277 }
278
279 return true;
280}
281
283 transform_stack_.back().transform = GetCurrentTransform() * transform;
284}
285
287 transform_stack_.back().transform = transform * GetCurrentTransform();
288}
289
291 transform_stack_.back().transform = {};
292}
293
296}
297
299 return transform_stack_.back().transform;
300}
301
302const std::optional<Rect> Canvas::GetCurrentLocalCullingBounds() const {
303 auto cull_rect = transform_stack_.back().cull_rect;
304 if (cull_rect.has_value()) {
305 Matrix inverse = transform_stack_.back().transform.Invert();
306 cull_rect = cull_rect.value().TransformBounds(inverse);
307 }
308 return cull_rect;
309}
310
313}
314
317}
318
321}
322
324 Concat(Matrix::MakeSkew(sx, sy));
325}
326
327void Canvas::Rotate(Radians radians) {
329}
330
331size_t Canvas::GetSaveCount() const {
332 return transform_stack_.size();
333}
334
336 while (GetSaveCount() > count) {
337 if (!Restore()) {
338 return;
339 }
340 }
341}
342
343void Canvas::DrawPath(const Path& path, const Paint& paint) {
344 Entity entity;
346 entity.SetBlendMode(paint.blend_mode);
347 entity.SetContents(CreatePathContentsWithFilters(paint, path));
348
349 AddRenderEntityToCurrentPass(std::move(entity));
350}
351
353 Entity entity;
355 entity.SetBlendMode(paint.blend_mode);
356 entity.SetContents(CreateCoverContentsWithFilters(paint));
357
358 AddRenderEntityToCurrentPass(std::move(entity));
359}
360
361bool Canvas::AttemptDrawBlurredRRect(const Rect& rect,
362 Size corner_radii,
363 const Paint& paint) {
364 if (paint.color_source.GetType() != ColorSource::Type::kColor ||
365 paint.style != Paint::Style::kFill) {
366 return false;
367 }
368
369 if (!paint.mask_blur_descriptor.has_value()) {
370 return false;
371 }
372
373 // A blur sigma that is not positive enough should not result in a blur.
374 if (paint.mask_blur_descriptor->sigma.sigma <= kEhCloseEnough) {
375 return false;
376 }
377
378 // For symmetrically mask blurred solid RRects, absorb the mask blur and use
379 // a faster SDF approximation.
380
381 Color rrect_color =
382 paint.HasColorFilter()
383 // Absorb the color filter, if any.
384 ? paint.GetColorFilter()->GetCPUColorFilterProc()(paint.color)
385 : paint.color;
386
387 Paint rrect_paint = {.mask_blur_descriptor = paint.mask_blur_descriptor};
388
389 // In some cases, we need to render the mask blur to a separate layer.
390 //
391 // 1. If the blur style is normal, we'll be drawing using one draw call and
392 // no clips. And so we can just wrap the RRect contents with the
393 // ImageFilter, which will get applied to the result as per usual.
394 //
395 // 2. If the blur style is solid, we combine the non-blurred RRect with the
396 // blurred RRect via two separate draw calls, and so we need to defer any
397 // fancy blending, translucency, or image filtering until after these two
398 // draws have been combined in a separate layer.
399 //
400 // 3. If the blur style is outer or inner, we apply the blur style via a
401 // clip. The ImageFilter needs to be applied to the mask blurred result.
402 // And so if there's an ImageFilter, we need to defer applying it until
403 // after the clipped RRect blur has been drawn to a separate texture.
404 // However, since there's only one draw call that produces color, we
405 // don't need to worry about the blend mode or translucency (unlike with
406 // BlurStyle::kSolid).
407 //
408 if ((paint.mask_blur_descriptor->style !=
412 (!rrect_color.IsOpaque() ||
414 Rect render_bounds = rect;
415 if (paint.mask_blur_descriptor->style !=
417 render_bounds =
418 render_bounds.Expand(paint.mask_blur_descriptor->sigma.sigma * 4.0);
419 }
420 // Defer the alpha, blend mode, and image filter to a separate layer.
421 SaveLayer({.color = Color::White().WithAlpha(rrect_color.alpha),
422 .blend_mode = paint.blend_mode,
423 .image_filter = paint.image_filter},
424 render_bounds, nullptr, ContentBoundsPromise::kContainsContents,
425 1u);
426 rrect_paint.color = rrect_color.WithAlpha(1);
427 } else {
428 rrect_paint.color = rrect_color;
429 rrect_paint.blend_mode = paint.blend_mode;
430 rrect_paint.image_filter = paint.image_filter;
431 Save(1u);
432 }
433
434 auto draw_blurred_rrect = [this, &rect, &corner_radii, &rrect_paint]() {
435 auto contents = std::make_shared<SolidRRectBlurContents>();
436
437 contents->SetColor(rrect_paint.color);
438 contents->SetSigma(rrect_paint.mask_blur_descriptor->sigma);
439 contents->SetRRect(rect, corner_radii);
440
441 Entity blurred_rrect_entity;
442 blurred_rrect_entity.SetTransform(GetCurrentTransform());
443 blurred_rrect_entity.SetBlendMode(rrect_paint.blend_mode);
444
445 rrect_paint.mask_blur_descriptor = std::nullopt;
446 blurred_rrect_entity.SetContents(
447 rrect_paint.WithFilters(std::move(contents)));
448 AddRenderEntityToCurrentPass(std::move(blurred_rrect_entity));
449 };
450
451 switch (rrect_paint.mask_blur_descriptor->style) {
453 draw_blurred_rrect();
454 break;
455 }
457 // First, draw the blurred RRect.
458 draw_blurred_rrect();
459 // Then, draw the non-blurred RRect on top.
460 Entity entity;
461 entity.SetTransform(GetCurrentTransform());
462 entity.SetBlendMode(rrect_paint.blend_mode);
463 entity.SetContents(CreateContentsForGeometryWithFilters(
464 rrect_paint, Geometry::MakeRoundRect(rect, corner_radii)));
465 AddRenderEntityToCurrentPass(std::move(entity), true);
466 break;
467 }
469 ClipRRect(rect, corner_radii, Entity::ClipOperation::kDifference);
470 draw_blurred_rrect();
471 break;
472 }
474 ClipRRect(rect, corner_radii, Entity::ClipOperation::kIntersect);
475 draw_blurred_rrect();
476 break;
477 }
478 }
479
480 Restore();
481
482 return true;
483}
484
485void Canvas::DrawLine(const Point& p0, const Point& p1, const Paint& paint) {
486 Entity entity;
487 entity.SetTransform(GetCurrentTransform());
488 entity.SetBlendMode(paint.blend_mode);
489 entity.SetContents(CreateContentsForGeometryWithFilters(
490 paint, Geometry::MakeLine(p0, p1, paint.stroke_width, paint.stroke_cap)));
491
492 AddRenderEntityToCurrentPass(std::move(entity));
493}
494
495void Canvas::DrawRect(const Rect& rect, const Paint& paint) {
496 if (paint.style == Paint::Style::kStroke) {
497 DrawPath(PathBuilder{}.AddRect(rect).TakePath(), paint);
498 return;
499 }
500
501 if (AttemptDrawBlurredRRect(rect, {}, paint)) {
502 return;
503 }
504
505 Entity entity;
506 entity.SetTransform(GetCurrentTransform());
507 entity.SetBlendMode(paint.blend_mode);
508 entity.SetContents(
509 CreateContentsForGeometryWithFilters(paint, Geometry::MakeRect(rect)));
510
511 AddRenderEntityToCurrentPass(std::move(entity));
512}
513
514void Canvas::DrawOval(const Rect& rect, const Paint& paint) {
515 if (rect.IsSquare()) {
516 // Circles have slightly less overhead and can do stroking
517 DrawCircle(rect.GetCenter(), rect.GetWidth() * 0.5f, paint);
518 return;
519 }
520
521 if (paint.style == Paint::Style::kStroke) {
522 // No stroked ellipses yet
523 DrawPath(PathBuilder{}.AddOval(rect).TakePath(), paint);
524 return;
525 }
526
527 if (AttemptDrawBlurredRRect(rect, rect.GetSize() * 0.5f, paint)) {
528 return;
529 }
530
531 Entity entity;
532 entity.SetTransform(GetCurrentTransform());
533 entity.SetBlendMode(paint.blend_mode);
534 entity.SetContents(
535 CreateContentsForGeometryWithFilters(paint, Geometry::MakeOval(rect)));
536
537 AddRenderEntityToCurrentPass(std::move(entity));
538}
539
540void Canvas::DrawRRect(const Rect& rect,
541 const Size& corner_radii,
542 const Paint& paint) {
543 if (AttemptDrawBlurredRRect(rect, corner_radii, paint)) {
544 return;
545 }
546
547 if (paint.style == Paint::Style::kFill) {
548 Entity entity;
549 entity.SetTransform(GetCurrentTransform());
550 entity.SetBlendMode(paint.blend_mode);
551 entity.SetContents(CreateContentsForGeometryWithFilters(
552 paint, Geometry::MakeRoundRect(rect, corner_radii)));
553
554 AddRenderEntityToCurrentPass(std::move(entity));
555 return;
556 }
557
558 auto path = PathBuilder{}
559 .SetConvexity(Convexity::kConvex)
560 .AddRoundedRect(rect, corner_radii)
562 .TakePath();
563 DrawPath(path, paint);
564}
565
566void Canvas::DrawCircle(const Point& center,
567 Scalar radius,
568 const Paint& paint) {
569 Size half_size(radius, radius);
570 if (AttemptDrawBlurredRRect(
571 Rect::MakeOriginSize(center - half_size, half_size * 2),
572 {radius, radius}, paint)) {
573 return;
574 }
575
576 Entity entity;
577 entity.SetTransform(GetCurrentTransform());
578 entity.SetBlendMode(paint.blend_mode);
579 auto geometry =
580 paint.style == Paint::Style::kStroke
581 ? Geometry::MakeStrokedCircle(center, radius, paint.stroke_width)
582 : Geometry::MakeCircle(center, radius);
583 entity.SetContents(
584 CreateContentsForGeometryWithFilters(paint, std::move(geometry)));
585
586 AddRenderEntityToCurrentPass(std::move(entity));
587}
588
589void Canvas::ClipPath(const Path& path, Entity::ClipOperation clip_op) {
590 auto bounds = path.GetBoundingBox();
591 ClipGeometry(Geometry::MakeFillPath(path), clip_op);
592 if (clip_op == Entity::ClipOperation::kIntersect) {
593 if (bounds.has_value()) {
594 IntersectCulling(bounds.value());
595 }
596 }
597}
598
599void Canvas::ClipRect(const Rect& rect, Entity::ClipOperation clip_op) {
600 auto geometry = Geometry::MakeRect(rect);
601 auto& cull_rect = transform_stack_.back().cull_rect;
602 if (clip_op == Entity::ClipOperation::kIntersect && //
603 cull_rect.has_value() && //
604 geometry->CoversArea(transform_stack_.back().transform, *cull_rect) //
605 ) {
606 return; // This clip will do nothing, so skip it.
607 }
608
609 ClipGeometry(geometry, clip_op);
610 switch (clip_op) {
611 case Entity::ClipOperation::kIntersect:
612 IntersectCulling(rect);
613 break;
614 case Entity::ClipOperation::kDifference:
615 SubtractCulling(rect);
616 break;
617 }
618}
619
620void Canvas::ClipOval(const Rect& bounds, Entity::ClipOperation clip_op) {
621 auto geometry = Geometry::MakeOval(bounds);
622 auto& cull_rect = transform_stack_.back().cull_rect;
623 if (clip_op == Entity::ClipOperation::kIntersect && //
624 cull_rect.has_value() && //
625 geometry->CoversArea(transform_stack_.back().transform, *cull_rect) //
626 ) {
627 return; // This clip will do nothing, so skip it.
628 }
629
630 ClipGeometry(geometry, clip_op);
631 switch (clip_op) {
632 case Entity::ClipOperation::kIntersect:
633 IntersectCulling(bounds);
634 break;
635 case Entity::ClipOperation::kDifference:
636 break;
637 }
638}
639
640void Canvas::ClipRRect(const Rect& rect,
641 const Size& corner_radii,
642 Entity::ClipOperation clip_op) {
643 // Does the rounded rect have a flat part on the top/bottom or left/right?
644 bool flat_on_TB = corner_radii.width * 2 < rect.GetWidth();
645 bool flat_on_LR = corner_radii.height * 2 < rect.GetHeight();
646 auto geometry = Geometry::MakeRoundRect(rect, corner_radii);
647 auto& cull_rect = transform_stack_.back().cull_rect;
648 if (clip_op == Entity::ClipOperation::kIntersect && //
649 cull_rect.has_value() && //
650 geometry->CoversArea(transform_stack_.back().transform, *cull_rect) //
651 ) {
652 return; // This clip will do nothing, so skip it.
653 }
654
655 ClipGeometry(geometry, clip_op);
656 switch (clip_op) {
657 case Entity::ClipOperation::kIntersect:
658 IntersectCulling(rect);
659 break;
660 case Entity::ClipOperation::kDifference:
661 if (corner_radii.IsEmpty()) {
662 SubtractCulling(rect);
663 } else {
664 // We subtract the inner "tall" and "wide" rectangle pieces
665 // that fit inside the corners which cover the greatest area
666 // without involving the curved corners
667 // Since this is a subtract operation, we can subtract each
668 // rectangle piece individually without fear of interference.
669 if (flat_on_TB) {
670 SubtractCulling(rect.Expand(Size{-corner_radii.width, 0.0}));
671 }
672 if (flat_on_LR) {
673 SubtractCulling(rect.Expand(Size{0.0, -corner_radii.height}));
674 }
675 }
676 break;
677 }
678}
679
680void Canvas::ClipGeometry(const std::shared_ptr<Geometry>& geometry,
681 Entity::ClipOperation clip_op) {
682 auto contents = std::make_shared<ClipContents>();
683 contents->SetGeometry(geometry);
684 contents->SetClipOperation(clip_op);
685
686 Entity entity;
687 entity.SetTransform(GetCurrentTransform());
688 entity.SetContents(std::move(contents));
689
690 AddClipEntityToCurrentPass(std::move(entity));
691
692 ++transform_stack_.back().clip_height;
693 ++transform_stack_.back().num_clips;
694}
695
696void Canvas::IntersectCulling(Rect clip_rect) {
697 clip_rect = clip_rect.TransformBounds(GetCurrentTransform());
698 std::optional<Rect>& cull_rect = transform_stack_.back().cull_rect;
699 if (cull_rect.has_value()) {
700 cull_rect = cull_rect
701 .value() //
702 .Intersection(clip_rect) //
703 .value_or(Rect{});
704 } else {
705 cull_rect = clip_rect;
706 }
707}
708
709void Canvas::SubtractCulling(Rect clip_rect) {
710 std::optional<Rect>& cull_rect = transform_stack_.back().cull_rect;
711 if (cull_rect.has_value()) {
712 clip_rect = clip_rect.TransformBounds(GetCurrentTransform());
713 cull_rect = cull_rect
714 .value() //
715 .Cutout(clip_rect) //
716 .value_or(Rect{});
717 }
718 // else (no cull) diff (any clip) is non-rectangular
719}
720
721void Canvas::RestoreClip() {
722 Entity entity;
723 entity.SetTransform(GetCurrentTransform());
724 // This path is empty because ClipRestoreContents just generates a quad that
725 // takes up the full render target.
726 auto clip_restore = std::make_shared<ClipRestoreContents>();
727 clip_restore->SetRestoreHeight(GetClipHeight());
728 entity.SetContents(std::move(clip_restore));
729
730 AddRenderEntityToCurrentPass(std::move(entity));
731}
732
733void Canvas::DrawPoints(std::vector<Point> points,
734 Scalar radius,
735 const Paint& paint,
736 PointStyle point_style) {
737 if (radius <= 0) {
738 return;
739 }
740
741 Entity entity;
742 entity.SetTransform(GetCurrentTransform());
743 entity.SetBlendMode(paint.blend_mode);
744 entity.SetContents(CreateContentsForGeometryWithFilters(
745 paint,
746 Geometry::MakePointField(std::move(points), radius,
747 /*round=*/point_style == PointStyle::kRound)));
748
749 AddRenderEntityToCurrentPass(std::move(entity));
750}
751
752void Canvas::DrawImage(const std::shared_ptr<Image>& image,
754 const Paint& paint,
755 SamplerDescriptor sampler) {
756 if (!image) {
757 return;
758 }
759
760 const auto source = Rect::MakeSize(image->GetSize());
761 const auto dest = source.Shift(offset);
762
763 DrawImageRect(image, source, dest, paint, std::move(sampler));
764}
765
766void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
767 Rect source,
768 Rect dest,
769 const Paint& paint,
770 SamplerDescriptor sampler,
771 SourceRectConstraint src_rect_constraint) {
772 if (!image || source.IsEmpty() || dest.IsEmpty()) {
773 return;
774 }
775
776 auto size = image->GetSize();
777
778 if (size.IsEmpty()) {
779 return;
780 }
781
782 auto texture_contents = TextureContents::MakeRect(dest);
783 texture_contents->SetTexture(image->GetTexture());
784 texture_contents->SetSourceRect(source);
785 texture_contents->SetStrictSourceRect(src_rect_constraint ==
786 SourceRectConstraint::kStrict);
787 texture_contents->SetSamplerDescriptor(std::move(sampler));
788 texture_contents->SetOpacity(paint.color.alpha);
789 texture_contents->SetDeferApplyingOpacity(paint.HasColorFilter());
790
791 std::shared_ptr<Contents> contents = texture_contents;
792 if (paint.mask_blur_descriptor.has_value()) {
793 contents = paint.mask_blur_descriptor->CreateMaskBlur(texture_contents);
794 }
795
796 Entity entity;
797 entity.SetBlendMode(paint.blend_mode);
798 entity.SetContents(paint.WithFilters(contents));
799 entity.SetTransform(GetCurrentTransform());
800
801 AddRenderEntityToCurrentPass(std::move(entity));
802}
803
804Picture Canvas::EndRecordingAsPicture() {
805 // Assign clip depths to any outstanding clip entities.
806 while (current_pass_ != nullptr) {
807 current_pass_->PopAllClips(current_depth_);
808 current_pass_ = current_pass_->GetSuperpass();
809 }
810
812 picture.pass = std::move(base_pass_);
813
814 Reset();
815 Initialize(initial_cull_rect_);
816
817 return picture;
818}
819
820EntityPass& Canvas::GetCurrentPass() {
821 FML_DCHECK(current_pass_ != nullptr);
822 return *current_pass_;
823}
824
825size_t Canvas::GetClipHeight() const {
826 return transform_stack_.back().clip_height;
827}
828
829void Canvas::AddRenderEntityToCurrentPass(Entity entity, bool reuse_depth) {
830 if (!reuse_depth) {
831 ++current_depth_;
832 }
833 entity.SetClipDepth(current_depth_);
834 entity.SetInheritedOpacity(transform_stack_.back().distributed_opacity);
835 GetCurrentPass().AddEntity(std::move(entity));
836}
837
838void Canvas::AddClipEntityToCurrentPass(Entity entity) {
839 GetCurrentPass().PushClip(std::move(entity));
840}
841
842void Canvas::SaveLayer(const Paint& paint,
843 std::optional<Rect> bounds,
844 const std::shared_ptr<ImageFilter>& backdrop_filter,
845 ContentBoundsPromise bounds_promise,
846 uint32_t total_content_depth,
847 bool can_distribute_opacity) {
848 if (can_distribute_opacity && !backdrop_filter &&
849 Paint::CanApplyOpacityPeephole(paint)) {
850 Save(false, total_content_depth, paint.blend_mode, backdrop_filter);
851 transform_stack_.back().distributed_opacity *= paint.color.alpha;
852 return;
853 }
854 TRACE_EVENT0("flutter", "Canvas::saveLayer");
855
856 Save(true, total_content_depth, paint.blend_mode, backdrop_filter);
857
858 // The DisplayList bounds/rtree doesn't account for filters applied to parent
859 // layers, and so sub-DisplayLists are getting culled as if no filters are
860 // applied.
861 // See also: https://github.com/flutter/flutter/issues/139294
862 if (paint.image_filter) {
863 transform_stack_.back().cull_rect = std::nullopt;
864 }
865
866 auto& new_layer_pass = GetCurrentPass();
867 if (bounds) {
868 new_layer_pass.SetBoundsLimit(bounds, bounds_promise);
869 }
870
871 if (paint.image_filter) {
872 MipCountVisitor mip_count_visitor;
873 paint.image_filter->Visit(mip_count_visitor);
874 new_layer_pass.SetRequiredMipCount(mip_count_visitor.GetRequiredMipCount());
875 }
876 // When applying a save layer, absorb any pending distributed opacity.
877 Paint paint_copy = paint;
878 paint_copy.color.alpha *= transform_stack_.back().distributed_opacity;
879 transform_stack_.back().distributed_opacity = 1.0;
880
881 new_layer_pass.SetDelegate(std::make_shared<PaintPassDelegate>(paint_copy));
882}
883
884void Canvas::DrawTextFrame(const std::shared_ptr<TextFrame>& text_frame,
885 Point position,
886 const Paint& paint) {
887 Entity entity;
888 entity.SetBlendMode(paint.blend_mode);
889
890 auto text_contents = std::make_shared<TextContents>();
891 text_contents->SetTextFrame(text_frame);
892 text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
893 text_contents->SetOffset(position);
894 text_contents->SetColor(paint.color);
895 text_contents->SetTextProperties(paint.color, //
896 paint.style == Paint::Style::kStroke, //
897 paint.stroke_width, //
898 paint.stroke_cap, //
899 paint.stroke_join, //
900 paint.stroke_miter //
901 );
902
903 entity.SetTransform(GetCurrentTransform() *
904 Matrix::MakeTranslation(position));
905
906 // TODO(bdero): This mask blur application is a hack. It will always wind up
907 // doing a gaussian blur that affects the color source itself
908 // instead of just the mask. The color filter text support
909 // needs to be reworked in order to interact correctly with
910 // mask filters.
911 // https://github.com/flutter/flutter/issues/133297
912 entity.SetContents(paint.WithFilters(paint.WithMaskBlur(
913 std::move(text_contents), true, GetCurrentTransform())));
914
915 AddRenderEntityToCurrentPass(std::move(entity));
916}
917
919 const std::shared_ptr<VerticesGeometry>& vertices,
920 const Paint& paint) {
921 // If there are no vertex color or texture coordinates. Or if there
922 // are vertex coordinates but its just a color.
923 if (vertices->HasVertexColors()) {
924 return false;
925 }
926 if (vertices->HasTextureCoordinates() &&
927 (paint.color_source.GetType() == ColorSource::Type::kColor)) {
928 return true;
929 }
930 return !vertices->HasTextureCoordinates();
931}
932
933void Canvas::DrawVertices(const std::shared_ptr<VerticesGeometry>& vertices,
934 BlendMode blend_mode,
935 const Paint& paint) {
936 // Override the blend mode with kDestination in order to match the behavior
937 // of Skia's SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER flag, which
938 // is enabled when the Flutter engine builds Skia.
939 if (paint.color_source.GetType() == ColorSource::Type::kColor) {
940 blend_mode = BlendMode::kDestination;
941 }
942
943 Entity entity;
944 entity.SetTransform(GetCurrentTransform());
945 entity.SetBlendMode(paint.blend_mode);
946
947 // If there are no vertex colors.
948 if (UseColorSourceContents(vertices, paint)) {
949 entity.SetContents(CreateContentsForGeometryWithFilters(paint, vertices));
950 AddRenderEntityToCurrentPass(std::move(entity));
951 return;
952 }
953
954 // If the blend mode is destination don't bother to bind or create a texture.
955 if (blend_mode == BlendMode::kDestination) {
956 auto contents = std::make_shared<VerticesSimpleBlendContents>();
957 contents->SetBlendMode(blend_mode);
958 contents->SetAlpha(paint.color.alpha);
959 contents->SetGeometry(vertices);
960 entity.SetContents(paint.WithFilters(std::move(contents)));
961 AddRenderEntityToCurrentPass(std::move(entity));
962 return;
963 }
964
965 // If there is a texture, use this directly. Otherwise render the color
966 // source to a texture.
967 if (std::optional<ImageData> maybe_image_data =
968 GetImageColorSourceData(paint.color_source)) {
969 const ImageData& image_data = maybe_image_data.value();
970 auto contents = std::make_shared<VerticesSimpleBlendContents>();
971 contents->SetBlendMode(blend_mode);
972 contents->SetAlpha(paint.color.alpha);
973 contents->SetGeometry(vertices);
974 contents->SetEffectTransform(image_data.effect_transform);
975 contents->SetTexture(image_data.texture);
976 contents->SetTileMode(image_data.x_tile_mode, image_data.y_tile_mode);
977
978 entity.SetContents(paint.WithFilters(std::move(contents)));
979 AddRenderEntityToCurrentPass(std::move(entity));
980 return;
981 }
982
983 auto src_paint = paint;
984 src_paint.color = paint.color.WithAlpha(1.0);
985
986 std::shared_ptr<Contents> src_contents =
987 src_paint.CreateContentsForGeometry(vertices);
988
989 // If the color source has an intrinsic size, then we use that to
990 // create the src contents as a simplification. Otherwise we use
991 // the extent of the texture coordinates to determine how large
992 // the src contents should be. If neither has a value we fall back
993 // to using the geometry coverage data.
994 Rect src_coverage;
995 auto size = src_contents->GetColorSourceSize();
996 if (size.has_value()) {
997 src_coverage = Rect::MakeXYWH(0, 0, size->width, size->height);
998 } else {
999 auto cvg = vertices->GetCoverage(Matrix{});
1000 FML_CHECK(cvg.has_value());
1001 src_coverage =
1002 // Covered by FML_CHECK.
1003 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1004 vertices->GetTextureCoordinateCoverge().value_or(cvg.value());
1005 }
1006 src_contents =
1007 src_paint.CreateContentsForGeometry(Geometry::MakeRect(src_coverage));
1008
1009 auto contents = std::make_shared<VerticesSimpleBlendContents>();
1010 contents->SetBlendMode(blend_mode);
1011 contents->SetAlpha(paint.color.alpha);
1012 contents->SetGeometry(vertices);
1013 contents->SetLazyTexture([src_contents](const ContentContext& renderer) {
1014 return src_contents->RenderToSnapshot(renderer, {})->texture;
1015 });
1016 entity.SetContents(paint.WithFilters(std::move(contents)));
1017 AddRenderEntityToCurrentPass(std::move(entity));
1018}
1019
1020void Canvas::DrawAtlas(const std::shared_ptr<Image>& atlas,
1021 std::vector<Matrix> transforms,
1022 std::vector<Rect> texture_coordinates,
1023 std::vector<Color> colors,
1024 BlendMode blend_mode,
1025 SamplerDescriptor sampler,
1026 std::optional<Rect> cull_rect,
1027 const Paint& paint) {
1028 if (!atlas) {
1029 return;
1030 }
1031
1032 std::shared_ptr<AtlasContents> contents = std::make_shared<AtlasContents>();
1033 contents->SetColors(std::move(colors));
1034 contents->SetTransforms(std::move(transforms));
1035 contents->SetTextureCoordinates(std::move(texture_coordinates));
1036 contents->SetTexture(atlas->GetTexture());
1037 contents->SetSamplerDescriptor(std::move(sampler));
1038 contents->SetBlendMode(blend_mode);
1039 contents->SetCullRect(cull_rect);
1040 contents->SetAlpha(paint.color.alpha);
1041
1042 Entity entity;
1043 entity.SetTransform(GetCurrentTransform());
1044 entity.SetBlendMode(paint.blend_mode);
1045 entity.SetContents(paint.WithFilters(contents));
1046
1047 AddRenderEntityToCurrentPass(std::move(entity));
1048}
1049
1050} // namespace impeller
static constexpr SkColor kColor
Definition: CanvasTest.cpp:265
int count
Definition: FontMgrTest.cpp:50
static const int points[]
static sk_sp< SkImage > color_filter(const SkImage *image, SkColorFilter *colorFilter)
static SkScalar center(float pos0, float pos1)
static constexpr uint32_t kMaxDepth
Definition: canvas.h:62
void Initialize(std::optional< Rect > cull_rect)
Definition: canvas.cc:164
const Matrix & GetCurrentTransform() const
Definition: canvas.cc:298
void RestoreToCount(size_t count)
Definition: canvas.cc:335
virtual bool Restore()
Definition: canvas.cc:257
size_t GetSaveCount() const
Definition: canvas.cc:331
void Concat(const Matrix &transform)
Definition: canvas.cc:282
void Transform(const Matrix &transform)
Definition: canvas.cc:294
std::optional< Rect > initial_cull_rect_
Definition: canvas.h:182
void PreConcat(const Matrix &transform)
Definition: canvas.cc:286
void Rotate(Radians radians)
Definition: canvas.cc:327
std::deque< CanvasStackEntry > transform_stack_
Definition: canvas.h:181
void ResetTransform()
Definition: canvas.cc:290
virtual ~Canvas()
void DrawPaint(const Paint &paint)
Definition: canvas.cc:352
void Skew(Scalar sx, Scalar sy)
Definition: canvas.cc:323
uint64_t current_depth_
Definition: canvas.h:183
void Scale(const Vector2 &scale)
Definition: canvas.cc:315
const std::optional< Rect > GetCurrentLocalCullingBounds() const
Definition: canvas.cc:302
void Reset()
Definition: canvas.cc:177
void DrawPath(const Path &path, const Paint &paint)
Definition: canvas.cc:343
virtual void Save(uint32_t total_content_depth=kMaxDepth)
Definition: canvas.cc:184
virtual void AddRenderEntityToCurrentPass(Entity entity, bool reuse_depth=false)
Definition: canvas.cc:829
void Translate(const Vector3 &offset)
Definition: canvas.cc:311
std::shared_ptr< ColorSourceContents > GetContents(const Paint &paint) const
void SetRequiredMipCount(int32_t mip_count)
Definition: entity_pass.h:177
void SetClipDepth(size_t clip_depth)
EntityPass * GetSuperpass() const
Definition: entity_pass.cc:263
void PopClips(size_t num_clips, uint64_t depth)
Definition: entity_pass.cc:117
int32_t GetRequiredMipCount() const
Definition: entity_pass.h:175
void SetClipHeight(size_t clip_height)
void SetTransform(Matrix transform)
EntityPass * AddSubpass(std::unique_ptr< EntityPass > pass)
Appends a given pass as a subpass.
Definition: entity_pass.cc:267
std::function< std::shared_ptr< FilterContents >(FilterInput::Ref, const Matrix &effect_transform, Entity::RenderingMode rendering_mode)> BackdropFilterProc
Definition: entity_pass.h:59
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition: entity.cc:62
bool SetInheritedOpacity(Scalar alpha)
Definition: entity.cc:134
void SetClipDepth(uint32_t clip_depth)
Definition: entity.cc:98
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:90
void SetBlendMode(BlendMode blend_mode)
Definition: entity.cc:115
static const int32_t kBlurFilterRequiredMipCount
@ kNormal
Blurred inside and outside.
@ kOuter
Nothing inside, blurred outside.
@ kInner
Blurred inside, nothing outside.
@ kSolid
Solid inside, blurred outside.
std::shared_ptr< FilterInput > Ref
Definition: filter_input.h:32
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
Definition: filter_input.cc:19
static std::shared_ptr< Geometry > MakeCover()
Definition: geometry.cc:85
static std::shared_ptr< Geometry > MakeRoundRect(const Rect &rect, const Size &radii)
Definition: geometry.cc:115
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:72
static std::shared_ptr< Geometry > MakeFillPath(const Path &path, std::optional< Rect > inner_rect=std::nullopt)
Definition: geometry.cc:60
PathBuilder & AddRect(Rect rect)
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:22
PathBuilder & SetBounds(Rect bounds)
Set the bounding box that will be used by Path.GetBoundingBox in place of performing the computation.
PathBuilder & AddOval(const Rect &rect)
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
PathBuilder & SetConvexity(Convexity value)
Definition: path_builder.cc:85
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:52
const Paint & paint
Definition: color_source.cc:38
SkBitmap source
Definition: examples.cpp:28
#define FML_CHECK(condition)
Definition: logging.h:85
#define FML_DCHECK(condition)
Definition: logging.h:103
static float max(float r, float g, float b)
Definition: hsl.cpp:49
FlTexture * texture
sk_sp< const SkImage > atlas
Definition: SkRecords.h:331
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkImage > image
Definition: SkRecords.h:269
sk_sp< const SkPicture > picture
Definition: SkRecords.h:299
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
PODArray< SkColor > colors
Definition: SkRecords.h:276
SK_API sk_sp< SkShader > Color(SkColor)
void DrawImage(SkCanvas *canvas, const SkImage *image, SkScalar x, SkScalar y, const SkSamplingOptions &sampling={}, const SkPaint *paint=nullptr, SkCanvas::SrcRectConstraint constraint=SkCanvas::kFast_SrcRectConstraint)
SK_API void DrawImageRect(SkCanvas *canvas, const SkImage *image, const SkRect &src, const SkRect &dst, const SkSamplingOptions &sampling={}, const SkPaint *paint=nullptr, SkCanvas::SrcRectConstraint constraint=SkCanvas::kFast_SrcRectConstraint)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
void Reset(SkPath *path)
Definition: path_ops.cc:40
CanvasPath Path
Definition: dart_ui.cc:58
void Initialize(zx::channel directory_request, std::optional< zx::eventpair > view_ref)
Initializes Dart bindings for the Fuchsia application model.
Definition: fuchsia.cc:103
float Scalar
Definition: scalar.h:18
SourceRectConstraint
Controls the behavior of the source rectangle given to DrawImageRect.
Definition: canvas.h:52
static bool UseColorSourceContents(const std::shared_ptr< VerticesGeometry > &vertices, const Paint &paint)
Definition: canvas.cc:918
constexpr float kEhCloseEnough
Definition: constants.h:56
TRect< Scalar > Rect
Definition: rect.h:769
PointStyle
Definition: canvas.h:43
BlendMode
Definition: color.h:59
ContentBoundsPromise
Definition: entity_pass.h:28
@ kContainsContents
The caller claims the bounds are a reasonably tight estimate of the coverage of the contents and shou...
GrOp::Owner MakeRect(GrRecordingContext *context, GrPaint &&paint, const SkRect &drawRect, const SkRect &localRect, const SkMatrix &localM)
Definition: TestOps.cpp:227
skgpu::graphite::DrawAtlas DrawAtlas
dest
Definition: zip.py:79
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
const Scalar scale
SeparatedVector2 offset
std::optional< Rect > cull_rect
Definition: canvas.h:34
Scalar alpha
Definition: color.h:143
static constexpr Color White()
Definition: color.h:266
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:280
std::shared_ptr< Texture > texture
Definition: color_source.h:68
Entity::TileMode y_tile_mode
Definition: color_source.h:70
Entity::TileMode x_tile_mode
Definition: color_source.h:69
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:117
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:213
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
ColorSource color_source
Definition: paint.h:69
std::shared_ptr< ColorFilter > GetColorFilter() const
Definition: paint.cc:222
bool HasColorFilter() const
Whether this paint has a color filter that can apply opacity.
Definition: paint.cc:236
Cap stroke_cap
Definition: paint.h:72
Join stroke_join
Definition: paint.h:73
Scalar stroke_miter
Definition: paint.h:74
Style style
Definition: paint.h:75
std::shared_ptr< ImageFilter > image_filter
Definition: paint.h:79
std::optional< MaskBlurDescriptor > mask_blur_descriptor
Definition: paint.h:81
Color color
Definition: paint.h:68
BlendMode blend_mode
Definition: paint.h:76
Scalar stroke_width
Definition: paint.h:71
constexpr auto GetBottom() const
Definition: rect.h:347
constexpr auto GetTop() const
Definition: rect.h:343
constexpr auto GetLeft() const
Definition: rect.h:341
constexpr auto GetRight() const
Definition: rect.h:345
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
Type height
Definition: size.h:23
Type width
Definition: size.h:22
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: size.h:105
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131