Flutter Impeller
impeller::Canvas Class Reference

#include <canvas.h>

Classes

class  PathBlurShape
 
class  RRectBlurShape
 
class  RSuperellipseBlurShape
 
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, IRect32 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 flutter::DlPath &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 DrawDashedLine (const Point &p0, const Point &p1, Scalar on_length, Scalar off_length, const Paint &paint)
 
void DrawRect (const Rect &rect, const Paint &paint)
 
void DrawOval (const Rect &rect, const Paint &paint)
 
void DrawArc (const Arc &arc, const Paint &paint)
 
void DrawRoundRect (const RoundRect &rect, const Paint &paint)
 
void DrawDiffRoundRect (const RoundRect &outer, const RoundRect &inner, const Paint &paint)
 
void DrawRoundSuperellipse (const RoundSuperellipse &rse, 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 118 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 122 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 288 of file canvas.cc.

292  : renderer_(renderer),
293  render_target_(render_target),
294  is_onscreen_(is_onscreen),
295  requires_readback_(requires_readback),
296  clip_coverage_stack_(EntityPassClipStack(
297  Rect::MakeSize(render_target.GetRenderTargetSize()))) {
298  Initialize(std::nullopt);
299  SetupRenderPass();
300 }
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 302 of file canvas.cc.

307  : renderer_(renderer),
308  render_target_(render_target),
309  is_onscreen_(is_onscreen),
310  requires_readback_(requires_readback),
311  clip_coverage_stack_(EntityPassClipStack(
312  Rect::MakeSize(render_target.GetRenderTargetSize()))) {
313  Initialize(cull_rect);
314  SetupRenderPass();
315 }

◆ Canvas() [3/3]

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

Definition at line 317 of file canvas.cc.

322  : renderer_(renderer),
323  render_target_(render_target),
324  is_onscreen_(is_onscreen),
325  requires_readback_(requires_readback),
326  clip_coverage_stack_(EntityPassClipStack(
327  Rect::MakeSize(render_target.GetRenderTargetSize()))) {
328  Initialize(Rect::MakeLTRB(cull_rect.GetLeft(), cull_rect.GetTop(),
329  cull_rect.GetRight(), cull_rect.GetBottom()));
330  SetupRenderPass();
331 }
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 1076 of file canvas.cc.

1078  {
1079  if (IsSkipping()) {
1080  return;
1081  }
1082 
1083  // Ideally the clip depth would be greater than the current rendering
1084  // depth because any rendering calls that follow this clip operation will
1085  // pre-increment the depth and then be rendering above our clip depth,
1086  // but that case will be caught by the CHECK in AddRenderEntity above.
1087  // In practice we sometimes have a clip set with no rendering after it
1088  // and in such cases the current depth will equal the clip depth.
1089  // Eventually the DisplayList should optimize these out, but it is hard
1090  // to know if a clip will actually be used in advance of storing it in
1091  // the DisplayList buffer.
1092  // See https://github.com/flutter/flutter/issues/147021
1093  FML_DCHECK(current_depth_ <= transform_stack_.back().clip_depth)
1094  << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
1095  uint32_t clip_depth = transform_stack_.back().clip_depth;
1096 
1097  const Matrix clip_transform =
1098  Matrix::MakeTranslation(Vector3(-GetGlobalPassPosition())) *
1100 
1101  std::optional<Rect> clip_coverage = geometry.GetCoverage(clip_transform);
1102  if (!clip_coverage.has_value()) {
1103  return;
1104  }
1105 
1106  ClipContents clip_contents(
1107  clip_coverage.value(),
1108  /*is_axis_aligned_rect=*/geometry.IsAxisAlignedRect() &&
1109  GetCurrentTransform().IsTranslationScaleOnly());
1110  clip_contents.SetClipOperation(clip_op);
1111 
1112  EntityPassClipStack::ClipStateResult clip_state_result =
1113  clip_coverage_stack_.RecordClip(
1114  clip_contents, //
1115  /*transform=*/clip_transform, //
1116  /*global_pass_position=*/GetGlobalPassPosition(), //
1117  /*clip_depth=*/clip_depth, //
1118  /*clip_height_floor=*/GetClipHeightFloor(), //
1119  /*is_aa=*/is_aa);
1120 
1121  if (clip_state_result.clip_did_change) {
1122  // We only need to update the pass scissor if the clip state has changed.
1123  SetClipScissor(
1124  clip_coverage_stack_.CurrentClipCoverage(),
1125  *render_passes_.back().GetInlinePassContext()->GetRenderPass(),
1126  GetGlobalPassPosition());
1127  }
1128 
1129  ++transform_stack_.back().clip_height;
1130  ++transform_stack_.back().num_clips;
1131 
1132  if (!clip_state_result.should_render) {
1133  return;
1134  }
1135 
1136  // Note: this is a bit of a hack. Its not possible to construct a geometry
1137  // result without begninning the render pass. We should refactor the geometry
1138  // objects so that they only need a reference to the render pass size and/or
1139  // orthographic transform.
1140  Entity entity;
1141  entity.SetTransform(clip_transform);
1142  entity.SetClipDepth(clip_depth);
1143 
1144  GeometryResult geometry_result = geometry.GetPositionBuffer(
1145  renderer_, //
1146  entity, //
1147  *render_passes_.back().GetInlinePassContext()->GetRenderPass() //
1148  );
1149  clip_contents.SetGeometry(geometry_result);
1150  clip_coverage_stack_.GetLastReplayResult().clip_contents.SetGeometry(
1151  geometry_result);
1152 
1153  clip_contents.Render(
1154  renderer_, *render_passes_.back().GetInlinePassContext()->GetRenderPass(),
1155  clip_depth);
1156 }
const Matrix & GetCurrentTransform() const
Definition: canvas.cc:362
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(), impeller::DlDispatcherBase::clipRoundRect(), and impeller::DlDispatcherBase::clipRoundSuperellipse().

◆ Concat()

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

Definition at line 346 of file canvas.cc.

346  {
347  transform_stack_.back().transform = GetCurrentTransform() * transform;
348 }

References GetCurrentTransform(), and transform.

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

◆ DrawArc()

void impeller::Canvas::DrawArc ( const Arc arc,
const Paint paint 
)

Definition at line 900 of file canvas.cc.

900  {
901  Entity entity;
902  entity.SetTransform(GetCurrentTransform());
903  entity.SetBlendMode(paint.blend_mode);
904 
905  if (paint.style == Paint::Style::kFill) {
906  ArcGeometry geom(arc);
907  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
908  return;
909  }
910 
911  const Rect& oval_bounds = arc.GetOvalBounds();
912  if (paint.stroke.width > oval_bounds.GetSize().MaxDimension()) {
913  // This is a special case for rendering arcs whose stroke width is so large
914  // you are effectively drawing a sector of a circle.
915  // https://github.com/flutter/flutter/issues/158567
916  Arc expanded_arc(oval_bounds.Expand(Size(paint.stroke.width * 0.5f)),
917  arc.GetStart(), arc.GetSweep(), true);
918 
919  ArcGeometry geom(expanded_arc);
920  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
921  return;
922  }
923 
924  // IncludeCenter incurs lots of extra work for stroking an arc, including:
925  // - It introduces segments to/from the center point (not too hard).
926  // - It introduces joins on those segments (a bit more complicated).
927  // - Even if the sweep is >=360 degrees, we still draw the segment to
928  // the center and it basically looks like a pie cut into the complete
929  // boundary circle, as if the slice were cut, but not extracted
930  // (hard to express as a continuous kTriangleStrip).
931  if (!arc.IncludeCenter()) {
932  if (arc.IsFullCircle()) {
933  return DrawOval(oval_bounds, paint);
934  }
935 
936  // Our fast stroking code only works for circular bounds as it assumes
937  // that the inner and outer radii can be scaled along each angular step
938  // of the arc - which is not true for elliptical arcs where the inner
939  // and outer samples are perpendicular to the traveling direction of the
940  // elliptical curve which may not line up with the center of the bounds.
941  if (oval_bounds.IsSquare()) {
942  ArcGeometry geom(arc, paint.stroke);
943  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
944  return;
945  }
946  }
947 
948  ArcStrokeGeometry geom(arc, paint.stroke);
949  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
950 }
void DrawOval(const Rect &rect, const Paint &paint)
Definition: canvas.cc:859
TRect< Scalar > Rect
Definition: rect.h:788
TSize< Scalar > Size
Definition: size.h:159

References impeller::Paint::blend_mode, DrawOval(), impeller::TRect< T >::Expand(), GetCurrentTransform(), impeller::Arc::GetOvalBounds(), impeller::TRect< T >::GetSize(), impeller::Arc::GetStart(), impeller::Arc::GetSweep(), impeller::Arc::IncludeCenter(), impeller::Arc::IsFullCircle(), impeller::TRect< T >::IsSquare(), impeller::Paint::kFill, impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke, impeller::Paint::style, and impeller::StrokeParameters::width.

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

◆ DrawAtlas()

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

Definition at line 1367 of file canvas.cc.

1368  {
1369  atlas_contents->SetAlpha(paint.color.alpha);
1370 
1371  Entity entity;
1372  entity.SetTransform(GetCurrentTransform());
1373  entity.SetBlendMode(paint.blend_mode);
1374  entity.SetContents(paint.WithFilters(atlas_contents));
1375 
1376  AddRenderEntityToCurrentPass(entity);
1377 }

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 1022 of file canvas.cc.

1024  {
1025  if (IsShadowBlurDrawOperation(paint)) {
1026  Rect bounds = Rect::MakeLTRB(center.x - radius, center.y - radius,
1027  center.x + radius, center.y + radius);
1028  RRectBlurShape shape(bounds, radius);
1029  if (AttemptDrawBlur(shape, paint)) {
1030  return;
1031  }
1032  }
1033 
1034  if (renderer_.GetContext()->GetFlags().use_sdfs &&
1035  !paint.mask_blur_descriptor.has_value()) {
1036  const bool is_stroked = paint.style == Paint::Style::kStroke;
1037 
1038  std::optional<CircleGeometry> geometry;
1039  if (is_stroked) {
1040  geometry.emplace(center, radius, paint.stroke.width);
1041  } else {
1042  geometry.emplace(center, radius);
1043  }
1044  geometry->SetAntialiasPadding(1.0f);
1045 
1046  auto contents = UberSDFContents::MakeCircle(
1047  /*color=*/paint.color, /*stroked=*/is_stroked, &geometry.value());
1048 
1049  Entity entity;
1050  entity.SetTransform(GetCurrentTransform());
1051  entity.SetBlendMode(paint.blend_mode);
1052 
1053  const Geometry* geom = contents->GetGeometry();
1054 
1055  AddRenderSDFEntityToCurrentPass(entity, geom, paint, std::move(contents));
1056  return;
1057  }
1058 
1059  if (AttemptDrawAntialiasedCircle(center, radius, paint)) {
1060  return;
1061  }
1062 
1063  Entity entity;
1064  entity.SetTransform(GetCurrentTransform());
1065  entity.SetBlendMode(paint.blend_mode);
1066 
1067  if (paint.style == Paint::Style::kStroke) {
1068  CircleGeometry geom(center, radius, paint.stroke.width);
1069  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1070  } else {
1071  CircleGeometry geom(center, radius);
1072  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1073  }
1074 }
std::shared_ptr< Context > GetContext() const
static std::unique_ptr< UberSDFContents > MakeCircle(Color color, bool stroked, const CircleGeometry *geometry)

References impeller::Paint::blend_mode, impeller::Paint::color, impeller::ContentContext::GetContext(), GetCurrentTransform(), impeller::Paint::kStroke, impeller::UberSDFContents::MakeCircle(), impeller::TRect< Scalar >::MakeLTRB(), impeller::Paint::mask_blur_descriptor, impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke, impeller::Paint::style, impeller::StrokeParameters::width, impeller::TPoint< T >::x, and impeller::TPoint< T >::y.

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

◆ DrawDashedLine()

void impeller::Canvas::DrawDashedLine ( const Point p0,
const Point p1,
Scalar  on_length,
Scalar  off_length,
const Paint paint 
)

Definition at line 790 of file canvas.cc.

794  {
795  // Reasons to defer to regular DrawLine:
796  // - performance for degenerate and "regular line" cases
797  // - length is non-positive - DrawLine will draw appropriate "dot"
798  // - off_length is non-positive - no gaps, DrawLine will draw it solid
799  // - on_length is negative - invalid dashing
800  //
801  // Note that a 0 length "on" dash will draw "dot"s every "off" distance
802  // apart so we proceed with the dashing process in that case.
803  Scalar length = p0.GetDistance(p1);
804  if (length > 0.0f && on_length >= 0.0f && off_length > 0.0f) {
805  Entity entity;
806  entity.SetTransform(GetCurrentTransform());
807  entity.SetBlendMode(paint.blend_mode);
808 
809  StrokeDashedLineGeometry geom(p0, p1, on_length, off_length, paint.stroke);
810  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
811  } else {
812  DrawLine(p0, p1, paint);
813  }
814 }
void DrawLine(const Point &p0, const Point &p1, const Paint &paint, bool reuse_depth=false)
Definition: canvas.cc:767
float Scalar
Definition: scalar.h:19

References impeller::Paint::blend_mode, DrawLine(), GetCurrentTransform(), impeller::TPoint< T >::GetDistance(), p1, impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), and impeller::Paint::stroke.

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

