Flutter Impeller
impeller::Canvas Class Reference

#include <canvas.h>

Classes

class  PathBlurShape
 
class  RRectBlurShape
 
class  RSuperellipseBlurShape
 
struct  SaveLayerState
 

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< Rect > GetLocalCoverageLimit () 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 Matrix & GetCurrentTransform () 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 Member Functions

static bool IsCompatibleWithSDFRendering (const Paint &paint)
 

Static Public Attributes

static constexpr uint32_t kMaxDepth = 1 << 24
 

Detailed Description

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

334  : renderer_(renderer),
335  render_target_(render_target),
336  is_onscreen_(is_onscreen),
337  requires_readback_(requires_readback),
338  clip_coverage_stack_(EntityPassClipStack(
339  Rect::MakeSize(render_target.GetRenderTargetSize()))) {
340  Initialize(std::nullopt);
341  SetupRenderPass();
342 }
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 344 of file canvas.cc.

349  : renderer_(renderer),
350  render_target_(render_target),
351  is_onscreen_(is_onscreen),
352  requires_readback_(requires_readback),
353  clip_coverage_stack_(EntityPassClipStack(
354  Rect::MakeSize(render_target.GetRenderTargetSize()))) {
355  Initialize(cull_rect);
356  SetupRenderPass();
357 }

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

364  : renderer_(renderer),
365  render_target_(render_target),
366  is_onscreen_(is_onscreen),
367  requires_readback_(requires_readback),
368  clip_coverage_stack_(EntityPassClipStack(
369  Rect::MakeSize(render_target.GetRenderTargetSize()))) {
370  Initialize(Rect::MakeLTRB(cull_rect.GetLeft(), cull_rect.GetTop(),
371  cull_rect.GetRight(), cull_rect.GetBottom()));
372  SetupRenderPass();
373 }
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 1292 of file canvas.cc.

1294  {
1295  if (IsSkipping()) {
1296  return;
1297  }
1298 
1299  // Ideally the clip depth would be greater than the current rendering
1300  // depth because any rendering calls that follow this clip operation will
1301  // pre-increment the depth and then be rendering above our clip depth,
1302  // but that case will be caught by the CHECK in AddRenderEntity above.
1303  // In practice we sometimes have a clip set with no rendering after it
1304  // and in such cases the current depth will equal the clip depth.
1305  // Eventually the DisplayList should optimize these out, but it is hard
1306  // to know if a clip will actually be used in advance of storing it in
1307  // the DisplayList buffer.
1308  // See https://github.com/flutter/flutter/issues/147021
1309  FML_DCHECK(current_depth_ <= transform_stack_.back().clip_depth)
1310  << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
1311  uint32_t clip_depth = transform_stack_.back().clip_depth;
1312 
1313  const Matrix clip_transform =
1314  Matrix::MakeTranslation(Vector3(-GetGlobalPassPosition())) *
1316 
1317  std::optional<Rect> clip_coverage = geometry.GetCoverage(clip_transform);
1318  if (!clip_coverage.has_value()) {
1319  return;
1320  }
1321 
1322  ClipContents clip_contents(
1323  clip_coverage.value(),
1324  /*is_axis_aligned_rect=*/geometry.IsAxisAlignedRect() &&
1325  GetCurrentTransform().IsTranslationScaleOnly());
1326  clip_contents.SetClipOperation(clip_op);
1327 
1328  EntityPassClipStack::ClipStateResult clip_state_result =
1329  clip_coverage_stack_.RecordClip(
1330  clip_contents, //
1331  /*transform=*/clip_transform, //
1332  /*global_pass_position=*/GetGlobalPassPosition(), //
1333  /*clip_depth=*/clip_depth, //
1334  /*clip_height_floor=*/GetClipHeightFloor(), //
1335  /*is_aa=*/is_aa);
1336 
1337  if (clip_state_result.clip_did_change) {
1338  // We only need to update the pass scissor if the clip state has changed.
1339  SetClipScissor(
1340  clip_coverage_stack_.CurrentClipCoverage(),
1341  *render_passes_.back().GetInlinePassContext()->GetRenderPass(),
1342  GetGlobalPassPosition());
1343  }
1344 
1345  ++transform_stack_.back().clip_height;
1346  ++transform_stack_.back().num_clips;
1347 
1348  if (!clip_state_result.should_render) {
1349  return;
1350  }
1351 
1352  // Note: this is a bit of a hack. Its not possible to construct a geometry
1353  // result without begninning the render pass. We should refactor the geometry
1354  // objects so that they only need a reference to the render pass size and/or
1355  // orthographic transform.
1356  Entity entity;
1357  entity.SetTransform(clip_transform);
1358  entity.SetClipDepth(clip_depth);
1359 
1360  GeometryResult geometry_result = geometry.GetPositionBuffer(
1361  renderer_, //
1362  entity, //
1363  *render_passes_.back().GetInlinePassContext()->GetRenderPass() //
1364  );
1365  clip_contents.SetGeometry(geometry_result);
1366  clip_coverage_stack_.GetLastReplayResult().clip_contents.SetGeometry(
1367  geometry_result);
1368 
1369  clip_contents.Render(
1370  renderer_, *render_passes_.back().GetInlinePassContext()->GetRenderPass(),
1371  clip_depth);
1372 }
const Matrix & GetCurrentTransform() const
Definition: canvas.cc:404
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 388 of file canvas.cc.

388  {
389  transform_stack_.back().transform = GetCurrentTransform() * transform;
390 }

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

1049  {
1050  Entity entity;
1051  entity.SetTransform(GetCurrentTransform());
1052  entity.SetBlendMode(paint.blend_mode);
1053 
1054  if (paint.style == Paint::Style::kFill) {
1055  ArcGeometry geom(arc);
1056  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1057  return;
1058  }
1059 
1060  const Rect& oval_bounds = arc.GetOvalBounds();
1061  if (paint.stroke.width > oval_bounds.GetSize().MaxDimension()) {
1062  // This is a special case for rendering arcs whose stroke width is so large
1063  // you are effectively drawing a sector of a circle.
1064  // https://github.com/flutter/flutter/issues/158567
1065  Arc expanded_arc(oval_bounds.Expand(Size(paint.stroke.width * 0.5f)),
1066  arc.GetStart(), arc.GetSweep(), true);
1067 
1068  ArcGeometry geom(expanded_arc);
1069  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1070  return;
1071  }
1072 
1073  // IncludeCenter incurs lots of extra work for stroking an arc, including:
1074  // - It introduces segments to/from the center point (not too hard).
1075  // - It introduces joins on those segments (a bit more complicated).
1076  // - Even if the sweep is >=360 degrees, we still draw the segment to
1077  // the center and it basically looks like a pie cut into the complete
1078  // boundary circle, as if the slice were cut, but not extracted
1079  // (hard to express as a continuous kTriangleStrip).
1080  if (!arc.IncludeCenter()) {
1081  if (arc.IsFullCircle()) {
1082  return DrawOval(oval_bounds, paint);
1083  }
1084 
1085  // Our fast stroking code only works for circular bounds as it assumes
1086  // that the inner and outer radii can be scaled along each angular step
1087  // of the arc - which is not true for elliptical arcs where the inner
1088  // and outer samples are perpendicular to the traveling direction of the
1089  // elliptical curve which may not line up with the center of the bounds.
1090  if (oval_bounds.IsSquare()) {
1091  ArcGeometry geom(arc, paint.stroke);
1092  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1093  return;
1094  }
1095  }
1096 
1097  ArcStrokeGeometry geom(arc, paint.stroke);
1098  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1099 }
void DrawOval(const Rect &rect, const Paint &paint)
Definition: canvas.cc:994
TRect< Scalar > Rect
Definition: rect.h:857
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 1588 of file canvas.cc.

