blob: 136b36c88306337a23eb242dc9da5c734515a97b [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_H_
#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_
#include <memory>
#include <optional>
#include "flutter/display_list/dl_sampling_options.h"
#include "flutter/display_list/geometry/dl_rtree.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 DlOpReceiver
// DlOpReceiver: 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 DlCanvas
// method calls and which can act as a DlOpReceiver as well
//
// Other files include various class definitions for dealing with display
// lists, such as:
// skia/dl_sk_*.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 DlOpReceiver, 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 DlOpReceiver can inherit from
// these utility classes to simplify its creation
//
// The Flutter DisplayList mechanism is used in a similar manner to the Skia
// SkPicture mechanism.
//
// A DisplayList must be created using a DisplayListBuilder using its stateless
// methods inherited from DlCanvas.
//
// A DisplayList can be read back by implementing the DlOpReceiver 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 DlSkCanvasDispatcher.
//
// 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(SetInvertColors) \
\
V(SetStrokeCap) \
V(SetStrokeJoin) \
\
V(SetStyle) \
V(SetStrokeWidth) \
V(SetStrokeMiter) \
\
V(SetColor) \
V(SetBlendMode) \
\
V(SetPodPathEffect) \
V(ClearPathEffect) \
\
V(ClearColorFilter) \
V(SetPodColorFilter) \
\
V(ClearColorSource) \
V(SetPodColorSource) \
V(SetImageColorSource) \
V(SetRuntimeEffectColorSource) \
\
V(ClearImageFilter) \
V(SetPodImageFilter) \
V(SetSharedImageFilter) \
\
V(ClearMaskFilter) \
V(SetPodMaskFilter) \
\
V(Save) \
V(SaveLayer) \
V(SaveLayerBackdrop) \
V(Restore) \
\
V(Translate) \
V(Scale) \
V(Rotate) \
V(Skew) \
V(Transform2DAffine) \
V(TransformFullPerspective) \
V(TransformReset) \
\
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(DrawAtlas) \
V(DrawAtlasCulled) \
\
V(DrawDisplayList) \
V(DrawTextBlob) \
V(DrawTextFrame) \
\
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)
#ifdef IMPELLER_ENABLE_3D
DL_OP_TO_ENUM_VALUE(SetSceneColorSource)
#endif // IMPELLER_ENABLE_3D
};
#undef DL_OP_TO_ENUM_VALUE
class DlOpReceiver;
class DisplayListBuilder;
class SaveLayerOptions {
public:
static const SaveLayerOptions kWithAttributes;
static const SaveLayerOptions kNoAttributes;
SaveLayerOptions() : flags_(0) {}
SaveLayerOptions(const SaveLayerOptions& options) : flags_(options.flags_) {}
explicit SaveLayerOptions(const SaveLayerOptions* options)
: flags_(options->flags_) {}
SaveLayerOptions without_optimizations() const {
SaveLayerOptions options;
options.fRendersWithAttributes = fRendersWithAttributes;
options.fBoundsFromCaller = fBoundsFromCaller;
return options;
}
bool renders_with_attributes() const { return fRendersWithAttributes; }
SaveLayerOptions with_renders_with_attributes() const {
SaveLayerOptions options(this);
options.fRendersWithAttributes = true;
return options;
}
bool can_distribute_opacity() const { return fCanDistributeOpacity; }
SaveLayerOptions with_can_distribute_opacity() const {
SaveLayerOptions options(this);
options.fCanDistributeOpacity = true;
return options;
}
// Returns true iff the bounds for the saveLayer operation were provided
// by the caller, otherwise the bounds will have been computed by the
// DisplayListBuilder and provided for reference.
bool bounds_from_caller() const { return fBoundsFromCaller; }
SaveLayerOptions with_bounds_from_caller() const {
SaveLayerOptions options(this);
options.fBoundsFromCaller = true;
return options;
}
SaveLayerOptions without_bounds_from_caller() const {
SaveLayerOptions options(this);
options.fBoundsFromCaller = false;
return options;
}
bool bounds_were_calculated() const { return !fBoundsFromCaller; }
// Returns true iff the bounds for the saveLayer do not fully cover the
// contained rendering operations. This will only occur if the original
// caller supplied bounds and those bounds were not a strict superset
// of the content bounds computed by the DisplayListBuilder.
bool content_is_clipped() const { return fContentIsClipped; }
SaveLayerOptions with_content_is_clipped() const {
SaveLayerOptions options(this);
options.fContentIsClipped = true;
return options;
}
SaveLayerOptions& operator=(const SaveLayerOptions& other) {
flags_ = other.flags_;
return *this;
}
bool operator==(const SaveLayerOptions& other) const {
return flags_ == other.flags_;
}
bool operator!=(const SaveLayerOptions& other) const {
return flags_ != other.flags_;
}
private:
union {
struct {
unsigned fRendersWithAttributes : 1;
unsigned fCanDistributeOpacity : 1;
unsigned fBoundsFromCaller : 1;
unsigned fContentIsClipped : 1;
};
uint32_t flags_;
};
};
// Manages a buffer allocated with malloc.
class DisplayListStorage {
public:
DisplayListStorage() = default;
DisplayListStorage(DisplayListStorage&&) = default;
uint8_t* get() const { return ptr_.get(); }
void realloc(size_t count) {
ptr_.reset(static_cast<uint8_t*>(std::realloc(ptr_.release(), count)));
FML_CHECK(ptr_);
}
private:
struct FreeDeleter {
void operator()(uint8_t* p) { std::free(p); }
};
std::unique_ptr<uint8_t, FreeDeleter> ptr_;
};
class Culler;
// The base class that contains a sequence of rendering operations
// for dispatch to a DlOpReceiver. These objects must be instantiated
// through an instance of DisplayListBuilder::build().
class DisplayList : public SkRefCnt {
public:
DisplayList();
~DisplayList();
void Dispatch(DlOpReceiver& ctx) const;
void Dispatch(DlOpReceiver& ctx, const SkRect& cull_rect) const;
void Dispatch(DlOpReceiver& ctx, const SkIRect& cull_rect) const;
// From historical behavior, SkPicture always included 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);
}
uint32_t op_count(bool nested = false) const {
return op_count_ + (nested ? nested_op_count_ : 0);
}
uint32_t total_depth() const { return total_depth_; }
uint32_t unique_id() const { return unique_id_; }
const SkRect& bounds() const { return bounds_; }
bool has_rtree() const { return rtree_ != nullptr; }
sk_sp<const DlRTree> rtree() const { return rtree_; }
bool Equals(const DisplayList* other) const;
bool Equals(const DisplayList& other) const { return Equals(&other); }
bool Equals(const sk_sp<const DisplayList>& other) const {
return Equals(other.get());
}
bool can_apply_group_opacity() const { return can_apply_group_opacity_; }
bool isUIThreadSafe() const { return is_ui_thread_safe_; }
/// @brief Indicates if there are any rendering operations in this
/// DisplayList that will modify a surface of transparent black
/// pixels.
///
/// This condition can be used to determine whether to create a cleared
/// surface, render a DisplayList into it, and then composite the
/// result into a scene. It is not uncommon for code in the engine to
/// come across such degenerate DisplayList objects when slicing up a
/// frame between platform views.
bool modifies_transparent_black() const {
return modifies_transparent_black_;
}
private:
DisplayList(DisplayListStorage&& ptr,
size_t byte_count,
uint32_t op_count,
size_t nested_byte_count,
uint32_t nested_op_count,
uint32_t total_depth,
const SkRect& bounds,
bool can_apply_group_opacity,
bool is_ui_thread_safe,
bool modifies_transparent_black,
sk_sp<const DlRTree> rtree);
static uint32_t next_unique_id();
static void DisposeOps(uint8_t* ptr, uint8_t* end);
const DisplayListStorage storage_;
const size_t byte_count_;
const uint32_t op_count_;
const size_t nested_byte_count_;
const uint32_t nested_op_count_;
const uint32_t total_depth_;
const uint32_t unique_id_;
const SkRect bounds_;
const bool can_apply_group_opacity_;
const bool is_ui_thread_safe_;
const bool modifies_transparent_black_;
const sk_sp<const DlRTree> rtree_;
void Dispatch(DlOpReceiver& ctx,
uint8_t* ptr,
uint8_t* end,
Culler& culler) const;
friend class DisplayListBuilder;
};
} // namespace flutter
#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_