◆ DrawDiffRoundRect()

void impeller::Canvas::DrawDiffRoundRect ( const RoundRect outer,
const RoundRect inner,
const Paint paint 
)

Definition at line 984 of file canvas.cc.

986  {
987  Entity entity;
988  entity.SetTransform(GetCurrentTransform());
989  entity.SetBlendMode(paint.blend_mode);
990 
991  if (paint.style == Paint::Style::kFill) {
992  FillDiffRoundRectGeometry geom(outer, inner);
993  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
994  } else {
995  StrokeDiffRoundRectGeometry geom(outer, inner, paint.stroke);
996  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
997  }
998 }

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

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

◆ DrawImage()

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

Definition at line 1176 of file canvas.cc.

1179  {
1180  if (!image) {
1181  return;
1182  }
1183 
1184  const Rect source = Rect::MakeSize(image->GetSize());
1185  const Rect dest = source.Shift(offset);
1186 
1187  DrawImageRect(image, source, dest, paint, sampler);
1188 }
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:1190
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:602

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

◆ 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 1190 of file canvas.cc.

1195  {
1196  if (!image || source.IsEmpty() || dest.IsEmpty()) {
1197  return;
1198  }
1199 
1200  ISize size = image->GetSize();
1201  if (size.IsEmpty()) {
1202  return;
1203  }
1204 
1205  std::optional<Rect> clipped_source =
1206  source.Intersection(Rect::MakeSize(size));
1207  if (!clipped_source) {
1208  return;
1209  }
1210 
1211  if (AttemptColorFilterOptimization(image, source, dest, paint, sampler,
1212  src_rect_constraint)) {
1213  return;
1214  }
1215 
1216  if (*clipped_source != source) {
1217  Scalar sx = dest.GetWidth() / source.GetWidth();
1218  Scalar sy = dest.GetHeight() / source.GetHeight();
1219  Scalar tx = dest.GetLeft() - source.GetLeft() * sx;
1220  Scalar ty = dest.GetTop() - source.GetTop() * sy;
1221  Matrix src_to_dest = Matrix::MakeTranslateScale({sx, sy, 1}, {tx, ty, 0});
1222  dest = clipped_source->TransformBounds(src_to_dest);
1223  }
1224 
1225  auto texture_contents = TextureContents::MakeRect(dest);
1226  texture_contents->SetTexture(image);
1227  texture_contents->SetSourceRect(*clipped_source);
1228  texture_contents->SetStrictSourceRect(src_rect_constraint ==
1230  texture_contents->SetSamplerDescriptor(sampler);
1231  texture_contents->SetOpacity(paint.color.alpha);
1232  texture_contents->SetDeferApplyingOpacity(paint.HasColorFilter());
1233 
1234  Entity entity;
1235  entity.SetBlendMode(paint.blend_mode);
1236  entity.SetTransform(GetCurrentTransform());
1237 
1238  if (!paint.mask_blur_descriptor.has_value()) {
1239  entity.SetContents(paint.WithFilters(std::move(texture_contents)));
1240  AddRenderEntityToCurrentPass(entity);
1241  return;
1242  }
1243 
1244  FillRectGeometry out_rect(Rect{});
1245 
1246  entity.SetContents(paint.WithFilters(
1247  paint.mask_blur_descriptor->CreateMaskBlur(texture_contents, &out_rect)));
1248  AddRenderEntityToCurrentPass(entity);
1249 }
static std::shared_ptr< TextureContents > MakeRect(Rect destination)
@ kStrict
Sample only within the source rectangle. May be slower.
ISize64 ISize
Definition: size.h:162
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::TSize< 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 767 of file canvas.cc.

770  {
771  Entity entity;
772  entity.SetTransform(GetCurrentTransform());
773  entity.SetBlendMode(paint.blend_mode);
774 
775  auto geometry = std::make_unique<LineGeometry>(p0, p1, paint.stroke);
776 
777  if ((renderer_.GetContext()->GetFlags().antialiased_lines ||
778  renderer_.GetContext()->GetFlags().use_sdfs) &&
779  !paint.color_filter && !paint.invert_colors && !paint.image_filter &&
780  !paint.mask_blur_descriptor.has_value() && !paint.color_source) {
781  auto contents = LineContents::Make(std::move(geometry), paint.color);
782  entity.SetContents(std::move(contents));
783  AddRenderEntityToCurrentPass(entity, reuse_depth);
784  } else {
785  AddRenderEntityWithFiltersToCurrentPass(entity, geometry.get(), paint,
786  /*reuse_depth=*/reuse_depth);
787  }
788 }
static std::unique_ptr< LineContents > Make(std::unique_ptr< LineGeometry > geometry, Color color)

References impeller::Paint::blend_mode, impeller::Paint::color, impeller::Paint::color_filter, impeller::Paint::color_source, impeller::ContentContext::GetContext(), GetCurrentTransform(), impeller::Paint::image_filter, impeller::Paint::invert_colors, impeller::LineContents::Make(), impeller::Paint::mask_blur_descriptor, p1, impeller::Entity::SetBlendMode(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), and impeller::Paint::stroke.

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

◆ DrawOval()

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

Definition at line 859 of file canvas.cc.

859  {
860  // TODO(jonahwilliams): This additional condition avoids an assert in the
861  // stroke circle geometry generator. I need to verify the condition that this
862  // assert prevents.
863  if (rect.IsSquare() && (paint.style == Paint::Style::kFill ||
864  (paint.style == Paint::Style::kStroke &&
865  paint.stroke.width < rect.GetWidth()))) {
866  // Circles have slightly less overhead and can do stroking
867  DrawCircle(rect.GetCenter(), rect.GetWidth() * 0.5f, paint);
868  return;
869  }
870 
871  if (IsShadowBlurDrawOperation(paint)) {
872  if (rect.IsSquare()) {
873  // RRectBlurShape takes the corner radii which are half of the
874  // overall width and height of the DrawOval bounds rect.
875  RRectBlurShape shape(rect, rect.GetWidth() * 0.5f);
876  if (AttemptDrawBlur(shape, paint)) {
877  return;
878  }
879  } else {
880  EllipsePathSource source(rect);
881  if (AttemptDrawBlurredPathSource(source, paint)) {
882  return;
883  }
884  }
885  }
886 
887  Entity entity;
888  entity.SetTransform(GetCurrentTransform());
889  entity.SetBlendMode(paint.blend_mode);
890 
891  if (paint.style == Paint::Style::kStroke) {
892  StrokeEllipseGeometry geom(rect, paint.stroke);
893  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
894  } else {
895  EllipseGeometry geom(rect);
896  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
897  }
898 }
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition: canvas.cc:1022

References impeller::Paint::blend_mode, DrawCircle(), impeller::TRect< T >::GetCenter(), GetCurrentTransform(), impeller::TRect< T >::GetWidth(), impeller::TRect< T >::IsSquare(), impeller::Paint::kFill, impeller::Paint::kStroke, impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke, impeller::Paint::style, and impeller::StrokeParameters::width.

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

◆ DrawPaint()

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

Definition at line 437 of file canvas.cc.

437  {
438  Entity entity;
439  entity.SetTransform(GetCurrentTransform());
440  entity.SetBlendMode(paint.blend_mode);
441 
442  CoverGeometry geom;
443  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
444 }

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 flutter::DlPath &  path,
const Paint paint 
)