1589  {
1590  atlas_contents->SetAlpha(paint.color.alpha);
1591 
1592  Entity entity;
1593  entity.SetTransform(GetCurrentTransform());
1594  entity.SetBlendMode(paint.blend_mode);
1595  entity.SetContents(paint.WithFilters(renderer_, atlas_contents));
1596 
1597  AddRenderEntityToCurrentPass(entity);
1598 }

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

1257  {
1258  if (IsShadowBlurDrawOperation(paint)) {
1259  Rect bounds = Rect::MakeCircleBounds(center, radius);
1260  RRectBlurShape shape(bounds, radius);
1261  if (AttemptDrawBlur(shape, paint)) {
1262  return;
1263  }
1264  }
1265 
1266  if (renderer_.GetContext()->GetFlags().use_sdfs &&
1268  auto params = UberSDFParameters::MakeCircle(
1269  /*color=*/paint.color, /*center=*/center, /*radius=*/radius,
1270  /*stroke=*/paint.GetStroke());
1271  AddRenderSDFEntityToCurrentPass(paint, params);
1272  return;
1273  }
1274 
1275  if (AttemptDrawAntialiasedCircle(center, radius, paint)) {
1276  return;
1277  }
1278 
1279  Entity entity;
1280  entity.SetTransform(GetCurrentTransform());
1281  entity.SetBlendMode(paint.blend_mode);
1282 
1283  if (paint.style == Paint::Style::kStroke) {
1284  CircleGeometry geom(center, radius, paint.stroke.width);
1285  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1286  } else {
1287  CircleGeometry geom(center, radius);
1288  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1289  }
1290 }
static bool IsCompatibleWithSDFRendering(const Paint &paint)
Definition: canvas.cc:2635
std::shared_ptr< Context > GetContext() const
constexpr static TRect MakeCircleBounds(const TPoint< Type > &center, Type radius)
Definition: rect.h:156
static UberSDFParameters MakeCircle(Color color, const Point &center, Scalar radius, std::optional< StrokeParameters > stroke)
Creates UberSDFParameters for a circle.

References impeller::Paint::blend_mode, impeller::Paint::color, impeller::ContentContext::GetContext(), GetCurrentTransform(), impeller::Paint::GetStroke(), IsCompatibleWithSDFRendering(), impeller::Paint::kStroke, impeller::UberSDFParameters::MakeCircle(), impeller::TRect< Scalar >::MakeCircleBounds(), impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke, impeller::Paint::style, and impeller::StrokeParameters::width.

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

919  {
920  // Reasons to defer to regular DrawLine:
921  // - performance for degenerate and "regular line" cases
922  // - length is non-positive - DrawLine will draw appropriate "dot"
923  // - off_length is non-positive - no gaps, DrawLine will draw it solid
924  // - on_length is negative - invalid dashing
925  //
926  // Note that a 0 length "on" dash will draw "dot"s every "off" distance
927  // apart so we proceed with the dashing process in that case.
928  Scalar length = p0.GetDistance(p1);
929  if (length > 0.0f && on_length >= 0.0f && off_length > 0.0f) {
930  Entity entity;
931  entity.SetTransform(GetCurrentTransform());
932  entity.SetBlendMode(paint.blend_mode);
933 
934  StrokeDashedLineGeometry geom(p0, p1, on_length, off_length, paint.stroke);
935  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
936  } else {
937  DrawLine(p0, p1, paint);
938  }
939 }
void DrawLine(const Point &p0, const Point &p1, const Paint &paint, bool reuse_depth=false)
Definition: canvas.cc:897
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 1170 of file canvas.cc.

1172  {
1173  Entity entity;
1174  entity.SetTransform(GetCurrentTransform());
1175  entity.SetBlendMode(paint.blend_mode);
1176 
1177  if (paint.style == Paint::Style::kFill) {
1178  FillDiffRoundRectGeometry geom(outer, inner);
1179  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1180  } else {
1181  StrokeDiffRoundRectGeometry geom(outer, inner, paint.stroke);
1182  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1183  }
1184 }

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

1395  {
1396  if (!image) {
1397  return;
1398  }
1399 
1400  const Rect source = Rect::MakeSize(image->GetSize());
1401  const Rect dest = source.Shift(offset);
1402 
1403  DrawImageRect(image, source, dest, paint, sampler);
1404 }
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:1406
constexpr TRect< T > Shift(T dx, T dy) const
Returns a new rectangle translated by the given offset.
Definition: rect.h:636

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

