Flutter Impeller
impeller::Canvas Class Reference

#include <canvas.h>

Classes

struct  SaveLayerState
 

Public Types

using BackdropFilterProc = std::function< std::shared_ptr< FilterContents >(FilterInput::Ref, const Matrix &effect_transform, Entity::RenderingMode rendering_mode)>
 

Public Member Functions

 Canvas (ContentContext &renderer, const RenderTarget &render_target, bool is_onscreen, bool requires_readback)
 
 Canvas (ContentContext &renderer, const RenderTarget &render_target, bool is_onscreen, bool requires_readback, Rect cull_rect)
 
 Canvas (ContentContext &renderer, const RenderTarget &render_target, bool is_onscreen, bool requires_readback, IRect cull_rect)
 
 ~Canvas ()=default
 
void SetBackdropData (std::unordered_map< int64_t, BackdropData > backdrop_data, size_t backdrop_count)
 Update the backdrop data used to group together backdrop filters within the same layer. More...
 
std::optional< RectGetLocalCoverageLimit () const
 Return the culling bounds of the current render target, or nullopt if there is no coverage. More...
 
void Save (uint32_t total_content_depth=kMaxDepth)
 
void SaveLayer (const Paint &paint, std::optional< Rect > bounds=std::nullopt, const flutter::DlImageFilter *backdrop_filter=nullptr, ContentBoundsPromise bounds_promise=ContentBoundsPromise::kUnknown, uint32_t total_content_depth=kMaxDepth, bool can_distribute_opacity=false, std::optional< int64_t > backdrop_id=std::nullopt)
 
bool Restore ()
 
size_t GetSaveCount () const
 
void RestoreToCount (size_t count)
 
const MatrixGetCurrentTransform () const
 
void ResetTransform ()
 
void Transform (const Matrix &transform)
 
void Concat (const Matrix &transform)
 
void PreConcat (const Matrix &transform)
 
void Translate (const Vector3 &offset)
 
void Scale (const Vector2 &scale)
 
void Scale (const Vector3 &scale)
 
void Skew (Scalar sx, Scalar sy)
 
void Rotate (Radians radians)
 
void DrawPath (const Path &path, const Paint &paint)
 
void DrawPaint (const Paint &paint)
 
void DrawLine (const Point &p0, const Point &p1, const Paint &paint, bool reuse_depth=false)
 
void DrawRect (const Rect &rect, const Paint &paint)
 
void DrawOval (const Rect &rect, const Paint &paint)
 
void DrawRoundRect (const RoundRect &rect, const Paint &paint)
 
void DrawCircle (const Point &center, Scalar radius, const Paint &paint)
 
void DrawPoints (const Point points[], uint32_t count, Scalar radius, const Paint &paint, PointStyle point_style)
 
void DrawImage (const std::shared_ptr< Texture > &image, Point offset, const Paint &paint, const SamplerDescriptor &sampler={})
 
void DrawImageRect (const std::shared_ptr< Texture > &image, Rect source, Rect dest, const Paint &paint, const SamplerDescriptor &sampler={}, SourceRectConstraint src_rect_constraint=SourceRectConstraint::kFast)
 
void DrawTextFrame (const std::shared_ptr< TextFrame > &text_frame, Point position, const Paint &paint)
 
void DrawVertices (const std::shared_ptr< VerticesGeometry > &vertices, BlendMode blend_mode, const Paint &paint)
 
void DrawAtlas (const std::shared_ptr< AtlasContents > &atlas_contents, const Paint &paint)
 
void ClipGeometry (const Geometry &geometry, Entity::ClipOperation clip_op, bool is_aa=true)
 
void EndReplay ()
 
uint64_t GetOpDepth () const
 
uint64_t GetMaxOpDepth () const
 
bool RequiresReadback () const
 
bool SupportsBlitToOnscreen () const
 
bool EnsureFinalMipmapGeneration () const
 

Static Public Attributes

static constexpr uint32_t kMaxDepth = 1 << 24
 

Detailed Description

Definition at line 115 of file canvas.h.

Member Typedef Documentation

◆ BackdropFilterProc

using impeller::Canvas::BackdropFilterProc = std::function<std::shared_ptr<FilterContents>( FilterInput::Ref, const Matrix& effect_transform, Entity::RenderingMode rendering_mode)>

Definition at line 119 of file canvas.h.

Constructor & Destructor Documentation

◆ Canvas() [1/3]

impeller::Canvas::Canvas ( ContentContext renderer,
const RenderTarget render_target,
bool  is_onscreen,
bool  requires_readback 
)

Definition at line 164 of file canvas.cc.

168  : renderer_(renderer),
169  render_target_(render_target),
170  is_onscreen_(is_onscreen),
171  requires_readback_(requires_readback),
172  clip_coverage_stack_(EntityPassClipStack(
173  Rect::MakeSize(render_target.GetRenderTargetSize()))) {
174  Initialize(std::nullopt);
175  SetupRenderPass();
176 }
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150

◆ Canvas() [2/3]

impeller::Canvas::Canvas ( ContentContext renderer,
const RenderTarget render_target,
bool  is_onscreen,
bool  requires_readback,
Rect  cull_rect 
)
explicit

Definition at line 178 of file canvas.cc.

183  : renderer_(renderer),
184  render_target_(render_target),
185  is_onscreen_(is_onscreen),
186  requires_readback_(requires_readback),
187  clip_coverage_stack_(EntityPassClipStack(
188  Rect::MakeSize(render_target.GetRenderTargetSize()))) {
189  Initialize(cull_rect);
190  SetupRenderPass();
191 }

◆ Canvas() [3/3]

impeller::Canvas::Canvas ( ContentContext renderer,
const RenderTarget render_target,
bool  is_onscreen,
bool  requires_readback,
IRect  cull_rect 
)
explicit

Definition at line 193 of file canvas.cc.

198  : renderer_(renderer),
199  render_target_(render_target),
200  is_onscreen_(is_onscreen),
201  requires_readback_(requires_readback),
202  clip_coverage_stack_(EntityPassClipStack(
203  Rect::MakeSize(render_target.GetRenderTargetSize()))) {
204  Initialize(Rect::MakeLTRB(cull_rect.GetLeft(), cull_rect.GetTop(),
205  cull_rect.GetRight(), cull_rect.GetBottom()));
206  SetupRenderPass();
207 }
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129

References impeller::TRect< T >::GetBottom(), impeller::TRect< T >::GetLeft(), impeller::TRect< T >::GetRight(), impeller::TRect< T >::GetTop(), and impeller::TRect< Scalar >::MakeLTRB().

◆ ~Canvas()

impeller::Canvas::~Canvas ( )
default

Member Function Documentation

◆ ClipGeometry()

void impeller::Canvas::ClipGeometry ( const Geometry geometry,
Entity::ClipOperation  clip_op,
bool  is_aa = true 
)

Definition at line 566 of file canvas.cc.

