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