Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
dl_builder.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
18#include "fml/logging.h"
19
20namespace flutter {
21
22namespace {
23
24// Returns a stroked RoundRect that is equivalent to a provided DiffRoundRect,
25// if one exists.
26//
27// A DiffRoundRect has an equivalent stroked RoundRect if all of the following
28// conditions are met:
29// - Its paint style is kFill.
30// - The bounds of its inner RoundRect is equal to the bounds of its outer
31// RoundRect inset by the same amount on each side. This inset amount is
32// the stroke width of the equivalent stroked RoundRect.
33// - Its outer and inner RoundRects both have circular corners.
34// - Each corner radius of the inner RoundRect is equal to the corresponding
35// outer RoundRect corner radius subtracting the stroke width.
36//
37// If all conditions are met, the equivalent stroked RoundRect is created by
38// expanding the inner RoundRect's sides and corner radii by half the stroke
39// width. (Or equivalently by insetting the outer RoundRect's sides and corner
40// radii by half the stroke width.)
41std::optional<std::pair<DlRoundRect, DlPaint>> DiffRoundRectToRoundRect(
42 const DlRoundRect& outer,
43 const DlRoundRect& inner,
44 const DlPaint& paint) {
45 if (paint.getDrawStyle() != DlDrawStyle::kFill) {
46 return std::nullopt;
47 }
48
49 // The stroke width is equal to the inset of the inner bounds from the outer
50 // bounds on each side. Arbitrarily pick the left side to initialize
51 // stroke_width.
52 const DlRect& outer_bounds = outer.GetBounds();
53 const DlRect& inner_bounds = inner.GetBounds();
54 const DlScalar stroke_width = inner_bounds.GetLeft() - outer_bounds.GetLeft();
55
56 // There are behavior differences between DiffRoundRect and stroked RoundRect
57 // when the calculated stroke width is 0 or negative. It's not clear what the
58 // right behavior is for this case, but we exit here and don't return a
59 // RoundRect to preserve the existing DiffRoundRect behavior.
60 if (stroke_width <= 0) {
61 return std::nullopt;
62 }
63
64 // Verify the other sides are inset by the same amount.
65 if (!DlScalarNearlyEqual(inner_bounds.GetTop() - outer_bounds.GetTop(),
66 stroke_width) ||
67 !DlScalarNearlyEqual(outer_bounds.GetRight() - inner_bounds.GetRight(),
68 stroke_width) ||
69 !DlScalarNearlyEqual(outer_bounds.GetBottom() - inner_bounds.GetBottom(),
70 stroke_width)) {
71 return std::nullopt;
72 }
73
74 // Verify the outer and inner RoundRects have circular corners.
75 const DlRoundingRadii& outer_radii = outer.GetRadii();
76 const DlRoundingRadii& inner_radii = inner.GetRadii();
77 if (!outer_radii.AreAllCornersCircular() ||
78 !inner_radii.AreAllCornersCircular()) {
79 return std::nullopt;
80 }
81
82 // Verify the corner radii are consistent with the stroke width.
84 outer_radii.top_left.width - inner_radii.top_left.width,
85 stroke_width) ||
87 outer_radii.top_right.width - inner_radii.top_right.width,
88 stroke_width) ||
90 outer_radii.bottom_left.width - inner_radii.bottom_left.width,
91 stroke_width) ||
93 outer_radii.bottom_right.width - inner_radii.bottom_right.width,
94 stroke_width)) {
95 return std::nullopt;
96 }
97
98 DlPaint stroke_paint = paint;
100 stroke_paint.setStrokeWidth(stroke_width);
101
102 const DlScalar half_stroke_width = stroke_width * 0.5f;
103 const DlRoundRect stroked_rrect = DlRoundRect::MakeRectRadii(
104 inner_bounds.Expand(half_stroke_width),
105 {
106 DlSize(inner_radii.top_left.width + half_stroke_width),
107 DlSize(inner_radii.top_right.width + half_stroke_width),
108 DlSize(inner_radii.bottom_left.width + half_stroke_width),
109 DlSize(inner_radii.bottom_right.width + half_stroke_width),
110 });
111
112 return std::make_pair(stroked_rrect, stroke_paint);
113}
114
115} // namespace
116
117// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst.
118static void CopyV(void* dst) {}
119
120template <typename S, typename... Rest>
121static void CopyV(void* dst, const S* src, int n, Rest&&... rest) {
122 FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0)
123 << "Expected " << dst << " to be aligned for at least " << alignof(S)
124 << " bytes.";
125 // If n is 0, there is nothing to copy into dst from src.
126 if (n > 0) {
127 memcpy(dst, src, n * sizeof(S));
128 dst = reinterpret_cast<void*>(reinterpret_cast<uint8_t*>(dst) +
129 n * sizeof(S));
130 }
131 // Repeat for the next items, if any
132 CopyV(dst, std::forward<Rest>(rest)...);
133}
134
135template <typename T, typename... Args>
136void* DisplayListBuilder::Push(size_t pod, Args&&... args) {
137 // Plan out where and how large a space we need
138 size_t size = SkAlignPtr(sizeof(T) + pod);
139 size_t offset = storage_.size();
140
141 // Allocate the space
142 auto ptr = storage_.allocate(size);
143 FML_CHECK(ptr);
144
145 // Initialize the space via the constructor
146 auto op = reinterpret_cast<T*>(ptr);
147 new (op) T{std::forward<Args>(args)...};
148 FML_DCHECK(op->type == T::kType);
149
150 // Adjust the counters and offsets (the memory is mostly initialized
151 // at this point except that the caller might do some pod-based copying
152 // past the end of the DlOp structure itself when we return)
153 offsets_.push_back(offset);
154 render_op_count_ += T::kRenderOpInc;
155 depth_ += T::kDepthInc * render_op_depth_cost_;
156 op_index_++;
157
158 return op + 1;
159}
160
161sk_sp<DisplayList> DisplayListBuilder::Build() {
162 while (save_stack_.size() > 1) {
163 restore();
164 }
165
166 int count = render_op_count_;
167 size_t nested_bytes = nested_bytes_;
168 int nested_count = nested_op_count_;
169 uint32_t total_depth = depth_;
170 bool opacity_compatible = current_layer().is_group_opacity_compatible();
171 bool is_safe = is_ui_thread_safe_;
172 bool affects_transparency = current_layer().affects_transparent_layer;
173 bool root_has_backdrop_filter = current_layer().contains_backdrop_filter;
174 bool root_is_unbounded = current_layer().is_unbounded;
175 DlBlendMode max_root_blend_mode = current_layer().max_blend_mode;
176
177 sk_sp<DlRTree> rtree;
178 DlRect bounds;
179 if (rtree_data_.has_value()) {
180 auto& rects = rtree_data_->rects;
181 auto& indices = rtree_data_->indices;
182 rtree = sk_make_sp<DlRTree>(rects.data(), rects.size(), indices.data(),
183 [](int id) { return id >= 0; });
184 // RTree bounds may be tighter due to applying filter bounds
185 // adjustments to each op as we restore layers rather than to
186 // the entire layer bounds.
187 bounds = rtree->bounds();
188 rtree_data_.reset();
189 } else {
190 bounds = current_layer().global_space_accumulator.GetBounds();
191 }
192
193 render_op_count_ = op_index_ = 0;
194 nested_bytes_ = nested_op_count_ = 0;
195 depth_ = 0;
196 is_ui_thread_safe_ = true;
197 current_opacity_compatibility_ = true;
198 render_op_depth_cost_ = 1u;
199 current_ = DlPaint();
200
201 save_stack_.pop_back();
202 Init(rtree != nullptr);
203
204 storage_.trim();
205 DisplayListStorage storage;
206 std::vector<size_t> offsets;
207 std::swap(offsets, offsets_);
208 std::swap(storage, storage_);
209
210 return sk_sp<DisplayList>(new DisplayList(
211 std::move(storage), std::move(offsets), count, nested_bytes, nested_count,
212 total_depth, bounds, opacity_compatible, is_safe, affects_transparency,
213 max_root_blend_mode, root_has_backdrop_filter, root_is_unbounded,
214 std::move(rtree)));
215}
216
217static constexpr DlRect kEmpty = DlRect();
218
219static const DlRect& ProtectEmpty(const DlRect& rect) {
220 // isEmpty protects us against NaN while we normalize any empty cull rects
221 return rect.IsEmpty() ? kEmpty : rect;
222}
223
225 bool prepare_rtree)
226 : original_cull_rect_(ProtectEmpty(cull_rect)) {
227 Init(prepare_rtree);
228}
229
230void DisplayListBuilder::Init(bool prepare_rtree) {
231 FML_DCHECK(save_stack_.empty());
232 FML_DCHECK(!rtree_data_.has_value());
233
234 save_stack_.emplace_back(original_cull_rect_);
235 current_info().is_nop = original_cull_rect_.IsEmpty();
236 if (prepare_rtree) {
237 rtree_data_.emplace();
238 }
239}
240
242 DisplayList::DisposeOps(storage_, offsets_);
243}
244
246 return offsets_.size();
247}
248
250 return offsets_.empty();
251}
252
254 return DlIRect::RoundOut(original_cull_rect_).GetSize();
255}
256
259 return SkImageInfo::MakeUnknown(size.width, size.height);
260}
261
262void DisplayListBuilder::onSetAntiAlias(bool aa) {
263 current_.setAntiAlias(aa);
264 Push<SetAntiAliasOp>(0, aa);
265}
266void DisplayListBuilder::onSetInvertColors(bool invert) {
267 current_.setInvertColors(invert);
268 Push<SetInvertColorsOp>(0, invert);
269 UpdateCurrentOpacityCompatibility();
270}
271void DisplayListBuilder::onSetStrokeCap(DlStrokeCap cap) {
272 current_.setStrokeCap(cap);
273 Push<SetStrokeCapOp>(0, cap);
274}
275void DisplayListBuilder::onSetStrokeJoin(DlStrokeJoin join) {
276 current_.setStrokeJoin(join);
277 Push<SetStrokeJoinOp>(0, join);
278}
279void DisplayListBuilder::onSetDrawStyle(DlDrawStyle style) {
280 current_.setDrawStyle(style);
281 Push<SetStyleOp>(0, style);
282}
283void DisplayListBuilder::onSetStrokeWidth(float width) {
284 current_.setStrokeWidth(width);
285 Push<SetStrokeWidthOp>(0, width);
286}
287void DisplayListBuilder::onSetStrokeMiter(float limit) {
288 current_.setStrokeMiter(limit);
289 Push<SetStrokeMiterOp>(0, limit);
290}
291void DisplayListBuilder::onSetColor(DlColor color) {
292 current_.setColor(color);
293 Push<SetColorOp>(0, color);
294}
295void DisplayListBuilder::onSetBlendMode(DlBlendMode mode) {
296 current_.setBlendMode(mode);
297 Push<SetBlendModeOp>(0, mode);
298 UpdateCurrentOpacityCompatibility();
299}
300
301void DisplayListBuilder::onSetColorSource(const DlColorSource* source) {
302 if (source == nullptr) {
303 current_.setColorSource(nullptr);
304 Push<ClearColorSourceOp>(0);
305 } else {
306 current_.setColorSource(source->shared());
307 is_ui_thread_safe_ = is_ui_thread_safe_ && source->isUIThreadSafe();
308 switch (source->type()) {
310 const DlImageColorSource* image_source = source->asImage();
311 FML_DCHECK(image_source);
312 Push<SetImageColorSourceOp>(0, image_source);
313 break;
314 }
316 const DlLinearGradientColorSource* linear = source->asLinearGradient();
317 FML_DCHECK(linear);
318 void* pod = Push<SetPodColorSourceOp>(linear->size());
319 new (pod) DlLinearGradientColorSource(linear);
320 break;
321 }
323 const DlRadialGradientColorSource* radial = source->asRadialGradient();
324 FML_DCHECK(radial);
325 void* pod = Push<SetPodColorSourceOp>(radial->size());
326 new (pod) DlRadialGradientColorSource(radial);
327 break;
328 }
330 const DlConicalGradientColorSource* conical =
331 source->asConicalGradient();
332 FML_DCHECK(conical);
333 void* pod = Push<SetPodColorSourceOp>(conical->size());
334 new (pod) DlConicalGradientColorSource(conical);
335 break;
336 }
338 const DlSweepGradientColorSource* sweep = source->asSweepGradient();
339 FML_DCHECK(sweep);
340 void* pod = Push<SetPodColorSourceOp>(sweep->size());
341 new (pod) DlSweepGradientColorSource(sweep);
342 break;
343 }
345 const DlRuntimeEffectColorSource* effect = source->asRuntimeEffect();
346 FML_DCHECK(effect);
347 Push<SetRuntimeEffectColorSourceOp>(0, effect);
348 break;
349 }
350 }
351 }
352 UpdateCurrentOpacityCompatibility();
353}
354void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) {
355 if (filter == nullptr) {
356 current_.setImageFilter(nullptr);
357 Push<ClearImageFilterOp>(0);
358 } else {
359 current_.setImageFilter(filter->shared());
360 switch (filter->type()) {
362 const DlBlurImageFilter* blur_filter = filter->asBlur();
363 FML_DCHECK(blur_filter);
364 void* pod = Push<SetPodImageFilterOp>(blur_filter->size());
365 new (pod) DlBlurImageFilter(blur_filter);
366 break;
367 }
369 const DlDilateImageFilter* dilate_filter = filter->asDilate();
370 FML_DCHECK(dilate_filter);
371 void* pod = Push<SetPodImageFilterOp>(dilate_filter->size());
372 new (pod) DlDilateImageFilter(dilate_filter);
373 break;
374 }
376 const DlErodeImageFilter* erode_filter = filter->asErode();
377 FML_DCHECK(erode_filter);
378 void* pod = Push<SetPodImageFilterOp>(erode_filter->size());
379 new (pod) DlErodeImageFilter(erode_filter);
380 break;
381 }
383 const DlMatrixImageFilter* matrix_filter = filter->asMatrix();
384 FML_DCHECK(matrix_filter);
385 void* pod = Push<SetPodImageFilterOp>(matrix_filter->size());
386 new (pod) DlMatrixImageFilter(matrix_filter);
387 break;
388 }
393 Push<SetSharedImageFilterOp>(0, filter);
394 break;
395 }
396 }
397 }
398 UpdateCurrentOpacityCompatibility();
399}
400void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) {
401 if (filter == nullptr) {
402 current_.setColorFilter(nullptr);
403 Push<ClearColorFilterOp>(0);
404 } else {
405 current_.setColorFilter(filter->shared());
406 switch (filter->type()) {
408 const DlBlendColorFilter* blend_filter = filter->asBlend();
409 FML_DCHECK(blend_filter);
410 void* pod = Push<SetPodColorFilterOp>(blend_filter->size());
411 new (pod) DlBlendColorFilter(blend_filter);
412 break;
413 }
415 const DlMatrixColorFilter* matrix_filter = filter->asMatrix();
416 FML_DCHECK(matrix_filter);
417 void* pod = Push<SetPodColorFilterOp>(matrix_filter->size());
418 new (pod) DlMatrixColorFilter(matrix_filter);
419 break;
420 }
422 void* pod = Push<SetPodColorFilterOp>(filter->size());
423 new (pod) DlSrgbToLinearGammaColorFilter();
424 break;
425 }
427 void* pod = Push<SetPodColorFilterOp>(filter->size());
428 new (pod) DlLinearToSrgbGammaColorFilter();
429 break;
430 }
431 }
432 }
433 UpdateCurrentOpacityCompatibility();
434}
435void DisplayListBuilder::onSetMaskFilter(const DlMaskFilter* filter) {
436 if (filter == nullptr) {
437 current_.setMaskFilter(nullptr);
438 render_op_depth_cost_ = 1u;
439 Push<ClearMaskFilterOp>(0);
440 } else {
441 current_.setMaskFilter(filter->shared());
442 render_op_depth_cost_ = 2u;
443 switch (filter->type()) {
445 const DlBlurMaskFilter* blur_filter = filter->asBlur();
446 FML_DCHECK(blur_filter);
447 void* pod = Push<SetPodMaskFilterOp>(blur_filter->size());
448 new (pod) DlBlurMaskFilter(blur_filter);
449 break;
450 }
451 }
452 }
453}
454
455void DisplayListBuilder::SetAttributesFromPaint(
456 const DlPaint& paint,
457 const DisplayListAttributeFlags flags) {
458 if (flags.applies_anti_alias()) {
459 setAntiAlias(paint.isAntiAlias());
460 }
461 if (flags.applies_alpha_or_color()) {
462 setColor(paint.getColor());
463 }
464 if (flags.applies_blend()) {
465 setBlendMode(paint.getBlendMode());
466 }
467 if (flags.applies_style()) {
468 setDrawStyle(paint.getDrawStyle());
469 }
470 if (flags.is_stroked(paint.getDrawStyle())) {
471 setStrokeWidth(paint.getStrokeWidth());
472 setStrokeMiter(paint.getStrokeMiter());
473 setStrokeCap(paint.getStrokeCap());
474 setStrokeJoin(paint.getStrokeJoin());
475 }
476 if (flags.applies_shader()) {
477 setColorSource(paint.getColorSourcePtr());
478 }
479 if (flags.applies_color_filter()) {
480 setInvertColors(paint.isInvertColors());
481 setColorFilter(paint.getColorFilterPtr());
482 }
483 if (flags.applies_image_filter()) {
484 setImageFilter(paint.getImageFilterPtr());
485 }
486 if (flags.applies_mask_filter()) {
487 setMaskFilter(paint.getMaskFilterPtr());
488 }
489}
490
491void DisplayListBuilder::checkForDeferredSave() {
492 if (current_info().has_deferred_save_op) {
493 size_t save_offset = storage_.size();
494 Push<SaveOp>(0);
495 current_info().save_offset = save_offset;
496 current_info().save_depth = depth_;
497 current_info().has_deferred_save_op = false;
498 }
499}
500
502 bool was_nop = current_info().is_nop;
503 save_stack_.emplace_back(&current_info());
504 current_info().is_nop = was_nop;
505
506 FML_DCHECK(save_stack_.size() >= 2u);
507 FML_DCHECK(current_info().has_deferred_save_op);
508}
509
510void DisplayListBuilder::saveLayer(const DlRect& bounds,
511 const SaveLayerOptions in_options,
512 const DlImageFilter* backdrop,
513 std::optional<int64_t> backdrop_id) {
514 SaveLayerOptions options = in_options.without_optimizations();
518 OpResult result = PaintResult(current_, flags);
519 if (result == OpResult::kNoEffect) {
520 // If we can't render, whether because we were already in a no-render
521 // state from the parent or because our own attributes make us a nop,
522 // we can just simplify this whole layer to a regular save that has
523 // nop state. We need to have a SaveInfo for the eventual restore(),
524 // but no rendering ops should be accepted between now and then so
525 // it doesn't need any of the data associated with a layer SaveInfo.
526 Save();
527 current_info().is_nop = true;
528 return;
529 }
530
531 if (backdrop != nullptr) {
532 current_layer().contains_backdrop_filter = true;
533 }
534
535 // Snapshot these values before we do any work as we need the values
536 // from before the method was called, but some of the operations below
537 // might update them.
538 size_t save_offset = storage_.size();
539 uint32_t save_depth = depth_;
540
541 // A backdrop will affect up to the entire surface, bounded by the clip
542 bool will_be_unbounded = (backdrop != nullptr);
543 std::shared_ptr<DlImageFilter> filter;
544
545 if (options.renders_with_attributes()) {
546 if (!paint_nops_on_transparency()) {
547 // We will fill the clip of the outer layer when we restore.
548 will_be_unbounded = true;
549 }
550 filter = current_.getImageFilter();
551 CheckLayerOpacityCompatibility(true);
552 UpdateLayerResult(result, true);
553 } else {
554 CheckLayerOpacityCompatibility(false);
555 UpdateLayerResult(result, false);
556 }
557
558 // The actual flood of the outer layer clip will occur after the
559 // (eventual) corresponding restore is called, but rather than
560 // remember this information in the LayerInfo until the restore
561 // method is processed, we just mark the unbounded state up front.
562 // Another reason to accumulate the clip here rather than in
563 // restore is so that this savelayer will be tagged in the rtree
564 // with its full bounds and the right op_index so that it doesn't
565 // get culled during rendering.
566 if (will_be_unbounded) {
567 AccumulateUnbounded();
568 }
569
570 // Accumulate information for the SaveInfo we are about to push onto the
571 // stack.
572 {
573 size_t rtree_index =
574 rtree_data_.has_value() ? rtree_data_->rects.size() : 0u;
575
576 save_stack_.emplace_back(&current_info(), filter, rtree_index);
577 FML_DCHECK(current_info().is_save_layer);
578 FML_DCHECK(!current_info().is_nop);
579 FML_DCHECK(!current_info().has_deferred_save_op);
580 current_info().save_offset = save_offset;
581 current_info().save_depth = save_depth;
582
583 // If we inherit some culling bounds and we have a filter then we need
584 // to adjust them so that we cull for the correct input space for the
585 // output of the filter.
586 if (filter) {
587 DlRect outer_cull_rect =
588 current_info().global_state.GetDeviceCullCoverage();
589 DlMatrix matrix = current_info().global_state.matrix();
590
591 DlIRect output_bounds = DlIRect::RoundOut(outer_cull_rect);
592 DlIRect input_bounds;
593 if (filter->get_input_device_bounds(output_bounds, matrix,
594 input_bounds)) {
595 current_info().global_state.resetDeviceCullRect(
596 DlRect::Make(input_bounds));
597 } else {
598 // Filter could not make any promises about the bounds it needs to
599 // fill the output space, so we use a maximal rect to accumulate
600 // the layer bounds.
601 current_info().global_state.resetDeviceCullRect(kMaxCullRect);
602 }
603 }
604
605 // We always want to cull based on user provided bounds, though, as
606 // that is legacy behavior even if it doesn't always work precisely
607 // in a rotated or skewed coordinate system (but it will work
608 // conservatively).
609 if (in_options.bounds_from_caller()) {
610 current_info().global_state.clipRect(bounds, DlClipOp::kIntersect, false);
611 }
612 }
613
614 // Accumulate options to store in the SaveLayer op record.
615 {
616 DlRect record_bounds;
617 if (in_options.bounds_from_caller()) {
618 options = options.with_bounds_from_caller();
619 record_bounds = bounds;
620 } else {
621 FML_DCHECK(record_bounds.IsEmpty());
622 }
623
624 if (backdrop) {
625 Push<SaveLayerBackdropOp>(0, options, record_bounds, backdrop,
626 backdrop_id);
627 } else {
628 Push<SaveLayerOp>(0, options, record_bounds);
629 }
630 }
631
632 if (options.renders_with_attributes()) {
633 // |current_opacity_compatibility_| does not take an ImageFilter into
634 // account because an individual primitive with an ImageFilter can apply
635 // opacity on top of it. But, if the layer is applying the ImageFilter
636 // then it cannot pass the opacity on.
637 if (!current_opacity_compatibility_ || filter) {
638 UpdateLayerOpacityCompatibility(false);
639 }
640 }
641}
642void DisplayListBuilder::SaveLayer(const std::optional<DlRect>& bounds,
643 const DlPaint* paint,
644 const DlImageFilter* backdrop,
645 std::optional<int64_t> backdrop_id) {
646 SaveLayerOptions options;
647 DlRect temp_bounds;
648 if (bounds.has_value()) {
649 options = options.with_bounds_from_caller();
650 temp_bounds = *bounds;
651 } else {
652 FML_DCHECK(temp_bounds.IsEmpty());
653 }
654 if (paint != nullptr) {
655 options = options.with_renders_with_attributes();
656 SetAttributesFromPaint(*paint,
658 }
659 saveLayer(temp_bounds, options, backdrop, backdrop_id);
660}
661
663 if (save_stack_.size() <= 1) {
664 return;
665 }
666
667 if (!current_info().has_deferred_save_op) {
668 SaveOpBase* op = reinterpret_cast<SaveOpBase*>(storage_.base() +
669 current_info().save_offset);
670 FML_CHECK(op->type == DisplayListOpType::kSave ||
671 op->type == DisplayListOpType::kSaveLayer ||
672 op->type == DisplayListOpType::kSaveLayerBackdrop);
673
674 op->restore_index = op_index_;
675 op->total_content_depth = depth_ - current_info().save_depth;
676
677 if (current_info().is_save_layer) {
678 RestoreLayer();
679 }
680
681 // Wait until all outgoing bounds information for the saveLayer is
682 // recorded before pushing the record to the buffer so that any rtree
683 // bounds will be attributed to the op_index of the restore op.
684 Push<RestoreOp>(0);
685 } else {
686 FML_DCHECK(!current_info().is_save_layer);
687 }
688
689 save_stack_.pop_back();
690}
691
692void DisplayListBuilder::RestoreLayer() {
693 FML_DCHECK(save_stack_.size() > 1);
694 FML_DCHECK(current_info().is_save_layer);
695 FML_DCHECK(!current_info().has_deferred_save_op);
696
697 // A saveLayer will usually do a final copy to the main buffer in
698 // addition to its content, but that is accounted for outside of
699 // the total content depth computed above in Restore.
700 depth_ += render_op_depth_cost_;
701
702 DlRect content_bounds = current_layer().layer_local_accumulator.GetBounds();
703
704 SaveLayerOpBase* layer_op = reinterpret_cast<SaveLayerOpBase*>(
705 storage_.base() + current_info().save_offset);
706 FML_CHECK(layer_op->type == DisplayListOpType::kSaveLayer ||
707 layer_op->type == DisplayListOpType::kSaveLayerBackdrop);
708
709 if (layer_op->options.bounds_from_caller()) {
710 DlRect user_bounds = layer_op->rect;
711 if (!content_bounds.IsEmpty() && !user_bounds.Contains(content_bounds)) {
712 layer_op->options = layer_op->options.with_content_is_clipped();
713 content_bounds = content_bounds.IntersectionOrEmpty(user_bounds);
714 }
715 }
716 layer_op->rect = content_bounds;
717 layer_op->max_blend_mode = current_layer().max_blend_mode;
718
719 if (current_layer().contains_backdrop_filter) {
720 layer_op->options = layer_op->options.with_contains_backdrop_filter();
721 }
722
723 if (current_layer().is_group_opacity_compatible()) {
724 layer_op->options = layer_op->options.with_can_distribute_opacity();
725 }
726
727 if (current_layer().is_unbounded) {
728 layer_op->options = layer_op->options.with_content_is_unbounded();
729 }
730
731 // Ensure that the bounds transferred in the following call will be
732 // attributed to the index of the restore op.
733 FML_DCHECK(layer_op->restore_index == op_index_);
734 TransferLayerBounds(content_bounds);
735}
736
737// There are a few different conditions and corresponding operations to
738// consider when transferring bounds from one layer to another. The current
739// layer will have accumulated its bounds into 2 potential places:
740//
741// - Its own private layer local bounds, which were potentially clipped by
742// the supplied bounds and passed here as the content_bounds.
743//
744// - Either the rtree rect list, or the global space accumulator, one or
745// the other.
746//
747// If there is no filter then the private layer bounds are complete and
748// they simply need to be passed along to the parent into its layer local
749// accumulator. Also, if there was no filter then the existing bounds
750// recorded in either the rtree rects or the layer's global space accumulator
751// (shared with its parent) need no updating so no global space transfer
752// has to occur.
753//
754// If there is a filter then the global content bounds will need to be
755// adjusted in one of two ways (rtree vs non-rtree):
756//
757// - If we are accumulating rtree rects then each of the rects accumulated
758// during this current layer will need to be updated by the filter in the
759// global coordinate space in which they were accumulated. In this mode
760// we should never have a global space accumulator on the layer.
761//
762// - Otherwise we were accumulating global bounds into our own private
763// global space accumulator which need to be adjusted in the global space
764// coordinate system by the filter.
765//
766// Finally, we will have to adjust the layer's content bounds by the filter
767// and accumulate those into the parent layer's local bounds.
768void DisplayListBuilder::TransferLayerBounds(const DlRect& content_bounds) {
769 auto& filter = current_layer().filter;
770
771 if (!filter) {
772 // We either accumulate global bounds into the rtree_data if there
773 // is one, or into the global_space_accumulator, but not both.
774 FML_DCHECK(!rtree_data_.has_value() ||
775 current_layer().global_space_accumulator.is_empty());
776
777 parent_info().AccumulateBoundsLocal(content_bounds);
778 parent_layer().global_space_accumulator.accumulate(
779 current_layer().global_space_accumulator);
780 return;
781 }
782
783 bool parent_is_flooded = false;
784 DlRect bounds_for_parent = content_bounds;
785
786 // First, let's adjust or transfer the global/rtree bounds by the filter.
787
788 // Matrix and Clip for the filter adjustment are the global values from
789 // just before our saveLayer and should still be the current values
790 // present in the parent layer.
791 const DlRect clip = parent_info().global_state.GetDeviceCullCoverage();
792 const DlMatrix matrix = parent_info().global_state.matrix();
793
794 if (rtree_data_.has_value()) {
795 // Neither current or parent layer should have any global bounds in
796 // their accumulator
797 FML_DCHECK(current_layer().global_space_accumulator.is_empty());
798 FML_DCHECK(parent_layer().global_space_accumulator.is_empty());
799
800 // The rtree rects were accumulated without the bounds modification of
801 // the filter applied to the layer so they may fail to trigger on a
802 // culled dispatch if their filter "fringes" are in the dispatch scope
803 // but their base rendering bounds are not. (Also, they will not
804 // contribute fully when we compute the overall bounds of this DL.)
805 //
806 // To make sure they are rendered in the culled dispatch situation, we
807 // revisit all of the RTree rects accumulated during the current layer
808 // (indicated by rtree_rects_start_index) and expand them by the filter.
809
810 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
811 if (AdjustRTreeRects(rtree_data_.value(), *filter, matrix, clip,
812 current_layer().rtree_rects_start_index)) {
813 parent_is_flooded = true;
814 }
815 } else {
816 DlRect global_bounds = current_layer().global_space_accumulator.GetBounds();
817 if (!global_bounds.IsEmpty()) {
818 DlIRect global_ibounds = DlIRect::RoundOut(global_bounds);
819 if (!filter->map_device_bounds(global_ibounds, matrix, global_ibounds)) {
820 parent_is_flooded = true;
821 } else {
822 global_bounds = DlRect::Make(global_ibounds);
823 std::optional<DlRect> clipped_bounds = global_bounds.Intersection(clip);
824 if (clipped_bounds.has_value()) {
825 parent_layer().global_space_accumulator.accumulate(
826 clipped_bounds.value());
827 }
828 }
829 }
830 }
831
832 // Now we visit the layer bounds which are in the layer's local coordinate
833 // system must be accumulated into the parent layer's bounds while
834 // adjusting them by the layer's local coordinate system (handled by the
835 // Accumulate() methods).
836
837 // A filter will happily adjust empty bounds to be non-empty, so we
838 // specifically avoid that case here. Also, if we are already planning
839 // to flood the parent due to any of the cases above, we don't need to
840 // run the filter on the content bounds only to discover the same
841 // condition.
842 if (!parent_is_flooded && !bounds_for_parent.IsEmpty()) {
843 DlRect mappable_bounds = bounds_for_parent;
844 if (filter->map_local_bounds(mappable_bounds, mappable_bounds)) {
845 bounds_for_parent = mappable_bounds;
846 } else {
847 parent_is_flooded = true;
848 }
849 }
850
851 if (parent_is_flooded) {
852 // All of the above computations deferred the flooded parent status
853 // to here. We need to mark the parent as flooded in both its layer
854 // and global accumulators. Note that even though the rtree rects
855 // were expanded to the size of the clip above, this method will still
856 // add one more rect to the rtree with the op index of the restore
857 // command to prevent the saveLayer itself from being elided in the
858 // rare case that there are no rendering ops in it, or somehow none
859 // of them were chosen by the rtree search (unlikely). The saveLayer
860 // must be processed for the parent flood to happen.
861 AccumulateUnbounded(parent_info());
862 } else {
863 parent_info().AccumulateBoundsLocal(bounds_for_parent);
864 }
865}
866
867bool DisplayListBuilder::AdjustRTreeRects(RTreeData& data,
868 const DlImageFilter& filter,
869 const DlMatrix& matrix,
870 const DlRect& clip,
871 size_t rect_start_index) {
872 auto& rects = data.rects;
873 auto& indices = data.indices;
874 FML_DCHECK(rects.size() == indices.size());
875 int ret = false;
876 auto rect_keep = rect_start_index;
877 for (size_t i = rect_start_index; i < rects.size(); i++) {
878 DlRect bounds = rects[i];
879 DlIRect ibounds = DlIRect::RoundOut(bounds);
880 if (filter.map_device_bounds(ibounds, matrix, ibounds)) {
881 bounds = DlRect::Make(ibounds);
882 } else {
883 bounds = clip;
884 ret = true;
885 }
886 auto clipped_bounds = bounds.Intersection(clip);
887 if (clipped_bounds.has_value()) {
888 indices[rect_keep] = indices[i];
889 rects[rect_keep] = clipped_bounds.value();
890 rect_keep++;
891 }
892 }
893 indices.resize(rect_keep);
894 rects.resize(rect_keep);
895 return ret;
896}
897
898void DisplayListBuilder::RestoreToCount(int restore_count) {
899 FML_DCHECK(restore_count <= GetSaveCount());
900 while (restore_count < GetSaveCount() && GetSaveCount() > 1) {
901 restore();
902 }
903}
904
906 if (std::isfinite(tx) && std::isfinite(ty) && (tx != 0.0 || ty != 0.0)) {
907 checkForDeferredSave();
908 Push<TranslateOp>(0, tx, ty);
909 global_state().translate(tx, ty);
910 layer_local_state().translate(tx, ty);
911 }
912}
914 if (std::isfinite(sx) && std::isfinite(sy) && (sx != 1.0 || sy != 1.0)) {
915 checkForDeferredSave();
916 Push<ScaleOp>(0, sx, sy);
917 global_state().scale(sx, sy);
918 layer_local_state().scale(sx, sy);
919 }
920}
922 if (SkScalarMod(degrees, 360.0) != 0.0) {
923 checkForDeferredSave();
924 Push<RotateOp>(0, degrees);
925 global_state().rotate(DlDegrees(degrees));
926 layer_local_state().rotate(DlDegrees(degrees));
927 }
928}
930 if (std::isfinite(sx) && std::isfinite(sy) && (sx != 0.0 || sy != 0.0)) {
931 checkForDeferredSave();
932 Push<SkewOp>(0, sx, sy);
933 global_state().skew(sx, sy);
934 layer_local_state().skew(sx, sy);
935 }
936}
937
938// clang-format off
939
940// 2x3 2D affine subset of a 4x4 transform in row major order
942 DlScalar mxx, DlScalar mxy, DlScalar mxt,
943 DlScalar myx, DlScalar myy, DlScalar myt) {
944 if (std::isfinite(mxx) && std::isfinite(myx) &&
945 std::isfinite(mxy) && std::isfinite(myy) &&
946 std::isfinite(mxt) && std::isfinite(myt)) {
947 if (mxx == 1 && mxy == 0 &&
948 myx == 0 && myy == 1) {
949 Translate(mxt, myt);
950 } else {
951 checkForDeferredSave();
952 Push<Transform2DAffineOp>(0,
953 mxx, mxy, mxt,
954 myx, myy, myt);
955 global_state().transform2DAffine(mxx, mxy, mxt,
956 myx, myy, myt);
957 layer_local_state().transform2DAffine(mxx, mxy, mxt,
958 myx, myy, myt);
959 }
960 }
961}
962// full 4x4 transform in row major order
964 DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt,
965 DlScalar myx, DlScalar myy, DlScalar myz, DlScalar myt,
966 DlScalar mzx, DlScalar mzy, DlScalar mzz, DlScalar mzt,
967 DlScalar mwx, DlScalar mwy, DlScalar mwz, DlScalar mwt) {
968 if ( mxz == 0 &&
969 myz == 0 &&
970 mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 &&
971 mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) {
972 transform2DAffine(mxx, mxy, mxt,
973 myx, myy, myt);
974 } else if (std::isfinite(mxx) && std::isfinite(mxy) &&
975 std::isfinite(mxz) && std::isfinite(mxt) &&
976 std::isfinite(myx) && std::isfinite(myy) &&
977 std::isfinite(myz) && std::isfinite(myt) &&
978 std::isfinite(mzx) && std::isfinite(mzy) &&
979 std::isfinite(mzz) && std::isfinite(mzt) &&
980 std::isfinite(mwx) && std::isfinite(mwy) &&
981 std::isfinite(mwz) && std::isfinite(mwt)) {
982 checkForDeferredSave();
983 Push<TransformFullPerspectiveOp>(0,
984 mxx, mxy, mxz, mxt,
985 myx, myy, myz, myt,
986 mzx, mzy, mzz, mzt,
987 mwx, mwy, mwz, mwt);
988 global_state().transformFullPerspective(mxx, mxy, mxz, mxt,
989 myx, myy, myz, myt,
990 mzx, mzy, mzz, mzt,
991 mwx, mwy, mwz, mwt);
992 layer_local_state().transformFullPerspective(mxx, mxy, mxz, mxt,
993 myx, myy, myz, myt,
994 mzx, mzy, mzz, mzt,
995 mwx, mwy, mwz, mwt);
996 }
997}
998// clang-format on
1000 checkForDeferredSave();
1001 Push<TransformResetOp>(0);
1002
1003 // The matrices in layer_tracker_ and tracker_ are similar, but
1004 // start at a different base transform. The tracker_ potentially
1005 // has some number of transform operations on it that prefix the
1006 // operations accumulated in layer_tracker_. So we can't set them both
1007 // to identity in parallel as they would no longer maintain their
1008 // relationship to each other.
1009 // Instead we reinterpret this operation as transforming by the
1010 // inverse of the current transform. Doing so to tracker_ sets it
1011 // to identity so we can avoid the math there, but we must do the
1012 // math the long way for layer_tracker_. This becomes:
1013 // layer_tracker_.transform(tracker_.inverse());
1014 if (!layer_local_state().inverseTransform(global_state())) {
1015 // If the inverse operation failed then that means that either
1016 // the matrix above the current layer was singular, or the matrix
1017 // became singular while we were accumulating the current layer.
1018 // In either case, we should no longer be accumulating any
1019 // contents so we set the layer tracking transform to a singular one.
1020 layer_local_state().setTransform(DlMatrix::MakeScale({0.0f, 0.0f, 0.0f}));
1021 }
1022
1023 global_state().setIdentity();
1024}
1027 matrix.e[0][0], matrix.e[1][0], matrix.e[2][0], matrix.e[3][0],
1028 matrix.e[0][1], matrix.e[1][1], matrix.e[2][1], matrix.e[3][1],
1029 matrix.e[0][2], matrix.e[1][2], matrix.e[2][2], matrix.e[3][2],
1030 matrix.e[0][3], matrix.e[1][3], matrix.e[2][3], matrix.e[3][3]);
1031}
1032
1034 DlClipOp clip_op,
1035 bool is_aa) {
1036 if (!rect.IsFinite()) {
1037 return;
1038 }
1039 if (current_info().is_nop) {
1040 return;
1041 }
1042 if (current_info().has_valid_clip && clip_op == DlClipOp::kIntersect &&
1043 layer_local_state().rect_covers_cull(rect)) {
1044 return;
1045 }
1046 global_state().clipRect(rect, clip_op, is_aa);
1047 layer_local_state().clipRect(rect, clip_op, is_aa);
1048 if (global_state().is_cull_rect_empty() ||
1049 layer_local_state().is_cull_rect_empty()) {
1050 current_info().is_nop = true;
1051 return;
1052 }
1053 current_info().has_valid_clip = true;
1054 checkForDeferredSave();
1055 switch (clip_op) {
1057 Push<ClipIntersectRectOp>(0, rect, is_aa);
1058 break;
1060 Push<ClipDifferenceRectOp>(0, rect, is_aa);
1061 break;
1062 }
1063}
1065 DlClipOp clip_op,
1066 bool is_aa) {
1067 if (!bounds.IsFinite()) {
1068 return;
1069 }
1070 if (current_info().is_nop) {
1071 return;
1072 }
1073 if (current_info().has_valid_clip && clip_op == DlClipOp::kIntersect &&
1074 layer_local_state().oval_covers_cull(bounds)) {
1075 return;
1076 }
1077 global_state().clipOval(bounds, clip_op, is_aa);
1078 layer_local_state().clipOval(bounds, clip_op, is_aa);
1079 if (global_state().is_cull_rect_empty() ||
1080 layer_local_state().is_cull_rect_empty()) {
1081 current_info().is_nop = true;
1082 return;
1083 }
1084 current_info().has_valid_clip = true;
1085 checkForDeferredSave();
1086 switch (clip_op) {
1088 Push<ClipIntersectOvalOp>(0, bounds, is_aa);
1089 break;
1091 Push<ClipDifferenceOvalOp>(0, bounds, is_aa);
1092 break;
1093 }
1094}
1096 DlClipOp clip_op,
1097 bool is_aa) {
1098 if (rrect.IsRect()) {
1099 ClipRect(rrect.GetBounds(), clip_op, is_aa);
1100 return;
1101 }
1102 if (rrect.IsOval()) {
1103 ClipOval(rrect.GetBounds(), clip_op, is_aa);
1104 return;
1105 }
1106 if (current_info().is_nop) {
1107 return;
1108 }
1109 if (current_info().has_valid_clip && clip_op == DlClipOp::kIntersect &&
1110 layer_local_state().rrect_covers_cull(rrect)) {
1111 return;
1112 }
1113 global_state().clipRRect(rrect, clip_op, is_aa);
1114 layer_local_state().clipRRect(rrect, clip_op, is_aa);
1115 if (global_state().is_cull_rect_empty() ||
1116 layer_local_state().is_cull_rect_empty()) {
1117 current_info().is_nop = true;
1118 return;
1119 }
1120 current_info().has_valid_clip = true;
1121 checkForDeferredSave();
1122 switch (clip_op) {
1124 Push<ClipIntersectRoundRectOp>(0, rrect, is_aa);
1125 break;
1127 Push<ClipDifferenceRoundRectOp>(0, rrect, is_aa);
1128 break;
1129 }
1130}
1132 DlClipOp clip_op,
1133 bool is_aa) {
1134 if (rse.IsRect()) {
1135 ClipRect(rse.GetBounds(), clip_op, is_aa);
1136 return;
1137 }
1138 if (rse.IsOval()) {
1139 ClipOval(rse.GetBounds(), clip_op, is_aa);
1140 return;
1141 }
1142 if (current_info().is_nop) {
1143 return;
1144 }
1145 if (current_info().has_valid_clip && clip_op == DlClipOp::kIntersect &&
1146 layer_local_state().rsuperellipse_covers_cull(rse)) {
1147 return;
1148 }
1149 global_state().clipRSuperellipse(rse, clip_op, is_aa);
1150 layer_local_state().clipRSuperellipse(rse, clip_op, is_aa);
1151 if (global_state().is_cull_rect_empty() ||
1152 layer_local_state().is_cull_rect_empty()) {
1153 current_info().is_nop = true;
1154 return;
1155 }
1156 current_info().has_valid_clip = true;
1157 checkForDeferredSave();
1158 switch (clip_op) {
1160 Push<ClipIntersectRoundSuperellipseOp>(0, rse, is_aa);
1161 break;
1163 Push<ClipDifferenceRoundSuperellipseOp>(0, rse, is_aa);
1164 break;
1165 }
1166}
1168 DlClipOp clip_op,
1169 bool is_aa) {
1170 if (current_info().is_nop) {
1171 return;
1172 }
1173 {
1174 DlRect rect;
1175 if (path.IsRect(&rect)) {
1176 ClipRect(rect, clip_op, is_aa);
1177 return;
1178 }
1179 if (path.IsOval(&rect)) {
1180 ClipOval(rect, clip_op, is_aa);
1181 return;
1182 }
1183 DlRoundRect rrect;
1184 if (path.IsRoundRect(&rrect)) {
1185 ClipRoundRect(rrect, clip_op, is_aa);
1186 return;
1187 }
1188 }
1189 global_state().clipPath(path, clip_op, is_aa);
1190 layer_local_state().clipPath(path, clip_op, is_aa);
1191 if (global_state().is_cull_rect_empty() ||
1192 layer_local_state().is_cull_rect_empty()) {
1193 current_info().is_nop = true;
1194 return;
1195 }
1196 current_info().has_valid_clip = true;
1197 checkForDeferredSave();
1198 switch (clip_op) {
1200 Push<ClipIntersectPathOp>(0, path, is_aa);
1201 break;
1203 Push<ClipDifferencePathOp>(0, path, is_aa);
1204 break;
1205 }
1206}
1207
1208bool DisplayListBuilder::QuickReject(const DlRect& bounds) const {
1209 return global_state().content_culled(bounds);
1210}
1211
1212void DisplayListBuilder::drawPaint() {
1213 OpResult result = PaintResult(current_, kDrawPaintFlags);
1214 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
1215 Push<DrawPaintOp>(0);
1216 CheckLayerOpacityCompatibility();
1217 UpdateLayerResult(result);
1218 }
1219}
1221 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPaintFlags);
1222 drawPaint();
1223}
1225 OpResult result = PaintResult(DlPaint(color).setBlendMode(mode));
1226 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
1227 Push<DrawColorOp>(0, color, mode);
1228 CheckLayerOpacityCompatibility(mode);
1229 UpdateLayerResult(result, mode);
1230 }
1231}
1232void DisplayListBuilder::drawLine(const DlPoint& p0, const DlPoint& p1) {
1233 DlRect bounds = DlRect::MakeLTRB(p0.x, p0.y, p1.x, p1.y).GetPositive();
1236 OpResult result = PaintResult(current_, flags);
1237 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1238 Push<DrawLineOp>(0, p0, p1);
1239 CheckLayerOpacityCompatibility();
1240 UpdateLayerResult(result);
1241 }
1242}
1244 const DlPoint& p1,
1245 const DlPaint& paint) {
1246 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags);
1247 drawLine(p0, p1);
1248}
1249void DisplayListBuilder::drawDashedLine(const DlPoint& p0,
1250 const DlPoint& p1,
1251 DlScalar on_length,
1252 DlScalar off_length) {
1253 DlRect bounds = DlRect::MakeLTRB(p0.x, p0.y, p1.x, p1.y).GetPositive();
1256 OpResult result = PaintResult(current_, flags);
1257 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1258 Push<DrawDashedLineOp>(0, p0, p1, on_length, off_length);
1259 CheckLayerOpacityCompatibility();
1260 UpdateLayerResult(result);
1261 }
1262}
1264 const DlPoint& p1,
1265 DlScalar on_length,
1266 DlScalar off_length,
1267 const DlPaint& paint) {
1268 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags);
1269 drawDashedLine(p0, p1, on_length, off_length);
1270}
1271void DisplayListBuilder::drawRect(const DlRect& rect) {
1273 OpResult result = PaintResult(current_, flags);
1274 if (result != OpResult::kNoEffect &&
1275 AccumulateOpBounds(rect.GetPositive(), flags)) {
1276 Push<DrawRectOp>(0, rect);
1277 CheckLayerOpacityCompatibility();
1278 UpdateLayerResult(result);
1279 }
1280}
1281void DisplayListBuilder::DrawRect(const DlRect& rect, const DlPaint& paint) {
1282 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRectFlags);
1283 drawRect(rect);
1284}
1285void DisplayListBuilder::drawOval(const DlRect& bounds) {
1287 OpResult result = PaintResult(current_, flags);
1288 if (result != OpResult::kNoEffect &&
1289 AccumulateOpBounds(bounds.GetPositive(), flags)) {
1290 Push<DrawOvalOp>(0, bounds);
1291 CheckLayerOpacityCompatibility();
1292 UpdateLayerResult(result);
1293 }
1294}
1295void DisplayListBuilder::DrawOval(const DlRect& bounds, const DlPaint& paint) {
1296 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawOvalFlags);
1297 drawOval(bounds);
1298}
1299void DisplayListBuilder::drawCircle(const DlPoint& center, DlScalar radius) {
1301 OpResult result = PaintResult(current_, flags);
1302 if (result != OpResult::kNoEffect) {
1303 DlRect bounds = DlRect::MakeCircleBounds(center, radius);
1304 if (AccumulateOpBounds(bounds, flags)) {
1305 Push<DrawCircleOp>(0, center, radius);
1306 CheckLayerOpacityCompatibility();
1307 UpdateLayerResult(result);
1308 }
1309 }
1310}
1312 DlScalar radius,
1313 const DlPaint& paint) {
1314 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawCircleFlags);
1315 drawCircle(center, radius);
1316}
1317void DisplayListBuilder::drawRoundRect(const DlRoundRect& rrect) {
1318 if (rrect.IsRect()) {
1319 drawRect(rrect.GetBounds());
1320 } else if (rrect.IsOval()) {
1321 drawOval(rrect.GetBounds());
1322 } else {
1323 DisplayListAttributeFlags flags = kDrawRRectFlags;
1324 OpResult result = PaintResult(current_, flags);
1325 if (result != OpResult::kNoEffect &&
1326 AccumulateOpBounds(rrect.GetBounds(), flags)) {
1327 Push<DrawRoundRectOp>(0, rrect);
1328 CheckLayerOpacityCompatibility();
1329 UpdateLayerResult(result);
1330 }
1331 }
1332}
1334 const DlPaint& paint) {
1335 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRRectFlags);
1336 drawRoundRect(rrect);
1337}
1338void DisplayListBuilder::drawDiffRoundRect(const DlRoundRect& outer,
1339 const DlRoundRect& inner) {
1341 OpResult result = PaintResult(current_, flags);
1342 if (result != OpResult::kNoEffect &&
1343 AccumulateOpBounds(outer.GetBounds(), flags)) {
1344 Push<DrawDiffRoundRectOp>(0, outer, inner);
1345 CheckLayerOpacityCompatibility();
1346 UpdateLayerResult(result);
1347 }
1348}
1350 const DlRoundRect& inner,
1351 const DlPaint& paint) {
1352 if (auto rrect_and_paint = DiffRoundRectToRoundRect(outer, inner, paint)) {
1353 DrawRoundRect(rrect_and_paint->first, rrect_and_paint->second);
1354 return;
1355 }
1356 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawDRRectFlags);
1357 drawDiffRoundRect(outer, inner);
1358}
1359void DisplayListBuilder::drawRoundSuperellipse(const DlRoundSuperellipse& rse) {
1360 if (rse.IsRect()) {
1361 drawRect(rse.GetBounds());
1362 } else if (rse.IsOval()) {
1363 drawOval(rse.GetBounds());
1364 } else {
1365 DisplayListAttributeFlags flags = kDrawRSuperellipseFlags;
1366 OpResult result = PaintResult(current_, flags);
1367 if (result != OpResult::kNoEffect &&
1368 AccumulateOpBounds(rse.GetBounds(), flags)) {
1369 // DrawRoundSuperellipseOp only supports filling. Anything related to
1370 // stroking must use path approximation.
1371 if (current_.getDrawStyle() == DlDrawStyle::kFill) {
1372 Push<DrawRoundSuperellipseOp>(0, rse);
1373 } else {
1374 DlPathBuilder builder;
1375 builder.AddRoundSuperellipse(DlRoundSuperellipse::MakeRectRadii(
1376 rse.GetBounds(), rse.GetRadii()));
1377 Push<DrawPathOp>(0, builder.TakePath());
1378 }
1379 CheckLayerOpacityCompatibility();
1380 UpdateLayerResult(result);
1381 }
1382 }
1383}
1385 const DlPaint& paint) {
1386 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRSuperellipseFlags);
1387 drawRoundSuperellipse(rse);
1388}
1389void DisplayListBuilder::drawPath(const DlPath& path) {
1391 OpResult result = PaintResult(current_, flags);
1392 if (result != OpResult::kNoEffect) {
1393 bool is_visible = AccumulateOpBounds(path.GetBounds(), flags);
1394 if (is_visible) {
1395 Push<DrawPathOp>(0, path);
1396 CheckLayerOpacityHairlineCompatibility();
1397 UpdateLayerResult(result);
1398 }
1399 }
1400}
1402 DlRect rect;
1403 bool closed;
1404 if (path.IsRect(&rect, &closed) &&
1405 (paint.getDrawStyle() == DlDrawStyle::kFill || closed)) {
1406 DrawRect(rect, paint);
1407 return;
1408 }
1409
1410 DlRoundRect rrect;
1411 if (path.IsRoundRect(&rrect)) {
1412 DrawRoundRect(rrect, paint);
1413 return;
1414 }
1415
1416 DlRect oval_bounds;
1417 if (path.IsOval(&oval_bounds)) {
1418 DrawOval(oval_bounds, paint);
1419 return;
1420 }
1421
1422 DlPoint start;
1423 DlPoint end;
1424 if (path.IsLine(&start, &end)) {
1425 DrawLine(start, end, paint);
1426 return;
1427 }
1428
1429 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPathFlags);
1430 drawPath(path);
1431}
1432
1433void DisplayListBuilder::drawArc(const DlRect& bounds,
1434 DlScalar start,
1435 DlScalar sweep,
1436 bool useCenter) {
1437 DisplayListAttributeFlags flags = //
1438 useCenter //
1441 OpResult result = PaintResult(current_, flags);
1442 // This could be tighter if we compute where the start and end
1443 // angles are and then also consider the quadrants swept and
1444 // the center if specified.
1445 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1446 Push<DrawArcOp>(0, bounds, start, sweep, useCenter);
1447 if (useCenter) {
1448 CheckLayerOpacityHairlineCompatibility();
1449 } else {
1450 CheckLayerOpacityCompatibility();
1451 }
1452 UpdateLayerResult(result);
1453 }
1454}
1456 DlScalar start,
1457 DlScalar sweep,
1458 bool useCenter,
1459 const DlPaint& paint) {
1460 SetAttributesFromPaint(
1461 paint, useCenter ? kDrawArcWithCenterFlags : kDrawArcNoCenterFlags);
1462 drawArc(bounds, start, sweep, useCenter);
1463}
1464
1465DisplayListAttributeFlags DisplayListBuilder::FlagsForPointMode(
1466 DlPointMode mode) {
1467 switch (mode) {
1474 }
1476}
1477void DisplayListBuilder::drawPoints(DlPointMode mode,
1478 uint32_t count,
1479 const DlPoint pts[]) {
1480 if (count == 0) {
1481 return;
1482 }
1483 DisplayListAttributeFlags flags = FlagsForPointMode(mode);
1484 OpResult result = PaintResult(current_, flags);
1485 if (result == OpResult::kNoEffect) {
1486 return;
1487 }
1488
1490 int bytes = count * sizeof(DlPoint);
1491 AccumulationRect accumulator;
1492 for (size_t i = 0; i < count; i++) {
1493 accumulator.accumulate(pts[i]);
1494 }
1495 if (!AccumulateOpBounds(accumulator.GetBounds(), flags)) {
1496 return;
1497 }
1498
1499 void* data_ptr;
1500 switch (mode) {
1502 data_ptr = Push<DrawPointsOp>(bytes, count);
1503 break;
1505 data_ptr = Push<DrawLinesOp>(bytes, count);
1506 break;
1508 data_ptr = Push<DrawPolygonOp>(bytes, count);
1509 break;
1510 default:
1512 return;
1513 }
1514 CopyV(data_ptr, pts, count);
1515 // drawPoints treats every point or line (or segment of a polygon)
1516 // as a completely separate operation meaning we cannot ensure
1517 // distribution of group opacity without analyzing the mode and the
1518 // bounds of every sub-primitive.
1519 // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c
1520 current_layer().layer_local_accumulator.record_overlapping_bounds();
1521 // Even though we've eliminated the possibility of opacity peephole
1522 // optimizations above, we still set the appropriate flags based on
1523 // the rendering attributes in case we solve the overlapping points
1524 // problem above.
1525 CheckLayerOpacityCompatibility();
1526 UpdateLayerResult(result);
1527}
1529 uint32_t count,
1530 const DlPoint pts[],
1531 const DlPaint& paint) {
1532 SetAttributesFromPaint(paint, FlagsForPointMode(mode));
1533 drawPoints(mode, count, pts);
1534}
1535void DisplayListBuilder::drawVertices(
1536 const std::shared_ptr<DlVertices>& vertices,
1537 DlBlendMode mode) {
1539 OpResult result = PaintResult(current_, flags);
1540 if (result != OpResult::kNoEffect &&
1541 AccumulateOpBounds(vertices->GetBounds(), flags)) {
1542 Push<DrawVerticesOp>(0, vertices, mode);
1543 // DrawVertices applies its colors to the paint so we have no way
1544 // of controlling opacity using the current paint attributes.
1545 // Although, examination of the |mode| might find some predictable
1546 // cases.
1547 UpdateLayerOpacityCompatibility(false);
1548 UpdateLayerResult(result);
1549 // Even though we already eliminated opacity peephole optimization
1550 // due to the color issues identified above, drawVertices also fails
1551 // based on the fact that the vertices are rendered independently
1552 // so we cannot guarantee the non-overlapping condition. We record
1553 // both conditions in case a solution is found to applying the
1554 // colors above - both conditions must be analyzed sufficiently
1555 // and implemented accordingly before drawVertices is compatible with
1556 // opacity peephole optimizations.
1557 current_layer().layer_local_accumulator.record_overlapping_bounds();
1558 }
1559}
1561 const std::shared_ptr<DlVertices>& vertices,
1562 DlBlendMode mode,
1563 const DlPaint& paint) {
1564 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawVerticesFlags);
1565 drawVertices(vertices, mode);
1566}
1567
1568void DisplayListBuilder::drawImage(const sk_sp<DlImage> image,
1569 const DlPoint& point,
1570 DlImageSampling sampling,
1571 bool render_with_attributes) {
1572 DisplayListAttributeFlags flags = render_with_attributes //
1575 OpResult result = PaintResult(current_, flags);
1576 if (result == OpResult::kNoEffect) {
1577 return;
1578 }
1579 DlRect bounds = DlRect::MakeXYWH(point.x, point.y, //
1580 image->width(), image->height());
1581 if (AccumulateOpBounds(bounds, flags)) {
1582 render_with_attributes
1583 ? Push<DrawImageWithAttrOp>(0, image, point, sampling)
1584 : Push<DrawImageOp>(0, image, point, sampling);
1585 CheckLayerOpacityCompatibility(render_with_attributes);
1586 UpdateLayerResult(result, render_with_attributes);
1587 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1588 }
1589}
1590void DisplayListBuilder::DrawImage(const sk_sp<DlImage>& image,
1591 const DlPoint& point,
1592 DlImageSampling sampling,
1593 const DlPaint* paint) {
1594 if (paint != nullptr) {
1595 SetAttributesFromPaint(*paint,
1597 drawImage(image, point, sampling, true);
1598 } else {
1599 drawImage(image, point, sampling, false);
1600 }
1601}
1602void DisplayListBuilder::drawImageRect(const sk_sp<DlImage> image,
1603 const DlRect& src,
1604 const DlRect& dst,
1605 DlImageSampling sampling,
1606 bool render_with_attributes,
1607 DlSrcRectConstraint constraint) {
1608 DisplayListAttributeFlags flags = render_with_attributes
1611 OpResult result = PaintResult(current_, flags);
1612 if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
1613 Push<DrawImageRectOp>(0, image, src, dst, sampling, render_with_attributes,
1614 constraint);
1615 CheckLayerOpacityCompatibility(render_with_attributes);
1616 UpdateLayerResult(result, render_with_attributes);
1617 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1618 }
1619}
1620void DisplayListBuilder::DrawImageRect(const sk_sp<DlImage>& image,
1621 const DlRect& src,
1622 const DlRect& dst,
1623 DlImageSampling sampling,
1624 const DlPaint* paint,
1625 DlSrcRectConstraint constraint) {
1626 if (paint != nullptr) {
1627 SetAttributesFromPaint(*paint,
1629 drawImageRect(image, src, dst, sampling, true, constraint);
1630 } else {
1631 drawImageRect(image, src, dst, sampling, false, constraint);
1632 }
1633}
1634void DisplayListBuilder::drawImageNine(const sk_sp<DlImage> image,
1635 const DlIRect& center,
1636 const DlRect& dst,
1637 DlFilterMode filter,
1638 bool render_with_attributes) {
1639 DisplayListAttributeFlags flags = render_with_attributes
1642 OpResult result = PaintResult(current_, flags);
1643 if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
1644 render_with_attributes
1645 ? Push<DrawImageNineWithAttrOp>(0, image, center, dst, filter)
1646 : Push<DrawImageNineOp>(0, image, center, dst, filter);
1647 CheckLayerOpacityCompatibility(render_with_attributes);
1648 UpdateLayerResult(result, render_with_attributes);
1649 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1650 }
1651}
1652void DisplayListBuilder::DrawImageNine(const sk_sp<DlImage>& image,
1653 const DlIRect& center,
1654 const DlRect& dst,
1655 DlFilterMode filter,
1656 const DlPaint* paint) {
1657 if (paint != nullptr) {
1658 SetAttributesFromPaint(*paint,
1660 drawImageNine(image, center, dst, filter, true);
1661 } else {
1662 drawImageNine(image, center, dst, filter, false);
1663 }
1664}
1665void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
1666 const DlRSTransform xform[],
1667 const DlRect tex[],
1668 const DlColor colors[],
1669 int count,
1670 DlBlendMode mode,
1671 DlImageSampling sampling,
1672 const DlRect* cull_rect,
1673 bool render_with_attributes) {
1674 DisplayListAttributeFlags flags = render_with_attributes //
1677 OpResult result = PaintResult(current_, flags);
1678 if (result == OpResult::kNoEffect) {
1679 return;
1680 }
1681 DlQuad quad;
1682 AccumulationRect accumulator;
1683 for (int i = 0; i < count; i++) {
1684 const DlRect& src = tex[i];
1685 xform[i].GetQuad(src.GetWidth(), src.GetHeight(), quad);
1686 for (int j = 0; j < 4; j++) {
1687 accumulator.accumulate(quad[j]);
1688 }
1689 }
1690 if (accumulator.is_empty() ||
1691 !AccumulateOpBounds(accumulator.GetBounds(), flags)) {
1692 return;
1693 }
1694
1695 // Accumulating the bounds might not trip the overlap condition if the
1696 // whole atlas operation is separated from other rendering calls, but
1697 // since each atlas op is treated as an independent operation, we have
1698 // to pass along our locally computed overlap condition for the individual
1699 // atlas operations to the layer accumulator.
1700 // Note that the above accumulation may falsely trigger the overlapping
1701 // state as it is done quad corner by quad corner and an entire quad may
1702 // be non-overlapping with the layer bounds, but as we add each point
1703 // independently it might expand the bounds on one corner and then flag
1704 // the condition when the next corner is added.
1705 if (accumulator.overlap_detected()) {
1706 current_layer().layer_local_accumulator.record_overlapping_bounds();
1707 }
1708
1709 int bytes = count * (sizeof(DlRSTransform) + sizeof(DlRect));
1710 void* data_ptr;
1711 if (colors != nullptr) {
1712 bytes += count * sizeof(DlColor);
1713 if (cull_rect != nullptr) {
1714 data_ptr =
1715 Push<DrawAtlasCulledOp>(bytes, atlas, count, mode, sampling, true,
1716 *cull_rect, render_with_attributes);
1717 } else {
1718 data_ptr = Push<DrawAtlasOp>(bytes, atlas, count, mode, sampling, true,
1719 render_with_attributes);
1720 }
1721 CopyV(data_ptr, xform, count, tex, count, colors, count);
1722 } else {
1723 if (cull_rect != nullptr) {
1724 data_ptr =
1725 Push<DrawAtlasCulledOp>(bytes, atlas, count, mode, sampling, false,
1726 *cull_rect, render_with_attributes);
1727 } else {
1728 data_ptr = Push<DrawAtlasOp>(bytes, atlas, count, mode, sampling, false,
1729 render_with_attributes);
1730 }
1731 CopyV(data_ptr, xform, count, tex, count);
1732 }
1733 // drawAtlas treats each image as a separate operation so we cannot rely
1734 // on it to distribute the opacity without overlap without checking all
1735 // of the transforms and texture rectangles.
1736 UpdateLayerOpacityCompatibility(false);
1737 UpdateLayerResult(result, render_with_attributes);
1738 is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe();
1739}
1740void DisplayListBuilder::DrawAtlas(const sk_sp<DlImage>& atlas,
1741 const DlRSTransform xform[],
1742 const DlRect tex[],
1743 const DlColor colors[],
1744 int count,
1745 DlBlendMode mode,
1746 DlImageSampling sampling,
1747 const DlRect* cull_rect,
1748 const DlPaint* paint) {
1749 if (paint != nullptr) {
1750 SetAttributesFromPaint(*paint,
1752 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1753 true);
1754 } else {
1755 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1756 false);
1757 }
1758}
1759
1760void DisplayListBuilder::DrawDisplayList(const sk_sp<DisplayList> display_list,
1761 DlScalar opacity) {
1762 if (!std::isfinite(opacity) || opacity <= SK_ScalarNearlyZero ||
1763 display_list->op_count() == 0 || display_list->GetBounds().IsEmpty() ||
1764 current_info().is_nop) {
1765 return;
1766 }
1767 const DlRect bounds = display_list->GetBounds();
1768 bool accumulated;
1769 sk_sp<const DlRTree> rtree;
1770 if (display_list->root_is_unbounded()) {
1771 accumulated = AccumulateUnbounded();
1772 } else if (!rtree_data_.has_value() || !(rtree = display_list->rtree())) {
1773 accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags);
1774 } else {
1775 std::list<DlRect> rects =
1776 rtree->searchAndConsolidateRects(GetLocalClipCoverage(), false);
1777 accumulated = false;
1778 for (const DlRect& rect : rects) {
1779 // TODO (https://github.com/flutter/flutter/issues/114919): Attributes
1780 // are not necessarily `kDrawDisplayListFlags`.
1781 if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) {
1782 accumulated = true;
1783 }
1784 }
1785 }
1786 if (!accumulated) {
1787 return;
1788 }
1789
1790 DlPaint current_paint = current_;
1791 Push<DrawDisplayListOp>(0, display_list,
1792 opacity < SK_Scalar1 ? opacity : SK_Scalar1);
1793
1794 // This depth increment accounts for every draw call in the child
1795 // DisplayList and is in addition to the implicit depth increment
1796 // that was performed when we pushed the DrawDisplayListOp. The
1797 // eventual dispatcher can use or ignore the implicit depth increment
1798 // as it sees fit depending on whether it needs to do rendering
1799 // before or after the drawDisplayList op, but it must be accounted
1800 // for if the depth value accounting is to remain consistent between
1801 // the recording and dispatching process.
1802 depth_ += display_list->total_depth();
1803
1804 is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe();
1805 // Not really necessary if the developer is interacting with us via
1806 // our attribute-state-less DlCanvas methods, but this avoids surprises
1807 // for those who may have been using the stateful Dispatcher methods.
1808 SetAttributesFromPaint(current_paint,
1810
1811 // The non-nested op count accumulated in the |Push| method will include
1812 // this call to |drawDisplayList| for non-nested op count metrics.
1813 // But, for nested op count metrics we want the |drawDisplayList| call itself
1814 // to be transparent. So we subtract 1 from our accumulated nested count to
1815 // balance out against the 1 that was accumulated into the regular count.
1816 // This behavior is identical to the way SkPicture computed nested op counts.
1817 nested_op_count_ += display_list->op_count(true) - 1;
1818 nested_bytes_ += display_list->bytes(true);
1819 UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity());
1820 // Nop DisplayLists are eliminated above so we either affect transparent
1821 // pixels or we do not. We should not have [kNoEffect].
1822 UpdateLayerResult(display_list->modifies_transparent_black()
1823 ? OpResult::kAffectsAll
1824 : OpResult::kPreservesTransparency,
1825 display_list->max_root_blend_mode());
1826 if (display_list->root_has_backdrop_filter()) {
1827 current_layer().contains_backdrop_filter = true;
1828 }
1829}
1830
1831void DisplayListBuilder::drawText(const std::shared_ptr<DlText>& text,
1832 DlScalar x,
1833 DlScalar y) {
1835 OpResult result = PaintResult(current_, flags);
1836 if (result == OpResult::kNoEffect) {
1837 return;
1838 }
1839 DlRect bounds = text->GetBounds().Shift(x, y);
1840 bool unclipped = AccumulateOpBounds(bounds, flags);
1841 // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the
1842 // unit tests can use Fuchsia's font manager instead of the empty default.
1843 // Until then we might encounter empty bounds for otherwise valid text and
1844 // thus we ignore the results from AccumulateOpBounds.
1845#if defined(OS_FUCHSIA)
1846 unclipped = true;
1847#endif // OS_FUCHSIA
1848 if (unclipped) {
1849 Push<DrawTextOp>(0, text, x, y);
1850 // There is no way to query if the glyphs of a text blob overlap and
1851 // there are no current guarantees from either Skia or Impeller that
1852 // they will protect overlapping glyphs from the effects of overdraw
1853 // so we must make the conservative assessment that this DL layer is
1854 // not compatible with group opacity inheritance.
1855 UpdateLayerOpacityCompatibility(false);
1856 UpdateLayerResult(result);
1857 }
1858}
1859
1860void DisplayListBuilder::DrawText(const std::shared_ptr<DlText>& text,
1861 DlScalar x,
1862 DlScalar y,
1863 const DlPaint& paint) {
1864 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextFlags);
1865 drawText(text, x, y);
1866}
1867
1869 const DlColor color,
1870 const DlScalar elevation,
1871 bool transparent_occluder,
1872 DlScalar dpr) {
1873 OpResult result = PaintResult(DlPaint(color));
1874 if (result != OpResult::kNoEffect) {
1875 DlRect shadow_bounds =
1876 DlCanvas::ComputeShadowBounds(path, elevation, dpr, GetMatrix());
1877 if (AccumulateOpBounds(shadow_bounds, kDrawShadowFlags)) {
1878 transparent_occluder //
1879 ? Push<DrawShadowTransparentOccluderOp>(0, path, color, elevation,
1880 dpr)
1881 : Push<DrawShadowOp>(0, path, color, elevation, dpr);
1882 UpdateLayerOpacityCompatibility(false);
1883 UpdateLayerResult(result, DlBlendMode::kSrcOver);
1884 }
1885 }
1886}
1887
1888bool DisplayListBuilder::AdjustBoundsForPaint(DlRect& bounds,
1890 if (flags.ignores_paint()) {
1891 return true;
1892 }
1893
1894 if (flags.is_geometric()) {
1895 bool is_stroked = flags.is_stroked(current_.getDrawStyle());
1896
1897 DisplayListSpecialGeometryFlags special_flags =
1898 flags.GeometryFlags(is_stroked);
1899
1900 if (is_stroked) {
1901 // Determine the max multiplier to the stroke width first.
1902 DlScalar pad = 1.0f;
1903 if (current_.getStrokeJoin() == DlStrokeJoin::kMiter &&
1904 special_flags.may_have_acute_joins()) {
1905 pad = std::max(pad, current_.getStrokeMiter());
1906 }
1907 if (current_.getStrokeCap() == DlStrokeCap::kSquare &&
1908 special_flags.may_have_diagonal_caps()) {
1909 pad = std::max(pad, SK_ScalarSqrt2);
1910 }
1911 DlScalar min_stroke_width = 0.01;
1912 pad *= std::max(current_.getStrokeWidth() * 0.5f, min_stroke_width);
1913 bounds = bounds.Expand(pad, pad);
1914 }
1915 }
1916
1917 if (flags.applies_mask_filter()) {
1918 const DlMaskFilter* filter = current_.getMaskFilterPtr();
1919 if (filter) {
1920 switch (filter->type()) {
1922 FML_DCHECK(filter->asBlur());
1923 DlScalar mask_sigma_pad = filter->asBlur()->sigma() * 3.0;
1924 bounds = bounds.Expand(mask_sigma_pad, mask_sigma_pad);
1925 }
1926 }
1927 }
1928 }
1929
1930 // Color filter does not modify bounds even if it affects transparent
1931 // black because it is clipped by the "mask" of the primitive. That
1932 // property only comes into play when it is applied to something like
1933 // a layer.
1934
1935 if (flags.applies_image_filter()) {
1936 const DlImageFilter* filter = current_.getImageFilterPtr();
1937 if (filter) {
1938 DlRect dl_bounds;
1939 if (!filter->map_local_bounds(bounds, dl_bounds)) {
1940 return false;
1941 }
1942 bounds = dl_bounds;
1943 }
1944 }
1945
1946 return true;
1947}
1948
1949bool DisplayListBuilder::AccumulateUnbounded(const SaveInfo& save) {
1950 if (!save.has_valid_clip) {
1951 save.layer_info->is_unbounded = true;
1952 }
1953 DlRect global_clip = save.global_state.GetDeviceCullCoverage();
1954 DlRect layer_clip = save.global_state.GetLocalCullCoverage();
1955 if (global_clip.IsEmpty() || !save.layer_state.mapAndClipRect(&layer_clip)) {
1956 return false;
1957 }
1958 if (rtree_data_.has_value()) {
1959 FML_DCHECK(save.layer_info->global_space_accumulator.is_empty());
1960 rtree_data_->rects.push_back(global_clip);
1961 rtree_data_->indices.push_back(op_index_);
1962 } else {
1963 save.layer_info->global_space_accumulator.accumulate(global_clip);
1964 }
1965 save.layer_info->layer_local_accumulator.accumulate(layer_clip);
1966 return true;
1967}
1968
1969bool DisplayListBuilder::AccumulateOpBounds(DlRect& bounds,
1970 DisplayListAttributeFlags flags) {
1971 if (AdjustBoundsForPaint(bounds, flags)) {
1972 return AccumulateBounds(bounds);
1973 } else {
1974 return AccumulateUnbounded();
1975 }
1976}
1977
1978bool DisplayListBuilder::AccumulateBounds(const DlRect& bounds,
1979 SaveInfo& layer,
1980 int id) {
1981 if (bounds.IsEmpty()) {
1982 return false;
1983 }
1984 DlRect global_bounds;
1985 DlRect layer_bounds;
1986 if (!layer.global_state.mapAndClipRect(bounds, &global_bounds) ||
1987 !layer.layer_state.mapAndClipRect(bounds, &layer_bounds)) {
1988 return false;
1989 }
1990 if (rtree_data_.has_value()) {
1991 FML_DCHECK(layer.layer_info->global_space_accumulator.is_empty());
1992 if (id >= 0) {
1993 rtree_data_->rects.push_back(global_bounds);
1994 rtree_data_->indices.push_back(id);
1995 }
1996 } else {
1997 layer.layer_info->global_space_accumulator.accumulate(global_bounds);
1998 }
1999 layer.layer_info->layer_local_accumulator.accumulate(layer_bounds);
2000 return true;
2001}
2002
2003bool DisplayListBuilder::SaveInfo::AccumulateBoundsLocal(const DlRect& bounds) {
2004 if (bounds.IsEmpty()) {
2005 return false;
2006 }
2007 DlRect local_bounds;
2008 if (!layer_state.mapAndClipRect(bounds, &local_bounds)) {
2009 return false;
2010 }
2011 layer_info->layer_local_accumulator.accumulate(local_bounds);
2012 return true;
2013}
2014
2015bool DisplayListBuilder::paint_nops_on_transparency() {
2016 // SkImageFilter::canComputeFastBounds tests for transparency behavior
2017 // This test assumes that the blend mode checked down below will
2018 // NOP on transparent black.
2019 if (current_.getImageFilterPtr() &&
2021 return false;
2022 }
2023
2024 // We filter the transparent black that is used for the background of a
2025 // saveLayer and make sure it returns transparent black. If it does, then
2026 // the color filter will leave all area surrounding the contents of the
2027 // save layer untouched out to the edge of the output surface.
2028 // This test assumes that the blend mode checked down below will
2029 // NOP on transparent black.
2030 if (current_.getColorFilterPtr() &&
2032 return false;
2033 }
2034
2035 // Unusual blendmodes require us to process a saved layer
2036 // even with operations outside the clip.
2037 // For example, DstIn is used by masking layers.
2038 // https://code.google.com/p/skia/issues/detail?id=1291
2039 // https://crbug.com/401593
2040 switch (current_.getBlendMode()) {
2041 // For each of the following transfer modes, if the source
2042 // alpha is zero (our transparent black), the resulting
2043 // blended pixel is not necessarily equal to the original
2044 // destination pixel.
2045 // Mathematically, any time in the following equations where
2046 // the result is not d assuming source is 0
2047 case DlBlendMode::kClear: // r = 0
2048 case DlBlendMode::kSrc: // r = s
2049 case DlBlendMode::kSrcIn: // r = s * da
2050 case DlBlendMode::kDstIn: // r = d * sa
2051 case DlBlendMode::kSrcOut: // r = s * (1-da)
2052 case DlBlendMode::kDstATop: // r = d*sa + s*(1-da)
2053 case DlBlendMode::kModulate: // r = s*d
2054 return false;
2055 break;
2056
2057 // And in these equations, the result must be d if the
2058 // source is 0
2059 case DlBlendMode::kDst: // r = d
2060 case DlBlendMode::kSrcOver: // r = s + (1-sa)*d
2061 case DlBlendMode::kDstOver: // r = d + (1-da)*s
2062 case DlBlendMode::kDstOut: // r = d * (1-sa)
2063 case DlBlendMode::kSrcATop: // r = s*da + d*(1-sa)
2064 case DlBlendMode::kXor: // r = s*(1-da) + d*(1-sa)
2065 case DlBlendMode::kPlus: // r = min(s + d, 1)
2066 case DlBlendMode::kScreen: // r = s + d - s*d
2067 case DlBlendMode::kOverlay: // multiply or screen, depending on dest
2068 case DlBlendMode::kDarken: // rc = s + d - max(s*da, d*sa),
2069 // ra = kSrcOver
2070 case DlBlendMode::kLighten: // rc = s + d - min(s*da, d*sa),
2071 // ra = kSrcOver
2072 case DlBlendMode::kColorDodge: // brighten destination to reflect source
2073 case DlBlendMode::kColorBurn: // darken destination to reflect source
2074 case DlBlendMode::kHardLight: // multiply or screen, depending on source
2075 case DlBlendMode::kSoftLight: // lighten or darken, depending on source
2076 case DlBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)),
2077 // ra = kSrcOver
2078 case DlBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver
2079 case DlBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d
2080 case DlBlendMode::kHue: // ra = kSrcOver
2081 case DlBlendMode::kSaturation: // ra = kSrcOver
2082 case DlBlendMode::kColor: // ra = kSrcOver
2083 case DlBlendMode::kLuminosity: // ra = kSrcOver
2084 return true;
2085 break;
2086 }
2087}
2088
2089DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint,
2090 DisplayListAttributeFlags flags) {
2091 DlColor color;
2092 if (flags.applies_color()) {
2093 const DlColorSource* source = paint.getColorSourcePtr();
2094 if (source) {
2095 // Suspecting that we need to modulate the ColorSource color by the
2096 // color property, see https://github.com/flutter/flutter/issues/159507
2097 color = source->is_opaque() ? DlColor::kBlack() : kAnyColor;
2098 } else {
2099 color = paint.getColor();
2100 }
2101 } else if (flags.applies_alpha()) {
2102 // If the operation applies alpha, but not color, then the only impact
2103 // of the alpha is to modulate the output towards transparency.
2104 // We can not guarantee an opaque source even if the alpha is opaque
2105 // since that would require knowing something about the colors that
2106 // the alpha is modulating, but we can guarantee a transparent source
2107 // if the alpha is 0.
2108 color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor;
2109 } else {
2110 color = kAnyColor;
2111 }
2112 if (flags.applies_image_filter()) {
2113 auto filter = paint.getImageFilterPtr();
2114 if (filter) {
2115 if (!color.isTransparent() || filter->modifies_transparent_black()) {
2116 color = kAnyColor;
2117 }
2118 }
2119 }
2120 if (flags.applies_color_filter()) {
2121 auto filter = paint.getColorFilterPtr();
2122 if (filter) {
2123 if (!color.isTransparent() || filter->modifies_transparent_black()) {
2124 color = kAnyColor;
2125 }
2126 }
2127 }
2128 return color;
2129}
2130
2131DisplayListBuilder::OpResult DisplayListBuilder::PaintResult(
2132 const DlPaint& paint,
2133 DisplayListAttributeFlags flags) {
2134 if (current_info().is_nop) {
2135 return OpResult::kNoEffect;
2136 }
2137 if (flags.applies_blend()) {
2138 switch (paint.getBlendMode()) {
2139 // Nop blend mode (singular, there is only one)
2140 case DlBlendMode::kDst:
2141 return OpResult::kNoEffect;
2142
2143 // Always clears pixels blend mode (singular, there is only one)
2144 case DlBlendMode::kClear:
2145 return OpResult::kPreservesTransparency;
2146
2147 case DlBlendMode::kHue:
2148 case DlBlendMode::kSaturation:
2149 case DlBlendMode::kColor:
2150 case DlBlendMode::kLuminosity:
2151 case DlBlendMode::kColorBurn:
2152 return GetEffectiveColor(paint, flags).isTransparent()
2153 ? OpResult::kNoEffect
2154 : OpResult::kAffectsAll;
2155
2156 // kSrcIn modifies pixels towards transparency
2157 case DlBlendMode::kSrcIn:
2158 return OpResult::kPreservesTransparency;
2159
2160 // These blend modes preserve destination alpha
2161 case DlBlendMode::kSrcATop:
2162 case DlBlendMode::kDstOut:
2163 return GetEffectiveColor(paint, flags).isTransparent()
2164 ? OpResult::kNoEffect
2165 : OpResult::kPreservesTransparency;
2166
2167 // Always destructive blend modes, potentially not affecting transparency
2168 case DlBlendMode::kSrc:
2169 case DlBlendMode::kSrcOut:
2170 case DlBlendMode::kDstATop:
2171 return GetEffectiveColor(paint, flags).isTransparent()
2172 ? OpResult::kPreservesTransparency
2173 : OpResult::kAffectsAll;
2174
2175 // The kDstIn blend mode modifies the destination unless the
2176 // source color is opaque.
2177 case DlBlendMode::kDstIn:
2178 return GetEffectiveColor(paint, flags).isOpaque()
2179 ? OpResult::kNoEffect
2180 : OpResult::kPreservesTransparency;
2181
2182 // The next group of blend modes modifies the destination unless the
2183 // source color is transparent.
2184 case DlBlendMode::kSrcOver:
2185 case DlBlendMode::kDstOver:
2186 case DlBlendMode::kXor:
2187 case DlBlendMode::kPlus:
2188 case DlBlendMode::kScreen:
2189 case DlBlendMode::kMultiply:
2190 case DlBlendMode::kOverlay:
2191 case DlBlendMode::kDarken:
2192 case DlBlendMode::kLighten:
2193 case DlBlendMode::kColorDodge:
2194 case DlBlendMode::kHardLight:
2195 case DlBlendMode::kSoftLight:
2196 case DlBlendMode::kDifference:
2197 case DlBlendMode::kExclusion:
2198 return GetEffectiveColor(paint, flags).isTransparent()
2199 ? OpResult::kNoEffect
2200 : OpResult::kAffectsAll;
2201
2202 // Modulate only leaves the pixel alone when the source is white.
2203 case DlBlendMode::kModulate:
2204 return GetEffectiveColor(paint, flags) == DlColor::kWhite()
2205 ? OpResult::kNoEffect
2206 : OpResult::kPreservesTransparency;
2207 }
2208 }
2209 return OpResult::kAffectsAll;
2210}
2211
2212} // namespace flutter
constexpr bool is_stroked(DlDrawStyle style=DlDrawStyle::kStroke) const
constexpr bool ignores_paint() const
constexpr bool is_geometric() const
constexpr bool applies_image_filter() const
const DisplayListSpecialGeometryFlags GeometryFlags(bool is_stroked) const
constexpr bool applies_mask_filter() const
void DrawOval(const DlRect &bounds, const DlPaint &paint) override
static constexpr DlRect kMaxCullRect
Definition dl_builder.h:32
void ClipRect(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void DrawImageRect(const sk_sp< DlImage > &image, const DlRect &src, const DlRect &dst, DlImageSampling sampling, const DlPaint *paint=nullptr, DlSrcRectConstraint constraint=DlSrcRectConstraint::kFast) override
void DrawVertices(const std::shared_ptr< DlVertices > &vertices, DlBlendMode mode, const DlPaint &paint) override
void DrawImageNine(const sk_sp< DlImage > &image, const DlIRect &center, const DlRect &dst, DlFilterMode filter, const DlPaint *paint=nullptr) override
void DrawAtlas(const sk_sp< DlImage > &atlas, const DlRSTransform xform[], const DlRect tex[], const DlColor colors[], int count, DlBlendMode mode, DlImageSampling sampling, const DlRect *cullRect, const DlPaint *paint=nullptr) override
void DrawRoundRect(const DlRoundRect &rrect, const DlPaint &paint) override
void RestoreToCount(int restore_count) override
void DrawArc(const DlRect &bounds, DlScalar start, DlScalar sweep, bool useCenter, const DlPaint &paint) override
void DrawShadow(const DlPath &path, const DlColor color, const DlScalar elevation, bool transparent_occluder, DlScalar dpr) override
Draws the shadow of the given |path| rendered in the provided |color| (which is only consulted for it...
void DrawImage(const sk_sp< DlImage > &image, const DlPoint &point, DlImageSampling sampling, const DlPaint *paint=nullptr) override
void DrawColor(DlColor color, DlBlendMode mode) override
DlMatrix GetMatrix() const override
Definition dl_builder.h:111
void DrawCircle(const DlPoint &center, DlScalar radius, const DlPaint &paint) override
void TransformReset() override
void SaveLayer(const std::optional< DlRect > &bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr, std::optional< int64_t > backdrop_id=std::nullopt) override
void ClipRoundSuperellipse(const DlRoundSuperellipse &rse, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void DrawLine(const DlPoint &p0, const DlPoint &p1, const DlPaint &paint) override
DlISize GetBaseLayerDimensions() const override
void ClipRoundRect(const DlRoundRect &rrect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
bool IsEmpty() const
Return true if the builder has not yet recorded any commands.
void Rotate(DlScalar degrees) override
void DrawText(const std::shared_ptr< DlText > &text, DlScalar x, DlScalar y, const DlPaint &paint) override
DisplayListBuilder(bool prepare_rtree)
Definition dl_builder.h:35
void DrawRoundSuperellipse(const DlRoundSuperellipse &rse, const DlPaint &paint) override
void Scale(DlScalar sx, DlScalar sy) override
void DrawDisplayList(const sk_sp< DisplayList > display_list, DlScalar opacity=SK_Scalar1) override
SkImageInfo GetImageInfo() const override
void Skew(DlScalar sx, DlScalar sy) override
void Translate(DlScalar tx, DlScalar ty) override
void Transform2DAffine(DlScalar mxx, DlScalar mxy, DlScalar mxt, DlScalar myx, DlScalar myy, DlScalar myt) override
void DrawPaint(const DlPaint &paint) override
size_t GetRecordCount() const
Return the number of records currently recorded in the builder.
void DrawDashedLine(const DlPoint &p0, const DlPoint &p1, DlScalar on_length, DlScalar off_length, const DlPaint &paint) override
sk_sp< DisplayList > Build()
void DrawPath(const DlPath &path, const DlPaint &paint) override
bool QuickReject(const DlRect &bounds) const override
void ClipPath(const DlPath &path, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
void DrawPoints(DlPointMode mode, uint32_t count, const DlPoint pts[], const DlPaint &paint) override
void ClipOval(const DlRect &bounds, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
DlRect GetLocalClipCoverage() const override
Definition dl_builder.h:145
int GetSaveCount() const override
Definition dl_builder.h:71
void TransformFullPerspective(DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt, DlScalar myx, DlScalar myy, DlScalar myz, DlScalar myt, DlScalar mzx, DlScalar mzy, DlScalar mzz, DlScalar mzt, DlScalar mwx, DlScalar mwy, DlScalar mwz, DlScalar mwt) override
void DrawDiffRoundRect(const DlRoundRect &outer, const DlRoundRect &inner, const DlPaint &paint) override
void Transform(const DlMatrix &matrix) override
void DrawRect(const DlRect &rect, const DlPaint &paint) override
void translate(DlScalar tx, DlScalar ty)
void transform2DAffine(DlScalar mxx, DlScalar mxy, DlScalar mxt, DlScalar myx, DlScalar myy, DlScalar myt)
void scale(DlScalar sx, DlScalar sy)
void clipRect(const DlRect &rect, DlClipOp op, bool is_aa)
void clipRRect(const DlRoundRect &rrect, DlClipOp op, bool is_aa)
void clipRSuperellipse(const DlRoundSuperellipse &rse, DlClipOp op, bool is_aa)
void transformFullPerspective(DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt, DlScalar myx, DlScalar myy, DlScalar myz, DlScalar myt, DlScalar mzx, DlScalar mzy, DlScalar mzz, DlScalar mzt, DlScalar mwx, DlScalar mwy, DlScalar mwz, DlScalar mwt)
void setTransform(const DlMatrix &matrix)
bool content_culled(const DlRect &content_bounds) const
void clipPath(const DlPath &path, DlClipOp op, bool is_aa)
void clipOval(const DlRect &bounds, DlClipOp op, bool is_aa)
void skew(DlScalar skx, DlScalar sky)
static constexpr DisplayListAttributeFlags kDrawAtlasFlags
static constexpr DisplayListAttributeFlags kDrawVerticesFlags
static constexpr DisplayListAttributeFlags kDrawArcWithCenterFlags
static constexpr DisplayListAttributeFlags kDrawPaintFlags
static constexpr DisplayListAttributeFlags kDrawArcNoCenterFlags
static constexpr DisplayListAttributeFlags kDrawImageRectWithPaintFlags
static constexpr DisplayListAttributeFlags kSaveLayerFlags
static constexpr DisplayListAttributeFlags kDrawOvalFlags
static constexpr DisplayListAttributeFlags kDrawPointsAsLinesFlags
static constexpr DisplayListAttributeFlags kDrawPointsAsPolygonFlags
static constexpr DisplayListAttributeFlags kDrawImageRectFlags
static constexpr DisplayListAttributeFlags kDrawCircleFlags
static constexpr DisplayListAttributeFlags kDrawDisplayListFlags
static constexpr DisplayListAttributeFlags kDrawPathFlags
static constexpr DisplayListAttributeFlags kDrawAtlasWithPaintFlags
static constexpr DisplayListAttributeFlags kDrawImageNineFlags
static constexpr DisplayListAttributeFlags kDrawLineFlags
static constexpr DisplayListAttributeFlags kSaveLayerWithPaintFlags
static constexpr DisplayListAttributeFlags kDrawPointsAsPointsFlags
static constexpr DisplayListAttributeFlags kDrawImageWithPaintFlags
static constexpr DisplayListAttributeFlags kDrawImageNineWithPaintFlags
static constexpr DisplayListAttributeFlags kDrawTextFlags
static constexpr DisplayListAttributeFlags kDrawShadowFlags
static constexpr DisplayListAttributeFlags kDrawRSuperellipseFlags
static constexpr DisplayListAttributeFlags kDrawImageFlags
static constexpr DisplayListAttributeFlags kDrawHVLineFlags
static constexpr DisplayListAttributeFlags kDrawRRectFlags
static constexpr DisplayListAttributeFlags kDrawDRRectFlags
static constexpr DisplayListAttributeFlags kDrawRectFlags
uint8_t * base()
Returns a pointer to the base of the storage.
Definition dl_storage.h:24
size_t size() const
Returns the currently allocated size.
Definition dl_storage.h:28
uint8_t * allocate(size_t needed)
Definition dl_storage.cc:39
static DlRect ComputeShadowBounds(const DlPath &path, float elevation, DlScalar dpr, const DlMatrix &ctm)
Compute the local coverage for a |DrawShadow| operation using the given parameters (excluding the col...
Definition dl_canvas.cc:125
virtual bool modifies_transparent_black() const =0
virtual bool modifies_transparent_black() const =0
static constexpr int kMaxDrawPointsCount
DlStrokeCap getStrokeCap() const
Definition dl_paint.h:98
DlPaint & setColor(DlColor color)
Definition dl_paint.h:70
DlPaint & setAntiAlias(bool isAntiAlias)
Definition dl_paint.h:58
DlPaint & setInvertColors(bool isInvertColors)
Definition dl_paint.h:64
DlBlendMode getBlendMode() const
Definition dl_paint.h:82
float getStrokeMiter() const
Definition dl_paint.h:120
DlStrokeJoin getStrokeJoin() const
Definition dl_paint.h:106
DlPaint & setStrokeCap(DlStrokeCap cap)
Definition dl_paint.h:101
const DlMaskFilter * getMaskFilterPtr() const
Definition dl_paint.h:183
DlPaint & setStrokeWidth(float width)
Definition dl_paint.h:115
const DlColorFilter * getColorFilterPtr() const
Definition dl_paint.h:147
DlPaint & setStrokeMiter(float miter)
Definition dl_paint.h:121
DlPaint & setBlendMode(DlBlendMode mode)
Definition dl_paint.h:85
const DlImageFilter * getImageFilterPtr() const
Definition dl_paint.h:165
DlDrawStyle getDrawStyle() const
Definition dl_paint.h:90
DlPaint & setImageFilter(std::nullptr_t filter)
Definition dl_paint.h:167
const std::shared_ptr< DlImageFilter > & getImageFilter() const
Definition dl_paint.h:162
float getStrokeWidth() const
Definition dl_paint.h:114
DlPaint & setMaskFilter(std::nullptr_t filter)
Definition dl_paint.h:185
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:93
DlPaint & setStrokeJoin(DlStrokeJoin join)
Definition dl_paint.h:109
DlPaint & setColorFilter(std::nullptr_t filter)
Definition dl_paint.h:149
DlPaint & setColorSource(std::nullptr_t source)
Definition dl_paint.h:131
SaveLayerOptions with_content_is_unbounded() const
SaveLayerOptions with_renders_with_attributes() const
SaveLayerOptions with_can_distribute_opacity() const
bool renders_with_attributes() const
SaveLayerOptions with_bounds_from_caller() const
bool bounds_from_caller() const
SaveLayerOptions with_contains_backdrop_filter() const
SaveLayerOptions without_optimizations() const
SaveLayerOptions with_content_is_clipped() const
int32_t x
FlutterVulkanImage * image
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_UNREACHABLE()
Definition logging.h:128
#define FML_DCHECK(condition)
Definition logging.h:122
std::u16string text
double y
impeller::Scalar DlScalar
impeller::RoundingRadii DlRoundingRadii
static constexpr DlRect kEmpty
DlStrokeJoin
Definition dl_paint.h:37
@ kMiter
extends to miter limit
constexpr bool DlScalarNearlyEqual(DlScalar x, DlScalar y, DlScalar tolerance=kEhCloseEnough)
impeller::RoundRect DlRoundRect
DlStrokeCap
Definition dl_paint.h:28
@ kSquare
adds square
impeller::RSTransform DlRSTransform
impeller::Matrix DlMatrix
impeller::Rect DlRect
impeller::Degrees DlDegrees
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
DlPointMode
Definition dl_types.h:15
@ kLines
draw each separate pair of points as a line segment
@ kPolygon
draw each pair of overlapping points as a line segment
@ kPoints
draw each point separately
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
DlDrawStyle
Definition dl_paint.h:19
@ kStroke
strokes boundary of shapes
@ kFill
fills interior of shapes
impeller::IRect32 DlIRect
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 switch_defs.h:36
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
static void CopyV(void *dst)
impeller::BlendMode DlBlendMode
impeller::Quad DlQuad
impeller::Point DlPoint
static const DlRect & ProtectEmpty(const DlRect &rect)
DlSrcRectConstraint
Definition dl_types.h:21
BlendMode
Definition color.h:58
flutter::DlColor DlColor
flutter::DlPaint DlPaint
int32_t width
const DisplayListOpType type
static constexpr DlColor kWhite()
Definition dl_color.h:70
static constexpr DlColor kBlack()
Definition dl_color.h:69
static constexpr DlColor kTransparent()
Definition dl_color.h:68
constexpr bool isTransparent() const
Definition dl_color.h:98
constexpr bool isOpaque() const
Definition dl_color.h:97
SaveLayerOptions options
A 4x4 matrix using column-major storage.
Definition matrix.h:37
Scalar e[4][4]
Definition matrix.h:40
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
void GetQuad(Scalar width, Scalar height, Quad &quad) const
static RoundRect MakeRectRadii(const Rect &rect, const RoundingRadii &radii)
Definition round_rect.cc:9
constexpr bool IsRect() const
Definition round_rect.h:67
constexpr const Rect & GetBounds() const
Definition round_rect.h:53
constexpr bool IsOval() const
Definition round_rect.h:71
constexpr const Rect & GetBounds() const
static RoundSuperellipse MakeRectRadii(const Rect &rect, const RoundingRadii &radii)
constexpr bool IsOval() const
constexpr bool IsRect() const
constexpr const RoundingRadii & GetRadii() const
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition rect.h:331
constexpr bool Contains(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the half-open interior of this rectangle.
Definition rect.h:255
static constexpr std::enable_if_t< std::is_floating_point_v< FT >, TRect > Make(const TRect< U > &rect)
Definition rect.h:181
constexpr auto GetLeft() const
Definition rect.h:385
RoundOut(const TRect< U > &r)
Definition rect.h:713
constexpr TRect GetPositive() const
Get a version of this rectangle that has a non-negative size.
Definition rect.h:432
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
IsFinite() const
Returns true if all of the fields of this floating point rectangle are finite.
Definition rect.h:322
static constexpr TRect MakeCircleBounds(const TPoint< Type > &center, Type radius)
Definition rect.h:156
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:652
constexpr TRect IntersectionOrEmpty(const TRect &o) const
Definition rect.h:576
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition rect.h:636
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
const size_t start
const size_t end