568  {
569  if (IsSkipping()) {
570  return;
571  }
572 
573  // Ideally the clip depth would be greater than the current rendering
574  // depth because any rendering calls that follow this clip operation will
575  // pre-increment the depth and then be rendering above our clip depth,
576  // but that case will be caught by the CHECK in AddRenderEntity above.
577  // In practice we sometimes have a clip set with no rendering after it
578  // and in such cases the current depth will equal the clip depth.
579  // Eventually the DisplayList should optimize these out, but it is hard
580  // to know if a clip will actually be used in advance of storing it in
581  // the DisplayList buffer.
582  // See https://github.com/flutter/flutter/issues/147021
583  FML_DCHECK(current_depth_ <= transform_stack_.back().clip_depth)
584  << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
585  uint32_t clip_depth = transform_stack_.back().clip_depth;
586 
587  const Matrix clip_transform =
588  Matrix::MakeTranslation(Vector3(-GetGlobalPassPosition())) *
590 
591  std::optional<Rect> clip_coverage = geometry.GetCoverage(clip_transform);
592  if (!clip_coverage.has_value()) {
593  return;
594  }
595 
596  ClipContents clip_contents(
597  clip_coverage.value(),
598  /*is_axis_aligned_rect=*/geometry.IsAxisAlignedRect() &&
599  GetCurrentTransform().IsTranslationScaleOnly());
600  clip_contents.SetClipOperation(clip_op);
601 
602  EntityPassClipStack::ClipStateResult clip_state_result =
603  clip_coverage_stack_.RecordClip(
604  clip_contents, //
605  /*transform=*/clip_transform, //
606  /*global_pass_position=*/GetGlobalPassPosition(), //
607  /*clip_depth=*/clip_depth, //
608  /*clip_height_floor=*/GetClipHeightFloor(), //
609  /*is_aa=*/is_aa);
610 
611  if (clip_state_result.clip_did_change) {
612  // We only need to update the pass scissor if the clip state has changed.
613  SetClipScissor(clip_coverage_stack_.CurrentClipCoverage(),
614  *render_passes_.back().inline_pass_context->GetRenderPass(),
615  GetGlobalPassPosition());
616  }
617 
618  ++transform_stack_.back().clip_height;
619  ++transform_stack_.back().num_clips;
620 
621  if (!clip_state_result.should_render) {
622  return;
623  }
624 
625  // Note: this is a bit of a hack. Its not possible to construct a geometry
626  // result without begninning the render pass. We should refactor the geometry
627  // objects so that they only need a reference to the render pass size and/or
628  // orthographic transform.
629  Entity entity;
630  entity.SetTransform(clip_transform);
631  entity.SetClipDepth(clip_depth);
632 
633  GeometryResult geometry_result = geometry.GetPositionBuffer(
634  renderer_, //
635  entity, //
636  *render_passes_.back().inline_pass_context->GetRenderPass() //
637  );
638  clip_contents.SetGeometry(geometry_result);
639  clip_coverage_stack_.GetLastReplayResult().clip_contents.SetGeometry(
640  geometry_result);
641 
642  clip_contents.Render(
643  renderer_, *render_passes_.back().inline_pass_context->GetRenderPass(),
644  clip_depth);
645 }
const Matrix & GetCurrentTransform() const
Definition: canvas.cc:238
void SetGeometry(GeometryResult geometry)
Set the pre-tessellated clip geometry.
std::optional< Rect > CurrentClipCoverage() const
ClipStateResult RecordClip(const ClipContents &clip_contents, Matrix transform, Point global_pass_position, uint32_t clip_depth, size_t clip_height_floor, bool is_aa)
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95

References impeller::EntityPassClipStack::ReplayResult::clip_contents, impeller::EntityPassClipStack::ClipStateResult::clip_did_change, impeller::EntityPassClipStack::CurrentClipCoverage(), impeller::Geometry::GetCoverage(), GetCurrentTransform(), impeller::EntityPassClipStack::GetLastReplayResult(), impeller::Geometry::GetPositionBuffer(), impeller::Geometry::IsAxisAlignedRect(), impeller::Matrix::MakeTranslation(), impeller::EntityPassClipStack::RecordClip(), impeller::ClipContents::Render(), impeller::Entity::SetClipDepth(), impeller::ClipContents::SetClipOperation(), impeller::ClipContents::SetGeometry(), impeller::Entity::SetTransform(), and impeller::EntityPassClipStack::ClipStateResult::should_render.

Referenced by impeller::DlDispatcherBase::clipOval(), impeller::DlDispatcherBase::clipPath(), impeller::DlDispatcherBase::clipRect(), and impeller::DlDispatcherBase::clipRoundRect().

◆ Concat()

void impeller::Canvas::Concat ( const Matrix transform)

Definition at line 222 of file canvas.cc.

222  {
223  transform_stack_.back().transform = GetCurrentTransform() * transform;
224 }

References GetCurrentTransform(), and transform.

Referenced by Rotate(), Scale(), Skew(), Transform(), and Translate().

◆ DrawAtlas()

void impeller::Canvas::DrawAtlas ( const std::shared_ptr< AtlasContents > &  atlas_contents,
const Paint paint 
)

Definition at line 852 of file canvas.cc.

853  {
854  atlas_contents->SetAlpha(paint.color.alpha);
855 
856  Entity entity;
857  entity.SetTransform(GetCurrentTransform());
858  entity.SetBlendMode(paint.blend_mode);
859  entity.SetContents(paint.WithFilters(atlas_contents));
860 
861  AddRenderEntityToCurrentPass(entity);
862 }

References impeller::Color::alpha, impeller::Paint::blend_mode, impeller::Paint::color, GetCurrentTransform(), impeller::Entity::SetBlendMode(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), and impeller::Paint::WithFilters().

Referenced by impeller::DlDispatcherBase::drawAtlas().

◆ DrawCircle()

void impeller::Canvas::DrawCircle ( const Point center,
Scalar  radius,
const Paint paint 
)

Definition at line 543 of file canvas.cc.

545  {
546  Size half_size(radius, radius);
547  if (AttemptDrawBlurredRRect(
548  Rect::MakeOriginSize(center - half_size, half_size * 2),
549  {radius, radius}, paint)) {
550  return;
551  }
552 
553  Entity entity;
554  entity.SetTransform(GetCurrentTransform());
555  entity.SetBlendMode(paint.blend_mode);
556 
557  if (paint.style == Paint::Style::kStroke) {
558  CircleGeometry geom(center, radius, paint.stroke_width);
559  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
560  } else {
561  CircleGeometry geom(center, radius);
562  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
563  }
564 }
TSize< Scalar > Size
Definition: size.h:171
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:144

References impeller::Paint::blend_mode, GetCurrentTransform(), impeller::Paint::kStroke, impeller::TRect< Scalar >::MakeOriginSize(), impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke_width, and impeller::Paint::style.

Referenced by impeller::DlDispatcherBase::drawCircle(), and DrawOval().

◆ DrawImage()

void impeller::Canvas::DrawImage ( const std::shared_ptr< Texture > &  image,
Point  offset,
const Paint paint,
const SamplerDescriptor sampler = {} 
)

Definition at line 665 of file canvas.cc.

668  {
669  if (!image) {
670  return;
671  }
672 
673  const auto source = Rect::MakeSize(image->GetSize());
674  const auto dest = source.Shift(offset);
675 
676  DrawImageRect(image, source, dest, paint, sampler);
677 }
void DrawImageRect(const std::shared_ptr< Texture > &image, Rect source, Rect dest, const Paint &paint, const SamplerDescriptor &sampler={}, SourceRectConstraint src_rect_constraint=SourceRectConstraint::kFast)
Definition: canvas.cc:679
SeparatedVector2 offset

References DrawImageRect(), impeller::TRect< Scalar >::MakeSize(), and offset.

◆ DrawImageRect()

void impeller::Canvas::DrawImageRect ( const std::shared_ptr< Texture > &  image,
Rect  source,
Rect  dest,
const Paint paint,
const SamplerDescriptor sampler = {},
SourceRectConstraint  src_rect_constraint = SourceRectConstraint::kFast 
)

Definition at line 679 of file canvas.cc.

684  {
685  if (!image || source.IsEmpty() || dest.IsEmpty()) {
686  return;
687  }
688 
689  auto size = image->GetSize();
690 
691  if (size.IsEmpty()) {
692  return;
693  }
694 
695  std::optional<Rect> clipped_source =
696  source.Intersection(Rect::MakeSize(size));
697  if (!clipped_source) {
698  return;
699  }
700  if (*clipped_source != source) {
701  Scalar sx = dest.GetWidth() / source.GetWidth();
702  Scalar sy = dest.GetHeight() / source.GetHeight();
703  Scalar tx = dest.GetLeft() - source.GetLeft() * sx;
704  Scalar ty = dest.GetTop() - source.GetTop() * sy;
705  Matrix src_to_dest = Matrix::MakeTranslateScale({sx, sy, 1}, {tx, ty, 0});
706  dest = clipped_source->TransformBounds(src_to_dest);
707  }
708 
709  auto texture_contents = TextureContents::MakeRect(dest);
710  texture_contents->SetTexture(image);
711  texture_contents->SetSourceRect(*clipped_source);
712  texture_contents->SetStrictSourceRect(src_rect_constraint ==
714  texture_contents->SetSamplerDescriptor(sampler);
715  texture_contents->SetOpacity(paint.color.alpha);
716  texture_contents->SetDeferApplyingOpacity(paint.HasColorFilter());
717 
718  Entity entity;
719  entity.SetBlendMode(paint.blend_mode);
720  entity.SetTransform(GetCurrentTransform());
721 
722  if (!paint.mask_blur_descriptor.has_value()) {
723  entity.SetContents(paint.WithFilters(std::move(texture_contents)));
724  AddRenderEntityToCurrentPass(entity);
725  return;
726  }
727 
728  RectGeometry out_rect(Rect{});
729 
730  entity.SetContents(paint.WithFilters(
731  paint.mask_blur_descriptor->CreateMaskBlur(texture_contents, &out_rect)));
732  AddRenderEntityToCurrentPass(entity);
733 }
static std::shared_ptr< TextureContents > MakeRect(Rect destination)
A common case factory that marks the texture contents as having a destination rectangle....
float Scalar
Definition: scalar.h:18
@ kStrict
Sample only within the source rectangle. May be slower.
TRect< Scalar > Rect
Definition: rect.h:792
static constexpr Matrix MakeTranslateScale(const Vector3 &s, const Vector3 &t)
Definition: matrix.h:113

References impeller::Color::alpha, impeller::Paint::blend_mode, impeller::Paint::color, GetCurrentTransform(), impeller::TRect< T >::GetHeight(), impeller::TRect< T >::GetLeft(), impeller::TRect< T >::GetTop(), impeller::TRect< T >::GetWidth(), impeller::Paint::HasColorFilter(), impeller::TRect< T >::Intersection(), impeller::TRect< T >::IsEmpty(), impeller::kStrict, impeller::TextureContents::MakeRect(), impeller::TRect< Scalar >::MakeSize(), impeller::Matrix::MakeTranslateScale(), impeller::Paint::mask_blur_descriptor, impeller::Entity::SetBlendMode(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), impeller::TRect< T >::TransformBounds(), and impeller::Paint::WithFilters().

Referenced by DrawImage(), impeller::DlDispatcherBase::drawImageRect(), and impeller::NinePatchConverter::DrawNinePatch().

◆ DrawLine()

void impeller::Canvas::DrawLine ( const Point p0,
const Point p1,
const Paint paint,
bool  reuse_depth = false 
)

Definition at line 455 of file canvas.cc.

458  {
459  Entity entity;
460  entity.SetTransform(GetCurrentTransform());
461  entity.SetBlendMode(paint.blend_mode);
462 
463  LineGeometry geom(p0, p1, paint.stroke_width, paint.stroke_cap);
464  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint,
465  /*reuse_depth=*/reuse_depth);
466 }