Definition at line 417 of file canvas.cc.

417  {
418  if (IsShadowBlurDrawOperation(paint)) {
419  if (AttemptDrawBlurredPathSource(path, paint)) {
420  return;
421  }
422  }
423 
424  Entity entity;
425  entity.SetTransform(GetCurrentTransform());
426  entity.SetBlendMode(paint.blend_mode);
427 
428  if (paint.style == Paint::Style::kFill) {
429  FillPathGeometry geom(path);
430  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
431  } else {
432  StrokePathGeometry geom(path, paint.stroke);
433  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
434  }
435 }

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

Referenced by impeller::DlDispatcherBase::drawPath(), DrawTextFrame(), 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 1158 of file canvas.cc.

1162  {
1163  if (radius <= 0) {
1164  return;
1165  }
1166 
1167  Entity entity;
1168  entity.SetTransform(GetCurrentTransform());
1169  entity.SetBlendMode(paint.blend_mode);
1170 
1171  PointFieldGeometry geom(points, count, radius,
1172  /*round=*/point_style == PointStyle::kRound);
1173  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1174 }
@ kRound
Points are drawn as squares.
std::vector< Point > points

References impeller::Paint::blend_mode, GetCurrentTransform(), impeller::kRound, points, 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 816 of file canvas.cc.