1411  {
1412  if (!image || source.IsEmpty() || dest.IsEmpty()) {
1413  return;
1414  }
1415 
1416  ISize size = image->GetSize();
1417  if (size.IsEmpty()) {
1418  return;
1419  }
1420 
1421  std::optional<Rect> clipped_source =
1422  source.Intersection(Rect::MakeSize(size));
1423  if (!clipped_source) {
1424  return;
1425  }
1426 
1427  if (AttemptColorFilterOptimization(image, source, dest, paint, sampler,
1428  src_rect_constraint)) {
1429  return;
1430  }
1431 
1432  if (*clipped_source != source) {
1433  Scalar sx = dest.GetWidth() / source.GetWidth();
1434  Scalar sy = dest.GetHeight() / source.GetHeight();
1435  Scalar tx = dest.GetLeft() - source.GetLeft() * sx;
1436  Scalar ty = dest.GetTop() - source.GetTop() * sy;
1437  Matrix src_to_dest = Matrix::MakeTranslateScale({sx, sy, 1}, {tx, ty, 0});
1438  dest = clipped_source->TransformBounds(src_to_dest);
1439  }
1440 
1441  auto texture_contents = TextureContents::MakeRect(dest);
1442  texture_contents->SetTexture(image);
1443  texture_contents->SetSourceRect(*clipped_source);
1444  texture_contents->SetStrictSourceRect(src_rect_constraint ==
1446  texture_contents->SetSamplerDescriptor(sampler);
1447  texture_contents->SetOpacity(paint.color.alpha);
1448  texture_contents->SetDeferApplyingOpacity(paint.HasColorFilter());
1449 
1450  Entity entity;
1451  entity.SetBlendMode(paint.blend_mode);
1452  entity.SetTransform(GetCurrentTransform());
1453 
1454  if (!paint.mask_blur_descriptor.has_value()) {
1455  entity.SetContents(
1456  paint.WithFilters(renderer_, std::move(texture_contents)));
1457  AddRenderEntityToCurrentPass(entity);
1458  return;
1459  }
1460 
1461  FillRectGeometry out_rect(Rect{});
1462 
1463  entity.SetContents(paint.WithFilters(
1464  renderer_,
1465  paint.mask_blur_descriptor->CreateMaskBlur(texture_contents, &out_rect)));
1466  AddRenderEntityToCurrentPass(entity);
1467 }
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 897 of file canvas.cc.

900  {
901  if (AttemptDrawLineSDF(p0, p1, paint, reuse_depth)) {
902  return;
903  }
904 
905  Entity entity;
906  entity.SetTransform(GetCurrentTransform());
907  entity.SetBlendMode(paint.blend_mode);
908 
909  auto geometry = std::make_unique<LineGeometry>(p0, p1, paint.stroke);
910 
911  AddRenderEntityWithFiltersToCurrentPass(entity, geometry.get(), paint,
912  reuse_depth);
913 }

References impeller::Paint::blend_mode, GetCurrentTransform(), p1, impeller::Entity::SetBlendMode(), 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 994 of file canvas.cc.

994  {
995  // TODO(jonahwilliams): This additional condition avoids an assert in the
996  // stroke circle geometry generator. I need to verify the condition that this
997  // assert prevents.
998  if (rect.IsSquare() && (paint.style == Paint::Style::kFill ||
999  (paint.style == Paint::Style::kStroke &&
1000  paint.stroke.width < rect.GetWidth()))) {
1001  // Circles have slightly less overhead and can do stroking
1002  DrawCircle(rect.GetCenter(), rect.GetWidth() * 0.5f, paint);
1003  return;
1004  }
1005 
1006  if (IsShadowBlurDrawOperation(paint)) {
1007  if (rect.IsSquare()) {
1008  // RRectBlurShape takes the corner radii which are half of the
1009  // overall width and height of the DrawOval bounds rect.
1010  RRectBlurShape shape(rect, rect.GetWidth() * 0.5f);
1011  if (AttemptDrawBlur(shape, paint)) {
1012  return;
1013  }
1014  } else {
1015  EllipsePathSource source(rect);
1016  if (AttemptDrawBlurredPathSource(source, paint)) {
1017  return;
1018  }
1019  }
1020  }
1021 
1022  Entity entity;
1023  entity.SetTransform(GetCurrentTransform());
1024  entity.SetBlendMode(paint.blend_mode);
1025 
1026  if (renderer_.GetContext()->GetFlags().use_sdfs &&
1028  UberSDFParameters params;
1029 
1030  if (paint.style == Paint::Style::kStroke) {
1031  params = UberSDFParameters::MakeOval(paint.color, rect, paint.stroke);
1032  } else {
1033  params = UberSDFParameters::MakeOval(paint.color, rect, std::nullopt);
1034  }
1035 
1036  AddRenderSDFEntityToCurrentPass(paint, params);
1037  return;
1038  }
1039 
1040  if (paint.style == Paint::Style::kStroke) {
1041  StrokeEllipseGeometry geom(rect, paint.stroke);
1042  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1043  } else {
1044  EllipseGeometry geom(rect);
1045  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1046  }
1047 }
void DrawCircle(const Point &center, Scalar radius, const Paint &paint)
Definition: canvas.cc:1255
static UberSDFParameters MakeOval(Color color, const Rect &bounds, std::optional< StrokeParameters > stroke)
Creates UberSDFParameters for an Oval.

References impeller::Paint::blend_mode, impeller::Paint::color, DrawCircle(), impeller::TRect< T >::GetCenter(), impeller::ContentContext::GetContext(), GetCurrentTransform(), impeller::TRect< T >::GetWidth(), IsCompatibleWithSDFRendering(), impeller::TRect< T >::IsSquare(), impeller::Paint::kFill, impeller::Paint::kStroke, impeller::UberSDFParameters::MakeOval(), 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 479 of file canvas.cc.

479  {
480  Entity entity;
481  entity.SetTransform(GetCurrentTransform());
482  entity.SetBlendMode(paint.blend_mode);
483 
484  CoverGeometry geom;
485  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
486 }

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

459  {
460  if (IsShadowBlurDrawOperation(paint)) {
461  if (AttemptDrawBlurredPathSource(path, paint)) {
462  return;
463  }
464  }
465 
466  Entity entity;
467  entity.SetTransform(GetCurrentTransform());
468  entity.SetBlendMode(paint.blend_mode);
469 
470  if (paint.style == Paint::Style::kFill) {
471  FillPathGeometry geom(path);
472  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
473  } else {
474  StrokePathGeometry geom(path, paint.stroke);
475  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
476  }
477 }

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

1378  {
1379  if (radius <= 0) {
1380  return;
1381  }
1382 
1383  Entity entity;
1384  entity.SetTransform(GetCurrentTransform());
1385  entity.SetBlendMode(paint.blend_mode);
1386 
1387  PointFieldGeometry geom(points, count, radius,
1388  /*round=*/point_style == PointStyle::kRound);
1389  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1390 }
@ 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 941 of file canvas.cc.

941  {
942  if (paint.style == Paint::Style::kFill && rect.IsEmpty()) {
943  return;
944  }
945 
946  if (IsShadowBlurDrawOperation(paint)) {
947  RRectBlurShape shape(rect, 0.0f);
948  if (AttemptDrawBlur(shape, paint)) {
949  return;
950  }
951  }
952 
953  if (renderer_.GetContext()->GetFlags().use_sdfs &&
955  Rect effective_rect = rect;
956  Color effective_color = paint.color;
957 
958  // Expand rect to 1 pixel minimum dimensions if applicable.
959  if (paint.style == Paint::Style::kFill &&
960  !GetCurrentTransform().HasPerspective2D()) {
961  auto [expanded, alpha_scaled_color] = ExpandRectToPixelMinimum(
962  rect, paint.color, GetCurrentTransform(), /*scale_alpha=*/true);
963 
964  if (expanded.IsEmpty()) {
965  // Rect is invisible due to transform scaling or alpha scaling.
966  return;
967  }
968 
969  effective_rect = expanded;
970  effective_color = alpha_scaled_color;
971  }
972 
973  auto params = UberSDFParameters::MakeRect(
974  /*color=*/effective_color,
975  /*rect=*/effective_rect,
976  /*stroke=*/paint.GetStroke());
977  AddRenderSDFEntityToCurrentPass(paint, params);
978  return;
979  }
980 
981  Entity entity;
982  entity.SetTransform(GetCurrentTransform());
983  entity.SetBlendMode(paint.blend_mode);
984 
985  if (paint.style == Paint::Style::kStroke) {
986  StrokeRectGeometry geom(rect, paint.stroke);
987  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
988  } else {
989  FillRectGeometry geom(rect);
990  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
991  }
992 }
static UberSDFParameters MakeRect(Color color, const Rect &rect, std::optional< StrokeParameters > stroke)
Creates UberSDFParameters for a rectangle.

References impeller::Paint::blend_mode, impeller::Paint::color, impeller::ContentContext::GetContext(), GetCurrentTransform(), impeller::Paint::GetStroke(), IsCompatibleWithSDFRendering(), impeller::TRect< T >::IsEmpty(), impeller::Paint::kFill, impeller::Paint::kStroke, impeller::UberSDFParameters::MakeRect(), impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke, 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 1101 of file canvas.cc.

1101  {
1102  if (paint.style == Paint::Style::kFill && round_rect.IsEmpty()) {
1103  return;
1104  }
1105 
1106  if (IsShadowBlurDrawOperation(paint)) {
1107  if (AttemptDrawBlurredRRect(round_rect, paint)) {
1108  return;
1109  }
1110  }
1111 
1112  const RoundingRadii& radii = round_rect.GetRadii();
1113 
1114  if (renderer_.GetContext()->GetFlags().use_sdfs &&
1115  IsCompatibleWithSDFRendering(paint) && radii.AreAllCornersCircular()) {
1116  Color effective_color = paint.color;
1117  Rect bounds = round_rect.GetBounds();
1118 
1119  // Expand rrect bounds to 1 pixel minimum dimensions if applicable.
1120  if (paint.style == Paint::Style::kFill &&
1121  !GetCurrentTransform().HasPerspective2D()) {
1122  auto [expanded, alpha_scaled_color] = ExpandRectToPixelMinimum(
1123  bounds, paint.color, GetCurrentTransform(), /*scale_alpha=*/true);
1124 
1125  if (expanded.IsEmpty()) {
1126  // RRect is invisible due to transform scaling or alpha scaling.
1127  return;
1128  }
1129 
1130  bounds = expanded;
1131  effective_color = alpha_scaled_color;
1132  }
1133 
1134  auto params = UberSDFParameters::MakeRoundedRect(
1135  /*color=*/effective_color,
1136  /*rect=*/bounds,
1137  /*radii=*/radii,
1138  /*stroke=*/paint.style == Paint::Style::kStroke
1139  ? std::make_optional(paint.stroke)
1140  : std::nullopt);
1141  AddRenderSDFEntityToCurrentPass(paint, params);
1142  return;
1143  }
1144 
1145  if (round_rect.GetRadii().AreAllCornersSame() &&
1146  paint.style == Paint::Style::kFill) {
1147  Entity entity;
1148  entity.SetTransform(GetCurrentTransform());
1149  entity.SetBlendMode(paint.blend_mode);
1150 
1151  RoundRectGeometry geom(round_rect.GetBounds(),
1152  round_rect.GetRadii().top_left);
1153  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1154  return;
1155  }
1156 
1157  Entity entity;
1158  entity.SetTransform(GetCurrentTransform());
1159  entity.SetBlendMode(paint.blend_mode);
1160 
1161  if (paint.style == Paint::Style::kFill) {
1162  FillRoundRectGeometry geom(round_rect);
1163  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1164  } else {
1165  StrokeRoundRectGeometry geom(round_rect, paint.stroke);
1166  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1167  }
1168 }
static UberSDFParameters MakeRoundedRect(Color color, const Rect &rect, const RoundingRadii &radii, std::optional< StrokeParameters > stroke)
Creates UberSDFParameters for a rounded rectangle.

References impeller::RoundingRadii::AreAllCornersCircular(), impeller::RoundingRadii::AreAllCornersSame(), impeller::Paint::blend_mode, impeller::Paint::color, impeller::RoundRect::GetBounds(), impeller::ContentContext::GetContext(), GetCurrentTransform(), impeller::RoundRect::GetRadii(), IsCompatibleWithSDFRendering(), impeller::RoundRect::IsEmpty(), impeller::Paint::kFill, impeller::Paint::kStroke, impeller::UberSDFParameters::MakeRoundedRect(), 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 1186 of file canvas.cc.

1187  {
1188  if (IsShadowBlurDrawOperation(paint)) {
1189  if (AttemptDrawBlurredRSuperellipse(round_superellipse, paint)) {
1190  return;
1191  }
1192  }
1193 
1194  Entity entity;
1195  entity.SetTransform(GetCurrentTransform());
1196  entity.SetBlendMode(paint.blend_mode);
1197 
1198  if (renderer_.GetContext()->GetFlags().use_sdfs &&
1200  auto round_superellipse_params = RoundSuperellipseParam::MakeBoundsRadii(
1201  round_superellipse.GetBounds(), round_superellipse.GetRadii());
1202 
1203  if (round_superellipse_params.all_corners_same) {
1205  /*color=*/paint.color,
1206  /*bounds=*/round_superellipse.GetBounds(),
1207  /*round_superellipse_params=*/round_superellipse_params,
1208  /*stroke=*/paint.GetStroke());
1209 
1210  AddRenderSDFEntityToCurrentPass(paint, params);
1211  return;
1212  } else {
1214  /*color=*/paint.color_source ? Color::White() : paint.color,
1215  /*bounds=*/round_superellipse.GetBounds(),
1216  /*round_superellipse_params=*/round_superellipse_params,
1217  /*stroke=*/paint.GetStroke());
1218 
1219  const Geometry* geom = contents->GetGeometry();
1220 
1221  if (paint.color_source) {
1222  std::shared_ptr<Contents> color_source_contents =
1223  paint.CreateContents(renderer_, geom);
1224  std::shared_ptr<Contents> final_contents =
1226  BlendMode::kSrcIn, {FilterInput::Make(std::move(contents)),
1227  FilterInput::Make(color_source_contents)});
1228 
1229  Paint new_paint = paint;
1230  new_paint.color_source = nullptr;
1231  AddRenderEntityWithFiltersToCurrentPass(entity, geom, new_paint,
1232  /*reuse_depth=*/false,
1233  /*override_contents=*/
1234  std::move(final_contents));
1235  } else {
1236  AddRenderEntityWithFiltersToCurrentPass(entity, geom, paint,
1237  /*reuse_depth=*/false,
1238  /*override_contents=*/
1239  std::move(contents));
1240  }
1241  return;
1242  }
1243  }
1244 
1245  if (paint.style == Paint::Style::kFill) {
1246  RoundSuperellipseGeometry geom(round_superellipse.GetBounds(),
1247  round_superellipse.GetRadii());
1248  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1249  } else {
1250  StrokeRoundSuperellipseGeometry geom(round_superellipse, paint.stroke);
1251  AddRenderEntityWithFiltersToCurrentPass(entity, &geom, paint);
1252  }
1253 }
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.
static std::unique_ptr< ComplexRoundedSuperellipseContents > Make(Color color, const Rect &bounds, const RoundSuperellipseParam &round_superellipse_params, std::optional< StrokeParameters > stroke)
static FilterInput::Ref Make(Variant input, bool msaa_enabled=true)
Definition: filter_input.cc:19
static constexpr Color White()
Definition: color.h:269
static RoundSuperellipseParam MakeBoundsRadii(const Rect &bounds, const RoundingRadii &radii)
static UberSDFParameters MakeRoundedSuperellipse(Color color, const Rect &bounds, const RoundSuperellipseParam &round_superellipse_params, std::optional< StrokeParameters > stroke)
Creates UberSDFParameters for an asymmetric round superellipse.