References impeller::Paint::blend_mode, GetCurrentTransform(), impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke_cap, and impeller::Paint::stroke_width.

Referenced by impeller::DlDispatcherBase::drawLine(), and impeller::DlDispatcherBase::drawPoints().

◆ DrawOval()

void impeller::Canvas::DrawOval ( const Rect rect,
const Paint paint 
)

Definition at line 486 of file canvas.cc.

486  {
487  // TODO(jonahwilliams): This additional condition avoids an assert in the
488  // stroke circle geometry generator. I need to verify the condition that this
489  // assert prevents.
490  if (rect.IsSquare() && (paint.style == Paint::Style::kFill ||
491  (paint.style == Paint::Style::kStroke &&
492  paint.stroke_width < rect.GetWidth()))) {
493  // Circles have slightly less overhead and can do stroking
494  DrawCircle(rect.GetCenter(), rect.GetWidth() * 0.5f, paint);
495  return;
496  }
497 
498  if (paint.style == Paint::Style::kStroke) {
499  // No stroked ellipses yet
500  DrawPath(PathBuilder{}.AddOval(rect).TakePath(), paint);
501  return;
502  }
503 
504  if (AttemptDrawBlurredRRect(rect, rect.GetSize() * 0.5f, paint)) {
505  return;
506  }
507 
508  Entity entity;
509  entity.SetTransform(GetCurrentTransform());
510  entity.SetBlendMode(paint.blend_mode);
511 
512  EllipseGeometry geom(rect);
513  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
514 }
void DrawPath(const Path &path, const Paint &paint)
Definition: canvas.cc:293
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition: canvas.cc:543

References impeller::PathBuilder::AddOval(), impeller::Paint::blend_mode, DrawCircle(), DrawPath(), impeller::TRect< T >::GetCenter(), GetCurrentTransform(), impeller::TRect< T >::GetSize(), impeller::TRect< T >::GetWidth(), impeller::TRect< T >::IsSquare(), impeller::Paint::kFill, impeller::Paint::kStroke, impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke_width, and impeller::Paint::style.

Referenced by impeller::DlDispatcherBase::drawOval(), and impeller::DlDispatcherBase::SimplifyOrDrawPath().

◆ DrawPaint()

void impeller::Canvas::DrawPaint ( const Paint paint)

Definition at line 308 of file canvas.cc.

308  {
309  Entity entity;
310  entity.SetTransform(GetCurrentTransform());
311  entity.SetBlendMode(paint.blend_mode);
312 
313  CoverGeometry geom;
314  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
315 }

References impeller::Paint::blend_mode, GetCurrentTransform(), impeller::Entity::SetBlendMode(), and impeller::Entity::SetTransform().

Referenced by impeller::DlDispatcherBase::drawColor(), and impeller::DlDispatcherBase::drawPaint().

◆ DrawPath()

void impeller::Canvas::DrawPath ( const Path path,
const Paint paint 
)

Definition at line 293 of file canvas.cc.

293  {
294  Entity entity;
295  entity.SetTransform(GetCurrentTransform());
296  entity.SetBlendMode(paint.blend_mode);
297 
298  if (paint.style == Paint::Style::kFill) {
299  FillPathGeometry geom(path);
300  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
301  } else {
302  StrokePathGeometry geom(path, paint.stroke_width, paint.stroke_miter,
303  paint.stroke_cap, paint.stroke_join);
304  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
305  }
306 }

References impeller::Paint::blend_mode, GetCurrentTransform(), impeller::Paint::kFill, impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke_cap, impeller::Paint::stroke_join, impeller::Paint::stroke_miter, impeller::Paint::stroke_width, and impeller::Paint::style.

Referenced by impeller::DlDispatcherBase::drawArc(), impeller::DlDispatcherBase::drawDashedLine(), impeller::DlDispatcherBase::drawDiffRoundRect(), DrawOval(), DrawRect(), DrawRoundRect(), and impeller::DlDispatcherBase::SimplifyOrDrawPath().

◆ DrawPoints()

void impeller::Canvas::DrawPoints ( const Point  points[],
uint32_t  count,
Scalar  radius,
const Paint paint,
PointStyle  point_style 
)

Definition at line 647 of file canvas.cc.

651  {
652  if (radius <= 0) {
653  return;
654  }
655 
656  Entity entity;
657  entity.SetTransform(GetCurrentTransform());
658  entity.SetBlendMode(paint.blend_mode);
659 
660  PointFieldGeometry geom(points, count, radius,
661  /*round=*/point_style == PointStyle::kRound);
662  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
663 }
@ kRound
Points are drawn as squares.

References impeller::Paint::blend_mode, GetCurrentTransform(), impeller::kRound, impeller::Entity::SetBlendMode(), and impeller::Entity::SetTransform().

Referenced by impeller::DlDispatcherBase::drawPoints().

◆ DrawRect()

void impeller::Canvas::DrawRect ( const Rect rect,
const Paint paint 
)

Definition at line 468 of file canvas.cc.

468  {
469  if (paint.style == Paint::Style::kStroke) {
470  DrawPath(PathBuilder{}.AddRect(rect).TakePath(), paint);
471  return;
472  }
473 
474  if (AttemptDrawBlurredRRect(rect, {}, paint)) {
475  return;
476  }
477 
478  Entity entity;
479  entity.SetTransform(GetCurrentTransform());
480  entity.SetBlendMode(paint.blend_mode);
481 
482  RectGeometry geom(rect);
483  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
484 }

References impeller::PathBuilder::AddRect(), impeller::Paint::blend_mode, DrawPath(), GetCurrentTransform(), impeller::Paint::kStroke, impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), and impeller::Paint::style.

Referenced by impeller::DlDispatcherBase::drawRect(), and impeller::DlDispatcherBase::SimplifyOrDrawPath().

◆ DrawRoundRect()

void impeller::Canvas::DrawRoundRect ( const RoundRect rect,
const Paint paint 
)

Definition at line 516 of file canvas.cc.

516  {
517  auto& rect = round_rect.GetBounds();
518  auto& radii = round_rect.GetRadii();
519  if (radii.AreAllCornersSame()) {
520  if (AttemptDrawBlurredRRect(rect, radii.top_left, paint)) {
521  return;
522  }
523 
524  if (paint.style == Paint::Style::kFill) {
525  Entity entity;
526  entity.SetTransform(GetCurrentTransform());
527  entity.SetBlendMode(paint.blend_mode);
528 
529  RoundRectGeometry geom(rect, radii.top_left);
530  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
531  return;
532  }
533  }
534 
535  auto path = PathBuilder{}
536  .SetConvexity(Convexity::kConvex)
537  .AddRoundRect(round_rect)
538  .SetBounds(rect)
539  .TakePath();
540  DrawPath(path, paint);
541 }

