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