Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 if (create_subpass) {
228 entry.rendering_mode = Entity::RenderingMode::kSubpass;
229 auto subpass = std::make_unique<EntityPass>();
230 subpass->SetEnableOffscreenCheckerboard(
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 current_pass_->SetClipDepth(++current_depth_);
268 current_pass_ = GetCurrentPass().GetSuperpass();
269 FML_DCHECK(current_pass_);
270 }
271
272 transform_stack_.pop_back();
273 if (num_clips > 0) {
274 RestoreClip();
275 }
276
277 return true;
278}
279
281 transform_stack_.back().transform = GetCurrentTransform() * transform;
282}
283
285 transform_stack_.back().transform = transform * GetCurrentTransform();
286}
287
289 transform_stack_.back().transform = {};
290}
291
295
297 return transform_stack_.back().transform;
298}
299
300const std::optional<Rect> Canvas::GetCurrentLocalCullingBounds() const {
301 auto cull_rect = transform_stack_.back().cull_rect;
302 if (cull_rect.has_value()) {
303 Matrix inverse = transform_stack_.back().transform.Invert();
304 cull_rect = cull_rect.value().TransformBounds(inverse);
305 }
306 return cull_rect;
307}
308
312
316
320
322 Concat(Matrix::MakeSkew(sx, sy));
323}
324
325void Canvas::Rotate(Radians radians) {
327}
328
329size_t Canvas::GetSaveCount() const {
330 return transform_stack_.size();
331}
332
334 while (GetSaveCount() > count) {
335 if (!Restore()) {
336 return;
337 }
338 }
339}
340
341void Canvas::DrawPath(const Path& path, const Paint& paint) {
342 Entity entity;
344 entity.SetBlendMode(paint.blend_mode);
345 entity.SetContents(CreatePathContentsWithFilters(paint, path));
346
347 AddRenderEntityToCurrentPass(std::move(entity));
348}
349
351 Entity entity;
353 entity.SetBlendMode(paint.blend_mode);
354 entity.SetContents(CreateCoverContentsWithFilters(paint));
355
356 AddRenderEntityToCurrentPass(std::move(entity));
357}
358
359bool Canvas::AttemptDrawBlurredRRect(const Rect& rect,
360 Size corner_radii,
361 const Paint& paint) {
362 if (paint.color_source.GetType() != ColorSource::Type::kColor ||
363 paint.style != Paint::Style::kFill) {
364 return false;
365 }
366
367 if (!paint.mask_blur_descriptor.has_value()) {
368 return false;
369 }
370
371 // A blur sigma that is not positive enough should not result in a blur.
372 if (paint.mask_blur_descriptor->sigma.sigma <= kEhCloseEnough) {
373 return false;
374 }
375
376 // For symmetrically mask blurred solid RRects, absorb the mask blur and use
377 // a faster SDF approximation.
378
379 Color rrect_color =
380 paint.HasColorFilter()
381 // Absorb the color filter, if any.
382 ? paint.GetColorFilter()->GetCPUColorFilterProc()(paint.color)
383 : paint.color;
384
385 Paint rrect_paint = {.mask_blur_descriptor = paint.mask_blur_descriptor};
386
387 // In some cases, we need to render the mask blur to a separate layer.
388 //
389 // 1. If the blur style is normal, we'll be drawing using one draw call and
390 // no clips. And so we can just wrap the RRect contents with the
391 // ImageFilter, which will get applied to the result as per usual.
392 //
393 // 2. If the blur style is solid, we combine the non-blurred RRect with the
394 // blurred RRect via two separate draw calls, and so we need to defer any
395 // fancy blending, translucency, or image filtering until after these two
396 // draws have been combined in a separate layer.
397 //
398 // 3. If the blur style is outer or inner, we apply the blur style via a
399 // clip. The ImageFilter needs to be applied to the mask blurred result.
400 // And so if there's an ImageFilter, we need to defer applying it until
401 // after the clipped RRect blur has been drawn to a separate texture.
402 // However, since there's only one draw call that produces color, we
403 // don't need to worry about the blend mode or translucency (unlike with
404 // BlurStyle::kSolid).
405 //
406 if ((paint.mask_blur_descriptor->style !=
408 paint.image_filter) ||
409 (paint.mask_blur_descriptor->style == FilterContents::BlurStyle::kSolid &&
410 (!rrect_color.IsOpaque() ||
411 paint.blend_mode != BlendMode::kSourceOver))) {
412 Rect render_bounds = rect;
413 if (paint.mask_blur_descriptor->style !=
415 render_bounds =
416 render_bounds.Expand(paint.mask_blur_descriptor->sigma.sigma * 4.0);
417 }
418 // Defer the alpha, blend mode, and image filter to a separate layer.
419 SaveLayer({.color = Color::White().WithAlpha(rrect_color.alpha),
420 .blend_mode = paint.blend_mode,
421 .image_filter = paint.image_filter},
422 render_bounds, nullptr, ContentBoundsPromise::kContainsContents,
423 1u);
424 rrect_paint.color = rrect_color.WithAlpha(1);
425 } else {
426 rrect_paint.color = rrect_color;
427 rrect_paint.blend_mode = paint.blend_mode;
428 rrect_paint.image_filter = paint.image_filter;
429 Save(1u);
430 }
431
432 auto draw_blurred_rrect = [this, &rect, &corner_radii, &rrect_paint]() {
433 auto contents = std::make_shared<SolidRRectBlurContents>();
434
435 contents->SetColor(rrect_paint.color);
436 contents->SetSigma(rrect_paint.mask_blur_descriptor->sigma);
437 contents->SetRRect(rect, corner_radii);
438
439 Entity blurred_rrect_entity;
440 blurred_rrect_entity.SetTransform(GetCurrentTransform());
441 blurred_rrect_entity.SetBlendMode(rrect_paint.blend_mode);
442
443 rrect_paint.mask_blur_descriptor = std::nullopt;
444 blurred_rrect_entity.SetContents(
445 rrect_paint.WithFilters(std::move(contents)));
446 AddRenderEntityToCurrentPass(std::move(blurred_rrect_entity));
447 };
448
449 switch (rrect_paint.mask_blur_descriptor->style) {
451 draw_blurred_rrect();
452 break;
453 }
455 // First, draw the blurred RRect.
456 draw_blurred_rrect();
457 // Then, draw the non-blurred RRect on top.
458 Entity entity;
459 entity.SetTransform(GetCurrentTransform());
460 entity.SetBlendMode(rrect_paint.blend_mode);
461 entity.SetContents(CreateContentsForGeometryWithFilters(
462 rrect_paint, Geometry::MakeRoundRect(rect, corner_radii)));
463 AddRenderEntityToCurrentPass(std::move(entity), true);
464 break;
465 }
468 draw_blurred_rrect();
469 break;
470 }
472 ClipRRect(rect, corner_radii, Entity::ClipOperation::kIntersect);
473 draw_blurred_rrect();
474 break;
475 }
476 }
477
478 Restore();
479
480 return true;
481}
482
483void Canvas::DrawLine(const Point& p0, const Point& p1, const Paint& paint) {
484 Entity entity;
486 entity.SetBlendMode(paint.blend_mode);
487 entity.SetContents(CreateContentsForGeometryWithFilters(
488 paint, Geometry::MakeLine(p0, p1, paint.stroke_width, paint.stroke_cap)));
489
490 AddRenderEntityToCurrentPass(std::move(entity));
491}
492
493void Canvas::DrawRect(const Rect& rect, const Paint& paint) {
494 if (paint.style == Paint::Style::kStroke) {
495 DrawPath(PathBuilder{}.AddRect(rect).TakePath(), paint);
496 return;
497 }
498
499 if (AttemptDrawBlurredRRect(rect, {}, paint)) {
500 return;
501 }
502
503 Entity entity;
505 entity.SetBlendMode(paint.blend_mode);
506 entity.SetContents(
507 CreateContentsForGeometryWithFilters(paint, Geometry::MakeRect(rect)));
508
509 AddRenderEntityToCurrentPass(std::move(entity));
510}
511
512void Canvas::DrawOval(const Rect& rect, const Paint& paint) {
513 if (rect.IsSquare()) {
514 // Circles have slightly less overhead and can do stroking
515 DrawCircle(rect.GetCenter(), rect.GetWidth() * 0.5f, paint);
516 return;
517 }
518
519 if (paint.style == Paint::Style::kStroke) {
520 // No stroked ellipses yet
521 DrawPath(PathBuilder{}.AddOval(rect).TakePath(), paint);
522 return;
523 }
524
525 if (AttemptDrawBlurredRRect(rect, rect.GetSize() * 0.5f, paint)) {
526 return;
527 }
528
529 Entity entity;
531 entity.SetBlendMode(paint.blend_mode);
532 entity.SetContents(
533 CreateContentsForGeometryWithFilters(paint, Geometry::MakeOval(rect)));
534
535 AddRenderEntityToCurrentPass(std::move(entity));
536}
537
538void Canvas::DrawRRect(const Rect& rect,
539 const Size& corner_radii,
540 const Paint& paint) {
541 if (AttemptDrawBlurredRRect(rect, corner_radii, paint)) {
542 return;
543 }
544
545 if (paint.style == Paint::Style::kFill) {
546 Entity entity;
548 entity.SetBlendMode(paint.blend_mode);
549 entity.SetContents(CreateContentsForGeometryWithFilters(
550 paint, Geometry::MakeRoundRect(rect, corner_radii)));
551
552 AddRenderEntityToCurrentPass(std::move(entity));
553 return;
554 }
555
556 auto path = PathBuilder{}
558 .AddRoundedRect(rect, corner_radii)
559 .SetBounds(rect)
560 .TakePath();
561 DrawPath(path, paint);
562}
563
565 Scalar radius,
566 const Paint& paint) {
567 Size half_size(radius, radius);
568 if (AttemptDrawBlurredRRect(
569 Rect::MakeOriginSize(center - half_size, half_size * 2),
570 {radius, radius}, paint)) {
571 return;
572 }
573
574 Entity entity;
576 entity.SetBlendMode(paint.blend_mode);
577 auto geometry =
579 ? Geometry::MakeStrokedCircle(center, radius, paint.stroke_width)
580 : Geometry::MakeCircle(center, radius);
581 entity.SetContents(
582 CreateContentsForGeometryWithFilters(paint, std::move(geometry)));
583
584 AddRenderEntityToCurrentPass(std::move(entity));
585}
586
587void Canvas::ClipPath(const Path& path, Entity::ClipOperation clip_op) {
588 auto bounds = path.GetBoundingBox();
589 ClipGeometry(Geometry::MakeFillPath(path), clip_op);
590 if (clip_op == Entity::ClipOperation::kIntersect) {
591 if (bounds.has_value()) {
592 IntersectCulling(bounds.value());
593 }
594 }
595}
596
597void Canvas::ClipRect(const Rect& rect, Entity::ClipOperation clip_op) {
598 auto geometry = Geometry::MakeRect(rect);
599 auto& cull_rect = transform_stack_.back().cull_rect;
600 if (clip_op == Entity::ClipOperation::kIntersect && //
601 cull_rect.has_value() && //
602 geometry->CoversArea(transform_stack_.back().transform, *cull_rect) //
603 ) {
604 return; // This clip will do nothing, so skip it.
605 }
606
607 ClipGeometry(geometry, clip_op);
608 switch (clip_op) {
610 IntersectCulling(rect);
611 break;
613 SubtractCulling(rect);
614 break;
615 }
616}
617
618void Canvas::ClipOval(const Rect& bounds, Entity::ClipOperation clip_op) {
619 auto geometry = Geometry::MakeOval(bounds);
620 auto& cull_rect = transform_stack_.back().cull_rect;
621 if (clip_op == Entity::ClipOperation::kIntersect && //
622 cull_rect.has_value() && //
623 geometry->CoversArea(transform_stack_.back().transform, *cull_rect) //
624 ) {
625 return; // This clip will do nothing, so skip it.
626 }
627
628 ClipGeometry(geometry, clip_op);
629 switch (clip_op) {
631 IntersectCulling(bounds);
632 break;
634 break;
635 }
636}
637
638void Canvas::ClipRRect(const Rect& rect,
639 const Size& corner_radii,
640 Entity::ClipOperation clip_op) {
641 // Does the rounded rect have a flat part on the top/bottom or left/right?
642 bool flat_on_TB = corner_radii.width * 2 < rect.GetWidth();
643 bool flat_on_LR = corner_radii.height * 2 < rect.GetHeight();
644 auto geometry = Geometry::MakeRoundRect(rect, corner_radii);
645 auto& cull_rect = transform_stack_.back().cull_rect;
646 if (clip_op == Entity::ClipOperation::kIntersect && //
647 cull_rect.has_value() && //
648 geometry->CoversArea(transform_stack_.back().transform, *cull_rect) //
649 ) {
650 return; // This clip will do nothing, so skip it.
651 }
652
653 ClipGeometry(geometry, clip_op);
654 switch (clip_op) {
656 IntersectCulling(rect);
657 break;
659 if (corner_radii.IsEmpty()) {
660 SubtractCulling(rect);
661 } else {
662 // We subtract the inner "tall" and "wide" rectangle pieces
663 // that fit inside the corners which cover the greatest area
664 // without involving the curved corners
665 // Since this is a subtract operation, we can subtract each
666 // rectangle piece individually without fear of interference.
667 if (flat_on_TB) {
668 SubtractCulling(rect.Expand(Size{-corner_radii.width, 0.0}));
669 }
670 if (flat_on_LR) {
671 SubtractCulling(rect.Expand(Size{0.0, -corner_radii.height}));
672 }
673 }
674 break;
675 }
676}
677
678void Canvas::ClipGeometry(const std::shared_ptr<Geometry>& geometry,
679 Entity::ClipOperation clip_op) {
680 auto contents = std::make_shared<ClipContents>();
681 contents->SetGeometry(geometry);
682 contents->SetClipOperation(clip_op);
683
684 Entity entity;
686 entity.SetContents(std::move(contents));
687
688 AddClipEntityToCurrentPass(std::move(entity));
689
690 ++transform_stack_.back().clip_height;
691 ++transform_stack_.back().num_clips;
692}
693
694void Canvas::IntersectCulling(Rect clip_rect) {
695 clip_rect = clip_rect.TransformBounds(GetCurrentTransform());
696 std::optional<Rect>& cull_rect = transform_stack_.back().cull_rect;
697 if (cull_rect.has_value()) {
698 cull_rect = cull_rect
699 .value() //
700 .Intersection(clip_rect) //
701 .value_or(Rect{});
702 } else {
703 cull_rect = clip_rect;
704 }
705}
706
707void Canvas::SubtractCulling(Rect clip_rect) {
708 std::optional<Rect>& cull_rect = transform_stack_.back().cull_rect;
709 if (cull_rect.has_value()) {
710 clip_rect = clip_rect.TransformBounds(GetCurrentTransform());
711 cull_rect = cull_rect
712 .value() //
713 .Cutout(clip_rect) //
714 .value_or(Rect{});
715 }
716 // else (no cull) diff (any clip) is non-rectangular
717}
718
719void Canvas::RestoreClip() {
720 Entity entity;
722 // This path is empty because ClipRestoreContents just generates a quad that
723 // takes up the full render target.
724 auto clip_restore = std::make_shared<ClipRestoreContents>();
725 clip_restore->SetRestoreHeight(GetClipHeight());
726 entity.SetContents(std::move(clip_restore));
727
728 AddRenderEntityToCurrentPass(std::move(entity));
729}
730
731void Canvas::DrawPoints(std::vector<Point> points,
732 Scalar radius,
733 const Paint& paint,
734 PointStyle point_style) {
735 if (radius <= 0) {
736 return;
737 }
738
739 Entity entity;
741 entity.SetBlendMode(paint.blend_mode);
742 entity.SetContents(CreateContentsForGeometryWithFilters(
743 paint,
744 Geometry::MakePointField(std::move(points), radius,
745 /*round=*/point_style == PointStyle::kRound)));
746
747 AddRenderEntityToCurrentPass(std::move(entity));
748}
749
750void Canvas::DrawImage(const std::shared_ptr<Image>& image,
752 const Paint& paint,
753 SamplerDescriptor sampler) {
754 if (!image) {
755 return;
756 }
757
758 const auto source = Rect::MakeSize(image->GetSize());
759 const auto dest = source.Shift(offset);
760
761 DrawImageRect(image, source, dest, paint, std::move(sampler));
762}
763
764void Canvas::DrawImageRect(const std::shared_ptr<Image>& image,
765 Rect source,
766 Rect dest,
767 const Paint& paint,
768 SamplerDescriptor sampler,
769 SourceRectConstraint src_rect_constraint) {
770 if (!image || source.IsEmpty() || dest.IsEmpty()) {
771 return;
772 }
773
774 auto size = image->GetSize();
775
776 if (size.IsEmpty()) {
777 return;
778 }
779
780 auto texture_contents = TextureContents::MakeRect(dest);
781 texture_contents->SetTexture(image->GetTexture());
782 texture_contents->SetSourceRect(source);
783 texture_contents->SetStrictSourceRect(src_rect_constraint ==
785 texture_contents->SetSamplerDescriptor(std::move(sampler));
786 texture_contents->SetOpacity(paint.color.alpha);
787 texture_contents->SetDeferApplyingOpacity(paint.HasColorFilter());
788
789 std::shared_ptr<Contents> contents = texture_contents;
790 if (paint.mask_blur_descriptor.has_value()) {
791 contents = paint.mask_blur_descriptor->CreateMaskBlur(texture_contents);
792 }
793
794 Entity entity;
795 entity.SetBlendMode(paint.blend_mode);
796 entity.SetContents(paint.WithFilters(contents));
798
799 AddRenderEntityToCurrentPass(std::move(entity));
800}
801
803 // Assign clip depths to any outstanding clip entities.
804 while (current_pass_ != nullptr) {
805 current_pass_->PopAllClips(current_depth_);
806 current_pass_ = current_pass_->GetSuperpass();
807 }
808
809 Picture picture;
810 picture.pass = std::move(base_pass_);
811
812 Reset();
814
815 return picture;
816}
817
818EntityPass& Canvas::GetCurrentPass() {
819 FML_DCHECK(current_pass_ != nullptr);
820 return *current_pass_;
821}
822
823size_t Canvas::GetClipHeight() const {
824 return transform_stack_.back().clip_height;
825}
826
827void Canvas::AddRenderEntityToCurrentPass(Entity entity, bool reuse_depth) {
828 if (!reuse_depth) {
830 }
832 GetCurrentPass().AddEntity(std::move(entity));
833}
834
836 GetCurrentPass().PushClip(std::move(entity));
837}
838
840 std::optional<Rect> bounds,
841 const std::shared_ptr<ImageFilter>& backdrop_filter,
842 ContentBoundsPromise bounds_promise,
843 uint32_t total_content_depth) {
844 TRACE_EVENT0("flutter", "Canvas::saveLayer");
845 Save(true, total_content_depth, paint.blend_mode, backdrop_filter);
846
847 // The DisplayList bounds/rtree doesn't account for filters applied to parent
848 // layers, and so sub-DisplayLists are getting culled as if no filters are
849 // applied.
850 // See also: https://github.com/flutter/flutter/issues/139294
851 if (paint.image_filter) {
852 transform_stack_.back().cull_rect = std::nullopt;
853 }
854
855 auto& new_layer_pass = GetCurrentPass();
856 if (bounds) {
857 new_layer_pass.SetBoundsLimit(bounds, bounds_promise);
858 }
859
860 if (paint.image_filter) {
861 MipCountVisitor mip_count_visitor;
862 paint.image_filter->Visit(mip_count_visitor);
863 new_layer_pass.SetRequiredMipCount(mip_count_visitor.GetRequiredMipCount());
864 }
865
866 // Only apply opacity peephole on default blending.
867 if (paint.blend_mode == BlendMode::kSourceOver) {
868 new_layer_pass.SetDelegate(
869 std::make_shared<OpacityPeepholePassDelegate>(paint));
870 } else {
871 new_layer_pass.SetDelegate(std::make_shared<PaintPassDelegate>(paint));
872 }
873}
874
875void Canvas::DrawTextFrame(const std::shared_ptr<TextFrame>& text_frame,
876 Point position,
877 const Paint& paint) {
878 Entity entity;
879 entity.SetBlendMode(paint.blend_mode);
880
881 auto text_contents = std::make_shared<TextContents>();
882 text_contents->SetTextFrame(text_frame);
883 text_contents->SetColor(paint.color);
884 text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
885
887 Matrix::MakeTranslation(position));
888
889 // TODO(bdero): This mask blur application is a hack. It will always wind up
890 // doing a gaussian blur that affects the color source itself
891 // instead of just the mask. The color filter text support
892 // needs to be reworked in order to interact correctly with
893 // mask filters.
894 // https://github.com/flutter/flutter/issues/133297
895 entity.SetContents(
896 paint.WithFilters(paint.WithMaskBlur(std::move(text_contents), true)));
897
898 AddRenderEntityToCurrentPass(std::move(entity));
899}
900
902 const std::shared_ptr<VerticesGeometry>& vertices,
903 const Paint& paint) {
904 // If there are no vertex color or texture coordinates. Or if there
905 // are vertex coordinates but its just a color.
906 if (vertices->HasVertexColors()) {
907 return false;
908 }
909 if (vertices->HasTextureCoordinates() &&
911 return true;
912 }
913 return !vertices->HasTextureCoordinates();
914}
915
916void Canvas::DrawVertices(const std::shared_ptr<VerticesGeometry>& vertices,
917 BlendMode blend_mode,
918 const Paint& paint) {
919 // Override the blend mode with kDestination in order to match the behavior
920 // of Skia's SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER flag, which
921 // is enabled when the Flutter engine builds Skia.
922 if (paint.color_source.GetType() == ColorSource::Type::kColor) {
923 blend_mode = BlendMode::kDestination;
924 }
925
926 Entity entity;
928 entity.SetBlendMode(paint.blend_mode);
929
930 // If there are no vertex colors.
931 if (UseColorSourceContents(vertices, paint)) {
932 entity.SetContents(CreateContentsForGeometryWithFilters(paint, vertices));
933 AddRenderEntityToCurrentPass(std::move(entity));
934 return;
935 }
936
937 // If the blend mode is destination don't bother to bind or create a texture.
938 if (blend_mode == BlendMode::kDestination) {
939 auto contents = std::make_shared<VerticesSimpleBlendContents>();
940 contents->SetBlendMode(blend_mode);
941 contents->SetAlpha(paint.color.alpha);
942 contents->SetGeometry(vertices);
943 entity.SetContents(paint.WithFilters(std::move(contents)));
944 AddRenderEntityToCurrentPass(std::move(entity));
945 return;
946 }
947
948 // If there is a texture, use this directly. Otherwise render the color
949 // source to a texture.
950 if (std::optional<ImageData> maybe_image_data =
951 GetImageColorSourceData(paint.color_source)) {
952 const ImageData& image_data = maybe_image_data.value();
953 auto contents = std::make_shared<VerticesSimpleBlendContents>();
954 contents->SetBlendMode(blend_mode);
955 contents->SetAlpha(paint.color.alpha);
956 contents->SetGeometry(vertices);
957 contents->SetEffectTransform(image_data.effect_transform);
958 contents->SetTexture(image_data.texture);
959 contents->SetTileMode(image_data.x_tile_mode, image_data.y_tile_mode);
960
961 entity.SetContents(paint.WithFilters(std::move(contents)));
962 AddRenderEntityToCurrentPass(std::move(entity));
963 return;
964 }
965
966 auto src_paint = paint;
967 src_paint.color = paint.color.WithAlpha(1.0);
968
969 std::shared_ptr<Contents> src_contents =
970 src_paint.CreateContentsForGeometry(vertices);
971
972 // If the color source has an intrinsic size, then we use that to
973 // create the src contents as a simplification. Otherwise we use
974 // the extent of the texture coordinates to determine how large
975 // the src contents should be. If neither has a value we fall back
976 // to using the geometry coverage data.
977 Rect src_coverage;
978 auto size = src_contents->GetColorSourceSize();
979 if (size.has_value()) {
980 src_coverage = Rect::MakeXYWH(0, 0, size->width, size->height);
981 } else {
982 auto cvg = vertices->GetCoverage(Matrix{});
983 FML_CHECK(cvg.has_value());
984 src_coverage =
985 // Covered by FML_CHECK.
986 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
987 vertices->GetTextureCoordinateCoverge().value_or(cvg.value());
988 }
989 src_contents =
990 src_paint.CreateContentsForGeometry(Geometry::MakeRect(src_coverage));
991
992 auto contents = std::make_shared<VerticesSimpleBlendContents>();
993 contents->SetBlendMode(blend_mode);
994 contents->SetAlpha(paint.color.alpha);
995 contents->SetGeometry(vertices);
996 contents->SetLazyTexture([src_contents](const ContentContext& renderer) {
997 return src_contents->RenderToSnapshot(renderer, {})->texture;
998 });
999 entity.SetContents(paint.WithFilters(std::move(contents)));
1000 AddRenderEntityToCurrentPass(std::move(entity));
1001}
1002
1003void Canvas::DrawAtlas(const std::shared_ptr<Image>& atlas,
1004 std::vector<Matrix> transforms,
1005 std::vector<Rect> texture_coordinates,
1006 std::vector<Color> colors,
1007 BlendMode blend_mode,
1008 SamplerDescriptor sampler,
1009 std::optional<Rect> cull_rect,
1010 const Paint& paint) {
1011 if (!atlas) {
1012 return;
1013 }
1014
1015 std::shared_ptr<AtlasContents> contents = std::make_shared<AtlasContents>();
1016 contents->SetColors(std::move(colors));
1017 contents->SetTransforms(std::move(transforms));
1018 contents->SetTextureCoordinates(std::move(texture_coordinates));
1019 contents->SetTexture(atlas->GetTexture());
1020 contents->SetSamplerDescriptor(std::move(sampler));
1021 contents->SetBlendMode(blend_mode);
1022 contents->SetCullRect(cull_rect);
1023 contents->SetAlpha(paint.color.alpha);
1024
1025 Entity entity;
1027 entity.SetBlendMode(paint.blend_mode);
1028 entity.SetContents(paint.WithFilters(contents));
1029
1030 AddRenderEntityToCurrentPass(std::move(entity));
1031}
1032
1033} // namespace impeller
int count
static const int points[]
SkColor4f color
static sk_sp< SkImage > color_filter(const SkImage *image, SkColorFilter *colorFilter)
static SkScalar center(float pos0, float pos1)
void DrawLine(const Point &p0, const Point &p1, const Paint &paint)
Definition canvas.cc:483
static constexpr uint32_t kMaxDepth
Definition canvas.h:61
void Initialize(std::optional< Rect > cull_rect)
Definition canvas.cc:164
const Matrix & GetCurrentTransform() const
Definition canvas.cc:296
void DrawImageRect(const std::shared_ptr< Image > &image, Rect source, Rect dest, const Paint &paint, SamplerDescriptor sampler={}, SourceRectConstraint src_rect_constraint=SourceRectConstraint::kFast)
Definition canvas.cc:764
void DrawVertices(const std::shared_ptr< VerticesGeometry > &vertices, BlendMode blend_mode, const Paint &paint)
Definition canvas.cc:916
virtual void SaveLayer(const Paint &paint, std::optional< Rect > bounds=std::nullopt, const std::shared_ptr< ImageFilter > &backdrop_filter=nullptr, ContentBoundsPromise bounds_promise=ContentBoundsPromise::kUnknown, uint32_t total_content_depth=kMaxDepth)
Definition canvas.cc:839
void DrawOval(const Rect &rect, const Paint &paint)
Definition canvas.cc:512
void RestoreToCount(size_t count)
Definition canvas.cc:333
void ClipPath(const Path &path, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition canvas.cc:587
virtual void AddClipEntityToCurrentPass(Entity entity)
Definition canvas.cc:835
size_t GetClipHeight() const
Definition canvas.cc:823
void ClipRRect(const Rect &rect, const Size &corner_radii, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition canvas.cc:638
virtual bool Restore()
Definition canvas.cc:257
size_t GetSaveCount() const
Definition canvas.cc:329
void Concat(const Matrix &transform)
Definition canvas.cc:280
void Transform(const Matrix &transform)
Definition canvas.cc:292
std::optional< Rect > initial_cull_rect_
Definition canvas.h:188
void PreConcat(const Matrix &transform)
Definition canvas.cc:284
void DrawImage(const std::shared_ptr< Image > &image, Point offset, const Paint &paint, SamplerDescriptor sampler={})
Definition canvas.cc:750
void Rotate(Radians radians)
Definition canvas.cc:325
std::deque< CanvasStackEntry > transform_stack_
Definition canvas.h:187
void ResetTransform()
Definition canvas.cc:288
virtual void DrawTextFrame(const std::shared_ptr< TextFrame > &text_frame, Point position, const Paint &paint)
Definition canvas.cc:875
virtual ~Canvas()
void DrawPaint(const Paint &paint)
Definition canvas.cc:350
void ClipRect(const Rect &rect, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition canvas.cc:597
void Skew(Scalar sx, Scalar sy)
Definition canvas.cc:321
uint64_t current_depth_
Definition canvas.h:189
void Scale(const Vector2 &scale)
Definition canvas.cc:313
const std::optional< Rect > GetCurrentLocalCullingBounds() const
Definition canvas.cc:300
Picture EndRecordingAsPicture()
Definition canvas.cc:802
void DrawPath(const Path &path, const Paint &paint)
Definition canvas.cc:341
virtual void Save(uint32_t total_content_depth=kMaxDepth)
Definition canvas.cc:184
void DrawPoints(std::vector< Point > points, Scalar radius, const Paint &paint, PointStyle point_style)
Definition canvas.cc:731
void ClipOval(const Rect &bounds, Entity::ClipOperation clip_op=Entity::ClipOperation::kIntersect)
Definition canvas.cc:618
void DrawRect(const Rect &rect, const Paint &paint)
Definition canvas.cc:493
void DrawRRect(const Rect &rect, const Size &corner_radii, const Paint &paint)
Definition canvas.cc:538
virtual void AddRenderEntityToCurrentPass(Entity entity, bool reuse_depth=false)
Definition canvas.cc:827
void DrawAtlas(const std::shared_ptr< Image > &atlas, std::vector< Matrix > transforms, std::vector< Rect > texture_coordinates, std::vector< Color > colors, BlendMode blend_mode, SamplerDescriptor sampler, std::optional< Rect > cull_rect, const Paint &paint)
Definition canvas.cc:1003
void Translate(const Vector3 &offset)
Definition canvas.cc:309
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition canvas.cc:564
struct impeller::Canvas::DebugOptions debug_options
std::shared_ptr< ColorSourceContents > GetContents(const Paint &paint) const
void SetRequiredMipCount(int32_t mip_count)
void SetClipDepth(size_t clip_depth)
void AddEntity(Entity entity)
Add an entity to the current entity pass.
EntityPass * GetSuperpass() const
void PopClips(size_t num_clips, uint64_t depth)
int32_t GetRequiredMipCount() const
void SetClipHeight(size_t clip_height)
void SetTransform(Matrix transform)
void PopAllClips(uint64_t depth)
EntityPass * AddSubpass(std::unique_ptr< EntityPass > pass)
Appends a given pass as a subpass.
void PushClip(Entity entity)
std::function< std::shared_ptr< FilterContents >(FilterInput::Ref, const Matrix &effect_transform, Entity::RenderingMode rendering_mode)> BackdropFilterProc
Definition entity_pass.h:61
void SetTransform(const Matrix &transform)
Set the global transform matrix for this Entity.
Definition entity.cc:62
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
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
static std::shared_ptr< Geometry > MakeCover()
Definition geometry.cc:85
static std::shared_ptr< Geometry > MakeOval(const Rect &rect)
Definition geometry.cc:93
static std::shared_ptr< Geometry > MakeLine(const Point &p0, const Point &p1, Scalar width, Cap cap)
Definition geometry.cc:97
static std::shared_ptr< Geometry > MakeCircle(const Point &center, Scalar radius)
Definition geometry.cc:104
static std::shared_ptr< Geometry > MakeRect(const Rect &rect)
Definition geometry.cc:89
static std::shared_ptr< Geometry > MakePointField(std::vector< Point > points, Scalar radius, bool round)
Definition geometry.cc:66
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 > MakeStrokedCircle(const Point &center, Scalar radius, Scalar stroke_width)
Definition geometry.cc:109
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)
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)
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition path.h:51
static std::shared_ptr< TextureContents > MakeRect(Rect destination)
A common case factory that marks the texture contents as having a destination rectangle....
const Paint & paint
sk_sp< SkImage > image
Definition examples.cpp:29
SkBitmap source
Definition examples.cpp:28
#define FML_CHECK(condition)
Definition logging.h:85
#define FML_DCHECK(condition)
Definition logging.h:103
FlTexture * texture
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
float Scalar
Definition scalar.h:18
SourceRectConstraint
Controls the behavior of the source rectangle given to DrawImageRect.
Definition canvas.h:51
@ kStrict
Sample only within the source rectangle. May be slower.
static bool UseColorSourceContents(const std::shared_ptr< VerticesGeometry > &vertices, const Paint &paint)
Definition canvas.cc:901
constexpr float kEhCloseEnough
Definition constants.h:56
TRect< Scalar > Rect
Definition rect.h:746
PointStyle
Definition canvas.h:42
@ kRound
Points are drawn as squares.
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...
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
const Scalar scale
Point offset
std::optional< Rect > cull_rect
Definition canvas.h:34
static constexpr Color White()
Definition color.h:256
constexpr Color WithAlpha(Scalar new_alpha) const
Definition color.h:270
std::shared_ptr< Texture > texture
Entity::TileMode y_tile_mode
Entity::TileMode x_tile_mode
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
Matrix Invert() const
Definition matrix.cc:97
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:56
std::shared_ptr< ColorFilter > GetColorFilter() const
Definition paint.cc:213
bool HasColorFilter() const
Whether this paint has a color filter that can apply opacity.
Definition paint.cc:227
Cap stroke_cap
Definition paint.h:60
Join stroke_join
Definition paint.h:61
Scalar stroke_miter
Definition paint.h:62
Style style
Definition paint.h:63
std::shared_ptr< ImageFilter > image_filter
Definition paint.h:67
std::optional< MaskBlurDescriptor > mask_blur_descriptor
Definition paint.h:69
Scalar stroke_width
Definition paint.h:59
constexpr auto GetBottom() const
Definition rect.h:324
constexpr auto GetTop() const
Definition rect.h:320
constexpr auto GetLeft() const
Definition rect.h:318
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition rect.h:140
constexpr auto GetRight() const
Definition rect.h:322
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:146
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
#define TRACE_EVENT0(category_group, name)