References impeller::PathBuilder::AddRoundRect(), impeller::Paint::blend_mode, DrawPath(), impeller::RoundRect::GetBounds(), GetCurrentTransform(), impeller::RoundRect::GetRadii(), impeller::kConvex, impeller::Paint::kFill, impeller::Entity::SetBlendMode(), impeller::PathBuilder::SetBounds(), impeller::PathBuilder::SetConvexity(), impeller::Entity::SetTransform(), impeller::Paint::style, and impeller::PathBuilder::TakePath().

Referenced by impeller::DlDispatcherBase::drawRoundRect(), and impeller::DlDispatcherBase::SimplifyOrDrawPath().

◆ DrawTextFrame()

void impeller::Canvas::DrawTextFrame ( const std::shared_ptr< TextFrame > &  text_frame,
Point  position,
const Paint paint 
)

Definition at line 1346 of file canvas.cc.

1348  {
1349  Entity entity;
1350  entity.SetClipDepth(GetClipHeight());
1351  entity.SetBlendMode(paint.blend_mode);
1352 
1353  auto text_contents = std::make_shared<TextContents>();
1354  text_contents->SetTextFrame(text_frame);
1355  text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
1356  text_contents->SetScale(GetCurrentTransform().GetMaxBasisLengthXY());
1357  text_contents->SetColor(paint.color);
1358  text_contents->SetOffset(position);
1359  text_contents->SetTextProperties(paint.color, //
1360  paint.style == Paint::Style::kStroke, //
1361  paint.stroke_width, //
1362  paint.stroke_cap, //
1363  paint.stroke_join, //
1364  paint.stroke_miter //
1365  );
1366 
1367  entity.SetTransform(GetCurrentTransform() *
1368  Matrix::MakeTranslation(position));
1369 
1370  // TODO(bdero): This mask blur application is a hack. It will always wind up
1371  // doing a gaussian blur that affects the color source itself
1372  // instead of just the mask. The color filter text support
1373  // needs to be reworked in order to interact correctly with
1374  // mask filters.
1375  // https://github.com/flutter/flutter/issues/133297
1376  entity.SetContents(paint.WithFilters(paint.WithMaskBlur(
1377  std::move(text_contents), true, GetCurrentTransform())));
1378 
1379  AddRenderEntityToCurrentPass(entity, false);
1380 }

References impeller::Paint::blend_mode, impeller::Paint::color, GetCurrentTransform(), impeller::Paint::kStroke, impeller::Matrix::MakeTranslation(), impeller::Paint::mask_blur_descriptor, impeller::Entity::SetBlendMode(), impeller::Entity::SetClipDepth(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), impeller::Paint::stroke_cap, impeller::Paint::stroke_join, impeller::Paint::stroke_miter, impeller::Paint::stroke_width, impeller::Paint::style, impeller::Paint::WithFilters(), and impeller::Paint::WithMaskBlur().

Referenced by impeller::DlDispatcherBase::drawTextFrame().

◆ DrawVertices()

void impeller::Canvas::DrawVertices ( const std::shared_ptr< VerticesGeometry > &  vertices,
BlendMode  blend_mode,
const Paint paint 
)

Definition at line 739 of file canvas.cc.

741  {
742  // Override the blend mode with kDestination in order to match the behavior
743  // of Skia's SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER flag, which
744  // is enabled when the Flutter engine builds Skia.
745  if (!paint.color_source) {
746  blend_mode = BlendMode::kDestination;
747  }
748 
749  Entity entity;
750  entity.SetTransform(GetCurrentTransform());
751  entity.SetBlendMode(paint.blend_mode);
752 
753  // If there are no vertex colors.
754  if (UseColorSourceContents(vertices, paint)) {
755  AddRenderEntityWithFiltersToCurrentPass(entity, vertices.get(), paint);
756  return;
757  }
758 
759  // If the blend mode is destination don't bother to bind or create a texture.
760  if (blend_mode == BlendMode::kDestination) {
761  auto contents = std::make_shared<VerticesSimpleBlendContents>();
762  contents->SetBlendMode(blend_mode);
763  contents->SetAlpha(paint.color.alpha);
764  contents->SetGeometry(vertices);
765  entity.SetContents(paint.WithFilters(std::move(contents)));
766  AddRenderEntityToCurrentPass(entity);
767  return;
768  }
769 
770  // If there is a texture, use this directly. Otherwise render the color
771  // source to a texture.
772  if (paint.color_source &&
773  paint.color_source->type() == flutter::DlColorSourceType::kImage) {
774  const flutter::DlImageColorSource* image_color_source =
775  paint.color_source->asImage();
776  FML_DCHECK(image_color_source &&
777  image_color_source->image()->impeller_texture());
778  auto texture = image_color_source->image()->impeller_texture();
779  auto x_tile_mode = static_cast<Entity::TileMode>(
780  image_color_source->horizontal_tile_mode());
781  auto y_tile_mode =
782  static_cast<Entity::TileMode>(image_color_source->vertical_tile_mode());
783  auto sampler_descriptor =
784  skia_conversions::ToSamplerDescriptor(image_color_source->sampling());
785  auto effect_transform = image_color_source->matrix();
786 
787  auto contents = std::make_shared<VerticesSimpleBlendContents>();
788  contents->SetBlendMode(blend_mode);
789  contents->SetAlpha(paint.color.alpha);
790  contents->SetGeometry(vertices);
791  contents->SetEffectTransform(effect_transform);
792  contents->SetTexture(texture);
793  contents->SetTileMode(x_tile_mode, y_tile_mode);
794  contents->SetSamplerDescriptor(sampler_descriptor);
795 
796  entity.SetContents(paint.WithFilters(std::move(contents)));
797  AddRenderEntityToCurrentPass(entity);
798  return;
799  }
800 
801  auto src_paint = paint;
802  src_paint.color = paint.color.WithAlpha(1.0);
803 
804  std::shared_ptr<ColorSourceContents> src_contents =
805  src_paint.CreateContents();
806  src_contents->SetGeometry(vertices.get());
807 
808  // If the color source has an intrinsic size, then we use that to
809  // create the src contents as a simplification. Otherwise we use
810  // the extent of the texture coordinates to determine how large
811  // the src contents should be. If neither has a value we fall back
812  // to using the geometry coverage data.
813  Rect src_coverage;
814  auto size = src_contents->GetColorSourceSize();
815  if (size.has_value()) {
816  src_coverage = Rect::MakeXYWH(0, 0, size->width, size->height);
817  } else {
818  auto cvg = vertices->GetCoverage(Matrix{});
819  FML_CHECK(cvg.has_value());
820  auto texture_coverage = vertices->GetTextureCoordinateCoverge();
821  if (texture_coverage.has_value()) {
822  src_coverage =
823  Rect::MakeOriginSize(texture_coverage->GetOrigin(),
824  texture_coverage->GetSize().Max({1, 1}));
825  } else {
826  src_coverage = cvg.value();
827  }
828  }
829  src_contents = src_paint.CreateContents();
830 
831  clip_geometry_.push_back(Geometry::MakeRect(Rect::Round(src_coverage)));
832  src_contents->SetGeometry(clip_geometry_.back().get());
833 
834  auto contents = std::make_shared<VerticesSimpleBlendContents>();
835  contents->SetBlendMode(blend_mode);
836  contents->SetAlpha(paint.color.alpha);
837  contents->SetGeometry(vertices);
838  contents->SetLazyTextureCoverage(src_coverage);
839  contents->SetLazyTexture([src_contents,
840  src_coverage](const ContentContext& renderer) {
841  // Applying the src coverage as the coverage limit prevents the 1px
842  // coverage pad from adding a border that is picked up by developer
843  // specified UVs.
844  auto snapshot =
845  src_contents->RenderToSnapshot(renderer, {}, Rect::Round(src_coverage));
846  return snapshot.has_value() ? snapshot->texture : nullptr;
847  });
848  entity.SetContents(paint.WithFilters(std::move(contents)));
849  AddRenderEntityToCurrentPass(entity);
850 }
static std::unique_ptr< Geometry > MakeRect(const Rect &rect)
Definition: geometry.cc:82
impeller::SamplerDescriptor ToSamplerDescriptor(const flutter::DlImageSampling options)
Round(const TRect< U > &r)
Definition: rect.h:699
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136