816  {
817  if (IsShadowBlurDrawOperation(paint)) {
818  RRectBlurShape shape(rect, 0.0f);
819  if (AttemptDrawBlur(shape, paint)) {
820  return;
821  }
822  }
823 
824  Entity entity;
825  entity.SetTransform(GetCurrentTransform());
826  entity.SetBlendMode(paint.blend_mode);
827 
828  if (renderer_.GetContext()->GetFlags().use_sdfs &&
829  !paint.mask_blur_descriptor.has_value()) {
830  Scalar expand_size = kAntialiasPadding;
831  if (paint.style == Paint::Style::kStroke) {
833  paint.stroke.width);
834  }
835 
836  FillRectGeometry geometry(rect);
837  geometry.SetAntialiasPadding(expand_size);
838 
839  auto contents = UberSDFContents::MakeRect(
840  /*color=*/paint.color, /*stroke_width=*/paint.stroke.width,
841  /*stroke_join=*/paint.stroke.join,
842  /*stroked=*/paint.style == Paint::Style::kStroke, &geometry);
843 
844  const Geometry* geom = contents->GetGeometry();
845 
846  AddRenderSDFEntityToCurrentPass(entity, geom, paint, std::move(contents));
847  return;
848  }
849 
850  if (paint.style == Paint::Style::kStroke) {
851  StrokeRectGeometry geom(rect, paint.stroke);
852  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
853  } else {
854  FillRectGeometry geom(rect);
855  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
856  }
857 }
static Scalar ComputePixelHalfWidth(const Matrix &transform, Scalar width)
static std::unique_ptr< UberSDFContents > MakeRect(Color color, Scalar stroke_width, Join stroke_join, bool stroked, const FillRectGeometry *geometry)

References impeller::Paint::blend_mode, impeller::Paint::color, impeller::LineGeometry::ComputePixelHalfWidth(), impeller::ContentContext::GetContext(), GetCurrentTransform(), impeller::StrokeParameters::join, impeller::Paint::kStroke, impeller::UberSDFContents::MakeRect(), impeller::Paint::mask_blur_descriptor, impeller::FillRectGeometry::SetAntialiasPadding(), impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke, impeller::Paint::style, and impeller::StrokeParameters::width.

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

◆ DrawRoundRect()

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

Definition at line 952 of file canvas.cc.

952  {
953  if (IsShadowBlurDrawOperation(paint)) {
954  if (AttemptDrawBlurredRRect(round_rect, paint)) {
955  return;
956  }
957  }
958 
959  if (round_rect.GetRadii().AreAllCornersSame() &&
960  paint.style == Paint::Style::kFill) {
961  Entity entity;
962  entity.SetTransform(GetCurrentTransform());
963  entity.SetBlendMode(paint.blend_mode);
964 
965  RoundRectGeometry geom(round_rect.GetBounds(),
966  round_rect.GetRadii().top_left);
967  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
968  return;
969  }
970 
971  Entity entity;
972  entity.SetTransform(GetCurrentTransform());
973  entity.SetBlendMode(paint.blend_mode);
974 
975  if (paint.style == Paint::Style::kFill) {
976  FillRoundRectGeometry geom(round_rect);
977  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
978  } else {
979  StrokeRoundRectGeometry geom(round_rect, paint.stroke);
980  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
981  }
982 }

References impeller::RoundingRadii::AreAllCornersSame(), impeller::Paint::blend_mode, impeller::RoundRect::GetBounds(), GetCurrentTransform(), impeller::RoundRect::GetRadii(), impeller::Paint::kFill, impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke, impeller::Paint::style, and impeller::RoundingRadii::top_left.

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

◆ DrawRoundSuperellipse()

void impeller::Canvas::DrawRoundSuperellipse ( const RoundSuperellipse rse,
const Paint paint 
)

Definition at line 1000 of file canvas.cc.

1001  {
1002  if (IsShadowBlurDrawOperation(paint)) {
1003  if (AttemptDrawBlurredRSuperellipse(round_superellipse, paint)) {
1004  return;
1005  }
1006  }
1007 
1008  Entity entity;
1009  entity.SetTransform(GetCurrentTransform());
1010  entity.SetBlendMode(paint.blend_mode);
1011 
1012  if (paint.style == Paint::Style::kFill) {
1013  RoundSuperellipseGeometry geom(round_superellipse.GetBounds(),
1014  round_superellipse.GetRadii());
1015  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1016  } else {
1017  StrokeRoundSuperellipseGeometry geom(round_superellipse, paint.stroke);
1018  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1019  }
1020 }

References impeller::Paint::blend_mode, impeller::RoundSuperellipse::GetBounds(), GetCurrentTransform(), impeller::RoundSuperellipse::GetRadii(), impeller::Paint::kFill, impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke, and impeller::Paint::style.

Referenced by impeller::DlDispatcherBase::drawRoundSuperellipse(), and impeller::testing::TEST_P().

◆ DrawTextFrame()

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

Definition at line 1919 of file canvas.cc.

1921  {
1923  if (max_scale * text_frame->GetFont().GetMetrics().point_size >
1924  kMaxTextScale) {
1925  fml::StatusOr<flutter::DlPath> path = text_frame->GetPath();
1926  if (path.ok()) {
1927  Save(1);
1928  Concat(Matrix::MakeTranslation(position));
1929  DrawPath(path.value(), paint);
1930  Restore();
1931  return;
1932  }
1933  }
1934 
1935  Entity entity;
1936  entity.SetClipDepth(GetClipHeight());
1937  entity.SetBlendMode(paint.blend_mode);
1938 
1939  auto text_contents = std::make_shared<TextContents>();
1940  text_contents->SetTextFrame(text_frame);
1941  text_contents->SetPosition(position);
1942  text_contents->SetScreenTransform(GetCurrentTransform());
1943  text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
1944  text_contents->SetColor(paint.color);
1945  text_contents->SetTextProperties(paint.color,
1946  paint.style == Paint::Style::kStroke
1947  ? std::optional(paint.stroke)
1948  : std::nullopt);
1949 
1950  entity.SetTransform(GetCurrentTransform());
1951 
1952  if (AttemptBlurredTextOptimization(text_frame, text_contents, entity,
1953  paint)) {
1954  return;
1955  }
1956 
1957  entity.SetContents(paint.WithFilters(std::move(text_contents)));
1958  AddRenderEntityToCurrentPass(entity, false);
1959 }
bool Restore()
Definition: canvas.cc:1727
void Concat(const Matrix &transform)
Definition: canvas.cc:346
void DrawPath(const flutter::DlPath &path, const Paint &paint)
Definition: canvas.cc:417
void Save(uint32_t total_content_depth=kMaxDepth)
Definition: canvas.cc:1435
static constexpr Scalar kMaxTextScale
Definition: canvas.cc:1917
Scalar GetMaxBasisLengthXY() const
Return the maximum scale applied specifically to either the X axis or Y axis unit vectors (the bases)...
Definition: matrix.h:328

References impeller::Paint::blend_mode, impeller::Paint::color, Concat(), DrawPath(), GetCurrentTransform(), impeller::Matrix::GetMaxBasisLengthXY(), impeller::kMaxTextScale, impeller::Paint::kStroke, impeller::Matrix::MakeTranslation(), impeller::Paint::mask_blur_descriptor, Restore(), Save(), impeller::Entity::SetBlendMode(), impeller::Entity::SetClipDepth(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), impeller::Paint::stroke, impeller::Paint::style, and impeller::Paint::WithFilters().

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

◆ DrawVertices()

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

Definition at line 1255 of file canvas.cc.