References impeller::Paint::blend_mode, impeller::Paint::color, impeller::Paint::color_source, impeller::Paint::CreateContents(), impeller::RoundSuperellipse::GetBounds(), impeller::ContentContext::GetContext(), GetCurrentTransform(), impeller::RoundSuperellipse::GetRadii(), impeller::Paint::GetStroke(), IsCompatibleWithSDFRendering(), impeller::Paint::kFill, impeller::kSrcIn, impeller::ComplexRoundedSuperellipseContents::Make(), impeller::FilterInput::Make(), impeller::ColorFilterContents::MakeBlend(), impeller::RoundSuperellipseParam::MakeBoundsRadii(), impeller::UberSDFParameters::MakeRoundedSuperellipse(), impeller::Entity::SetBlendMode(), impeller::Entity::SetTransform(), impeller::Paint::stroke, impeller::Paint::style, and impeller::Color::White().

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

2134  {
2136  if (max_scale * text_frame->GetFont().GetMetrics().point_size >
2137  kMaxTextScale) {
2138  fml::StatusOr<flutter::DlPath> path = text_frame->GetPath();
2139  if (path.ok()) {
2140  Save(1);
2141  Concat(Matrix::MakeTranslation(position));
2142  DrawPath(path.value(), paint);
2143  Restore();
2144  return;
2145  }
2146  }
2147 
2148  Entity entity;
2149  entity.SetClipDepth(GetClipHeight());
2150  entity.SetBlendMode(paint.blend_mode);
2151 
2152  auto text_contents = std::make_shared<TextContents>();
2153  text_contents->SetTextFrame(text_frame);
2154  text_contents->SetPosition(position);
2155  text_contents->SetScreenTransform(GetCurrentTransform());
2156  text_contents->SetForceTextColor(paint.mask_blur_descriptor.has_value());
2157  text_contents->SetColor(paint.color);
2158  text_contents->SetTextProperties(paint.color, paint.GetStroke());
2159 
2160  entity.SetTransform(GetCurrentTransform().Translate(position));
2161 
2162  if (AttemptBlurredTextOptimization(text_frame, text_contents, entity,
2163  paint)) {
2164  return;
2165  }
2166 
2167  entity.SetContents(paint.WithFilters(renderer_, std::move(text_contents)));
2168  AddRenderEntityToCurrentPass(entity, false);
2169 }
bool Restore()
Definition: canvas.cc:1940
void Concat(const Matrix &transform)
Definition: canvas.cc:388
void DrawPath(const flutter::DlPath &path, const Paint &paint)
Definition: canvas.cc:459
void Save(uint32_t total_content_depth=kMaxDepth)
Definition: canvas.cc:1656
void Translate(const Vector3 &offset)
Definition: canvas.cc:408
static constexpr Scalar kMaxTextScale
Definition: canvas.cc:2130
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::Paint::GetStroke(), impeller::kMaxTextScale, impeller::Matrix::MakeTranslation(), impeller::Paint::mask_blur_descriptor, Restore(), Save(), impeller::Entity::SetBlendMode(), impeller::Entity::SetClipDepth(), impeller::Entity::SetContents(), impeller::Entity::SetTransform(), Translate(), 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 1473 of file canvas.cc.