References impeller::Color::alpha, impeller::Paint::blend_mode, impeller::Paint::color, impeller::Paint::color_source, GetCurrentTransform(), impeller::kDestination, impeller::TRect< Scalar >::MakeOriginSize(), impeller::Geometry::MakeRect(), impeller::TRect< Scalar >::MakeXYWH(), impeller::TRect< Scalar >::Round(), impeller::Entity::SetBlendMode(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), impeller::skia_conversions::ToSamplerDescriptor(), impeller::Color::WithAlpha(), and impeller::Paint::WithFilters().

Referenced by impeller::CanvasDlDispatcher::drawVertices(), and impeller::testing::TEST_P().

◆ EndReplay()

void impeller::Canvas::EndReplay ( )

Definition at line 1759 of file canvas.cc.

1759  {
1760  FML_DCHECK(render_passes_.size() == 1u);
1761  render_passes_.back().inline_pass_context->GetRenderPass();
1762  render_passes_.back().inline_pass_context->EndPass(
1763  /*is_onscreen=*/!requires_readback_ && is_onscreen_);
1764  backdrop_data_.clear();
1765 
1766  // If requires_readback_ was true, then we rendered to an offscreen texture
1767  // instead of to the onscreen provided in the render target. Now we need to
1768  // draw or blit the offscreen back to the onscreen.
1769  if (requires_readback_) {
1770  BlitToOnscreen(/*is_onscreen_=*/is_onscreen_);
1771  }
1772  if (!EnsureFinalMipmapGeneration()) {
1773  VALIDATION_LOG << "Failed to generate onscreen mipmaps.";
1774  }
1775  if (!renderer_.GetContext()->FlushCommandBuffers()) {
1776  // Not much we can do.
1777  VALIDATION_LOG << "Failed to submit command buffers";
1778  }
1779  render_passes_.clear();
1780  renderer_.GetRenderTargetCache()->End();
1781  clip_geometry_.clear();
1782 
1783  Reset();
1784  Initialize(initial_cull_rect_);
1785 }
bool EnsureFinalMipmapGeneration() const
Definition: canvas.cc:1741
const std::shared_ptr< RenderTargetAllocator > & GetRenderTargetCache() const
std::shared_ptr< Context > GetContext() const
#define VALIDATION_LOG
Definition: validation.h:91

References VALIDATION_LOG.

Referenced by impeller::CanvasDlDispatcher::FinishRecording(), and impeller::testing::TEST_P().

◆ EnsureFinalMipmapGeneration()

bool impeller::Canvas::EnsureFinalMipmapGeneration ( ) const

For picture snapshots we need addition steps to verify that final mipmaps are generated.

Definition at line 1741 of file canvas.cc.

1741  {
1742  if (!render_target_.GetRenderTargetTexture()->NeedsMipmapGeneration()) {
1743  return true;
1744  }
1745  std::shared_ptr<CommandBuffer> cmd_buffer =
1746  renderer_.GetContext()->CreateCommandBuffer();
1747  if (!cmd_buffer) {
1748  return false;
1749  }
1750  std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
1751  if (!blit_pass) {
1752  return false;
1753  }
1754  blit_pass->GenerateMipmap(render_target_.GetRenderTargetTexture());
1755  blit_pass->EncodeCommands();
1756  return renderer_.GetContext()->EnqueueCommandBuffer(std::move(cmd_buffer));
1757 }
std::shared_ptr< Texture > GetRenderTargetTexture() const

◆ GetCurrentTransform()

const Matrix & impeller::Canvas::GetCurrentTransform ( ) const

◆ GetLocalCoverageLimit()

std::optional< Rect > impeller::Canvas::GetLocalCoverageLimit ( ) const

Return the culling bounds of the current render target, or nullopt if there is no coverage.

Definition at line 935 of file canvas.cc.

935  {
936  if (!clip_coverage_stack_.HasCoverage()) {
937  // The current clip is empty. This means the pass texture won't be
938  // visible, so skip it.
939  return std::nullopt;
940  }
941 
942  auto maybe_current_clip_coverage = clip_coverage_stack_.CurrentClipCoverage();
943  if (!maybe_current_clip_coverage.has_value()) {
944  return std::nullopt;
945  }
946 
947  auto current_clip_coverage = maybe_current_clip_coverage.value();
948 
949  // The maximum coverage of the subpass. Subpasses textures should never
950  // extend outside the parent pass texture or the current clip coverage.
951  std::optional<Rect> maybe_coverage_limit =
952  Rect::MakeOriginSize(GetGlobalPassPosition(),
953  Size(render_passes_.back()
954  .inline_pass_context->GetTexture()
955  ->GetSize()))
956  .Intersection(current_clip_coverage);
957 
958  if (!maybe_coverage_limit.has_value() || maybe_coverage_limit->IsEmpty()) {
959  return std::nullopt;
960  }
961 
962  return maybe_coverage_limit->Intersection(
963  Rect::MakeSize(render_target_.GetRenderTargetSize()));
964 }
ISize GetRenderTargetSize() const
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:532

References impeller::EntityPassClipStack::CurrentClipCoverage(), impeller::RenderTarget::GetRenderTargetSize(), impeller::EntityPassClipStack::HasCoverage(), impeller::TRect< T >::Intersection(), impeller::TRect< Scalar >::MakeOriginSize(), and impeller::TRect< Scalar >::MakeSize().

Referenced by impeller::DlDispatcherBase::drawDisplayList(), and SaveLayer().

◆ GetMaxOpDepth()

uint64_t impeller::Canvas::GetMaxOpDepth ( ) const
inline

Definition at line 244 of file canvas.h.

244 { return transform_stack_.back().clip_depth; }

Referenced by impeller::CanvasDlDispatcher::saveLayer().

◆ GetOpDepth()

uint64_t impeller::Canvas::GetOpDepth ( ) const
inline

Definition at line 242 of file canvas.h.

242 { return current_depth_; }

Referenced by impeller::CanvasDlDispatcher::saveLayer().

◆ GetSaveCount()

size_t impeller::Canvas::GetSaveCount ( ) const

Definition at line 277 of file canvas.cc.

277  {
278  return transform_stack_.size();
279 }

Referenced by impeller::DlDispatcherBase::drawDisplayList(), and RestoreToCount().

◆ PreConcat()

void impeller::Canvas::PreConcat ( const Matrix transform)

Definition at line 226 of file canvas.cc.

226  {
227  transform_stack_.back().transform = transform * GetCurrentTransform();
228 }

References GetCurrentTransform(), and transform.

Referenced by impeller::DlDispatcherBase::drawShadow().

◆ RequiresReadback()

bool impeller::Canvas::RequiresReadback ( ) const
inline

Definition at line 252 of file canvas.h.

252 { return requires_readback_; }

◆ ResetTransform()

void impeller::Canvas::ResetTransform ( )

Definition at line 230 of file canvas.cc.

230  {
231  transform_stack_.back().transform = {};
232 }

Referenced by impeller::DlDispatcherBase::transformReset().

◆ Restore()

bool impeller::Canvas::Restore ( )

Definition at line 1204 of file canvas.cc.