1257  {
1258  // Override the blend mode with kDestination in order to match the behavior
1259  // of Skia's SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER flag, which
1260  // is enabled when the Flutter engine builds Skia.
1261  if (!paint.color_source) {
1262  blend_mode = BlendMode::kDst;
1263  }
1264 
1265  Entity entity;
1266  entity.SetTransform(GetCurrentTransform());
1267  entity.SetBlendMode(paint.blend_mode);
1268 
1269  // If there are no vertex colors.
1270  if (UseColorSourceContents(vertices, paint)) {
1271  AddRenderEntityWithFiltersToCurrentPass(entity, vertices.get(), paint);
1272  return;
1273  }
1274 
1275  // If the blend mode is destination don't bother to bind or create a texture.
1276  if (blend_mode == BlendMode::kDst) {
1277  auto contents = std::make_shared<VerticesSimpleBlendContents>();
1278  contents->SetBlendMode(blend_mode);
1279  contents->SetAlpha(paint.color.alpha);
1280  contents->SetGeometry(vertices);
1281  entity.SetContents(paint.WithFilters(std::move(contents)));
1282  AddRenderEntityToCurrentPass(entity);
1283  return;
1284  }
1285 
1286  // If there is a texture, use this directly. Otherwise render the color
1287  // source to a texture.
1288  if (paint.color_source &&
1289  paint.color_source->type() == flutter::DlColorSourceType::kImage) {
1290  const flutter::DlImageColorSource* image_color_source =
1291  paint.color_source->asImage();
1292  FML_DCHECK(image_color_source &&
1293  image_color_source->image()->impeller_texture());
1294  auto texture = image_color_source->image()->impeller_texture();
1295  auto x_tile_mode = static_cast<Entity::TileMode>(
1296  image_color_source->horizontal_tile_mode());
1297  auto y_tile_mode =
1298  static_cast<Entity::TileMode>(image_color_source->vertical_tile_mode());
1299  auto sampler_descriptor =
1300  skia_conversions::ToSamplerDescriptor(image_color_source->sampling());
1301  auto effect_transform = image_color_source->matrix();
1302 
1303  auto contents = std::make_shared<VerticesSimpleBlendContents>();
1304  contents->SetBlendMode(blend_mode);
1305  contents->SetAlpha(paint.color.alpha);
1306  contents->SetGeometry(vertices);
1307  contents->SetEffectTransform(effect_transform);
1308  contents->SetTexture(texture);
1309  contents->SetTileMode(x_tile_mode, y_tile_mode);
1310  contents->SetSamplerDescriptor(sampler_descriptor);
1311 
1312  entity.SetContents(paint.WithFilters(std::move(contents)));
1313  AddRenderEntityToCurrentPass(entity);
1314  return;
1315  }
1316 
1317  auto src_paint = paint;
1318  src_paint.color = paint.color.WithAlpha(1.0);
1319 
1320  std::shared_ptr<ColorSourceContents> src_contents =
1321  src_paint.CreateContents(vertices.get());
1322 
1323  // If the color source has an intrinsic size, then we use that to
1324  // create the src contents as a simplification. Otherwise we use
1325  // the extent of the texture coordinates to determine how large
1326  // the src contents should be. If neither has a value we fall back
1327  // to using the geometry coverage data.
1328  Rect src_coverage;
1329  auto size = src_contents->GetColorSourceSize();
1330  if (size.has_value()) {
1331  src_coverage = Rect::MakeXYWH(0, 0, size->width, size->height);
1332  } else {
1333  auto cvg = vertices->GetCoverage(Matrix{});
1334  FML_CHECK(cvg.has_value());
1335  auto texture_coverage = vertices->GetTextureCoordinateCoverage();
1336  if (texture_coverage.has_value()) {
1337  src_coverage =
1338  Rect::MakeOriginSize(texture_coverage->GetOrigin(),
1339  texture_coverage->GetSize().Max({1, 1}));
1340  } else {
1341  // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1342  src_coverage = cvg.value();
1343  }
1344  }
1345  clip_geometry_.push_back(Geometry::MakeRect(Rect::Round(src_coverage)));
1346  src_contents = src_paint.CreateContents(clip_geometry_.back().get());
1347 
1348  auto contents = std::make_shared<VerticesSimpleBlendContents>();
1349  contents->SetBlendMode(blend_mode);
1350  contents->SetAlpha(paint.color.alpha);
1351  contents->SetGeometry(vertices);
1352  contents->SetLazyTextureCoverage(src_coverage);
1353  contents->SetLazyTexture(
1354  [src_contents, src_coverage](
1355  const ContentContext& renderer) -> std::shared_ptr<Texture> {
1356  // Applying the src coverage as the coverage limit prevents the 1px
1357  // coverage pad from adding a border that is picked up by developer
1358  // specified UVs.
1359  auto snapshot = src_contents->RenderToSnapshot(
1360  renderer, {}, {.coverage_limit = Rect::Round(src_coverage)});
1361  return snapshot.has_value() ? snapshot->texture : nullptr;
1362  });
1363  entity.SetContents(paint.WithFilters(std::move(contents)));
1364  AddRenderEntityToCurrentPass(entity);
1365 }
static std::unique_ptr< Geometry > MakeRect(const Rect &rect)
Definition: geometry.cc:83
impeller::SamplerDescriptor ToSamplerDescriptor(const flutter::DlImageSampling options)
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:144
Round(const TRect< U > &r)
Definition: rect.h:695
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::kDst, 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 2377 of file canvas.cc.

2377  {
2378  FML_DCHECK(render_passes_.size() == 1u);
2379  render_passes_.back().GetInlinePassContext()->GetRenderPass();
2380  render_passes_.back().GetInlinePassContext()->EndPass(
2381  /*is_onscreen=*/!requires_readback_ && is_onscreen_);
2382  backdrop_data_.clear();
2383 
2384  // If requires_readback_ was true, then we rendered to an offscreen texture
2385  // instead of to the onscreen provided in the render target. Now we need to
2386  // draw or blit the offscreen back to the onscreen.
2387  if (requires_readback_) {
2388  BlitToOnscreen(/*is_onscreen_=*/is_onscreen_);
2389  }
2390  if (!EnsureFinalMipmapGeneration()) {
2391  VALIDATION_LOG << "Failed to generate onscreen mipmaps.";
2392  }
2393  if (!renderer_.GetContext()->FlushCommandBuffers()) {
2394  // Not much we can do.
2395  VALIDATION_LOG << "Failed to submit command buffers";
2396  }
2397  render_passes_.clear();
2398  renderer_.GetRenderTargetCache()->End();
2399  clip_geometry_.clear();
2400 
2401  Reset();
2402  Initialize(initial_cull_rect_);
2403 }
bool EnsureFinalMipmapGeneration() const
Definition: canvas.cc:2359
const std::shared_ptr< RenderTargetAllocator > & GetRenderTargetCache() 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 2359 of file canvas.cc.

2359  {
2360  if (!render_target_.GetRenderTargetTexture()->NeedsMipmapGeneration()) {
2361  return true;
2362  }
2363  std::shared_ptr<CommandBuffer> cmd_buffer =
2364  renderer_.GetContext()->CreateCommandBuffer();
2365  if (!cmd_buffer) {
2366  return false;
2367  }
2368  std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
2369  if (!blit_pass) {
2370  return false;
2371  }
2372  blit_pass->GenerateMipmap(render_target_.GetRenderTargetTexture());
2373  blit_pass->EncodeCommands();
2374  return renderer_.GetContext()->EnqueueCommandBuffer(std::move(cmd_buffer));
2375 }
std::shared_ptr< Texture > GetRenderTargetTexture() const

◆ GetCurrentTransform()

◆ 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 1452 of file canvas.cc.

