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