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"
14#include "third_party/skia/include/core/SkScalar.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, int render_op_inc, Args&&... args) {
44 size_t size = SkAlignPtr(x: 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(count: allocated_);
52 FML_DCHECK(storage_.get());
53 memset(s: storage_.get() + used_, c: 0, n: 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_ += render_op_inc;
62 op_index_++;
63 return op + 1;
64}
65
66sk_sp<DisplayList> DisplayListBuilder::Build() {
67 while (layer_stack_.size() > 1) {
68 restore();
69 }
70
71 size_t bytes = used_;
72 int count = render_op_count_;
73 size_t nested_bytes = nested_bytes_;
74 int nested_count = nested_op_count_;
75 bool 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
79 used_ = allocated_ = render_op_count_ = op_index_ = 0;
80 nested_bytes_ = nested_op_count_ = 0;
81 is_ui_thread_safe_ = true;
82 storage_.realloc(count: bytes);
83 layer_stack_.pop_back();
84 layer_stack_.emplace_back();
85 tracker_.reset();
86 current_ = DlPaint();
87
88 return sk_sp<DisplayList>(new DisplayList(
89 std::move(storage_), bytes, count, nested_bytes, nested_count, bounds(),
90 compatible, is_safe, affects_transparency, rtree()));
91}
92
93DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect,
94 bool prepare_rtree)
95 : tracker_(cull_rect, SkMatrix::I()) {
96 if (prepare_rtree) {
97 accumulator_ = std::make_unique<RTreeBoundsAccumulator>();
98 } else {
99 accumulator_ = std::make_unique<RectBoundsAccumulator>();
100 }
101
102 layer_stack_.emplace_back();
103 current_layer_ = &layer_stack_.back();
104}
105
106DisplayListBuilder::~DisplayListBuilder() {
107 uint8_t* ptr = storage_.get();
108 if (ptr) {
109 DisplayList::DisposeOps(ptr, end: ptr + used_);
110 }
111}
112
113SkISize DisplayListBuilder::GetBaseLayerSize() const {
114 return tracker_.base_device_cull_rect().roundOut().size();
115}
116
117SkImageInfo DisplayListBuilder::GetImageInfo() const {
118 SkISize size = GetBaseLayerSize();
119 return SkImageInfo::MakeUnknown(width: size.width(), height: size.height());
120}
121
122void DisplayListBuilder::onSetAntiAlias(bool aa) {
123 current_.setAntiAlias(aa);
124 Push<SetAntiAliasOp>(pod: 0, render_op_inc: 0, args&: aa);
125}
126void DisplayListBuilder::onSetDither(bool dither) {
127 current_.setDither(dither);
128 Push<SetDitherOp>(pod: 0, render_op_inc: 0, args&: dither);
129}
130void DisplayListBuilder::onSetInvertColors(bool invert) {
131 current_.setInvertColors(invert);
132 Push<SetInvertColorsOp>(pod: 0, render_op_inc: 0, args&: invert);
133 UpdateCurrentOpacityCompatibility();
134}
135void DisplayListBuilder::onSetStrokeCap(DlStrokeCap cap) {
136 current_.setStrokeCap(cap);
137 Push<SetStrokeCapOp>(pod: 0, render_op_inc: 0, args&: cap);
138}
139void DisplayListBuilder::onSetStrokeJoin(DlStrokeJoin join) {
140 current_.setStrokeJoin(join);
141 Push<SetStrokeJoinOp>(pod: 0, render_op_inc: 0, args&: join);
142}
143void DisplayListBuilder::onSetDrawStyle(DlDrawStyle style) {
144 current_.setDrawStyle(style);
145 Push<SetStyleOp>(pod: 0, render_op_inc: 0, args&: style);
146}
147void DisplayListBuilder::onSetStrokeWidth(float width) {
148 current_.setStrokeWidth(width);
149 Push<SetStrokeWidthOp>(pod: 0, render_op_inc: 0, args&: width);
150}
151void DisplayListBuilder::onSetStrokeMiter(float limit) {
152 current_.setStrokeMiter(limit);
153 Push<SetStrokeMiterOp>(pod: 0, render_op_inc: 0, args&: limit);
154}
155void DisplayListBuilder::onSetColor(DlColor color) {
156 current_.setColor(color);
157 Push<SetColorOp>(pod: 0, render_op_inc: 0, args&: color);
158}
159void DisplayListBuilder::onSetBlendMode(DlBlendMode mode) {
160 current_.setBlendMode(mode);
161 Push<SetBlendModeOp>(pod: 0, render_op_inc: 0, args&: mode);
162 UpdateCurrentOpacityCompatibility();
163}
164
165void DisplayListBuilder::onSetColorSource(const DlColorSource* source) {
166 if (source == nullptr) {
167 current_.setColorSource(nullptr);
168 Push<ClearColorSourceOp>(pod: 0, render_op_inc: 0);
169 } else {
170 current_.setColorSource(source->shared());
171 is_ui_thread_safe_ = is_ui_thread_safe_ && source->isUIThreadSafe();
172 switch (source->type()) {
173 case DlColorSourceType::kColor: {
174 const DlColorColorSource* color_source = source->asColor();
175 current_.setColorSource(nullptr);
176 setColor(color_source->color());
177 break;
178 }
179 case DlColorSourceType::kImage: {
180 const DlImageColorSource* image_source = source->asImage();
181 FML_DCHECK(image_source);
182 Push<SetImageColorSourceOp>(pod: 0, render_op_inc: 0, args&: image_source);
183 break;
184 }
185 case DlColorSourceType::kLinearGradient: {
186 const DlLinearGradientColorSource* linear = source->asLinearGradient();
187 FML_DCHECK(linear);
188 void* pod = Push<SetPodColorSourceOp>(pod: linear->size(), render_op_inc: 0);
189 new (pod) DlLinearGradientColorSource(linear);
190 break;
191 }
192 case DlColorSourceType::kRadialGradient: {
193 const DlRadialGradientColorSource* radial = source->asRadialGradient();
194 FML_DCHECK(radial);
195 void* pod = Push<SetPodColorSourceOp>(pod: radial->size(), render_op_inc: 0);
196 new (pod) DlRadialGradientColorSource(radial);
197 break;
198 }
199 case DlColorSourceType::kConicalGradient: {
200 const DlConicalGradientColorSource* conical =
201 source->asConicalGradient();
202 FML_DCHECK(conical);
203 void* pod = Push<SetPodColorSourceOp>(pod: conical->size(), render_op_inc: 0);
204 new (pod) DlConicalGradientColorSource(conical);
205 break;
206 }
207 case DlColorSourceType::kSweepGradient: {
208 const DlSweepGradientColorSource* sweep = source->asSweepGradient();
209 FML_DCHECK(sweep);
210 void* pod = Push<SetPodColorSourceOp>(pod: sweep->size(), render_op_inc: 0);
211 new (pod) DlSweepGradientColorSource(sweep);
212 break;
213 }
214 case DlColorSourceType::kRuntimeEffect: {
215 const DlRuntimeEffectColorSource* effect = source->asRuntimeEffect();
216 FML_DCHECK(effect);
217 Push<SetRuntimeEffectColorSourceOp>(pod: 0, render_op_inc: 0, args&: effect);
218 break;
219 }
220#ifdef IMPELLER_ENABLE_3D
221 case DlColorSourceType::kScene: {
222 const DlSceneColorSource* scene = source->asScene();
223 FML_DCHECK(scene);
224 Push<SetSceneColorSourceOp>(0, 0, scene);
225 break;
226 }
227#endif // IMPELLER_ENABLE_3D
228 }
229 }
230}
231void DisplayListBuilder::onSetImageFilter(const DlImageFilter* filter) {
232 if (filter == nullptr) {
233 current_.setImageFilter(nullptr);
234 Push<ClearImageFilterOp>(pod: 0, render_op_inc: 0);
235 } else {
236 current_.setImageFilter(filter->shared());
237 switch (filter->type()) {
238 case DlImageFilterType::kBlur: {
239 const DlBlurImageFilter* blur_filter = filter->asBlur();
240 FML_DCHECK(blur_filter);
241 void* pod = Push<SetPodImageFilterOp>(pod: blur_filter->size(), render_op_inc: 0);
242 new (pod) DlBlurImageFilter(blur_filter);
243 break;
244 }
245 case DlImageFilterType::kDilate: {
246 const DlDilateImageFilter* dilate_filter = filter->asDilate();
247 FML_DCHECK(dilate_filter);
248 void* pod = Push<SetPodImageFilterOp>(pod: dilate_filter->size(), render_op_inc: 0);
249 new (pod) DlDilateImageFilter(dilate_filter);
250 break;
251 }
252 case DlImageFilterType::kErode: {
253 const DlErodeImageFilter* erode_filter = filter->asErode();
254 FML_DCHECK(erode_filter);
255 void* pod = Push<SetPodImageFilterOp>(pod: erode_filter->size(), render_op_inc: 0);
256 new (pod) DlErodeImageFilter(erode_filter);
257 break;
258 }
259 case DlImageFilterType::kMatrix: {
260 const DlMatrixImageFilter* matrix_filter = filter->asMatrix();
261 FML_DCHECK(matrix_filter);
262 void* pod = Push<SetPodImageFilterOp>(pod: matrix_filter->size(), render_op_inc: 0);
263 new (pod) DlMatrixImageFilter(matrix_filter);
264 break;
265 }
266 case DlImageFilterType::kCompose:
267 case DlImageFilterType::kLocalMatrix:
268 case DlImageFilterType::kColorFilter: {
269 Push<SetSharedImageFilterOp>(pod: 0, render_op_inc: 0, args&: filter);
270 break;
271 }
272 }
273 }
274}
275void DisplayListBuilder::onSetColorFilter(const DlColorFilter* filter) {
276 if (filter == nullptr) {
277 current_.setColorFilter(nullptr);
278 Push<ClearColorFilterOp>(pod: 0, render_op_inc: 0);
279 } else {
280 current_.setColorFilter(filter->shared());
281 switch (filter->type()) {
282 case DlColorFilterType::kBlend: {
283 const DlBlendColorFilter* blend_filter = filter->asBlend();
284 FML_DCHECK(blend_filter);
285 void* pod = Push<SetPodColorFilterOp>(pod: blend_filter->size(), render_op_inc: 0);
286 new (pod) DlBlendColorFilter(blend_filter);
287 break;
288 }
289 case DlColorFilterType::kMatrix: {
290 const DlMatrixColorFilter* matrix_filter = filter->asMatrix();
291 FML_DCHECK(matrix_filter);
292 void* pod = Push<SetPodColorFilterOp>(pod: matrix_filter->size(), render_op_inc: 0);
293 new (pod) DlMatrixColorFilter(matrix_filter);
294 break;
295 }
296 case DlColorFilterType::kSrgbToLinearGamma: {
297 void* pod = Push<SetPodColorFilterOp>(pod: filter->size(), render_op_inc: 0);
298 new (pod) DlSrgbToLinearGammaColorFilter();
299 break;
300 }
301 case DlColorFilterType::kLinearToSrgbGamma: {
302 void* pod = Push<SetPodColorFilterOp>(pod: filter->size(), render_op_inc: 0);
303 new (pod) DlLinearToSrgbGammaColorFilter();
304 break;
305 }
306 }
307 }
308 UpdateCurrentOpacityCompatibility();
309}
310void DisplayListBuilder::onSetPathEffect(const DlPathEffect* effect) {
311 if (effect == nullptr) {
312 current_.setPathEffect(nullptr);
313 Push<ClearPathEffectOp>(pod: 0, render_op_inc: 0);
314 } else {
315 current_.setPathEffect(effect->shared());
316 switch (effect->type()) {
317 case DlPathEffectType::kDash: {
318 const DlDashPathEffect* dash_effect = effect->asDash();
319 void* pod = Push<SetPodPathEffectOp>(pod: dash_effect->size(), render_op_inc: 0);
320 new (pod) DlDashPathEffect(dash_effect);
321 break;
322 }
323 }
324 }
325}
326void DisplayListBuilder::onSetMaskFilter(const DlMaskFilter* filter) {
327 if (filter == nullptr) {
328 current_.setMaskFilter(nullptr);
329 Push<ClearMaskFilterOp>(pod: 0, render_op_inc: 0);
330 } else {
331 current_.setMaskFilter(filter->shared());
332 switch (filter->type()) {
333 case DlMaskFilterType::kBlur: {
334 const DlBlurMaskFilter* blur_filter = filter->asBlur();
335 FML_DCHECK(blur_filter);
336 void* pod = Push<SetPodMaskFilterOp>(pod: blur_filter->size(), render_op_inc: 0);
337 new (pod) DlBlurMaskFilter(blur_filter);
338 break;
339 }
340 }
341 }
342}
343
344void DisplayListBuilder::SetAttributesFromPaint(
345 const DlPaint& paint,
346 const DisplayListAttributeFlags flags) {
347 if (flags.applies_anti_alias()) {
348 setAntiAlias(paint.isAntiAlias());
349 }
350 if (flags.applies_dither()) {
351 setDither(paint.isDither());
352 }
353 if (flags.applies_alpha_or_color()) {
354 setColor(paint.getColor().argb);
355 }
356 if (flags.applies_blend()) {
357 setBlendMode(paint.getBlendMode());
358 }
359 if (flags.applies_style()) {
360 setDrawStyle(paint.getDrawStyle());
361 }
362 if (flags.is_stroked(style: paint.getDrawStyle())) {
363 setStrokeWidth(paint.getStrokeWidth());
364 setStrokeMiter(paint.getStrokeMiter());
365 setStrokeCap(paint.getStrokeCap());
366 setStrokeJoin(paint.getStrokeJoin());
367 }
368 if (flags.applies_shader()) {
369 setColorSource(paint.getColorSource().get());
370 }
371 if (flags.applies_color_filter()) {
372 setInvertColors(paint.isInvertColors());
373 setColorFilter(paint.getColorFilter().get());
374 }
375 if (flags.applies_image_filter()) {
376 setImageFilter(paint.getImageFilter().get());
377 }
378 if (flags.applies_path_effect()) {
379 setPathEffect(paint.getPathEffect().get());
380 }
381 if (flags.applies_mask_filter()) {
382 setMaskFilter(paint.getMaskFilter().get());
383 }
384}
385
386void DisplayListBuilder::checkForDeferredSave() {
387 if (current_layer_->has_deferred_save_op_) {
388 size_t save_offset_ = used_;
389 Push<SaveOp>(pod: 0, render_op_inc: 1);
390 current_layer_->save_offset_ = save_offset_;
391 current_layer_->has_deferred_save_op_ = false;
392 }
393}
394
395void DisplayListBuilder::Save() {
396 bool is_nop = current_layer_->is_nop_;
397 layer_stack_.emplace_back();
398 current_layer_ = &layer_stack_.back();
399 current_layer_->has_deferred_save_op_ = true;
400 current_layer_->is_nop_ = is_nop;
401 tracker_.save();
402 accumulator()->save();
403}
404
405void DisplayListBuilder::Restore() {
406 if (layer_stack_.size() > 1) {
407 SaveOpBase* op = reinterpret_cast<SaveOpBase*>(
408 storage_.get() + current_layer_->save_offset());
409 if (!current_layer_->has_deferred_save_op_) {
410 op->restore_index = op_index_;
411 Push<RestoreOp>(pod: 0, render_op_inc: 1);
412 }
413 // Grab the current layer info before we push the restore
414 // on the stack.
415 LayerInfo layer_info = layer_stack_.back();
416
417 tracker_.restore();
418 layer_stack_.pop_back();
419 current_layer_ = &layer_stack_.back();
420 bool is_unbounded = layer_info.is_unbounded();
421
422 // Before we pop_back we will get the current layer bounds from the
423 // current accumulator and adjust it as required based on the filter.
424 std::shared_ptr<const DlImageFilter> filter = layer_info.filter();
425 if (filter) {
426 const SkRect clip = tracker_.device_cull_rect();
427 if (!accumulator()->restore(
428 map: [filter = filter, matrix = GetTransform()](const SkRect& input,
429 SkRect& output) {
430 SkIRect output_bounds;
431 bool ret = filter->map_device_bounds(input_bounds: input.roundOut(), ctm: matrix,
432 output_bounds);
433 output.set(output_bounds);
434 return ret;
435 },
436 clip: &clip)) {
437 is_unbounded = true;
438 }
439 } else {
440 accumulator()->restore();
441 }
442
443 if (is_unbounded) {
444 AccumulateUnbounded();
445 }
446
447 if (layer_info.has_layer()) {
448 // Layers are never deferred for now, we need to update the
449 // following code if we ever do saveLayer culling...
450 FML_DCHECK(!layer_info.has_deferred_save_op_);
451 if (layer_info.is_group_opacity_compatible()) {
452 // We are now going to go back and modify the matching saveLayer
453 // call to add the option indicating it can distribute an opacity
454 // value to its children.
455 //
456 // Note that this operation cannot and does not change the size
457 // or structure of the SaveLayerOp record. It only sets an option
458 // flag on an existing field.
459 //
460 // Note that these kinds of modification operations on data already
461 // in the DisplayList are only allowed *during* the build phase.
462 // Once built, the DisplayList records must remain read only to
463 // ensure consistency of rendering and |Equals()| behavior.
464 op->options = op->options.with_can_distribute_opacity();
465 }
466 } else {
467 // For regular save() ops there was no protecting layer so we have to
468 // accumulate the values into the enclosing layer.
469 if (layer_info.cannot_inherit_opacity()) {
470 current_layer_->mark_incompatible();
471 } else if (layer_info.has_compatible_op()) {
472 current_layer_->add_compatible_op();
473 }
474 }
475 }
476}
477void DisplayListBuilder::RestoreToCount(int restore_count) {
478 FML_DCHECK(restore_count <= GetSaveCount());
479 while (restore_count < GetSaveCount() && GetSaveCount() > 1) {
480 restore();
481 }
482}
483void DisplayListBuilder::saveLayer(const SkRect* bounds,
484 const SaveLayerOptions in_options,
485 const DlImageFilter* backdrop) {
486 SaveLayerOptions options = in_options.without_optimizations();
487 DisplayListAttributeFlags flags = options.renders_with_attributes()
488 ? kSaveLayerWithPaintFlags
489 : kSaveLayerFlags;
490 OpResult result = PaintResult(paint: current_, flags);
491 if (result == OpResult::kNoEffect) {
492 save();
493 current_layer_->is_nop_ = true;
494 return;
495 }
496 size_t save_layer_offset = used_;
497 if (options.renders_with_attributes()) {
498 // The actual flood of the outer layer clip will occur after the
499 // (eventual) corresponding restore is called, but rather than
500 // remember this information in the LayerInfo until the restore
501 // method is processed, we just mark the unbounded state up front.
502 // Another reason to accumulate the clip here rather than in
503 // restore is so that this savelayer will be tagged in the rtree
504 // with its full bounds and the right op_index so that it doesn't
505 // get culled during rendering.
506 if (!paint_nops_on_transparency()) {
507 // We will fill the clip of the outer layer when we restore.
508 // Accumulate should always return true here because if the
509 // clip was empty then that would have been caught up above
510 // when we tested the PaintResult.
511 [[maybe_unused]] bool unclipped = AccumulateUnbounded();
512 FML_DCHECK(unclipped);
513 }
514 CheckLayerOpacityCompatibility(uses_blend_attribute: true);
515 layer_stack_.emplace_back(args&: save_layer_offset, args: true,
516 args: current_.getImageFilter());
517 } else {
518 CheckLayerOpacityCompatibility(uses_blend_attribute: false);
519 layer_stack_.emplace_back(args&: save_layer_offset, args: true, args: nullptr);
520 }
521 current_layer_ = &layer_stack_.back();
522
523 tracker_.save();
524 accumulator()->save();
525
526 if (backdrop) {
527 // A backdrop will affect up to the entire surface, bounded by the clip
528 // Accumulate should always return true here because if the
529 // clip was empty then that would have been caught up above
530 // when we tested the PaintResult.
531 [[maybe_unused]] bool unclipped = AccumulateUnbounded();
532 FML_DCHECK(unclipped);
533 bounds //
534 ? Push<SaveLayerBackdropBoundsOp>(pod: 0, render_op_inc: 1, args&: options, args: *bounds, args&: backdrop)
535 : Push<SaveLayerBackdropOp>(pod: 0, render_op_inc: 1, args&: options, args&: backdrop);
536 } else {
537 bounds //
538 ? Push<SaveLayerBoundsOp>(pod: 0, render_op_inc: 1, args&: options, args: *bounds)
539 : Push<SaveLayerOp>(pod: 0, render_op_inc: 1, args&: options);
540 }
541
542 if (options.renders_with_attributes()) {
543 // |current_opacity_compatibility_| does not take an ImageFilter into
544 // account because an individual primitive with an ImageFilter can apply
545 // opacity on top of it. But, if the layer is applying the ImageFilter
546 // then it cannot pass the opacity on.
547 if (!current_opacity_compatibility_ ||
548 current_.getImageFilter() != nullptr) {
549 UpdateLayerOpacityCompatibility(compatible: false);
550 }
551 }
552 UpdateLayerResult(result);
553
554 if (options.renders_with_attributes() && current_.getImageFilter()) {
555 // We use |resetCullRect| here because we will be accumulating bounds of
556 // primitives before applying the filter to those bounds. We might
557 // encounter a primitive whose bounds are clipped, but whose filtered
558 // bounds will not be clipped. If the individual rendering ops bounds
559 // are clipped, it will not contribute to the overall bounds which
560 // could lead to inaccurate (subset) bounds of the DisplayList.
561 // We need to reset the cull rect here to avoid this premature clipping.
562 // The filtered bounds will be clipped to the existing clip rect when
563 // this layer is restored.
564 // If bounds is null then the original cull_rect will be used.
565 tracker_.resetCullRect(cull_rect: bounds);
566 } else if (bounds) {
567 // Even though Skia claims that the bounds are only a hint, they actually
568 // use them as the temporary layer bounds during rendering the layer, so
569 // we set them as if a clip operation were performed.
570 tracker_.clipRect(rect: *bounds, op: ClipOp::kIntersect, is_aa: false);
571 }
572}
573void DisplayListBuilder::SaveLayer(const SkRect* bounds,
574 const DlPaint* paint,
575 const DlImageFilter* backdrop) {
576 if (paint != nullptr) {
577 SetAttributesFromPaint(paint: *paint,
578 flags: DisplayListOpFlags::kSaveLayerWithPaintFlags);
579 saveLayer(bounds, in_options: SaveLayerOptions::kWithAttributes, backdrop);
580 } else {
581 saveLayer(bounds, in_options: SaveLayerOptions::kNoAttributes, backdrop);
582 }
583}
584
585void DisplayListBuilder::Translate(SkScalar tx, SkScalar ty) {
586 if (SkScalarIsFinite(x: tx) && SkScalarIsFinite(x: ty) &&
587 (tx != 0.0 || ty != 0.0)) {
588 checkForDeferredSave();
589 Push<TranslateOp>(pod: 0, render_op_inc: 1, args&: tx, args&: ty);
590 tracker_.translate(tx, ty);
591 }
592}
593void DisplayListBuilder::Scale(SkScalar sx, SkScalar sy) {
594 if (SkScalarIsFinite(x: sx) && SkScalarIsFinite(x: sy) &&
595 (sx != 1.0 || sy != 1.0)) {
596 checkForDeferredSave();
597 Push<ScaleOp>(pod: 0, render_op_inc: 1, args&: sx, args&: sy);
598 tracker_.scale(sx, sy);
599 }
600}
601void DisplayListBuilder::Rotate(SkScalar degrees) {
602 if (SkScalarMod(degrees, 360.0) != 0.0) {
603 checkForDeferredSave();
604 Push<RotateOp>(pod: 0, render_op_inc: 1, args&: degrees);
605 tracker_.rotate(degrees);
606 }
607}
608void DisplayListBuilder::Skew(SkScalar sx, SkScalar sy) {
609 if (SkScalarIsFinite(x: sx) && SkScalarIsFinite(x: sy) &&
610 (sx != 0.0 || sy != 0.0)) {
611 checkForDeferredSave();
612 Push<SkewOp>(pod: 0, render_op_inc: 1, args&: sx, args&: sy);
613 tracker_.skew(skx: sx, sky: sy);
614 }
615}
616
617// clang-format off
618
619// 2x3 2D affine subset of a 4x4 transform in row major order
620void DisplayListBuilder::Transform2DAffine(
621 SkScalar mxx, SkScalar mxy, SkScalar mxt,
622 SkScalar myx, SkScalar myy, SkScalar myt) {
623 if (SkScalarsAreFinite(a: mxx, b: myx) &&
624 SkScalarsAreFinite(a: mxy, b: myy) &&
625 SkScalarsAreFinite(a: mxt, b: myt)) {
626 if (mxx == 1 && mxy == 0 &&
627 myx == 0 && myy == 1) {
628 Translate(tx: mxt, ty: myt);
629 } else {
630 checkForDeferredSave();
631 Push<Transform2DAffineOp>(pod: 0, render_op_inc: 1,
632 args&: mxx, args&: mxy, args&: mxt,
633 args&: myx, args&: myy, args&: myt);
634 tracker_.transform2DAffine(mxx, mxy, mxt,
635 myx, myy, myt);
636 }
637 }
638}
639// full 4x4 transform in row major order
640void DisplayListBuilder::TransformFullPerspective(
641 SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
642 SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
643 SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
644 SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) {
645 if ( mxz == 0 &&
646 myz == 0 &&
647 mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 &&
648 mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) {
649 Transform2DAffine(mxx, mxy, mxt,
650 myx, myy, myt);
651 } else if (SkScalarsAreFinite(a: mxx, b: mxy) && SkScalarsAreFinite(a: mxz, b: mxt) &&
652 SkScalarsAreFinite(a: myx, b: myy) && SkScalarsAreFinite(a: myz, b: myt) &&
653 SkScalarsAreFinite(a: mzx, b: mzy) && SkScalarsAreFinite(a: mzz, b: mzt) &&
654 SkScalarsAreFinite(a: mwx, b: mwy) && SkScalarsAreFinite(a: mwz, b: mwt)) {
655 checkForDeferredSave();
656 Push<TransformFullPerspectiveOp>(pod: 0, render_op_inc: 1,
657 args&: mxx, args&: mxy, args&: mxz, args&: mxt,
658 args&: myx, args&: myy, args&: myz, args&: myt,
659 args&: mzx, args&: mzy, args&: mzz, args&: mzt,
660 args&: mwx, args&: mwy, args&: mwz, args&: mwt);
661 tracker_.transformFullPerspective(mxx, mxy, mxz, mxt,
662 myx, myy, myz, myt,
663 mzx, mzy, mzz, mzt,
664 mwx, mwy, mwz, mwt);
665 }
666}
667// clang-format on
668void DisplayListBuilder::TransformReset() {
669 checkForDeferredSave();
670 Push<TransformResetOp>(pod: 0, render_op_inc: 0);
671 tracker_.setIdentity();
672}
673void DisplayListBuilder::Transform(const SkMatrix* matrix) {
674 if (matrix != nullptr) {
675 Transform(matrix44: SkM44(*matrix));
676 }
677}
678void DisplayListBuilder::Transform(const SkM44* m44) {
679 if (m44 != nullptr) {
680 transformFullPerspective(
681 mxx: m44->rc(r: 0, c: 0), mxy: m44->rc(r: 0, c: 1), mxz: m44->rc(r: 0, c: 2), mxt: m44->rc(r: 0, c: 3),
682 myx: m44->rc(r: 1, c: 0), myy: m44->rc(r: 1, c: 1), myz: m44->rc(r: 1, c: 2), myt: m44->rc(r: 1, c: 3),
683 mzx: m44->rc(r: 2, c: 0), mzy: m44->rc(r: 2, c: 1), mzz: m44->rc(r: 2, c: 2), mzt: m44->rc(r: 2, c: 3),
684 mwx: m44->rc(r: 3, c: 0), mwy: m44->rc(r: 3, c: 1), mwz: m44->rc(r: 3, c: 2), mwt: m44->rc(r: 3, c: 3));
685 }
686}
687
688void DisplayListBuilder::ClipRect(const SkRect& rect,
689 ClipOp clip_op,
690 bool is_aa) {
691 if (!rect.isFinite()) {
692 return;
693 }
694 tracker_.clipRect(rect, op: clip_op, is_aa);
695 if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) {
696 current_layer_->is_nop_ = true;
697 return;
698 }
699 checkForDeferredSave();
700 switch (clip_op) {
701 case ClipOp::kIntersect:
702 Push<ClipIntersectRectOp>(pod: 0, render_op_inc: 1, args: rect, args&: is_aa);
703 break;
704 case ClipOp::kDifference:
705 Push<ClipDifferenceRectOp>(pod: 0, render_op_inc: 1, args: rect, args&: is_aa);
706 break;
707 }
708}
709void DisplayListBuilder::ClipRRect(const SkRRect& rrect,
710 ClipOp clip_op,
711 bool is_aa) {
712 if (rrect.isRect()) {
713 clipRect(rect: rrect.rect(), clip_op, is_aa);
714 } else {
715 tracker_.clipRRect(rrect, op: clip_op, is_aa);
716 if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) {
717 current_layer_->is_nop_ = true;
718 return;
719 }
720 checkForDeferredSave();
721 switch (clip_op) {
722 case ClipOp::kIntersect:
723 Push<ClipIntersectRRectOp>(pod: 0, render_op_inc: 1, args: rrect, args&: is_aa);
724 break;
725 case ClipOp::kDifference:
726 Push<ClipDifferenceRRectOp>(pod: 0, render_op_inc: 1, args: rrect, args&: is_aa);
727 break;
728 }
729 }
730}
731void DisplayListBuilder::ClipPath(const SkPath& path,
732 ClipOp clip_op,
733 bool is_aa) {
734 if (!path.isInverseFillType()) {
735 SkRect rect;
736 if (path.isRect(rect: &rect)) {
737 this->clipRect(rect, clip_op, is_aa);
738 return;
739 }
740 SkRRect rrect;
741 if (path.isOval(bounds: &rect)) {
742 rrect.setOval(rect);
743 this->clipRRect(rrect, clip_op, is_aa);
744 return;
745 }
746 if (path.isRRect(rrect: &rrect)) {
747 this->clipRRect(rrect, clip_op, is_aa);
748 return;
749 }
750 }
751 tracker_.clipPath(path, op: clip_op, is_aa);
752 if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) {
753 current_layer_->is_nop_ = true;
754 return;
755 }
756 checkForDeferredSave();
757 switch (clip_op) {
758 case ClipOp::kIntersect:
759 Push<ClipIntersectPathOp>(pod: 0, render_op_inc: 1, args: path, args&: is_aa);
760 break;
761 case ClipOp::kDifference:
762 Push<ClipDifferencePathOp>(pod: 0, render_op_inc: 1, args: path, args&: is_aa);
763 break;
764 }
765}
766
767bool DisplayListBuilder::QuickReject(const SkRect& bounds) const {
768 return tracker_.content_culled(content_bounds: bounds);
769}
770
771void DisplayListBuilder::drawPaint() {
772 OpResult result = PaintResult(paint: current_, flags: kDrawPaintFlags);
773 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
774 Push<DrawPaintOp>(pod: 0, render_op_inc: 1);
775 CheckLayerOpacityCompatibility();
776 UpdateLayerResult(result);
777 }
778}
779void DisplayListBuilder::DrawPaint(const DlPaint& paint) {
780 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawPaintFlags);
781 drawPaint();
782}
783void DisplayListBuilder::DrawColor(DlColor color, DlBlendMode mode) {
784 OpResult result = PaintResult(paint: DlPaint(color).setBlendMode(mode));
785 if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
786 Push<DrawColorOp>(pod: 0, render_op_inc: 1, args&: color, args&: mode);
787 CheckLayerOpacityCompatibility(mode);
788 UpdateLayerResult(result);
789 }
790}
791void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) {
792 SkRect bounds = SkRect::MakeLTRB(l: p0.fX, t: p0.fY, r: p1.fX, b: p1.fY).makeSorted();
793 DisplayListAttributeFlags flags =
794 (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags
795 : kDrawHVLineFlags;
796 OpResult result = PaintResult(paint: current_, flags);
797 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
798 Push<DrawLineOp>(pod: 0, render_op_inc: 1, args: p0, args: p1);
799 CheckLayerOpacityCompatibility();
800 UpdateLayerResult(result);
801 }
802}
803void DisplayListBuilder::DrawLine(const SkPoint& p0,
804 const SkPoint& p1,
805 const DlPaint& paint) {
806 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawLineFlags);
807 drawLine(p0, p1);
808}
809void DisplayListBuilder::drawRect(const SkRect& rect) {
810 DisplayListAttributeFlags flags = kDrawRectFlags;
811 OpResult result = PaintResult(paint: current_, flags);
812 if (result != OpResult::kNoEffect &&
813 AccumulateOpBounds(bounds: rect.makeSorted(), flags)) {
814 Push<DrawRectOp>(pod: 0, render_op_inc: 1, args: rect);
815 CheckLayerOpacityCompatibility();
816 UpdateLayerResult(result);
817 }
818}
819void DisplayListBuilder::DrawRect(const SkRect& rect, const DlPaint& paint) {
820 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawRectFlags);
821 drawRect(rect);
822}
823void DisplayListBuilder::drawOval(const SkRect& bounds) {
824 DisplayListAttributeFlags flags = kDrawOvalFlags;
825 OpResult result = PaintResult(paint: current_, flags);
826 if (result != OpResult::kNoEffect &&
827 AccumulateOpBounds(bounds: bounds.makeSorted(), flags)) {
828 Push<DrawOvalOp>(pod: 0, render_op_inc: 1, args: bounds);
829 CheckLayerOpacityCompatibility();
830 UpdateLayerResult(result);
831 }
832}
833void DisplayListBuilder::DrawOval(const SkRect& bounds, const DlPaint& paint) {
834 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawOvalFlags);
835 drawOval(bounds);
836}
837void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) {
838 DisplayListAttributeFlags flags = kDrawCircleFlags;
839 OpResult result = PaintResult(paint: current_, flags);
840 if (result != OpResult::kNoEffect) {
841 SkRect bounds = SkRect::MakeLTRB(l: center.fX - radius, t: center.fY - radius,
842 r: center.fX + radius, b: center.fY + radius);
843 if (AccumulateOpBounds(bounds, flags)) {
844 Push<DrawCircleOp>(pod: 0, render_op_inc: 1, args: center, args&: radius);
845 CheckLayerOpacityCompatibility();
846 UpdateLayerResult(result);
847 }
848 }
849}
850void DisplayListBuilder::DrawCircle(const SkPoint& center,
851 SkScalar radius,
852 const DlPaint& paint) {
853 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawCircleFlags);
854 drawCircle(center, radius);
855}
856void DisplayListBuilder::drawRRect(const SkRRect& rrect) {
857 if (rrect.isRect()) {
858 drawRect(rect: rrect.rect());
859 } else if (rrect.isOval()) {
860 drawOval(bounds: rrect.rect());
861 } else {
862 DisplayListAttributeFlags flags = kDrawRRectFlags;
863 OpResult result = PaintResult(paint: current_, flags);
864 if (result != OpResult::kNoEffect &&
865 AccumulateOpBounds(bounds: rrect.getBounds(), flags)) {
866 Push<DrawRRectOp>(pod: 0, render_op_inc: 1, args: rrect);
867 CheckLayerOpacityCompatibility();
868 UpdateLayerResult(result);
869 }
870 }
871}
872void DisplayListBuilder::DrawRRect(const SkRRect& rrect, const DlPaint& paint) {
873 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawRRectFlags);
874 drawRRect(rrect);
875}
876void DisplayListBuilder::drawDRRect(const SkRRect& outer,
877 const SkRRect& inner) {
878 DisplayListAttributeFlags flags = kDrawDRRectFlags;
879 OpResult result = PaintResult(paint: current_, flags);
880 if (result != OpResult::kNoEffect &&
881 AccumulateOpBounds(bounds: outer.getBounds(), flags)) {
882 Push<DrawDRRectOp>(pod: 0, render_op_inc: 1, args: outer, args: inner);
883 CheckLayerOpacityCompatibility();
884 UpdateLayerResult(result);
885 }
886}
887void DisplayListBuilder::DrawDRRect(const SkRRect& outer,
888 const SkRRect& inner,
889 const DlPaint& paint) {
890 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawDRRectFlags);
891 drawDRRect(outer, inner);
892}
893void DisplayListBuilder::drawPath(const SkPath& path) {
894 DisplayListAttributeFlags flags = kDrawPathFlags;
895 OpResult result = PaintResult(paint: current_, flags);
896 if (result != OpResult::kNoEffect) {
897 bool is_visible = path.isInverseFillType()
898 ? AccumulateUnbounded()
899 : AccumulateOpBounds(bounds: path.getBounds(), flags);
900 if (is_visible) {
901 Push<DrawPathOp>(pod: 0, render_op_inc: 1, args: path);
902 CheckLayerOpacityHairlineCompatibility();
903 UpdateLayerResult(result);
904 }
905 }
906}
907void DisplayListBuilder::DrawPath(const SkPath& path, const DlPaint& paint) {
908 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawPathFlags);
909 drawPath(path);
910}
911
912void DisplayListBuilder::drawArc(const SkRect& bounds,
913 SkScalar start,
914 SkScalar sweep,
915 bool useCenter) {
916 DisplayListAttributeFlags flags = //
917 useCenter //
918 ? kDrawArcWithCenterFlags
919 : kDrawArcNoCenterFlags;
920 OpResult result = PaintResult(paint: current_, flags);
921 // This could be tighter if we compute where the start and end
922 // angles are and then also consider the quadrants swept and
923 // the center if specified.
924 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
925 Push<DrawArcOp>(pod: 0, render_op_inc: 1, args: bounds, args&: start, args&: sweep, args&: useCenter);
926 if (useCenter) {
927 CheckLayerOpacityHairlineCompatibility();
928 } else {
929 CheckLayerOpacityCompatibility();
930 }
931 UpdateLayerResult(result);
932 }
933}
934void DisplayListBuilder::DrawArc(const SkRect& bounds,
935 SkScalar start,
936 SkScalar sweep,
937 bool useCenter,
938 const DlPaint& paint) {
939 SetAttributesFromPaint(
940 paint, flags: useCenter ? kDrawArcWithCenterFlags : kDrawArcNoCenterFlags);
941 drawArc(bounds, start, sweep, useCenter);
942}
943
944DisplayListAttributeFlags DisplayListBuilder::FlagsForPointMode(
945 PointMode mode) {
946 switch (mode) {
947 case DlCanvas::PointMode::kPoints:
948 return kDrawPointsAsPointsFlags;
949 case PointMode::kLines:
950 return kDrawPointsAsLinesFlags;
951 case PointMode::kPolygon:
952 return kDrawPointsAsPolygonFlags;
953 }
954 FML_UNREACHABLE();
955}
956void DisplayListBuilder::drawPoints(PointMode mode,
957 uint32_t count,
958 const SkPoint pts[]) {
959 if (count == 0) {
960 return;
961 }
962 DisplayListAttributeFlags flags = FlagsForPointMode(mode);
963 OpResult result = PaintResult(paint: current_, flags);
964 if (result == OpResult::kNoEffect) {
965 return;
966 }
967
968 FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount);
969 int bytes = count * sizeof(SkPoint);
970 RectBoundsAccumulator ptBounds;
971 for (size_t i = 0; i < count; i++) {
972 ptBounds.accumulate(p: pts[i]);
973 }
974 SkRect point_bounds = ptBounds.bounds();
975 if (!AccumulateOpBounds(bounds&: point_bounds, flags)) {
976 return;
977 }
978
979 void* data_ptr;
980 switch (mode) {
981 case PointMode::kPoints:
982 data_ptr = Push<DrawPointsOp>(pod: bytes, render_op_inc: 1, args&: count);
983 break;
984 case PointMode::kLines:
985 data_ptr = Push<DrawLinesOp>(pod: bytes, render_op_inc: 1, args&: count);
986 break;
987 case PointMode::kPolygon:
988 data_ptr = Push<DrawPolygonOp>(pod: bytes, render_op_inc: 1, args&: count);
989 break;
990 default:
991 FML_UNREACHABLE();
992 return;
993 }
994 CopyV(dst: data_ptr, src: pts, n: count);
995 // drawPoints treats every point or line (or segment of a polygon)
996 // as a completely separate operation meaning we cannot ensure
997 // distribution of group opacity without analyzing the mode and the
998 // bounds of every sub-primitive.
999 // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c
1000 UpdateLayerOpacityCompatibility(compatible: false);
1001 UpdateLayerResult(result);
1002}
1003void DisplayListBuilder::DrawPoints(PointMode mode,
1004 uint32_t count,
1005 const SkPoint pts[],
1006 const DlPaint& paint) {
1007 SetAttributesFromPaint(paint, flags: FlagsForPointMode(mode));
1008 drawPoints(mode, count, pts);
1009}
1010void DisplayListBuilder::drawVertices(const DlVertices* vertices,
1011 DlBlendMode mode) {
1012 DisplayListAttributeFlags flags = kDrawVerticesFlags;
1013 OpResult result = PaintResult(paint: current_, flags);
1014 if (result != OpResult::kNoEffect &&
1015 AccumulateOpBounds(bounds: vertices->bounds(), flags)) {
1016 void* pod = Push<DrawVerticesOp>(pod: vertices->size(), render_op_inc: 1, args&: mode);
1017 new (pod) DlVertices(vertices);
1018 // DrawVertices applies its colors to the paint so we have no way
1019 // of controlling opacity using the current paint attributes.
1020 // Although, examination of the |mode| might find some predictable
1021 // cases.
1022 UpdateLayerOpacityCompatibility(compatible: false);
1023 UpdateLayerResult(result);
1024 }
1025}
1026void DisplayListBuilder::DrawVertices(const DlVertices* vertices,
1027 DlBlendMode mode,
1028 const DlPaint& paint) {
1029 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawVerticesFlags);
1030 drawVertices(vertices, mode);
1031}
1032
1033void DisplayListBuilder::drawImage(const sk_sp<DlImage> image,
1034 const SkPoint point,
1035 DlImageSampling sampling,
1036 bool render_with_attributes) {
1037 DisplayListAttributeFlags flags = render_with_attributes //
1038 ? kDrawImageWithPaintFlags
1039 : kDrawImageFlags;
1040 OpResult result = PaintResult(paint: current_, flags);
1041 if (result == OpResult::kNoEffect) {
1042 return;
1043 }
1044 SkRect bounds = SkRect::MakeXYWH(x: point.fX, y: point.fY, //
1045 w: image->width(), h: image->height());
1046 if (AccumulateOpBounds(bounds, flags)) {
1047 render_with_attributes
1048 ? Push<DrawImageWithAttrOp>(pod: 0, render_op_inc: 1, args: image, args: point, args&: sampling)
1049 : Push<DrawImageOp>(pod: 0, render_op_inc: 1, args: image, args: point, args&: sampling);
1050 CheckLayerOpacityCompatibility(uses_blend_attribute: render_with_attributes);
1051 UpdateLayerResult(result);
1052 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1053 }
1054}
1055void DisplayListBuilder::DrawImage(const sk_sp<DlImage>& image,
1056 const SkPoint point,
1057 DlImageSampling sampling,
1058 const DlPaint* paint) {
1059 if (paint != nullptr) {
1060 SetAttributesFromPaint(paint: *paint,
1061 flags: DisplayListOpFlags::kDrawImageWithPaintFlags);
1062 drawImage(image, point, sampling, render_with_attributes: true);
1063 } else {
1064 drawImage(image, point, sampling, render_with_attributes: false);
1065 }
1066}
1067void DisplayListBuilder::drawImageRect(const sk_sp<DlImage> image,
1068 const SkRect& src,
1069 const SkRect& dst,
1070 DlImageSampling sampling,
1071 bool render_with_attributes,
1072 SrcRectConstraint constraint) {
1073 DisplayListAttributeFlags flags = render_with_attributes
1074 ? kDrawImageRectWithPaintFlags
1075 : kDrawImageRectFlags;
1076 OpResult result = PaintResult(paint: current_, flags);
1077 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds: dst, flags)) {
1078 Push<DrawImageRectOp>(pod: 0, render_op_inc: 1, args: image, args: src, args: dst, args&: sampling,
1079 args&: render_with_attributes, args&: constraint);
1080 CheckLayerOpacityCompatibility(uses_blend_attribute: render_with_attributes);
1081 UpdateLayerResult(result);
1082 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1083 }
1084}
1085void DisplayListBuilder::DrawImageRect(const sk_sp<DlImage>& image,
1086 const SkRect& src,
1087 const SkRect& dst,
1088 DlImageSampling sampling,
1089 const DlPaint* paint,
1090 SrcRectConstraint constraint) {
1091 if (paint != nullptr) {
1092 SetAttributesFromPaint(paint: *paint,
1093 flags: DisplayListOpFlags::kDrawImageRectWithPaintFlags);
1094 drawImageRect(image, src, dst, sampling, render_with_attributes: true, constraint);
1095 } else {
1096 drawImageRect(image, src, dst, sampling, render_with_attributes: false, constraint);
1097 }
1098}
1099void DisplayListBuilder::drawImageNine(const sk_sp<DlImage> image,
1100 const SkIRect& center,
1101 const SkRect& dst,
1102 DlFilterMode filter,
1103 bool render_with_attributes) {
1104 DisplayListAttributeFlags flags = render_with_attributes
1105 ? kDrawImageNineWithPaintFlags
1106 : kDrawImageNineFlags;
1107 OpResult result = PaintResult(paint: current_, flags);
1108 if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds: dst, flags)) {
1109 render_with_attributes
1110 ? Push<DrawImageNineWithAttrOp>(pod: 0, render_op_inc: 1, args: image, args: center, args: dst, args&: filter)
1111 : Push<DrawImageNineOp>(pod: 0, render_op_inc: 1, args: image, args: center, args: dst, args&: filter);
1112 CheckLayerOpacityCompatibility(uses_blend_attribute: render_with_attributes);
1113 UpdateLayerResult(result);
1114 is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
1115 }
1116}
1117void DisplayListBuilder::DrawImageNine(const sk_sp<DlImage>& image,
1118 const SkIRect& center,
1119 const SkRect& dst,
1120 DlFilterMode filter,
1121 const DlPaint* paint) {
1122 if (paint != nullptr) {
1123 SetAttributesFromPaint(paint: *paint,
1124 flags: DisplayListOpFlags::kDrawImageNineWithPaintFlags);
1125 drawImageNine(image, center, dst, filter, render_with_attributes: true);
1126 } else {
1127 drawImageNine(image, center, dst, filter, render_with_attributes: false);
1128 }
1129}
1130void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
1131 const SkRSXform xform[],
1132 const SkRect tex[],
1133 const DlColor colors[],
1134 int count,
1135 DlBlendMode mode,
1136 DlImageSampling sampling,
1137 const SkRect* cull_rect,
1138 bool render_with_attributes) {
1139 DisplayListAttributeFlags flags = render_with_attributes //
1140 ? kDrawAtlasWithPaintFlags
1141 : kDrawAtlasFlags;
1142 OpResult result = PaintResult(paint: current_, flags);
1143 if (result == OpResult::kNoEffect) {
1144 return;
1145 }
1146 SkPoint quad[4];
1147 RectBoundsAccumulator atlasBounds;
1148 for (int i = 0; i < count; i++) {
1149 const SkRect& src = tex[i];
1150 xform[i].toQuad(width: src.width(), height: src.height(), quad);
1151 for (int j = 0; j < 4; j++) {
1152 atlasBounds.accumulate(p: quad[j]);
1153 }
1154 }
1155 if (atlasBounds.is_empty() ||
1156 !AccumulateOpBounds(bounds: atlasBounds.bounds(), flags)) {
1157 return;
1158 }
1159
1160 int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect));
1161 void* data_ptr;
1162 if (colors != nullptr) {
1163 bytes += count * sizeof(DlColor);
1164 if (cull_rect != nullptr) {
1165 data_ptr =
1166 Push<DrawAtlasCulledOp>(pod: bytes, render_op_inc: 1, args: atlas, args&: count, args&: mode, args&: sampling, args: true,
1167 args: *cull_rect, args&: render_with_attributes);
1168 } else {
1169 data_ptr = Push<DrawAtlasOp>(pod: bytes, render_op_inc: 1, args: atlas, args&: count, args&: mode, args&: sampling, args: true,
1170 args&: render_with_attributes);
1171 }
1172 CopyV(dst: data_ptr, src: xform, n: count, rest&: tex, rest&: count, rest&: colors, rest&: count);
1173 } else {
1174 if (cull_rect != nullptr) {
1175 data_ptr =
1176 Push<DrawAtlasCulledOp>(pod: bytes, render_op_inc: 1, args: atlas, args&: count, args&: mode, args&: sampling, args: false,
1177 args: *cull_rect, args&: render_with_attributes);
1178 } else {
1179 data_ptr = Push<DrawAtlasOp>(pod: bytes, render_op_inc: 1, args: atlas, args&: count, args&: mode, args&: sampling,
1180 args: false, args&: render_with_attributes);
1181 }
1182 CopyV(dst: data_ptr, src: xform, n: count, rest&: tex, rest&: count);
1183 }
1184 // drawAtlas treats each image as a separate operation so we cannot rely
1185 // on it to distribute the opacity without overlap without checking all
1186 // of the transforms and texture rectangles.
1187 UpdateLayerOpacityCompatibility(compatible: false);
1188 UpdateLayerResult(result);
1189 is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe();
1190}
1191void DisplayListBuilder::DrawAtlas(const sk_sp<DlImage>& atlas,
1192 const SkRSXform xform[],
1193 const SkRect tex[],
1194 const DlColor colors[],
1195 int count,
1196 DlBlendMode mode,
1197 DlImageSampling sampling,
1198 const SkRect* cull_rect,
1199 const DlPaint* paint) {
1200 if (paint != nullptr) {
1201 SetAttributesFromPaint(paint: *paint,
1202 flags: DisplayListOpFlags::kDrawAtlasWithPaintFlags);
1203 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1204 render_with_attributes: true);
1205 } else {
1206 drawAtlas(atlas, xform, tex, colors, count, mode, sampling, cull_rect,
1207 render_with_attributes: false);
1208 }
1209}
1210
1211void DisplayListBuilder::DrawDisplayList(const sk_sp<DisplayList> display_list,
1212 SkScalar opacity) {
1213 if (!SkScalarIsFinite(x: opacity) || opacity <= SK_ScalarNearlyZero ||
1214 display_list->op_count() == 0 || display_list->bounds().isEmpty() ||
1215 current_layer_->is_nop_) {
1216 return;
1217 }
1218 const SkRect bounds = display_list->bounds();
1219 bool accumulated;
1220 switch (accumulator()->type()) {
1221 case BoundsAccumulatorType::kRect:
1222 accumulated = AccumulateOpBounds(bounds, flags: kDrawDisplayListFlags);
1223 break;
1224 case BoundsAccumulatorType::kRTree:
1225 auto rtree = display_list->rtree();
1226 if (rtree) {
1227 std::list<SkRect> rects =
1228 rtree->searchAndConsolidateRects(query: bounds, deband: false);
1229 accumulated = false;
1230 for (const SkRect& rect : rects) {
1231 // TODO (https://github.com/flutter/flutter/issues/114919): Attributes
1232 // are not necessarily `kDrawDisplayListFlags`.
1233 if (AccumulateOpBounds(bounds: rect, flags: kDrawDisplayListFlags)) {
1234 accumulated = true;
1235 }
1236 }
1237 } else {
1238 accumulated = AccumulateOpBounds(bounds, flags: kDrawDisplayListFlags);
1239 }
1240 break;
1241 }
1242 if (!accumulated) {
1243 return;
1244 }
1245
1246 DlPaint current_paint = current_;
1247 Push<DrawDisplayListOp>(pod: 0, render_op_inc: 1, args: display_list,
1248 args: opacity < SK_Scalar1 ? opacity : SK_Scalar1);
1249 is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe();
1250 // Not really necessary if the developer is interacting with us via
1251 // our attribute-state-less DlCanvas methods, but this avoids surprises
1252 // for those who may have been using the stateful Dispatcher methods.
1253 SetAttributesFromPaint(paint: current_paint,
1254 flags: DisplayListOpFlags::kSaveLayerWithPaintFlags);
1255
1256 // The non-nested op count accumulated in the |Push| method will include
1257 // this call to |drawDisplayList| for non-nested op count metrics.
1258 // But, for nested op count metrics we want the |drawDisplayList| call itself
1259 // to be transparent. So we subtract 1 from our accumulated nested count to
1260 // balance out against the 1 that was accumulated into the regular count.
1261 // This behavior is identical to the way SkPicture computed nested op counts.
1262 nested_op_count_ += display_list->op_count(nested: true) - 1;
1263 nested_bytes_ += display_list->bytes(nested: true);
1264 UpdateLayerOpacityCompatibility(compatible: display_list->can_apply_group_opacity());
1265 // Nop DisplayLists are eliminated above so we either affect transparent
1266 // pixels or we do not. We should not have [kNoEffect].
1267 UpdateLayerResult(result: display_list->modifies_transparent_black()
1268 ? OpResult::kAffectsAll
1269 : OpResult::kPreservesTransparency);
1270}
1271void DisplayListBuilder::drawTextBlob(const sk_sp<SkTextBlob> blob,
1272 SkScalar x,
1273 SkScalar y) {
1274 DisplayListAttributeFlags flags = kDrawTextBlobFlags;
1275 OpResult result = PaintResult(paint: current_, flags);
1276 if (result == OpResult::kNoEffect) {
1277 return;
1278 }
1279 bool unclipped = AccumulateOpBounds(bounds: blob->bounds().makeOffset(dx: x, dy: y), flags);
1280 // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the
1281 // unit tests can use Fuchsia's font manager instead of the empty default.
1282 // Until then we might encounter empty bounds for otherwise valid text and
1283 // thus we ignore the results from AccumulateOpBounds.
1284#if defined(OS_FUCHSIA)
1285 unclipped = true;
1286#endif // OS_FUCHSIA
1287 if (unclipped) {
1288 Push<DrawTextBlobOp>(pod: 0, render_op_inc: 1, args: blob, args&: x, args&: y);
1289 // There is no way to query if the glyphs of a text blob overlap and
1290 // there are no current guarantees from either Skia or Impeller that
1291 // they will protect overlapping glyphs from the effects of overdraw
1292 // so we must make the conservative assessment that this DL layer is
1293 // not compatible with group opacity inheritance.
1294 UpdateLayerOpacityCompatibility(compatible: false);
1295 UpdateLayerResult(result);
1296 }
1297}
1298void DisplayListBuilder::DrawTextBlob(const sk_sp<SkTextBlob>& blob,
1299 SkScalar x,
1300 SkScalar y,
1301 const DlPaint& paint) {
1302 SetAttributesFromPaint(paint, flags: DisplayListOpFlags::kDrawTextBlobFlags);
1303 drawTextBlob(blob, x, y);
1304}
1305void DisplayListBuilder::DrawShadow(const SkPath& path,
1306 const DlColor color,
1307 const SkScalar elevation,
1308 bool transparent_occluder,
1309 SkScalar dpr) {
1310 OpResult result = PaintResult(paint: DlPaint(color));
1311 if (result != OpResult::kNoEffect) {
1312 SkRect shadow_bounds =
1313 DlCanvas::ComputeShadowBounds(path, elevation, dpr, ctm: GetTransform());
1314 if (AccumulateOpBounds(bounds&: shadow_bounds, flags: kDrawShadowFlags)) {
1315 transparent_occluder //
1316 ? Push<DrawShadowTransparentOccluderOp>(pod: 0, render_op_inc: 1, args: path, args: color, args: elevation,
1317 args&: dpr)
1318 : Push<DrawShadowOp>(pod: 0, render_op_inc: 1, args: path, args: color, args: elevation, args&: dpr);
1319 UpdateLayerOpacityCompatibility(compatible: false);
1320 UpdateLayerResult(result);
1321 }
1322 }
1323}
1324
1325bool DisplayListBuilder::ComputeFilteredBounds(SkRect& bounds,
1326 const DlImageFilter* filter) {
1327 if (filter) {
1328 if (!filter->map_local_bounds(input_bounds: bounds, output_bounds&: bounds)) {
1329 return false;
1330 }
1331 }
1332 return true;
1333}
1334
1335bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds,
1336 DisplayListAttributeFlags flags) {
1337 if (flags.ignores_paint()) {
1338 return true;
1339 }
1340
1341 if (flags.is_geometric()) {
1342 bool is_stroked = flags.is_stroked(style: current_.getDrawStyle());
1343
1344 // Path effect occurs before stroking...
1345 DisplayListSpecialGeometryFlags special_flags =
1346 flags.WithPathEffect(effect: current_.getPathEffectPtr(), is_stroked);
1347 if (current_.getPathEffect()) {
1348 auto effect_bounds = current_.getPathEffect()->effect_bounds(bounds);
1349 if (!effect_bounds.has_value()) {
1350 return false;
1351 }
1352 bounds = effect_bounds.value();
1353 }
1354
1355 if (is_stroked) {
1356 // Determine the max multiplier to the stroke width first.
1357 SkScalar pad = 1.0f;
1358 if (current_.getStrokeJoin() == DlStrokeJoin::kMiter &&
1359 special_flags.may_have_acute_joins()) {
1360 pad = std::max(a: pad, b: current_.getStrokeMiter());
1361 }
1362 if (current_.getStrokeCap() == DlStrokeCap::kSquare &&
1363 special_flags.may_have_diagonal_caps()) {
1364 pad = std::max(a: pad, SK_ScalarSqrt2);
1365 }
1366 SkScalar min_stroke_width = 0.01;
1367 pad *= std::max(a: current_.getStrokeWidth() * 0.5f, b: min_stroke_width);
1368 bounds.outset(dx: pad, dy: pad);
1369 }
1370 }
1371
1372 if (flags.applies_mask_filter()) {
1373 auto filter = current_.getMaskFilter();
1374 if (filter) {
1375 switch (filter->type()) {
1376 case DlMaskFilterType::kBlur: {
1377 FML_DCHECK(filter->asBlur());
1378 SkScalar mask_sigma_pad = filter->asBlur()->sigma() * 3.0;
1379 bounds.outset(dx: mask_sigma_pad, dy: mask_sigma_pad);
1380 }
1381 }
1382 }
1383 }
1384
1385 if (flags.applies_image_filter()) {
1386 return ComputeFilteredBounds(bounds, filter: current_.getImageFilter().get());
1387 }
1388
1389 return true;
1390}
1391
1392bool DisplayListBuilder::AccumulateUnbounded() {
1393 SkRect clip = tracker_.device_cull_rect();
1394 if (clip.isEmpty()) {
1395 return false;
1396 }
1397 accumulator()->accumulate(r: clip, index: op_index_);
1398 return true;
1399}
1400
1401bool DisplayListBuilder::AccumulateOpBounds(SkRect& bounds,
1402 DisplayListAttributeFlags flags) {
1403 if (AdjustBoundsForPaint(bounds, flags)) {
1404 return AccumulateBounds(bounds);
1405 } else {
1406 return AccumulateUnbounded();
1407 }
1408}
1409bool DisplayListBuilder::AccumulateBounds(SkRect& bounds) {
1410 if (!bounds.isEmpty()) {
1411 tracker_.mapRect(rect: &bounds);
1412 if (bounds.intersect(r: tracker_.device_cull_rect())) {
1413 accumulator()->accumulate(r: bounds, index: op_index_);
1414 return true;
1415 }
1416 }
1417 return false;
1418}
1419
1420bool DisplayListBuilder::paint_nops_on_transparency() {
1421 // SkImageFilter::canComputeFastBounds tests for transparency behavior
1422 // This test assumes that the blend mode checked down below will
1423 // NOP on transparent black.
1424 if (current_.getImageFilterPtr() &&
1425 current_.getImageFilterPtr()->modifies_transparent_black()) {
1426 return false;
1427 }
1428
1429 // We filter the transparent black that is used for the background of a
1430 // saveLayer and make sure it returns transparent black. If it does, then
1431 // the color filter will leave all area surrounding the contents of the
1432 // save layer untouched out to the edge of the output surface.
1433 // This test assumes that the blend mode checked down below will
1434 // NOP on transparent black.
1435 if (current_.getColorFilterPtr() &&
1436 current_.getColorFilterPtr()->modifies_transparent_black()) {
1437 return false;
1438 }
1439
1440 // Unusual blendmodes require us to process a saved layer
1441 // even with operations outside the clip.
1442 // For example, DstIn is used by masking layers.
1443 // https://code.google.com/p/skia/issues/detail?id=1291
1444 // https://crbug.com/401593
1445 switch (current_.getBlendMode()) {
1446 // For each of the following transfer modes, if the source
1447 // alpha is zero (our transparent black), the resulting
1448 // blended pixel is not necessarily equal to the original
1449 // destination pixel.
1450 // Mathematically, any time in the following equations where
1451 // the result is not d assuming source is 0
1452 case DlBlendMode::kClear: // r = 0
1453 case DlBlendMode::kSrc: // r = s
1454 case DlBlendMode::kSrcIn: // r = s * da
1455 case DlBlendMode::kDstIn: // r = d * sa
1456 case DlBlendMode::kSrcOut: // r = s * (1-da)
1457 case DlBlendMode::kDstATop: // r = d*sa + s*(1-da)
1458 case DlBlendMode::kModulate: // r = s*d
1459 return false;
1460 break;
1461
1462 // And in these equations, the result must be d if the
1463 // source is 0
1464 case DlBlendMode::kDst: // r = d
1465 case DlBlendMode::kSrcOver: // r = s + (1-sa)*d
1466 case DlBlendMode::kDstOver: // r = d + (1-da)*s
1467 case DlBlendMode::kDstOut: // r = d * (1-sa)
1468 case DlBlendMode::kSrcATop: // r = s*da + d*(1-sa)
1469 case DlBlendMode::kXor: // r = s*(1-da) + d*(1-sa)
1470 case DlBlendMode::kPlus: // r = min(s + d, 1)
1471 case DlBlendMode::kScreen: // r = s + d - s*d
1472 case DlBlendMode::kOverlay: // multiply or screen, depending on dest
1473 case DlBlendMode::kDarken: // rc = s + d - max(s*da, d*sa),
1474 // ra = kSrcOver
1475 case DlBlendMode::kLighten: // rc = s + d - min(s*da, d*sa),
1476 // ra = kSrcOver
1477 case DlBlendMode::kColorDodge: // brighten destination to reflect source
1478 case DlBlendMode::kColorBurn: // darken destination to reflect source
1479 case DlBlendMode::kHardLight: // multiply or screen, depending on source
1480 case DlBlendMode::kSoftLight: // lighten or darken, depending on source
1481 case DlBlendMode::kDifference: // rc = s + d - 2*(min(s*da, d*sa)),
1482 // ra = kSrcOver
1483 case DlBlendMode::kExclusion: // rc = s + d - two(s*d), ra = kSrcOver
1484 case DlBlendMode::kMultiply: // r = s*(1-da) + d*(1-sa) + s*d
1485 case DlBlendMode::kHue: // ra = kSrcOver
1486 case DlBlendMode::kSaturation: // ra = kSrcOver
1487 case DlBlendMode::kColor: // ra = kSrcOver
1488 case DlBlendMode::kLuminosity: // ra = kSrcOver
1489 return true;
1490 break;
1491 }
1492}
1493
1494DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint,
1495 DisplayListAttributeFlags flags) {
1496 DlColor color;
1497 if (flags.applies_color()) {
1498 const DlColorSource* source = paint.getColorSourcePtr();
1499 if (source) {
1500 if (source->asColor()) {
1501 color = source->asColor()->color();
1502 } else {
1503 color = source->is_opaque() ? DlColor::kBlack() : kAnyColor;
1504 }
1505 } else {
1506 color = paint.getColor();
1507 }
1508 } else if (flags.applies_alpha()) {
1509 // If the operation applies alpha, but not color, then the only impact
1510 // of the alpha is to modulate the output towards transparency.
1511 // We can not guarantee an opaque source even if the alpha is opaque
1512 // since that would require knowing something about the colors that
1513 // the alpha is modulating, but we can guarantee a transparent source
1514 // if the alpha is 0.
1515 color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor;
1516 } else {
1517 color = kAnyColor;
1518 }
1519 if (flags.applies_image_filter()) {
1520 auto filter = paint.getImageFilterPtr();
1521 if (filter) {
1522 if (!color.isTransparent() || filter->modifies_transparent_black()) {
1523 color = kAnyColor;
1524 }
1525 }
1526 }
1527 if (flags.applies_color_filter()) {
1528 auto filter = paint.getColorFilterPtr();
1529 if (filter) {
1530 if (!color.isTransparent() || filter->modifies_transparent_black()) {
1531 color = kAnyColor;
1532 }
1533 }
1534 }
1535 return color;
1536}
1537
1538DisplayListBuilder::OpResult DisplayListBuilder::PaintResult(
1539 const DlPaint& paint,
1540 DisplayListAttributeFlags flags) {
1541 if (current_layer_->is_nop_) {
1542 return OpResult::kNoEffect;
1543 }
1544 if (flags.applies_blend()) {
1545 switch (paint.getBlendMode()) {
1546 // Nop blend mode (singular, there is only one)
1547 case DlBlendMode::kDst:
1548 return OpResult::kNoEffect;
1549
1550 // Always clears pixels blend mode (singular, there is only one)
1551 case DlBlendMode::kClear:
1552 return OpResult::kPreservesTransparency;
1553
1554 case DlBlendMode::kHue:
1555 case DlBlendMode::kSaturation:
1556 case DlBlendMode::kColor:
1557 case DlBlendMode::kLuminosity:
1558 case DlBlendMode::kColorBurn:
1559 return GetEffectiveColor(paint, flags).isTransparent()
1560 ? OpResult::kNoEffect
1561 : OpResult::kAffectsAll;
1562
1563 // kSrcIn modifies pixels towards transparency
1564 case DlBlendMode::kSrcIn:
1565 return OpResult::kPreservesTransparency;
1566
1567 // These blend modes preserve destination alpha
1568 case DlBlendMode::kSrcATop:
1569 case DlBlendMode::kDstOut:
1570 return GetEffectiveColor(paint, flags).isTransparent()
1571 ? OpResult::kNoEffect
1572 : OpResult::kPreservesTransparency;
1573
1574 // Always destructive blend modes, potentially not affecting transparency
1575 case DlBlendMode::kSrc:
1576 case DlBlendMode::kSrcOut:
1577 case DlBlendMode::kDstATop:
1578 return GetEffectiveColor(paint, flags).isTransparent()
1579 ? OpResult::kPreservesTransparency
1580 : OpResult::kAffectsAll;
1581
1582 // The kDstIn blend mode modifies the destination unless the
1583 // source color is opaque.
1584 case DlBlendMode::kDstIn:
1585 return GetEffectiveColor(paint, flags).isOpaque()
1586 ? OpResult::kNoEffect
1587 : OpResult::kPreservesTransparency;
1588
1589 // The next group of blend modes modifies the destination unless the
1590 // source color is transparent.
1591 case DlBlendMode::kSrcOver:
1592 case DlBlendMode::kDstOver:
1593 case DlBlendMode::kXor:
1594 case DlBlendMode::kPlus:
1595 case DlBlendMode::kScreen:
1596 case DlBlendMode::kMultiply:
1597 case DlBlendMode::kOverlay:
1598 case DlBlendMode::kDarken:
1599 case DlBlendMode::kLighten:
1600 case DlBlendMode::kColorDodge:
1601 case DlBlendMode::kHardLight:
1602 case DlBlendMode::kSoftLight:
1603 case DlBlendMode::kDifference:
1604 case DlBlendMode::kExclusion:
1605 return GetEffectiveColor(paint, flags).isTransparent()
1606 ? OpResult::kNoEffect
1607 : OpResult::kAffectsAll;
1608
1609 // Modulate only leaves the pixel alone when the source is white.
1610 case DlBlendMode::kModulate:
1611 return GetEffectiveColor(paint, flags) == DlColor::kWhite()
1612 ? OpResult::kNoEffect
1613 : OpResult::kPreservesTransparency;
1614 }
1615 }
1616 return OpResult::kAffectsAll;
1617}
1618
1619} // namespace flutter
1620

source code of flutter_engine/flutter/display_list/dl_builder.cc