1452  {
1453  if (!clip_coverage_stack_.HasCoverage()) {
1454  // The current clip is empty. This means the pass texture won't be
1455  // visible, so skip it.
1456  return std::nullopt;
1457  }
1458 
1459  std::optional<Rect> maybe_current_clip_coverage =
1460  clip_coverage_stack_.CurrentClipCoverage();
1461  if (!maybe_current_clip_coverage.has_value()) {
1462  return std::nullopt;
1463  }
1464 
1465  Rect current_clip_coverage = maybe_current_clip_coverage.value();
1466 
1467  FML_CHECK(!render_passes_.empty());
1468  const LazyRenderingConfig& back_render_pass = render_passes_.back();
1469  std::shared_ptr<Texture> back_texture =
1470  back_render_pass.GetInlinePassContext()->GetTexture();
1471  FML_CHECK(back_texture) << "Context is valid:"
1472  << back_render_pass.GetInlinePassContext()->IsValid();
1473 
1474  // The maximum coverage of the subpass. Subpasses textures should never
1475  // extend outside the parent pass texture or the current clip coverage.
1476  std::optional<Rect> maybe_coverage_limit =
1477  Rect::MakeOriginSize(GetGlobalPassPosition(),
1478  Size(back_texture->GetSize()))
1479  .Intersection(current_clip_coverage);
1480 
1481  if (!maybe_coverage_limit.has_value() || maybe_coverage_limit->IsEmpty()) {
1482  return std::nullopt;
1483  }
1484 
1485  return maybe_coverage_limit->Intersection(
1486  Rect::MakeSize(render_target_.GetRenderTargetSize()));
1487 }
ISize GetRenderTargetSize() const
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:528

References impeller::EntityPassClipStack::CurrentClipCoverage(), impeller::LazyRenderingConfig::GetInlinePassContext(), impeller::RenderTarget::GetRenderTargetSize(), impeller::InlinePassContext::GetTexture(), impeller::EntityPassClipStack::HasCoverage(), impeller::TRect< T >::Intersection(), impeller::InlinePassContext::IsValid(), 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 261 of file canvas.h.

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

◆ GetOpDepth()

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

Definition at line 259 of file canvas.h.

259 { return current_depth_; }

◆ GetSaveCount()

size_t impeller::Canvas::GetSaveCount ( ) const

Definition at line 401 of file canvas.cc.

401  {
402  return transform_stack_.size();
403 }

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

◆ PreConcat()

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

Definition at line 350 of file canvas.cc.

350  {
351  transform_stack_.back().transform = transform * GetCurrentTransform();
352 }

References GetCurrentTransform(), and transform.

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

◆ RequiresReadback()

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

Definition at line 269 of file canvas.h.

269 { return requires_readback_; }

◆ ResetTransform()

void impeller::Canvas::ResetTransform ( )

Definition at line 354 of file canvas.cc.

354  {
355  transform_stack_.back().transform = {};
356 }

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

◆ Restore()

bool impeller::Canvas::Restore ( )

Definition at line 1727 of file canvas.cc.

1727  {
1728  FML_DCHECK(transform_stack_.size() > 0);
1729  if (transform_stack_.size() == 1) {
1730  return false;
1731  }
1732 
1733  // This check is important to make sure we didn't exceed the depth
1734  // that the clips were rendered at while rendering any of the
1735  // rendering ops. It is OK for the current depth to equal the
1736  // outgoing clip depth because that means the clipping would have
1737  // been successful up through the last rendering op, but it cannot
1738  // be greater.
1739  // Also, we bump the current rendering depth to the outgoing clip
1740  // depth so that future rendering operations are not clipped by
1741  // any of the pixels set by the expiring clips. It is OK for the
1742  // estimates used to determine the clip depth in save/saveLayer
1743  // to be overly conservative, but we need to jump the depth to
1744  // the clip depth so that the next rendering op will get a
1745  // larger depth (it will pre-increment the current_depth_ value).
1746  FML_DCHECK(current_depth_ <= transform_stack_.back().clip_depth)
1747  << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
1748  current_depth_ = transform_stack_.back().clip_depth;
1749 
1750  if (IsSkipping()) {
1751  transform_stack_.pop_back();
1752  return true;
1753  }
1754 
1755  if (transform_stack_.back().rendering_mode ==
1757  transform_stack_.back().rendering_mode ==
1759  auto lazy_render_pass = std::move(render_passes_.back());
1760  render_passes_.pop_back();
1761  // Force the render pass to be constructed if it never was.
1762  lazy_render_pass.GetInlinePassContext()->GetRenderPass();
1763 
1764  SaveLayerState save_layer_state = save_layer_state_.back();
1765  save_layer_state_.pop_back();
1766  auto global_pass_position = GetGlobalPassPosition();
1767 
1768  std::shared_ptr<Contents> contents = CreateContentsForSubpassTarget(
1769  save_layer_state.paint, //
1770  lazy_render_pass.GetInlinePassContext()->GetTexture(), //
1771  Matrix::MakeTranslation(Vector3{-global_pass_position}) * //
1772  transform_stack_.back().transform //
1773  );
1774 
1775  lazy_render_pass.GetInlinePassContext()->EndPass();
1776 
1777  // Round the subpass texture position for pixel alignment with the parent
1778  // pass render target. By default, we draw subpass textures with nearest
1779  // sampling, so aligning here is important for avoiding visual nearest
1780  // sampling errors caused by limited floating point precision when
1781  // straddling a half pixel boundary.
1782  Point subpass_texture_position;
1783  if (transform_stack_.back().did_round_out) {
1784  // Subpass coverage was rounded out, origin potentially moved "down" by
1785  // as much as a pixel.
1786  subpass_texture_position =
1787  (save_layer_state.coverage.GetOrigin() - global_pass_position)
1788  .Floor();
1789  } else {
1790  // Subpass coverage was truncated. Pick the closest phyiscal pixel.
1791  subpass_texture_position =
1792  (save_layer_state.coverage.GetOrigin() - global_pass_position)
1793  .Round();
1794  }
1795 
1796  Entity element_entity;
1797  element_entity.SetClipDepth(++current_depth_);
1798  element_entity.SetContents(std::move(contents));
1799  element_entity.SetBlendMode(save_layer_state.paint.blend_mode);
1800  element_entity.SetTransform(
1801  Matrix::MakeTranslation(Vector3(subpass_texture_position)));
1802 
1803  if (element_entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
1804  if (renderer_.GetDeviceCapabilities().SupportsFramebufferFetch()) {
1805  ApplyFramebufferBlend(element_entity);
1806  } else {
1807  // End the active pass and flush the buffer before rendering "advanced"
1808  // blends. Advanced blends work by binding the current render target
1809  // texture as an input ("destination"), blending with a second texture
1810  // input ("source"), writing the result to an intermediate texture, and
1811  // finally copying the data from the intermediate texture back to the
1812  // render target texture. And so all of the commands that have written
1813  // to the render target texture so far need to execute before it's bound
1814  // for blending (otherwise the blend pass will end up executing before
1815  // all the previous commands in the active pass).
1816  auto input_texture = FlipBackdrop(GetGlobalPassPosition());
1817  if (!input_texture) {
1818  return false;
1819  }
1820 
1821  FilterInput::Vector inputs = {
1822  FilterInput::Make(input_texture,
1823  element_entity.GetTransform().Invert()),
1824  FilterInput::Make(element_entity.GetContents())};
1825  auto contents = ColorFilterContents::MakeBlend(
1826  element_entity.GetBlendMode(), inputs);
1827  contents->SetCoverageHint(element_entity.GetCoverage());
1828  element_entity.SetContents(std::move(contents));
1829  element_entity.SetBlendMode(BlendMode::kSrc);
1830  }
1831  }
1832 
1833  element_entity.Render(
1834  renderer_, //
1835  *render_passes_.back().GetInlinePassContext()->GetRenderPass() //
1836  );
1837  clip_coverage_stack_.PopSubpass();
1838  transform_stack_.pop_back();
1839 
1840  // We don't need to restore clips if a saveLayer was performed, as the clip
1841  // state is per render target, and no more rendering operations will be
1842  // performed as the render target workloaded is completed in the restore.
1843  return true;
1844  }
1845 
1846  size_t num_clips = transform_stack_.back().num_clips;
1847  transform_stack_.pop_back();
1848 
1849  if (num_clips > 0) {
1850  EntityPassClipStack::ClipStateResult clip_state_result =
1851  clip_coverage_stack_.RecordRestore(GetGlobalPassPosition(),
1852  GetClipHeight());
1853 
1854  // Clip restores are never required with depth based clipping.
1855  FML_DCHECK(!clip_state_result.should_render);
1856  if (clip_state_result.clip_did_change) {
1857  // We only need to update the pass scissor if the clip state has changed.
1858  SetClipScissor(
1859  clip_coverage_stack_.CurrentClipCoverage(), //
1860  *render_passes_.back().GetInlinePassContext()->GetRenderPass(), //
1861  GetGlobalPassPosition() //
1862  );
1863  }
1864  }
1865 
1866  return true;
1867 }
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:28
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:426

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::kSrc, 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(), DrawTextFrame(), impeller::DlDispatcherBase::restore(), and RestoreToCount().

