Flutter Engine
 
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 // Accumulate should always return true here because if the
465 // clip was empty then that would have been caught up above
466 // when we tested the PaintResult.
467 [[maybe_unused]] bool unclipped = AccumulateUnbounded();
468 FML_DCHECK(unclipped);
469 }
470
471 // Accumulate information for the SaveInfo we are about to push onto the
472 // stack.
473 {
474 size_t rtree_index =
475 rtree_data_.has_value() ? rtree_data_->rects.size() : 0u;
476
477 save_stack_.emplace_back(&current_info(), filter, rtree_index);
478 FML_DCHECK(current_info().is_save_layer);
479 FML_DCHECK(!current_info().is_nop);
480 FML_DCHECK(!current_info().has_deferred_save_op);
481 current_info().save_offset = save_offset;
482 current_info().save_depth = save_depth;
483
484 // If we inherit some culling bounds and we have a filter then we need
485 // to adjust them so that we cull for the correct input space for the
486 // output of the filter.
487 if (filter) {
488 DlRect outer_cull_rect =
489 current_info().global_state.GetDeviceCullCoverage();
490 DlMatrix matrix = current_info().global_state.matrix();
491
492 DlIRect output_bounds = DlIRect::RoundOut(outer_cull_rect);
493 DlIRect input_bounds;
494 if (filter->get_input_device_bounds(output_bounds, matrix,
495 input_bounds)) {
496 current_info().global_state.resetDeviceCullRect(
497 DlRect::Make(input_bounds));
498 } else {
499 // Filter could not make any promises about the bounds it needs to
500 // fill the output space, so we use a maximal rect to accumulate
501 // the layer bounds.
502 current_info().global_state.resetDeviceCullRect(kMaxCullRect);
503 }
504 }
505
506 // We always want to cull based on user provided bounds, though, as
507 // that is legacy behavior even if it doesn't always work precisely
508 // in a rotated or skewed coordinate system (but it will work
509 // conservatively).
510 if (in_options.bounds_from_caller()) {
511 current_info().global_state.clipRect(bounds, DlClipOp::kIntersect, false);
512 }
513 }
514
515 // Accumulate options to store in the SaveLayer op record.
516 {
517 DlRect record_bounds;
518 if (in_options.bounds_from_caller()) {
519 options = options.with_bounds_from_caller();
520 record_bounds = bounds;
521 } else {
522 FML_DCHECK(record_bounds.IsEmpty());
523 }
524
525 if (backdrop) {
526 Push<SaveLayerBackdropOp>(0, options, record_bounds, backdrop,
527 backdrop_id);
528 } else {
529 Push<SaveLayerOp>(0, options, record_bounds);
530 }
531 }
532
533 if (options.renders_with_attributes()) {
534 // |current_opacity_compatibility_| does not take an ImageFilter into
535 // account because an individual primitive with an ImageFilter can apply
536 // opacity on top of it. But, if the layer is applying the ImageFilter
537 // then it cannot pass the opacity on.
538 if (!current_opacity_compatibility_ || filter) {
539 UpdateLayerOpacityCompatibility(false);
540 }
541 }
542}
543void DisplayListBuilder::SaveLayer(const std::optional<DlRect>& bounds,
544 const DlPaint* paint,
545 const DlImageFilter* backdrop,
546 std::optional<int64_t> backdrop_id) {
547 SaveLayerOptions options;
548 DlRect temp_bounds;
549 if (bounds.has_value()) {
550 options = options.with_bounds_from_caller();
551 temp_bounds = *bounds;
552 } else {
553 FML_DCHECK(temp_bounds.IsEmpty());
554 }
555 if (paint != nullptr) {
556 options = options.with_renders_with_attributes();
557 SetAttributesFromPaint(*paint,
559 }
560 saveLayer(temp_bounds, options, backdrop, backdrop_id);
561}
562
564 if (save_stack_.size() <= 1) {
565 return;
566 }
567
568 if (!current_info().has_deferred_save_op) {
569 SaveOpBase* op = reinterpret_cast<SaveOpBase*>(storage_.base() +
570 current_info().save_offset);
571 FML_CHECK(op->type == DisplayListOpType::kSave ||
572 op->type == DisplayListOpType::kSaveLayer ||
573 op->type == DisplayListOpType::kSaveLayerBackdrop);
574
575 op->restore_index = op_index_;
576 op->total_content_depth = depth_ - current_info().save_depth;
577
578 if (current_info().is_save_layer) {
579 RestoreLayer();
580 }
581
582 // Wait until all outgoing bounds information for the saveLayer is
583 // recorded before pushing the record to the buffer so that any rtree
584 // bounds will be attributed to the op_index of the restore op.
585 Push<RestoreOp>(0);
586 } else {
587 FML_DCHECK(!current_info().is_save_layer);
588 }
589
590 save_stack_.pop_back();
591}
592
593void DisplayListBuilder::RestoreLayer() {
594 FML_DCHECK(save_stack_.size() > 1);
595 FML_DCHECK(current_info().is_save_layer);
596 FML_DCHECK(!current_info().has_deferred_save_op);
597
598 // A saveLayer will usually do a final copy to the main buffer in
599 // addition to its content, but that is accounted for outside of
600 // the total content depth computed above in Restore.
601 depth_ += render_op_depth_cost_;
602
603 DlRect content_bounds = current_layer().layer_local_accumulator.GetBounds();
604
605 SaveLayerOpBase* layer_op = reinterpret_cast<SaveLayerOpBase*>(
606 storage_.base() + current_info().save_offset);
607 FML_CHECK(layer_op->type == DisplayListOpType::kSaveLayer ||
608 layer_op->type == DisplayListOpType::kSaveLayerBackdrop);
609
610 if (layer_op->options.bounds_from_caller()) {
611 DlRect user_bounds = layer_op->rect;
612 if (!content_bounds.IsEmpty() && !user_bounds.Contains(content_bounds)) {
613 layer_op->options = layer_op->options.with_content_is_clipped();
614 content_bounds = content_bounds.IntersectionOrEmpty(user_bounds);
615 }
616 }
617 layer_op->rect = content_bounds;
618 layer_op->max_blend_mode = current_layer().max_blend_mode;
619
620 if (current_layer().contains_backdrop_filter) {
621 layer_op->options = layer_op->options.with_contains_backdrop_filter();
622 }
623
624 if (current_layer().is_group_opacity_compatible()) {
625 layer_op->options = layer_op->options.with_can_distribute_opacity();
626 }
627
628 if (current_layer().is_unbounded) {
629 layer_op->options = layer_op->options.with_content_is_unbounded();
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 DlRect& 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 DlRect 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 DlRect clip = parent_info().global_state.GetDeviceCullCoverage();
693 const DlMatrix matrix = parent_info().global_state.matrix();
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 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
712 if (AdjustRTreeRects(rtree_data_.value(), *filter, matrix, clip,
713 current_layer().rtree_rects_start_index)) {
714 parent_is_flooded = true;
715 }
716 } else {
717 DlRect global_bounds = current_layer().global_space_accumulator.GetBounds();
718 if (!global_bounds.IsEmpty()) {
719 DlIRect global_ibounds = DlIRect::RoundOut(global_bounds);
720 if (!filter->map_device_bounds(global_ibounds, matrix, global_ibounds)) {
721 parent_is_flooded = true;
722 } else {
723 global_bounds = DlRect::Make(global_ibounds);
724 std::optional<DlRect> clipped_bounds = global_bounds.Intersection(clip);
725 if (clipped_bounds.has_value()) {
726 parent_layer().global_space_accumulator.accumulate(
727 clipped_bounds.value());
728 }
729 }
730 }
731 }
732
733 // Now we visit the layer bounds which are in the layer's local coordinate
734 // system must be accumulated into the parent layer's bounds while
735 // adjusting them by the layer's local coordinate system (handled by the
736 // Accumulate() methods).
737
738 // A filter will happily adjust empty bounds to be non-empty, so we
739 // specifically avoid that case here. Also, if we are already planning
740 // to flood the parent due to any of the cases above, we don't need to
741 // run the filter on the content bounds only to discover the same
742 // condition.
743 if (!parent_is_flooded && !bounds_for_parent.IsEmpty()) {
744 DlRect mappable_bounds = bounds_for_parent;
745 if (filter->map_local_bounds(mappable_bounds, mappable_bounds)) {
746 bounds_for_parent = mappable_bounds;
747 } else {
748 parent_is_flooded = true;
749 }
750 }
751
752 if (parent_is_flooded) {
753 // All of the above computations deferred the flooded parent status
754 // to here. We need to mark the parent as flooded in both its layer
755 // and global accumulators. Note that even though the rtree rects
756 // were expanded to the size of the clip above, this method will still
757 // add one more rect to the rtree with the op index of the restore
758 // command to prevent the saveLayer itself from being elided in the
759 // rare case that there are no rendering ops in it, or somehow none
760 // of them were chosen by the rtree search (unlikely). The saveLayer
761 // must be processed for the parent flood to happen.
762 AccumulateUnbounded(parent_info());
763 } else {
764 parent_info().AccumulateBoundsLocal(bounds_for_parent);
765 }
766}
767
768bool DisplayListBuilder::AdjustRTreeRects(RTreeData& data,
769 const DlImageFilter& filter,
770 const DlMatrix& matrix,
771 const DlRect& clip,
772 size_t rect_start_index) {
773 auto& rects = data.rects;
774 auto& indices = data.indices;
775 FML_DCHECK(rects.size() == indices.size());
776 int ret = false;
777 auto rect_keep = rect_start_index;
778 for (size_t i = rect_start_index; i < rects.size(); i++) {
779 DlRect bounds = rects[i];
780 DlIRect ibounds = DlIRect::RoundOut(bounds);
781 if (filter.map_device_bounds(ibounds, matrix, ibounds)) {
782 bounds = DlRect::Make(ibounds);
783 } else {
784 bounds = clip;
785 ret = true;
786 }
787 auto clipped_bounds = bounds.Intersection(clip);
788 if (clipped_bounds.has_value()) {
789 indices[rect_keep] = indices[i];
790 rects[rect_keep] = clipped_bounds.value();
791 rect_keep++;
792 }
793 }
794 indices.resize(rect_keep);
795 rects.resize(rect_keep);
796 return ret;
797}
798
799void DisplayListBuilder::RestoreToCount(int restore_count) {
800 FML_DCHECK(restore_count <= GetSaveCount());
801 while (restore_count < GetSaveCount() && GetSaveCount() > 1) {
802 restore();
803 }
804}
805
807 if (std::isfinite(tx) && std::isfinite(ty) && (tx != 0.0 || ty != 0.0)) {
808 checkForDeferredSave();
809 Push<TranslateOp>(0, tx, ty);
810 global_state().translate(tx, ty);
811 layer_local_state().translate(tx, ty);
812 }
813}
815 if (std::isfinite(sx) && std::isfinite(sy) && (sx != 1.0 || sy != 1.0)) {
816 checkForDeferredSave();
817 Push<ScaleOp>(0, sx, sy);
818 global_state().scale(sx, sy);
819 layer_local_state().scale(sx, sy);
820 }
821}
823 if (SkScalarMod(degrees, 360.0) != 0.0) {
824 checkForDeferredSave();
825 Push<RotateOp>(0, degrees);
826 global_state().rotate(DlDegrees(degrees));
827 layer_local_state().rotate(DlDegrees(degrees));
828 }
829}
831 if (std::isfinite(sx) && std::isfinite(sy) && (sx != 0.0 || sy != 0.0)) {
832 checkForDeferredSave();
833 Push<SkewOp>(0, sx, sy);
834 global_state().skew(sx, sy);
835 layer_local_state().skew(sx, sy);
836 }
837}
838
839// clang-format off
840
841// 2x3 2D affine subset of a 4x4 transform in row major order
843 DlScalar mxx, DlScalar mxy, DlScalar mxt,
844 DlScalar myx, DlScalar myy, DlScalar myt) {
845 if (std::isfinite(mxx) && std::isfinite(myx) &&
846 std::isfinite(mxy) && std::isfinite(myy) &&
847 std::isfinite(mxt) && std::isfinite(myt)) {
848 if (mxx == 1 && mxy == 0 &&
849 myx == 0 && myy == 1) {
850 Translate(mxt, myt);
851 } else {
852 checkForDeferredSave();
853 Push<Transform2DAffineOp>(0,
854 mxx, mxy, mxt,
855 myx, myy, myt);
856 global_state().transform2DAffine(mxx, mxy, mxt,
857 myx, myy, myt);
858 layer_local_state().transform2DAffine(mxx, mxy, mxt,
859 myx, myy, myt);
860 }
861 }
862}
863// full 4x4 transform in row major order
865 DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt,
866 DlScalar myx, DlScalar myy, DlScalar myz, DlScalar myt,
867 DlScalar mzx, DlScalar mzy, DlScalar mzz, DlScalar mzt,
868 DlScalar mwx, DlScalar mwy, DlScalar mwz, DlScalar mwt) {
869 if ( mxz == 0 &&
870 myz == 0 &&
871 mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 &&
872 mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) {
873 transform2DAffine(mxx, mxy, mxt,
874 myx, myy, myt);
875 } else if (std::isfinite(mxx) && std::isfinite(mxy) &&
876 std::isfinite(mxz) && std::isfinite(mxt) &&
877 std::isfinite(myx) && std::isfinite(myy) &&
878 std::isfinite(myz) && std::isfinite(myt) &&
879 std::isfinite(mzx) && std::isfinite(mzy) &&
880 std::isfinite(mzz) && std::isfinite(mzt) &&
881 std::isfinite(mwx) && std::isfinite(mwy) &&
882 std::isfinite(mwz) && std::isfinite(mwt)) {
883 checkForDeferredSave();
884 Push<TransformFullPerspectiveOp>(0,
885 mxx, mxy, mxz, mxt,
886 myx, myy, myz, myt,
887 mzx, mzy, mzz, mzt,
888 mwx, mwy, mwz, mwt);
889 global_state().transformFullPerspective(mxx, mxy, mxz, mxt,
890 myx, myy, myz, myt,
891 mzx, mzy, mzz, mzt,
892 mwx, mwy, mwz, mwt);
893 layer_local_state().transformFullPerspective(mxx, mxy, mxz, mxt,
894 myx, myy, myz, myt,
895 mzx, mzy, mzz, mzt,
896 mwx, mwy, mwz, mwt);
897 }
898}
899// clang-format on
901 checkForDeferredSave();
902 Push<TransformResetOp>(0);
903
904 // The matrices in layer_tracker_ and tracker_ are similar, but
905 // start at a different base transform. The tracker_ potentially
906 // has some number of transform operations on it that prefix the
907 // operations accumulated in layer_tracker_. So we can't set them both
908 // to identity in parallel as they would no longer maintain their
909 // relationship to each other.
910 // Instead we reinterpret this operation as transforming by the
911 // inverse of the current transform. Doing so to tracker_ sets it
912 // to identity so we can avoid the math there, but we must do the
913 // math the long way for layer_tracker_. This becomes:
914 // layer_tracker_.transform(tracker_.inverse());
915 if (!layer_local_state().inverseTransform(global_state())) {
916 // If the inverse operation failed then that means that either
917 // the matrix above the current layer was singular, or the matrix
918 // became singular while we were accumulating the current layer.
919 // In either case, we should no longer be accumulating any
920 // contents so we set the layer tracking transform to a singular one.
921 layer_local_state().setTransform(DlMatrix::MakeScale({0.0f, 0.0f, 0.0f}));
922 }
923
924 global_state().setIdentity();
925}
928 matrix.e[0][0], matrix.e[1][0], matrix.e[2][0], matrix.e[3][0],
929 matrix.e[0][1], matrix.e[1][1], matrix.e[2][1], matrix.e[3][1],
930 matrix.e[0][2], matrix.e[1][2], matrix.e[2][2], matrix.e[3][2],
931 matrix.e[0][3], matrix.e[1][3], matrix.e[2][3], matrix.e[3][3]);
932}
933
935 DlClipOp 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 && clip_op == DlClipOp::kIntersect &&
944 layer_local_state().rect_covers_cull(rect)) {
945 return;
946 }
947 global_state().clipRect(rect, clip_op, is_aa);
948 layer_local_state().clipRect(rect, clip_op, is_aa);
949 if (global_state().is_cull_rect_empty() ||
950 layer_local_state().is_cull_rect_empty()) {
951 current_info().is_nop = true;
952 return;
953 }
954 current_info().has_valid_clip = true;
955 checkForDeferredSave();
956 switch (clip_op) {
958 Push<ClipIntersectRectOp>(0, rect, is_aa);
959 break;
961 Push<ClipDifferenceRectOp>(0, rect, is_aa);
962 break;
963 }
964}
966 DlClipOp clip_op,
967 bool is_aa) {
968 if (!bounds.IsFinite()) {
969 return;
970 }
971 if (current_info().is_nop) {
972 return;
973 }
974 if (current_info().has_valid_clip && clip_op == DlClipOp::kIntersect &&
975 layer_local_state().oval_covers_cull(bounds)) {
976 return;
977 }
978 global_state().clipOval(bounds, clip_op, is_aa);
979 layer_local_state().clipOval(bounds, clip_op, is_aa);
980 if (global_state().is_cull_rect_empty() ||
981 layer_local_state().is_cull_rect_empty()) {
982 current_info().is_nop = true;
983 return;
984 }
985 current_info().has_valid_clip = true;
986 checkForDeferredSave();
987 switch (clip_op) {
989 Push<ClipIntersectOvalOp>(0, bounds, is_aa);
990 break;
992 Push<ClipDifferenceOvalOp>(0, bounds, is_aa);
993 break;
994 }
995}
997 DlClipOp clip_op,
998 bool is_aa) {
999 if (rrect.IsRect()) {
1000 ClipRect(rrect.GetBounds(), clip_op, is_aa);
1001 return;
1002 }
1003 if (rrect.IsOval()) {
1004 ClipOval(rrect.GetBounds(), clip_op, is_aa);
1005 return;
1006 }
1007 if (current_info().is_nop) {
1008 return;
1009 }
1010 if (current_info().has_valid_clip && clip_op == DlClipOp::kIntersect &&
1011 layer_local_state().rrect_covers_cull(rrect)) {
1012 return;
1013 }
1014 global_state().clipRRect(rrect, clip_op, is_aa);
1015 layer_local_state().clipRRect(rrect, clip_op, is_aa);
1016 if (global_state().is_cull_rect_empty() ||
1017 layer_local_state().is_cull_rect_empty()) {
1018 current_info().is_nop = true;
1019 return;
1020 }
1021 current_info().has_valid_clip = true;
1022 checkForDeferredSave();
1023 switch (clip_op) {
1025 Push<ClipIntersectRoundRectOp>(0, rrect, is_aa);
1026 break;
1028 Push<ClipDifferenceRoundRectOp>(0, rrect, is_aa);
1029 break;
1030 }
1031}
1033 DlClipOp clip_op,
1034 bool is_aa) {
1035 if (rse.IsRect()) {
1036 ClipRect(rse.GetBounds(), clip_op, is_aa);
1037 return;
1038 }
1039 if (rse.IsOval()) {
1040 ClipOval(rse.GetBounds(), clip_op, is_aa);
1041 return;
1042 }
1043 if (current_info().is_nop) {
1044 return;
1045 }
1046 if (current_info().has_valid_clip && clip_op == DlClipOp::kIntersect &&
1047 layer_local_state().rsuperellipse_covers_cull(rse)) {
1048 return;
1049 }
1050 global_state().clipRSuperellipse(rse, clip_op, is_aa);
1051 layer_local_state().clipRSuperellipse(rse, clip_op, is_aa);
1052 if (global_state().is_cull_rect_empty() ||
1053 layer_local_state().is_cull_rect_empty()) {
1054 current_info().is_nop = true;
1055 return;
1056 }
1057 current_info().has_valid_clip = true;
1058 checkForDeferredSave();
1059 switch (clip_op) {
1061 Push<ClipIntersectRoundSuperellipseOp>(0, rse, is_aa);
1062 break;
1064 Push<ClipDifferenceRoundSuperellipseOp>(0, rse, is_aa);
1065 break;
1066 }
1067}
1069 DlClipOp clip_op,
1070 bool is_aa) {
1071 if (current_info().is_nop) {
1072 return;
1073 }
1074 {
1075 DlRect rect;
1076 if (path.IsRect(&rect)) {
1077 ClipRect(rect, clip_op, is_aa);
1078 return;
1079 }
1080 if (path.IsOval(&rect)) {
1081 ClipOval(rect, clip_op, is_aa);
1082 return;
1083 }
1084 DlRoundRect rrect;
1085 if (path.IsRoundRect(&rrect)) {
1086 ClipRoundRect(rrect, clip_op, is_aa);
1087 return;
1088 }
1089 }
1090 global_state().clipPath(path, clip_op, is_aa);
1091 layer_local_state().clipPath(path, clip_op, is_aa);
1092 if (global_state().is_cull_rect_empty() ||
1093 layer_local_state().is_cull_rect_empty()) {
1094 current_info().is_nop = true;
1095 return;
1096 }
1097 current_info().has_valid_clip = true;
1098 checkForDeferredSave();
1099 switch (clip_op) {
1101 Push<ClipIntersectPathOp>(0, path, is_aa);
1102 break;
1104 Push<ClipDifferencePathOp>(0, path, is_aa);
1105 break;
1106 }
1107}
1108
1109bool DisplayListBuilder::QuickReject(const DlRect& bounds) const {
1110 return global_state().content_culled(bounds);
1111}
1112
1113void DisplayListBuilder::drawPaint() {
1114 OpResult result = PaintResult(current_, kDrawPaintFlags);
1115 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
1116 Push<DrawPaintOp>(0);
1117 CheckLayerOpacityCompatibility();
1118 UpdateLayerResult(result);
1119 }
1120}
1122 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPaintFlags);
1123 drawPaint();
1124}
1126 OpResult result = PaintResult(DlPaint(color).setBlendMode(mode));
1127 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
1128 Push<DrawColorOp>(0, color, mode);
1129 CheckLayerOpacityCompatibility(mode);
1130 UpdateLayerResult(result, mode);
1131 }
1132}
1133void DisplayListBuilder::drawLine(const DlPoint& p0, const DlPoint& p1) {
1134 DlRect bounds = DlRect::MakeLTRB(p0.x, p0.y, p1.x, p1.y).GetPositive();
1137 OpResult result = PaintResult(current_, flags);
1138 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1139 Push<DrawLineOp>(0, p0, p1);
1140 CheckLayerOpacityCompatibility();
1141 UpdateLayerResult(result);
1142 }
1143}
1145 const DlPoint& p1,
1146 const DlPaint& paint) {
1147 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags);
1148 drawLine(p0, p1);
1149}
1150void DisplayListBuilder::drawDashedLine(const DlPoint& p0,
1151 const DlPoint& p1,
1152 DlScalar on_length,
1153 DlScalar off_length) {
1154 DlRect bounds = DlRect::MakeLTRB(p0.x, p0.y, p1.x, p1.y).GetPositive();
1157 OpResult result = PaintResult(current_, flags);
1158 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1159 Push<DrawDashedLineOp>(0, p0, p1, on_length, off_length);
1160 CheckLayerOpacityCompatibility();
1161 UpdateLayerResult(result);
1162 }
1163}
1165 const DlPoint& p1,
1166 DlScalar on_length,
1167 DlScalar off_length,
1168 const DlPaint& paint) {
1169 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags);
1170 drawDashedLine(p0, p1, on_length, off_length);
1171}
1172void DisplayListBuilder::drawRect(const DlRect& rect) {
1174 OpResult result = PaintResult(current_, flags);
1175 if (result != OpResult::kNoEffect &&
1176 AccumulateOpBounds(rect.GetPositive(), flags)) {
1177 Push<DrawRectOp>(0, rect);
1178 CheckLayerOpacityCompatibility();
1179 UpdateLayerResult(result);
1180 }
1181}
1182void DisplayListBuilder::DrawRect(const DlRect& rect, const DlPaint& paint) {
1183 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRectFlags);
1184 drawRect(rect);
1185}
1186void DisplayListBuilder::drawOval(const DlRect& bounds) {
1188 OpResult result = PaintResult(current_, flags);
1189 if (result != OpResult::kNoEffect &&
1190 AccumulateOpBounds(bounds.GetPositive(), flags)) {
1191 Push<DrawOvalOp>(0, bounds);
1192 CheckLayerOpacityCompatibility();
1193 UpdateLayerResult(result);
1194 }
1195}
1196void DisplayListBuilder::DrawOval(const DlRect& bounds, const DlPaint& paint) {
1197 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawOvalFlags);
1198 drawOval(bounds);
1199}
1200void DisplayListBuilder::drawCircle(const DlPoint& center, DlScalar radius) {
1202 OpResult result = PaintResult(current_, flags);
1203 if (result != OpResult::kNoEffect) {
1204 DlRect bounds = DlRect::MakeLTRB(center.x - radius, center.y - radius,
1205 center.x + radius, center.y + radius);
1206 if (AccumulateOpBounds(bounds, flags)) {
1207 Push<DrawCircleOp>(0, center, radius);
1208 CheckLayerOpacityCompatibility();
1209 UpdateLayerResult(result);
1210 }
1211 }
1212}
1214 DlScalar radius,
1215 const DlPaint& paint) {
1216 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawCircleFlags);
1217 drawCircle(center, radius);
1218}
1219void DisplayListBuilder::drawRoundRect(const DlRoundRect& rrect) {
1220 if (rrect.IsRect()) {
1221 drawRect(rrect.GetBounds());
1222 } else if (rrect.IsOval()) {
1223 drawOval(rrect.GetBounds());
1224 } else {
1225 DisplayListAttributeFlags flags = kDrawRRectFlags;
1226 OpResult result = PaintResult(current_, flags);
1227 if (result != OpResult::kNoEffect &&
1228 AccumulateOpBounds(rrect.GetBounds(), flags)) {
1229 Push<DrawRoundRectOp>(0, rrect);
1230 CheckLayerOpacityCompatibility();
1231 UpdateLayerResult(result);
1232 }
1233 }
1234}
1236 const DlPaint& paint) {
1237 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRRectFlags);
1238 drawRoundRect(rrect);
1239}
1240void DisplayListBuilder::drawDiffRoundRect(const DlRoundRect& outer,
1241 const DlRoundRect& inner) {
1243 OpResult result = PaintResult(current_, flags);
1244 if (result != OpResult::kNoEffect &&
1245 AccumulateOpBounds(outer.GetBounds(), flags)) {
1246 Push<DrawDiffRoundRectOp>(0, outer, inner);
1247 CheckLayerOpacityCompatibility();
1248 UpdateLayerResult(result);
1249 }
1250}
1252 const DlRoundRect& inner,
1253 const DlPaint& paint) {
1254 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawDRRectFlags);
1255 drawDiffRoundRect(outer, inner);
1256}
1257void DisplayListBuilder::drawRoundSuperellipse(const DlRoundSuperellipse& rse) {
1258 if (rse.IsRect()) {
1259 drawRect(rse.GetBounds());
1260 } else if (rse.IsOval()) {
1261 drawOval(rse.GetBounds());
1262 } else {
1263 DisplayListAttributeFlags flags = kDrawRSuperellipseFlags;
1264 OpResult result = PaintResult(current_, flags);
1265 if (result != OpResult::kNoEffect &&
1266 AccumulateOpBounds(rse.GetBounds(), flags)) {
1267 // DrawRoundSuperellipseOp only supports filling. Anything related to
1268 // stroking must use path approximation.
1269 if (current_.getDrawStyle() == DlDrawStyle::kFill) {
1270 Push<DrawRoundSuperellipseOp>(0, rse);
1271 } else {
1272 DlPathBuilder builder;
1273 builder.AddRoundSuperellipse(DlRoundSuperellipse::MakeRectRadii(
1274 rse.GetBounds(), rse.GetRadii()));
1275 Push<DrawPathOp>(0, builder.TakePath());
1276 }
1277 CheckLayerOpacityCompatibility();
1278 UpdateLayerResult(result);
1279 }
1280 }
1281}
1283 const DlPaint& paint) {
1284 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRSuperellipseFlags);
1285 drawRoundSuperellipse(rse);
1286}
1287void DisplayListBuilder::drawPath(const DlPath& path) {
1289 OpResult result = PaintResult(current_, flags);
1290 if (result != OpResult::kNoEffect) {
1291 bool is_visible = AccumulateOpBounds(path.GetBounds(), flags);
1292 if (is_visible) {
1293 Push<DrawPathOp>(0, path);
1294 CheckLayerOpacityHairlineCompatibility();
1295 UpdateLayerResult(result);
1296 }
1297 }
1298}
1300 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPathFlags);
1301 drawPath(path);
1302}
1303
1304void DisplayListBuilder::drawArc(const DlRect& bounds,
1305 DlScalar start,
1306 DlScalar sweep,
1307 bool useCenter) {
1308 DisplayListAttributeFlags flags = //
1309 useCenter //
1312 OpResult result = PaintResult(current_, flags);
1313 // This could be tighter if we compute where the start and end
1314 // angles are and then also consider the quadrants swept and
1315 // the center if specified.
1316 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1317 Push<DrawArcOp>(0, bounds, start, sweep, useCenter);
1318 if (useCenter) {
1319 CheckLayerOpacityHairlineCompatibility();
1320 } else {
1321 CheckLayerOpacityCompatibility();
1322 }
1323 UpdateLayerResult(result);
1324 }
1325}
1327 DlScalar start,
1328 DlScalar sweep,
1329 bool useCenter,
1330 const DlPaint& paint) {
1331 SetAttributesFromPaint(
1332 paint, useCenter ? kDrawArcWithCenterFlags : kDrawArcNoCenterFlags);
1333 drawArc(bounds, start, sweep, useCenter);
1334}
1335
1336DisplayListAttributeFlags DisplayListBuilder::FlagsForPointMode(
1337 DlPointMode mode) {
1338 switch (mode) {
1345 }
1347}
1348void DisplayListBuilder::drawPoints(DlPointMode mode,
1349 uint32_t count,
1350 const DlPoint pts[]) {
1351 if (count == 0) {
1352 return;
1353 }
1354 DisplayListAttributeFlags flags = FlagsForPointMode(mode);
1355 OpResult result = PaintResult(current_, flags);
1356 if (result == OpResult::kNoEffect) {
1357 return;
1358 }
1359
1361 int bytes = count * sizeof(DlPoint);
1362 AccumulationRect accumulator;
1363 for (size_t i = 0; i < count; i++) {
1364 accumulator.accumulate(pts[i]);
1365 }
1366 if (!AccumulateOpBounds(accumulator.GetBounds(), flags)) {
1367 return;
1368 }
1369
1370 void* data_ptr;
1371 switch (mode) {
1373 data_ptr = Push<DrawPointsOp>(bytes, count);
1374 break;
1376 data_ptr = Push<DrawLinesOp>(bytes, count);
1377 break;
1379 data_ptr = Push<DrawPolygonOp>(bytes, count);
1380 break;
1381 default:
1383 return;
1384 }
1385 CopyV(data_ptr, pts, count);
1386 // drawPoints treats every point or line (or segment of a polygon)
1387 // as a completely separate operation meaning we cannot ensure
1388 // distribution of group opacity without analyzing the mode and the
1389 // bounds of every sub-primitive.
1390 // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c
1391 current_layer().layer_local_accumulator.record_overlapping_bounds();
1392 // Even though we've eliminated the possibility of opacity peephole
1393 // optimizations above, we still set the appropriate flags based on
1394 // the rendering attributes in case we solve the overlapping points
1395 // problem above.
1396 CheckLayerOpacityCompatibility();
1397 UpdateLayerResult(result);
1398}
1400 uint32_t count,
1401 const DlPoint pts[],
1402 const DlPaint& paint) {
1403 SetAttributesFromPaint(paint, FlagsForPointMode(mode));
1404 drawPoints(mode, count, pts);
1405}
1406void DisplayListBuilder::drawVertices(
1407 const std::shared_ptr<DlVertices>& vertices,
1408 DlBlendMode mode) {
1410 OpResult result = PaintResult(current_, flags);
1411 if (result != OpResult::kNoEffect &&
1412 AccumulateOpBounds(vertices->GetBounds(), flags)) {
1413 Push<DrawVerticesOp>(0, vertices, mode);
1414 // DrawVertices applies its colors to the paint so we have no way
1415 // of controlling opacity using the current paint attributes.
1416 // Although, examination of the |mode| might find some predictable
1417 // cases.
1418 UpdateLayerOpacityCompatibility(false);
1419 UpdateLayerResult(result);
1420 // Even though we already eliminated opacity peephole optimization
1421 // due to the color issues identified above, drawVertices also fails
1422 // based on the fact that the vertices are rendered independently
1423 // so we cannot guarantee the non-overlapping condition. We record
1424 // both conditions in case a solution is found to applying the
1425 // colors above - both conditions must be analyzed sufficiently
1426 // and implemented accordingly before drawVertices is compatible with
1427 // opacity peephole optimizations.
1428 current_layer().layer_local_accumulator.record_overlapping_bounds();
1429 }
1430}
1432 const std::shared_ptr<DlVertices>& vertices,
1433 DlBlendMode mode,
1434 const DlPaint& paint) {
1435 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawVerticesFlags);
1436 drawVertices(vertices, mode);
1437}
1438
1439void DisplayListBuilder::drawImage(const sk_sp<DlImage> image,
1440 const DlPoint& point,
1441 DlImageSampling sampling,
1442 bool render_with_attributes) {
1443 DisplayListAttributeFlags flags = render_with_attributes //
1446 OpResult result = PaintResult(current_, flags);
1447 if (result == OpResult::kNoEffect) {
1448 return;
1449 }
1450 DlRect bounds = DlRect::MakeXYWH(point.x, point.y, //
1451 image->width(), image->height());
1452 if (AccumulateOpBounds(bounds, flags)) {
1453 render_with_attributes
1454 ? Push<DrawImageWithAttrOp>(0, image, point, sampling)
1455 : Push<DrawImageOp>(0, image, point, sampling);
1456 CheckLayerOpacityCompatibility(render_with_attributes);
1457 UpdateLayerResult(result, render_with_attributes);
1458 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1459 }
1460}
1461void DisplayListBuilder::DrawImage(const sk_sp<DlImage>& image,
1462 const DlPoint& point,
1463 DlImageSampling sampling,
1464 const DlPaint* paint) {
1465 if (paint != nullptr) {
1466 SetAttributesFromPaint(*paint,
1468 drawImage(image, point, sampling, true);
1469 } else {
1470 drawImage(image, point, sampling, false);
1471 }
1472}
1473void DisplayListBuilder::drawImageRect(const sk_sp<DlImage> image,
1474 const DlRect& src,
1475 const DlRect& dst,
1476 DlImageSampling sampling,
1477 bool render_with_attributes,
1478 DlSrcRectConstraint constraint) {
1479 DisplayListAttributeFlags flags = render_with_attributes
1482 OpResult result = PaintResult(current_, flags);
1483 if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
1484 Push<DrawImageRectOp>(0, image, src, dst, sampling, render_with_attributes,
1485 constraint);
1486 CheckLayerOpacityCompatibility(render_with_attributes);
1487 UpdateLayerResult(result, render_with_attributes);
1488 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1489 }
1490}
1491void DisplayListBuilder::DrawImageRect(const sk_sp<DlImage>& image,
1492 const DlRect& src,
1493 const DlRect& dst,
1494 DlImageSampling sampling,
1495 const DlPaint* paint,
1496 DlSrcRectConstraint constraint) {
1497 if (paint != nullptr) {
1498 SetAttributesFromPaint(*paint,
1500 drawImageRect(image, src, dst, sampling, true, constraint);
1501 } else {
1502 drawImageRect(image, src, dst, sampling, false, constraint);
1503 }
1504}
1505void DisplayListBuilder::drawImageNine(const sk_sp<DlImage> image,
1506 const DlIRect& center,
1507 const DlRect& dst,
1508 DlFilterMode filter,
1509 bool render_with_attributes) {
1510 DisplayListAttributeFlags flags = render_with_attributes
1513 OpResult result = PaintResult(current_, flags);
1514 if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
1515 render_with_attributes
1516 ? Push<DrawImageNineWithAttrOp>(0, image, center, dst, filter)
1517 : Push<DrawImageNineOp>(0, image, center, dst, filter);
1518 CheckLayerOpacityCompatibility(render_with_attributes);
1519 UpdateLayerResult(result, render_with_attributes);
1520 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1521 }
1522}
1523void DisplayListBuilder::DrawImageNine(const sk_sp<DlImage>& image,
1524 const DlIRect& center,
1525 const DlRect& dst,
1526 DlFilterMode filter,
1527 const DlPaint* paint) {
1528 if (paint != nullptr) {
1529 SetAttributesFromPaint(*paint,
1531 drawImageNine(image, center, dst, filter, true);
1532 } else {
1533 drawImageNine(image, center, dst, filter, false);
1534 }
1535}
1536void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
1537 const DlRSTransform xform[],
1538 const DlRect tex[],
1539 const DlColor colors[],
1540 int count,
1541 DlBlendMode mode,
1542 DlImageSampling sampling,
1543 const DlRect* cull_rect,
1544 bool render_with_attributes) {
1545 DisplayListAttributeFlags flags = render_with_attributes //
1548 OpResult result = PaintResult(current_, flags);
1549 if (result == OpResult::kNoEffect) {
1550 return;
1551 }
1552 DlQuad quad;
1553 AccumulationRect accumulator;
1554 for (int i = 0; i < count; i++) {
1555 const DlRect& src = tex[i];
1556 xform[i].GetQuad(src.GetWidth(), src.GetHeight(), quad);
1557 for (int j = 0; j < 4; j++) {
1558 accumulator.accumulate(quad[j]);
1559 }
1560 }
1561 if (accumulator.is_empty() ||
1562 !AccumulateOpBounds(accumulator.GetBounds(), flags)) {
1563 return;
1564 }
1565
1566 // Accumulating the bounds might not trip the overlap condition if the
1567 // whole atlas operation is separated from other rendering calls, but
1568 // since each atlas op is treated as an independent operation, we have
1569 // to pass along our locally computed overlap condition for the individual
1570 // atlas operations to the layer accumulator.
1571 // Note that the above accumulation may falsely trigger the overlapping
1572 // state as it is done quad corner by quad corner and an entire quad may
1573 // be non-overlapping with the layer bounds, but as we add each point
1574 // independently it might expand the bounds on one corner and then flag
1575 // the condition when the next corner is added.
1576 if (accumulator.overlap_detected()) {
1577 current_layer().layer_local_accumulator.record_overlapping_bounds();
1578 }
1579
1580 int bytes = count * (sizeof(DlRSTransform) + sizeof(DlRect));
1581 void* data_ptr;
1582 if (colors != nullptr) {
1583 bytes += count * sizeof(DlColor);
1584 if (cull_rect != nullptr) {
1585 data_ptr =
1586 Push<DrawAtlasCulledOp>(bytes, atlas, count, mode, sampling, true,
1587 *cull_rect, render_with_attributes);
1588 } else {
1589 data_ptr = Push<DrawAtlasOp>(bytes, atlas, count, mode, sampling, true,
1590 render_with_attributes);
1591 }
1592 CopyV(data_ptr, xform, count, tex, count, colors, count);
1593 } else {
1594 if (cull_rect != nullptr) {
1595 data_ptr =
1596 Push<DrawAtlasCulledOp>(bytes, atlas, count, mode, sampling, false,
1597 *cull_rect, render_with_attributes);
1598 } else {
1599 data_ptr = Push<DrawAtlasOp>(bytes, atlas, count, mode, sampling, false,
1600 render_with_attributes);
1601 }
1602 CopyV(data_ptr, xform, count, tex, count);
1603 }
1604 // drawAtlas treats each image as a separate operation so we cannot rely
1605 // on it to distribute the opacity without overlap without checking all
1606 // of the transforms and texture rectangles.
1607 UpdateLayerOpacityCompatibility(false);
1608 UpdateLayerResult(result, render_with_attributes);
1609 is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe();
1610}
1611void DisplayListBuilder::DrawAtlas(const sk_sp<DlImage>& atlas,
1612 const DlRSTransform xform[],
1613 const DlRect tex[],
1614 const DlColor colors[],
1615 int count,
1616 DlBlendMode mode,
1617 DlImageSampling sampling,
1618 const DlRect* cull_rect,
1619 const DlPaint* paint) {
1620 if (paint != nullptr) {
1621 SetAttributesFromPaint(*paint,
1623 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1624 true);
1625 } else {
1626 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1627 false);
1628 }
1629}
1630
1631void DisplayListBuilder::DrawDisplayList(const sk_sp<DisplayList> display_list,
1632 DlScalar opacity) {
1633 if (!std::isfinite(opacity) || opacity <= SK_ScalarNearlyZero ||
1634 display_list->op_count() == 0 || display_list->GetBounds().IsEmpty() ||
1635 current_info().is_nop) {
1636 return;
1637 }
1638 const DlRect bounds = display_list->GetBounds();
1639 bool accumulated;
1640 sk_sp<const DlRTree> rtree;
1641 if (display_list->root_is_unbounded()) {
1642 accumulated = AccumulateUnbounded();
1643 } else if (!rtree_data_.has_value() || !(rtree = display_list->rtree())) {
1644 accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags);
1645 } else {
1646 std::list<DlRect> rects =
1647 rtree->searchAndConsolidateRects(GetLocalClipCoverage(), false);
1648 accumulated = false;
1649 for (const DlRect& rect : rects) {
1650 // TODO (https://github.com/flutter/flutter/issues/114919): Attributes
1651 // are not necessarily `kDrawDisplayListFlags`.
1652 if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) {
1653 accumulated = true;
1654 }
1655 }
1656 }
1657 if (!accumulated) {
1658 return;
1659 }
1660
1661 DlPaint current_paint = current_;
1662 Push<DrawDisplayListOp>(0, display_list,
1663 opacity < SK_Scalar1 ? opacity : SK_Scalar1);
1664
1665 // This depth increment accounts for every draw call in the child
1666 // DisplayList and is in addition to the implicit depth increment
1667 // that was performed when we pushed the DrawDisplayListOp. The
1668 // eventual dispatcher can use or ignore the implicit depth increment
1669 // as it sees fit depending on whether it needs to do rendering
1670 // before or after the drawDisplayList op, but it must be accounted
1671 // for if the depth value accounting is to remain consistent between
1672 // the recording and dispatching process.
1673 depth_ += display_list->total_depth();
1674
1675 is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe();
1676 // Not really necessary if the developer is interacting with us via
1677 // our attribute-state-less DlCanvas methods, but this avoids surprises
1678 // for those who may have been using the stateful Dispatcher methods.
1679 SetAttributesFromPaint(current_paint,
1681
1682 // The non-nested op count accumulated in the |Push| method will include
1683 // this call to |drawDisplayList| for non-nested op count metrics.
1684 // But, for nested op count metrics we want the |drawDisplayList| call itself
1685 // to be transparent. So we subtract 1 from our accumulated nested count to
1686 // balance out against the 1 that was accumulated into the regular count.
1687 // This behavior is identical to the way SkPicture computed nested op counts.
1688 nested_op_count_ += display_list->op_count(true) - 1;
1689 nested_bytes_ += display_list->bytes(true);
1690 UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity());
1691 // Nop DisplayLists are eliminated above so we either affect transparent
1692 // pixels or we do not. We should not have [kNoEffect].
1693 UpdateLayerResult(display_list->modifies_transparent_black()
1694 ? OpResult::kAffectsAll
1695 : OpResult::kPreservesTransparency,
1696 display_list->max_root_blend_mode());
1697 if (display_list->root_has_backdrop_filter()) {
1698 current_layer().contains_backdrop_filter = true;
1699 }
1700}
1701
1702void DisplayListBuilder::drawText(const std::shared_ptr<DlText>& text,
1703 DlScalar x,
1704 DlScalar y) {
1706 OpResult result = PaintResult(current_, flags);
1707 if (result == OpResult::kNoEffect) {
1708 return;
1709 }
1710 DlRect bounds = text->GetBounds().Shift(x, y);
1711 bool unclipped = AccumulateOpBounds(bounds, flags);
1712 // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the
1713 // unit tests can use Fuchsia's font manager instead of the empty default.
1714 // Until then we might encounter empty bounds for otherwise valid text and
1715 // thus we ignore the results from AccumulateOpBounds.
1716#if defined(OS_FUCHSIA)
1717 unclipped = true;
1718#endif // OS_FUCHSIA
1719 if (unclipped) {
1720 Push<DrawTextOp>(0, text, x, y);
1721 // There is no way to query if the glyphs of a text blob overlap and
1722 // there are no current guarantees from either Skia or Impeller that
1723 // they will protect overlapping glyphs from the effects of overdraw
1724 // so we must make the conservative assessment that this DL layer is
1725 // not compatible with group opacity inheritance.
1726 UpdateLayerOpacityCompatibility(false);
1727 UpdateLayerResult(result);
1728 }
1729}
1730
1731void DisplayListBuilder::DrawText(const std::shared_ptr<DlText>& text,
1732 DlScalar x,
1733 DlScalar y,
1734 const DlPaint& paint) {
1735 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextFlags);
1736 drawText(text, x, y);
1737}
1738
1740 const DlColor color,
1741 const DlScalar elevation,
1742 bool transparent_occluder,
1743 DlScalar dpr) {
1744 OpResult result = PaintResult(DlPaint(color));
1745 if (result != OpResult::kNoEffect) {
1746 DlRect shadow_bounds =
1747 DlCanvas::ComputeShadowBounds(path, elevation, dpr, GetMatrix());
1748 if (AccumulateOpBounds(shadow_bounds, kDrawShadowFlags)) {
1749 transparent_occluder //
1750 ? Push<DrawShadowTransparentOccluderOp>(0, path, color, elevation,
1751 dpr)
1752 : Push<DrawShadowOp>(0, path, color, elevation, dpr);
1753 UpdateLayerOpacityCompatibility(false);
1754 UpdateLayerResult(result, DlBlendMode::kSrcOver);
1755 }
1756 }
1757}
1758
1759bool DisplayListBuilder::AdjustBoundsForPaint(DlRect& bounds,
1761 if (flags.ignores_paint()) {
1762 return true;
1763 }
1764
1765 if (flags.is_geometric()) {
1766 bool is_stroked = flags.is_stroked(current_.getDrawStyle());
1767
1768 DisplayListSpecialGeometryFlags special_flags =
1769 flags.GeometryFlags(is_stroked);
1770
1771 if (is_stroked) {
1772 // Determine the max multiplier to the stroke width first.
1773 DlScalar pad = 1.0f;
1774 if (current_.getStrokeJoin() == DlStrokeJoin::kMiter &&
1775 special_flags.may_have_acute_joins()) {
1776 pad = std::max(pad, current_.getStrokeMiter());
1777 }
1778 if (current_.getStrokeCap() == DlStrokeCap::kSquare &&
1779 special_flags.may_have_diagonal_caps()) {
1780 pad = std::max(pad, SK_ScalarSqrt2);
1781 }
1782 DlScalar min_stroke_width = 0.01;
1783 pad *= std::max(current_.getStrokeWidth() * 0.5f, min_stroke_width);
1784 bounds = bounds.Expand(pad, pad);
1785 }
1786 }
1787
1788 if (flags.applies_mask_filter()) {
1789 const DlMaskFilter* filter = current_.getMaskFilterPtr();
1790 if (filter) {
1791 switch (filter->type()) {
1793 FML_DCHECK(filter->asBlur());
1794 DlScalar mask_sigma_pad = filter->asBlur()->sigma() * 3.0;
1795 bounds = bounds.Expand(mask_sigma_pad, mask_sigma_pad);
1796 }
1797 }
1798 }
1799 }
1800
1801 // Color filter does not modify bounds even if it affects transparent
1802 // black because it is clipped by the "mask" of the primitive. That
1803 // property only comes into play when it is applied to something like
1804 // a layer.
1805
1806 if (flags.applies_image_filter()) {
1807 const DlImageFilter* filter = current_.getImageFilterPtr();
1808 if (filter) {
1809 DlRect dl_bounds;
1810 if (!filter->map_local_bounds(bounds, dl_bounds)) {
1811 return false;
1812 }
1813 bounds = dl_bounds;
1814 }
1815 }
1816
1817 return true;
1818}
1819
1820bool DisplayListBuilder::AccumulateUnbounded(const SaveInfo& save) {
1821 if (!save.has_valid_clip) {
1822 save.layer_info->is_unbounded = true;
1823 }
1824 DlRect global_clip = save.global_state.GetDeviceCullCoverage();
1825 DlRect layer_clip = save.global_state.GetLocalCullCoverage();
1826 if (global_clip.IsEmpty() || !save.layer_state.mapAndClipRect(&layer_clip)) {
1827 return false;
1828 }
1829 if (rtree_data_.has_value()) {
1830 FML_DCHECK(save.layer_info->global_space_accumulator.is_empty());
1831 rtree_data_->rects.push_back(global_clip);
1832 rtree_data_->indices.push_back(op_index_);
1833 } else {
1834 save.layer_info->global_space_accumulator.accumulate(global_clip);
1835 }
1836 save.layer_info->layer_local_accumulator.accumulate(layer_clip);
1837 return true;
1838}
1839
1840bool DisplayListBuilder::AccumulateOpBounds(DlRect& bounds,
1841 DisplayListAttributeFlags flags) {
1842 if (AdjustBoundsForPaint(bounds, flags)) {
1843 return AccumulateBounds(bounds);
1844 } else {
1845 return AccumulateUnbounded();
1846 }
1847}
1848
1849bool DisplayListBuilder::AccumulateBounds(const DlRect& bounds,
1850 SaveInfo& layer,
1851 int id) {
1852 if (bounds.IsEmpty()) {
1853 return false;
1854 }
1855 DlRect global_bounds;
1856 DlRect layer_bounds;
1857 if (!layer.global_state.mapAndClipRect(bounds, &global_bounds) ||
1858 !layer.layer_state.mapAndClipRect(bounds, &layer_bounds)) {
1859 return false;
1860 }
1861 if (rtree_data_.has_value()) {
1862 FML_DCHECK(layer.layer_info->global_space_accumulator.is_empty());
1863 if (id >= 0) {
1864 rtree_data_->rects.push_back(global_bounds);
1865 rtree_data_->indices.push_back(id);
1866 }
1867 } else {
1868 layer.layer_info->global_space_accumulator.accumulate(global_bounds);
1869 }
1870 layer.layer_info->layer_local_accumulator.accumulate(layer_bounds);
1871 return true;
1872}
1873
1874bool DisplayListBuilder::SaveInfo::AccumulateBoundsLocal(const DlRect& bounds) {
1875 if (bounds.IsEmpty()) {
1876 return false;
1877 }
1878 DlRect local_bounds;
1879 if (!layer_state.mapAndClipRect(bounds, &local_bounds)) {
1880 return false;
1881 }
1882 layer_info->layer_local_accumulator.accumulate(local_bounds);
1883 return true;
1884}
1885
1886bool DisplayListBuilder::paint_nops_on_transparency() {
1887 // SkImageFilter::canComputeFastBounds tests for transparency behavior
1888 // This test assumes that the blend mode checked down below will
1889 // NOP on transparent black.
1890 if (current_.getImageFilterPtr() &&
1892 return false;
1893 }
1894
1895 // We filter the transparent black that is used for the background of a
1896 // saveLayer and make sure it returns transparent black. If it does, then
1897 // the color filter will leave all area surrounding the contents of the
1898 // save layer untouched out to the edge of the output surface.
1899 // This test assumes that the blend mode checked down below will
1900 // NOP on transparent black.
1901 if (current_.getColorFilterPtr() &&
1903 return false;
1904 }
1905
1906 // Unusual blendmodes require us to process a saved layer
1907 // even with operations outside the clip.
1908 // For example, DstIn is used by masking layers.
1909 // https://code.google.com/p/skia/issues/detail?id=1291
1910 // https://crbug.com/401593
1911 switch (current_.getBlendMode()) {
1912 // For each of the following transfer modes, if the source
1913 // alpha is zero (our transparent black), the resulting
1914 // blended pixel is not necessarily equal to the original
1915 // destination pixel.
1916 // Mathematically, any time in the following equations where
1917 // the result is not d assuming source is 0
1918 case DlBlendMode::kClear: // r = 0
1919 case DlBlendMode::kSrc: // r = s
1920 case DlBlendMode::kSrcIn: // r = s * da
1921 case DlBlendMode::kDstIn: // r = d * sa
1922 case DlBlendMode::kSrcOut: // r = s * (1-da)
1923 case DlBlendMode::kDstATop: // r = d*sa + s*(1-da)
1924 case DlBlendMode::kModulate: // r = s*d
1925 return false;
1926 break;
1927
1928 // And in these equations, the result must be d if the
1929 // source is 0
1930 case DlBlendMode::kDst: // r = d
1931 case DlBlendMode::kSrcOver: // r = s + (1-sa)*d
1932 case DlBlendMode::kDstOver: // r = d + (1-da)*s
1933 case DlBlendMode::kDstOut: // r = d * (1-sa)
1934 case DlBlendMode::kSrcATop: // r = s*da + d*(1-sa)
1935 case DlBlendMode::kXor: // r = s*(1-da) + d*(1-sa)
1936 case DlBlendMode::kPlus: // r = min(s + d, 1)
1937 case DlBlendMode::kScreen: // r = s + d - s*d
1938 case DlBlendMode::kOverlay: // multiply or screen, depending on dest
1939 case DlBlendMode::kDarken: // rc = s + d - max(s*da, d*sa),
1940 // ra = kSrcOver
1941 case DlBlendMode::kLighten: // rc = s + d - min(s*da, d*sa),
1942 // ra = kSrcOver
1943 case DlBlendMode::kColorDodge: // brighten destination to reflect source
1944 case DlBlendMode::kColorBurn: // darken destination to reflect source
1945 case DlBlendMode::kHardLight: // multiply or screen, depending on source
1946 case DlBlendMode::kSoftLight: // lighten or darken, depending on source
1947 case DlBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)),
1948 // ra = kSrcOver
1949 case DlBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver
1950 case DlBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d
1951 case DlBlendMode::kHue: // ra = kSrcOver
1952 case DlBlendMode::kSaturation: // ra = kSrcOver
1953 case DlBlendMode::kColor: // ra = kSrcOver
1954 case DlBlendMode::kLuminosity: // ra = kSrcOver
1955 return true;
1956 break;
1957 }
1958}
1959
1960DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint,
1961 DisplayListAttributeFlags flags) {
1962 DlColor color;
1963 if (flags.applies_color()) {
1964 const DlColorSource* source = paint.getColorSourcePtr();
1965 if (source) {
1966 // Suspecting that we need to modulate the ColorSource color by the
1967 // color property, see https://github.com/flutter/flutter/issues/159507
1968 color = source->is_opaque() ? DlColor::kBlack() : kAnyColor;
1969 } else {
1970 color = paint.getColor();
1971 }
1972 } else if (flags.applies_alpha()) {
1973 // If the operation applies alpha, but not color, then the only impact
1974 // of the alpha is to modulate the output towards transparency.
1975 // We can not guarantee an opaque source even if the alpha is opaque
1976 // since that would require knowing something about the colors that
1977 // the alpha is modulating, but we can guarantee a transparent source
1978 // if the alpha is 0.
1979 color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor;
1980 } else {
1981 color = kAnyColor;
1982 }
1983 if (flags.applies_image_filter()) {
1984 auto filter = paint.getImageFilterPtr();
1985 if (filter) {
1986 if (!color.isTransparent() || filter->modifies_transparent_black()) {
1987 color = kAnyColor;
1988 }
1989 }
1990 }
1991 if (flags.applies_color_filter()) {
1992 auto filter = paint.getColorFilterPtr();
1993 if (filter) {
1994 if (!color.isTransparent() || filter->modifies_transparent_black()) {
1995 color = kAnyColor;
1996 }
1997 }
1998 }
1999 return color;
2000}
2001
2002DisplayListBuilder::OpResult DisplayListBuilder::PaintResult(
2003 const DlPaint& paint,
2004 DisplayListAttributeFlags flags) {
2005 if (current_info().is_nop) {
2006 return OpResult::kNoEffect;
2007 }
2008 if (flags.applies_blend()) {
2009 switch (paint.getBlendMode()) {
2010 // Nop blend mode (singular, there is only one)
2011 case DlBlendMode::kDst:
2012 return OpResult::kNoEffect;
2013
2014 // Always clears pixels blend mode (singular, there is only one)
2015 case DlBlendMode::kClear:
2016 return OpResult::kPreservesTransparency;
2017
2018 case DlBlendMode::kHue:
2019 case DlBlendMode::kSaturation:
2020 case DlBlendMode::kColor:
2021 case DlBlendMode::kLuminosity:
2022 case DlBlendMode::kColorBurn:
2023 return GetEffectiveColor(paint, flags).isTransparent()
2024 ? OpResult::kNoEffect
2025 : OpResult::kAffectsAll;
2026
2027 // kSrcIn modifies pixels towards transparency
2028 case DlBlendMode::kSrcIn:
2029 return OpResult::kPreservesTransparency;
2030
2031 // These blend modes preserve destination alpha
2032 case DlBlendMode::kSrcATop:
2033 case DlBlendMode::kDstOut:
2034 return GetEffectiveColor(paint, flags).isTransparent()
2035 ? OpResult::kNoEffect
2036 : OpResult::kPreservesTransparency;
2037
2038 // Always destructive blend modes, potentially not affecting transparency
2039 case DlBlendMode::kSrc:
2040 case DlBlendMode::kSrcOut:
2041 case DlBlendMode::kDstATop:
2042 return GetEffectiveColor(paint, flags).isTransparent()
2043 ? OpResult::kPreservesTransparency
2044 : OpResult::kAffectsAll;
2045
2046 // The kDstIn blend mode modifies the destination unless the
2047 // source color is opaque.
2048 case DlBlendMode::kDstIn:
2049 return GetEffectiveColor(paint, flags).isOpaque()
2050 ? OpResult::kNoEffect
2051 : OpResult::kPreservesTransparency;
2052
2053 // The next group of blend modes modifies the destination unless the
2054 // source color is transparent.
2055 case DlBlendMode::kSrcOver:
2056 case DlBlendMode::kDstOver:
2057 case DlBlendMode::kXor:
2058 case DlBlendMode::kPlus:
2059 case DlBlendMode::kScreen:
2060 case DlBlendMode::kMultiply:
2061 case DlBlendMode::kOverlay:
2062 case DlBlendMode::kDarken:
2063 case DlBlendMode::kLighten:
2064 case DlBlendMode::kColorDodge:
2065 case DlBlendMode::kHardLight:
2066 case DlBlendMode::kSoftLight:
2067 case DlBlendMode::kDifference:
2068 case DlBlendMode::kExclusion:
2069 return GetEffectiveColor(paint, flags).isTransparent()
2070 ? OpResult::kNoEffect
2071 : OpResult::kAffectsAll;
2072
2073 // Modulate only leaves the pixel alone when the source is white.
2074 case DlBlendMode::kModulate:
2075 return GetEffectiveColor(paint, flags) == DlColor::kWhite()
2076 ? OpResult::kNoEffect
2077 : OpResult::kPreservesTransparency;
2078 }
2079 }
2080 return OpResult::kAffectsAll;
2081}
2082
2083} // 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