1475  {
1476  // Override the blend mode with kDestination in order to match the behavior
1477  // of Skia's SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER flag, which
1478  // is enabled when the Flutter engine builds Skia.
1479  if (!paint.color_source) {
1480  blend_mode = BlendMode::kDst;
1481  }
1482 
1483  Entity entity;
1484  entity.SetTransform(GetCurrentTransform());
1485  entity.SetBlendMode(paint.blend_mode);
1486 
1487  // If there are no vertex colors.
1488  if (UseColorSourceContents(vertices, paint)) {
1489  AddRenderEntityWithFiltersToCurrentPass(entity, vertices.get(), paint);
1490  return;
1491  }
1492 
1493  // If the blend mode is destination don't bother to bind or create a texture.
1494  if (blend_mode == BlendMode::kDst) {
1495  auto contents = std::make_shared<VerticesSimpleBlendContents>();
1496  contents->SetBlendMode(blend_mode);
1497  contents->SetAlpha(paint.color.alpha);
1498  contents->SetGeometry(vertices);
1499  entity.SetContents(paint.WithFilters(renderer_, std::move(contents)));
1500  AddRenderEntityToCurrentPass(entity);
1501  return;
1502  }
1503 
1504  // If there is a texture, use this directly. Otherwise render the color
1505  // source to a texture.
1506  if (paint.color_source &&
1507  paint.color_source->type() == flutter::DlColorSourceType::kImage) {
1508  const flutter::DlImageColorSource* image_color_source =
1509  paint.color_source->asImage();
1510  FML_DCHECK(image_color_source);
1511  auto texture =
1512  image_color_source->image()->asImpellerImage()->GetCachedTexture(
1513  renderer_);
1514  FML_DCHECK(texture);
1515  auto x_tile_mode = static_cast<Entity::TileMode>(
1516  image_color_source->horizontal_tile_mode());
1517  auto y_tile_mode =
1518  static_cast<Entity::TileMode>(image_color_source->vertical_tile_mode());
1519  auto sampler_descriptor =
1520  skia_conversions::ToSamplerDescriptor(image_color_source->sampling());
1521  auto effect_transform = image_color_source->matrix();
1522 
1523  auto contents = std::make_shared<VerticesSimpleBlendContents>();
1524  contents->SetBlendMode(blend_mode);
1525  contents->SetAlpha(paint.color.alpha);
1526  contents->SetGeometry(vertices);
1527  contents->SetEffectTransform(effect_transform);
1528  contents->SetTexture(texture);
1529  contents->SetTileMode(x_tile_mode, y_tile_mode);
1530  contents->SetSamplerDescriptor(sampler_descriptor);
1531 
1532  entity.SetContents(paint.WithFilters(renderer_, std::move(contents)));
1533  AddRenderEntityToCurrentPass(entity);
1534  return;
1535  }
1536 
1537  auto src_paint = paint;
1538  src_paint.color = paint.color.WithAlpha(1.0);
1539 
1540  std::shared_ptr<ColorSourceContents> src_contents =
1541  src_paint.CreateContents(renderer_, vertices.get());
1542 
1543  // If the color source has an intrinsic size, then we use that to
1544  // create the src contents as a simplification. Otherwise we use
1545  // the extent of the texture coordinates to determine how large
1546  // the src contents should be. If neither has a value we fall back
1547  // to using the geometry coverage data.
1548  Rect src_coverage;
1549  auto size = src_contents->GetColorSourceSize();
1550  if (size.has_value()) {
1551  src_coverage = Rect::MakeXYWH(0, 0, size->width, size->height);
1552  } else {
1553  auto cvg = vertices->GetCoverage(Matrix{});
1554  FML_CHECK(cvg.has_value());
1555  auto texture_coverage = vertices->GetTextureCoordinateCoverage();
1556  if (texture_coverage.has_value()) {
1557  src_coverage =
1558  Rect::MakeOriginSize(texture_coverage->GetOrigin(),
1559  texture_coverage->GetSize().Max({1, 1}));
1560  } else {
1561  // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1562  src_coverage = cvg.value();
1563  }
1564  }
1565  clip_geometry_.push_back(Geometry::MakeRect(Rect::Round(src_coverage)));
1566  src_contents =
1567  src_paint.CreateContents(renderer_, clip_geometry_.back().get());
1568 
1569  auto contents = std::make_shared<VerticesSimpleBlendContents>();
1570  contents->SetBlendMode(blend_mode);
1571  contents->SetAlpha(paint.color.alpha);
1572  contents->SetGeometry(vertices);
1573  contents->SetLazyTextureCoverage(src_coverage);
1574  contents->SetLazyTexture(
1575  [src_contents, src_coverage](
1576  const ContentContext& renderer) -> std::shared_ptr<Texture> {
1577  // Applying the src coverage as the coverage limit prevents the 1px
1578  // coverage pad from adding a border that is picked up by developer
1579  // specified UVs.
1580  auto snapshot = src_contents->RenderToSnapshot(
1581  renderer, {}, {.coverage_limit = Rect::Round(src_coverage)});
1582  return snapshot.has_value() ? snapshot->texture : nullptr;
1583  });
1584  entity.SetContents(paint.WithFilters(renderer_, std::move(contents)));
1585  AddRenderEntityToCurrentPass(entity);
1586 }
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:764
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 2607 of file canvas.cc.

2607  {
2608  FML_DCHECK(render_passes_.size() == 1u);
2609  render_passes_.back().GetInlinePassContext()->GetRenderPass();
2610  render_passes_.back().GetInlinePassContext()->EndPass(
2611  /*is_onscreen=*/!requires_readback_ && is_onscreen_);
2612  backdrop_data_.clear();
2613 
2614  // If requires_readback_ was true, then we rendered to an offscreen texture
2615  // instead of to the onscreen provided in the render target. Now we need to
2616  // draw or blit the offscreen back to the onscreen.
2617  if (requires_readback_) {
2618  BlitToOnscreen(/*is_onscreen_=*/is_onscreen_);
2619  }
2620  if (!EnsureFinalMipmapGeneration()) {
2621  VALIDATION_LOG << "Failed to generate onscreen mipmaps.";
2622  }
2623  if (!renderer_.GetContext()->FlushCommandBuffers()) {
2624  // Not much we can do.
2625  VALIDATION_LOG << "Failed to submit command buffers";
2626  }
2627  render_passes_.clear();
2628  renderer_.GetRenderTargetCache()->End();
2629  clip_geometry_.clear();
2630 
2631  Reset();
2632  Initialize(initial_cull_rect_);
2633 }
bool EnsureFinalMipmapGeneration() const
Definition: canvas.cc:2589
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 2589 of file canvas.cc.

2589  {
2590  if (!render_target_.GetRenderTargetTexture()->NeedsMipmapGeneration()) {
2591  return true;
2592  }
2593  std::shared_ptr<CommandBuffer> cmd_buffer =
2594  renderer_.GetContext()->CreateCommandBuffer();
2595  if (!cmd_buffer) {
2596  return false;
2597  }
2598  std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
2599  if (!blit_pass) {
2600  return false;
2601  }
2602  blit_pass->GenerateMipmap(render_target_.GetRenderTargetTexture());
2603  blit_pass->EncodeCommands();
2604  return renderer_.GetContext()->EnqueueCommandBuffer(std::move(cmd_buffer));
2605 }
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 1673 of file canvas.cc.

