Flutter Engine
 
Loading...
Searching...
No Matches
dl_dispatcher.cc File Reference

Go to the source code of this file.

Namespaces

namespace  impeller
 

Macros

#define USE_DEPTH_WATCHER   true
 
#define AUTO_DEPTH_WATCHER(d)
 
#define AUTO_DEPTH_CHECK()
 
#define UNIMPLEMENTED    FML_DLOG(ERROR) << "Unimplemented detail in " << __FUNCTION__;
 

Functions

static impeller::SamplerDescriptor impeller::ToSamplerDescriptor (const flutter::DlFilterMode options)
 
static std::optional< const Rectimpeller::ToOptRect (const flutter::DlRect *rect)
 
static Paint::Style impeller::ToStyle (flutter::DlDrawStyle style)
 
static FilterContents::BlurStyle impeller::ToBlurStyle (flutter::DlBlurStyle blur_style)
 
static Entity::ClipOperation impeller::ToClipOperation (flutter::DlClipOp clip_op)
 
static bool impeller::RequiresReadbackForBlends (const ContentContext &renderer, flutter::DlBlendMode max_root_blend_mode)
 Subclasses.
 
std::shared_ptr< Textureimpeller::DisplayListToTexture (const sk_sp< flutter::DisplayList > &display_list, ISize size, AiksContext &context, bool reset_host_buffer=true, bool generate_mips=false)
 Render the provided display list to a texture with the given size.
 
bool impeller::RenderToTarget (ContentContext &context, RenderTarget render_target, const sk_sp< flutter::DisplayList > &display_list, Rect cull_rect, bool reset_host_buffer, bool is_onscreen=true)
 Render the provided display list to the render target.
 

Macro Definition Documentation

◆ AUTO_DEPTH_CHECK

#define AUTO_DEPTH_CHECK ( )

Definition at line 115 of file dl_dispatcher.cc.

◆ AUTO_DEPTH_WATCHER

#define AUTO_DEPTH_WATCHER (   d)

Definition at line 114 of file dl_dispatcher.cc.

◆ UNIMPLEMENTED

#define UNIMPLEMENTED    FML_DLOG(ERROR) << "Unimplemented detail in " << __FUNCTION__;

Definition at line 119 of file dl_dispatcher.cc.