◆ RestoreToCount()

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

Definition at line 409 of file canvas.cc.

409  {
410  while (GetSaveCount() > count) {
411  if (!Restore()) {
412  return;
413  }
414  }
415 }
size_t GetSaveCount() const
Definition: canvas.cc:401

References GetSaveCount(), and Restore().

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

◆ Rotate()

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

Definition at line 382 of file canvas.cc.

382  {
383  Concat(Matrix::MakeRotationZ(radians));
384 }
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 1435 of file canvas.cc.

1435  {
1436  if (IsSkipping()) {
1437  return SkipUntilMatchingRestore(total_content_depth);
1438  }
1439 
1440  auto entry = CanvasStackEntry{};
1441  entry.transform = transform_stack_.back().transform;
1442  entry.clip_depth = current_depth_ + total_content_depth;
1443  entry.distributed_opacity = transform_stack_.back().distributed_opacity;
1444  FML_DCHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
1445  << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
1446  << " after allocating " << total_content_depth;
1447  entry.clip_height = transform_stack_.back().clip_height;
1448  entry.rendering_mode = Entity::RenderingMode::kDirect;
1449  transform_stack_.push_back(entry);
1450 }

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

Referenced by impeller::DlDispatcherBase::drawDisplayList(), impeller::DlDispatcherBase::drawShadow(), DrawTextFrame(), 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 1489 of file canvas.cc.