1673  {
1674  if (!clip_coverage_stack_.HasCoverage()) {
1675  // The current clip is empty. This means the pass texture won't be
1676  // visible, so skip it.
1677  return std::nullopt;
1678  }
1679 
1680  std::optional<Rect> maybe_current_clip_coverage =
1681  clip_coverage_stack_.CurrentClipCoverage();
1682  if (!maybe_current_clip_coverage.has_value()) {
1683  return std::nullopt;
1684  }
1685 
1686  Rect current_clip_coverage = maybe_current_clip_coverage.value();
1687 
1688  FML_CHECK(!render_passes_.empty());
1689  const LazyRenderingConfig& back_render_pass = render_passes_.back();
1690  std::shared_ptr<Texture> back_texture =
1691  back_render_pass.GetInlinePassContext()->GetTexture();
1692  FML_CHECK(back_texture) << "Context is valid:"
1693  << back_render_pass.GetInlinePassContext()->IsValid();
1694 
1695  // The maximum coverage of the subpass. Subpasses textures should never
1696  // extend outside the parent pass texture or the current clip coverage.
1697  std::optional<Rect> maybe_coverage_limit =
1698  Rect::MakeOriginSize(GetGlobalPassPosition(),
1699  Size(back_texture->GetSize()))
1700  .Intersection(current_clip_coverage);
1701 
1702  if (!maybe_coverage_limit.has_value() || maybe_coverage_limit->IsEmpty()) {
1703  return std::nullopt;
1704  }
1705 
1706  return maybe_coverage_limit->Intersection(
1707  Rect::MakeSize(render_target_.GetRenderTargetSize()));
1708 }
ISize GetRenderTargetSize() const
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition: rect.h:562

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 257 of file canvas.h.

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

◆ GetOpDepth()

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

Definition at line 255 of file canvas.h.

255 { return current_depth_; }

◆ GetSaveCount()

size_t impeller::Canvas::GetSaveCount ( ) const

Definition at line 443 of file canvas.cc.

443  {
444  return transform_stack_.size();
445 }

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

◆ IsCompatibleWithSDFRendering()

bool impeller::Canvas::IsCompatibleWithSDFRendering ( const Paint &  paint)
static

Returns true if the paint is compatible with SDF rendering.

Visible for testing.

Definition at line 2635 of file canvas.cc.

2635  {
2636  if (!paint.anti_alias) {
2637  return false;
2638  }
2639  if (paint.mask_blur_descriptor.has_value()) {
2640  return false;
2641  }
2642  switch (paint.blend_mode) {
2643  // Incompatible blend modes:
2644  case BlendMode::kClear:
2645  case BlendMode::kSrc:
2646  case BlendMode::kSrcIn:
2647  case BlendMode::kDstIn:
2648  case BlendMode::kSrcOut:
2649  case BlendMode::kDstATop:
2650  case BlendMode::kPlus:
2651  case BlendMode::kModulate:
2652  return false;
2653  // Compatible blend modes:
2654  case BlendMode::kDst:
2655  case BlendMode::kSrcOver:
2656  case BlendMode::kDstOver:
2657  case BlendMode::kDstOut:
2658  case BlendMode::kSrcATop:
2659  case BlendMode::kXor:
2660  case BlendMode::kScreen:
2661  case BlendMode::kOverlay:
2662  case BlendMode::kDarken:
2663  case BlendMode::kLighten:
2665  case BlendMode::kColorBurn:
2666  case BlendMode::kHardLight:
2667  case BlendMode::kSoftLight:
2669  case BlendMode::kExclusion:
2670  case BlendMode::kMultiply:
2671  case BlendMode::kHue:
2673  case BlendMode::kColor:
2675  return true;
2676  }
2677 }

References impeller::Paint::anti_alias, impeller::Paint::blend_mode, and impeller::Paint::mask_blur_descriptor.

Referenced by DrawCircle(), DrawOval(), DrawRect(), DrawRoundRect(), DrawRoundSuperellipse(), impeller::testing::TEST(), and impeller::testing::TEST_P().

◆ PreConcat()

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

Definition at line 392 of file canvas.cc.

392  {
393  transform_stack_.back().transform = transform * GetCurrentTransform();
394 }

References GetCurrentTransform(), and transform.

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

◆ RequiresReadback()

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

Definition at line 265 of file canvas.h.

265 { return requires_readback_; }

◆ ResetTransform()

void impeller::Canvas::ResetTransform ( )

Definition at line 396 of file canvas.cc.

396  {
397  transform_stack_.back().transform = {};
398 }

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

◆ Restore()

bool impeller::Canvas::Restore ( )

Definition at line 1940 of file canvas.cc.

1940  {
1941  FML_DCHECK(transform_stack_.size() > 0);
1942  if (transform_stack_.size() == 1) {
1943  return false;
1944  }
1945 
1946  // This check is important to make sure we didn't exceed the depth
1947  // that the clips were rendered at while rendering any of the
1948  // rendering ops. It is OK for the current depth to equal the
1949  // outgoing clip depth because that means the clipping would have
1950  // been successful up through the last rendering op, but it cannot
1951  // be greater.
1952  // Also, we bump the current rendering depth to the outgoing clip
1953  // depth so that future rendering operations are not clipped by
1954  // any of the pixels set by the expiring clips. It is OK for the
1955  // estimates used to determine the clip depth in save/saveLayer
1956  // to be overly conservative, but we need to jump the depth to
1957  // the clip depth so that the next rendering op will get a
1958  // larger depth (it will pre-increment the current_depth_ value).
1959  FML_DCHECK(current_depth_ <= transform_stack_.back().clip_depth)
1960  << current_depth_ << " <=? " << transform_stack_.back().clip_depth;
1961  current_depth_ = transform_stack_.back().clip_depth;
1962 
1963  if (IsSkipping()) {
1964  transform_stack_.pop_back();
1965  return true;
1966  }
1967 
1968  if (transform_stack_.back().rendering_mode ==
1970  transform_stack_.back().rendering_mode ==
1972  auto lazy_render_pass = std::move(render_passes_.back());
1973  render_passes_.pop_back();
1974  // Force the render pass to be constructed if it never was.
1975  lazy_render_pass.GetInlinePassContext()->GetRenderPass();
1976 
1977  SaveLayerState save_layer_state = save_layer_state_.back();
1978  save_layer_state_.pop_back();
1979  auto global_pass_position = GetGlobalPassPosition();
1980 
1981  std::shared_ptr<Contents> contents = CreateContentsForSubpassTarget(
1982  renderer_, save_layer_state.paint, //
1983  lazy_render_pass.GetInlinePassContext()->GetTexture(), //
1984  Matrix::MakeTranslation(Vector3{-global_pass_position}) * //
1985  transform_stack_.back().transform //
1986  );
1987 
1988  lazy_render_pass.GetInlinePassContext()->EndPass();
1989 
1990  // Round the subpass texture position for pixel alignment with the parent
1991  // pass render target. By default, we draw subpass textures with nearest
1992  // sampling, so aligning here is important for avoiding visual nearest
1993  // sampling errors caused by limited floating point precision when
1994  // straddling a half pixel boundary.
1995  Point subpass_texture_position;
1996  if (transform_stack_.back().did_round_out) {
1997  // Subpass coverage was rounded out, origin potentially moved "down" by
1998  // as much as a pixel.
1999  subpass_texture_position =
2000  (save_layer_state.coverage.GetOrigin() - global_pass_position)
2001  .Floor();
2002  } else {
2003  // Subpass coverage was truncated. Pick the closest phyiscal pixel.
2004  subpass_texture_position =
2005  (save_layer_state.coverage.GetOrigin() - global_pass_position)
2006  .Round();
2007  }
2008 
2009  Entity element_entity;
2010  element_entity.SetClipDepth(++current_depth_);
2011  element_entity.SetContents(std::move(contents));
2012  element_entity.SetBlendMode(save_layer_state.paint.blend_mode);
2013  element_entity.SetTransform(
2014  Matrix::MakeTranslation(Vector3(subpass_texture_position)));
2015 
2016  if (element_entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
2017  if (renderer_.GetDeviceCapabilities().SupportsFramebufferFetch()) {
2018  ApplyFramebufferBlend(element_entity);
2019  } else {
2020  // End the active pass and flush the buffer before rendering "advanced"
2021  // blends. Advanced blends work by binding the current render target
2022  // texture as an input ("destination"), blending with a second texture
2023  // input ("source"), writing the result to an intermediate texture, and
2024  // finally copying the data from the intermediate texture back to the
2025  // render target texture. And so all of the commands that have written
2026  // to the render target texture so far need to execute before it's bound
2027  // for blending (otherwise the blend pass will end up executing before
2028  // all the previous commands in the active pass).
2029  auto input_texture = FlipBackdrop(GetGlobalPassPosition());
2030  if (!input_texture) {
2031  return false;
2032  }
2033 
2034  FilterInput::Vector inputs = {
2035  FilterInput::Make(input_texture,
2036  element_entity.GetTransform().Invert()),
2037  FilterInput::Make(element_entity.GetContents())};
2038  auto contents = ColorFilterContents::MakeBlend(
2039  element_entity.GetBlendMode(), inputs);
2040  contents->SetCoverageHint(element_entity.GetCoverage());
2041  element_entity.SetContents(std::move(contents));
2042  element_entity.SetBlendMode(BlendMode::kSrc);
2043  }
2044  }
2045 
2046  element_entity.Render(
2047  renderer_, //
2048  *render_passes_.back().GetInlinePassContext()->GetRenderPass() //
2049  );
2050  clip_coverage_stack_.PopSubpass();
2051  transform_stack_.pop_back();
2052 
2053  // We don't need to restore clips if a saveLayer was performed, as the clip
2054  // state is per render target, and no more rendering operations will be
2055  // performed as the render target workloaded is completed in the restore.
2056  return true;
2057  }
2058 
2059  size_t num_clips = transform_stack_.back().num_clips;
2060  transform_stack_.pop_back();
2061 
2062  if (num_clips > 0) {
2063  EntityPassClipStack::ClipStateResult clip_state_result =
2064  clip_coverage_stack_.RecordRestore(GetGlobalPassPosition(),
2065  GetClipHeight());
2066 
2067  // Clip restores are never required with depth based clipping.
2068  FML_DCHECK(!clip_state_result.should_render);
2069  if (clip_state_result.clip_did_change) {
2070  // We only need to update the pass scissor if the clip state has changed.
2071  SetClipScissor(
2072  clip_coverage_stack_.CurrentClipCoverage(), //
2073  *render_passes_.back().GetInlinePassContext()->GetRenderPass(), //
2074  GetGlobalPassPosition() //
2075  );
2076  }
2077  }
2078 
2079  return true;
2080 }
virtual bool SupportsFramebufferFetch() const =0
Whether the context backend is able to support pipelines with shaders that read from the framebuffer ...
const Capabilities & GetDeviceCapabilities() const
static constexpr BlendMode kLastPipelineBlendMode
Definition: entity.h:28
ClipStateResult RecordRestore(Point global_pass_position, size_t restore_height)
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 451 of file canvas.cc.

