blob: a3c3f8ba559b67fe159820d12736f6090fee7c72 [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_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_
#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_
#include "flutter/display_list/display_list.h"
#include "flutter/display_list/display_list_comparable.h"
#include "flutter/display_list/display_list_dispatcher.h"
#include "flutter/display_list/display_list_flags.h"
#include "flutter/display_list/types.h"
#include "flutter/fml/macros.h"
namespace flutter {
// 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,
DisplayListOpFlags {
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(const DlColorFilter* filter) override {
if (NotEquals(current_color_filter_, filter)) {
onSetColorFilter(filter);
}
}
void setPathEffect(sk_sp<SkPathEffect> effect) override {
if (current_path_effect_ != effect) {
onSetPathEffect(std::move(effect));
}
}
void setMaskFilter(const DlMaskFilter* filter) override {
if (NotEquals(current_mask_filter_, filter)) {
onSetMaskFilter(filter);
}
}
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_; }
std::shared_ptr<const DlColorFilter> 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_; }
std::shared_ptr<const DlMaskFilter> getMaskFilter() const {
return current_mask_filter_;
}
sk_sp<SkImageFilter> getImageFilter() const { return current_image_filter_; }
void save() override;
// Only the |renders_with_attributes()| option will be accepted here. Any
// other flags will be ignored and calculated anew as the DisplayList is
// built. Alternatively, use the |saveLayer(SkRect, bool)| method.
void saveLayer(const SkRect* bounds, const SaveLayerOptions options) override;
// Convenience method with just a boolean to indicate whether the saveLayer
// should apply the rendering attributes.
void saveLayer(const SkRect* bounds, bool renders_with_attributes) {
saveLayer(bounds, renders_with_attributes
? SaveLayerOptions::kWithAttributes
: SaveLayerOptions::kNoAttributes);
}
void restore() override;
int getSaveCount() { return layer_stack_.size(); }
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;
// 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;
}
struct LayerInfo {
LayerInfo(size_t save_layer_offset = 0, bool has_layer = false)
: save_layer_offset(save_layer_offset),
has_layer(has_layer),
cannot_inherit_opacity(false),
has_compatible_op(false) {}
// The offset into the memory buffer where the saveLayer DLOp record
// for this saveLayer() call is placed. This may be needed if the
// eventual restore() call has discovered important information about
// the records inside the saveLayer that may impact how the saveLayer
// is handled (e.g., |cannot_inherit_opacity| == false).
// This offset is only valid if |has_layer| is true.
size_t save_layer_offset;
bool has_layer;
bool cannot_inherit_opacity;
bool has_compatible_op;
bool is_group_opacity_compatible() const { return !cannot_inherit_opacity; }
void mark_incompatible() { cannot_inherit_opacity = true; }
// For now this only allows a single compatible op to mark the
// layer as being compatible with group opacity. If we start
// computing bounds of ops in the Builder methods then we
// can upgrade this to checking for overlapping ops.
// See https://github.com/flutter/flutter/issues/93899
void add_compatible_op() {
if (!cannot_inherit_opacity) {
if (has_compatible_op) {
cannot_inherit_opacity = true;
} else {
has_compatible_op = true;
}
}
}
};
std::vector<LayerInfo> layer_stack_;
LayerInfo* current_layer_;
// This flag indicates whether or not the current rendering attributes
// are compatible with rendering ops applying an inherited opacity.
bool current_opacity_compatibility_ = true;
// Returns the compatibility of a given blend mode for applying an
// inherited opacity value to modulate the visibility of the op.
// For now we only accept SrcOver blend modes but this could be expanded
// in the future to include other (rarely used) modes that also modulate
// the opacity of a rendering operation at the cost of a switch statement
// or lookup table.
static bool IsOpacityCompatible(SkBlendMode mode) {
return (mode == SkBlendMode::kSrcOver);
}
void UpdateCurrentOpacityCompatibility() {
current_opacity_compatibility_ = //
current_color_filter_ == nullptr && //
!current_invert_colors_ && //
current_blender_ == nullptr && //
IsOpacityCompatible(current_blend_mode_);
}
// Update the opacity compatibility flags of the current layer for an op
// that has determined its compatibility as indicated by |compatible|.
void UpdateLayerOpacityCompatibility(bool compatible) {
if (compatible) {
current_layer_->add_compatible_op();
} else {
current_layer_->mark_incompatible();
}
}
// Check for opacity compatibility for an op that may or may not use the
// current rendering attributes as indicated by |uses_blend_attribute|.
// If the flag is false then the rendering op will be able to substitute
// a default Paint object with the opacity applied using the default SrcOver
// blend mode which is always compatible with applying an inherited opacity.
void CheckLayerOpacityCompatibility(bool uses_blend_attribute = true) {
UpdateLayerOpacityCompatibility(!uses_blend_attribute ||
current_opacity_compatibility_);
}
void CheckLayerOpacityHairlineCompatibility() {
UpdateLayerOpacityCompatibility(
current_opacity_compatibility_ &&
(current_style_ == SkPaint::kFill_Style || current_stroke_width_ > 0));
}
// Check for opacity compatibility for an op that ignores the current
// attributes and uses the indicated blend |mode| to render to the layer.
// This is only used by |drawColor| currently.
void CheckLayerOpacityCompatibility(SkBlendMode mode) {
UpdateLayerOpacityCompatibility(IsOpacityCompatible(mode));
}
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(const DlColorFilter* filter);
void onSetPathEffect(sk_sp<SkPathEffect> effect);
void onSetMaskFilter(const DlMaskFilter* 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_;
std::shared_ptr<const DlColorFilter> current_color_filter_;
sk_sp<SkImageFilter> current_image_filter_;
sk_sp<SkPathEffect> current_path_effect_;
std::shared_ptr<const DlMaskFilter> current_mask_filter_;
};
} // namespace flutter
#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_