1204  {
1205  FML_DCHECK(transform_stack_.size() > 0);
1206  if (transform_stack_.size() == 1) {
1207  return false;
1208  }
1209 
1210  // This check is important to make sure we didn't exceed the depth
1211  // that the clips were rendered at while rendering any of the
1212  // rendering ops. It is OK for the current depth to equal the
1213  // outgoing clip depth because that means the clipping would have
1214  // been successful up through the last rendering op, but it cannot
1215  // be greater.
1216  // Also, we bump the current rendering depth to the outgoing clip
1217  // depth so that future rendering operations are not clipped by
1218  // any of the pixels set by the expiring clips. It is OK for the
1219  // estimates used to determine the clip depth in save/saveLayer
1220  // to be overly conservative, but we need to jump the depth to
1221  // the clip depth so that the next rendering op will get a
1222  // larger depth (it will pre-increment the current_depth_ value).
1223  FML_DCHECK(current_depth_ <= transform_stack_.back().clip_depth)
1224  << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
1225  current_depth_ = transform_stack_.back().clip_depth;
1226 
1227  if (IsSkipping()) {
1228  transform_stack_.pop_back();
1229  return true;
1230  }
1231 
1232  if (transform_stack_.back().rendering_mode ==
1234  transform_stack_.back().rendering_mode ==
1236  auto lazy_render_pass = std::move(render_passes_.back());
1237  render_passes_.pop_back();
1238  // Force the render pass to be constructed if it never was.
1239  lazy_render_pass.inline_pass_context->GetRenderPass();
1240 
1241  SaveLayerState save_layer_state = save_layer_state_.back();
1242  save_layer_state_.pop_back();
1243  auto global_pass_position = GetGlobalPassPosition();
1244 
1245  std::shared_ptr<Contents> contents = CreateContentsForSubpassTarget(
1246  save_layer_state.paint, //
1247  lazy_render_pass.inline_pass_context->GetTexture(), //
1248  Matrix::MakeTranslation(Vector3{-global_pass_position}) * //
1249  transform_stack_.back().transform //
1250  );
1251 
1252  lazy_render_pass.inline_pass_context->EndPass();
1253 
1254  // Round the subpass texture position for pixel alignment with the parent
1255  // pass render target. By default, we draw subpass textures with nearest
1256  // sampling, so aligning here is important for avoiding visual nearest
1257  // sampling errors caused by limited floating point precision when
1258  // straddling a half pixel boundary.
1259  Point subpass_texture_position;
1260  if (transform_stack_.back().did_round_out) {
1261  // Subpass coverage was rounded out, origin potentially moved "down" by
1262  // as much as a pixel.
1263  subpass_texture_position =
1264  (save_layer_state.coverage.GetOrigin() - global_pass_position)
1265  .Floor();
1266  } else {
1267  // Subpass coverage was truncated. Pick the closest phyiscal pixel.
1268  subpass_texture_position =
1269  (save_layer_state.coverage.GetOrigin() - global_pass_position)
1270  .Round();
1271  }
1272 
1273  Entity element_entity;
1274  element_entity.SetClipDepth(++current_depth_);
1275  element_entity.SetContents(std::move(contents));
1276  element_entity.SetBlendMode(save_layer_state.paint.blend_mode);
1277  element_entity.SetTransform(
1278  Matrix::MakeTranslation(Vector3(subpass_texture_position)));
1279 
1280  if (element_entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
1281  if (renderer_.GetDeviceCapabilities().SupportsFramebufferFetch()) {
1282  ApplyFramebufferBlend(element_entity);
1283  } else {
1284  // End the active pass and flush the buffer before rendering "advanced"
1285  // blends. Advanced blends work by binding the current render target
1286  // texture as an input ("destination"), blending with a second texture
1287  // input ("source"), writing the result to an intermediate texture, and
1288  // finally copying the data from the intermediate texture back to the
1289  // render target texture. And so all of the commands that have written
1290  // to the render target texture so far need to execute before it's bound
1291  // for blending (otherwise the blend pass will end up executing before
1292  // all the previous commands in the active pass).
1293  auto input_texture = FlipBackdrop(GetGlobalPassPosition());
1294  if (!input_texture) {
1295  return false;
1296  }
1297 
1298  FilterInput::Vector inputs = {
1299  FilterInput::Make(input_texture,
1300  element_entity.GetTransform().Invert()),
1301  FilterInput::Make(element_entity.GetContents())};
1302  auto contents = ColorFilterContents::MakeBlend(
1303  element_entity.GetBlendMode(), inputs);
1304  contents->SetCoverageHint(element_entity.GetCoverage());
1305  element_entity.SetContents(std::move(contents));
1306  element_entity.SetBlendMode(BlendMode::kSource);
1307  }
1308  }
1309 
1310  element_entity.Render(
1311  renderer_, //
1312  *render_passes_.back().inline_pass_context->GetRenderPass() //
1313  );
1314  clip_coverage_stack_.PopSubpass();
1315  transform_stack_.pop_back();
1316 
1317  // We don't need to restore clips if a saveLayer was performed, as the clip
1318  // state is per render target, and no more rendering operations will be
1319  // performed as the render target workloaded is completed in the restore.
1320  return true;
1321  }
1322 
1323  size_t num_clips = transform_stack_.back().num_clips;
1324  transform_stack_.pop_back();
1325 
1326  if (num_clips > 0) {
1327  EntityPassClipStack::ClipStateResult clip_state_result =
1328  clip_coverage_stack_.RecordRestore(GetGlobalPassPosition(),
1329  GetClipHeight());
1330 
1331  // Clip restores are never required with depth based clipping.
1332  FML_DCHECK(!clip_state_result.should_render);
1333  if (clip_state_result.clip_did_change) {
1334  // We only need to update the pass scissor if the clip state has changed.
1335  SetClipScissor(
1336  clip_coverage_stack_.CurrentClipCoverage(), //
1337  *render_passes_.back().inline_pass_context->GetRenderPass(), //
1338  GetGlobalPassPosition() //
1339  );
1340  }
1341  }
1342 
1343  return true;
1344 }
virtual bool SupportsFramebufferFetch() const =0
Whether the context backend is able to support pipelines with shaders that read from the framebuffer ...
static std::shared_ptr< ColorFilterContents > MakeBlend(BlendMode blend_mode, FilterInput::Vector inputs, std::optional< Color > foreground_color=std::nullopt)
the [inputs] are expected to be in the order of dst, src.
const Capabilities & GetDeviceCapabilities() const
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:22
ClipStateResult RecordRestore(Point global_pass_position, size_t restore_height)
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
Definition: filter_input.cc:19
std::vector< FilterInput::Ref > Vector
Definition: filter_input.h:33
TPoint< Scalar > Point
Definition: point.h:327

References impeller::Paint::blend_mode, impeller::EntityPassClipStack::ClipStateResult::clip_did_change, impeller::Canvas::SaveLayerState::coverage, impeller::EntityPassClipStack::CurrentClipCoverage(), impeller::Entity::GetBlendMode(), impeller::Entity::GetContents(), impeller::Entity::GetCoverage(), impeller::ContentContext::GetDeviceCapabilities(), impeller::TRect< T >::GetOrigin(), impeller::Entity::GetTransform(), impeller::Matrix::Invert(), impeller::Entity::kLastPipelineBlendMode, impeller::kSource, impeller::Entity::kSubpassAppendSnapshotTransform, impeller::Entity::kSubpassPrependSnapshotTransform, impeller::FilterInput::Make(), impeller::ColorFilterContents::MakeBlend(), impeller::Matrix::MakeTranslation(), impeller::Canvas::SaveLayerState::paint, impeller::EntityPassClipStack::PopSubpass(), impeller::EntityPassClipStack::RecordRestore(), impeller::Entity::Render(), impeller::Entity::SetBlendMode(), impeller::Entity::SetClipDepth(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), impeller::EntityPassClipStack::ClipStateResult::should_render, and impeller::Capabilities::SupportsFramebufferFetch().

Referenced by impeller::DlDispatcherBase::drawShadow(), impeller::DlDispatcherBase::restore(), and RestoreToCount().

◆ RestoreToCount()

void impeller::Canvas::RestoreToCount ( size_t  count)

Definition at line 285 of file canvas.cc.

285  {
286  while (GetSaveCount() > count) {
287  if (!Restore()) {
288  return;
289  }
290  }
291 }
bool Restore()
Definition: canvas.cc:1204
size_t GetSaveCount() const
Definition: canvas.cc:277

References GetSaveCount(), and Restore().

Referenced by impeller::DlDispatcherBase::drawDisplayList().

◆ Rotate()

void impeller::Canvas::Rotate ( Radians  radians)

Definition at line 258 of file canvas.cc.

258  {
259  Concat(Matrix::MakeRotationZ(radians));
260 }
void Concat(const Matrix &transform)
Definition: canvas.cc:222
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:223

References Concat(), and impeller::Matrix::MakeRotationZ().

Referenced by impeller::DlDispatcherBase::rotate().

◆ Save()

void impeller::Canvas::Save ( uint32_t  total_content_depth = kMaxDepth)

Definition at line 918 of file canvas.cc.

918  {
919  if (IsSkipping()) {
920  return SkipUntilMatchingRestore(total_content_depth);
921  }
922 
923  auto entry = CanvasStackEntry{};
924  entry.transform = transform_stack_.back().transform;
925  entry.clip_depth = current_depth_ + total_content_depth;
926  entry.distributed_opacity = transform_stack_.back().distributed_opacity;
927  FML_DCHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
928  << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
929  << " after allocating " << total_content_depth;
930  entry.clip_height = transform_stack_.back().clip_height;
931  entry.rendering_mode = Entity::RenderingMode::kDirect;
932  transform_stack_.push_back(entry);
933 }

References impeller::Entity::kDirect, and impeller::CanvasStackEntry::transform.

Referenced by impeller::DlDispatcherBase::drawDisplayList(), impeller::DlDispatcherBase::drawShadow(), impeller::DlDispatcherBase::save(), and SaveLayer().