451  {
452  while (GetSaveCount() > count) {
453  if (!Restore()) {
454  return;
455  }
456  }
457 }
size_t GetSaveCount() const
Definition: canvas.cc:443

References GetSaveCount(), and Restore().

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

◆ Rotate()

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

Definition at line 424 of file canvas.cc.

424  {
425  Concat(Matrix::MakeRotationZ(radians));
426 }
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 1656 of file canvas.cc.

1656  {
1657  if (IsSkipping()) {
1658  return SkipUntilMatchingRestore(total_content_depth);
1659  }
1660 
1661  auto entry = CanvasStackEntry{};
1662  entry.transform = transform_stack_.back().transform;
1663  entry.clip_depth = current_depth_ + total_content_depth;
1664  entry.distributed_opacity = transform_stack_.back().distributed_opacity;
1665  FML_DCHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
1666  << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
1667  << " after allocating " << total_content_depth;
1668  entry.clip_height = transform_stack_.back().clip_height;
1669  entry.rendering_mode = Entity::RenderingMode::kDirect;
1670  transform_stack_.push_back(entry);
1671 }

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

1716  {
1717  TRACE_EVENT0("flutter", "Canvas::saveLayer");
1718  if (IsSkipping()) {
1719  return SkipUntilMatchingRestore(total_content_depth);
1720  }
1721 
1722  auto maybe_coverage_limit = GetLocalCoverageLimit();
1723  if (!maybe_coverage_limit.has_value()) {
1724  return SkipUntilMatchingRestore(total_content_depth);
1725  }
1726  auto coverage_limit = maybe_coverage_limit.value();
1727 
1728  if (can_distribute_opacity && !backdrop_filter &&
1730  bounds_promise != ContentBoundsPromise::kMayClipContents) {
1731  Save(total_content_depth);
1732  transform_stack_.back().distributed_opacity *= paint.color.alpha;
1733  return;
1734  }
1735 
1736  std::shared_ptr<FilterContents> filter_contents = paint.WithImageFilter(
1737  renderer_, Rect(), transform_stack_.back().transform,
1739 
1740  std::optional<Rect> maybe_subpass_coverage = ComputeSaveLayerCoverage(
1741  bounds.value_or(Rect::MakeMaximum()),
1742  transform_stack_.back().transform, //
1743  coverage_limit, //
1744  filter_contents, //
1745  /*flood_output_coverage=*/
1746  Entity::IsBlendModeDestructive(paint.blend_mode), //
1747  /*flood_input_coverage=*/!!backdrop_filter ||
1748  (paint.color_filter &&
1749  paint.color_filter->modifies_transparent_black()) //
1750  );
1751 
1752  if (!maybe_subpass_coverage.has_value()) {
1753  return SkipUntilMatchingRestore(total_content_depth);
1754  }
1755 
1756  auto subpass_coverage = maybe_subpass_coverage.value();
1757 
1758  // When an image filter is present, clamp to avoid flicking due to nearest
1759  // sampled image. For other cases, round out to ensure than any geometry is
1760  // not cut off.
1761  //
1762  // See also this bug: https://github.com/flutter/flutter/issues/144213
1763  //
1764  // TODO(jonahwilliams): this could still round out for filters that use decal
1765  // sampling mode.
1767  bool did_round_out = false;
1768  Point coverage_origin_adjustment = Point{0, 0};
1769  if (paint.image_filter) {
1770  subpass_size = ISize(subpass_coverage.GetSize());
1771  } else {
1772  did_round_out = true;
1773  subpass_size =
1774  static_cast<ISize>(IRect::RoundOut(subpass_coverage).GetSize());
1775  // If rounding out, adjust the coverage to account for the subpixel shift.
1776  coverage_origin_adjustment =
1777  Point(subpass_coverage.GetLeftTop().x -
1778  std::floor(subpass_coverage.GetLeftTop().x),
1779  subpass_coverage.GetLeftTop().y -
1780  std::floor(subpass_coverage.GetLeftTop().y));
1781  }
1782  if (subpass_size.IsEmpty()) {
1783  return SkipUntilMatchingRestore(total_content_depth);
1784  }
1785 
1786  // When there are scaling filters present, these contents may exceed the
1787  // maximum texture size. Perform a clamp here, which may cause rendering
1788  // artifacts.
1789  subpass_size = subpass_size.Min(renderer_.GetContext()
1790  ->GetCapabilities()
1791  ->GetMaximumRenderPassAttachmentSize());
1792 
1793  // Backdrop filter state, ignored if there is no BDF.
1794  std::shared_ptr<FilterContents> backdrop_filter_contents;
1795  Point local_position = Point(0, 0);
1796  if (backdrop_filter) {
1797  local_position = subpass_coverage.GetOrigin() - GetGlobalPassPosition();
1798 
1799  std::shared_ptr<Texture> input_texture;
1800 
1801  // If the backdrop ID is not nullopt and there is more than one usage
1802  // of it in the current scene, cache the backdrop texture and remove it from
1803  // the current entity pass flip.
1804  bool will_cache_backdrop_texture = false;
1805  BackdropData* backdrop_data = nullptr;
1806  // If we've reached this point, there is at least one backdrop filter. But
1807  // potentially more if there is a backdrop id. We may conditionally set this
1808  // to a higher value in the if block below.
1809  size_t backdrop_count = 1;
1810  if (backdrop_id.has_value()) {
1811  std::unordered_map<int64_t, BackdropData>::iterator backdrop_data_it =
1812  backdrop_data_.find(backdrop_id.value());
1813  if (backdrop_data_it != backdrop_data_.end()) {
1814  backdrop_data = &backdrop_data_it->second;
1815  will_cache_backdrop_texture =
1816  backdrop_data_it->second.backdrop_count > 1;
1817  backdrop_count = backdrop_data_it->second.backdrop_count;
1818  }
1819  }
1820 
1821  if (!will_cache_backdrop_texture || !backdrop_data->texture_slot) {
1822  backdrop_count_ -= backdrop_count;
1823 
1824  // The onscreen texture can be flipped to if:
1825  // 1. The device supports framebuffer fetch
1826  // 2. There are no more backdrop filters
1827  // 3. The current render pass is for the onscreen pass.
1828  const bool should_use_onscreen =
1830  backdrop_count_ == 0 && render_passes_.size() == 1u;
1831  input_texture = FlipBackdrop(
1832  GetGlobalPassPosition(), //
1833  /*should_remove_texture=*/will_cache_backdrop_texture, //
1834  /*should_use_onscreen=*/should_use_onscreen //
1835  );
1836  if (!input_texture) {
1837  // Validation failures are logged in FlipBackdrop.
1838  return;
1839  }
1840 
1841  if (will_cache_backdrop_texture) {
1842  backdrop_data->texture_slot = input_texture;
1843  }
1844  } else {
1845  input_texture = backdrop_data->texture_slot;
1846  }
1847 
1848  backdrop_filter_contents =
1849  WrapInput(renderer_, backdrop_filter,
1850  FilterInput::Make(std::move(input_texture)));
1851  backdrop_filter_contents->SetEffectTransform(
1852  transform_stack_.back().transform.Basis());
1853  backdrop_filter_contents->SetRenderingMode(
1854  transform_stack_.back().transform.HasTranslation()
1857 
1858  if (will_cache_backdrop_texture) {
1859  FML_DCHECK(backdrop_data);
1860  // If all filters on the shared backdrop layer are equal, process the
1861  // layer once.
1862  if (backdrop_data->all_filters_equal &&
1863  !backdrop_data->shared_filter_snapshot.has_value()) {
1864  // TODO(157110): compute minimum input hint.
1865  backdrop_data->shared_filter_snapshot =
1866  backdrop_filter_contents->RenderToSnapshot(renderer_, {}, {});
1867  }
1868 
1869  std::optional<Snapshot> maybe_snapshot =
1870  backdrop_data->shared_filter_snapshot;
1871  if (maybe_snapshot.has_value()) {
1872  const Snapshot& snapshot = maybe_snapshot.value();
1873  std::shared_ptr<TextureContents> contents = TextureContents::MakeRect(
1874  subpass_coverage.Shift(-GetGlobalPassPosition()));
1875  auto scaled =
1876  subpass_coverage.TransformBounds(snapshot.transform.Invert());
1877  contents->SetTexture(snapshot.texture);
1878  contents->SetSourceRect(scaled);
1879  contents->SetSamplerDescriptor(snapshot.sampler_descriptor);
1880 
1881  // This backdrop entity sets a depth value as it is written to the newly
1882  // flipped backdrop and not into a new saveLayer.
1883  Entity backdrop_entity;
1884  backdrop_entity.SetContents(std::move(contents));
1885  backdrop_entity.SetClipDepth(++current_depth_);
1886  backdrop_entity.SetBlendMode(paint.blend_mode);
1887 
1888  backdrop_entity.Render(renderer_, GetCurrentRenderPass());
1889  Save(0);
1890  return;
1891  }
1892  }
1893  }
1894 
1895  // When applying a save layer, absorb any pending distributed opacity.
1896  Paint paint_copy = paint;
1897  paint_copy.color.alpha *= transform_stack_.back().distributed_opacity;
1898  transform_stack_.back().distributed_opacity = 1.0;
1899 
1900  render_passes_.push_back(
1901  LazyRenderingConfig(renderer_, //
1902  CreateRenderTarget(renderer_, //
1903  subpass_size, //
1905  )));
1906  save_layer_state_.push_back(SaveLayerState{
1907  paint_copy, subpass_coverage.Shift(-coverage_origin_adjustment)});
1908 
1909  CanvasStackEntry entry;
1910  entry.transform = transform_stack_.back().transform;
1911  entry.clip_depth = current_depth_ + total_content_depth;
1912  FML_DCHECK(entry.clip_depth <= transform_stack_.back().clip_depth)
1913  << entry.clip_depth << " <=? " << transform_stack_.back().clip_depth
1914  << " after allocating " << total_content_depth;
1915  entry.clip_height = transform_stack_.back().clip_height;
1917  entry.did_round_out = did_round_out;
1918  transform_stack_.emplace_back(entry);
1919 
1920  // Start non-collapsed subpasses with a fresh clip coverage stack limited by
1921  // the subpass coverage. This is important because image filters applied to
1922  // save layers may transform the subpass texture after it's rendered,
1923  // causing parent clip coverage to get misaligned with the actual area that
1924  // the subpass will affect in the parent pass.
1925  clip_coverage_stack_.PushSubpass(subpass_coverage, GetClipHeight());
1926 
1927  if (!backdrop_filter_contents) {
1928  return;
1929  }
1930 
1931  // Render the backdrop entity.
1932  Entity backdrop_entity;
1933  backdrop_entity.SetContents(std::move(backdrop_filter_contents));
1934  backdrop_entity.SetTransform(
1935  Matrix::MakeTranslation(Vector3(-local_position)));
1936  backdrop_entity.SetClipDepth(std::numeric_limits<uint32_t>::max());
1937  backdrop_entity.Render(renderer_, GetCurrentRenderPass());
1938 }
std::optional< Rect > GetLocalCoverageLimit() const
Return the culling bounds of the current render target, or nullopt if there is no coverage.
Definition: canvas.cc:1673
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)
ISize subpass_size
The output size of the down-sampling pass.
std::shared_ptr< FilterContents > WrapInput(const ContentContext &renderer, const flutter::DlImageFilter *filter, const FilterInput::Ref &input)
Generate a new FilterContents using this filter's configuration.
Definition: image_filter.cc:20
@ 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:275
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:42
RoundOut(const TRect< U > &r)
Definition: rect.h:748
constexpr static TRect MakeMaximum()
Definition: rect.h:212

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

412  {
413  Concat(Matrix::MakeScale(scale));
414 }
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 416 of file canvas.cc.

416  {
417  Concat(Matrix::MakeScale(scale));
418 }

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

2410  {
2411  backdrop_data_ = std::move(backdrop_data);
2412  backdrop_count_ = backdrop_count;
2413 }

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

◆ Skew()

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

Definition at line 420 of file canvas.cc.

420  {
421  Concat(Matrix::MakeSkew(sx, sy));
422 }
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 2531 of file canvas.cc.

2531  {
2532  return renderer_.GetContext()
2533  ->GetCapabilities()
2534  ->SupportsTextureToTextureBlits() &&
2535  renderer_.GetContext()->GetBackendType() ==
2537 }

◆ Transform()

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

Definition at line 400 of file canvas.cc.

400  {
401  Concat(transform);
402 }

References Concat(), and transform.

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

◆ Translate()

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

Definition at line 408 of file canvas.cc.

408  {
410 }

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

Referenced by DrawTextFrame(), and impeller::DlDispatcherBase::translate().

Member Data Documentation

◆ kMaxDepth

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

Definition at line 121 of file canvas.h.


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