Flutter Engine
The Flutter Engine
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
5#include "flutter/display_list/dl_builder.h"
6
7#include "flutter/display_list/display_list.h"
8#include "flutter/display_list/dl_blend_mode.h"
9#include "flutter/display_list/dl_op_flags.h"
10#include "flutter/display_list/dl_op_records.h"
11#include "flutter/display_list/effects/dl_color_source.h"
12#include "flutter/display_list/utils/dl_accumulation_rect.h"
13#include "fml/logging.h"
15
16namespace flutter {
17
18#define DL_BUILDER_PAGE 4096
19
20// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst.
21static void CopyV(void* dst) {}
22
23template <typename S, typename... Rest>
24static void CopyV(void* dst, const S* src, int n, Rest&&... rest) {
25 FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0)
26 << "Expected " << dst << " to be aligned for at least " << alignof(S)
27 << " bytes.";
28 // If n is 0, there is nothing to copy into dst from src.
29 if (n > 0) {
30 memcpy(dst, src, n * sizeof(S));
31 dst = reinterpret_cast<void*>(reinterpret_cast<uint8_t*>(dst) +
32 n * sizeof(S));
33 }
34 // Repeat for the next items, if any
35 CopyV(dst, std::forward<Rest>(rest)...);
36}
37
38static constexpr inline bool is_power_of_two(int value) {
39 return (value & (value - 1)) == 0;
40}
41
42template <typename T, typename... Args>
43void* DisplayListBuilder::Push(size_t pod, Args&&... args) {
44 size_t size = SkAlignPtr(sizeof(T) + pod);
45 FML_DCHECK(size < (1 << 24));
46 if (used_ + size > allocated_) {
47 static_assert(is_power_of_two(DL_BUILDER_PAGE),
48 "This math needs updating for non-pow2.");
49 // Next greater multiple of DL_BUILDER_PAGE.
50 allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1);
51 storage_.realloc(allocated_);
52 FML_DCHECK(storage_.get());
53 memset(storage_.get() + used_, 0, allocated_ - used_);
54 }
55 FML_DCHECK(used_ + size <= allocated_);
56 auto op = reinterpret_cast<T*>(storage_.get() + used_);
57 used_ += size;
58 new (op) T{std::forward<Args>(args)...};
59 op->type = T::kType;
60 op->size = size;
61 render_op_count_ += T::kRenderOpInc;
62 depth_ += T::kDepthInc * render_op_depth_cost_;
63 op_index_++;
64 return op + 1;
65}
66
68 while (save_stack_.size() > 1) {
69 restore();
70 }
71
72 size_t bytes = used_;
73 int count = render_op_count_;
74 size_t nested_bytes = nested_bytes_;
75 int nested_count = nested_op_count_;
76 uint32_t total_depth = depth_;
77 bool opacity_compatible = current_layer().is_group_opacity_compatible();
78 bool is_safe = is_ui_thread_safe_;
79 bool affects_transparency = current_layer().affects_transparent_layer;
80 bool root_has_backdrop_filter = current_layer().contains_backdrop_filter;
81 DlBlendMode max_root_blend_mode = current_layer().max_blend_mode;
82
83 sk_sp<DlRTree> rtree;
85 if (rtree_data_.has_value()) {
86 auto& rects = rtree_data_->rects;
87 auto& indices = rtree_data_->indices;
88 rtree = sk_make_sp<DlRTree>(rects.data(), rects.size(), indices.data(),
89 [](int id) { return id >= 0; });
90 // RTree bounds may be tighter due to applying filter bounds
91 // adjustments to each op as we restore layers rather than to
92 // the entire layer bounds.
93 bounds = rtree->bounds();
94 rtree_data_.reset();
95 } else {
96 bounds = current_layer().global_space_accumulator.bounds();
97 }
98
99 used_ = allocated_ = render_op_count_ = op_index_ = 0;
100 nested_bytes_ = nested_op_count_ = 0;
101 depth_ = 0;
102 is_ui_thread_safe_ = true;
103 current_opacity_compatibility_ = true;
104 render_op_depth_cost_ = 1u;
105 current_ = DlPaint();
106
107 save_stack_.pop_back();
108 Init(rtree != nullptr);
109
110 storage_.realloc(bytes);
112 std::move(storage_), bytes, count, nested_bytes, nested_count,
113 total_depth, bounds, opacity_compatible, is_safe, affects_transparency,
114 max_root_blend_mode, root_has_backdrop_filter, std::move(rtree)));
115}
116
117static constexpr DlRect kEmpty = DlRect();
118
119static const DlRect& ProtectEmpty(const SkRect& rect) {
120 // isEmpty protects us against NaN while we normalize any empty cull rects
121 return rect.isEmpty() ? kEmpty : ToDlRect(rect);
122}
123
125 bool prepare_rtree)
126 : original_cull_rect_(ProtectEmpty(cull_rect)) {
127 Init(prepare_rtree);
128}
129
130void DisplayListBuilder::Init(bool prepare_rtree) {
131 FML_DCHECK(save_stack_.empty());
132 FML_DCHECK(!rtree_data_.has_value());
133
134 save_stack_.emplace_back(original_cull_rect_);
135 current_info().is_nop = original_cull_rect_.IsEmpty();
136 if (prepare_rtree) {
137 rtree_data_.emplace();
138 }
139}
140
142 uint8_t* ptr = storage_.get();
143 if (ptr) {
144 DisplayList::DisposeOps(ptr, ptr + used_);
145 }
146}
147
149 return ToSkISize(DlIRect::RoundOut(original_cull_rect_).GetSize());
150}
151
154 return SkImageInfo::MakeUnknown(size.width(), size.height());
155}
156
157void DisplayListBuilder::onSetAntiAlias(bool aa) {
158 current_.setAntiAlias(aa);
159 Push<SetAntiAliasOp>(0, aa);
160}
161void DisplayListBuilder::onSetInvertColors(bool invert) {
162 current_.setInvertColors(invert);
163 Push<SetInvertColorsOp>(0, invert);
164 UpdateCurrentOpacityCompatibility();
165}
166void DisplayListBuilder::onSetStrokeCap(DlStrokeCap cap) {
167 current_.setStrokeCap(cap);
168 Push<SetStrokeCapOp>(0, cap);
169}
170void DisplayListBuilder::onSetStrokeJoin(DlStrokeJoin join) {
171 current_.setStrokeJoin(join);
172 Push<SetStrokeJoinOp>(0, join);
173}
174void DisplayListBuilder::onSetDrawStyle(DlDrawStyle style) {
175 current_.setDrawStyle(style);
176 Push<SetStyleOp>(0, style);
177}
178void DisplayListBuilder::onSetStrokeWidth(float width) {
179 current_.setStrokeWidth(width);
180 Push<SetStrokeWidthOp>(0, width);
181}
182void DisplayListBuilder::onSetStrokeMiter(float limit) {
183 current_.setStrokeMiter(limit);
184 Push<SetStrokeMiterOp>(0, limit);
185}
186void DisplayListBuilder::onSetColor(DlColor color) {
187 current_.setColor(color);
188 Push<SetColorOp>(0, color);
189}
190void DisplayListBuilder::onSetBlendMode(DlBlendMode mode) {
191 current_.setBlendMode(mode);
192 Push<SetBlendModeOp>(0, mode);
193 UpdateCurrentOpacityCompatibility();
194}
195
196void DisplayListBuilder::onSetColorSource(const DlColorSource* source) {
197 if (source == nullptr) {
198 current_.setColorSource(nullptr);
199 Push<ClearColorSourceOp>(0);
200 } else {
201 current_.setColorSource(source->shared());
202 is_ui_thread_safe_ = is_ui_thread_safe_ && source->isUIThreadSafe();
203 switch (source->type()) {
205 const DlColorColorSource* color_source = source->asColor();
206 current_.setColorSource(nullptr);
207 setColor(color_source->color());
208 break;
209 }
211 const DlImageColorSource* image_source = source->asImage();
212 FML_DCHECK(image_source);
213 Push<SetImageColorSourceOp>(0, image_source);
214 break;
215 }
217 const DlLinearGradientColorSource* linear = source->asLinearGradient();
219 void* pod = Push<SetPodColorSourceOp>(linear->size());
220 new (pod) DlLinearGradientColorSource(linear);
221 break;
222 }
224 const DlRadialGradientColorSource* radial = source->asRadialGradient();
225 FML_DCHECK(radial);
226 void* pod = Push<SetPodColorSourceOp>(radial->size());
227 new (pod) DlRadialGradientColorSource(radial);
228 break;
229 }
231 const DlConicalGradientColorSource* conical =
232 source->asConicalGradient();
233 FML_DCHECK(conical);
234 void* pod = Push<SetPodColorSourceOp>(conical->size());
235 new (pod) DlConicalGradientColorSource(conical);
236 break;
237 }
239 const DlSweepGradientColorSource* sweep = source->asSweepGradient();
240 FML_DCHECK(sweep);
241 void* pod = Push<SetPodColorSourceOp>(sweep->size());
242 new (pod) DlSweepGradientColorSource(sweep);
243 break;
244 }
246 const DlRuntimeEffectColorSource* effect = source->asRuntimeEffect();
247 FML_DCHECK(effect);
248 Push<SetRuntimeEffectColorSourceOp>(0, effect);
249 break;
250 }
251#ifdef IMPELLER_ENABLE_3D
252 case DlColorSourceType::kScene: {
253 const DlSceneColorSource* scene = source->asScene();
254 FML_DCHECK(scene);
255 Push<SetSceneColorSourceOp>(0, scene);
256 break;
257 }
258#endif // IMPELLER_ENABLE_3D
259 }
260 }
261}
262void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) {
263 if (filter == nullptr) {
264 current_.setImageFilter(nullptr);
265 Push<ClearImageFilterOp>(0);
266 } else {
267 current_.setImageFilter(filter->shared());
268 switch (filter->type()) {
270 const DlBlurImageFilter* blur_filter = filter->asBlur();
271 FML_DCHECK(blur_filter);
272 void* pod = Push<SetPodImageFilterOp>(blur_filter->size());
273 new (pod) DlBlurImageFilter(blur_filter);
274 break;
275 }
277 const DlDilateImageFilter* dilate_filter = filter->asDilate();
278 FML_DCHECK(dilate_filter);
279 void* pod = Push<SetPodImageFilterOp>(dilate_filter->size());
280 new (pod) DlDilateImageFilter(dilate_filter);
281 break;
282 }
284 const DlErodeImageFilter* erode_filter = filter->asErode();
285 FML_DCHECK(erode_filter);
286 void* pod = Push<SetPodImageFilterOp>(erode_filter->size());
287 new (pod) DlErodeImageFilter(erode_filter);
288 break;
289 }
291 const DlMatrixImageFilter* matrix_filter = filter->asMatrix();
292 FML_DCHECK(matrix_filter);
293 void* pod = Push<SetPodImageFilterOp>(matrix_filter->size());
294 new (pod) DlMatrixImageFilter(matrix_filter);
295 break;
296 }
300 Push<SetSharedImageFilterOp>(0, filter);
301 break;
302 }
303 }
304 }
305}
306void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) {
307 if (filter == nullptr) {
308 current_.setColorFilter(nullptr);
309 Push<ClearColorFilterOp>(0);
310 } else {
311 current_.setColorFilter(filter->shared());
312 switch (filter->type()) {
314 const DlBlendColorFilter* blend_filter = filter->asBlend();
315 FML_DCHECK(blend_filter);
316 void* pod = Push<SetPodColorFilterOp>(blend_filter->size());
317 new (pod) DlBlendColorFilter(blend_filter);
318 break;
319 }
321 const DlMatrixColorFilter* matrix_filter = filter->asMatrix();
322 FML_DCHECK(matrix_filter);
323 void* pod = Push<SetPodColorFilterOp>(matrix_filter->size());
324 new (pod) DlMatrixColorFilter(matrix_filter);
325 break;
326 }
328 void* pod = Push<SetPodColorFilterOp>(filter->size());
329 new (pod) DlSrgbToLinearGammaColorFilter();
330 break;
331 }
333 void* pod = Push<SetPodColorFilterOp>(filter->size());
334 new (pod) DlLinearToSrgbGammaColorFilter();
335 break;
336 }
337 }
338 }
339 UpdateCurrentOpacityCompatibility();
340}
341void DisplayListBuilder::onSetMaskFilter(const DlMaskFilter* filter) {
342 if (filter == nullptr) {
343 current_.setMaskFilter(nullptr);
344 render_op_depth_cost_ = 1u;
345 Push<ClearMaskFilterOp>(0);
346 } else {
347 current_.setMaskFilter(filter->shared());
348 render_op_depth_cost_ = 2u;
349 switch (filter->type()) {
351 const DlBlurMaskFilter* blur_filter = filter->asBlur();
352 FML_DCHECK(blur_filter);
353 void* pod = Push<SetPodMaskFilterOp>(blur_filter->size());
354 new (pod) DlBlurMaskFilter(blur_filter);
355 break;
356 }
357 }
358 }
359}
360
361void DisplayListBuilder::SetAttributesFromPaint(
362 const DlPaint& paint,
363 const DisplayListAttributeFlags flags) {
364 if (flags.applies_anti_alias()) {
365 setAntiAlias(paint.isAntiAlias());
366 }
367 if (flags.applies_alpha_or_color()) {
368 setColor(paint.getColor());
369 }
370 if (flags.applies_blend()) {
371 setBlendMode(paint.getBlendMode());
372 }
373 if (flags.applies_style()) {
374 setDrawStyle(paint.getDrawStyle());
375 }
376 if (flags.is_stroked(paint.getDrawStyle())) {
377 setStrokeWidth(paint.getStrokeWidth());
378 setStrokeMiter(paint.getStrokeMiter());
379 setStrokeCap(paint.getStrokeCap());
380 setStrokeJoin(paint.getStrokeJoin());
381 }
382 if (flags.applies_shader()) {
383 setColorSource(paint.getColorSource().get());
384 }
385 if (flags.applies_color_filter()) {
386 setInvertColors(paint.isInvertColors());
387 setColorFilter(paint.getColorFilter().get());
388 }
389 if (flags.applies_image_filter()) {
390 setImageFilter(paint.getImageFilter().get());
391 }
392 if (flags.applies_mask_filter()) {
393 setMaskFilter(paint.getMaskFilter().get());
394 }
395}
396
397void DisplayListBuilder::checkForDeferredSave() {
398 if (current_info().has_deferred_save_op) {
399 size_t save_offset = used_;
400 Push<SaveOp>(0);
401 current_info().save_offset = save_offset;
402 current_info().save_depth = depth_;
403 current_info().has_deferred_save_op = false;
404 }
405}
406
408 bool was_nop = current_info().is_nop;
409 save_stack_.emplace_back(&current_info());
410 current_info().is_nop = was_nop;
411
412 FML_DCHECK(save_stack_.size() >= 2u);
413 FML_DCHECK(current_info().has_deferred_save_op);
414}
415
416void DisplayListBuilder::saveLayer(const SkRect& bounds,
417 const SaveLayerOptions in_options,
418 const DlImageFilter* backdrop) {
420 DisplayListAttributeFlags flags = options.renders_with_attributes()
423 OpResult result = PaintResult(current_, flags);
424 if (result == OpResult::kNoEffect) {
425 // If we can't render, whether because we were already in a no-render
426 // state from the parent or because our own attributes make us a nop,
427 // we can just simplify this whole layer to a regular save that has
428 // nop state. We need to have a SaveInfo for the eventual restore(),
429 // but no rendering ops should be accepted between now and then so
430 // it doesn't need any of the data associated with a layer SaveInfo.
431 Save();
432 current_info().is_nop = true;
433 return;
434 }
435
436 if (backdrop != nullptr) {
437 current_layer().contains_backdrop_filter = true;
438 }
439
440 // Snapshot these values before we do any work as we need the values
441 // from before the method was called, but some of the operations below
442 // might update them.
443 size_t save_offset = used_;
444 uint32_t save_depth = depth_;
445
446 // A backdrop will affect up to the entire surface, bounded by the clip
447 bool will_be_unbounded = (backdrop != nullptr);
448 std::shared_ptr<const DlImageFilter> filter;
449
450 if (options.renders_with_attributes()) {
451 if (!paint_nops_on_transparency()) {
452 // We will fill the clip of the outer layer when we restore.
453 will_be_unbounded = true;
454 }
455 filter = current_.getImageFilter();
456 CheckLayerOpacityCompatibility(true);
457 UpdateLayerResult(result, true);
458 } else {
459 CheckLayerOpacityCompatibility(false);
460 UpdateLayerResult(result, false);
461 }
462
463 // The actual flood of the outer layer clip will occur after the
464 // (eventual) corresponding restore is called, but rather than
465 // remember this information in the LayerInfo until the restore
466 // method is processed, we just mark the unbounded state up front.
467 // Another reason to accumulate the clip here rather than in
468 // restore is so that this savelayer will be tagged in the rtree
469 // with its full bounds and the right op_index so that it doesn't
470 // get culled during rendering.
471 if (will_be_unbounded) {
472 // Accumulate should always return true here because if the
473 // clip was empty then that would have been caught up above
474 // when we tested the PaintResult.
475 [[maybe_unused]] bool unclipped = AccumulateUnbounded();
476 FML_DCHECK(unclipped);
477 }
478
479 // Accumulate information for the SaveInfo we are about to push onto the
480 // stack.
481 {
482 size_t rtree_index =
483 rtree_data_.has_value() ? rtree_data_->rects.size() : 0u;
484
485 save_stack_.emplace_back(&current_info(), filter, rtree_index);
486 FML_DCHECK(current_info().is_save_layer);
487 FML_DCHECK(!current_info().is_nop);
488 FML_DCHECK(!current_info().has_deferred_save_op);
489 current_info().save_offset = save_offset;
490 current_info().save_depth = save_depth;
491
492 // If we inherit some culling bounds and we have a filter then we need
493 // to adjust them so that we cull for the correct input space for the
494 // output of the filter.
495 if (filter) {
496 SkRect outer_cull_rect = current_info().global_state.device_cull_rect();
497 SkMatrix matrix = current_info().global_state.matrix_3x3();
498
499 SkIRect output_bounds = outer_cull_rect.roundOut();
500 SkIRect input_bounds;
501 if (filter->get_input_device_bounds(output_bounds, matrix,
502 input_bounds)) {
503 current_info().global_state.resetDeviceCullRect(
504 SkRect::Make(input_bounds));
505 } else {
506 // Filter could not make any promises about the bounds it needs to
507 // fill the output space, so we use a maximal rect to accumulate
508 // the layer bounds.
509 current_info().global_state.resetDeviceCullRect(kMaxCullRect);
510 }
511 }
512
513 // We always want to cull based on user provided bounds, though, as
514 // that is legacy behavior even if it doesn't always work precisely
515 // in a rotated or skewed coordinate system (but it will work
516 // conservatively).
517 if (in_options.bounds_from_caller()) {
518 current_info().global_state.clipRect(bounds, ClipOp::kIntersect, false);
519 }
520 }
521
522 // Accumulate options to store in the SaveLayer op record.
523 {
524 SkRect record_bounds;
525 if (in_options.bounds_from_caller()) {
526 options = options.with_bounds_from_caller();
527 record_bounds = bounds;
528 } else {
529 FML_DCHECK(record_bounds.isEmpty());
530 }
531
532 if (backdrop) {
533 Push<SaveLayerBackdropOp>(0, options, record_bounds, backdrop);
534 } else {
535 Push<SaveLayerOp>(0, options, record_bounds);
536 }
537 }
538
539 if (options.renders_with_attributes()) {
540 // |current_opacity_compatibility_| does not take an ImageFilter into
541 // account because an individual primitive with an ImageFilter can apply
542 // opacity on top of it. But, if the layer is applying the ImageFilter
543 // then it cannot pass the opacity on.
544 if (!current_opacity_compatibility_ || filter) {
545 UpdateLayerOpacityCompatibility(false);
546 }
547 }
548}
550 const DlPaint* paint,
551 const DlImageFilter* backdrop) {
553 SkRect temp_bounds;
554 if (bounds) {
555 options = options.with_bounds_from_caller();
556 temp_bounds = *bounds;
557 } else {
558 temp_bounds.setEmpty();
559 }
560 if (paint != nullptr) {
561 options = options.with_renders_with_attributes();
562 SetAttributesFromPaint(*paint,
564 }
565 saveLayer(temp_bounds, options, backdrop);
566}
567
569 if (save_stack_.size() <= 1) {
570 return;
571 }
572
573 if (!current_info().has_deferred_save_op) {
574 SaveOpBase* op = reinterpret_cast<SaveOpBase*>(storage_.get() +
575 current_info().save_offset);
576 FML_CHECK(op->type == DisplayListOpType::kSave ||
577 op->type == DisplayListOpType::kSaveLayer ||
578 op->type == DisplayListOpType::kSaveLayerBackdrop);
579
580 op->restore_index = op_index_;
581 op->total_content_depth = depth_ - current_info().save_depth;
582
583 if (current_info().is_save_layer) {
584 RestoreLayer();
585 }
586
587 // Wait until all outgoing bounds information for the saveLayer is
588 // recorded before pushing the record to the buffer so that any rtree
589 // bounds will be attributed to the op_index of the restore op.
590 Push<RestoreOp>(0);
591 } else {
592 FML_DCHECK(!current_info().is_save_layer);
593 }
594
595 save_stack_.pop_back();
596}
597
598void DisplayListBuilder::RestoreLayer() {
599 FML_DCHECK(save_stack_.size() > 1);
600 FML_DCHECK(current_info().is_save_layer);
601 FML_DCHECK(!current_info().has_deferred_save_op);
602
603 // A saveLayer will usually do a final copy to the main buffer in
604 // addition to its content, but that is accounted for outside of
605 // the total content depth computed above in Restore.
606 depth_ += render_op_depth_cost_;
607
608 SkRect content_bounds = current_layer().layer_local_accumulator.bounds();
609
610 SaveLayerOpBase* layer_op = reinterpret_cast<SaveLayerOpBase*>(
611 storage_.get() + current_info().save_offset);
612 FML_CHECK(layer_op->type == DisplayListOpType::kSaveLayer ||
613 layer_op->type == DisplayListOpType::kSaveLayerBackdrop);
614
615 if (layer_op->options.bounds_from_caller()) {
616 if (!content_bounds.isEmpty() && !layer_op->rect.contains(content_bounds)) {
617 layer_op->options = layer_op->options.with_content_is_clipped();
618 content_bounds.intersect(layer_op->rect);
619 }
620 }
621 layer_op->rect = content_bounds;
622 layer_op->max_blend_mode = current_layer().max_blend_mode;
623
624 if (current_layer().contains_backdrop_filter) {
625 layer_op->options = layer_op->options.with_contains_backdrop_filter();
626 }
627
628 if (current_layer().is_group_opacity_compatible()) {
629 layer_op->options = layer_op->options.with_can_distribute_opacity();
630 }
631
632 // Ensure that the bounds transferred in the following call will be
633 // attributed to the index of the restore op.
634 FML_DCHECK(layer_op->restore_index == op_index_);
635 TransferLayerBounds(content_bounds);
636}
637
638// There are a few different conditions and corresponding operations to
639// consider when transferring bounds from one layer to another. The current
640// layer will have accumulated its bounds into 2 potential places:
641//
642// - Its own private layer local bounds, which were potentially clipped by
643// the supplied bounds and passed here as the content_bounds.
644//
645// - Either the rtree rect list, or the global space accumulator, one or
646// the other.
647//
648// If there is no filter then the private layer bounds are complete and
649// they simply need to be passed along to the parent into its layer local
650// accumulator. Also, if there was no filter then the existing bounds
651// recorded in either the rtree rects or the layer's global space accumulator
652// (shared with its parent) need no updating so no global space transfer
653// has to occur.
654//
655// If there is a filter then the global content bounds will need to be
656// adjusted in one of two ways (rtree vs non-rtree):
657//
658// - If we are accumulating rtree rects then each of the rects accumulated
659// during this current layer will need to be updated by the filter in the
660// global coordinate space in which they were accumulated. In this mode
661// we should never have a global space accumulator on the layer.
662//
663// - Otherwise we were accumulating global bounds into our own private
664// global space accumulator which need to be adjusted in the global space
665// coordinate system by the filter.
666//
667// Finally, we will have to adjust the layer's content bounds by the filter
668// and accumulate those into the parent layer's local bounds.
669void DisplayListBuilder::TransferLayerBounds(const SkRect& content_bounds) {
670 auto& filter = current_layer().filter;
671
672 if (!filter) {
673 // We either accumulate global bounds into the rtree_data if there
674 // is one, or into the global_space_accumulator, but not both.
675 FML_DCHECK(!rtree_data_.has_value() ||
676 current_layer().global_space_accumulator.is_empty());
677
678 parent_info().AccumulateBoundsLocal(content_bounds);
679 parent_layer().global_space_accumulator.accumulate(
680 current_layer().global_space_accumulator);
681 return;
682 }
683
684 bool parent_is_flooded = false;
685 SkRect bounds_for_parent = content_bounds;
686
687 // First, let's adjust or transfer the global/rtree bounds by the filter.
688
689 // Matrix and Clip for the filter adjustment are the global values from
690 // just before our saveLayer and should still be the current values
691 // present in the parent layer.
692 const SkRect clip = parent_info().global_state.device_cull_rect();
693 const SkMatrix matrix = parent_info().global_state.matrix_3x3();
694
695 if (rtree_data_.has_value()) {
696 // Neither current or parent layer should have any global bounds in
697 // their accumulator
698 FML_DCHECK(current_layer().global_space_accumulator.is_empty());
699 FML_DCHECK(parent_layer().global_space_accumulator.is_empty());
700
701 // The rtree rects were accumulated without the bounds modification of
702 // the filter applied to the layer so they may fail to trigger on a
703 // culled dispatch if their filter "fringes" are in the dispatch scope
704 // but their base rendering bounds are not. (Also, they will not
705 // contribute fully when we compute the overall bounds of this DL.)
706 //
707 // To make sure they are rendered in the culled dispatch situation, we
708 // revisit all of the RTree rects accumulated during the current layer
709 // (indicated by rtree_rects_start_index) and expand them by the filter.
710
711 if (AdjustRTreeRects(rtree_data_.value(), *filter, matrix, clip,
712 current_layer().rtree_rects_start_index)) {
713 parent_is_flooded = true;
714 }
715 } else {
716 SkRect global_bounds = current_layer().global_space_accumulator.bounds();
717 if (!global_bounds.isEmpty()) {
718 SkIRect global_ibounds = global_bounds.roundOut();
719 if (!filter->map_device_bounds(global_ibounds, matrix, global_ibounds)) {
720 parent_is_flooded = true;
721 } else {
722 global_bounds.set(global_ibounds);
723 if (global_bounds.intersect(clip)) {
724 parent_layer().global_space_accumulator.accumulate(global_bounds);
725 }
726 }
727 }
728 }
729
730 // Now we visit the layer bounds which are in the layer's local coordinate
731 // system must be accumulated into the parent layer's bounds while
732 // adjusting them by the layer's local coordinate system (handled by the
733 // Accumulate() methods).
734
735 // A filter will happily adjust empty bounds to be non-empty, so we
736 // specifically avoid that case here. Also, if we are already planning
737 // to flood the parent due to any of the cases above, we don't need to
738 // run the filter on the content bounds only to discover the same
739 // condition.
740 if (!parent_is_flooded && !bounds_for_parent.isEmpty()) {
741 if (!filter->map_local_bounds(bounds_for_parent, bounds_for_parent)) {
742 parent_is_flooded = true;
743 }
744 }
745
746 if (parent_is_flooded) {
747 // All of the above computations deferred the flooded parent status
748 // to here. We need to mark the parent as flooded in both its layer
749 // and global accumulators. Note that even though the rtree rects
750 // were expanded to the size of the clip above, this method will still
751 // add one more rect to the rtree with the op index of the restore
752 // command to prevent the saveLayer itself from being elided in the
753 // rare case that there are no rendering ops in it, or somehow none
754 // of them were chosen by the rtree search (unlikely). The saveLayer
755 // must be processed for the parent flood to happen.
756 AccumulateUnbounded(parent_info());
757 } else {
758 parent_info().AccumulateBoundsLocal(bounds_for_parent);
759 }
760}
761
762bool DisplayListBuilder::AdjustRTreeRects(RTreeData& data,
763 const DlImageFilter& filter,
764 const SkMatrix& matrix,
765 const SkRect& clip,
766 size_t rect_start_index) {
767 auto& rects = data.rects;
768 auto& indices = data.indices;
769 FML_DCHECK(rects.size() == indices.size());
770 int ret = false;
771 auto rect_keep = rect_start_index;
772 for (size_t i = rect_start_index; i < rects.size(); i++) {
773 SkRect bounds = rects[i];
774 SkIRect ibounds;
775 if (filter.map_device_bounds(bounds.roundOut(), matrix, ibounds)) {
776 bounds.set(ibounds);
777 } else {
778 bounds = clip;
779 ret = true;
780 }
781 if (bounds.intersect(clip)) {
782 indices[rect_keep] = indices[i];
783 rects[rect_keep] = bounds;
784 rect_keep++;
785 }
786 }
787 indices.resize(rect_keep);
788 rects.resize(rect_keep);
789 return ret;
790}
791
792void DisplayListBuilder::RestoreToCount(int restore_count) {
793 FML_DCHECK(restore_count <= GetSaveCount());
794 while (restore_count < GetSaveCount() && GetSaveCount() > 1) {
795 restore();
796 }
797}
798
800 if (std::isfinite(tx) && std::isfinite(ty) && (tx != 0.0 || ty != 0.0)) {
801 checkForDeferredSave();
802 Push<TranslateOp>(0, tx, ty);
803 global_state().translate(tx, ty);
804 layer_local_state().translate(tx, ty);
805 }
806}
808 if (std::isfinite(sx) && std::isfinite(sy) && (sx != 1.0 || sy != 1.0)) {
809 checkForDeferredSave();
810 Push<ScaleOp>(0, sx, sy);
811 global_state().scale(sx, sy);
812 layer_local_state().scale(sx, sy);
813 }
814}
816 if (SkScalarMod(degrees, 360.0) != 0.0) {
817 checkForDeferredSave();
818 Push<RotateOp>(0, degrees);
819 global_state().rotate(degrees);
820 layer_local_state().rotate(degrees);
821 }
822}
824 if (std::isfinite(sx) && std::isfinite(sy) && (sx != 0.0 || sy != 0.0)) {
825 checkForDeferredSave();
826 Push<SkewOp>(0, sx, sy);
827 global_state().skew(sx, sy);
828 layer_local_state().skew(sx, sy);
829 }
830}
831
832// clang-format off
833
834// 2x3 2D affine subset of a 4x4 transform in row major order
836 SkScalar mxx, SkScalar mxy, SkScalar mxt,
837 SkScalar myx, SkScalar myy, SkScalar myt) {
838 if (std::isfinite(mxx) && std::isfinite(myx) &&
839 std::isfinite(mxy) && std::isfinite(myy) &&
840 std::isfinite(mxt) && std::isfinite(myt)) {
841 if (mxx == 1 && mxy == 0 &&
842 myx == 0 && myy == 1) {
843 Translate(mxt, myt);
844 } else {
845 checkForDeferredSave();
846 Push<Transform2DAffineOp>(0,
847 mxx, mxy, mxt,
848 myx, myy, myt);
849 global_state().transform2DAffine(mxx, mxy, mxt,
850 myx, myy, myt);
851 layer_local_state().transform2DAffine(mxx, mxy, mxt,
852 myx, myy, myt);
853 }
854 }
855}
856// full 4x4 transform in row major order
858 SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
859 SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
860 SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
861 SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) {
862 if ( mxz == 0 &&
863 myz == 0 &&
864 mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 &&
865 mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) {
866 Transform2DAffine(mxx, mxy, mxt,
867 myx, myy, myt);
868 } else if (std::isfinite(mxx) && std::isfinite(mxy) &&
869 std::isfinite(mxz) && std::isfinite(mxt) &&
870 std::isfinite(myx) && std::isfinite(myy) &&
871 std::isfinite(myz) && std::isfinite(myt) &&
872 std::isfinite(mzx) && std::isfinite(mzy) &&
873 std::isfinite(mzz) && std::isfinite(mzt) &&
874 std::isfinite(mwx) && std::isfinite(mwy) &&
875 std::isfinite(mwz) && std::isfinite(mwt)) {
876 checkForDeferredSave();
877 Push<TransformFullPerspectiveOp>(0,
878 mxx, mxy, mxz, mxt,
879 myx, myy, myz, myt,
880 mzx, mzy, mzz, mzt,
881 mwx, mwy, mwz, mwt);
882 global_state().transformFullPerspective(mxx, mxy, mxz, mxt,
883 myx, myy, myz, myt,
884 mzx, mzy, mzz, mzt,
885 mwx, mwy, mwz, mwt);
886 layer_local_state().transformFullPerspective(mxx, mxy, mxz, mxt,
887 myx, myy, myz, myt,
888 mzx, mzy, mzz, mzt,
889 mwx, mwy, mwz, mwt);
890 }
891}
892// clang-format on
894 checkForDeferredSave();
895 Push<TransformResetOp>(0);
896
897 // The matrices in layer_tracker_ and tracker_ are similar, but
898 // start at a different base transform. The tracker_ potentially
899 // has some number of transform operations on it that prefix the
900 // operations accumulated in layer_tracker_. So we can't set them both
901 // to identity in parallel as they would no longer maintain their
902 // relationship to each other.
903 // Instead we reinterpret this operation as transforming by the
904 // inverse of the current transform. Doing so to tracker_ sets it
905 // to identity so we can avoid the math there, but we must do the
906 // math the long way for layer_tracker_. This becomes:
907 // layer_tracker_.transform(tracker_.inverse());
908 if (!layer_local_state().inverseTransform(global_state())) {
909 // If the inverse operation failed then that means that either
910 // the matrix above the current layer was singular, or the matrix
911 // became singular while we were accumulating the current layer.
912 // In either case, we should no longer be accumulating any
913 // contents so we set the layer tracking transform to a singular one.
914 layer_local_state().setTransform(SkMatrix::Scale(0.0f, 0.0f));
915 }
916
917 global_state().setIdentity();
918}
920 if (matrix != nullptr) {
922 }
923}
925 if (m44 != nullptr) {
926 transformFullPerspective(
927 m44->rc(0, 0), m44->rc(0, 1), m44->rc(0, 2), m44->rc(0, 3),
928 m44->rc(1, 0), m44->rc(1, 1), m44->rc(1, 2), m44->rc(1, 3),
929 m44->rc(2, 0), m44->rc(2, 1), m44->rc(2, 2), m44->rc(2, 3),
930 m44->rc(3, 0), m44->rc(3, 1), m44->rc(3, 2), m44->rc(3, 3));
931 }
932}
933
935 ClipOp clip_op,
936 bool is_aa) {
937 if (!rect.isFinite()) {
938 return;
939 }
940 if (current_info().is_nop) {
941 return;
942 }
943 if (current_info().has_valid_clip &&
944 clip_op == DlCanvas::ClipOp::kIntersect &&
945 layer_local_state().rect_covers_cull(rect)) {
946 return;
947 }
948 global_state().clipRect(rect, clip_op, is_aa);
949 layer_local_state().clipRect(rect, clip_op, is_aa);
950 if (global_state().is_cull_rect_empty() ||
951 layer_local_state().is_cull_rect_empty()) {
952 current_info().is_nop = true;
953 return;
954 }
955 current_info().has_valid_clip = true;
956 checkForDeferredSave();
957 switch (clip_op) {
959 Push<ClipIntersectRectOp>(0, rect, is_aa);
960 break;
962 Push<ClipDifferenceRectOp>(0, rect, is_aa);
963 break;
964 }
965}
967 ClipOp clip_op,
968 bool is_aa) {
969 if (rrect.isRect()) {
970 clipRect(rrect.rect(), clip_op, is_aa);
971 return;
972 }
973 if (current_info().is_nop) {
974 return;
975 }
976 if (current_info().has_valid_clip &&
977 clip_op == DlCanvas::ClipOp::kIntersect &&
978 layer_local_state().rrect_covers_cull(rrect)) {
979 return;
980 }
981 global_state().clipRRect(rrect, clip_op, is_aa);
982 layer_local_state().clipRRect(rrect, clip_op, is_aa);
983 if (global_state().is_cull_rect_empty() ||
984 layer_local_state().is_cull_rect_empty()) {
985 current_info().is_nop = true;
986 return;
987 }
988 current_info().has_valid_clip = true;
989 checkForDeferredSave();
990 switch (clip_op) {
992 Push<ClipIntersectRRectOp>(0, rrect, is_aa);
993 break;
995 Push<ClipDifferenceRRectOp>(0, rrect, is_aa);
996 break;
997 }
998}
1000 ClipOp clip_op,
1001 bool is_aa) {
1002 if (current_info().is_nop) {
1003 return;
1004 }
1005 if (!path.isInverseFillType()) {
1006 SkRect rect;
1007 if (path.isRect(&rect)) {
1008 this->clipRect(rect, clip_op, is_aa);
1009 return;
1010 }
1011 SkRRect rrect;
1012 if (path.isOval(&rect)) {
1014 this->clipRRect(rrect, clip_op, is_aa);
1015 return;
1016 }
1017 if (path.isRRect(&rrect)) {
1018 this->clipRRect(rrect, clip_op, is_aa);
1019 return;
1020 }
1021 }
1022 global_state().clipPath(path, clip_op, is_aa);
1023 layer_local_state().clipPath(path, clip_op, is_aa);
1024 if (global_state().is_cull_rect_empty() ||
1025 layer_local_state().is_cull_rect_empty()) {
1026 current_info().is_nop = true;
1027 return;
1028 }
1029 current_info().has_valid_clip = true;
1030 checkForDeferredSave();
1031 switch (clip_op) {
1032 case ClipOp::kIntersect:
1033 Push<ClipIntersectPathOp>(0, path, is_aa);
1034 break;
1036 Push<ClipDifferencePathOp>(0, path, is_aa);
1037 break;
1038 }
1039}
1040
1042 return global_state().content_culled(bounds);
1043}
1044
1045void DisplayListBuilder::drawPaint() {
1046 OpResult result = PaintResult(current_, kDrawPaintFlags);
1047 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
1048 Push<DrawPaintOp>(0);
1049 CheckLayerOpacityCompatibility();
1050 UpdateLayerResult(result);
1051 }
1052}
1054 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPaintFlags);
1055 drawPaint();
1056}
1058 OpResult result = PaintResult(DlPaint(color).setBlendMode(mode));
1059 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
1060 Push<DrawColorOp>(0, color, mode);
1061 CheckLayerOpacityCompatibility(mode);
1062 UpdateLayerResult(result, mode);
1063 }
1064}
1065void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) {
1066 SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted();
1068 (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags
1070 OpResult result = PaintResult(current_, flags);
1071 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1072 Push<DrawLineOp>(0, p0, p1);
1073 CheckLayerOpacityCompatibility();
1074 UpdateLayerResult(result);
1075 }
1076}
1078 const SkPoint& p1,
1079 const DlPaint& paint) {
1080 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags);
1081 drawLine(p0, p1);
1082}
1083void DisplayListBuilder::drawDashedLine(const DlPoint& p0,
1084 const DlPoint& p1,
1085 DlScalar on_length,
1086 DlScalar off_length) {
1087 SkRect bounds = SkRect::MakeLTRB(p0.x, p0.y, p1.x, p1.y).makeSorted();
1089 (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags
1091 OpResult result = PaintResult(current_, flags);
1092 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1093 Push<DrawDashedLineOp>(0, p0, p1, on_length, off_length);
1094 CheckLayerOpacityCompatibility();
1095 UpdateLayerResult(result);
1096 }
1097}
1099 const DlPoint& p1,
1100 DlScalar on_length,
1101 DlScalar off_length,
1102 const DlPaint& paint) {
1103 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags);
1104 drawDashedLine(p0, p1, on_length, off_length);
1105}
1106void DisplayListBuilder::drawRect(const SkRect& rect) {
1108 OpResult result = PaintResult(current_, flags);
1109 if (result != OpResult::kNoEffect &&
1110 AccumulateOpBounds(rect.makeSorted(), flags)) {
1111 Push<DrawRectOp>(0, rect);
1112 CheckLayerOpacityCompatibility();
1113 UpdateLayerResult(result);
1114 }
1115}
1117 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRectFlags);
1118 drawRect(rect);
1119}
1120void DisplayListBuilder::drawOval(const SkRect& bounds) {
1122 OpResult result = PaintResult(current_, flags);
1123 if (result != OpResult::kNoEffect &&
1124 AccumulateOpBounds(bounds.makeSorted(), flags)) {
1125 Push<DrawOvalOp>(0, bounds);
1126 CheckLayerOpacityCompatibility();
1127 UpdateLayerResult(result);
1128 }
1129}
1131 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawOvalFlags);
1132 drawOval(bounds);
1133}
1134void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) {
1136 OpResult result = PaintResult(current_, flags);
1137 if (result != OpResult::kNoEffect) {
1138 SkRect bounds = SkRect::MakeLTRB(center.fX - radius, center.fY - radius,
1139 center.fX + radius, center.fY + radius);
1140 if (AccumulateOpBounds(bounds, flags)) {
1141 Push<DrawCircleOp>(0, center, radius);
1142 CheckLayerOpacityCompatibility();
1143 UpdateLayerResult(result);
1144 }
1145 }
1146}
1148 SkScalar radius,
1149 const DlPaint& paint) {
1150 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawCircleFlags);
1151 drawCircle(center, radius);
1152}
1153void DisplayListBuilder::drawRRect(const SkRRect& rrect) {
1154 if (rrect.isRect()) {
1155 drawRect(rrect.rect());
1156 } else if (rrect.isOval()) {
1157 drawOval(rrect.rect());
1158 } else {
1159 DisplayListAttributeFlags flags = kDrawRRectFlags;
1160 OpResult result = PaintResult(current_, flags);
1161 if (result != OpResult::kNoEffect &&
1162 AccumulateOpBounds(rrect.getBounds(), flags)) {
1163 Push<DrawRRectOp>(0, rrect);
1164 CheckLayerOpacityCompatibility();
1165 UpdateLayerResult(result);
1166 }
1167 }
1168}
1170 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRRectFlags);
1171 drawRRect(rrect);
1172}
1173void DisplayListBuilder::drawDRRect(const SkRRect& outer,
1174 const SkRRect& inner) {
1176 OpResult result = PaintResult(current_, flags);
1177 if (result != OpResult::kNoEffect &&
1178 AccumulateOpBounds(outer.getBounds(), flags)) {
1179 Push<DrawDRRectOp>(0, outer, inner);
1180 CheckLayerOpacityCompatibility();
1181 UpdateLayerResult(result);
1182 }
1183}
1185 const SkRRect& inner,
1186 const DlPaint& paint) {
1187 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawDRRectFlags);
1188 drawDRRect(outer, inner);
1189}
1190void DisplayListBuilder::drawPath(const SkPath& path) {
1192 OpResult result = PaintResult(current_, flags);
1193 if (result != OpResult::kNoEffect) {
1194 bool is_visible = path.isInverseFillType()
1195 ? AccumulateUnbounded()
1196 : AccumulateOpBounds(path.getBounds(), flags);
1197 if (is_visible) {
1198 Push<DrawPathOp>(0, path);
1199 CheckLayerOpacityHairlineCompatibility();
1200 UpdateLayerResult(result);
1201 }
1202 }
1203}
1205 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPathFlags);
1206 drawPath(path);
1207}
1208
1209void DisplayListBuilder::drawArc(const SkRect& bounds,
1210 SkScalar start,
1211 SkScalar sweep,
1212 bool useCenter) {
1214 useCenter //
1217 OpResult result = PaintResult(current_, flags);
1218 // This could be tighter if we compute where the start and end
1219 // angles are and then also consider the quadrants swept and
1220 // the center if specified.
1221 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1222 Push<DrawArcOp>(0, bounds, start, sweep, useCenter);
1223 if (useCenter) {
1224 CheckLayerOpacityHairlineCompatibility();
1225 } else {
1226 CheckLayerOpacityCompatibility();
1227 }
1228 UpdateLayerResult(result);
1229 }
1230}
1232 SkScalar start,
1233 SkScalar sweep,
1234 bool useCenter,
1235 const DlPaint& paint) {
1236 SetAttributesFromPaint(
1238 drawArc(bounds, start, sweep, useCenter);
1239}
1240
1241DisplayListAttributeFlags DisplayListBuilder::FlagsForPointMode(
1242 PointMode mode) {
1243 switch (mode) {
1246 case PointMode::kLines:
1250 }
1252}
1253void DisplayListBuilder::drawPoints(PointMode mode,
1254 uint32_t count,
1255 const SkPoint pts[]) {
1256 if (count == 0) {
1257 return;
1258 }
1259 DisplayListAttributeFlags flags = FlagsForPointMode(mode);
1260 OpResult result = PaintResult(current_, flags);
1261 if (result == OpResult::kNoEffect) {
1262 return;
1263 }
1264
1266 int bytes = count * sizeof(SkPoint);
1267 AccumulationRect accumulator;
1268 for (size_t i = 0; i < count; i++) {
1269 accumulator.accumulate(pts[i]);
1270 }
1271 SkRect point_bounds = accumulator.bounds();
1272 if (!AccumulateOpBounds(point_bounds, flags)) {
1273 return;
1274 }
1275
1276 void* data_ptr;
1277 switch (mode) {
1278 case PointMode::kPoints:
1279 data_ptr = Push<DrawPointsOp>(bytes, count);
1280 break;
1281 case PointMode::kLines:
1282 data_ptr = Push<DrawLinesOp>(bytes, count);
1283 break;
1285 data_ptr = Push<DrawPolygonOp>(bytes, count);
1286 break;
1287 default:
1289 return;
1290 }
1291 CopyV(data_ptr, pts, count);
1292 // drawPoints treats every point or line (or segment of a polygon)
1293 // as a completely separate operation meaning we cannot ensure
1294 // distribution of group opacity without analyzing the mode and the
1295 // bounds of every sub-primitive.
1296 // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c
1297 current_layer().layer_local_accumulator.record_overlapping_bounds();
1298 // Even though we've eliminated the possibility of opacity peephole
1299 // optimizations above, we still set the appropriate flags based on
1300 // the rendering attributes in case we solve the overlapping points
1301 // problem above.
1302 CheckLayerOpacityCompatibility();
1303 UpdateLayerResult(result);
1304}
1306 uint32_t count,
1307 const SkPoint pts[],
1308 const DlPaint& paint) {
1309 SetAttributesFromPaint(paint, FlagsForPointMode(mode));
1310 drawPoints(mode, count, pts);
1311}
1312void DisplayListBuilder::drawVertices(const DlVertices* vertices,
1313 DlBlendMode mode) {
1315 OpResult result = PaintResult(current_, flags);
1316 if (result != OpResult::kNoEffect &&
1317 AccumulateOpBounds(vertices->bounds(), flags)) {
1318 void* pod = Push<DrawVerticesOp>(vertices->size(), mode);
1319 new (pod) DlVertices(vertices);
1320 // DrawVertices applies its colors to the paint so we have no way
1321 // of controlling opacity using the current paint attributes.
1322 // Although, examination of the |mode| might find some predictable
1323 // cases.
1324 UpdateLayerOpacityCompatibility(false);
1325 UpdateLayerResult(result);
1326 // Even though we already eliminated opacity peephole optimization
1327 // due to the color issues identified above, drawVertices also fails
1328 // based on the fact that the vertices are rendered independently
1329 // so we cannot guarantee the non-overlapping condition. We record
1330 // both conditions in case a solution is found to applying the
1331 // colors above - both conditions must be analyzed sufficiently
1332 // and implemented accordingly before drawVertices is compatible with
1333 // opacity peephole optimizations.
1334 current_layer().layer_local_accumulator.record_overlapping_bounds();
1335 }
1336}
1339 const DlPaint& paint) {
1340 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawVerticesFlags);
1341 drawVertices(vertices, mode);
1342}
1343
1344void DisplayListBuilder::drawImage(const sk_sp<DlImage> image,
1345 const SkPoint point,
1347 bool render_with_attributes) {
1348 DisplayListAttributeFlags flags = render_with_attributes //
1351 OpResult result = PaintResult(current_, flags);
1352 if (result == OpResult::kNoEffect) {
1353 return;
1354 }
1355 SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, //
1356 image->width(), image->height());
1357 if (AccumulateOpBounds(bounds, flags)) {
1358 render_with_attributes
1359 ? Push<DrawImageWithAttrOp>(0, image, point, sampling)
1360 : Push<DrawImageOp>(0, image, point, sampling);
1361 CheckLayerOpacityCompatibility(render_with_attributes);
1362 UpdateLayerResult(result, render_with_attributes);
1363 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1364 }
1365}
1367 const SkPoint point,
1369 const DlPaint* paint) {
1370 if (paint != nullptr) {
1371 SetAttributesFromPaint(*paint,
1373 drawImage(image, point, sampling, true);
1374 } else {
1375 drawImage(image, point, sampling, false);
1376 }
1377}
1378void DisplayListBuilder::drawImageRect(const sk_sp<DlImage> image,
1379 const SkRect& src,
1380 const SkRect& dst,
1382 bool render_with_attributes,
1383 SrcRectConstraint constraint) {
1384 DisplayListAttributeFlags flags = render_with_attributes
1387 OpResult result = PaintResult(current_, flags);
1388 if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
1389 Push<DrawImageRectOp>(0, image, src, dst, sampling, render_with_attributes,
1390 constraint);
1391 CheckLayerOpacityCompatibility(render_with_attributes);
1392 UpdateLayerResult(result, render_with_attributes);
1393 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1394 }
1395}
1397 const SkRect& src,
1398 const SkRect& dst,
1400 const DlPaint* paint,
1401 SrcRectConstraint constraint) {
1402 if (paint != nullptr) {
1403 SetAttributesFromPaint(*paint,
1405 drawImageRect(image, src, dst, sampling, true, constraint);
1406 } else {
1407 drawImageRect(image, src, dst, sampling, false, constraint);
1408 }
1409}
1410void DisplayListBuilder::drawImageNine(const sk_sp<DlImage> image,
1411 const SkIRect& center,
1412 const SkRect& dst,
1413 DlFilterMode filter,
1414 bool render_with_attributes) {
1415 DisplayListAttributeFlags flags = render_with_attributes
1418 OpResult result = PaintResult(current_, flags);
1419 if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
1420 render_with_attributes
1421 ? Push<DrawImageNineWithAttrOp>(0, image, center, dst, filter)
1422 : Push<DrawImageNineOp>(0, image, center, dst, filter);
1423 CheckLayerOpacityCompatibility(render_with_attributes);
1424 UpdateLayerResult(result, render_with_attributes);
1425 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1426 }
1427}
1429 const SkIRect& center,
1430 const SkRect& dst,
1431 DlFilterMode filter,
1432 const DlPaint* paint) {
1433 if (paint != nullptr) {
1434 SetAttributesFromPaint(*paint,
1436 drawImageNine(image, center, dst, filter, true);
1437 } else {
1438 drawImageNine(image, center, dst, filter, false);
1439 }
1440}
1441void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
1442 const SkRSXform xform[],
1443 const SkRect tex[],
1444 const DlColor colors[],
1445 int count,
1448 const SkRect* cull_rect,
1449 bool render_with_attributes) {
1450 DisplayListAttributeFlags flags = render_with_attributes //
1453 OpResult result = PaintResult(current_, flags);
1454 if (result == OpResult::kNoEffect) {
1455 return;
1456 }
1457 SkPoint quad[4];
1458 AccumulationRect accumulator;
1459 for (int i = 0; i < count; i++) {
1460 const SkRect& src = tex[i];
1461 xform[i].toQuad(src.width(), src.height(), quad);
1462 for (int j = 0; j < 4; j++) {
1463 accumulator.accumulate(quad[j]);
1464 }
1465 }
1466 if (accumulator.is_empty() ||
1467 !AccumulateOpBounds(accumulator.bounds(), flags)) {
1468 return;
1469 }
1470 // Accumulating the bounds might not trip the overlap condition if the
1471 // whole atlas operation is separated from other rendering calls, but
1472 // since each atlas op is treated as an independent operation, we have
1473 // to pass along our locally computed overlap condition for the individual
1474 // atlas operations to the layer accumulator.
1475 // Note that the above accumulation may falsely trigger the overlapping
1476 // state as it is done quad corner by quad corner and an entire quad may
1477 // be non-overlapping with the layer bounds, but as we add each point
1478 // independently it might expand the bounds on one corner and then flag
1479 // the condition when the next corner is added.
1480 if (accumulator.overlap_detected()) {
1481 current_layer().layer_local_accumulator.record_overlapping_bounds();
1482 }
1483
1484 int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect));
1485 void* data_ptr;
1486 if (colors != nullptr) {
1487 bytes += count * sizeof(DlColor);
1488 if (cull_rect != nullptr) {
1489 data_ptr =
1490 Push<DrawAtlasCulledOp>(bytes, atlas, count, mode, sampling, true,
1491 *cull_rect, render_with_attributes);
1492 } else {
1493 data_ptr = Push<DrawAtlasOp>(bytes, atlas, count, mode, sampling, true,
1494 render_with_attributes);
1495 }
1496 CopyV(data_ptr, xform, count, tex, count, colors, count);
1497 } else {
1498 if (cull_rect != nullptr) {
1499 data_ptr =
1500 Push<DrawAtlasCulledOp>(bytes, atlas, count, mode, sampling, false,
1501 *cull_rect, render_with_attributes);
1502 } else {
1503 data_ptr = Push<DrawAtlasOp>(bytes, atlas, count, mode, sampling, false,
1504 render_with_attributes);
1505 }
1506 CopyV(data_ptr, xform, count, tex, count);
1507 }
1508 // drawAtlas treats each image as a separate operation so we cannot rely
1509 // on it to distribute the opacity without overlap without checking all
1510 // of the transforms and texture rectangles.
1511 UpdateLayerOpacityCompatibility(false);
1512 UpdateLayerResult(result, render_with_attributes);
1513 is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe();
1514}
1516 const SkRSXform xform[],
1517 const SkRect tex[],
1518 const DlColor colors[],
1519 int count,
1522 const SkRect* cull_rect,
1523 const DlPaint* paint) {
1524 if (paint != nullptr) {
1525 SetAttributesFromPaint(*paint,
1527 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1528 true);
1529 } else {
1530 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1531 false);
1532 }
1533}
1534
1536 SkScalar opacity) {
1537 if (!std::isfinite(opacity) || opacity <= SK_ScalarNearlyZero ||
1538 display_list->op_count() == 0 || display_list->bounds().isEmpty() ||
1539 current_info().is_nop) {
1540 return;
1541 }
1542 const SkRect bounds = display_list->bounds();
1543 bool accumulated;
1545 if (!rtree_data_.has_value() || !(rtree = display_list->rtree())) {
1546 accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags);
1547 } else {
1548 std::list<SkRect> rects =
1549 rtree->searchAndConsolidateRects(GetLocalClipBounds(), false);
1550 accumulated = false;
1551 for (const SkRect& rect : rects) {
1552 // TODO (https://github.com/flutter/flutter/issues/114919): Attributes
1553 // are not necessarily `kDrawDisplayListFlags`.
1554 if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) {
1555 accumulated = true;
1556 }
1557 }
1558 }
1559 if (!accumulated) {
1560 return;
1561 }
1562
1563 DlPaint current_paint = current_;
1564 Push<DrawDisplayListOp>(0, display_list,
1565 opacity < SK_Scalar1 ? opacity : SK_Scalar1);
1566
1567 // This depth increment accounts for every draw call in the child
1568 // DisplayList and is in addition to the implicit depth increment
1569 // that was performed when we pushed the DrawDisplayListOp. The
1570 // eventual dispatcher can use or ignore the implicit depth increment
1571 // as it sees fit depending on whether it needs to do rendering
1572 // before or after the drawDisplayList op, but it must be accounted
1573 // for if the depth value accounting is to remain consistent between
1574 // the recording and dispatching process.
1575 depth_ += display_list->total_depth();
1576
1577 is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe();
1578 // Not really necessary if the developer is interacting with us via
1579 // our attribute-state-less DlCanvas methods, but this avoids surprises
1580 // for those who may have been using the stateful Dispatcher methods.
1581 SetAttributesFromPaint(current_paint,
1583
1584 // The non-nested op count accumulated in the |Push| method will include
1585 // this call to |drawDisplayList| for non-nested op count metrics.
1586 // But, for nested op count metrics we want the |drawDisplayList| call itself
1587 // to be transparent. So we subtract 1 from our accumulated nested count to
1588 // balance out against the 1 that was accumulated into the regular count.
1589 // This behavior is identical to the way SkPicture computed nested op counts.
1590 nested_op_count_ += display_list->op_count(true) - 1;
1591 nested_bytes_ += display_list->bytes(true);
1592 UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity());
1593 // Nop DisplayLists are eliminated above so we either affect transparent
1594 // pixels or we do not. We should not have [kNoEffect].
1595 UpdateLayerResult(display_list->modifies_transparent_black()
1596 ? OpResult::kAffectsAll
1597 : OpResult::kPreservesTransparency,
1598 display_list->max_root_blend_mode());
1599 if (display_list->root_has_backdrop_filter()) {
1600 current_layer().contains_backdrop_filter = true;
1601 }
1602}
1603void DisplayListBuilder::drawTextBlob(const sk_sp<SkTextBlob> blob,
1604 SkScalar x,
1605 SkScalar y) {
1607 OpResult result = PaintResult(current_, flags);
1608 if (result == OpResult::kNoEffect) {
1609 return;
1610 }
1611 bool unclipped = AccumulateOpBounds(blob->bounds().makeOffset(x, y), flags);
1612 // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the
1613 // unit tests can use Fuchsia's font manager instead of the empty default.
1614 // Until then we might encounter empty bounds for otherwise valid text and
1615 // thus we ignore the results from AccumulateOpBounds.
1616#if defined(OS_FUCHSIA)
1617 unclipped = true;
1618#endif // OS_FUCHSIA
1619 if (unclipped) {
1620 Push<DrawTextBlobOp>(0, blob, x, y);
1621 // There is no way to query if the glyphs of a text blob overlap and
1622 // there are no current guarantees from either Skia or Impeller that
1623 // they will protect overlapping glyphs from the effects of overdraw
1624 // so we must make the conservative assessment that this DL layer is
1625 // not compatible with group opacity inheritance.
1626 UpdateLayerOpacityCompatibility(false);
1627 UpdateLayerResult(result);
1628 }
1629}
1631 SkScalar x,
1632 SkScalar y,
1633 const DlPaint& paint) {
1634 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextBlobFlags);
1635 drawTextBlob(blob, x, y);
1636}
1637
1639 const std::shared_ptr<impeller::TextFrame>& text_frame,
1640 SkScalar x,
1641 SkScalar y) {
1643 OpResult result = PaintResult(current_, flags);
1644 if (result == OpResult::kNoEffect) {
1645 return;
1646 }
1647 impeller::Rect bounds = text_frame->GetBounds();
1648 SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
1649 bounds.GetRight(), bounds.GetBottom());
1650 bool unclipped = AccumulateOpBounds(sk_bounds.makeOffset(x, y), flags);
1651 // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the
1652 // unit tests can use Fuchsia's font manager instead of the empty default.
1653 // Until then we might encounter empty bounds for otherwise valid text and
1654 // thus we ignore the results from AccumulateOpBounds.
1655#if defined(OS_FUCHSIA)
1656 unclipped = true;
1657#endif // OS_FUCHSIA
1658 if (unclipped) {
1659 Push<DrawTextFrameOp>(0, text_frame, x, y);
1660 // There is no way to query if the glyphs of a text blob overlap and
1661 // there are no current guarantees from either Skia or Impeller that
1662 // they will protect overlapping glyphs from the effects of overdraw
1663 // so we must make the conservative assessment that this DL layer is
1664 // not compatible with group opacity inheritance.
1665 UpdateLayerOpacityCompatibility(false);
1666 UpdateLayerResult(result);
1667 }
1668}
1669
1671 const std::shared_ptr<impeller::TextFrame>& text_frame,
1672 SkScalar x,
1673 SkScalar y,
1674 const DlPaint& paint) {
1675 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextBlobFlags);
1676 drawTextFrame(text_frame, x, y);
1677}
1678
1680 const DlColor color,
1681 const SkScalar elevation,
1682 bool transparent_occluder,
1683 SkScalar dpr) {
1684 OpResult result = PaintResult(DlPaint(color));
1685 if (result != OpResult::kNoEffect) {
1686 SkRect shadow_bounds =
1688 if (AccumulateOpBounds(shadow_bounds, kDrawShadowFlags)) {
1689 transparent_occluder //
1690 ? Push<DrawShadowTransparentOccluderOp>(0, path, color, elevation,
1691 dpr)
1692 : Push<DrawShadowOp>(0, path, color, elevation, dpr);
1693 UpdateLayerOpacityCompatibility(false);
1694 UpdateLayerResult(result, DlBlendMode::kSrcOver);
1695 }
1696 }
1697}
1698
1699bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds,
1701 if (flags.ignores_paint()) {
1702 return true;
1703 }
1704
1705 if (flags.is_geometric()) {
1706 bool is_stroked = flags.is_stroked(current_.getDrawStyle());
1707
1708 // Path effect occurs before stroking...
1709 DisplayListSpecialGeometryFlags special_flags =
1710 flags.GeometryFlags(is_stroked);
1711
1712 if (is_stroked) {
1713 // Determine the max multiplier to the stroke width first.
1714 SkScalar pad = 1.0f;
1715 if (current_.getStrokeJoin() == DlStrokeJoin::kMiter &&
1716 special_flags.may_have_acute_joins()) {
1717 pad = std::max(pad, current_.getStrokeMiter());
1718 }
1719 if (current_.getStrokeCap() == DlStrokeCap::kSquare &&
1720 special_flags.may_have_diagonal_caps()) {
1721 pad = std::max(pad, SK_ScalarSqrt2);
1722 }
1723 SkScalar min_stroke_width = 0.01;
1724 pad *= std::max(current_.getStrokeWidth() * 0.5f, min_stroke_width);
1725 bounds.outset(pad, pad);
1726 }
1727 }
1728
1729 if (flags.applies_mask_filter()) {
1730 auto filter = current_.getMaskFilter();
1731 if (filter) {
1732 switch (filter->type()) {
1734 FML_DCHECK(filter->asBlur());
1735 SkScalar mask_sigma_pad = filter->asBlur()->sigma() * 3.0;
1736 bounds.outset(mask_sigma_pad, mask_sigma_pad);
1737 }
1738 }
1739 }
1740 }
1741
1742 // Color filter does not modify bounds even if it affects transparent
1743 // black because it is clipped by the "mask" of the primitive. That
1744 // property only comes into play when it is applied to something like
1745 // a layer.
1746
1747 if (flags.applies_image_filter()) {
1748 auto filter = current_.getImageFilterPtr();
1749 if (filter && !filter->map_local_bounds(bounds, bounds)) {
1750 return false;
1751 }
1752 }
1753
1754 return true;
1755}
1756
1757bool DisplayListBuilder::AccumulateUnbounded(const SaveInfo& save) {
1758 SkRect global_clip = save.global_state.device_cull_rect();
1759 SkRect layer_clip = save.global_state.local_cull_rect();
1760 if (global_clip.isEmpty() || !save.layer_state.mapAndClipRect(&layer_clip)) {
1761 return false;
1762 }
1763 if (rtree_data_.has_value()) {
1764 FML_DCHECK(save.layer_info->global_space_accumulator.is_empty());
1765 rtree_data_->rects.push_back(global_clip);
1766 rtree_data_->indices.push_back(op_index_);
1767 } else {
1768 save.layer_info->global_space_accumulator.accumulate(global_clip);
1769 }
1770 save.layer_info->layer_local_accumulator.accumulate(layer_clip);
1771 return true;
1772}
1773
1774bool DisplayListBuilder::AccumulateOpBounds(SkRect& bounds,
1775 DisplayListAttributeFlags flags) {
1776 if (AdjustBoundsForPaint(bounds, flags)) {
1777 return AccumulateBounds(bounds);
1778 } else {
1779 return AccumulateUnbounded();
1780 }
1781}
1782
1783bool DisplayListBuilder::AccumulateBounds(const SkRect& bounds,
1784 SaveInfo& layer,
1785 int id) {
1786 if (bounds.isEmpty()) {
1787 return false;
1788 }
1789 SkRect global_bounds;
1790 SkRect layer_bounds;
1791 if (!layer.global_state.mapAndClipRect(bounds, &global_bounds) ||
1792 !layer.layer_state.mapAndClipRect(bounds, &layer_bounds)) {
1793 return false;
1794 }
1795 if (rtree_data_.has_value()) {
1796 FML_DCHECK(layer.layer_info->global_space_accumulator.is_empty());
1797 if (id >= 0) {
1798 rtree_data_->rects.push_back(global_bounds);
1799 rtree_data_->indices.push_back(id);
1800 }
1801 } else {
1802 layer.layer_info->global_space_accumulator.accumulate(global_bounds);
1803 }
1804 layer.layer_info->layer_local_accumulator.accumulate(layer_bounds);
1805 return true;
1806}
1807
1808bool DisplayListBuilder::SaveInfo::AccumulateBoundsLocal(const SkRect& bounds) {
1809 if (bounds.isEmpty()) {
1810 return false;
1811 }
1812 SkRect local_bounds;
1813 if (!layer_state.mapAndClipRect(bounds, &local_bounds)) {
1814 return false;
1815 }
1816 layer_info->layer_local_accumulator.accumulate(local_bounds);
1817 return true;
1818}
1819
1820bool DisplayListBuilder::paint_nops_on_transparency() {
1821 // SkImageFilter::canComputeFastBounds tests for transparency behavior
1822 // This test assumes that the blend mode checked down below will
1823 // NOP on transparent black.
1824 if (current_.getImageFilterPtr() &&
1826 return false;
1827 }
1828
1829 // We filter the transparent black that is used for the background of a
1830 // saveLayer and make sure it returns transparent black. If it does, then
1831 // the color filter will leave all area surrounding the contents of the
1832 // save layer untouched out to the edge of the output surface.
1833 // This test assumes that the blend mode checked down below will
1834 // NOP on transparent black.
1835 if (current_.getColorFilterPtr() &&
1837 return false;
1838 }
1839
1840 // Unusual blendmodes require us to process a saved layer
1841 // even with operations outside the clip.
1842 // For example, DstIn is used by masking layers.
1843 // https://code.google.com/p/skia/issues/detail?id=1291
1844 // https://crbug.com/401593
1845 switch (current_.getBlendMode()) {
1846 // For each of the following transfer modes, if the source
1847 // alpha is zero (our transparent black), the resulting
1848 // blended pixel is not necessarily equal to the original
1849 // destination pixel.
1850 // Mathematically, any time in the following equations where
1851 // the result is not d assuming source is 0
1852 case DlBlendMode::kClear: // r = 0
1853 case DlBlendMode::kSrc: // r = s
1854 case DlBlendMode::kSrcIn: // r = s * da
1855 case DlBlendMode::kDstIn: // r = d * sa
1856 case DlBlendMode::kSrcOut: // r = s * (1-da)
1857 case DlBlendMode::kDstATop: // r = d*sa + s*(1-da)
1858 case DlBlendMode::kModulate: // r = s*d
1859 return false;
1860 break;
1861
1862 // And in these equations, the result must be d if the
1863 // source is 0
1864 case DlBlendMode::kDst: // r = d
1865 case DlBlendMode::kSrcOver: // r = s + (1-sa)*d
1866 case DlBlendMode::kDstOver: // r = d + (1-da)*s
1867 case DlBlendMode::kDstOut: // r = d * (1-sa)
1868 case DlBlendMode::kSrcATop: // r = s*da + d*(1-sa)
1869 case DlBlendMode::kXor: // r = s*(1-da) + d*(1-sa)
1870 case DlBlendMode::kPlus: // r = min(s + d, 1)
1871 case DlBlendMode::kScreen: // r = s + d - s*d
1872 case DlBlendMode::kOverlay: // multiply or screen, depending on dest
1873 case DlBlendMode::kDarken: // rc = s + d - max(s*da, d*sa),
1874 // ra = kSrcOver
1875 case DlBlendMode::kLighten: // rc = s + d - min(s*da, d*sa),
1876 // ra = kSrcOver
1877 case DlBlendMode::kColorDodge: // brighten destination to reflect source
1878 case DlBlendMode::kColorBurn: // darken destination to reflect source
1879 case DlBlendMode::kHardLight: // multiply or screen, depending on source
1880 case DlBlendMode::kSoftLight: // lighten or darken, depending on source
1881 case DlBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)),
1882 // ra = kSrcOver
1883 case DlBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver
1884 case DlBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d
1885 case DlBlendMode::kHue: // ra = kSrcOver
1886 case DlBlendMode::kSaturation: // ra = kSrcOver
1887 case DlBlendMode::kColor: // ra = kSrcOver
1888 case DlBlendMode::kLuminosity: // ra = kSrcOver
1889 return true;
1890 break;
1891 }
1892}
1893
1894DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint,
1895 DisplayListAttributeFlags flags) {
1896 DlColor color;
1897 if (flags.applies_color()) {
1898 const DlColorSource* source = paint.getColorSourcePtr();
1899 if (source) {
1900 if (source->asColor()) {
1901 color = source->asColor()->color();
1902 } else {
1903 color = source->is_opaque() ? DlColor::kBlack() : kAnyColor;
1904 }
1905 } else {
1906 color = paint.getColor();
1907 }
1908 } else if (flags.applies_alpha()) {
1909 // If the operation applies alpha, but not color, then the only impact
1910 // of the alpha is to modulate the output towards transparency.
1911 // We can not guarantee an opaque source even if the alpha is opaque
1912 // since that would require knowing something about the colors that
1913 // the alpha is modulating, but we can guarantee a transparent source
1914 // if the alpha is 0.
1915 color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor;
1916 } else {
1917 color = kAnyColor;
1918 }
1919 if (flags.applies_image_filter()) {
1920 auto filter = paint.getImageFilterPtr();
1921 if (filter) {
1922 if (!color.isTransparent() || filter->modifies_transparent_black()) {
1923 color = kAnyColor;
1924 }
1925 }
1926 }
1927 if (flags.applies_color_filter()) {
1928 auto filter = paint.getColorFilterPtr();
1929 if (filter) {
1930 if (!color.isTransparent() || filter->modifies_transparent_black()) {
1931 color = kAnyColor;
1932 }
1933 }
1934 }
1935 return color;
1936}
1937
1938DisplayListBuilder::OpResult DisplayListBuilder::PaintResult(
1939 const DlPaint& paint,
1940 DisplayListAttributeFlags flags) {
1941 if (current_info().is_nop) {
1942 return OpResult::kNoEffect;
1943 }
1944 if (flags.applies_blend()) {
1945 switch (paint.getBlendMode()) {
1946 // Nop blend mode (singular, there is only one)
1947 case DlBlendMode::kDst:
1948 return OpResult::kNoEffect;
1949
1950 // Always clears pixels blend mode (singular, there is only one)
1952 return OpResult::kPreservesTransparency;
1953
1954 case DlBlendMode::kHue:
1959 return GetEffectiveColor(paint, flags).isTransparent()
1960 ? OpResult::kNoEffect
1961 : OpResult::kAffectsAll;
1962
1963 // kSrcIn modifies pixels towards transparency
1965 return OpResult::kPreservesTransparency;
1966
1967 // These blend modes preserve destination alpha
1970 return GetEffectiveColor(paint, flags).isTransparent()
1971 ? OpResult::kNoEffect
1972 : OpResult::kPreservesTransparency;
1973
1974 // Always destructive blend modes, potentially not affecting transparency
1975 case DlBlendMode::kSrc:
1978 return GetEffectiveColor(paint, flags).isTransparent()
1979 ? OpResult::kPreservesTransparency
1980 : OpResult::kAffectsAll;
1981
1982 // The kDstIn blend mode modifies the destination unless the
1983 // source color is opaque.
1985 return GetEffectiveColor(paint, flags).isOpaque()
1986 ? OpResult::kNoEffect
1987 : OpResult::kPreservesTransparency;
1988
1989 // The next group of blend modes modifies the destination unless the
1990 // source color is transparent.
1993 case DlBlendMode::kXor:
1994 case DlBlendMode::kPlus:
2005 return GetEffectiveColor(paint, flags).isTransparent()
2006 ? OpResult::kNoEffect
2007 : OpResult::kAffectsAll;
2008
2009 // Modulate only leaves the pixel alone when the source is white.
2011 return GetEffectiveColor(paint, flags) == DlColor::kWhite()
2012 ? OpResult::kNoEffect
2013 : OpResult::kPreservesTransparency;
2014 }
2015 }
2016 return OpResult::kAffectsAll;
2017}
2018
2019} // namespace flutter
const char * options
int count
Definition: FontMgrTest.cpp:50
static constexpr T SkAlignPtr(T x)
Definition: SkAlign.h:23
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define SkScalarMod(x, y)
Definition: SkScalar.h:41
#define SK_Scalar1
Definition: SkScalar.h:18
#define SK_ScalarNearlyZero
Definition: SkScalar.h:99
#define SK_ScalarSqrt2
Definition: SkScalar.h:20
const sk_sp< Effect > & get() const
Definition: RefCntTest.cpp:125
sk_sp< SkImage > asImage() const
Definition: SkBitmap.cpp:645
int width() const
Definition: SkImage.h:285
int height() const
Definition: SkImage.h:291
Definition: SkM44.h:150
SkScalar rc(int r, int c) const
Definition: SkM44.h:261
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75
Definition: SkPath.h:59
bool isOval() const
Definition: SkRRect.h:85
const SkRect & rect() const
Definition: SkRRect.h:264
void setOval(const SkRect &oval)
Definition: SkRRect.cpp:30
bool isRect() const
Definition: SkRRect.h:84
const SkRect & getBounds() const
Definition: SkRRect.h:279
const SkRect & bounds() const
Definition: SkTextBlob.h:53
static constexpr SkRect kMaxCullRect
Definition: dl_builder.h:32
void Skew(SkScalar sx, SkScalar sy) override
Definition: dl_builder.cc:823
SkISize GetBaseLayerSize() const override
Definition: dl_builder.cc:148
void DrawLine(const SkPoint &p0, const SkPoint &p1, const DlPaint &paint) override
Definition: dl_builder.cc:1077
void RestoreToCount(int restore_count) override
Definition: dl_builder.cc:792
void DrawDRRect(const SkRRect &outer, const SkRRect &inner, const DlPaint &paint) override
Definition: dl_builder.cc:1184
void DrawVertices(const DlVertices *vertices, DlBlendMode mode, const DlPaint &paint) override
Definition: dl_builder.cc:1337
void TransformFullPerspective(SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override
Definition: dl_builder.cc:857
SkRect GetLocalClipBounds() const override
Definition: dl_builder.h:139
void DrawRect(const SkRect &rect, const DlPaint &paint) override
Definition: dl_builder.cc:1116
void Transform(const SkMatrix *matrix) override
Definition: dl_builder.cc:919
void DrawColor(DlColor color, DlBlendMode mode) override
Definition: dl_builder.cc:1057
void DrawOval(const SkRect &bounds, const DlPaint &paint) override
Definition: dl_builder.cc:1130
void TransformReset() override
Definition: dl_builder.cc:893
void ClipRRect(const SkRRect &rrect, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false) override
Definition: dl_builder.cc:966
bool QuickReject(const SkRect &bounds) const override
Definition: dl_builder.cc:1041
void Translate(SkScalar tx, SkScalar ty) override
Definition: dl_builder.cc:799
void DrawTextFrame(const std::shared_ptr< impeller::TextFrame > &text_frame, SkScalar x, SkScalar y, const DlPaint &paint) override
Definition: dl_builder.cc:1670
void DrawRRect(const SkRRect &rrect, const DlPaint &paint) override
Definition: dl_builder.cc:1169
void Scale(SkScalar sx, SkScalar sy) override
Definition: dl_builder.cc:807
void DrawPath(const SkPath &path, const DlPaint &paint) override
Definition: dl_builder.cc:1204
SkMatrix GetTransform() const override
Definition: dl_builder.h:113
void Rotate(SkScalar degrees) override
Definition: dl_builder.cc:815
void DrawCircle(const SkPoint &center, SkScalar radius, const DlPaint &paint) override
Definition: dl_builder.cc:1147
void DrawPoints(PointMode mode, uint32_t count, const SkPoint pts[], const DlPaint &paint) override
Definition: dl_builder.cc:1305
void Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myt) override
Definition: dl_builder.cc:835
DisplayListBuilder(bool prepare_rtree)
Definition: dl_builder.h:35
void drawTextFrame(const std::shared_ptr< impeller::TextFrame > &text_frame, SkScalar x, SkScalar y) override
Definition: dl_builder.cc:1638
SkImageInfo GetImageInfo() const override
Definition: dl_builder.cc:152
void DrawTextBlob(const sk_sp< SkTextBlob > &blob, SkScalar x, SkScalar y, const DlPaint &paint) override
Definition: dl_builder.cc:1630
void DrawImage(const sk_sp< DlImage > &image, const SkPoint point, DlImageSampling sampling, const DlPaint *paint=nullptr) override
Definition: dl_builder.cc:1366
void DrawShadow(const SkPath &path, const DlColor color, const SkScalar elevation, bool transparent_occluder, SkScalar dpr) override
Definition: dl_builder.cc:1679
void DrawPaint(const DlPaint &paint) override
Definition: dl_builder.cc:1053
void SaveLayer(const SkRect *bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr) override
Definition: dl_builder.cc:549
void DrawDashedLine(const DlPoint &p0, const DlPoint &p1, DlScalar on_length, DlScalar off_length, const DlPaint &paint) override
Definition: dl_builder.cc:1098
sk_sp< DisplayList > Build()
Definition: dl_builder.cc:67
void ClipRect(const SkRect &rect, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false) override
Definition: dl_builder.cc:934
void DrawImageRect(const sk_sp< DlImage > &image, const SkRect &src, const SkRect &dst, DlImageSampling sampling, const DlPaint *paint=nullptr, SrcRectConstraint constraint=SrcRectConstraint::kFast) override
Definition: dl_builder.cc:1396
void DrawDisplayList(const sk_sp< DisplayList > display_list, SkScalar opacity=SK_Scalar1) override
Definition: dl_builder.cc:1535
void ClipPath(const SkPath &path, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false) override
Definition: dl_builder.cc:999
void DrawImageNine(const sk_sp< DlImage > &image, const SkIRect &center, const SkRect &dst, DlFilterMode filter, const DlPaint *paint=nullptr) override
Definition: dl_builder.cc:1428
int GetSaveCount() const override
Definition: dl_builder.h:58
void DrawArc(const SkRect &bounds, SkScalar start, SkScalar sweep, bool useCenter, const DlPaint &paint) override
Definition: dl_builder.cc:1231
void DrawAtlas(const sk_sp< DlImage > &atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], int count, DlBlendMode mode, DlImageSampling sampling, const SkRect *cullRect, const DlPaint *paint=nullptr) override
Definition: dl_builder.cc:1515
void scale(SkScalar sx, SkScalar sy)
void clipRRect(const SkRRect &rrect, ClipOp op, bool is_aa)
void clipRect(const DlRect &rect, ClipOp op, bool is_aa)
void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myt)
void setTransform(const DlMatrix &matrix)
void clipPath(const SkPath &path, ClipOp op, bool is_aa)
void transformFullPerspective(SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt)
void skew(SkScalar skx, SkScalar sky)
bool content_culled(const DlRect &content_bounds) const
void translate(SkScalar tx, SkScalar ty)
static constexpr DisplayListAttributeFlags kDrawAtlasFlags
Definition: dl_op_flags.h:395
static constexpr DisplayListAttributeFlags kDrawVerticesFlags
Definition: dl_op_flags.h:365
static constexpr DisplayListAttributeFlags kDrawArcWithCenterFlags
Definition: dl_op_flags.h:340
static constexpr DisplayListAttributeFlags kDrawPaintFlags
Definition: dl_op_flags.h:291
static constexpr DisplayListAttributeFlags kDrawArcNoCenterFlags
Definition: dl_op_flags.h:334
static constexpr DisplayListAttributeFlags kDrawImageRectWithPaintFlags
Definition: dl_op_flags.h:384
static constexpr DisplayListAttributeFlags kSaveLayerFlags
Definition: dl_op_flags.h:277
static constexpr DisplayListAttributeFlags kDrawOvalFlags
Definition: dl_op_flags.h:310
static constexpr DisplayListAttributeFlags kDrawTextBlobFlags
Definition: dl_op_flags.h:404
static constexpr DisplayListAttributeFlags kDrawPointsAsLinesFlags
Definition: dl_op_flags.h:352
static constexpr DisplayListAttributeFlags kDrawPointsAsPolygonFlags
Definition: dl_op_flags.h:359
static constexpr DisplayListAttributeFlags kDrawImageRectFlags
Definition: dl_op_flags.h:381
static constexpr DisplayListAttributeFlags kDrawCircleFlags
Definition: dl_op_flags.h:314
static constexpr DisplayListAttributeFlags kDrawDisplayListFlags
Definition: dl_op_flags.h:401
static constexpr DisplayListAttributeFlags kDrawPathFlags
Definition: dl_op_flags.h:326
static constexpr DisplayListAttributeFlags kDrawAtlasWithPaintFlags
Definition: dl_op_flags.h:398
static constexpr DisplayListAttributeFlags kDrawImageNineFlags
Definition: dl_op_flags.h:389
static constexpr DisplayListAttributeFlags kDrawLineFlags
Definition: dl_op_flags.h:301
static constexpr DisplayListAttributeFlags kSaveLayerWithPaintFlags
Definition: dl_op_flags.h:280
static constexpr DisplayListAttributeFlags kDrawPointsAsPointsFlags
Definition: dl_op_flags.h:346
static constexpr DisplayListAttributeFlags kDrawImageWithPaintFlags
Definition: dl_op_flags.h:376
static constexpr DisplayListAttributeFlags kDrawImageNineWithPaintFlags
Definition: dl_op_flags.h:392
static constexpr DisplayListAttributeFlags kDrawShadowFlags
Definition: dl_op_flags.h:410
static constexpr DisplayListAttributeFlags kDrawImageFlags
Definition: dl_op_flags.h:373
static constexpr DisplayListAttributeFlags kDrawHVLineFlags
Definition: dl_op_flags.h:296
static constexpr DisplayListAttributeFlags kDrawRRectFlags
Definition: dl_op_flags.h:318
static constexpr DisplayListAttributeFlags kDrawDRRectFlags
Definition: dl_op_flags.h:322
static constexpr DisplayListAttributeFlags kDrawRectFlags
Definition: dl_op_flags.h:305
void realloc(size_t count)
Definition: display_list.h:251
@ 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
static SkRect ComputeShadowBounds(const SkPath &path, float elevation, SkScalar dpr, const SkMatrix &ctm)
Definition: dl_canvas.cc:12
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:99
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:83
DlPaint & setColorFilter(const std::shared_ptr< const DlColorFilter > &filter)
Definition: dl_paint.h:144
DlPaint & setMaskFilter(const std::shared_ptr< DlMaskFilter > &filter)
Definition: dl_paint.h:170
float getStrokeMiter() const
Definition: dl_paint.h:121
DlStrokeJoin getStrokeJoin() const
Definition: dl_paint.h:107
DlPaint & setStrokeCap(DlStrokeCap cap)
Definition: dl_paint.h:102
DlPaint & setStrokeWidth(float width)
Definition: dl_paint.h:116
const DlColorFilter * getColorFilterPtr() const
Definition: dl_paint.h:143
DlPaint & setStrokeMiter(float miter)
Definition: dl_paint.h:122
DlPaint & setBlendMode(DlBlendMode mode)
Definition: dl_paint.h:86
const DlImageFilter * getImageFilterPtr() const
Definition: dl_paint.h:156
DlDrawStyle getDrawStyle() const
Definition: dl_paint.h:91
std::shared_ptr< const DlMaskFilter > getMaskFilter() const
Definition: dl_paint.h:166
DlPaint & setImageFilter(const std::shared_ptr< const DlImageFilter > &filter)
Definition: dl_paint.h:157
float getStrokeWidth() const
Definition: dl_paint.h:115
std::shared_ptr< const DlImageFilter > getImageFilter() const
Definition: dl_paint.h:153
DlPaint & setDrawStyle(DlDrawStyle style)
Definition: dl_paint.h:94
DlPaint & setStrokeJoin(DlStrokeJoin join)
Definition: dl_paint.h:110
DlPaint & setColorSource(std::shared_ptr< const DlColorSource > source)
Definition: dl_paint.h:131
Holds all of the data (both required and optional) for a DisplayList drawVertices call.
Definition: dl_vertices.h:71
SkRect bounds() const
Returns the bounds of the vertices.
Definition: dl_vertices.h:194
size_t size() const
Returns the size of the object including all of the inlined data.
Definition: dl_vertices.cc:82
SaveLayerOptions with_can_distribute_opacity() const
Definition: display_list.h:177
bool bounds_from_caller() const
Definition: display_list.h:186
SaveLayerOptions with_contains_backdrop_filter() const
Definition: display_list.h:211
SaveLayerOptions without_optimizations() const
Definition: display_list.h:162
SaveLayerOptions with_content_is_clipped() const
Definition: display_list.h:204
const Paint & paint
Definition: color_source.cc:38
#define DL_BUILDER_PAGE
Definition: dl_builder.cc:18
DlColor color
SkBitmap source
Definition: examples.cpp:28
float SkScalar
Definition: extension.cpp:12
FlutterSemanticsFlag flags
gboolean invert
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint8_t value
GAsyncResult * result
#define FML_CHECK(condition)
Definition: logging.h:85
#define FML_UNREACHABLE()
Definition: logging.h:109
#define FML_DCHECK(condition)
Definition: logging.h:103
static float max(float r, float g, float b)
Definition: hsl.cpp:49
double y
double x
sk_sp< const SkImage > atlas
Definition: SkRecords.h:331
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkImage > image
Definition: SkRecords.h:269
sk_sp< const SkImageFilter > backdrop
Definition: SkRecords.h:191
SkRRect rrect
Definition: SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
PODArray< SkColor > colors
Definition: SkRecords.h:276
SkSamplingOptions sampling
Definition: SkRecords.h:337
DlCanvas::PointMode PointMode
impeller::Scalar DlScalar
static constexpr DlRect kEmpty
Definition: dl_builder.cc:117
DlStrokeJoin
Definition: dl_paint.h:37
@ kMiter
extends to miter limit
DlStrokeCap
Definition: dl_paint.h:28
@ kSquare
adds square
impeller::Rect DlRect
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
static constexpr bool is_power_of_two(int value)
Definition: dl_builder.cc:38
DlDrawStyle
Definition: dl_paint.h:19
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition: switches.h:41
static void CopyV(void *dst)
Definition: dl_builder.cc:21
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static const DlRect & ProtectEmpty(const SkRect &rect)
Definition: dl_builder.cc:119
const DlRect & ToDlRect(const SkRect &rect)
const SkISize & ToSkISize(const DlISize &size)
@ kSrcOut
r = s * (1-da)
@ kExclusion
rc = s + d - two(s*d), ra = kSrcOver
@ kSaturation
saturation of source with hue and luminosity of destination
@ kColorBurn
darken destination to reflect source
@ kPlus
r = min(s + d, 1)
@ kLighten
rc = s + d - min(s*da, d*sa), ra = kSrcOver
@ kHue
hue of source with saturation and luminosity of destination
@ kMultiply
r = s*(1-da) + d*(1-sa) + s*d
@ kColorDodge
brighten destination to reflect source
@ kScreen
r = s + d - s*d
@ kSrcOver
r = s + (1-sa)*d
@ kXor
r = s*(1-da) + d*(1-sa)
@ kLuminosity
luminosity of source with hue and saturation of destination
@ kSoftLight
lighten or darken, depending on source
@ kDifference
rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver
@ kOverlay
multiply or screen, depending on destination
@ kSrcATop
r = s*da + d*(1-sa)
@ kDstATop
r = d*sa + s*(1-da)
@ kDstOver
r = d + (1-da)*s
@ kColor
hue and saturation of source with luminosity of destination
@ kHardLight
multiply or screen, depending on source
@ kDstOut
r = d * (1-sa)
@ kDarken
rc = s + d - max(s*da, d*sa), ra = kSrcOver
dst
Definition: cp.py:12
SINT bool isfinite(const Vec< N, T > &v)
Definition: SkVx.h:1003
flutter::DisplayList DisplayList
flutter::DlColor DlColor
flutter::DlPaint DlPaint
#define T
Definition: precompiler.cc:65
int32_t width
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
Definition: SkRect.h:32
Definition: SkSize.h:16
static SkImageInfo MakeUnknown()
Definition: SkImageInfo.h:357
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165
void toQuad(SkScalar width, SkScalar height, SkPoint quad[4]) const
Definition: SkRSXform.cpp:9
SkRect makeSorted() const
Definition: SkRect.h:1330
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
constexpr SkRect makeOffset(float dx, float dy) const
Definition: SkRect.h:965
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
bool contains(SkScalar x, SkScalar y) const
Definition: extension.cpp:19
void roundOut(SkIRect *dst) const
Definition: SkRect.h:1241
bool isEmpty() const
Definition: SkRect.h:693
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
void set(const SkIRect &src)
Definition: SkRect.h:849
void setEmpty()
Definition: SkRect.h:842
DisplayListOpType type
static constexpr DlColor kWhite()
Definition: dl_color.h:23
static constexpr DlColor kBlack()
Definition: dl_color.h:22
static constexpr DlColor kTransparent()
Definition: dl_color.h:21
constexpr bool isTransparent() const
Definition: dl_color.h:40
constexpr bool isOpaque() const
Definition: dl_color.h:39
uint32_t total_content_depth
SaveLayerOptions options
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: rect.h:287
RoundOut(const TRect< U > &r)
Definition: rect.h:666
static sk_sp< SkShader > linear(sk_sp< SkShader > shader)