1495  {
1496  TRACE_EVENT0("flutter", "Canvas::saveLayer");
1497  if (IsSkipping()) {
1498  return SkipUntilMatchingRestore(total_content_depth);
1499  }
1500 
1501  auto maybe_coverage_limit = GetLocalCoverageLimit();
1502  if (!maybe_coverage_limit.has_value()) {
1503  return SkipUntilMatchingRestore(total_content_depth);
1504  }
1505  auto coverage_limit = maybe_coverage_limit.value();
1506 
1507  if (can_distribute_opacity && !backdrop_filter &&
1509  bounds_promise != ContentBoundsPromise::kMayClipContents) {
1510  Save(total_content_depth);
1511  transform_stack_.back().distributed_opacity *= paint.color.alpha;
1512  return;
1513  }
1514 
1515  std::shared_ptr<FilterContents> filter_contents = paint.WithImageFilter(
1516  Rect(), transform_stack_.back().transform,
1518 
1519  std::optional<Rect> maybe_subpass_coverage = ComputeSaveLayerCoverage(
1520  bounds.value_or(Rect::MakeMaximum()),
1521  transform_stack_.back().transform, //
1522  coverage_limit, //
1523  filter_contents, //
1524  /*flood_output_coverage=*/
1525  Entity::IsBlendModeDestructive(paint.blend_mode), //
1526  /*flood_input_coverage=*/!!backdrop_filter ||
1527  (paint.color_filter &&
1528  paint.color_filter->modifies_transparent_black()) //
1529  );
1530 
1531  if (!maybe_subpass_coverage.has_value()) {
1532  return SkipUntilMatchingRestore(total_content_depth);
1533  }
1534 
1535  auto subpass_coverage = maybe_subpass_coverage.value();
1536 
1537  // When an image filter is present, clamp to avoid flicking due to nearest
1538  // sampled image. For other cases, round out to ensure than any geometry is
1539  // not cut off.
1540  //
1541  // See also this bug: https://github.com/flutter/flutter/issues/144213
1542  //
1543  // TODO(jonahwilliams): this could still round out for filters that use decal
1544  // sampling mode.
1546  bool did_round_out = false;
1547  Point coverage_origin_adjustment = Point{0, 0};
1548  if (paint.image_filter) {
1549  subpass_size = ISize(subpass_coverage.GetSize());
1550  } else {
1551  did_round_out = true;
1552  subpass_size =
1553  static_cast<ISize>(IRect::RoundOut(subpass_coverage).GetSize());
1554  // If rounding out, adjust the coverage to account for the subpixel shift.
1555  coverage_origin_adjustment =
1556  Point(subpass_coverage.GetLeftTop().x -
1557  std::floor(subpass_coverage.GetLeftTop().x),
1558  subpass_coverage.GetLeftTop().y -
1559  std::floor(subpass_coverage.GetLeftTop().y));
1560  }
1561  if (subpass_size.IsEmpty()) {
1562  return SkipUntilMatchingRestore(total_content_depth);
1563  }
1564 
1565  // When there are scaling filters present, these contents may exceed the
1566  // maximum texture size. Perform a clamp here, which may cause rendering
1567  // artifacts.
1568  subpass_size = subpass_size.Min(renderer_.GetContext()
1569  ->GetCapabilities()
1570  ->GetMaximumRenderPassAttachmentSize());
1571 
1572  // Backdrop filter state, ignored if there is no BDF.
1573  std::shared_ptr<FilterContents> backdrop_filter_contents;
1574  Point local_position = Point(0, 0);
1575  if (backdrop_filter) {
1576  local_position = subpass_coverage.GetOrigin() - GetGlobalPassPosition();
1577  Canvas::BackdropFilterProc backdrop_filter_proc =
1578  [backdrop_filter = backdrop_filter](
1579  const FilterInput::Ref& input, const Matrix& effect_transform,
1580  Entity::RenderingMode rendering_mode) {
1581  auto filter = WrapInput(backdrop_filter, input);
1582  filter->SetEffectTransform(effect_transform);
1583  filter->SetRenderingMode(rendering_mode);
1584  return filter;
1585  };
1586 
1587  std::shared_ptr<Texture> input_texture;
1588 
1589  // If the backdrop ID is not nullopt and there is more than one usage
1590  // of it in the current scene, cache the backdrop texture and remove it from
1591  // the current entity pass flip.
1592  bool will_cache_backdrop_texture = false;
1593  BackdropData* backdrop_data = nullptr;
1594  // If we've reached this point, there is at least one backdrop filter. But
1595  // potentially more if there is a backdrop id. We may conditionally set this
1596  // to a higher value in the if block below.
1597  size_t backdrop_count = 1;
1598  if (backdrop_id.has_value()) {
1599  std::unordered_map<int64_t, BackdropData>::iterator backdrop_data_it =
1600  backdrop_data_.find(backdrop_id.value());
1601  if (backdrop_data_it != backdrop_data_.end()) {
1602  backdrop_data = &backdrop_data_it->second;
1603  will_cache_backdrop_texture =
1604  backdrop_data_it->second.backdrop_count > 1;
1605  backdrop_count = backdrop_data_it->second.backdrop_count;
1606  }
1607  }
1608 
1609  if (!will_cache_backdrop_texture || !backdrop_data->texture_slot) {
1610  backdrop_count_ -= backdrop_count;
1611 
1612  // The onscreen texture can be flipped to if:
1613  // 1. The device supports framebuffer fetch
1614  // 2. There are no more backdrop filters
1615  // 3. The current render pass is for the onscreen pass.
1616  const bool should_use_onscreen =
1618  backdrop_count_ == 0 && render_passes_.size() == 1u;
1619  input_texture = FlipBackdrop(
1620  GetGlobalPassPosition(), //
1621  /*should_remove_texture=*/will_cache_backdrop_texture, //
1622  /*should_use_onscreen=*/should_use_onscreen //
1623  );
1624  if (!input_texture) {
1625  // Validation failures are logged in FlipBackdrop.
1626  return;
1627  }
1628 
1629  if (will_cache_backdrop_texture) {
1630  backdrop_data->texture_slot = input_texture;
1631  }
1632  } else {
1633  input_texture = backdrop_data->texture_slot;
1634  }
1635 
1636  backdrop_filter_contents = backdrop_filter_proc(
1637  FilterInput::Make(std::move(input_texture)),
1638  transform_stack_.back().transform.Basis(),
1639  // When the subpass has a translation that means the math with
1640  // the snapshot has to be different.
1641  transform_stack_.back().transform.HasTranslation()
1644 
1645  if (will_cache_backdrop_texture) {
1646  FML_DCHECK(backdrop_data);
1647  // If all filters on the shared backdrop layer are equal, process the
1648  // layer once.
1649  if (backdrop_data->all_filters_equal &&
1650  !backdrop_data->shared_filter_snapshot.has_value()) {
1651  // TODO(157110): compute minimum input hint.
1652  backdrop_data->shared_filter_snapshot =
1653  backdrop_filter_contents->RenderToSnapshot(renderer_, {}, {});
1654  }
1655 
1656  std::optional<Snapshot> maybe_snapshot =
1657  backdrop_data->shared_filter_snapshot;
1658  if (maybe_snapshot.has_value()) {
1659  const Snapshot& snapshot = maybe_snapshot.value();
1660  std::shared_ptr<TextureContents> contents = TextureContents::MakeRect(
1661  subpass_coverage.Shift(-GetGlobalPassPosition()));
1662  auto scaled =
1663  subpass_coverage.TransformBounds(snapshot.transform.Invert());
1664  contents->SetTexture(snapshot.texture);
1665  contents->SetSourceRect(scaled);
1666  contents->SetSamplerDescriptor(snapshot.sampler_descriptor);
1667 
1668  // This backdrop entity sets a depth value as it is written to the newly
1669  // flipped backdrop and not into a new saveLayer.
1670  Entity backdrop_entity;
1671  backdrop_entity.SetContents(std::move(contents));
1672  backdrop_entity.SetClipDepth(++current_depth_);
1673  backdrop_entity.SetBlendMode(paint.blend_mode);
1674 
1675  backdrop_entity.Render(renderer_, GetCurrentRenderPass());
1676  Save(0);
1677  return;
1678  }
1679  }
1680  }
1681 
1682  // When applying a save layer, absorb any pending distributed opacity.
1683  Paint paint_copy = paint;
1684  paint_copy.color.alpha *= transform_stack_.back().distributed_opacity;
1685  transform_stack_.back().distributed_opacity = 1.0;
1686 
1687  render_passes_.push_back(
1688  LazyRenderingConfig(renderer_, //
1689  CreateRenderTarget(renderer_, //
1690  subpass_size, //
1692  )));
1693  save_layer_state_.push_back(SaveLayerState{
1694  paint_copy, subpass_coverage.Shift(-coverage_origin_adjustment)});
1695 
1696  CanvasStackEntry entry;
1697  entry.transform = transform_stack_.back().transform;
1698  entry.clip_depth = current_depth_ + total_content_depth;
1699  FML_DCHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
1700  << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
1701  << " after allocating " << total_content_depth;
1702  entry.clip_height = transform_stack_.back().clip_height;
1704  entry.did_round_out = did_round_out;
1705  transform_stack_.emplace_back(entry);
1706 
1707  // Start non-collapsed subpasses with a fresh clip coverage stack limited by
1708  // the subpass coverage. This is important because image filters applied to
1709  // save layers may transform the subpass texture after it's rendered,
1710  // causing parent clip coverage to get misaligned with the actual area that
1711  // the subpass will affect in the parent pass.
1712  clip_coverage_stack_.PushSubpass(subpass_coverage, GetClipHeight());
1713 
1714  if (!backdrop_filter_contents) {
1715  return;
1716  }
1717 
1718  // Render the backdrop entity.
1719  Entity backdrop_entity;
1720  backdrop_entity.SetContents(std::move(backdrop_filter_contents));
1721  backdrop_entity.SetTransform(
1722  Matrix::MakeTranslation(Vector3(-local_position)));
1723  backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
1724  backdrop_entity.Render(renderer_, GetCurrentRenderPass());
1725 }
std::optional< Rect > GetLocalCoverageLimit() const
Return the culling bounds of the current render target, or nullopt if there is no coverage.
Definition: canvas.cc:1452
std::function< std::shared_ptr< FilterContents >(FilterInput::Ref, const Matrix &effect_transform, Entity::RenderingMode rendering_mode)> BackdropFilterProc
Definition: canvas.h:125
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:18
@ 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.
static constexpr Color BlackTransparent()
Definition: color.h:270
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:40
RoundOut(const TRect< U > &r)
Definition: rect.h:679
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 370 of file canvas.cc.

370  {
371  Concat(Matrix::MakeScale(scale));
372 }
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104

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

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

◆ Scale() [2/2]

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

Definition at line 374 of file canvas.cc.

374  {
375  Concat(Matrix::MakeScale(scale));
376 }

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

◆ 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 2178 of file canvas.cc.

2180  {
2181  backdrop_data_ = std::move(backdrop_data);
2182  backdrop_count_ = backdrop_count;
2183 }

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

◆ Skew()

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

Definition at line 378 of file canvas.cc.

378  {
379  Concat(Matrix::MakeSkew(sx, sy));
380 }
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 2301 of file canvas.cc.

2301  {
2302  return renderer_.GetContext()
2303  ->GetCapabilities()
2304  ->SupportsTextureToTextureBlits() &&
2305  renderer_.GetContext()->GetBackendType() ==
2307 }

◆ Transform()

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

Definition at line 358 of file canvas.cc.

358  {
359  Concat(transform);
360 }

References Concat(), and transform.

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

◆ Translate()

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

Definition at line 366 of file canvas.cc.

366  {
368 }

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

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

Member Data Documentation

◆ kMaxDepth

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

Definition at line 120 of file canvas.h.


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