122 {
124 switch (options) {
127 desc.label = "Nearest Sampler";
128 break;
131 desc.label = "Linear Sampler";
132 break;
133 default:
134 break;
135 }
136 return desc;
137}
138
139static std::optional<const Rect> ToOptRect(const flutter::DlRect* rect) {
140 if (rect == nullptr) {
141 return std::nullopt;
142 }
143 return *rect;
144}
145
146// |flutter::DlOpReceiver|
147void DlDispatcherBase::setAntiAlias(bool aa) {
149
150 // Nothing to do because AA is implicit.
151}
152
153static Paint::Style ToStyle(flutter::DlDrawStyle style) {
154 switch (style) {
156 return Paint::Style::kFill;
158 return Paint::Style::kStroke;
161 break;
162 }
163 return Paint::Style::kFill;
164}
165
166// |flutter::DlOpReceiver|
167void DlDispatcherBase::setDrawStyle(flutter::DlDrawStyle style) {
169
170 paint_.style = ToStyle(style);
171}
172
173// |flutter::DlOpReceiver|
174void DlDispatcherBase::setColor(flutter::DlColor color) {
176
177 paint_.color = skia_conversions::ToColor(color);
178}
179
180// |flutter::DlOpReceiver|
181void DlDispatcherBase::setStrokeWidth(DlScalar width) {
183
184 paint_.stroke.width = width;
185}
186
187// |flutter::DlOpReceiver|
188void DlDispatcherBase::setStrokeMiter(DlScalar limit) {
190
191 paint_.stroke.miter_limit = limit;
192}
193
194// |flutter::DlOpReceiver|
195void DlDispatcherBase::setStrokeCap(flutter::DlStrokeCap cap) {
197
198 switch (cap) {
200 paint_.stroke.cap = Cap::kButt;
201 break;
203 paint_.stroke.cap = Cap::kRound;
204 break;
206 paint_.stroke.cap = Cap::kSquare;
207 break;
208 }
209}
210
211// |flutter::DlOpReceiver|
212void DlDispatcherBase::setStrokeJoin(flutter::DlStrokeJoin join) {
214
215 switch (join) {
217 paint_.stroke.join = Join::kMiter;
218 break;
220 paint_.stroke.join = Join::kRound;
221 break;
223 paint_.stroke.join = Join::kBevel;
224 break;
225 }
226}
227
228// |flutter::DlOpReceiver|
229void DlDispatcherBase::setColorSource(const flutter::DlColorSource* source) {
231
232 paint_.color_source = source;
233}
234
235// |flutter::DlOpReceiver|
236void DlDispatcherBase::setColorFilter(const flutter::DlColorFilter* filter) {
238
239 paint_.color_filter = filter;
240}
241
242// |flutter::DlOpReceiver|
243void DlDispatcherBase::setInvertColors(bool invert) {
245
246 paint_.invert_colors = invert;
247}
248
249// |flutter::DlOpReceiver|
250void DlDispatcherBase::setBlendMode(flutter::DlBlendMode dl_mode) {
252
253 paint_.blend_mode = dl_mode;
254}
255
256static FilterContents::BlurStyle ToBlurStyle(flutter::DlBlurStyle blur_style) {
257 switch (blur_style) {
259 return FilterContents::BlurStyle::kNormal;
261 return FilterContents::BlurStyle::kSolid;
263 return FilterContents::BlurStyle::kOuter;
265 return FilterContents::BlurStyle::kInner;
266 }
267}
268
269// |flutter::DlOpReceiver|
270void DlDispatcherBase::setMaskFilter(const flutter::DlMaskFilter* filter) {
272
273 // Needs https://github.com/flutter/flutter/issues/95434
274 if (filter == nullptr) {
275 paint_.mask_blur_descriptor = std::nullopt;
276 return;
277 }
278 switch (filter->type()) {
280 auto blur = filter->asBlur();
281
282 paint_.mask_blur_descriptor = {
283 .style = ToBlurStyle(blur->style()),
284 .sigma = Sigma(blur->sigma()),
285 .respect_ctm = blur->respectCTM(),
286 };
287 break;
288 }
289 }
290}
291
292// |flutter::DlOpReceiver|
293void DlDispatcherBase::setImageFilter(const flutter::DlImageFilter* filter) {
295
296 paint_.image_filter = filter;
297}
298
299// |flutter::DlOpReceiver|
300void DlDispatcherBase::save(uint32_t total_content_depth) {
302
303 GetCanvas().Save(total_content_depth);
304}
305
306// |flutter::DlOpReceiver|
307void DlDispatcherBase::saveLayer(const DlRect& bounds,
308 const flutter::SaveLayerOptions& options,
309 uint32_t total_content_depth,
310 flutter::DlBlendMode max_content_mode,
311 const flutter::DlImageFilter* backdrop,
312 std::optional<int64_t> backdrop_id) {
314
315 auto paint = options.renders_with_attributes() ? paint_ : Paint{};
316 auto promise = options.content_is_clipped()
317 ? ContentBoundsPromise::kMayClipContents
318 : ContentBoundsPromise::kContainsContents;
319 std::optional<Rect> impeller_bounds;
320 // If the content is unbounded but has developer specified bounds, we take
321 // the original bounds so that we clip the content as expected.
322 if (!options.content_is_unbounded() || options.bounds_from_caller()) {
323 impeller_bounds = bounds;
324 }
325
326 GetCanvas().SaveLayer(
327 paint, impeller_bounds, backdrop, promise, total_content_depth,
328 // Unbounded content can still have user specified bounds that require a
329 // saveLayer to be created to perform the clip.
330 options.can_distribute_opacity() && !options.content_is_unbounded(),
331 backdrop_id //
332 );
333}
334
335// |flutter::DlOpReceiver|
336void DlDispatcherBase::restore() {
337 GetCanvas().Restore();
338}
339
340// |flutter::DlOpReceiver|
341void DlDispatcherBase::translate(DlScalar tx, DlScalar ty) {
343
344 GetCanvas().Translate({tx, ty, 0.0});
345}
346
347// |flutter::DlOpReceiver|
348void DlDispatcherBase::scale(DlScalar sx, DlScalar sy) {
350
351 GetCanvas().Scale({sx, sy, 1.0});
352}
353
354// |flutter::DlOpReceiver|
355void DlDispatcherBase::rotate(DlScalar degrees) {
357
358 GetCanvas().Rotate(Degrees{degrees});
359}
360
361// |flutter::DlOpReceiver|
362void DlDispatcherBase::skew(DlScalar sx, DlScalar sy) {
364
365 GetCanvas().Skew(sx, sy);
366}
367
368// clang-format off
369// |flutter::DlOpReceiver|
370void DlDispatcherBase::transform2DAffine(
371 DlScalar mxx, DlScalar mxy, DlScalar mxt,
372 DlScalar myx, DlScalar myy, DlScalar myt) {
374
375 transformFullPerspective(
376 mxx, mxy, 0, mxt,
377 myx, myy, 0, myt,
378 0 , 0, 1, 0,
379 0 , 0, 0, 1
380 );
381}
382// clang-format on
383
384// clang-format off
385// |flutter::DlOpReceiver|
386void DlDispatcherBase::transformFullPerspective(
387 DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt,
388 DlScalar myx, DlScalar myy, DlScalar myz, DlScalar myt,
389 DlScalar mzx, DlScalar mzy, DlScalar mzz, DlScalar mzt,
390 DlScalar mwx, DlScalar mwy, DlScalar mwz, DlScalar mwt) {
392
393 // The order of arguments is row-major but Impeller matrices are
394 // column-major.
395 auto transform = Matrix{
396 mxx, myx, mzx, mwx,
397 mxy, myy, mzy, mwy,
398 mxz, myz, mzz, mwz,
399 mxt, myt, mzt, mwt
400 };
401 GetCanvas().Transform(transform);
402}
403// clang-format on
404
405// |flutter::DlOpReceiver|
406void DlDispatcherBase::transformReset() {
408
409 GetCanvas().ResetTransform();
410 GetCanvas().Transform(initial_matrix_);
411}
412
413static Entity::ClipOperation ToClipOperation(flutter::DlClipOp clip_op) {
414 switch (clip_op) {
416 return Entity::ClipOperation::kDifference;
418 return Entity::ClipOperation::kIntersect;
419 }
420}
421
422// |flutter::DlOpReceiver|
423void DlDispatcherBase::clipRect(const DlRect& rect,
424 flutter::DlClipOp clip_op,
425 bool is_aa) {
427
428 FillRectGeometry geom(rect);
429 GetCanvas().ClipGeometry(geom, ToClipOperation(clip_op), /*is_aa=*/is_aa);
430}
431
432// |flutter::DlOpReceiver|
433void DlDispatcherBase::clipOval(const DlRect& bounds,
434 flutter::DlClipOp clip_op,
435 bool is_aa) {
437
438 EllipseGeometry geom(bounds);
439 GetCanvas().ClipGeometry(geom, ToClipOperation(clip_op));
440}
441
442// |flutter::DlOpReceiver|
443void DlDispatcherBase::clipRoundRect(const DlRoundRect& rrect,
444 flutter::DlClipOp sk_op,
445 bool is_aa) {
447
448 auto clip_op = ToClipOperation(sk_op);
449 if (rrect.IsRect()) {
450 FillRectGeometry geom(rrect.GetBounds());
451 GetCanvas().ClipGeometry(geom, clip_op, /*is_aa=*/is_aa);
452 } else if (rrect.IsOval()) {
453 EllipseGeometry geom(rrect.GetBounds());
454 GetCanvas().ClipGeometry(geom, clip_op);
455 } else if (rrect.GetRadii().AreAllCornersSame()) {
456 RoundRectGeometry geom(rrect.GetBounds(), rrect.GetRadii().top_left);
457 GetCanvas().ClipGeometry(geom, clip_op);
458 } else {
459 FillRoundRectGeometry geom(rrect);
460 GetCanvas().ClipGeometry(geom, clip_op);
461 }
462}
463
464// |flutter::DlOpReceiver|
465void DlDispatcherBase::clipRoundSuperellipse(const DlRoundSuperellipse& rse,
466 flutter::DlClipOp sk_op,
467 bool is_aa) {
469
470 auto clip_op = ToClipOperation(sk_op);
471 if (rse.IsRect()) {
472 FillRectGeometry geom(rse.GetBounds());
473 GetCanvas().ClipGeometry(geom, clip_op, /*is_aa=*/is_aa);
474 } else if (rse.IsOval()) {
475 EllipseGeometry geom(rse.GetBounds());
476 GetCanvas().ClipGeometry(geom, clip_op);
477 } else {
478 RoundSuperellipseGeometry geom(rse.GetBounds(), rse.GetRadii());
479 GetCanvas().ClipGeometry(geom, clip_op);
480 }
481}
482
483// |flutter::DlOpReceiver|
484void DlDispatcherBase::clipPath(const DlPath& path,
485 flutter::DlClipOp sk_op,
486 bool is_aa) {
488
489 auto clip_op = ToClipOperation(sk_op);
490
491 DlRect rect;
492 if (path.IsRect(&rect)) {
493 FillRectGeometry geom(rect);
494 GetCanvas().ClipGeometry(geom, clip_op, /*is_aa=*/is_aa);
495 } else if (path.IsOval(&rect)) {
496 EllipseGeometry geom(rect);
497 GetCanvas().ClipGeometry(geom, clip_op);
498 } else {
499 DlRoundRect rrect;
500 if (path.IsRoundRect(&rrect) && rrect.GetRadii().AreAllCornersSame()) {
501 RoundRectGeometry geom(rrect.GetBounds(), rrect.GetRadii().top_left);
502 GetCanvas().ClipGeometry(geom, clip_op);
503 } else {
504 FillPathGeometry geom(path);
505 GetCanvas().ClipGeometry(geom, clip_op);
506 }
507 }
508}
509
510// |flutter::DlOpReceiver|
511void DlDispatcherBase::drawColor(flutter::DlColor color,
512 flutter::DlBlendMode dl_mode) {
514
515 Paint paint;
516 paint.color = skia_conversions::ToColor(color);
517 paint.blend_mode = dl_mode;
518 GetCanvas().DrawPaint(paint);
519}
520
521// |flutter::DlOpReceiver|
522void DlDispatcherBase::drawPaint() {
524
525 GetCanvas().DrawPaint(paint_);
526}
527
528// |flutter::DlOpReceiver|
529void DlDispatcherBase::drawLine(const DlPoint& p0, const DlPoint& p1) {
531
532 GetCanvas().DrawLine(p0, p1, paint_);
533}
534
535// |flutter::DlOpReceiver|
536void DlDispatcherBase::drawDashedLine(const DlPoint& p0,
537 const DlPoint& p1,
538 DlScalar on_length,
539 DlScalar off_length) {
541
542 GetCanvas().DrawDashedLine(p0, p1, on_length, off_length, paint_);
543}
544
545// |flutter::DlOpReceiver|
546void DlDispatcherBase::drawRect(const DlRect& rect) {
548
549 GetCanvas().DrawRect(rect, paint_);
550}
551
552// |flutter::DlOpReceiver|
553void DlDispatcherBase::drawOval(const DlRect& bounds) {
555
556 GetCanvas().DrawOval(bounds, paint_);
557}
558
559// |flutter::DlOpReceiver|
560void DlDispatcherBase::drawCircle(const DlPoint& center, DlScalar radius) {
562
563 GetCanvas().DrawCircle(center, radius, paint_);
564}
565
566// |flutter::DlOpReceiver|
567void DlDispatcherBase::drawRoundRect(const DlRoundRect& rrect) {
569
570 GetCanvas().DrawRoundRect(rrect, paint_);
571}
572
573// |flutter::DlOpReceiver|
574void DlDispatcherBase::drawDiffRoundRect(const DlRoundRect& outer,
575 const DlRoundRect& inner) {
577
578 GetCanvas().DrawDiffRoundRect(outer, inner, paint_);
579}
580
581// |flutter::DlOpReceiver|
582void DlDispatcherBase::drawRoundSuperellipse(const DlRoundSuperellipse& rse) {
584
585 GetCanvas().DrawRoundSuperellipse(rse, paint_);
586}
587
588// |flutter::DlOpReceiver|
589void DlDispatcherBase::drawPath(const DlPath& path) {
591
592 SimplifyOrDrawPath(GetCanvas(), path, paint_);
593}
594
595void DlDispatcherBase::SimplifyOrDrawPath(Canvas& canvas,
596 const DlPath& path,
597 const Paint& paint) {
598 DlRect rect;
599
600 // We can't "optimize" a path into a rectangle if it's open.
601 bool closed;
602 if (path.IsRect(&rect, &closed) && closed) {
603 canvas.DrawRect(rect, paint);
604 return;
605 }
606
607 DlRoundRect rrect;
608 if (path.IsRoundRect(&rrect) && rrect.GetRadii().AreAllCornersSame()) {
609 canvas.DrawRoundRect(rrect, paint);
610 return;
611 }
612
613 if (path.IsOval(&rect)) {
614 canvas.DrawOval(rect, paint);
615 return;
616 }
617
619 DlPoint end;
620 if (path.IsLine(&start, &end)) {
621 canvas.DrawLine(start, end, paint);
622 return;
623 }
624
625 canvas.DrawPath(path, paint);
626}
627
628// |flutter::DlOpReceiver|
629void DlDispatcherBase::drawArc(const DlRect& oval_bounds,
630 DlScalar start_degrees,
631 DlScalar sweep_degrees,
632 bool use_center) {
634
635 GetCanvas().DrawArc(Arc(oval_bounds, Degrees(start_degrees),
636 Degrees(sweep_degrees), use_center),
637 paint_);
638}
639
640// |flutter::DlOpReceiver|
641void DlDispatcherBase::drawPoints(flutter::DlPointMode mode,
642 uint32_t count,
643 const DlPoint points[]) {
645
646 Paint paint = paint_;
647 paint.style = Paint::Style::kStroke;
648 switch (mode) {
650 // Cap::kButt is also treated as a square.
651 PointStyle point_style = paint.stroke.cap == Cap::kRound
652 ? PointStyle::kRound
653 : PointStyle::kSquare;
654 Scalar radius = paint.stroke.width;
655 if (radius > 0) {
656 radius /= 2.0;
657 }
658 GetCanvas().DrawPoints(points, count, radius, paint, point_style);
659 } break;
661 for (uint32_t i = 1; i < count; i += 2) {
662 Point p0 = points[i - 1];
663 Point p1 = points[i];
664 GetCanvas().DrawLine(p0, p1, paint, /*reuse_depth=*/i > 1);
665 }
666 break;
668 if (count > 1) {
669 Point p0 = points[0];
670 for (uint32_t i = 1; i < count; i++) {
671 Point p1 = points[i];
672 GetCanvas().DrawLine(p0, p1, paint, /*reuse_depth=*/i > 1);
673 p0 = p1;
674 }
675 }
676 break;
677 }
678}
679
680void DlDispatcherBase::drawVertices(
681 const std::shared_ptr<flutter::DlVertices>& vertices,
682 flutter::DlBlendMode dl_mode) {}
683
684// |flutter::DlOpReceiver|
685void DlDispatcherBase::drawImage(const sk_sp<flutter::DlImage> image,
686 const DlPoint& point,
688 bool render_with_attributes) {
690
691 if (!image) {
692 return;
693 }
694
695 auto texture = image->impeller_texture();
696 if (!texture) {
697 return;
698 }
699
700 const auto size = texture->GetSize();
701 const auto src = DlRect::MakeWH(size.width, size.height);
702 const auto dest = DlRect::MakeXYWH(point.x, point.y, size.width, size.height);
703
704 drawImageRect(image, // image
705 src, // source rect
706 dest, // destination rect
707 sampling, // sampling options
708 render_with_attributes, // render with attributes
710 );
711}
712
713// |flutter::DlOpReceiver|
714void DlDispatcherBase::drawImageRect(const sk_sp<flutter::DlImage> image,
715 const DlRect& src,
716 const DlRect& dst,
718 bool render_with_attributes,
722
723 GetCanvas().DrawImageRect(
724 image->impeller_texture(), // image
725 src, // source rect
726 dst, // destination rect
727 render_with_attributes ? paint_ : Paint(), // paint
728 skia_conversions::ToSamplerDescriptor(sampling) // sampling
729 );
730}
731
732// |flutter::DlOpReceiver|
733void DlDispatcherBase::drawImageNine(const sk_sp<flutter::DlImage> image,
734 const DlIRect& center,
735 const DlRect& dst,
737 bool render_with_attributes) {
739
740 NinePatchConverter converter = {};
741 converter.DrawNinePatch(image->impeller_texture(),
742 Rect::MakeLTRB(center.GetLeft(), center.GetTop(),
743 center.GetRight(), center.GetBottom()),
744 dst, ToSamplerDescriptor(filter), &GetCanvas(),
745 &paint_);
746}
747
748// |flutter::DlOpReceiver|
749void DlDispatcherBase::drawAtlas(const sk_sp<flutter::DlImage> atlas,
750 const RSTransform xform[],
751 const DlRect tex[],
752 const flutter::DlColor colors[],
753 int count,
756 const DlRect* cull_rect,
757 bool render_with_attributes) {
759
760 auto geometry =
761 DlAtlasGeometry(atlas->impeller_texture(), //
762 xform, //
763 tex, //
764 colors, //
765 static_cast<size_t>(count), //
766 mode, //
767 skia_conversions::ToSamplerDescriptor(sampling), //
768 ToOptRect(cull_rect) //
769 );
770 auto atlas_contents = std::make_shared<AtlasContents>();
771 atlas_contents->SetGeometry(&geometry);
772
773 GetCanvas().DrawAtlas(atlas_contents, paint_);
774}
775
776// |flutter::DlOpReceiver|
777void DlDispatcherBase::drawDisplayList(
778 const sk_sp<flutter::DisplayList> display_list,
779 DlScalar opacity) {
780 AUTO_DEPTH_WATCHER(display_list->total_depth());
781
782 // Save all values that must remain untouched after the operation.
783 Paint saved_paint = paint_;
784 Matrix saved_initial_matrix = initial_matrix_;
785
786 // Establish a new baseline for interpreting the new DL.
787 // Matrix and clip are left untouched, the current
788 // transform is saved as the new base matrix, and paint
789 // values are reset to defaults.
790 initial_matrix_ = GetCanvas().GetCurrentTransform();
791 paint_ = Paint();
792
793 // Handle passed opacity in the most brute-force way by using
794 // a SaveLayer. If the display_list is able to inherit the
795 // opacity, this could also be handled by modulating all of its
796 // attribute settings (for example, color), by the indicated
797 // opacity.
798 int restore_count = GetCanvas().GetSaveCount();
799 if (opacity < SK_Scalar1) {
800 Paint save_paint;
801 save_paint.color = Color(0, 0, 0, opacity);
802 GetCanvas().SaveLayer(save_paint, display_list->GetBounds(), nullptr,
803 ContentBoundsPromise::kContainsContents,
804 display_list->total_depth(),
805 display_list->can_apply_group_opacity());
806 } else {
807 // The display list may alter the clip, which must be restored to the
808 // current clip at the end of playback.
809 GetCanvas().Save(display_list->total_depth());
810 }
811
812 // TODO(131445): Remove this restriction if we can correctly cull with
813 // perspective transforms.
814 if (display_list->has_rtree() && !initial_matrix_.HasPerspective()) {
815 // The canvas remembers the screen-space culling bounds clipped by
816 // the surface and the history of clip calls. DisplayList can cull
817 // the ops based on a rectangle expressed in its "destination bounds"
818 // so we need the canvas to transform those into the current local
819 // coordinate space into which the DisplayList will be rendered.
820 auto global_culling_bounds = GetCanvas().GetLocalCoverageLimit();
821 if (global_culling_bounds.has_value()) {
822 Rect cull_rect = global_culling_bounds->TransformBounds(
823 GetCanvas().GetCurrentTransform().Invert());
824 display_list->Dispatch(*this, cull_rect);
825 } else {
826 // If the culling bounds are empty, this display list can be skipped
827 // entirely.
828 }
829 } else {
830 display_list->Dispatch(*this);
831 }
832
833 // Restore all saved state back to what it was before we interpreted
834 // the display_list
836 GetCanvas().RestoreToCount(restore_count);
837 initial_matrix_ = saved_initial_matrix;
838 paint_ = saved_paint;
839}
840
841// |flutter::DlOpReceiver|
842void DlDispatcherBase::drawText(const std::shared_ptr<flutter::DlText>& text,
843 DlScalar x,
844 DlScalar y) {
846
847 auto text_frame = text->GetTextFrame();
848
849 // When running with Impeller enabled Skia text blobs are converted to
850 // Impeller text frames in paragraph_skia.cc
851 FML_CHECK(text_frame != nullptr);
852 GetCanvas().DrawTextFrame(text_frame, //
853 impeller::Point{x, y}, //
854 paint_ //
855 );
856}
857
858// |flutter::DlOpReceiver|
859void DlDispatcherBase::drawShadow(const DlPath& path,
860 const flutter::DlColor color,
861 const DlScalar elevation,
862 bool transparent_occluder,
863 DlScalar dpr) {
865
866 Color spot_color = skia_conversions::ToColor(color);
867 spot_color.alpha *= 0.25;
868
869 // Compute the spot color -- ported from SkShadowUtils::ComputeTonalColors.
870 {
871 Scalar max =
872 std::max(std::max(spot_color.red, spot_color.green), spot_color.blue);
873 Scalar min =
874 std::min(std::min(spot_color.red, spot_color.green), spot_color.blue);
875 Scalar luminance = (min + max) * 0.5;
876
877 Scalar alpha_adjust =
878 (2.6f + (-2.66667f + 1.06667f * spot_color.alpha) * spot_color.alpha) *
879 spot_color.alpha;
880 Scalar color_alpha =
881 (3.544762f + (-4.891428f + 2.3466f * luminance) * luminance) *
882 luminance;
883 color_alpha = std::clamp(alpha_adjust * color_alpha, 0.0f, 1.0f);
884
885 Scalar greyscale_alpha =
886 std::clamp(spot_color.alpha * (1 - 0.4f * luminance), 0.0f, 1.0f);
887
888 Scalar color_scale = color_alpha * (1 - greyscale_alpha);
889 Scalar tonal_alpha = color_scale + greyscale_alpha;
890 Scalar unpremul_scale = tonal_alpha != 0 ? color_scale / tonal_alpha : 0;
891 spot_color = Color(unpremul_scale * spot_color.red,
892 unpremul_scale * spot_color.green,
893 unpremul_scale * spot_color.blue, tonal_alpha);
894 }
895
896 Vector3 light_position(0, -1, 1);
897 Scalar occluder_z = dpr * elevation;
898
899 constexpr Scalar kLightRadius = 800 / 600; // Light radius / light height
900
901 Paint paint;
902 paint.style = Paint::Style::kFill;
903 paint.color = spot_color;
904 paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
905 .style = FilterContents::BlurStyle::kNormal,
906 .sigma = Radius{kLightRadius * occluder_z /
907 GetCanvas().GetCurrentTransform().GetScale().y},
908 };
909
910 GetCanvas().Save(1u);
911 GetCanvas().PreConcat(
912 Matrix::MakeTranslation(Vector2(0, -occluder_z * light_position.y)));
913
914 SimplifyOrDrawPath(GetCanvas(), path, paint);
916
917 GetCanvas().Restore();
918}
919
920/// Subclasses
921
922static bool RequiresReadbackForBlends(
923 const ContentContext& renderer,
924 flutter::DlBlendMode max_root_blend_mode) {
925 return !renderer.GetDeviceCapabilities().SupportsFramebufferFetch() &&
926 max_root_blend_mode > Entity::kLastPipelineBlendMode;
927}
928
929CanvasDlDispatcher::CanvasDlDispatcher(ContentContext& renderer,
930 RenderTarget& render_target,
931 bool is_onscreen,
932 bool has_root_backdrop_filter,
933 flutter::DlBlendMode max_root_blend_mode,
934 IRect32 cull_rect)
935 : canvas_(renderer,
936 render_target,
937 is_onscreen,
938 has_root_backdrop_filter ||
939 RequiresReadbackForBlends(renderer, max_root_blend_mode),
940 cull_rect),
941 renderer_(renderer) {}
942
943Canvas& CanvasDlDispatcher::GetCanvas() {
944 return canvas_;
945}
946
947void CanvasDlDispatcher::drawVertices(
948 const std::shared_ptr<flutter::DlVertices>& vertices,
949 flutter::DlBlendMode dl_mode) {
951
952 GetCanvas().DrawVertices(
953 std::make_shared<DlVerticesGeometry>(vertices, renderer_), dl_mode,
954 paint_);
955}
956
957void CanvasDlDispatcher::SetBackdropData(
958 std::unordered_map<int64_t, BackdropData> backdrop,
959 size_t backdrop_count) {
960 GetCanvas().SetBackdropData(std::move(backdrop), backdrop_count);
961}
962
963//// Text Frame Dispatcher
964
965FirstPassDispatcher::FirstPassDispatcher(const ContentContext& renderer,
966 const Matrix& initial_matrix,
967 const Rect cull_rect)
968 : renderer_(renderer), matrix_(initial_matrix) {
969 cull_rect_state_.push_back(cull_rect);
970}
971
972FirstPassDispatcher::~FirstPassDispatcher() {
973 FML_DCHECK(cull_rect_state_.size() == 1);
974}
975
976void FirstPassDispatcher::save() {
977 stack_.emplace_back(matrix_);
978 cull_rect_state_.push_back(cull_rect_state_.back());
979}
980
981void FirstPassDispatcher::saveLayer(const DlRect& bounds,
982 const flutter::SaveLayerOptions options,
983 const flutter::DlImageFilter* backdrop,
984 std::optional<int64_t> backdrop_id) {
985 save();
986
987 backdrop_count_ += (backdrop == nullptr ? 0 : 1);
988 if (backdrop != nullptr && backdrop_id.has_value()) {
989 std::shared_ptr<flutter::DlImageFilter> shared_backdrop =
990 backdrop->shared();
991 std::unordered_map<int64_t, BackdropData>::iterator existing =
992 backdrop_data_.find(backdrop_id.value());
993 if (existing == backdrop_data_.end()) {
994 backdrop_data_[backdrop_id.value()] =
995 BackdropData{.backdrop_count = 1, .last_backdrop = shared_backdrop};
996 } else {
997 BackdropData& data = existing->second;
998 data.backdrop_count++;
999 if (data.all_filters_equal) {
1000 data.all_filters_equal = (*data.last_backdrop == *shared_backdrop);
1001 data.last_backdrop = shared_backdrop;
1002 }
1003 }
1004 }
1005
1006 // This dispatcher does not track enough state to accurately compute
1007 // cull rects with image filters.
1008 auto global_cull_rect = cull_rect_state_.back();
1009 if (has_image_filter_ || global_cull_rect.IsMaximum()) {
1010 cull_rect_state_.back() = Rect::MakeMaximum();
1011 } else {
1012 auto global_save_bounds = bounds.TransformBounds(matrix_);
1013 auto new_cull_rect = global_cull_rect.Intersection(global_save_bounds);
1014 if (new_cull_rect.has_value()) {
1015 cull_rect_state_.back() = new_cull_rect.value();
1016 } else {
1017 cull_rect_state_.back() = Rect::MakeLTRB(0, 0, 0, 0);
1018 }
1019 }
1020}
1021
1022void FirstPassDispatcher::restore() {
1023 matrix_ = stack_.back();
1024 stack_.pop_back();
1025 cull_rect_state_.pop_back();
1026}
1027
1028void FirstPassDispatcher::translate(DlScalar tx, DlScalar ty) {
1029 matrix_ = matrix_.Translate({tx, ty});
1030}
1031
1032void FirstPassDispatcher::scale(DlScalar sx, DlScalar sy) {
1033 matrix_ = matrix_.Scale({sx, sy, 1.0f});
1034}
1035
1036void FirstPassDispatcher::rotate(DlScalar degrees) {
1037 matrix_ = matrix_ * Matrix::MakeRotationZ(Degrees(degrees));
1038}
1039
1040void FirstPassDispatcher::skew(DlScalar sx, DlScalar sy) {
1041 matrix_ = matrix_ * Matrix::MakeSkew(sx, sy);
1042}
1043
1044// clang-format off
1045// 2x3 2D affine subset of a 4x4 transform in row major order
1046void FirstPassDispatcher::transform2DAffine(
1047 DlScalar mxx, DlScalar mxy, DlScalar mxt,
1048 DlScalar myx, DlScalar myy, DlScalar myt) {
1049 matrix_ = matrix_ * Matrix::MakeColumn(
1050 mxx, myx, 0.0f, 0.0f,
1051 mxy, myy, 0.0f, 0.0f,
1052 0.0f, 0.0f, 1.0f, 0.0f,
1053 mxt, myt, 0.0f, 1.0f
1054 );
1055}
1056// clang-format on
1057
1058// clang-format off
1059// full 4x4 transform in row major order
1060void FirstPassDispatcher::transformFullPerspective(
1061 DlScalar mxx, DlScalar mxy, DlScalar mxz, DlScalar mxt,
1062 DlScalar myx, DlScalar myy, DlScalar myz, DlScalar myt,
1063 DlScalar mzx, DlScalar mzy, DlScalar mzz, DlScalar mzt,
1064 DlScalar mwx, DlScalar mwy, DlScalar mwz, DlScalar mwt) {
1065 matrix_ = matrix_ * Matrix::MakeColumn(
1066 mxx, myx, mzx, mwx,
1067 mxy, myy, mzy, mwy,
1068 mxz, myz, mzz, mwz,
1069 mxt, myt, mzt, mwt
1070 );
1071}
1072// clang-format on
1073
1074void FirstPassDispatcher::transformReset() {
1075 matrix_ = Matrix();
1076}
1077
1078void FirstPassDispatcher::drawText(const std::shared_ptr<flutter::DlText>& text,
1079 DlScalar x,
1080 DlScalar y) {
1081 GlyphProperties properties;
1082 auto text_frame = text->GetTextFrame();
1083 if (text_frame == nullptr) {
1084 return;
1085 }
1086
1087 if (paint_.style == Paint::Style::kStroke) {
1088 properties.stroke = paint_.stroke;
1089 }
1090
1091 if (text_frame->HasColor()) {
1092 // Alpha is always applied when rendering, remove it here so
1093 // we do not double-apply the alpha.
1094 properties.color = paint_.color.WithAlpha(1.0);
1095 }
1096 auto scale = TextFrame::RoundScaledFontSize(
1097 (matrix_ * Matrix::MakeTranslation(Point(x, y))).GetMaxBasisLengthXY());
1098
1099 renderer_.GetLazyGlyphAtlas()->AddTextFrame(
1100 text_frame, //
1101 scale, //
1102 Point(x, y), //
1103 matrix_,
1104 (properties.stroke.has_value() || text_frame->HasColor()) //
1105 ? std::optional<GlyphProperties>(properties) //
1106 : std::nullopt //
1107 );
1108}
1109
1110const Rect FirstPassDispatcher::GetCurrentLocalCullingBounds() const {
1111 auto cull_rect = cull_rect_state_.back();
1112 if (!cull_rect.IsEmpty() && !cull_rect.IsMaximum()) {
1113 Matrix inverse = matrix_.Invert();
1114 cull_rect = cull_rect.TransformBounds(inverse);
1115 }
1116 return cull_rect;
1117}
1118
1119void FirstPassDispatcher::drawDisplayList(
1120 const sk_sp<flutter::DisplayList> display_list,
1121 DlScalar opacity) {
1122 [[maybe_unused]] size_t stack_depth = stack_.size();
1123 save();
1124 Paint old_paint = paint_;
1125 paint_ = Paint{};
1126 bool old_has_image_filter = has_image_filter_;
1127 has_image_filter_ = false;
1128
1129 if (matrix_.HasPerspective()) {
1130 display_list->Dispatch(*this);
1131 } else {
1132 Rect local_cull_bounds = GetCurrentLocalCullingBounds();
1133 if (local_cull_bounds.IsMaximum()) {
1134 display_list->Dispatch(*this);
1135 } else if (!local_cull_bounds.IsEmpty()) {
1136 DlIRect cull_rect = DlIRect::RoundOut(local_cull_bounds);
1137 display_list->Dispatch(*this, cull_rect);
1138 }
1139 }
1140
1141 restore();
1142 paint_ = old_paint;
1143 has_image_filter_ = old_has_image_filter;
1144 FML_DCHECK(stack_depth == stack_.size());
1145}
1146
1147// |flutter::DlOpReceiver|
1148void FirstPassDispatcher::setDrawStyle(flutter::DlDrawStyle style) {
1149 paint_.style = ToStyle(style);
1150}
1151
1152// |flutter::DlOpReceiver|
1153void FirstPassDispatcher::setColor(flutter::DlColor color) {
1154 paint_.color = skia_conversions::ToColor(color);
1155}
1156
1157// |flutter::DlOpReceiver|
1158void FirstPassDispatcher::setStrokeWidth(DlScalar width) {
1159 paint_.stroke.width = width;
1160}
1161
1162// |flutter::DlOpReceiver|
1163void FirstPassDispatcher::setStrokeMiter(DlScalar limit) {
1164 paint_.stroke.miter_limit = limit;
1165}
1166
1167// |flutter::DlOpReceiver|
1168void FirstPassDispatcher::setStrokeCap(flutter::DlStrokeCap cap) {
1169 switch (cap) {
1171 paint_.stroke.cap = Cap::kButt;
1172 break;
1174 paint_.stroke.cap = Cap::kRound;
1175 break;
1177 paint_.stroke.cap = Cap::kSquare;
1178 break;
1179 }
1180}
1181
1182// |flutter::DlOpReceiver|
1183void FirstPassDispatcher::setStrokeJoin(flutter::DlStrokeJoin join) {
1184 switch (join) {
1186 paint_.stroke.join = Join::kMiter;
1187 break;
1189 paint_.stroke.join = Join::kRound;
1190 break;
1192 paint_.stroke.join = Join::kBevel;
1193 break;
1194 }
1195}
1196
1197// |flutter::DlOpReceiver|
1198void FirstPassDispatcher::setImageFilter(const flutter::DlImageFilter* filter) {
1199 if (filter == nullptr) {
1200 has_image_filter_ = false;
1201 } else {
1202 has_image_filter_ = true;
1203 }
1204}
1205
1206std::pair<std::unordered_map<int64_t, BackdropData>, size_t>
1207FirstPassDispatcher::TakeBackdropData() {
1208 std::unordered_map<int64_t, BackdropData> temp;
1209 std::swap(temp, backdrop_data_);
1210 return std::make_pair(temp, backdrop_count_);
1211}
1212
1213std::shared_ptr<Texture> DisplayListToTexture(
1214 const sk_sp<flutter::DisplayList>& display_list,
1215 ISize size,
1216 AiksContext& context,
1217 bool reset_host_buffer,
1218 bool generate_mips) {
1219 int mip_count = 1;
1220 if (generate_mips) {
1221 mip_count = size.MipCount();
1222 }
1223 // Do not use the render target cache as the lifecycle of this texture
1224 // will outlive a particular frame.
1225 impeller::RenderTargetAllocator render_target_allocator =
1227 context.GetContext()->GetResourceAllocator());
1229 if (context.GetContext()->GetCapabilities()->SupportsOffscreenMSAA()) {
1230 target = render_target_allocator.CreateOffscreenMSAA(
1231 *context.GetContext(), // context
1232 size, // size
1233 /*mip_count=*/mip_count,
1234 "Picture Snapshot MSAA", // label
1236 kDefaultColorAttachmentConfigMSAA // color_attachment_config
1237 );
1238 } else {
1239 target = render_target_allocator.CreateOffscreen(
1240 *context.GetContext(), // context
1241 size, // size
1242 /*mip_count=*/mip_count,
1243 "Picture Snapshot", // label
1245 kDefaultColorAttachmentConfig // color_attachment_config
1246 );
1247 }
1248 if (!target.IsValid()) {
1249 return nullptr;
1250 }
1251
1252 DlIRect cull_rect = DlIRect::MakeWH(size.width, size.height);
1254 context.GetContentContext(), impeller::Matrix(), Rect::MakeSize(size));
1255 display_list->Dispatch(collector, cull_rect);
1256 impeller::CanvasDlDispatcher impeller_dispatcher(
1257 context.GetContentContext(), //
1258 target, //
1259 /*is_onscreen=*/false, //
1260 display_list->root_has_backdrop_filter(), //
1261 display_list->max_root_blend_mode(), //
1263 );
1264 const auto& [data, count] = collector.TakeBackdropData();
1265 impeller_dispatcher.SetBackdropData(data, count);
1266 context.GetContentContext().GetTextShadowCache().MarkFrameStart();
1267 fml::ScopedCleanupClosure cleanup([&] {
1268 if (reset_host_buffer) {
1269 context.GetContentContext().GetTransientsDataBuffer().Reset();
1270 context.GetContentContext().GetTransientsIndexesBuffer().Reset();
1271 }
1272 context.GetContentContext().GetTextShadowCache().MarkFrameEnd();
1273 context.GetContentContext().GetLazyGlyphAtlas()->ResetTextFrames();
1274 context.GetContext()->DisposeThreadLocalCachedResources();
1275 });
1276
1277 display_list->Dispatch(impeller_dispatcher, cull_rect);
1278 impeller_dispatcher.FinishRecording();
1279
1280 return target.GetRenderTargetTexture();
1281}
1282
1283bool RenderToTarget(ContentContext& context,
1284 RenderTarget render_target,
1285 const sk_sp<flutter::DisplayList>& display_list,
1286 Rect cull_rect,
1287 bool reset_host_buffer,
1288 bool is_onscreen) {
1289 FirstPassDispatcher collector(context, impeller::Matrix(), cull_rect);
1290 display_list->Dispatch(collector, cull_rect);
1291
1292 impeller::CanvasDlDispatcher impeller_dispatcher(
1293 context, //
1294 render_target, //
1295 /*is_onscreen=*/is_onscreen, //
1296 display_list->root_has_backdrop_filter(), //
1297 display_list->max_root_blend_mode(), //
1298 IRect32::RoundOut(cull_rect) //
1299 );
1300 const auto& [data, count] = collector.TakeBackdropData();
1301 impeller_dispatcher.SetBackdropData(data, count);
1302 context.GetTextShadowCache().MarkFrameStart();
1303 fml::ScopedCleanupClosure cleanup([&] {
1304 if (reset_host_buffer) {
1305 context.ResetTransientsBuffers();
1306 }
1307 context.GetTextShadowCache().MarkFrameEnd();
1308 });
1309
1310 display_list->Dispatch(impeller_dispatcher, cull_rect);
1311 impeller_dispatcher.FinishRecording();
1312 context.GetLazyGlyphAtlas()->ResetTextFrames();
1313
1314 return true;
1315}
1316
1317} // namespace impeller
virtual T type() const =0
virtual std::shared_ptr< D > shared() const =0
DlBlurStyle style() const
virtual const DlBlurMaskFilter * asBlur() const
bool content_is_clipped() const
bool renders_with_attributes() const
bool bounds_from_caller() const
bool content_is_unbounded() const
bool can_distribute_opacity() const
Wraps a closure that is invoked in the destructor unless released by the caller.
Definition closure.h:32
a wrapper around the impeller [Allocator] instance that can be used to provide caching of allocated r...
virtual RenderTarget CreateOffscreenMSAA(const Context &context, ISize size, int mip_count, std::string_view label="Offscreen MSAA", RenderTarget::AttachmentConfigMSAA color_attachment_config=RenderTarget::kDefaultColorAttachmentConfigMSAA, std::optional< RenderTarget::AttachmentConfig > stencil_attachment_config=RenderTarget::kDefaultStencilAttachmentConfig, const std::shared_ptr< Texture > &existing_color_msaa_texture=nullptr, const std::shared_ptr< Texture > &existing_color_resolve_texture=nullptr, const std::shared_ptr< Texture > &existing_depth_stencil_texture=nullptr)
virtual RenderTarget CreateOffscreen(const Context &context, ISize size, int mip_count, std::string_view label="Offscreen", RenderTarget::AttachmentConfig color_attachment_config=RenderTarget::kDefaultColorAttachmentConfig, std::optional< RenderTarget::AttachmentConfig > stencil_attachment_config=RenderTarget::kDefaultStencilAttachmentConfig, const std::shared_ptr< Texture > &existing_color_texture=nullptr, const std::shared_ptr< Texture > &existing_depth_stencil_texture=nullptr)
#define AUTO_DEPTH_WATCHER(d)
#define UNIMPLEMENTED
#define AUTO_DEPTH_CHECK()
int32_t x
FlutterVulkanImage * image
uint32_t * target
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_DCHECK(condition)
Definition logging.h:122
std::u16string text
FlTexture * texture
double y
DlStrokeJoin
Definition dl_paint.h:37
@ kMiter
extends to miter limit
@ kBevel
connects outside edges
impeller::RoundRect DlRoundRect
DlStrokeCap
Definition dl_paint.h:28
@ kRound
adds circle
@ kButt
no stroke extension
@ kSquare
adds square
impeller::Rect DlRect
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
DlPointMode
Definition dl_types.h:15
@ kLines
draw each separate pair of points as a line segment
@ kPolygon
draw each pair of overlapping points as a line segment
@ kPoints
draw each point separately
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
DlDrawStyle
Definition dl_paint.h:19
@ kStrokeAndFill
both strokes and fills shapes
@ kStroke
strokes boundary of shapes
@ kFill
fills interior of shapes
impeller::IRect32 DlIRect
@ kNormal
fuzzy inside and outside
@ kOuter
nothing inside, fuzzy outside
@ kInner
fuzzy inside, nothing outside
@ kSolid
solid inside, fuzzy outside
void Reset(SkPathBuilder *path)
Definition path_ops.cc:40
impeller::Point DlPoint
DlSrcRectConstraint
Definition dl_types.h:21
std::shared_ptr< Texture > DisplayListToTexture(const sk_sp< flutter::DisplayList > &display_list, ISize size, AiksContext &context, bool reset_host_buffer, bool generate_mips)
Render the provided display list to a texture with the given size.
Point Vector2
Definition point.h:331
float Scalar
Definition scalar.h:19
static Paint::Style ToStyle(flutter::DlDrawStyle style)
TRect< Scalar > Rect
Definition rect.h:788
static std::optional< const Rect > ToOptRect(const flutter::DlRect *rect)
PointStyle
Definition canvas.h:65
TPoint< Scalar > Point
Definition point.h:327
static Entity::ClipOperation ToClipOperation(flutter::DlClipOp clip_op)
static bool RequiresReadbackForBlends(const ContentContext &renderer, flutter::DlBlendMode max_root_blend_mode)
Subclasses.
static impeller::SamplerDescriptor ToSamplerDescriptor(const flutter::DlFilterMode options)
BlendMode
Definition color.h:58
bool RenderToTarget(ContentContext &context, RenderTarget render_target, const sk_sp< flutter::DisplayList > &display_list, Rect cull_rect, bool reset_host_buffer, bool is_onscreen)
Render the provided display list to the render target.
@ kNearest
Select nearest to the sample point. Most widely supported.
static FilterContents::BlurStyle ToBlurStyle(flutter::DlBlurStyle blur_style)
Definition ref_ptr.h:261
int32_t width
A 4x4 matrix using column-major storage.
Definition matrix.h:37
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition rect.h:472
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
const size_t start
const size_t end
std::vector< Point > points
std::shared_ptr< const fml::Mapping > data

◆ USE_DEPTH_WATCHER

#define USE_DEPTH_WATCHER   true

Definition at line 43 of file dl_dispatcher.cc.