◆ SaveLayer()

void impeller::Canvas::SaveLayer ( const Paint paint,
std::optional< Rect bounds = std::nullopt,
const flutter::DlImageFilter *  backdrop_filter = nullptr,
ContentBoundsPromise  bounds_promise = ContentBoundsPromise::kUnknown,
uint32_t  total_content_depth = kMaxDepth,
bool  can_distribute_opacity = false,
std::optional< int64_t >  backdrop_id = std::nullopt 
)

Definition at line 966 of file canvas.cc.

972  {
973  TRACE_EVENT0("flutter", "Canvas::saveLayer");
974  if (IsSkipping()) {
975  return SkipUntilMatchingRestore(total_content_depth);
976  }
977 
978  auto maybe_coverage_limit = GetLocalCoverageLimit();
979  if (!maybe_coverage_limit.has_value()) {
980  return SkipUntilMatchingRestore(total_content_depth);
981  }
982  auto coverage_limit = maybe_coverage_limit.value();
983 
984  if (can_distribute_opacity && !backdrop_filter &&
986  bounds_promise != ContentBoundsPromise::kMayClipContents) {
987  Save(total_content_depth);
988  transform_stack_.back().distributed_opacity *= paint.color.alpha;
989  return;
990  }
991 
992  std::shared_ptr<FilterContents> filter_contents = paint.WithImageFilter(
993  Rect(), transform_stack_.back().transform,
995 
996  std::optional<Rect> maybe_subpass_coverage = ComputeSaveLayerCoverage(
997  bounds.value_or(Rect::MakeMaximum()),
998  transform_stack_.back().transform, //
999  coverage_limit, //
1000  filter_contents, //
1001  /*flood_output_coverage=*/
1002  Entity::IsBlendModeDestructive(paint.blend_mode), //
1003  /*flood_input_coverage=*/!!backdrop_filter ||
1004  (paint.color_filter &&
1005  paint.color_filter->modifies_transparent_black()) //
1006  );
1007 
1008  if (!maybe_subpass_coverage.has_value()) {
1009  return SkipUntilMatchingRestore(total_content_depth);
1010  }
1011 
1012  auto subpass_coverage = maybe_subpass_coverage.value();
1013 
1014  // When an image filter is present, clamp to avoid flicking due to nearest
1015  // sampled image. For other cases, round out to ensure than any geometry is
1016  // not cut off.
1017  //
1018  // See also this bug: https://github.com/flutter/flutter/issues/144213
1019  //
1020  // TODO(jonahwilliams): this could still round out for filters that use decal
1021  // sampling mode.
1023  bool did_round_out = false;
1024  Point coverage_origin_adjustment = Point{0, 0};
1025  if (paint.image_filter) {
1026  subpass_size = ISize(subpass_coverage.GetSize());
1027  } else {
1028  did_round_out = true;
1029  subpass_size =
1030  static_cast<ISize>(IRect::RoundOut(subpass_coverage).GetSize());
1031  // If rounding out, adjust the coverage to account for the subpixel shift.
1032  coverage_origin_adjustment =
1033  Point(subpass_coverage.GetLeftTop().x -
1034  std::floor(subpass_coverage.GetLeftTop().x),
1035  subpass_coverage.GetLeftTop().y -
1036  std::floor(subpass_coverage.GetLeftTop().y));
1037  }
1038  if (subpass_size.IsEmpty()) {
1039  return SkipUntilMatchingRestore(total_content_depth);
1040  }
1041 
1042  // When there are scaling filters present, these contents may exceed the
1043  // maximum texture size. Perform a clamp here, which may cause rendering
1044  // artifacts.
1045  subpass_size = subpass_size.Min(renderer_.GetContext()
1046  ->GetCapabilities()
1047  ->GetMaximumRenderPassAttachmentSize());
1048 
1049  // Backdrop filter state, ignored if there is no BDF.
1050  std::shared_ptr<FilterContents> backdrop_filter_contents;
1051  Point local_position = Point(0, 0);
1052  if (backdrop_filter) {
1053  local_position = subpass_coverage.GetOrigin() - GetGlobalPassPosition();
1054  Canvas::BackdropFilterProc backdrop_filter_proc =
1055  [backdrop_filter = backdrop_filter](
1056  const FilterInput::Ref& input, const Matrix& effect_transform,
1057  Entity::RenderingMode rendering_mode) {
1058  auto filter = WrapInput(backdrop_filter, input);
1059  filter->SetEffectTransform(effect_transform);
1060  filter->SetRenderingMode(rendering_mode);
1061  return filter;
1062  };
1063 
1064  std::shared_ptr<Texture> input_texture;
1065 
1066  // If the backdrop ID is not nullopt and there is more than one usage
1067  // of it in the current scene, cache the backdrop texture and remove it from
1068  // the current entity pass flip.
1069  bool will_cache_backdrop_texture = false;
1070  BackdropData* backdrop_data = nullptr;
1071  // If we've reached this point, there is at least one backdrop filter. But
1072  // potentially more if there is a backdrop id. We may conditionally set this
1073  // to a higher value in the if block below.
1074  size_t backdrop_count = 1;
1075  if (backdrop_id.has_value()) {
1076  std::unordered_map<int64_t, BackdropData>::iterator backdrop_data_it =
1077  backdrop_data_.find(backdrop_id.value());
1078  if (backdrop_data_it != backdrop_data_.end()) {
1079  backdrop_data = &backdrop_data_it->second;
1080  will_cache_backdrop_texture =
1081  backdrop_data_it->second.backdrop_count > 1;
1082  backdrop_count = backdrop_data_it->second.backdrop_count;
1083  }
1084  }
1085 
1086  if (!will_cache_backdrop_texture || !backdrop_data->texture_slot) {
1087  backdrop_count_ -= backdrop_count;
1088 
1089  // The onscreen texture can be flipped to if:
1090  // 1. The device supports framebuffer fetch
1091  // 2. There are no more backdrop filters
1092  // 3. The current render pass is for the onscreen pass.
1093  const bool should_use_onscreen =
1095  backdrop_count_ == 0 && render_passes_.size() == 1u;
1096  input_texture = FlipBackdrop(
1097  GetGlobalPassPosition(), //
1098  /*should_remove_texture=*/will_cache_backdrop_texture, //
1099  /*should_use_onscreen=*/should_use_onscreen //
1100  );
1101  if (!input_texture) {
1102  // Validation failures are logged in FlipBackdrop.
1103  return;
1104  }
1105 
1106  if (will_cache_backdrop_texture) {
1107  backdrop_data->texture_slot = input_texture;
1108  }
1109  } else {
1110  input_texture = backdrop_data->texture_slot;
1111  }
1112 
1113  backdrop_filter_contents = backdrop_filter_proc(
1114  FilterInput::Make(std::move(input_texture)),
1115  transform_stack_.back().transform.Basis(),
1116  // When the subpass has a translation that means the math with
1117  // the snapshot has to be different.
1118  transform_stack_.back().transform.HasTranslation()
1121 
1122  if (will_cache_backdrop_texture) {
1123  FML_DCHECK(backdrop_data);
1124  // If all filters on the shared backdrop layer are equal, process the
1125  // layer once.
1126  if (backdrop_data->all_filters_equal &&
1127  !backdrop_data->shared_filter_snapshot.has_value()) {
1128  // TODO(157110): compute minimum input hint.
1129  backdrop_data->shared_filter_snapshot =
1130  backdrop_filter_contents->RenderToSnapshot(renderer_, {});
1131  }
1132 
1133  std::optional<Snapshot> maybe_snapshot =
1134  backdrop_data->shared_filter_snapshot;
1135  if (maybe_snapshot.has_value()) {
1136  Snapshot snapshot = maybe_snapshot.value();
1137  std::shared_ptr<TextureContents> contents = TextureContents::MakeRect(
1138  subpass_coverage.Shift(-GetGlobalPassPosition()));
1139  auto scaled =
1140  subpass_coverage.TransformBounds(snapshot.transform.Invert());
1141  contents->SetTexture(snapshot.texture);
1142  contents->SetSourceRect(scaled);
1143  contents->SetSamplerDescriptor(snapshot.sampler_descriptor);
1144 
1145  // This backdrop entity sets a depth value as it is written to the newly
1146  // flipped backdrop and not into a new saveLayer.
1147  Entity backdrop_entity;
1148  backdrop_entity.SetContents(std::move(contents));
1149  backdrop_entity.SetClipDepth(++current_depth_);
1150  backdrop_entity.SetBlendMode(paint.blend_mode);
1151 
1152  backdrop_entity.Render(renderer_, GetCurrentRenderPass());
1153  Save(0);
1154  return;
1155  }
1156  }
1157  }
1158 
1159  // When applying a save layer, absorb any pending distributed opacity.
1160  Paint paint_copy = paint;
1161  paint_copy.color.alpha *= transform_stack_.back().distributed_opacity;
1162  transform_stack_.back().distributed_opacity = 1.0;
1163 
1164  render_passes_.push_back(
1165  LazyRenderingConfig(renderer_, //
1166  CreateRenderTarget(renderer_, //
1167  subpass_size, //
1169  )));
1170  save_layer_state_.push_back(SaveLayerState{
1171  paint_copy, subpass_coverage.Shift(-coverage_origin_adjustment)});
1172 
1173  CanvasStackEntry entry;
1174  entry.transform = transform_stack_.back().transform;
1175  entry.clip_depth = current_depth_ + total_content_depth;
1176  FML_DCHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
1177  << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
1178  << " after allocating " << total_content_depth;
1179  entry.clip_height = transform_stack_.back().clip_height;
1181  entry.did_round_out = did_round_out;
1182  transform_stack_.emplace_back(entry);
1183 
1184  // Start non-collapsed subpasses with a fresh clip coverage stack limited by
1185  // the subpass coverage. This is important because image filters applied to
1186  // save layers may transform the subpass texture after it's rendered,
1187  // causing parent clip coverage to get misaligned with the actual area that
1188  // the subpass will affect in the parent pass.
1189  clip_coverage_stack_.PushSubpass(subpass_coverage, GetClipHeight());
1190 
1191  if (!backdrop_filter_contents) {
1192  return;
1193  }
1194 
1195  // Render the backdrop entity.
1196  Entity backdrop_entity;
1197  backdrop_entity.SetContents(std::move(backdrop_filter_contents));
1198  backdrop_entity.SetTransform(
1199  Matrix::MakeTranslation(Vector3(-local_position)));
1200  backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
1201  backdrop_entity.Render(renderer_, GetCurrentRenderPass());
1202 }
std::optional< Rect > GetLocalCoverageLimit() const
Return the culling bounds of the current render target, or nullopt if there is no coverage.
Definition: canvas.cc:935
std::function< std::shared_ptr< FilterContents >(FilterInput::Ref, const Matrix &effect_transform, Entity::RenderingMode rendering_mode)> BackdropFilterProc
Definition: canvas.h:122
void Save(uint32_t total_content_depth=kMaxDepth)
Definition: canvas.cc:918
static bool IsBlendModeDestructive(BlendMode blend_mode)
Returns true if the blend mode is "destructive", meaning that even fully transparent source colors wo...
Definition: entity.cc:128
void PushSubpass(std::optional< Rect > subpass_coverage, size_t clip_height)
std::shared_ptr< FilterInput > Ref
Definition: filter_input.h:32
ISize subpass_size
The output size of the down-sampling pass.
std::shared_ptr< FilterContents > WrapInput(const flutter::DlImageFilter *filter, const FilterInput::Ref &input)
Generate a new FilterContents using this filter's configuration.
Definition: image_filter.cc:17
@ kMayClipContents
The caller claims the bounds are a subset of an estimate of the reasonably tight bounds but likely cl...
std::optional< Rect > ComputeSaveLayerCoverage(const Rect &content_coverage, const Matrix &effect_transform, const Rect &coverage_limit, const std::shared_ptr< FilterContents > &image_filter, bool flood_output_coverage, bool flood_input_coverage)
Compute the coverage of a subpass in the global coordinate space.
ISize64 ISize
Definition: size.h:174
static constexpr Color BlackTransparent()
Definition: color.h:269
static bool CanApplyOpacityPeephole(const Paint &paint)
Whether or not a save layer with the provided paint can perform the opacity peephole optimization.
Definition: paint.h:39
RoundOut(const TRect< U > &r)
Definition: rect.h:683
constexpr static TRect MakeMaximum()
Definition: rect.h:188

References impeller::BackdropData::all_filters_equal, impeller::Color::alpha, impeller::BackdropData::backdrop_count, impeller::Color::BlackTransparent(), impeller::Paint::blend_mode, impeller::Paint::CanApplyOpacityPeephole(), impeller::CanvasStackEntry::clip_depth, impeller::CanvasStackEntry::clip_height, impeller::Paint::color, impeller::Paint::color_filter, impeller::ComputeSaveLayerCoverage(), impeller::CanvasStackEntry::did_round_out, impeller::ContentContext::GetContext(), impeller::ContentContext::GetDeviceCapabilities(), GetLocalCoverageLimit(), impeller::Paint::image_filter, impeller::Matrix::Invert(), impeller::Entity::IsBlendModeDestructive(), impeller::kMayClipContents, impeller::Entity::kSubpassAppendSnapshotTransform, impeller::Entity::kSubpassPrependSnapshotTransform, impeller::FilterInput::Make(), impeller::TRect< Scalar >::MakeMaximum(), impeller::TextureContents::MakeRect(), impeller::Matrix::MakeTranslation(), impeller::EntityPassClipStack::PushSubpass(), impeller::Entity::Render(), impeller::CanvasStackEntry::rendering_mode, impeller::TRect< T >::RoundOut(), impeller::Snapshot::sampler_descriptor, Save(), impeller::Entity::SetBlendMode(), impeller::Entity::SetClipDepth(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), impeller::BackdropData::shared_filter_snapshot, subpass_size, impeller::Capabilities::SupportsFramebufferFetch(), impeller::Snapshot::texture, impeller::BackdropData::texture_slot, impeller::CanvasStackEntry::transform, impeller::Snapshot::transform, impeller::Paint::WithImageFilter(), and impeller::WrapInput().

Referenced by impeller::DlDispatcherBase::drawDisplayList(), and impeller::DlDispatcherBase::saveLayer().

◆ Scale() [1/2]

void impeller::Canvas::Scale ( const Vector2 scale)

Definition at line 246 of file canvas.cc.

246  {
248 }
const Scalar scale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104

References Concat(), impeller::Matrix::MakeScale(), and scale.

Referenced by impeller::DlDispatcherBase::scale().

◆ Scale() [2/2]

void impeller::Canvas::Scale ( const Vector3 scale)

Definition at line 250 of file canvas.cc.

250  {
252 }

References Concat(), impeller::Matrix::MakeScale(), and scale.

◆ SetBackdropData()

void impeller::Canvas::SetBackdropData ( std::unordered_map< int64_t, BackdropData backdrop_data,
size_t  backdrop_count 
)

Update the backdrop data used to group together backdrop filters within the same layer.

Definition at line 1558 of file canvas.cc.

1560  {
1561  backdrop_data_ = std::move(backdrop_data);
1562  backdrop_count_ = backdrop_count;
1563 }

Referenced by impeller::CanvasDlDispatcher::SetBackdropData().

◆ Skew()

void impeller::Canvas::Skew ( Scalar  sx,
Scalar  sy 
)

Definition at line 254 of file canvas.cc.

254  {
255  Concat(Matrix::MakeSkew(sx, sy));
256 }
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:127

References Concat(), and impeller::Matrix::MakeSkew().

Referenced by impeller::DlDispatcherBase::skew().

◆ SupportsBlitToOnscreen()

bool impeller::Canvas::SupportsBlitToOnscreen ( ) const

Definition at line 1684 of file canvas.cc.

1684  {
1685  return renderer_.GetContext()
1686  ->GetCapabilities()
1687  ->SupportsTextureToTextureBlits() &&
1688  renderer_.GetContext()->GetBackendType() !=
1690 }

◆ Transform()

void impeller::Canvas::Transform ( const Matrix transform)

Definition at line 234 of file canvas.cc.

234  {
235  Concat(transform);
236 }

References Concat(), and transform.

Referenced by impeller::DlDispatcherBase::transformFullPerspective(), and impeller::DlDispatcherBase::transformReset().

◆ Translate()

void impeller::Canvas::Translate ( const Vector3 offset)

Definition at line 242 of file canvas.cc.

242  {
244 }

References Concat(), impeller::Matrix::MakeTranslation(), and offset.

Referenced by impeller::DlDispatcherBase::translate().

Member Data Documentation

◆ kMaxDepth

constexpr uint32_t impeller::Canvas::kMaxDepth = 1 << 24
staticconstexpr

Definition at line 117 of file canvas.h.


The documentation for this class was generated from the following files: