Flutter Engine
The 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
5#include "flutter/display_list/dl_builder.h"
6
7#include "flutter/display_list/display_list.h"
8#include "flutter/display_list/dl_blend_mode.h"
9#include "flutter/display_list/dl_op_flags.h"
10#include "flutter/display_list/dl_op_records.h"
11#include "flutter/display_list/effects/dl_color_source.h"
12#include "flutter/display_list/utils/dl_bounds_accumulator.h"
13#include "fml/logging.h"
15
16namespace flutter {
17
18#define DL_BUILDER_PAGE 4096
19
20// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst.
21static void CopyV(void* dst) {}
22
23template <typename S, typename... Rest>
24static void CopyV(void* dst, const S* src, int n, Rest&&... rest) {
25 FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0)
26 << "Expected " << dst << " to be aligned for at least " << alignof(S)
27 << " bytes.";
28 // If n is 0, there is nothing to copy into dst from src.
29 if (n > 0) {
30 memcpy(dst, src, n * sizeof(S));
31 dst = reinterpret_cast<void*>(reinterpret_cast<uint8_t*>(dst) +
32 n * sizeof(S));
33 }
34 // Repeat for the next items, if any
35 CopyV(dst, std::forward<Rest>(rest)...);
36}
37
38static constexpr inline bool is_power_of_two(int value) {
39 return (value & (value - 1)) == 0;
40}
41
42template <typename T, typename... Args>
43void* DisplayListBuilder::Push(size_t pod, Args&&... args) {
44 size_t size = SkAlignPtr(sizeof(T) + pod);
45 FML_DCHECK(size < (1 << 24));
46 if (used_ + size > allocated_) {
47 static_assert(is_power_of_two(DL_BUILDER_PAGE),
48 "This math needs updating for non-pow2.");
49 // Next greater multiple of DL_BUILDER_PAGE.
50 allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1);
51 storage_.realloc(allocated_);
52 FML_DCHECK(storage_.get());
53 memset(storage_.get() + used_, 0, allocated_ - used_);
54 }
55 FML_DCHECK(used_ + size <= allocated_);
56 auto op = reinterpret_cast<T*>(storage_.get() + used_);
57 used_ += size;
58 new (op) T{std::forward<Args>(args)...};
59 op->type = T::kType;
60 op->size = size;
61 render_op_count_ += T::kRenderOpInc;
62 depth_ += T::kDepthInc * render_op_depth_cost_;
63 op_index_++;
64 return op + 1;
65}
66
68 while (layer_stack_.size() > 1) {
69 restore();
70 }
71
72 size_t bytes = used_;
73 int count = render_op_count_;
74 size_t nested_bytes = nested_bytes_;
75 int nested_count = nested_op_count_;
76 uint32_t total_depth = depth_;
77 bool compatible = current_layer_->is_group_opacity_compatible();
78 bool is_safe = is_ui_thread_safe_;
79 bool affects_transparency = current_layer_->affects_transparent_layer();
80
81 sk_sp<DlRTree> rtree = this->rtree();
82 SkRect bounds = rtree ? rtree->bounds() : this->bounds();
83
84 used_ = allocated_ = render_op_count_ = op_index_ = 0;
85 nested_bytes_ = nested_op_count_ = 0;
86 depth_ = 0;
87 is_ui_thread_safe_ = true;
88 storage_.realloc(bytes);
89 layer_stack_.pop_back();
90 layer_stack_.emplace_back();
91 current_layer_ = &layer_stack_.back();
92 tracker_.reset();
93 layer_tracker_.reset();
94 current_ = DlPaint();
95
96 return sk_sp<DisplayList>(
97 new DisplayList(std::move(storage_), bytes, count, nested_bytes,
98 nested_count, total_depth, bounds, compatible, is_safe,
99 affects_transparency, std::move(rtree)));
100}
101
103 bool prepare_rtree)
104 : tracker_(cull_rect, SkMatrix::I()) {
105 if (prepare_rtree) {
106 accumulator_ = std::make_unique<RTreeBoundsAccumulator>();
107 } else {
108 accumulator_ = std::make_unique<RectBoundsAccumulator>();
109 }
110
111 layer_stack_.emplace_back();
112 current_layer_ = &layer_stack_.back();
113}
114
116 uint8_t* ptr = storage_.get();
117 if (ptr) {
118 DisplayList::DisposeOps(ptr, ptr + used_);
119 }
120}
121
123 return tracker_.base_device_cull_rect().roundOut().size();
124}
125
130
131void DisplayListBuilder::onSetAntiAlias(bool aa) {
132 current_.setAntiAlias(aa);
133 Push<SetAntiAliasOp>(0, aa);
134}
135void DisplayListBuilder::onSetInvertColors(bool invert) {
136 current_.setInvertColors(invert);
137 Push<SetInvertColorsOp>(0, invert);
138 UpdateCurrentOpacityCompatibility();
139}
140void DisplayListBuilder::onSetStrokeCap(DlStrokeCap cap) {
141 current_.setStrokeCap(cap);
142 Push<SetStrokeCapOp>(0, cap);
143}
144void DisplayListBuilder::onSetStrokeJoin(DlStrokeJoin join) {
145 current_.setStrokeJoin(join);
146 Push<SetStrokeJoinOp>(0, join);
147}
148void DisplayListBuilder::onSetDrawStyle(DlDrawStyle style) {
149 current_.setDrawStyle(style);
150 Push<SetStyleOp>(0, style);
151}
152void DisplayListBuilder::onSetStrokeWidth(float width) {
153 current_.setStrokeWidth(width);
154 Push<SetStrokeWidthOp>(0, width);
155}
156void DisplayListBuilder::onSetStrokeMiter(float limit) {
157 current_.setStrokeMiter(limit);
158 Push<SetStrokeMiterOp>(0, limit);
159}
160void DisplayListBuilder::onSetColor(DlColor color) {
161 current_.setColor(color);
162 Push<SetColorOp>(0, color);
163}
164void DisplayListBuilder::onSetBlendMode(DlBlendMode mode) {
165 current_.setBlendMode(mode);
166 Push<SetBlendModeOp>(0, mode);
167 UpdateCurrentOpacityCompatibility();
168}
169
170void DisplayListBuilder::onSetColorSource(const DlColorSource* source) {
171 if (source == nullptr) {
172 current_.setColorSource(nullptr);
173 Push<ClearColorSourceOp>(0);
174 } else {
175 current_.setColorSource(source->shared());
176 is_ui_thread_safe_ = is_ui_thread_safe_ && source->isUIThreadSafe();
177 switch (source->type()) {
179 const DlColorColorSource* color_source = source->asColor();
180 current_.setColorSource(nullptr);
181 setColor(color_source->color());
182 break;
183 }
185 const DlImageColorSource* image_source = source->asImage();
186 FML_DCHECK(image_source);
187 Push<SetImageColorSourceOp>(0, image_source);
188 break;
189 }
191 const DlLinearGradientColorSource* linear = source->asLinearGradient();
193 void* pod = Push<SetPodColorSourceOp>(linear->size());
194 new (pod) DlLinearGradientColorSource(linear);
195 break;
196 }
198 const DlRadialGradientColorSource* radial = source->asRadialGradient();
199 FML_DCHECK(radial);
200 void* pod = Push<SetPodColorSourceOp>(radial->size());
201 new (pod) DlRadialGradientColorSource(radial);
202 break;
203 }
205 const DlConicalGradientColorSource* conical =
206 source->asConicalGradient();
207 FML_DCHECK(conical);
208 void* pod = Push<SetPodColorSourceOp>(conical->size());
209 new (pod) DlConicalGradientColorSource(conical);
210 break;
211 }
213 const DlSweepGradientColorSource* sweep = source->asSweepGradient();
214 FML_DCHECK(sweep);
215 void* pod = Push<SetPodColorSourceOp>(sweep->size());
216 new (pod) DlSweepGradientColorSource(sweep);
217 break;
218 }
220 const DlRuntimeEffectColorSource* effect = source->asRuntimeEffect();
221 FML_DCHECK(effect);
222 Push<SetRuntimeEffectColorSourceOp>(0, effect);
223 break;
224 }
225#ifdef IMPELLER_ENABLE_3D
226 case DlColorSourceType::kScene: {
227 const DlSceneColorSource* scene = source->asScene();
228 FML_DCHECK(scene);
229 Push<SetSceneColorSourceOp>(0, scene);
230 break;
231 }
232#endif // IMPELLER_ENABLE_3D
233 }
234 }
235}
236void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) {
237 if (filter == nullptr) {
238 current_.setImageFilter(nullptr);
239 Push<ClearImageFilterOp>(0);
240 } else {
241 current_.setImageFilter(filter->shared());
242 switch (filter->type()) {
244 const DlBlurImageFilter* blur_filter = filter->asBlur();
245 FML_DCHECK(blur_filter);
246 void* pod = Push<SetPodImageFilterOp>(blur_filter->size());
247 new (pod) DlBlurImageFilter(blur_filter);
248 break;
249 }
251 const DlDilateImageFilter* dilate_filter = filter->asDilate();
252 FML_DCHECK(dilate_filter);
253 void* pod = Push<SetPodImageFilterOp>(dilate_filter->size());
254 new (pod) DlDilateImageFilter(dilate_filter);
255 break;
256 }
258 const DlErodeImageFilter* erode_filter = filter->asErode();
259 FML_DCHECK(erode_filter);
260 void* pod = Push<SetPodImageFilterOp>(erode_filter->size());
261 new (pod) DlErodeImageFilter(erode_filter);
262 break;
263 }
265 const DlMatrixImageFilter* matrix_filter = filter->asMatrix();
266 FML_DCHECK(matrix_filter);
267 void* pod = Push<SetPodImageFilterOp>(matrix_filter->size());
268 new (pod) DlMatrixImageFilter(matrix_filter);
269 break;
270 }
274 Push<SetSharedImageFilterOp>(0, filter);
275 break;
276 }
277 }
278 }
279}
280void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) {
281 if (filter == nullptr) {
282 current_.setColorFilter(nullptr);
283 Push<ClearColorFilterOp>(0);
284 } else {
285 current_.setColorFilter(filter->shared());
286 switch (filter->type()) {
288 const DlBlendColorFilter* blend_filter = filter->asBlend();
289 FML_DCHECK(blend_filter);
290 void* pod = Push<SetPodColorFilterOp>(blend_filter->size());
291 new (pod) DlBlendColorFilter(blend_filter);
292 break;
293 }
295 const DlMatrixColorFilter* matrix_filter = filter->asMatrix();
296 FML_DCHECK(matrix_filter);
297 void* pod = Push<SetPodColorFilterOp>(matrix_filter->size());
298 new (pod) DlMatrixColorFilter(matrix_filter);
299 break;
300 }
302 void* pod = Push<SetPodColorFilterOp>(filter->size());
303 new (pod) DlSrgbToLinearGammaColorFilter();
304 break;
305 }
307 void* pod = Push<SetPodColorFilterOp>(filter->size());
308 new (pod) DlLinearToSrgbGammaColorFilter();
309 break;
310 }
311 }
312 }
313 UpdateCurrentOpacityCompatibility();
314}
315void DisplayListBuilder::onSetPathEffect(const DlPathEffect* effect) {
316 if (effect == nullptr) {
317 current_.setPathEffect(nullptr);
318 Push<ClearPathEffectOp>(0);
319 } else {
320 current_.setPathEffect(effect->shared());
321 switch (effect->type()) {
323 const DlDashPathEffect* dash_effect = effect->asDash();
324 void* pod = Push<SetPodPathEffectOp>(dash_effect->size());
325 new (pod) DlDashPathEffect(dash_effect);
326 break;
327 }
328 }
329 }
330}
331void DisplayListBuilder::onSetMaskFilter(const DlMaskFilter* filter) {
332 if (filter == nullptr) {
333 current_.setMaskFilter(nullptr);
334 render_op_depth_cost_ = 1u;
335 Push<ClearMaskFilterOp>(0);
336 } else {
337 current_.setMaskFilter(filter->shared());
338 render_op_depth_cost_ = 2u;
339 switch (filter->type()) {
341 const DlBlurMaskFilter* blur_filter = filter->asBlur();
342 FML_DCHECK(blur_filter);
343 void* pod = Push<SetPodMaskFilterOp>(blur_filter->size());
344 new (pod) DlBlurMaskFilter(blur_filter);
345 break;
346 }
347 }
348 }
349}
350
351void DisplayListBuilder::SetAttributesFromPaint(
352 const DlPaint& paint,
353 const DisplayListAttributeFlags flags) {
354 if (flags.applies_anti_alias()) {
355 setAntiAlias(paint.isAntiAlias());
356 }
357 if (flags.applies_alpha_or_color()) {
358 setColor(paint.getColor());
359 }
360 if (flags.applies_blend()) {
361 setBlendMode(paint.getBlendMode());
362 }
363 if (flags.applies_style()) {
364 setDrawStyle(paint.getDrawStyle());
365 }
366 if (flags.is_stroked(paint.getDrawStyle())) {
367 setStrokeWidth(paint.getStrokeWidth());
368 setStrokeMiter(paint.getStrokeMiter());
369 setStrokeCap(paint.getStrokeCap());
370 setStrokeJoin(paint.getStrokeJoin());
371 }
372 if (flags.applies_shader()) {
373 setColorSource(paint.getColorSource().get());
374 }
375 if (flags.applies_color_filter()) {
376 setInvertColors(paint.isInvertColors());
377 setColorFilter(paint.getColorFilter().get());
378 }
379 if (flags.applies_image_filter()) {
380 setImageFilter(paint.getImageFilter().get());
381 }
382 if (flags.applies_path_effect()) {
383 setPathEffect(paint.getPathEffect().get());
384 }
385 if (flags.applies_mask_filter()) {
386 setMaskFilter(paint.getMaskFilter().get());
387 }
388}
389
390void DisplayListBuilder::checkForDeferredSave() {
391 if (current_layer_->has_deferred_save_op_) {
392 size_t save_offset_ = used_;
393 Push<SaveOp>(0);
394 current_layer_->save_offset_ = save_offset_;
395 current_layer_->start_depth_ = depth_;
396 current_layer_->has_deferred_save_op_ = false;
397 }
398}
399
401 layer_stack_.emplace_back();
402 current_layer_ = &layer_stack_.back();
403
404 FML_DCHECK(layer_stack_.size() >= 2u);
405 // Note we can't use the previous value of current_layer_ because
406 // the emplace_back() may have moved the storage locations, so we
407 // recompute the location of the penultimate layer info here.
408 auto parent_layer = &layer_stack_.end()[-2];
409
410 current_layer_->has_deferred_save_op_ = true;
411 current_layer_->is_nop_ = parent_layer->is_nop_;
412
413 if (parent_layer->layer_accumulator_) {
414 FML_DCHECK(layer_tracker_);
415 // If the previous layer was using an accumulator, we need to keep
416 // filling it with content bounds. We reuse the previous accumulator
417 // for this layer, but clone the associated transform so that new
418 // transform mutations are restricted to this save/restore context.
419 current_layer_->layer_accumulator_ = parent_layer->layer_accumulator_;
420 layer_tracker_->save();
421 } else {
422 FML_DCHECK(!layer_tracker_);
423 }
424
425 tracker_.save();
426 accumulator()->save();
427}
428
430 if (layer_stack_.size() <= 1) {
431 return;
432 }
433
434 SaveOpBase* op = reinterpret_cast<SaveOpBase*>(storage_.get() +
435 current_layer_->save_offset());
436
437 if (!current_layer_->has_deferred_save_op_) {
438 op->restore_index = op_index_;
439 op->total_content_depth = depth_ - current_layer_->start_depth_;
440 Push<RestoreOp>(0);
441 if (current_layer_->is_save_layer()) {
442 // A saveLayer will usually do a final copy to the main buffer in
443 // addition to its content, but that is accounted for outside of
444 // the total content depth computed above.
445 depth_ += render_op_depth_cost_;
446 }
447 }
448
449 std::shared_ptr<const DlImageFilter> filter = current_layer_->filter();
450 {
451 // We should not pop the stack until we are done synching up the current
452 // and parent layers.
453 auto parent_layer = &layer_stack_.end()[-2];
454
455 if (current_layer_->is_save_layer()) {
456 // Layers are never deferred for now, we need to update the
457 // following code if we ever do saveLayer culling...
458 FML_DCHECK(!current_layer_->has_deferred_save_op_);
459 FML_DCHECK(current_layer_->layer_accumulator_);
460
461 SkRect content_bounds = current_layer_->layer_accumulator_->bounds();
462
463 switch (op->type) {
464 case DisplayListOpType::kSaveLayer:
465 case DisplayListOpType::kSaveLayerBackdrop: {
466 SaveLayerOpBase* layer_op = reinterpret_cast<SaveLayerOpBase*>(op);
467 if (op->options.bounds_from_caller()) {
468 if (!content_bounds.isEmpty() &&
469 !layer_op->rect.contains(content_bounds)) {
471 content_bounds.intersect(layer_op->rect);
472 }
473 }
474 layer_op->rect = content_bounds;
475 break;
476 }
477 default:
479 }
480
481 if (layer_tracker_->getSaveCount() > 1) {
482 layer_tracker_->restore();
483 } else {
484 // If this was the last layer in the tracker, then there should
485 // be no parent saveLayer.
486 FML_DCHECK(!parent_layer->layer_accumulator_);
487 layer_tracker_.reset();
488 }
489
490 if (parent_layer->layer_accumulator_) {
491 SkRect bounds_for_parent = content_bounds;
492 if (filter) {
493 if (!filter->map_local_bounds(bounds_for_parent, bounds_for_parent)) {
494 parent_layer->set_unbounded();
495 }
496 }
497 // The content_bounds were accumulated in the base coordinate system
498 // of the current layer, and have been adjusted there according to
499 // its image filter.
500 // The content bounds accumulation of the parent layer is relative
501 // to the parent's base coordinate system, so we need to adjust
502 // bounds_for_parent to that coordinate space.
503 FML_DCHECK(layer_tracker_);
504 layer_tracker_->mapRect(&bounds_for_parent);
505 parent_layer->layer_accumulator_->accumulate(bounds_for_parent);
506 }
507
508 if (current_layer_->is_group_opacity_compatible()) {
509 // We are now going to go back and modify the matching saveLayer
510 // call to add the option indicating it can distribute an opacity
511 // value to its children.
512 //
513 // Note that this operation cannot and does not change the size
514 // or structure of the SaveLayerOp record. It only sets an option
515 // flag on an existing field.
516 //
517 // Note that these kinds of modification operations on data already
518 // in the DisplayList are only allowed *during* the build phase.
519 // Once built, the DisplayList records must remain read only to
520 // ensure consistency of rendering and |Equals()| behavior.
522 }
523 } else {
524 if (layer_tracker_) {
525 FML_DCHECK(layer_tracker_->getSaveCount() > 1);
526 layer_tracker_->restore();
527 }
528 // For regular save() ops there was no protecting layer so we have to
529 // accumulate the inheritance properties into the enclosing layer.
530 if (current_layer_->cannot_inherit_opacity()) {
531 parent_layer->mark_incompatible();
532 } else if (current_layer_->has_compatible_op()) {
533 parent_layer->add_compatible_op();
534 }
535 }
536 }
537
538 // Remember whether the outgoing layer was unbounded so we can adjust
539 // for it below after we apply the outgoing layer's filter to the bounds.
540 bool popped_was_unbounded = current_layer_->is_unbounded();
541
542 // parent_layer is no longer in scope, time to pop the layer.
543 layer_stack_.pop_back();
544 tracker_.restore();
545 current_layer_ = &layer_stack_.back();
546
547 // As we pop the accumulator, use the filter that was applied to the
548 // outgoing layer (saved above, if any) to adjust the bounds that
549 // were accumulated while that layer was active.
550 if (filter) {
551 const SkRect clip = tracker_.device_cull_rect();
552 if (!accumulator()->restore(
553 [filter = filter, matrix = GetTransform()](const SkRect& input,
554 SkRect& output) {
555 SkIRect output_bounds;
556 bool ret = filter->map_device_bounds(input.roundOut(), matrix,
557 output_bounds);
558 output.set(output_bounds);
559 return ret;
560 },
561 &clip)) {
562 popped_was_unbounded = true;
563 }
564 } else {
565 accumulator()->restore();
566 }
567
568 if (popped_was_unbounded) {
569 AccumulateUnbounded();
570 }
571}
572void DisplayListBuilder::RestoreToCount(int restore_count) {
573 FML_DCHECK(restore_count <= GetSaveCount());
574 while (restore_count < GetSaveCount() && GetSaveCount() > 1) {
575 restore();
576 }
577}
579 const SaveLayerOptions in_options,
580 const DlImageFilter* backdrop) {
582 DisplayListAttributeFlags flags = options.renders_with_attributes()
585 OpResult result = PaintResult(current_, flags);
586 if (result == OpResult::kNoEffect) {
587 save();
588 current_layer_->is_nop_ = true;
589 return;
590 }
591
592 size_t save_layer_offset = used_;
593
594 if (options.renders_with_attributes()) {
595 // The actual flood of the outer layer clip will occur after the
596 // (eventual) corresponding restore is called, but rather than
597 // remember this information in the LayerInfo until the restore
598 // method is processed, we just mark the unbounded state up front.
599 // Another reason to accumulate the clip here rather than in
600 // restore is so that this savelayer will be tagged in the rtree
601 // with its full bounds and the right op_index so that it doesn't
602 // get culled during rendering.
603 if (!paint_nops_on_transparency()) {
604 // We will fill the clip of the outer layer when we restore.
605 // Accumulate should always return true here because if the
606 // clip was empty then that would have been caught up above
607 // when we tested the PaintResult.
608 [[maybe_unused]] bool unclipped = AccumulateUnbounded();
609 FML_DCHECK(unclipped);
610 }
611 CheckLayerOpacityCompatibility(true);
612 layer_stack_.emplace_back(save_layer_offset, depth_);
613 layer_stack_.back().filter_ = current_.getImageFilter();
614 } else {
615 CheckLayerOpacityCompatibility(false);
616 layer_stack_.emplace_back(save_layer_offset, depth_);
617 }
618 current_layer_ = &layer_stack_.back();
619 current_layer_->is_save_layer_ = true;
620
621 tracker_.save();
622 accumulator()->save();
623
624 SkRect record_bounds;
625 if (in_options.bounds_from_caller()) {
626 options = options.with_bounds_from_caller();
627 record_bounds = bounds;
628 } else {
629 FML_DCHECK(record_bounds.isEmpty());
630 }
631 current_layer_->layer_accumulator_.reset(new RectBoundsAccumulator());
632 if (layer_tracker_) {
633 layer_tracker_->save();
634 layer_tracker_->setTransform(SkMatrix::I());
635 } else {
636 SkRect cull_rect;
637 if (in_options.bounds_from_caller()) {
638 cull_rect = bounds;
639 } else {
640 cull_rect = tracker_.local_cull_rect();
641 }
642 layer_tracker_.reset(
643 new DisplayListMatrixClipTracker(cull_rect, SkMatrix::I()));
644 }
645
646 if (backdrop) {
647 // A backdrop will affect up to the entire surface, bounded by the clip
648 // Accumulate should always return true here because if the
649 // clip was empty then that would have been caught up above
650 // when we tested the PaintResult.
651 [[maybe_unused]] bool unclipped = AccumulateUnbounded();
652 FML_DCHECK(unclipped);
653 Push<SaveLayerBackdropOp>(0, options, record_bounds, backdrop);
654 } else {
655 Push<SaveLayerOp>(0, options, record_bounds);
656 }
657
658 if (options.renders_with_attributes()) {
659 // |current_opacity_compatibility_| does not take an ImageFilter into
660 // account because an individual primitive with an ImageFilter can apply
661 // opacity on top of it. But, if the layer is applying the ImageFilter
662 // then it cannot pass the opacity on.
663 if (!current_opacity_compatibility_ ||
664 current_.getImageFilter() != nullptr) {
665 UpdateLayerOpacityCompatibility(false);
666 }
667 }
668 UpdateLayerResult(result);
669
670 if (options.renders_with_attributes() && current_.getImageFilter()) {
671 // We use |resetCullRect| here because we will be accumulating bounds of
672 // primitives before applying the filter to those bounds. We might
673 // encounter a primitive whose bounds are clipped, but whose filtered
674 // bounds will not be clipped. If the individual rendering ops bounds
675 // are clipped, it will not contribute to the overall bounds which
676 // could lead to inaccurate (subset) bounds of the DisplayList.
677 // We need to reset the cull rect here to avoid this premature clipping.
678 // The filtered bounds will be clipped to the existing clip rect when
679 // this layer is restored.
680 // If bounds is null then the original cull_rect will be used.
681 tracker_.resetLocalCullRect(in_options.bounds_from_caller() ? &bounds
682 : nullptr);
683 } else if (in_options.bounds_from_caller()) {
684 // Even though Skia claims that the bounds are only a hint, they actually
685 // use them as the temporary layer bounds during rendering the layer, so
686 // we set them as if a clip operation were performed.
687 tracker_.clipRect(bounds, ClipOp::kIntersect, false);
688 }
689}
691 const DlPaint* paint,
692 const DlImageFilter* backdrop) {
694 SkRect temp_bounds;
695 if (bounds) {
696 options = options.with_bounds_from_caller();
697 temp_bounds = *bounds;
698 } else {
699 temp_bounds.setEmpty();
700 }
701 if (paint != nullptr) {
702 options = options.with_renders_with_attributes();
703 SetAttributesFromPaint(*paint,
705 }
706 saveLayer(temp_bounds, options, backdrop);
707}
708
710 if (std::isfinite(tx) && std::isfinite(ty) && (tx != 0.0 || ty != 0.0)) {
711 checkForDeferredSave();
712 Push<TranslateOp>(0, tx, ty);
713 tracker_.translate(tx, ty);
714 if (layer_tracker_) {
715 layer_tracker_->translate(tx, ty);
716 }
717 }
718}
720 if (std::isfinite(sx) && std::isfinite(sy) && (sx != 1.0 || sy != 1.0)) {
721 checkForDeferredSave();
722 Push<ScaleOp>(0, sx, sy);
723 tracker_.scale(sx, sy);
724 if (layer_tracker_) {
725 layer_tracker_->scale(sx, sy);
726 }
727 }
728}
730 if (SkScalarMod(degrees, 360.0) != 0.0) {
731 checkForDeferredSave();
732 Push<RotateOp>(0, degrees);
733 tracker_.rotate(degrees);
734 if (layer_tracker_) {
735 layer_tracker_->rotate(degrees);
736 }
737 }
738}
740 if (std::isfinite(sx) && std::isfinite(sy) && (sx != 0.0 || sy != 0.0)) {
741 checkForDeferredSave();
742 Push<SkewOp>(0, sx, sy);
743 tracker_.skew(sx, sy);
744 if (layer_tracker_) {
745 layer_tracker_->skew(sx, sy);
746 }
747 }
748}
749
750// clang-format off
751
752// 2x3 2D affine subset of a 4x4 transform in row major order
754 SkScalar mxx, SkScalar mxy, SkScalar mxt,
755 SkScalar myx, SkScalar myy, SkScalar myt) {
756 if (std::isfinite(mxx) && std::isfinite(myx) &&
757 std::isfinite(mxy) && std::isfinite(myy) &&
758 std::isfinite(mxt) && std::isfinite(myt)) {
759 if (mxx == 1 && mxy == 0 &&
760 myx == 0 && myy == 1) {
761 Translate(mxt, myt);
762 } else {
763 checkForDeferredSave();
764 Push<Transform2DAffineOp>(0,
765 mxx, mxy, mxt,
766 myx, myy, myt);
767 tracker_.transform2DAffine(mxx, mxy, mxt,
768 myx, myy, myt);
769 if (layer_tracker_) {
770 layer_tracker_->transform2DAffine(mxx, mxy, mxt,
771 myx, myy, myt);
772 }
773 }
774 }
775}
776// full 4x4 transform in row major order
778 SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
779 SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
780 SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
781 SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) {
782 if ( mxz == 0 &&
783 myz == 0 &&
784 mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 &&
785 mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) {
786 Transform2DAffine(mxx, mxy, mxt,
787 myx, myy, myt);
788 } else if (std::isfinite(mxx) && std::isfinite(mxy) &&
789 std::isfinite(mxz) && std::isfinite(mxt) &&
790 std::isfinite(myx) && std::isfinite(myy) &&
791 std::isfinite(myz) && std::isfinite(myt) &&
792 std::isfinite(mzx) && std::isfinite(mzy) &&
793 std::isfinite(mzz) && std::isfinite(mzt) &&
794 std::isfinite(mwx) && std::isfinite(mwy) &&
795 std::isfinite(mwz) && std::isfinite(mwt)) {
796 checkForDeferredSave();
797 Push<TransformFullPerspectiveOp>(0,
798 mxx, mxy, mxz, mxt,
799 myx, myy, myz, myt,
800 mzx, mzy, mzz, mzt,
801 mwx, mwy, mwz, mwt);
802 tracker_.transformFullPerspective(mxx, mxy, mxz, mxt,
803 myx, myy, myz, myt,
804 mzx, mzy, mzz, mzt,
805 mwx, mwy, mwz, mwt);
806 if (layer_tracker_) {
807 layer_tracker_->transformFullPerspective(mxx, mxy, mxz, mxt,
808 myx, myy, myz, myt,
809 mzx, mzy, mzz, mzt,
810 mwx, mwy, mwz, mwt);
811 }
812 }
813}
814// clang-format on
816 checkForDeferredSave();
817 Push<TransformResetOp>(0);
818 if (layer_tracker_) {
819 // The matrices in layer_tracker_ and tracker_ are similar, but
820 // start at a different base transform. The tracker_ potentially
821 // has some number of transform operations on it that prefix the
822 // operations accumulated in layer_tracker_. So we can't set them both
823 // to identity in parallel as they would no longer maintain their
824 // relationship to each other.
825 // Instead we reinterpret this operation as transforming by the
826 // inverse of the current transform. Doing so to tracker_ sets it
827 // to identity so we can avoid the math there, but we must do the
828 // math the long way for layer_tracker_. This becomes:
829 // layer_tracker_.transform(tracker_.inverse());
830 if (!layer_tracker_->inverseTransform(tracker_)) {
831 // If the inverse operation failed then that means that either
832 // the matrix above the current layer was singular, or the matrix
833 // became singular while we were accumulating the current layer.
834 // In either case, we should no longer be accumulating any
835 // contents so we set the layer tracking transform to a singular one.
836 layer_tracker_->setTransform(SkMatrix::Scale(0.0f, 0.0f));
837 }
838 }
839 tracker_.setIdentity();
840}
842 if (matrix != nullptr) {
843 Transform(SkM44(*matrix));
844 }
845}
847 if (m44 != nullptr) {
849 m44->rc(0, 0), m44->rc(0, 1), m44->rc(0, 2), m44->rc(0, 3),
850 m44->rc(1, 0), m44->rc(1, 1), m44->rc(1, 2), m44->rc(1, 3),
851 m44->rc(2, 0), m44->rc(2, 1), m44->rc(2, 2), m44->rc(2, 3),
852 m44->rc(3, 0), m44->rc(3, 1), m44->rc(3, 2), m44->rc(3, 3));
853 }
854}
855
857 ClipOp clip_op,
858 bool is_aa) {
859 if (!rect.isFinite()) {
860 return;
861 }
862 tracker_.clipRect(rect, clip_op, is_aa);
863 if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) {
864 current_layer_->is_nop_ = true;
865 return;
866 }
867 checkForDeferredSave();
868 switch (clip_op) {
869 case ClipOp::kIntersect:
870 Push<ClipIntersectRectOp>(0, rect, is_aa);
871 break;
872 case ClipOp::kDifference:
873 Push<ClipDifferenceRectOp>(0, rect, is_aa);
874 break;
875 }
876}
878 ClipOp clip_op,
879 bool is_aa) {
880 if (rrect.isRect()) {
881 clipRect(rrect.rect(), clip_op, is_aa);
882 } else {
883 tracker_.clipRRect(rrect, clip_op, is_aa);
884 if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) {
885 current_layer_->is_nop_ = true;
886 return;
887 }
888 checkForDeferredSave();
889 switch (clip_op) {
890 case ClipOp::kIntersect:
891 Push<ClipIntersectRRectOp>(0, rrect, is_aa);
892 break;
893 case ClipOp::kDifference:
894 Push<ClipDifferenceRRectOp>(0, rrect, is_aa);
895 break;
896 }
897 }
898}
900 ClipOp clip_op,
901 bool is_aa) {
902 if (!path.isInverseFillType()) {
903 SkRect rect;
904 if (path.isRect(&rect)) {
905 this->clipRect(rect, clip_op, is_aa);
906 return;
907 }
908 SkRRect rrect;
909 if (path.isOval(&rect)) {
910 rrect.setOval(rect);
911 this->clipRRect(rrect, clip_op, is_aa);
912 return;
913 }
914 if (path.isRRect(&rrect)) {
915 this->clipRRect(rrect, clip_op, is_aa);
916 return;
917 }
918 }
919 tracker_.clipPath(path, clip_op, is_aa);
920 if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) {
921 current_layer_->is_nop_ = true;
922 return;
923 }
924 checkForDeferredSave();
925 switch (clip_op) {
926 case ClipOp::kIntersect:
927 Push<ClipIntersectPathOp>(0, path, is_aa);
928 break;
929 case ClipOp::kDifference:
930 Push<ClipDifferencePathOp>(0, path, is_aa);
931 break;
932 }
933}
934
935bool DisplayListBuilder::QuickReject(const SkRect& bounds) const {
936 return tracker_.content_culled(bounds);
937}
938
940 OpResult result = PaintResult(current_, kDrawPaintFlags);
941 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
942 Push<DrawPaintOp>(0);
943 CheckLayerOpacityCompatibility();
944 UpdateLayerResult(result);
945 }
946}
948 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPaintFlags);
949 drawPaint();
950}
952 OpResult result = PaintResult(DlPaint(color).setBlendMode(mode));
953 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
954 Push<DrawColorOp>(0, color, mode);
955 CheckLayerOpacityCompatibility(mode);
956 UpdateLayerResult(result);
957 }
958}
959void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) {
960 SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted();
962 (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags
964 OpResult result = PaintResult(current_, flags);
965 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
966 Push<DrawLineOp>(0, p0, p1);
967 CheckLayerOpacityCompatibility();
968 UpdateLayerResult(result);
969 }
970}
972 const SkPoint& p1,
973 const DlPaint& paint) {
974 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawLineFlags);
975 drawLine(p0, p1);
976}
979 OpResult result = PaintResult(current_, flags);
980 if (result != OpResult::kNoEffect &&
981 AccumulateOpBounds(rect.makeSorted(), flags)) {
982 Push<DrawRectOp>(0, rect);
983 CheckLayerOpacityCompatibility();
984 UpdateLayerResult(result);
985 }
986}
988 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRectFlags);
989 drawRect(rect);
990}
993 OpResult result = PaintResult(current_, flags);
994 if (result != OpResult::kNoEffect &&
995 AccumulateOpBounds(bounds.makeSorted(), flags)) {
996 Push<DrawOvalOp>(0, bounds);
997 CheckLayerOpacityCompatibility();
998 UpdateLayerResult(result);
999 }
1000}
1002 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawOvalFlags);
1003 drawOval(bounds);
1004}
1007 OpResult result = PaintResult(current_, flags);
1008 if (result != OpResult::kNoEffect) {
1009 SkRect bounds = SkRect::MakeLTRB(center.fX - radius, center.fY - radius,
1010 center.fX + radius, center.fY + radius);
1011 if (AccumulateOpBounds(bounds, flags)) {
1012 Push<DrawCircleOp>(0, center, radius);
1013 CheckLayerOpacityCompatibility();
1014 UpdateLayerResult(result);
1015 }
1016 }
1017}
1019 SkScalar radius,
1020 const DlPaint& paint) {
1021 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawCircleFlags);
1022 drawCircle(center, radius);
1023}
1025 if (rrect.isRect()) {
1026 drawRect(rrect.rect());
1027 } else if (rrect.isOval()) {
1028 drawOval(rrect.rect());
1029 } else {
1031 OpResult result = PaintResult(current_, flags);
1032 if (result != OpResult::kNoEffect &&
1033 AccumulateOpBounds(rrect.getBounds(), flags)) {
1034 Push<DrawRRectOp>(0, rrect);
1035 CheckLayerOpacityCompatibility();
1036 UpdateLayerResult(result);
1037 }
1038 }
1039}
1041 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRRectFlags);
1042 drawRRect(rrect);
1043}
1045 const SkRRect& inner) {
1047 OpResult result = PaintResult(current_, flags);
1048 if (result != OpResult::kNoEffect &&
1049 AccumulateOpBounds(outer.getBounds(), flags)) {
1050 Push<DrawDRRectOp>(0, outer, inner);
1051 CheckLayerOpacityCompatibility();
1052 UpdateLayerResult(result);
1053 }
1054}
1056 const SkRRect& inner,
1057 const DlPaint& paint) {
1058 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawDRRectFlags);
1059 drawDRRect(outer, inner);
1060}
1063 OpResult result = PaintResult(current_, flags);
1064 if (result != OpResult::kNoEffect) {
1065 bool is_visible = path.isInverseFillType()
1066 ? AccumulateUnbounded()
1067 : AccumulateOpBounds(path.getBounds(), flags);
1068 if (is_visible) {
1069 Push<DrawPathOp>(0, path);
1070 CheckLayerOpacityHairlineCompatibility();
1071 UpdateLayerResult(result);
1072 }
1073 }
1074}
1076 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPathFlags);
1077 drawPath(path);
1078}
1079
1082 SkScalar sweep,
1083 bool useCenter) {
1085 useCenter //
1088 OpResult result = PaintResult(current_, flags);
1089 // This could be tighter if we compute where the start and end
1090 // angles are and then also consider the quadrants swept and
1091 // the center if specified.
1092 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
1093 Push<DrawArcOp>(0, bounds, start, sweep, useCenter);
1094 if (useCenter) {
1095 CheckLayerOpacityHairlineCompatibility();
1096 } else {
1097 CheckLayerOpacityCompatibility();
1098 }
1099 UpdateLayerResult(result);
1100 }
1101}
1104 SkScalar sweep,
1105 bool useCenter,
1106 const DlPaint& paint) {
1107 SetAttributesFromPaint(
1109 drawArc(bounds, start, sweep, useCenter);
1110}
1111
1112DisplayListAttributeFlags DisplayListBuilder::FlagsForPointMode(
1113 PointMode mode) {
1114 switch (mode) {
1117 case PointMode::kLines:
1119 case PointMode::kPolygon:
1121 }
1123}
1125 uint32_t count,
1126 const SkPoint pts[]) {
1127 if (count == 0) {
1128 return;
1129 }
1130 DisplayListAttributeFlags flags = FlagsForPointMode(mode);
1131 OpResult result = PaintResult(current_, flags);
1132 if (result == OpResult::kNoEffect) {
1133 return;
1134 }
1135
1137 int bytes = count * sizeof(SkPoint);
1138 RectBoundsAccumulator ptBounds;
1139 for (size_t i = 0; i < count; i++) {
1140 ptBounds.accumulate(pts[i]);
1141 }
1142 SkRect point_bounds = ptBounds.bounds();
1143 if (!AccumulateOpBounds(point_bounds, flags)) {
1144 return;
1145 }
1146
1147 void* data_ptr;
1148 switch (mode) {
1149 case PointMode::kPoints:
1150 data_ptr = Push<DrawPointsOp>(bytes, count);
1151 break;
1152 case PointMode::kLines:
1153 data_ptr = Push<DrawLinesOp>(bytes, count);
1154 break;
1155 case PointMode::kPolygon:
1156 data_ptr = Push<DrawPolygonOp>(bytes, count);
1157 break;
1158 default:
1160 return;
1161 }
1162 CopyV(data_ptr, pts, count);
1163 // drawPoints treats every point or line (or segment of a polygon)
1164 // as a completely separate operation meaning we cannot ensure
1165 // distribution of group opacity without analyzing the mode and the
1166 // bounds of every sub-primitive.
1167 // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c
1168 UpdateLayerOpacityCompatibility(false);
1169 UpdateLayerResult(result);
1170}
1172 uint32_t count,
1173 const SkPoint pts[],
1174 const DlPaint& paint) {
1175 SetAttributesFromPaint(paint, FlagsForPointMode(mode));
1176 drawPoints(mode, count, pts);
1177}
1179 DlBlendMode mode) {
1181 OpResult result = PaintResult(current_, flags);
1182 if (result != OpResult::kNoEffect &&
1183 AccumulateOpBounds(vertices->bounds(), flags)) {
1184 void* pod = Push<DrawVerticesOp>(vertices->size(), mode);
1185 new (pod) DlVertices(vertices);
1186 // DrawVertices applies its colors to the paint so we have no way
1187 // of controlling opacity using the current paint attributes.
1188 // Although, examination of the |mode| might find some predictable
1189 // cases.
1190 UpdateLayerOpacityCompatibility(false);
1191 UpdateLayerResult(result);
1192 }
1193}
1196 const DlPaint& paint) {
1197 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawVerticesFlags);
1198 drawVertices(vertices, mode);
1199}
1200
1202 const SkPoint point,
1203 DlImageSampling sampling,
1204 bool render_with_attributes) {
1205 DisplayListAttributeFlags flags = render_with_attributes //
1208 OpResult result = PaintResult(current_, flags);
1209 if (result == OpResult::kNoEffect) {
1210 return;
1211 }
1212 SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, //
1213 image->width(), image->height());
1214 if (AccumulateOpBounds(bounds, flags)) {
1215 render_with_attributes
1216 ? Push<DrawImageWithAttrOp>(0, image, point, sampling)
1217 : Push<DrawImageOp>(0, image, point, sampling);
1218 CheckLayerOpacityCompatibility(render_with_attributes);
1219 UpdateLayerResult(result);
1220 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1221 }
1222}
1224 const SkPoint point,
1225 DlImageSampling sampling,
1226 const DlPaint* paint) {
1227 if (paint != nullptr) {
1228 SetAttributesFromPaint(*paint,
1230 drawImage(image, point, sampling, true);
1231 } else {
1232 drawImage(image, point, sampling, false);
1233 }
1234}
1236 const SkRect& src,
1237 const SkRect& dst,
1238 DlImageSampling sampling,
1239 bool render_with_attributes,
1240 SrcRectConstraint constraint) {
1241 DisplayListAttributeFlags flags = render_with_attributes
1244 OpResult result = PaintResult(current_, flags);
1245 if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
1246 Push<DrawImageRectOp>(0, image, src, dst, sampling, render_with_attributes,
1247 constraint);
1248 CheckLayerOpacityCompatibility(render_with_attributes);
1249 UpdateLayerResult(result);
1250 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1251 }
1252}
1254 const SkRect& src,
1255 const SkRect& dst,
1256 DlImageSampling sampling,
1257 const DlPaint* paint,
1258 SrcRectConstraint constraint) {
1259 if (paint != nullptr) {
1260 SetAttributesFromPaint(*paint,
1262 drawImageRect(image, src, dst, sampling, true, constraint);
1263 } else {
1264 drawImageRect(image, src, dst, sampling, false, constraint);
1265 }
1266}
1268 const SkIRect& center,
1269 const SkRect& dst,
1270 DlFilterMode filter,
1271 bool render_with_attributes) {
1272 DisplayListAttributeFlags flags = render_with_attributes
1275 OpResult result = PaintResult(current_, flags);
1276 if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
1277 render_with_attributes
1278 ? Push<DrawImageNineWithAttrOp>(0, image, center, dst, filter)
1279 : Push<DrawImageNineOp>(0, image, center, dst, filter);
1280 CheckLayerOpacityCompatibility(render_with_attributes);
1281 UpdateLayerResult(result);
1282 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1283 }
1284}
1286 const SkIRect& center,
1287 const SkRect& dst,
1288 DlFilterMode filter,
1289 const DlPaint* paint) {
1290 if (paint != nullptr) {
1291 SetAttributesFromPaint(*paint,
1293 drawImageNine(image, center, dst, filter, true);
1294 } else {
1295 drawImageNine(image, center, dst, filter, false);
1296 }
1297}
1299 const SkRSXform xform[],
1300 const SkRect tex[],
1301 const DlColor colors[],
1302 int count,
1304 DlImageSampling sampling,
1305 const SkRect* cull_rect,
1306 bool render_with_attributes) {
1307 DisplayListAttributeFlags flags = render_with_attributes //
1310 OpResult result = PaintResult(current_, flags);
1311 if (result == OpResult::kNoEffect) {
1312 return;
1313 }
1314 SkPoint quad[4];
1315 RectBoundsAccumulator atlasBounds;
1316 for (int i = 0; i < count; i++) {
1317 const SkRect& src = tex[i];
1318 xform[i].toQuad(src.width(), src.height(), quad);
1319 for (int j = 0; j < 4; j++) {
1320 atlasBounds.accumulate(quad[j]);
1321 }
1322 }
1323 if (atlasBounds.is_empty() ||
1324 !AccumulateOpBounds(atlasBounds.bounds(), flags)) {
1325 return;
1326 }
1327
1328 int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect));
1329 void* data_ptr;
1330 if (colors != nullptr) {
1331 bytes += count * sizeof(DlColor);
1332 if (cull_rect != nullptr) {
1333 data_ptr =
1334 Push<DrawAtlasCulledOp>(bytes, atlas, count, mode, sampling, true,
1335 *cull_rect, render_with_attributes);
1336 } else {
1337 data_ptr = Push<DrawAtlasOp>(bytes, atlas, count, mode, sampling, true,
1338 render_with_attributes);
1339 }
1340 CopyV(data_ptr, xform, count, tex, count, colors, count);
1341 } else {
1342 if (cull_rect != nullptr) {
1343 data_ptr =
1344 Push<DrawAtlasCulledOp>(bytes, atlas, count, mode, sampling, false,
1345 *cull_rect, render_with_attributes);
1346 } else {
1347 data_ptr = Push<DrawAtlasOp>(bytes, atlas, count, mode, sampling, false,
1348 render_with_attributes);
1349 }
1350 CopyV(data_ptr, xform, count, tex, count);
1351 }
1352 // drawAtlas treats each image as a separate operation so we cannot rely
1353 // on it to distribute the opacity without overlap without checking all
1354 // of the transforms and texture rectangles.
1355 UpdateLayerOpacityCompatibility(false);
1356 UpdateLayerResult(result);
1357 is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe();
1358}
1360 const SkRSXform xform[],
1361 const SkRect tex[],
1362 const DlColor colors[],
1363 int count,
1365 DlImageSampling sampling,
1366 const SkRect* cull_rect,
1367 const DlPaint* paint) {
1368 if (paint != nullptr) {
1369 SetAttributesFromPaint(*paint,
1371 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1372 true);
1373 } else {
1374 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1375 false);
1376 }
1377}
1378
1380 SkScalar opacity) {
1381 if (!std::isfinite(opacity) || opacity <= SK_ScalarNearlyZero ||
1382 display_list->op_count() == 0 || display_list->bounds().isEmpty() ||
1383 current_layer_->is_nop_) {
1384 return;
1385 }
1386 const SkRect bounds = display_list->bounds();
1387 bool accumulated;
1388 switch (accumulator()->type()) {
1390 accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags);
1391 break;
1393 auto rtree = display_list->rtree();
1394 if (rtree) {
1395 std::list<SkRect> rects =
1396 rtree->searchAndConsolidateRects(GetLocalClipBounds(), false);
1397 accumulated = false;
1398 for (const SkRect& rect : rects) {
1399 // TODO (https://github.com/flutter/flutter/issues/114919): Attributes
1400 // are not necessarily `kDrawDisplayListFlags`.
1401 if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) {
1402 accumulated = true;
1403 }
1404 }
1405 } else {
1406 accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags);
1407 }
1408 break;
1409 }
1410 if (!accumulated) {
1411 return;
1412 }
1413
1414 DlPaint current_paint = current_;
1415 Push<DrawDisplayListOp>(0, display_list,
1416 opacity < SK_Scalar1 ? opacity : SK_Scalar1);
1417
1418 // This depth increment accounts for every draw call in the child
1419 // DisplayList and is in addition to the implicit depth increment
1420 // that was performed when we pushed the DrawDisplayListOp. The
1421 // eventual dispatcher can use or ignore the implicit depth increment
1422 // as it sees fit depending on whether it needs to do rendering
1423 // before or after the drawDisplayList op, but it must be accounted
1424 // for if the depth value accounting is to remain consistent between
1425 // the recording and dispatching process.
1426 depth_ += display_list->total_depth();
1427
1428 is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe();
1429 // Not really necessary if the developer is interacting with us via
1430 // our attribute-state-less DlCanvas methods, but this avoids surprises
1431 // for those who may have been using the stateful Dispatcher methods.
1432 SetAttributesFromPaint(current_paint,
1434
1435 // The non-nested op count accumulated in the |Push| method will include
1436 // this call to |drawDisplayList| for non-nested op count metrics.
1437 // But, for nested op count metrics we want the |drawDisplayList| call itself
1438 // to be transparent. So we subtract 1 from our accumulated nested count to
1439 // balance out against the 1 that was accumulated into the regular count.
1440 // This behavior is identical to the way SkPicture computed nested op counts.
1441 nested_op_count_ += display_list->op_count(true) - 1;
1442 nested_bytes_ += display_list->bytes(true);
1443 UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity());
1444 // Nop DisplayLists are eliminated above so we either affect transparent
1445 // pixels or we do not. We should not have [kNoEffect].
1446 UpdateLayerResult(display_list->modifies_transparent_black()
1447 ? OpResult::kAffectsAll
1448 : OpResult::kPreservesTransparency);
1449}
1451 SkScalar x,
1452 SkScalar y) {
1454 OpResult result = PaintResult(current_, flags);
1455 if (result == OpResult::kNoEffect) {
1456 return;
1457 }
1458 bool unclipped = AccumulateOpBounds(blob->bounds().makeOffset(x, y), flags);
1459 // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the
1460 // unit tests can use Fuchsia's font manager instead of the empty default.
1461 // Until then we might encounter empty bounds for otherwise valid text and
1462 // thus we ignore the results from AccumulateOpBounds.
1463#if defined(OS_FUCHSIA)
1464 unclipped = true;
1465#endif // OS_FUCHSIA
1466 if (unclipped) {
1467 Push<DrawTextBlobOp>(0, blob, x, y);
1468 // There is no way to query if the glyphs of a text blob overlap and
1469 // there are no current guarantees from either Skia or Impeller that
1470 // they will protect overlapping glyphs from the effects of overdraw
1471 // so we must make the conservative assessment that this DL layer is
1472 // not compatible with group opacity inheritance.
1473 UpdateLayerOpacityCompatibility(false);
1474 UpdateLayerResult(result);
1475 }
1476}
1478 SkScalar x,
1479 SkScalar y,
1480 const DlPaint& paint) {
1481 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextBlobFlags);
1482 drawTextBlob(blob, x, y);
1483}
1484
1486 const std::shared_ptr<impeller::TextFrame>& text_frame,
1487 SkScalar x,
1488 SkScalar y) {
1490 OpResult result = PaintResult(current_, flags);
1491 if (result == OpResult::kNoEffect) {
1492 return;
1493 }
1494 impeller::Rect bounds = text_frame->GetBounds();
1495 SkRect sk_bounds = SkRect::MakeLTRB(bounds.GetLeft(), bounds.GetTop(),
1496 bounds.GetRight(), bounds.GetBottom());
1497 bool unclipped = AccumulateOpBounds(sk_bounds.makeOffset(x, y), flags);
1498 // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the
1499 // unit tests can use Fuchsia's font manager instead of the empty default.
1500 // Until then we might encounter empty bounds for otherwise valid text and
1501 // thus we ignore the results from AccumulateOpBounds.
1502#if defined(OS_FUCHSIA)
1503 unclipped = true;
1504#endif // OS_FUCHSIA
1505 if (unclipped) {
1506 Push<DrawTextFrameOp>(0, text_frame, x, y);
1507 // There is no way to query if the glyphs of a text blob overlap and
1508 // there are no current guarantees from either Skia or Impeller that
1509 // they will protect overlapping glyphs from the effects of overdraw
1510 // so we must make the conservative assessment that this DL layer is
1511 // not compatible with group opacity inheritance.
1512 UpdateLayerOpacityCompatibility(false);
1513 UpdateLayerResult(result);
1514 }
1515}
1516
1518 const std::shared_ptr<impeller::TextFrame>& text_frame,
1519 SkScalar x,
1520 SkScalar y,
1521 const DlPaint& paint) {
1522 SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawTextBlobFlags);
1523 drawTextFrame(text_frame, x, y);
1524}
1525
1527 const DlColor color,
1528 const SkScalar elevation,
1529 bool transparent_occluder,
1530 SkScalar dpr) {
1531 OpResult result = PaintResult(DlPaint(color));
1532 if (result != OpResult::kNoEffect) {
1533 SkRect shadow_bounds =
1535 if (AccumulateOpBounds(shadow_bounds, kDrawShadowFlags)) {
1536 transparent_occluder //
1537 ? Push<DrawShadowTransparentOccluderOp>(0, path, color, elevation,
1538 dpr)
1539 : Push<DrawShadowOp>(0, path, color, elevation, dpr);
1540 UpdateLayerOpacityCompatibility(false);
1541 UpdateLayerResult(result);
1542 }
1543 }
1544}
1545
1546bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds,
1548 if (flags.ignores_paint()) {
1549 return true;
1550 }
1551
1552 if (flags.is_geometric()) {
1553 bool is_stroked = flags.is_stroked(current_.getDrawStyle());
1554
1555 // Path effect occurs before stroking...
1556 DisplayListSpecialGeometryFlags special_flags =
1557 flags.WithPathEffect(current_.getPathEffectPtr(), is_stroked);
1558 if (current_.getPathEffect()) {
1559 auto effect_bounds = current_.getPathEffect()->effect_bounds(bounds);
1560 if (!effect_bounds.has_value()) {
1561 return false;
1562 }
1563 bounds = effect_bounds.value();
1564 }
1565
1566 if (is_stroked) {
1567 // Determine the max multiplier to the stroke width first.
1568 SkScalar pad = 1.0f;
1569 if (current_.getStrokeJoin() == DlStrokeJoin::kMiter &&
1570 special_flags.may_have_acute_joins()) {
1571 pad = std::max(pad, current_.getStrokeMiter());
1572 }
1573 if (current_.getStrokeCap() == DlStrokeCap::kSquare &&
1574 special_flags.may_have_diagonal_caps()) {
1575 pad = std::max(pad, SK_ScalarSqrt2);
1576 }
1577 SkScalar min_stroke_width = 0.01;
1578 pad *= std::max(current_.getStrokeWidth() * 0.5f, min_stroke_width);
1579 bounds.outset(pad, pad);
1580 }
1581 }
1582
1583 if (flags.applies_mask_filter()) {
1584 auto filter = current_.getMaskFilter();
1585 if (filter) {
1586 switch (filter->type()) {
1588 FML_DCHECK(filter->asBlur());
1589 SkScalar mask_sigma_pad = filter->asBlur()->sigma() * 3.0;
1590 bounds.outset(mask_sigma_pad, mask_sigma_pad);
1591 }
1592 }
1593 }
1594 }
1595
1596 // Color filter does not modify bounds even if it affects transparent
1597 // black because it is clipped by the "mask" of the primitive. That
1598 // property only comes into play when it is applied to something like
1599 // a layer.
1600
1601 if (flags.applies_image_filter()) {
1602 auto filter = current_.getImageFilterPtr();
1603 if (filter && !filter->map_local_bounds(bounds, bounds)) {
1604 return false;
1605 }
1606 }
1607
1608 return true;
1609}
1610
1611bool DisplayListBuilder::AccumulateUnbounded() {
1612 SkRect clip = tracker_.device_cull_rect();
1613 if (clip.isEmpty()) {
1614 return false;
1615 }
1616 accumulator()->accumulate(clip, op_index_);
1617 if (current_layer_->layer_accumulator_) {
1618 FML_DCHECK(layer_tracker_);
1619 current_layer_->layer_accumulator_->accumulate(
1620 layer_tracker_->device_cull_rect());
1621 }
1622 return true;
1623}
1624
1625bool DisplayListBuilder::AccumulateOpBounds(SkRect& bounds,
1626 DisplayListAttributeFlags flags) {
1627 if (AdjustBoundsForPaint(bounds, flags)) {
1628 return AccumulateBounds(bounds);
1629 } else {
1630 return AccumulateUnbounded();
1631 }
1632}
1633bool DisplayListBuilder::AccumulateBounds(SkRect& bounds) {
1634 if (!bounds.isEmpty()) {
1635 SkRect device_bounds;
1636 tracker_.mapRect(bounds, &device_bounds);
1637 if (device_bounds.intersect(tracker_.device_cull_rect())) {
1638 accumulator()->accumulate(device_bounds, op_index_);
1639 if (current_layer_->layer_accumulator_) {
1640 FML_DCHECK(layer_tracker_);
1641 SkRect layer_bounds;
1642 layer_tracker_->mapRect(bounds, &layer_bounds);
1643 current_layer_->layer_accumulator_->accumulate(layer_bounds);
1644 }
1645 return true;
1646 }
1647 }
1648 return false;
1649}
1650
1651bool DisplayListBuilder::paint_nops_on_transparency() {
1652 // SkImageFilter::canComputeFastBounds tests for transparency behavior
1653 // This test assumes that the blend mode checked down below will
1654 // NOP on transparent black.
1655 if (current_.getImageFilterPtr() &&
1657 return false;
1658 }
1659
1660 // We filter the transparent black that is used for the background of a
1661 // saveLayer and make sure it returns transparent black. If it does, then
1662 // the color filter will leave all area surrounding the contents of the
1663 // save layer untouched out to the edge of the output surface.
1664 // This test assumes that the blend mode checked down below will
1665 // NOP on transparent black.
1666 if (current_.getColorFilterPtr() &&
1668 return false;
1669 }
1670
1671 // Unusual blendmodes require us to process a saved layer
1672 // even with operations outside the clip.
1673 // For example, DstIn is used by masking layers.
1674 // https://code.google.com/p/skia/issues/detail?id=1291
1675 // https://crbug.com/401593
1676 switch (current_.getBlendMode()) {
1677 // For each of the following transfer modes, if the source
1678 // alpha is zero (our transparent black), the resulting
1679 // blended pixel is not necessarily equal to the original
1680 // destination pixel.
1681 // Mathematically, any time in the following equations where
1682 // the result is not d assuming source is 0
1683 case DlBlendMode::kClear: // r = 0
1684 case DlBlendMode::kSrc: // r = s
1685 case DlBlendMode::kSrcIn: // r = s * da
1686 case DlBlendMode::kDstIn: // r = d * sa
1687 case DlBlendMode::kSrcOut: // r = s * (1-da)
1688 case DlBlendMode::kDstATop: // r = d*sa + s*(1-da)
1689 case DlBlendMode::kModulate: // r = s*d
1690 return false;
1691 break;
1692
1693 // And in these equations, the result must be d if the
1694 // source is 0
1695 case DlBlendMode::kDst: // r = d
1696 case DlBlendMode::kSrcOver: // r = s + (1-sa)*d
1697 case DlBlendMode::kDstOver: // r = d + (1-da)*s
1698 case DlBlendMode::kDstOut: // r = d * (1-sa)
1699 case DlBlendMode::kSrcATop: // r = s*da + d*(1-sa)
1700 case DlBlendMode::kXor: // r = s*(1-da) + d*(1-sa)
1701 case DlBlendMode::kPlus: // r = min(s + d, 1)
1702 case DlBlendMode::kScreen: // r = s + d - s*d
1703 case DlBlendMode::kOverlay: // multiply or screen, depending on dest
1704 case DlBlendMode::kDarken: // rc = s + d - max(s*da, d*sa),
1705 // ra = kSrcOver
1706 case DlBlendMode::kLighten: // rc = s + d - min(s*da, d*sa),
1707 // ra = kSrcOver
1708 case DlBlendMode::kColorDodge: // brighten destination to reflect source
1709 case DlBlendMode::kColorBurn: // darken destination to reflect source
1710 case DlBlendMode::kHardLight: // multiply or screen, depending on source
1711 case DlBlendMode::kSoftLight: // lighten or darken, depending on source
1712 case DlBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)),
1713 // ra = kSrcOver
1714 case DlBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver
1715 case DlBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d
1716 case DlBlendMode::kHue: // ra = kSrcOver
1717 case DlBlendMode::kSaturation: // ra = kSrcOver
1718 case DlBlendMode::kColor: // ra = kSrcOver
1719 case DlBlendMode::kLuminosity: // ra = kSrcOver
1720 return true;
1721 break;
1722 }
1723}
1724
1725DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint,
1726 DisplayListAttributeFlags flags) {
1727 DlColor color;
1728 if (flags.applies_color()) {
1729 const DlColorSource* source = paint.getColorSourcePtr();
1730 if (source) {
1731 if (source->asColor()) {
1732 color = source->asColor()->color();
1733 } else {
1734 color = source->is_opaque() ? DlColor::kBlack() : kAnyColor;
1735 }
1736 } else {
1737 color = paint.getColor();
1738 }
1739 } else if (flags.applies_alpha()) {
1740 // If the operation applies alpha, but not color, then the only impact
1741 // of the alpha is to modulate the output towards transparency.
1742 // We can not guarantee an opaque source even if the alpha is opaque
1743 // since that would require knowing something about the colors that
1744 // the alpha is modulating, but we can guarantee a transparent source
1745 // if the alpha is 0.
1746 color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor;
1747 } else {
1748 color = kAnyColor;
1749 }
1750 if (flags.applies_image_filter()) {
1751 auto filter = paint.getImageFilterPtr();
1752 if (filter) {
1753 if (!color.isTransparent() || filter->modifies_transparent_black()) {
1754 color = kAnyColor;
1755 }
1756 }
1757 }
1758 if (flags.applies_color_filter()) {
1759 auto filter = paint.getColorFilterPtr();
1760 if (filter) {
1761 if (!color.isTransparent() || filter->modifies_transparent_black()) {
1762 color = kAnyColor;
1763 }
1764 }
1765 }
1766 return color;
1767}
1768
1769DisplayListBuilder::OpResult DisplayListBuilder::PaintResult(
1770 const DlPaint& paint,
1771 DisplayListAttributeFlags flags) {
1772 if (current_layer_->is_nop_) {
1773 return OpResult::kNoEffect;
1774 }
1775 if (flags.applies_blend()) {
1776 switch (paint.getBlendMode()) {
1777 // Nop blend mode (singular, there is only one)
1778 case DlBlendMode::kDst:
1779 return OpResult::kNoEffect;
1780
1781 // Always clears pixels blend mode (singular, there is only one)
1783 return OpResult::kPreservesTransparency;
1784
1785 case DlBlendMode::kHue:
1790 return GetEffectiveColor(paint, flags).isTransparent()
1791 ? OpResult::kNoEffect
1792 : OpResult::kAffectsAll;
1793
1794 // kSrcIn modifies pixels towards transparency
1796 return OpResult::kPreservesTransparency;
1797
1798 // These blend modes preserve destination alpha
1801 return GetEffectiveColor(paint, flags).isTransparent()
1802 ? OpResult::kNoEffect
1803 : OpResult::kPreservesTransparency;
1804
1805 // Always destructive blend modes, potentially not affecting transparency
1806 case DlBlendMode::kSrc:
1809 return GetEffectiveColor(paint, flags).isTransparent()
1810 ? OpResult::kPreservesTransparency
1811 : OpResult::kAffectsAll;
1812
1813 // The kDstIn blend mode modifies the destination unless the
1814 // source color is opaque.
1816 return GetEffectiveColor(paint, flags).isOpaque()
1817 ? OpResult::kNoEffect
1818 : OpResult::kPreservesTransparency;
1819
1820 // The next group of blend modes modifies the destination unless the
1821 // source color is transparent.
1824 case DlBlendMode::kXor:
1825 case DlBlendMode::kPlus:
1836 return GetEffectiveColor(paint, flags).isTransparent()
1837 ? OpResult::kNoEffect
1838 : OpResult::kAffectsAll;
1839
1840 // Modulate only leaves the pixel alone when the source is white.
1842 return GetEffectiveColor(paint, flags) == DlColor::kWhite()
1843 ? OpResult::kNoEffect
1844 : OpResult::kPreservesTransparency;
1845 }
1846 }
1847 return OpResult::kAffectsAll;
1848}
1849
1850} // namespace flutter
const char * options
int count
static bool compatible(const MTLRenderPassAttachmentDescriptor *first, const MTLRenderPassAttachmentDescriptor *second, const GrMtlPipelineState *pipelineState)
SkColor4f color
static constexpr T SkAlignPtr(T x)
Definition SkAlign.h:23
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
#define SkScalarMod(x, y)
Definition SkScalar.h:41
#define SK_Scalar1
Definition SkScalar.h:18
#define SK_ScalarNearlyZero
Definition SkScalar.h:99
#define SK_ScalarSqrt2
Definition SkScalar.h:20
static SkScalar center(float pos0, float pos1)
const sk_sp< Effect > & get() const
sk_sp< SkImage > asImage() const
Definition SkBitmap.cpp:645
int width() const
Definition SkImage.h:285
int height() const
Definition SkImage.h:291
Definition SkM44.h:150
SkScalar rc(int r, int c) const
Definition SkM44.h:261
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
static const SkMatrix & I()
bool isEmpty() const
Definition SkPath.cpp:406
bool isOval() const
Definition SkRRect.h:85
const SkRect & rect() const
Definition SkRRect.h:264
void setOval(const SkRect &oval)
Definition SkRRect.cpp:30
bool isRect() const
Definition SkRRect.h:84
const SkRect & getBounds() const
Definition SkRRect.h:279
virtual void accumulate(const SkRect &r, int index=0)=0
virtual void restore()=0
void setBlendMode(DlBlendMode mode) override
Definition dl_builder.h:315
void Skew(SkScalar sx, SkScalar sy) override
SkISize GetBaseLayerSize() const override
void DrawLine(const SkPoint &p0, const SkPoint &p1, const DlPaint &paint) override
void drawRect(const SkRect &rect) override
void RestoreToCount(int restore_count) override
void clipRRect(const SkRRect &rrect, ClipOp clip_op, bool is_aa) override
Definition dl_builder.h:400
void DrawDRRect(const SkRRect &outer, const SkRRect &inner, const DlPaint &paint) override
void DrawVertices(const DlVertices *vertices, DlBlendMode mode, const DlPaint &paint) override
void setColorSource(const DlColorSource *source) override
Definition dl_builder.h:321
void TransformFullPerspective(SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override
void drawPath(const SkPath &path) override
SkRect GetLocalClipBounds() const override
Definition dl_builder.h:139
void DrawRect(const SkRect &rect, const DlPaint &paint) override
void transformFullPerspective(SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override
Definition dl_builder.h:381
void setDrawStyle(DlDrawStyle style) override
Definition dl_builder.h:291
void Transform(const SkMatrix *matrix) override
void setStrokeCap(DlStrokeCap cap) override
Definition dl_builder.h:279
void setMaskFilter(const DlMaskFilter *filter) override
Definition dl_builder.h:345
void DrawColor(DlColor color, DlBlendMode mode) override
void DrawOval(const SkRect &bounds, const DlPaint &paint) override
void clipRect(const SkRect &rect, ClipOp clip_op, bool is_aa) override
Definition dl_builder.h:396
void setImageFilter(const DlImageFilter *filter) override
Definition dl_builder.h:327
void TransformReset() override
void ClipRRect(const SkRRect &rrect, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false) override
void setStrokeMiter(float limit) override
Definition dl_builder.h:303
void saveLayer(const SkRect &bounds, const SaveLayerOptions options, const DlImageFilter *backdrop) override
bool QuickReject(const SkRect &bounds) const override
void Translate(SkScalar tx, SkScalar ty) override
void DrawTextFrame(const std::shared_ptr< impeller::TextFrame > &text_frame, SkScalar x, SkScalar y, const DlPaint &paint) override
void DrawRRect(const SkRRect &rrect, const DlPaint &paint) override
void Scale(SkScalar sx, SkScalar sy) override
void DrawPath(const SkPath &path, const DlPaint &paint) override
SkMatrix GetTransform() const override
Definition dl_builder.h:113
void drawAtlas(const sk_sp< DlImage > atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], int count, DlBlendMode mode, DlImageSampling sampling, const SkRect *cullRect, bool render_with_attributes) override
void Rotate(SkScalar degrees) override
void setStrokeJoin(DlStrokeJoin join) override
Definition dl_builder.h:285
void DrawCircle(const SkPoint &center, SkScalar radius, const DlPaint &paint) override
void DrawPoints(PointMode mode, uint32_t count, const SkPoint pts[], const DlPaint &paint) override
void Transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myt) override
void setStrokeWidth(float width) override
Definition dl_builder.h:297
DisplayListBuilder(bool prepare_rtree)
Definition dl_builder.h:35
void drawImageNine(const sk_sp< DlImage > image, const SkIRect &center, const SkRect &dst, DlFilterMode filter, bool render_with_attributes) override
void drawArc(const SkRect &bounds, SkScalar start, SkScalar sweep, bool useCenter) override
void drawTextFrame(const std::shared_ptr< impeller::TextFrame > &text_frame, SkScalar x, SkScalar y) override
SkImageInfo GetImageInfo() const override
void drawRRect(const SkRRect &rrect) override
void drawDRRect(const SkRRect &outer, const SkRRect &inner) override
void drawImageRect(const sk_sp< DlImage > image, const SkRect &src, const SkRect &dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint=SrcRectConstraint::kFast) override
void DrawTextBlob(const sk_sp< SkTextBlob > &blob, SkScalar x, SkScalar y, const DlPaint &paint) override
void DrawImage(const sk_sp< DlImage > &image, const SkPoint point, DlImageSampling sampling, const DlPaint *paint=nullptr) override
void drawVertices(const DlVertices *vertices, DlBlendMode mode) override
void DrawShadow(const SkPath &path, const DlColor color, const SkScalar elevation, bool transparent_occluder, SkScalar dpr) override
void setAntiAlias(bool aa) override
Definition dl_builder.h:267
void DrawPaint(const DlPaint &paint) override
void SaveLayer(const SkRect *bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr) override
void setPathEffect(const DlPathEffect *effect) override
Definition dl_builder.h:339
sk_sp< DisplayList > Build()
Definition dl_builder.cc:67
void setInvertColors(bool invert) override
Definition dl_builder.h:273
void ClipRect(const SkRect &rect, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false) override
void DrawImageRect(const sk_sp< DlImage > &image, const SkRect &src, const SkRect &dst, DlImageSampling sampling, const DlPaint *paint=nullptr, SrcRectConstraint constraint=SrcRectConstraint::kFast) override
void setColorFilter(const DlColorFilter *filter) override
Definition dl_builder.h:333
void DrawDisplayList(const sk_sp< DisplayList > display_list, SkScalar opacity=SK_Scalar1) override
void drawPoints(PointMode mode, uint32_t count, const SkPoint pts[]) override
void ClipPath(const SkPath &path, ClipOp clip_op=ClipOp::kIntersect, bool is_aa=false) override
void DrawImageNine(const sk_sp< DlImage > &image, const SkIRect &center, const SkRect &dst, DlFilterMode filter, const DlPaint *paint=nullptr) override
void drawCircle(const SkPoint &center, SkScalar radius) override
void drawOval(const SkRect &bounds) override
void drawTextBlob(const sk_sp< SkTextBlob > blob, SkScalar x, SkScalar y) override
int GetSaveCount() const override
Definition dl_builder.h:58
void drawLine(const SkPoint &p0, const SkPoint &p1) override
void DrawArc(const SkRect &bounds, SkScalar start, SkScalar sweep, bool useCenter, const DlPaint &paint) override
void DrawAtlas(const sk_sp< DlImage > &atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], int count, DlBlendMode mode, DlImageSampling sampling, const SkRect *cullRect, const DlPaint *paint=nullptr) override
void setColor(DlColor color) override
Definition dl_builder.h:309
void drawImage(const sk_sp< DlImage > image, const SkPoint point, DlImageSampling sampling, bool render_with_attributes) override
void clipRect(const DlRect &rect, ClipOp op, bool is_aa)
void clipPath(const SkPath &path, ClipOp op, bool is_aa)
void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myt)
void resetLocalCullRect(const DlRect *cull_rect=nullptr)
void translate(SkScalar tx, SkScalar ty)
void clipRRect(const SkRRect &rrect, ClipOp op, bool is_aa)
void transformFullPerspective(SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt)
void skew(SkScalar skx, SkScalar sky)
bool content_culled(const SkRect &content_bounds) const
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 kDrawTextBlobFlags
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 kDrawShadowFlags
static constexpr DisplayListAttributeFlags kDrawImageFlags
static constexpr DisplayListAttributeFlags kDrawHVLineFlags
static constexpr DisplayListAttributeFlags kDrawRRectFlags
static constexpr DisplayListAttributeFlags kDrawDRRectFlags
static constexpr DisplayListAttributeFlags kDrawRectFlags
void realloc(size_t count)
@ kPoints
draw each point separately
static SkRect ComputeShadowBounds(const SkPath &path, float elevation, SkScalar dpr, const SkMatrix &ctm)
Definition dl_canvas.cc:12
virtual bool modifies_transparent_black() const =0
virtual bool modifies_transparent_black() const =0
static constexpr int kMaxDrawPointsCount
DlStrokeCap getStrokeCap() const
Definition dl_paint.h:100
DlPaint & setPathEffect(const std::shared_ptr< DlPathEffect > &pathEffect)
Definition dl_paint.h:184
DlPaint & setColor(DlColor color)
Definition dl_paint.h:71
DlPaint & setAntiAlias(bool isAntiAlias)
Definition dl_paint.h:59
DlPaint & setInvertColors(bool isInvertColors)
Definition dl_paint.h:65
DlBlendMode getBlendMode() const
Definition dl_paint.h:84
DlPaint & setColorFilter(const std::shared_ptr< const DlColorFilter > &filter)
Definition dl_paint.h:145
DlPaint & setMaskFilter(const std::shared_ptr< DlMaskFilter > &filter)
Definition dl_paint.h:171
float getStrokeMiter() const
Definition dl_paint.h:122
std::shared_ptr< const DlPathEffect > getPathEffect() const
Definition dl_paint.h:180
DlStrokeJoin getStrokeJoin() const
Definition dl_paint.h:108
DlPaint & setStrokeCap(DlStrokeCap cap)
Definition dl_paint.h:103
DlPaint & setStrokeWidth(float width)
Definition dl_paint.h:117
const DlColorFilter * getColorFilterPtr() const
Definition dl_paint.h:144
DlPaint & setStrokeMiter(float miter)
Definition dl_paint.h:123
DlPaint & setBlendMode(DlBlendMode mode)
Definition dl_paint.h:87
const DlImageFilter * getImageFilterPtr() const
Definition dl_paint.h:157
const DlPathEffect * getPathEffectPtr() const
Definition dl_paint.h:183
DlDrawStyle getDrawStyle() const
Definition dl_paint.h:92
std::shared_ptr< const DlMaskFilter > getMaskFilter() const
Definition dl_paint.h:167
DlPaint & setImageFilter(const std::shared_ptr< const DlImageFilter > &filter)
Definition dl_paint.h:158
float getStrokeWidth() const
Definition dl_paint.h:116
std::shared_ptr< const DlImageFilter > getImageFilter() const
Definition dl_paint.h:154
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:95
DlPaint & setStrokeJoin(DlStrokeJoin join)
Definition dl_paint.h:111
DlPaint & setColorSource(std::shared_ptr< const DlColorSource > source)
Definition dl_paint.h:132
Holds all of the data (both required and optional) for a DisplayList drawVertices call.
Definition dl_vertices.h:71
SkRect bounds() const
Returns the bounds of the vertices.
size_t size() const
Returns the size of the object including all of the inlined data.
void accumulate(SkScalar x, SkScalar y)
SaveLayerOptions with_can_distribute_opacity() const
bool bounds_from_caller() const
SaveLayerOptions without_optimizations() const
SaveLayerOptions with_content_is_clipped() const
const Paint & paint
#define DL_BUILDER_PAGE
Definition dl_builder.cc:18
sk_sp< SkImage > image
Definition examples.cpp:29
SkBitmap source
Definition examples.cpp:28
float SkScalar
Definition extension.cpp:12
FlutterSemanticsFlag flags
gboolean invert
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint8_t value
GAsyncResult * result
#define FML_UNREACHABLE()
Definition logging.h:109
#define FML_DCHECK(condition)
Definition logging.h:103
double y
double x
Optional< SkRect > bounds
Definition SkRecords.h:189
DlStrokeJoin
Definition dl_paint.h:38
@ kMiter
extends to miter limit
DlStrokeCap
Definition dl_paint.h:29
@ kSquare
adds square
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
static constexpr bool is_power_of_two(int value)
Definition dl_builder.cc:38
DlDrawStyle
Definition dl_paint.h:20
static void CopyV(void *dst)
Definition dl_builder.cc:21
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition switches.h:228
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
@ kSrcOut
r = s * (1-da)
@ kExclusion
rc = s + d - two(s*d), ra = kSrcOver
@ kSaturation
saturation of source with hue and luminosity of destination
@ kColorBurn
darken destination to reflect source
@ kPlus
r = min(s + d, 1)
@ kLighten
rc = s + d - min(s*da, d*sa), ra = kSrcOver
@ kHue
hue of source with saturation and luminosity of destination
@ kMultiply
r = s*(1-da) + d*(1-sa) + s*d
@ kColorDodge
brighten destination to reflect source
@ kScreen
r = s + d - s*d
@ kSrcOver
r = s + (1-sa)*d
@ kXor
r = s*(1-da) + d*(1-sa)
@ kLuminosity
luminosity of source with hue and saturation of destination
@ kSoftLight
lighten or darken, depending on source
@ kDifference
rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver
@ kOverlay
multiply or screen, depending on destination
@ kSrcATop
r = s*da + d*(1-sa)
@ kDstATop
r = d*sa + s*(1-da)
@ kDstOver
r = d + (1-da)*s
@ kColor
hue and saturation of source with luminosity of destination
@ kHardLight
multiply or screen, depending on source
@ kDstOut
r = d * (1-sa)
@ kDarken
rc = s + d - max(s*da, d*sa), ra = kSrcOver
#define T
int32_t width
Definition SkMD5.cpp:134
static SkImageInfo MakeUnknown()
float fX
x-axis value
float fY
y-axis value
void toQuad(SkScalar width, SkScalar height, SkPoint quad[4]) const
Definition SkRSXform.cpp:9
SkRect makeSorted() const
Definition SkRect.h:1330
constexpr SkRect makeOffset(float dx, float dy) const
Definition SkRect.h:965
bool intersect(const SkRect &r)
Definition SkRect.cpp:114
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
bool contains(SkScalar x, SkScalar y) const
Definition extension.cpp:19
void roundOut(SkIRect *dst) const
Definition SkRect.h:1241
bool isEmpty() const
Definition SkRect.h:693
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
void setEmpty()
Definition SkRect.h:842
DisplayListOpType type
static constexpr DlColor kWhite()
Definition dl_color.h:23
static constexpr DlColor kBlack()
Definition dl_color.h:22
static constexpr DlColor kTransparent()
Definition dl_color.h:21
constexpr bool isTransparent() const
Definition dl_color.h:36
constexpr bool isOpaque() const
Definition dl_color.h:35
SaveLayerOptions options
static sk_sp< SkShader > linear(sk_sp< SkShader > shader)