blob: ebaed09d8af5b5893cf02b3d257eb6b00c5b18ff [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLOW_DISPLAY_LIST_H_
#define FLUTTER_FLOW_DISPLAY_LIST_H_
#include <optional>
#include "third_party/skia/include/core/SkBlender.h"
#include "third_party/skia/include/core/SkBlurTypes.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageFilter.h"
#include "third_party/skia/include/core/SkMaskFilter.h"
#include "third_party/skia/include/core/SkPathEffect.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkShader.h"
#include "third_party/skia/include/core/SkVertices.h"
#include "flutter/fml/logging.h"
// The Flutter DisplayList mechanism encapsulates a persistent sequence of
// rendering operations.
//
// This file contains the definitions for:
// DisplayList: the base class that holds the information about the
// sequence of operations and can dispatch them to a Dispatcher
// Dispatcher: a pure virtual interface which can be implemented to field
// the requests for purposes such as sending them to an SkCanvas
// or detecting various rendering optimization scenarios
// DisplayListBuilder: a class for constructing a DisplayList from the same
// calls defined in the Dispatcher
//
// Other files include various class definitions for dealing with display
// lists, such as:
// display_list_canvas.h: classes to interact between SkCanvas and DisplayList
// (SkCanvas->DisplayList adapter and vice versa)
//
// display_list_utils.h: various utility classes to ease implementing
// a Dispatcher, including NOP implementations of
// the attribute, clip, and transform methods,
// classes to track attributes, clips, and transforms
// and a class to compute the bounds of a DisplayList
// Any class implementing Dispatcher can inherit from
// these utility classes to simplify its creation
//
// The Flutter DisplayList mechanism can be used in place of the Skia
// SkPicture mechanism. The primary means of communication into and out
// of the DisplayList is through the Dispatcher virtual class which
// provides a nearly 1:1 translation between the records of the DisplayList
// to method calls.
//
// A DisplayList can be created directly using a DisplayListBuilder and
// the Dispatcher methods that it implements, or it can be created from
// a sequence of SkCanvas calls using the DisplayListCanvasRecorder class.
//
// A DisplayList can be read back by implementing the Dispatcher virtual
// methods (with help from some of the classes in the utils file) and
// passing an instance to the dispatch() method, or it can be rendered
// to Skia using a DisplayListCanvasDispatcher or simply by passing an
// SkCanvas pointer to its renderTo() method.
//
// The mechanism is inspired by the SkLiteDL class that is not directly
// supported by Skia, but has been recommended as a basis for custom
// display lists for a number of their customers.
namespace flutter {
#define FOR_EACH_DISPLAY_LIST_OP(V) \
V(SetAntiAlias) \
V(SetDither) \
V(SetInvertColors) \
\
V(SetStrokeCap) \
V(SetStrokeJoin) \
\
V(SetStyle) \
V(SetStrokeWidth) \
V(SetStrokeMiter) \
\
V(SetColor) \
V(SetBlendMode) \
\
V(SetBlender) \
V(ClearBlender) \
V(SetShader) \
V(ClearShader) \
V(SetColorFilter) \
V(ClearColorFilter) \
V(SetImageFilter) \
V(ClearImageFilter) \
V(SetPathEffect) \
V(ClearPathEffect) \
\
V(ClearMaskFilter) \
V(SetMaskFilter) \
V(SetMaskBlurFilterNormal) \
V(SetMaskBlurFilterSolid) \
V(SetMaskBlurFilterOuter) \
V(SetMaskBlurFilterInner) \
\
V(Save) \
V(SaveLayer) \
V(SaveLayerBounds) \
V(Restore) \
\
V(Translate) \
V(Scale) \
V(Rotate) \
V(Skew) \
V(Transform2DAffine) \
V(TransformFullPerspective) \
\
V(ClipIntersectRect) \
V(ClipIntersectRRect) \
V(ClipIntersectPath) \
V(ClipDifferenceRect) \
V(ClipDifferenceRRect) \
V(ClipDifferencePath) \
\
V(DrawPaint) \
V(DrawColor) \
\
V(DrawLine) \
V(DrawRect) \
V(DrawOval) \
V(DrawCircle) \
V(DrawRRect) \
V(DrawDRRect) \
V(DrawArc) \
V(DrawPath) \
\
V(DrawPoints) \
V(DrawLines) \
V(DrawPolygon) \
V(DrawVertices) \
\
V(DrawImage) \
V(DrawImageWithAttr) \
V(DrawImageRect) \
V(DrawImageNine) \
V(DrawImageNineWithAttr) \
V(DrawImageLattice) \
V(DrawAtlas) \
V(DrawAtlasCulled) \
\
V(DrawSkPicture) \
V(DrawSkPictureMatrix) \
V(DrawDisplayList) \
V(DrawTextBlob) \
\
V(DrawShadow) \
V(DrawShadowTransparentOccluder)
#define DL_OP_TO_ENUM_VALUE(name) k##name,
enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) };
#undef DL_OP_TO_ENUM_VALUE
class Dispatcher;
class DisplayListBuilder;
// The base class that contains a sequence of rendering operations
// for dispatch to a Dispatcher. These objects must be instantiated
// through an instance of DisplayListBuilder::build().
class DisplayList : public SkRefCnt {
public:
static const SkSamplingOptions NearestSampling;
static const SkSamplingOptions LinearSampling;
static const SkSamplingOptions MipmapSampling;
static const SkSamplingOptions CubicSampling;
DisplayList()
: byte_count_(0),
op_count_(0),
nested_byte_count_(0),
nested_op_count_(0),
unique_id_(0),
bounds_({0, 0, 0, 0}),
bounds_cull_({0, 0, 0, 0}) {}
~DisplayList();
void Dispatch(Dispatcher& ctx) const {
uint8_t* ptr = storage_.get();
Dispatch(ctx, ptr, ptr + byte_count_);
}
void RenderTo(SkCanvas* canvas) const;
// SkPicture always includes nested bytes, but nested ops are
// only included if requested. The defaults used here for these
// accessors follow that pattern.
size_t bytes(bool nested = true) const {
return sizeof(DisplayList) + byte_count_ +
(nested ? nested_byte_count_ : 0);
}
int op_count(bool nested = false) const {
return op_count_ + (nested ? nested_op_count_ : 0);
}
uint32_t unique_id() const { return unique_id_; }
const SkRect& bounds() {
if (bounds_.width() < 0.0) {
// ComputeBounds() will leave the variable with a
// non-negative width and height
ComputeBounds();
}
return bounds_;
}
bool Equals(const DisplayList& other) const;
private:
DisplayList(uint8_t* ptr,
size_t byte_count,
int op_count,
size_t nested_byte_count,
int nested_op_count,
const SkRect& cull_rect);
std::unique_ptr<uint8_t, SkFunctionWrapper<void(void*), sk_free>> storage_;
size_t byte_count_;
int op_count_;
size_t nested_byte_count_;
int nested_op_count_;
uint32_t unique_id_;
SkRect bounds_;
// Only used for drawPaint() and drawColor()
SkRect bounds_cull_;
void ComputeBounds();
void Dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const;
friend class DisplayListBuilder;
};
// The pure virtual interface for interacting with a display list.
// This interface represents the methods used to build a list
// through the DisplayListBuilder and also the methods that will
// be invoked through the DisplayList::dispatch() method.
class Dispatcher {
public:
// MaxDrawPointsCount * sizeof(SkPoint) must be less than 1 << 32
static constexpr int kMaxDrawPointsCount = ((1 << 29) - 1);
// The following methods are nearly 1:1 with the methods on SkPaint and
// carry the same meanings. Each method sets a persistent value for the
// attribute for the rest of the display list or until it is reset by
// another method that changes the same attribute. The current set of
// attributes is not affected by |save| and |restore|.
virtual void setAntiAlias(bool aa) = 0;
virtual void setDither(bool dither) = 0;
virtual void setStyle(SkPaint::Style style) = 0;
virtual void setColor(SkColor color) = 0;
virtual void setStrokeWidth(SkScalar width) = 0;
virtual void setStrokeMiter(SkScalar limit) = 0;
virtual void setStrokeCap(SkPaint::Cap cap) = 0;
virtual void setStrokeJoin(SkPaint::Join join) = 0;
virtual void setShader(sk_sp<SkShader> shader) = 0;
virtual void setColorFilter(sk_sp<SkColorFilter> filter) = 0;
// setInvertColors does not exist in SkPaint, but is a quick way to set
// a ColorFilter that inverts the rgb values of all rendered colors.
// It is not reset by |setColorFilter|, but instead composed with that
// filter so that the color inversion happens after the ColorFilter.
virtual void setInvertColors(bool invert) = 0;
virtual void setBlendMode(SkBlendMode mode) = 0;
virtual void setBlender(sk_sp<SkBlender> blender) = 0;
virtual void setPathEffect(sk_sp<SkPathEffect> effect) = 0;
virtual void setMaskFilter(sk_sp<SkMaskFilter> filter) = 0;
// setMaskBlurFilter is a quick way to set the parameters for a
// mask blur filter without constructing an SkMaskFilter object.
// It is equivalent to setMaskFilter(SkMaskFilter::MakeBlur(style, sigma)).
// To reset the filter use setMaskFilter(nullptr).
virtual void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) = 0;
virtual void setImageFilter(sk_sp<SkImageFilter> filter) = 0;
// All of the following methods are nearly 1:1 with their counterparts
// in |SkCanvas| and have the same behavior and output.
virtual void save() = 0;
// The |restore_with_paint| parameter determines whether the existing
// rendering attributes will be applied to the save layer surface while
// rendering it back to the current surface. If the parameter is false
// then this method is equivalent to |SkCanvas::saveLayer| with a null
// paint object.
virtual void saveLayer(const SkRect* bounds, bool restore_with_paint) = 0;
virtual void restore() = 0;
virtual void translate(SkScalar tx, SkScalar ty) = 0;
virtual void scale(SkScalar sx, SkScalar sy) = 0;
virtual void rotate(SkScalar degrees) = 0;
virtual void skew(SkScalar sx, SkScalar sy) = 0;
// The transform methods all assume the following math for transforming
// an arbitrary 3D homogenous point (x, y, z, w).
// All coordinates in the rendering methods (and SkPoint and SkRect objects)
// represent a simplified coordinate (x, y, 0, 1).
// x' = x * mxx + y * mxy + z * mxz + w * mxt
// y' = x * myx + y * myy + z * myz + w * myt
// z' = x * mzx + y * mzy + z * mzz + w * mzt
// w' = x * mwx + y * mwy + z * mwz + w * mwt
// Note that for non-homogenous 2D coordinates, the last column in those
// equations is multiplied by 1 and is simply adding a translation and
// so is referred to with the final letter "t" here instead of "w".
//
// In 2D coordinates, z=0 and so the 3rd column always evaluates to 0.
//
// In non-perspective transforms, the 4th row has identity values
// and so w` = w. (i.e. w'=1 for 2d points transformed by a matrix
// with identity values in the last row).
//
// In affine 2D transforms, the 3rd and 4th row and 3rd column are all
// identity values and so z` = z (which is 0 for 2D coordinates) and
// the x` and y` equations don't see a contribution from a z coordinate
// and the w' ends up being the same as the w from the source coordinate
// (which is 1 for a 2D coordinate).
//
// Here is the math for transforming a 2D source coordinate and
// looking for the destination 2D coordinate (for a surface that
// does not have a Z buffer or track the Z coordinates in any way)
// Source coordinate = (x, y, 0, 1)
// x' = x * mxx + y * mxy + 0 * mxz + 1 * mxt
// y' = x * myx + y * myy + 0 * myz + 1 * myt
// z' = x * mzx + y * mzy + 0 * mzz + 1 * mzt
// w' = x * mwx + y * mwy + 0 * mwz + 1 * mwt
// Destination coordinate does not need z', so this reduces to:
// x' = x * mxx + y * mxy + mxt
// y' = x * myx + y * myy + myt
// w' = x * mwx + y * mwy + mwt
// Destination coordinate is (x' / w', y' / w', 0, 1)
// Note that these are the matrix values in SkMatrix which means that
// an SkMatrix contains enough data to transform a 2D source coordinate
// and place it on a 2D surface, but is otherwise not enough to continue
// concatenating with further matrices as its missing elements will not
// be able to model the interplay between the rows and columns that
// happens during a full 4x4 by 4x4 matrix multiplication.
//
// If the transform doesn't have any perspective parts (the last
// row is identity - 0, 0, 0, 1), then this further simplifies to:
// x' = x * mxx + y * mxy + mxt
// y' = x * myx + y * myy + myt
// w' = x * 0 + y * 0 + 1 = 1
//
// In short, while the full 4x4 set of matrix entries needs to be
// maintained for accumulating transform mutations accurately, the
// actual end work of transforming a single 2D coordinate (or, in
// the case of bounds transformations, 4 of them) can be accomplished
// with the 9 values from transform3x3 or SkMatrix.
//
// The only need for the w value here is for homogenous coordinates
// which only come up if the perspective elements (the 4th row) of
// a transform are non-identity. Otherwise the w always ends up
// being 1 in all calculations. If the matrix has perspecitve elements
// then the final transformed coordinates will have a w that is not 1
// and the actual coordinates are determined by dividing out that w
// factor resulting in a real-world point expressed as (x, y, z, 1).
//
// Because of the predominance of 2D affine transforms the
// 2x3 subset of the 4x4 transform matrix is special cased with
// its own dispatch method that omits the last 2 rows and the 3rd
// column. Even though a 3x3 subset is enough for transforming
// leaf coordinates as shown above, no method is provided for
// representing a 3x3 transform in the DisplayList since if there
// is perspective involved then a full 4x4 matrix should be provided
// for accurate concatenations. Providing a 3x3 method or record
// in the stream would encourage developers to prematurely subset
// a full perspective matrix.
// clang-format off
// |transform2DAffine| is equivalent to concatenating the internal
// 4x4 transform with the following row major transform matrix:
// [ mxx mxy 0 mxt ]
// [ myx myy 0 myt ]
// [ 0 0 1 0 ]
// [ 0 0 0 1 ]
virtual void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt,
SkScalar myx, SkScalar myy, SkScalar myt) = 0;
// |transformFullPerspective| is equivalent to concatenating the internal
// 4x4 transform with the following row major transform matrix:
// [ mxx mxy mxz mxt ]
// [ myx myy myz myt ]
// [ mzx mzy mzz mzt ]
// [ mwx mwy mwz mwt ]
virtual void transformFullPerspective(
SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) = 0;
// clang-format on
virtual void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) = 0;
virtual void clipRRect(const SkRRect& rrect,
SkClipOp clip_op,
bool is_aa) = 0;
virtual void clipPath(const SkPath& path, SkClipOp clip_op, bool is_aa) = 0;
// The following rendering methods all take their rendering attributes
// from the last value set by the attribute methods above (regardless
// of any |save| or |restore| operations which do not affect attributes).
// In cases where a paint object may have been optional in the SkCanvas
// method, the methods here will generally offer a boolean parameter
// which specifies whether to honor the attributes of the display list
// stream, or assume default attributes.
virtual void drawColor(SkColor color, SkBlendMode mode) = 0;
virtual void drawPaint() = 0;
virtual void drawLine(const SkPoint& p0, const SkPoint& p1) = 0;
virtual void drawRect(const SkRect& rect) = 0;
virtual void drawOval(const SkRect& bounds) = 0;
virtual void drawCircle(const SkPoint& center, SkScalar radius) = 0;
virtual void drawRRect(const SkRRect& rrect) = 0;
virtual void drawDRRect(const SkRRect& outer, const SkRRect& inner) = 0;
virtual void drawPath(const SkPath& path) = 0;
virtual void drawArc(const SkRect& oval_bounds,
SkScalar start_degrees,
SkScalar sweep_degrees,
bool use_center) = 0;
virtual void drawPoints(SkCanvas::PointMode mode,
uint32_t count,
const SkPoint points[]) = 0;
virtual void drawVertices(const sk_sp<SkVertices> vertices,
SkBlendMode mode) = 0;
virtual void drawImage(const sk_sp<SkImage> image,
const SkPoint point,
const SkSamplingOptions& sampling,
bool render_with_attributes) = 0;
virtual void drawImageRect(const sk_sp<SkImage> image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
bool render_with_attributes,
SkCanvas::SrcRectConstraint constraint) = 0;
virtual void drawImageNine(const sk_sp<SkImage> image,
const SkIRect& center,
const SkRect& dst,
SkFilterMode filter,
bool render_with_attributes) = 0;
virtual void drawImageLattice(const sk_sp<SkImage> image,
const SkCanvas::Lattice& lattice,
const SkRect& dst,
SkFilterMode filter,
bool render_with_attributes) = 0;
virtual void drawAtlas(const sk_sp<SkImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const SkColor colors[],
int count,
SkBlendMode mode,
const SkSamplingOptions& sampling,
const SkRect* cull_rect,
bool render_with_attributes) = 0;
virtual void drawPicture(const sk_sp<SkPicture> picture,
const SkMatrix* matrix,
bool render_with_attributes) = 0;
virtual void drawDisplayList(const sk_sp<DisplayList> display_list) = 0;
virtual void drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) = 0;
virtual void drawShadow(const SkPath& path,
const SkColor color,
const SkScalar elevation,
bool transparent_occluder,
SkScalar dpr) = 0;
};
/// The base class for the classes that maintain a list of
/// attributes that might be important for a number of operations
/// including which rendering attributes need to be set before
/// calling a rendering method (all |drawSomething| calls),
/// or for determining which exceptional conditions may need
/// to be accounted for in bounds calculations.
/// This class contains only protected definitions and helper methods
/// for the public classes |DisplayListAttributeFlags| and
/// |DisplayListSpecialGeometryFlags|.
class DisplayListFlags {
protected:
// A drawing operation that is not geometric in nature (but which
// may still apply a MaskFilter - see |kUsesMaskFilter_| below).
static constexpr int kIsNonGeometric_ = 0;
// A geometric operation that is defined as a fill operation
// regardless of what the current paint Style is set to.
// This flag will automatically assume |kUsesMaskFilter_|.
static constexpr int kIsFilledGeometry_ = 1 << 0;
// A geometric operation that is defined as a stroke operation
// regardless of what the current paint Style is set to.
// This flag will automatically assume |kUsesMaskFilter_|.
static constexpr int kIsStrokedGeometry_ = 1 << 1;
// A geometric operation that may be a stroke or fill operation
// depending on the current state of the paint Style attribute.
// This flag will automatically assume |kUsesMaskFilter_|.
static constexpr int kIsDrawnGeometry_ = 1 << 2;
static constexpr int kIsAnyGeometryMask_ = //
kIsFilledGeometry_ | //
kIsStrokedGeometry_ | //
kIsDrawnGeometry_;
// A primitive that floods the surface (or clip) with no
// natural bounds, such as |drawColor| or |drawPaint|.
static constexpr int kFloodsSurface_ = 1 << 3;
static constexpr int kMayHaveCaps_ = 1 << 4;
static constexpr int kMayHaveJoins_ = 1 << 5;
static constexpr int kButtCapIsSquare_ = 1 << 6;
// A geometric operation which has a path that might have
// end caps that are not rectilinear which means that square
// end caps might project further than half the stroke width
// from the geometry bounds.
// A rectilinear path such as |drawRect| will not have
// diagonal end caps. |drawLine| might have diagonal end
// caps depending on the angle of the line, and more likely
// |drawPath| will often have such end caps.
static constexpr int kMayHaveDiagonalCaps_ = 1 << 7;
// A geometric operation which has joined vertices that are
// not guaranteed to be smooth (angles of incoming and outgoing)
// segments at some joins may not have the same angle) or
// rectilinear (squares have right angles at the corners, but
// those corners will never extend past the bounding box of
// the geometry pre-transform).
// |drawRect|, |drawOval| and |drawRRect| all have well
// behaved joins, but |drawPath| might have joins that cause
// mitered extensions outside the pre-transformed bounding box.
static constexpr int kMayHaveAcuteJoins_ = 1 << 8;
static constexpr int kAnySpecialGeometryMask_ = //
kMayHaveCaps_ | kMayHaveJoins_ | kButtCapIsSquare_ | //
kMayHaveDiagonalCaps_ | kMayHaveAcuteJoins_;
// clang-format off
static constexpr int kUsesAntiAlias_ = 1 << 10;
static constexpr int kUsesDither_ = 1 << 11;
static constexpr int kUsesAlpha_ = 1 << 12;
static constexpr int kUsesColor_ = 1 << 13;
static constexpr int kUsesBlend_ = 1 << 14;
static constexpr int kUsesShader_ = 1 << 15;
static constexpr int kUsesColorFilter_ = 1 << 16;
static constexpr int kUsesPathEffect_ = 1 << 17;
static constexpr int kUsesMaskFilter_ = 1 << 18;
static constexpr int kUsesImageFilter_ = 1 << 19;
static constexpr int kIgnoresPaint_ = 1 << 30;
// clang-format on
static constexpr int kAnyAttributeMask_ = //
kUsesAntiAlias_ | kUsesDither_ | kUsesAlpha_ | kUsesColor_ | kUsesBlend_ |
kUsesShader_ | kUsesColorFilter_ | kUsesPathEffect_ | kUsesMaskFilter_ |
kUsesImageFilter_;
};
class DisplayListFlagsBase : protected DisplayListFlags {
protected:
DisplayListFlagsBase(int flags) : flags_(flags) {}
const int flags_;
bool has_any(int qFlags) const { return (flags_ & qFlags) != 0; }
bool has_all(int qFlags) const { return (flags_ & qFlags) == qFlags; }
bool has_none(int qFlags) const { return (flags_ & qFlags) == 0; }
};
/// An attribute class for advertising specific properties of
/// a geometric attribute that can affect the computation of
/// the bounds of the primitive.
class DisplayListSpecialGeometryFlags : DisplayListFlagsBase {
public:
/// The geometry may have segments that end without closing the path.
bool may_have_end_caps() const { return has_any(kMayHaveCaps_); }
/// The geometry may have segments connect non-continuously.
bool may_have_joins() const { return has_any(kMayHaveJoins_); }
/// Mainly for drawPoints(PointMode) where Butt caps are rendered as squares.
bool butt_cap_becomes_square() const { return has_any(kButtCapIsSquare_); }
/// The geometry may have segments that end on a diagonal
/// such that their end caps extend further than the default
/// |strokeWidth * 0.5| margin around the geometry.
bool may_have_diagonal_caps() const { return has_any(kMayHaveDiagonalCaps_); }
/// The geometry may have segments that meet at vertices at
/// an acute angle such that the miter joins will extend
/// further than the default |strokeWidth * 0.5| margin around
/// the geometry.
bool may_have_acute_joins() const { return has_any(kMayHaveAcuteJoins_); }
private:
DisplayListSpecialGeometryFlags(int flags) : DisplayListFlagsBase(flags) {
FML_DCHECK((flags & kAnySpecialGeometryMask_) == flags);
}
const DisplayListSpecialGeometryFlags with(int extra) const {
return extra == 0 ? *this : DisplayListSpecialGeometryFlags(flags_ | extra);
}
friend class DisplayListAttributeFlags;
};
class DisplayListAttributeFlags : DisplayListFlagsBase {
public:
const DisplayListSpecialGeometryFlags WithPathEffect(
sk_sp<SkPathEffect> effect) const {
if (is_geometric() && effect) {
SkPathEffect::DashInfo info;
if (effect->asADash(&info) == SkPathEffect::kDash_DashType) {
// A dash effect has a very simple impact. It cannot introduce any
// miter joins that weren't already present in the original path
// and it does not grow the bounds of the path, but it can add
// end caps to areas that might not have had them before so all
// we need to do is to indicate the potential for diagonal
// end caps and move on.
return special_flags_.with(kMayHaveCaps_ | kMayHaveDiagonalCaps_);
} else {
// An arbitrary path effect can introduce joins at an arbitrary
// angle and may change the geometry of the end caps
return special_flags_.with(kMayHaveCaps_ | kMayHaveDiagonalCaps_ |
kMayHaveJoins_ | kMayHaveAcuteJoins_);
}
}
return special_flags_;
}
bool ignores_paint() const { return has_any(kIgnoresPaint_); }
bool applies_anti_alias() const { return has_any(kUsesAntiAlias_); }
bool applies_dither() const { return has_any(kUsesDither_); }
bool applies_color() const { return has_any(kUsesColor_); }
bool applies_alpha() const { return has_any(kUsesAlpha_); }
bool applies_alpha_or_color() const {
return has_any(kUsesAlpha_ | kUsesColor_);
}
/// The primitive dynamically determines whether it is a stroke or fill
/// operation (or both) based on the setting of the |Style| attribute.
bool applies_style() const { return has_any(kIsDrawnGeometry_); }
/// The primitive can use any of the stroke attributes, such as
/// StrokeWidth, StrokeMiter, StrokeCap, or StrokeJoin. This
/// method will return if the primitive is defined as one that
/// strokes its geometry (such as |drawLine|) or if it is defined
/// as one that honors the Style attribute. If the Style attribute
/// is known then a more accurate answer can be returned from
/// the |is_stroked| method by supplying the actual setting of
/// the style.
// bool applies_stroke_attributes() const { return is_stroked(); }
bool applies_shader() const { return has_any(kUsesShader_); }
/// The primitive honors the current SkColorFilter, including
/// the related attribute InvertColors
bool applies_color_filter() const { return has_any(kUsesColorFilter_); }
/// The primitive honors the SkBlendMode or SkBlender
bool applies_blend() const { return has_any(kUsesBlend_); }
bool applies_path_effect() const { return has_any(kUsesPathEffect_); }
/// The primitive honors the SkMaskFilter whether set using the
/// filter object or using the convenience method |setMaskBlurFilter|
bool applies_mask_filter() const { return has_any(kUsesMaskFilter_); }
bool applies_image_filter() const { return has_any(kUsesImageFilter_); }
bool is_geometric() const { return has_any(kIsAnyGeometryMask_); }
bool always_stroked() const { return has_any(kIsStrokedGeometry_); }
bool is_stroked(SkPaint::Style style = SkPaint::Style::kStroke_Style) const {
return (
has_any(kIsStrokedGeometry_) ||
(style != SkPaint::Style::kFill_Style && has_any(kIsDrawnGeometry_)));
}
bool is_flood() const { return has_any(kFloodsSurface_); }
private:
DisplayListAttributeFlags(int flags)
: DisplayListFlagsBase(flags),
special_flags_(flags & kAnySpecialGeometryMask_) {
FML_DCHECK((flags & kIsAnyGeometryMask_) == kIsNonGeometric_ ||
(flags & kIsAnyGeometryMask_) == kIsFilledGeometry_ ||
(flags & kIsAnyGeometryMask_) == kIsStrokedGeometry_ ||
(flags & kIsAnyGeometryMask_) == kIsDrawnGeometry_);
FML_DCHECK(((flags & kAnyAttributeMask_) == 0) !=
((flags & kIgnoresPaint_) == 0));
FML_DCHECK((flags & kIsAnyGeometryMask_) != 0 ||
(flags & kAnySpecialGeometryMask_) == 0);
}
const DisplayListAttributeFlags with(int extra) const {
return extra == 0 ? *this : DisplayListAttributeFlags(flags_ | extra);
}
const DisplayListAttributeFlags without(int remove) const {
FML_DCHECK(has_all(remove));
return (flags_ & ~remove);
}
const DisplayListSpecialGeometryFlags special_flags_;
friend class DisplayListOpFlags;
};
class DisplayListOpFlags : DisplayListFlags {
public:
static const DisplayListAttributeFlags kSaveLayerFlags;
static const DisplayListAttributeFlags kSaveLayerWithPaintFlags;
static const DisplayListAttributeFlags kDrawColorFlags;
static const DisplayListAttributeFlags kDrawPaintFlags;
static const DisplayListAttributeFlags kDrawLineFlags;
// Special case flags for horizonal and vertical lines
static const DisplayListAttributeFlags kDrawHVLineFlags;
static const DisplayListAttributeFlags kDrawRectFlags;
static const DisplayListAttributeFlags kDrawOvalFlags;
static const DisplayListAttributeFlags kDrawCircleFlags;
static const DisplayListAttributeFlags kDrawRRectFlags;
static const DisplayListAttributeFlags kDrawDRRectFlags;
static const DisplayListAttributeFlags kDrawPathFlags;
static const DisplayListAttributeFlags kDrawArcNoCenterFlags;
static const DisplayListAttributeFlags kDrawArcWithCenterFlags;
static const DisplayListAttributeFlags kDrawPointsAsPointsFlags;
static const DisplayListAttributeFlags kDrawPointsAsLinesFlags;
static const DisplayListAttributeFlags kDrawPointsAsPolygonFlags;
static const DisplayListAttributeFlags kDrawVerticesFlags;
static const DisplayListAttributeFlags kDrawImageFlags;
static const DisplayListAttributeFlags kDrawImageWithPaintFlags;
static const DisplayListAttributeFlags kDrawImageRectFlags;
static const DisplayListAttributeFlags kDrawImageRectWithPaintFlags;
static const DisplayListAttributeFlags kDrawImageNineFlags;
static const DisplayListAttributeFlags kDrawImageNineWithPaintFlags;
static const DisplayListAttributeFlags kDrawImageLatticeFlags;
static const DisplayListAttributeFlags kDrawImageLatticeWithPaintFlags;
static const DisplayListAttributeFlags kDrawAtlasFlags;
static const DisplayListAttributeFlags kDrawAtlasWithPaintFlags;
static const DisplayListAttributeFlags kDrawPictureFlags;
static const DisplayListAttributeFlags kDrawPictureWithPaintFlags;
static const DisplayListAttributeFlags kDrawDisplayListFlags;
static const DisplayListAttributeFlags kDrawTextBlobFlags;
static const DisplayListAttributeFlags kDrawShadowFlags;
};
// The primary class used to build a display list. The list of methods
// here matches the list of methods invoked on a |Dispatcher|.
// If there is some code that already renders to an SkCanvas object,
// those rendering commands can be captured into a DisplayList using
// the DisplayListCanvasRecorder class.
class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt {
public:
explicit DisplayListBuilder(const SkRect& cull_rect = kMaxCullRect_);
~DisplayListBuilder();
void setAntiAlias(bool aa) override {
if (current_anti_alias_ != aa) {
onSetAntiAlias(aa);
}
}
void setDither(bool dither) override {
if (current_dither_ != dither) {
onSetDither(dither);
}
}
void setInvertColors(bool invert) override {
if (current_invert_colors_ != invert) {
onSetInvertColors(invert);
}
}
void setStrokeCap(SkPaint::Cap cap) override {
if (current_stroke_cap_ != cap) {
onSetStrokeCap(cap);
}
}
void setStrokeJoin(SkPaint::Join join) override {
if (current_stroke_join_ != join) {
onSetStrokeJoin(join);
}
}
void setStyle(SkPaint::Style style) override {
if (current_style_ != style) {
onSetStyle(style);
}
}
void setStrokeWidth(SkScalar width) override {
if (current_stroke_width_ != width) {
onSetStrokeWidth(width);
}
}
void setStrokeMiter(SkScalar limit) override {
if (current_stroke_miter_ != limit) {
onSetStrokeMiter(limit);
}
}
void setColor(SkColor color) override {
if (current_color_ != color) {
onSetColor(color);
}
}
void setBlendMode(SkBlendMode mode) override {
if (current_blender_ || current_blend_mode_ != mode) {
onSetBlendMode(mode);
}
}
void setBlender(sk_sp<SkBlender> blender) override {
if (!blender) {
setBlendMode(SkBlendMode::kSrcOver);
} else if (current_blender_ != blender) {
onSetBlender(std::move(blender));
}
}
void setShader(sk_sp<SkShader> shader) override {
if (current_shader_ != shader) {
onSetShader(std::move(shader));
}
}
void setImageFilter(sk_sp<SkImageFilter> filter) override {
if (current_image_filter_ != filter) {
onSetImageFilter(std::move(filter));
}
}
void setColorFilter(sk_sp<SkColorFilter> filter) override {
if (current_color_filter_ != filter) {
onSetColorFilter(std::move(filter));
}
}
void setPathEffect(sk_sp<SkPathEffect> effect) override {
if (current_path_effect_ != effect) {
onSetPathEffect(std::move(effect));
}
}
void setMaskFilter(sk_sp<SkMaskFilter> filter) override {
if (mask_sigma_valid(current_mask_sigma_) ||
current_mask_filter_ != filter) {
onSetMaskFilter(std::move(filter));
}
}
void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override {
if (!mask_sigma_valid(sigma)) {
// SkMastFilter::MakeBlur(invalid sigma) returns a nullptr, so we
// reset the mask filter here rather than recording the invalid values.
setMaskFilter(nullptr);
} else if (current_mask_style_ != style || current_mask_sigma_ != sigma) {
onSetMaskBlurFilter(style, sigma);
}
}
bool isAntiAlias() const { return current_anti_alias_; }
bool isDither() const { return current_dither_; }
SkPaint::Style getStyle() const { return current_style_; }
SkColor getColor() const { return current_color_; }
SkScalar getStrokeWidth() const { return current_stroke_width_; }
SkScalar getStrokeMiter() const { return current_stroke_miter_; }
SkPaint::Cap getStrokeCap() const { return current_stroke_cap_; }
SkPaint::Join getStrokeJoin() const { return current_stroke_join_; }
sk_sp<SkShader> getShader() const { return current_shader_; }
sk_sp<SkColorFilter> getColorFilter() const { return current_color_filter_; }
bool isInvertColors() const { return current_invert_colors_; }
std::optional<SkBlendMode> getBlendMode() const {
if (current_blender_) {
// The setters will turn "Mode" style blenders into "blend_mode"s
return {};
}
return current_blend_mode_;
}
sk_sp<SkBlender> getBlender() const {
return current_blender_ ? current_blender_
: SkBlender::Mode(current_blend_mode_);
}
sk_sp<SkPathEffect> getPathEffect() const { return current_path_effect_; }
sk_sp<SkMaskFilter> getMaskFilter() const {
return mask_sigma_valid(current_mask_sigma_)
? SkMaskFilter::MakeBlur(current_mask_style_,
current_mask_sigma_)
: current_mask_filter_;
}
// No utility getter for the utility setter:
// void setMaskBlurFilter (SkBlurStyle style, SkScalar sigma)
sk_sp<SkImageFilter> getImageFilter() const { return current_image_filter_; }
void save() override;
void saveLayer(const SkRect* bounds, bool restore_with_paint) override;
void restore() override;
int getSaveCount() { return save_level_ + 1; }
void translate(SkScalar tx, SkScalar ty) override;
void scale(SkScalar sx, SkScalar sy) override;
void rotate(SkScalar degrees) override;
void skew(SkScalar sx, SkScalar sy) override;
void setAttributesFromPaint(const SkPaint& paint,
const DisplayListAttributeFlags flags);
// clang-format off
// 2x3 2D affine subset of a 4x4 transform in row major order
void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt,
SkScalar myx, SkScalar myy, SkScalar myt) override;
// full 4x4 transform in row major order
void transformFullPerspective(
SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override;
// clang-format on
void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override;
void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override;
void clipPath(const SkPath& path, SkClipOp clip_op, bool is_aa) override;
void drawPaint() override;
void drawColor(SkColor color, SkBlendMode mode) override;
void drawLine(const SkPoint& p0, const SkPoint& p1) override;
void drawRect(const SkRect& rect) override;
void drawOval(const SkRect& bounds) override;
void drawCircle(const SkPoint& center, SkScalar radius) override;
void drawRRect(const SkRRect& rrect) override;
void drawDRRect(const SkRRect& outer, const SkRRect& inner) override;
void drawPath(const SkPath& path) override;
void drawArc(const SkRect& bounds,
SkScalar start,
SkScalar sweep,
bool useCenter) override;
void drawPoints(SkCanvas::PointMode mode,
uint32_t count,
const SkPoint pts[]) override;
void drawVertices(const sk_sp<SkVertices> vertices,
SkBlendMode mode) override;
void drawImage(const sk_sp<SkImage> image,
const SkPoint point,
const SkSamplingOptions& sampling,
bool render_with_attributes) override;
void drawImageRect(
const sk_sp<SkImage> image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
bool render_with_attributes,
SkCanvas::SrcRectConstraint constraint =
SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint) override;
void drawImageNine(const sk_sp<SkImage> image,
const SkIRect& center,
const SkRect& dst,
SkFilterMode filter,
bool render_with_attributes) override;
void drawImageLattice(const sk_sp<SkImage> image,
const SkCanvas::Lattice& lattice,
const SkRect& dst,
SkFilterMode filter,
bool render_with_attributes) override;
void drawAtlas(const sk_sp<SkImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const SkColor colors[],
int count,
SkBlendMode mode,
const SkSamplingOptions& sampling,
const SkRect* cullRect,
bool render_with_attributes) override;
void drawPicture(const sk_sp<SkPicture> picture,
const SkMatrix* matrix,
bool render_with_attributes) override;
void drawDisplayList(const sk_sp<DisplayList> display_list) override;
void drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) override;
void drawShadow(const SkPath& path,
const SkColor color,
const SkScalar elevation,
bool transparent_occluder,
SkScalar dpr) override;
sk_sp<DisplayList> Build();
private:
SkAutoTMalloc<uint8_t> storage_;
size_t used_ = 0;
size_t allocated_ = 0;
int op_count_ = 0;
int save_level_ = 0;
// bytes and ops from |drawPicture| and |drawDisplayList|
size_t nested_bytes_ = 0;
int nested_op_count_ = 0;
SkRect cull_rect_;
static constexpr SkRect kMaxCullRect_ =
SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F);
template <typename T, typename... Args>
void* Push(size_t extra, int op_inc, Args&&... args);
// kInvalidSigma is used to indicate that no MaskBlur is currently set.
static constexpr SkScalar kInvalidSigma = 0.0;
static bool mask_sigma_valid(SkScalar sigma) {
return SkScalarIsFinite(sigma) && sigma > 0.0;
}
void onSetAntiAlias(bool aa);
void onSetDither(bool dither);
void onSetInvertColors(bool invert);
void onSetStrokeCap(SkPaint::Cap cap);
void onSetStrokeJoin(SkPaint::Join join);
void onSetStyle(SkPaint::Style style);
void onSetStrokeWidth(SkScalar width);
void onSetStrokeMiter(SkScalar limit);
void onSetColor(SkColor color);
void onSetBlendMode(SkBlendMode mode);
void onSetBlender(sk_sp<SkBlender> blender);
void onSetShader(sk_sp<SkShader> shader);
void onSetImageFilter(sk_sp<SkImageFilter> filter);
void onSetColorFilter(sk_sp<SkColorFilter> filter);
void onSetPathEffect(sk_sp<SkPathEffect> effect);
void onSetMaskFilter(sk_sp<SkMaskFilter> filter);
void onSetMaskBlurFilter(SkBlurStyle style, SkScalar sigma);
// These values should match the defaults of the Dart Paint object.
bool current_anti_alias_ = false;
bool current_dither_ = false;
bool current_invert_colors_ = false;
SkColor current_color_ = 0xFF000000;
SkPaint::Style current_style_ = SkPaint::Style::kFill_Style;
SkScalar current_stroke_width_ = 0.0;
SkScalar current_stroke_miter_ = 4.0;
SkPaint::Cap current_stroke_cap_ = SkPaint::Cap::kButt_Cap;
SkPaint::Join current_stroke_join_ = SkPaint::Join::kMiter_Join;
// If |current_blender_| is set then |current_blend_mode_| should be ignored
SkBlendMode current_blend_mode_ = SkBlendMode::kSrcOver;
sk_sp<SkBlender> current_blender_;
sk_sp<SkShader> current_shader_;
sk_sp<SkColorFilter> current_color_filter_;
sk_sp<SkImageFilter> current_image_filter_;
sk_sp<SkPathEffect> current_path_effect_;
sk_sp<SkMaskFilter> current_mask_filter_;
SkBlurStyle current_mask_style_;
SkScalar current_mask_sigma_ = kInvalidSigma;
};
} // namespace flutter
#endif // FLUTTER_FLOW_DISPLAY_LIST_H_