blob: 061f29365df454553d02c939c4f063859c811432 [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_OPS_H_
#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_OPS_H_
#include "display_list_color_source.h"
#include "flutter/display_list/display_list.h"
#include "flutter/display_list/display_list_blend_mode.h"
#include "flutter/display_list/display_list_dispatcher.h"
#include "flutter/display_list/display_list_sampling_options.h"
#include "flutter/display_list/types.h"
#include "flutter/fml/macros.h"
namespace flutter {
// Most Ops can be bulk compared using memcmp because they contain
// only numeric values or constructs that are constructed from numeric
// values.
//
// Some contain sk_sp<> references which can also be bulk compared
// to see if they are pointing to the same reference. (Note that
// two sk_sp<> that refer to the same object are themselves ==.)
//
// Only a DLOp that wants to do a deep compare needs to override the
// DLOp::equals() method and return a value of kEqual or kNotEqual.
enum class DisplayListCompare {
// The Op is deferring comparisons to a bulk memcmp performed lazily
// across all bulk-comparable ops.
kUseBulkCompare,
// The Op provided a specific equals method that spotted a difference
kNotEqual,
// The Op provided a specific equals method that saw no differences
kEqual,
};
// "DLOpPackLabel" is just a label for the pack pragma so it can be popped
// later.
#pragma pack(push, DLOpPackLabel, 8)
// Assuming a 64-bit platform (most of our platforms at this time?)
// the following comments are a "worst case" assessment of how well
// these structures pack into memory. They may be packed more tightly
// on some of the 32-bit platforms that we see in older phones.
//
// Struct allocation in the DL memory is aligned to a void* boundary
// which means that the minimum (aligned) struct size will be 8 bytes.
// The DLOp base uses 4 bytes so each Op-specific struct gets 4 bytes
// of data for "free" and works best when it packs well into an 8-byte
// aligned size.
struct DLOp {
DisplayListOpType type : 8;
uint32_t size : 24;
DisplayListCompare equals(const DLOp* other) const {
return DisplayListCompare::kUseBulkCompare;
}
};
// 4 byte header + 4 byte payload packs into minimum 8 bytes
#define DEFINE_SET_BOOL_OP(name) \
struct Set##name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kSet##name; \
\
explicit Set##name##Op(bool value) : value(value) {} \
\
const bool value; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.set##name(value); \
} \
};
DEFINE_SET_BOOL_OP(AntiAlias)
DEFINE_SET_BOOL_OP(Dither)
DEFINE_SET_BOOL_OP(InvertColors)
#undef DEFINE_SET_BOOL_OP
// 4 byte header + 4 byte payload packs into minimum 8 bytes
#define DEFINE_SET_ENUM_OP(name) \
struct SetStroke##name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kSetStroke##name; \
\
explicit SetStroke##name##Op(DlStroke##name value) : value(value) {} \
\
const DlStroke##name value; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.setStroke##name(value); \
} \
};
DEFINE_SET_ENUM_OP(Cap)
DEFINE_SET_ENUM_OP(Join)
#undef DEFINE_SET_ENUM_OP
// 4 byte header + 4 byte payload packs into minimum 8 bytes
struct SetStyleOp final : DLOp {
static const auto kType = DisplayListOpType::kSetStyle;
explicit SetStyleOp(DlDrawStyle style) : style(style) {}
const DlDrawStyle style;
void dispatch(Dispatcher& dispatcher) const { dispatcher.setStyle(style); }
};
// 4 byte header + 4 byte payload packs into minimum 8 bytes
struct SetStrokeWidthOp final : DLOp {
static const auto kType = DisplayListOpType::kSetStrokeWidth;
explicit SetStrokeWidthOp(float width) : width(width) {}
const float width;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.setStrokeWidth(width);
}
};
// 4 byte header + 4 byte payload packs into minimum 8 bytes
struct SetStrokeMiterOp final : DLOp {
static const auto kType = DisplayListOpType::kSetStrokeMiter;
explicit SetStrokeMiterOp(float limit) : limit(limit) {}
const float limit;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.setStrokeMiter(limit);
}
};
// 4 byte header + 4 byte payload packs into minimum 8 bytes
struct SetColorOp final : DLOp {
static const auto kType = DisplayListOpType::kSetColor;
explicit SetColorOp(DlColor color) : color(color) {}
const DlColor color;
void dispatch(Dispatcher& dispatcher) const { dispatcher.setColor(color); }
};
// 4 byte header + 4 byte payload packs into minimum 8 bytes
struct SetBlendModeOp final : DLOp {
static const auto kType = DisplayListOpType::kSetBlendMode;
explicit SetBlendModeOp(DlBlendMode mode) : mode(mode) {}
const DlBlendMode mode;
void dispatch(Dispatcher& dispatcher) const { dispatcher.setBlendMode(mode); }
};
// Clear: 4 byte header + unused 4 byte payload uses 8 bytes
// (4 bytes unused)
// Set: 4 byte header + an sk_sp (ptr) uses 16 bytes due to the
// alignment of the ptr.
// (4 bytes unused)
#define DEFINE_SET_CLEAR_SKREF_OP(name, field) \
struct Clear##name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kClear##name; \
\
Clear##name##Op() {} \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.set##name(nullptr); \
} \
}; \
struct Set##name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kSet##name; \
\
explicit Set##name##Op(sk_sp<Sk##name> field) : field(std::move(field)) {} \
\
sk_sp<Sk##name> field; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.set##name(field); \
} \
};
DEFINE_SET_CLEAR_SKREF_OP(Blender, blender)
#undef DEFINE_SET_CLEAR_SKREF_OP
// Clear: 4 byte header + unused 4 byte payload uses 8 bytes
// (4 bytes unused)
// Set: 4 byte header + unused 4 byte struct padding + Dl<name>
// instance copied to the memory following the record
// yields a size and efficiency that has somewhere between
// 4 and 8 bytes unused
// SetSk: 4 byte header + an sk_sp (ptr) uses 16 bytes due to the
// alignment of the ptr.
// (4 bytes unused)
#define DEFINE_SET_CLEAR_DLATTR_OP(name, sk_name, field) \
struct Clear##name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kClear##name; \
\
Clear##name##Op() {} \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.set##name(nullptr); \
} \
}; \
struct SetPod##name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kSetPod##name; \
\
SetPod##name##Op() {} \
\
void dispatch(Dispatcher& dispatcher) const { \
const Dl##name* filter = reinterpret_cast<const Dl##name*>(this + 1); \
dispatcher.set##name(filter); \
} \
}; \
struct SetSk##name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kSetSk##name; \
\
SetSk##name##Op(sk_sp<Sk##sk_name> field) : field(field) {} \
\
sk_sp<Sk##sk_name> field; \
\
void dispatch(Dispatcher& dispatcher) const { \
DlUnknown##name dl_filter(field); \
dispatcher.set##name(&dl_filter); \
} \
};
DEFINE_SET_CLEAR_DLATTR_OP(ColorFilter, ColorFilter, filter)
DEFINE_SET_CLEAR_DLATTR_OP(ImageFilter, ImageFilter, filter)
DEFINE_SET_CLEAR_DLATTR_OP(MaskFilter, MaskFilter, filter)
DEFINE_SET_CLEAR_DLATTR_OP(ColorSource, Shader, source)
DEFINE_SET_CLEAR_DLATTR_OP(PathEffect, PathEffect, effect)
#undef DEFINE_SET_CLEAR_DLATTR_OP
// 4 byte header + 80 bytes for the embedded DlImageColorSource
// uses 84 total bytes (4 bytes unused)
struct SetImageColorSourceOp : DLOp {
static const auto kType = DisplayListOpType::kSetImageColorSource;
SetImageColorSourceOp(const DlImageColorSource* source)
: source(source->image(),
source->horizontal_tile_mode(),
source->vertical_tile_mode(),
source->sampling(),
source->matrix_ptr()) {}
const DlImageColorSource source;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.setColorSource(&source);
}
};
// 56 bytes: 4 byte header, 4 byte padding, 8 for vtable, 8 * 2 for sk_sps, 24
// for the std::vector.
struct SetRuntimeEffectColorSourceOp : DLOp {
static const auto kType = DisplayListOpType::kSetRuntimeEffectColorSource;
SetRuntimeEffectColorSourceOp(const DlRuntimeEffectColorSource* source)
: source(source->runtime_effect(),
source->samplers(),
source->uniform_data()) {}
const DlRuntimeEffectColorSource source;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.setColorSource(&source);
}
DisplayListCompare equals(const SetRuntimeEffectColorSourceOp* other) const {
return (source == other->source) ? DisplayListCompare::kEqual
: DisplayListCompare::kNotEqual;
}
};
// 4 byte header + 16 byte payload uses 24 total bytes (4 bytes unused)
struct SetSharedImageFilterOp : DLOp {
static const auto kType = DisplayListOpType::kSetSharedImageFilter;
SetSharedImageFilterOp(const DlImageFilter* filter)
: filter(filter->shared()) {}
const std::shared_ptr<DlImageFilter> filter;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.setImageFilter(filter.get());
}
DisplayListCompare equals(const SetSharedImageFilterOp* other) const {
return Equals(filter, other->filter) ? DisplayListCompare::kEqual
: DisplayListCompare::kNotEqual;
}
};
// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused)
struct SaveOp final : DLOp {
static const auto kType = DisplayListOpType::kSave;
SaveOp() {}
void dispatch(Dispatcher& dispatcher) const { dispatcher.save(); }
};
// 4 byte header + 4 byte payload packs into minimum 8 bytes
struct SaveLayerOp final : DLOp {
static const auto kType = DisplayListOpType::kSaveLayer;
explicit SaveLayerOp(const SaveLayerOptions options) : options(options) {}
SaveLayerOptions options;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.saveLayer(nullptr, options);
}
};
// 4 byte header + 20 byte payload packs evenly into 24 bytes
struct SaveLayerBoundsOp final : DLOp {
static const auto kType = DisplayListOpType::kSaveLayerBounds;
SaveLayerBoundsOp(SkRect rect, const SaveLayerOptions options)
: options(options), rect(rect) {}
SaveLayerOptions options;
const SkRect rect;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.saveLayer(&rect, options);
}
};
// 4 byte header + 20 byte payload packs into minimum 24 bytes
struct SaveLayerBackdropOp final : DLOp {
static const auto kType = DisplayListOpType::kSaveLayerBackdrop;
explicit SaveLayerBackdropOp(const SaveLayerOptions options,
const DlImageFilter* backdrop)
: options(options), backdrop(backdrop->shared()) {}
SaveLayerOptions options;
const std::shared_ptr<DlImageFilter> backdrop;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.saveLayer(nullptr, options, backdrop.get());
}
DisplayListCompare equals(const SaveLayerBackdropOp* other) const {
return options == other->options && Equals(backdrop, other->backdrop)
? DisplayListCompare::kEqual
: DisplayListCompare::kNotEqual;
}
};
// 4 byte header + 36 byte payload packs evenly into 36 bytes
struct SaveLayerBackdropBoundsOp final : DLOp {
static const auto kType = DisplayListOpType::kSaveLayerBackdropBounds;
SaveLayerBackdropBoundsOp(SkRect rect,
const SaveLayerOptions options,
const DlImageFilter* backdrop)
: options(options), rect(rect), backdrop(backdrop->shared()) {}
SaveLayerOptions options;
const SkRect rect;
const std::shared_ptr<DlImageFilter> backdrop;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.saveLayer(&rect, options, backdrop.get());
}
DisplayListCompare equals(const SaveLayerBackdropBoundsOp* other) const {
return (options == other->options && rect == other->rect &&
Equals(backdrop, other->backdrop))
? DisplayListCompare::kEqual
: DisplayListCompare::kNotEqual;
}
};
// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused)
struct RestoreOp final : DLOp {
static const auto kType = DisplayListOpType::kRestore;
RestoreOp() {}
void dispatch(Dispatcher& dispatcher) const { dispatcher.restore(); }
};
// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes
// (4 bytes unused)
struct TranslateOp final : DLOp {
static const auto kType = DisplayListOpType::kTranslate;
TranslateOp(SkScalar tx, SkScalar ty) : tx(tx), ty(ty) {}
const SkScalar tx;
const SkScalar ty;
void dispatch(Dispatcher& dispatcher) const { dispatcher.translate(tx, ty); }
};
// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes
// (4 bytes unused)
struct ScaleOp final : DLOp {
static const auto kType = DisplayListOpType::kScale;
ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {}
const SkScalar sx;
const SkScalar sy;
void dispatch(Dispatcher& dispatcher) const { dispatcher.scale(sx, sy); }
};
// 4 byte header + 4 byte payload packs into minimum 8 bytes
struct RotateOp final : DLOp {
static const auto kType = DisplayListOpType::kRotate;
explicit RotateOp(SkScalar degrees) : degrees(degrees) {}
const SkScalar degrees;
void dispatch(Dispatcher& dispatcher) const { dispatcher.rotate(degrees); }
};
// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes
// (4 bytes unused)
struct SkewOp final : DLOp {
static const auto kType = DisplayListOpType::kSkew;
SkewOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {}
const SkScalar sx;
const SkScalar sy;
void dispatch(Dispatcher& dispatcher) const { dispatcher.skew(sx, sy); }
};
// 4 byte header + 24 byte payload uses 28 bytes but is rounded up to 32 bytes
// (4 bytes unused)
struct Transform2DAffineOp final : DLOp {
static const auto kType = DisplayListOpType::kTransform2DAffine;
// clang-format off
Transform2DAffineOp(SkScalar mxx, SkScalar mxy, SkScalar mxt,
SkScalar myx, SkScalar myy, SkScalar myt)
: mxx(mxx), mxy(mxy), mxt(mxt), myx(myx), myy(myy), myt(myt) {}
// clang-format on
const SkScalar mxx, mxy, mxt;
const SkScalar myx, myy, myt;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.transform2DAffine(mxx, mxy, mxt, //
myx, myy, myt);
}
};
// 4 byte header + 64 byte payload uses 68 bytes which is rounded up to 72 bytes
// (4 bytes unused)
struct TransformFullPerspectiveOp final : DLOp {
static const auto kType = DisplayListOpType::kTransformFullPerspective;
// clang-format off
TransformFullPerspectiveOp(
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)
: mxx(mxx), mxy(mxy), mxz(mxz), mxt(mxt),
myx(myx), myy(myy), myz(myz), myt(myt),
mzx(mzx), mzy(mzy), mzz(mzz), mzt(mzt),
mwx(mwx), mwy(mwy), mwz(mwz), mwt(mwt) {}
// clang-format on
const SkScalar mxx, mxy, mxz, mxt;
const SkScalar myx, myy, myz, myt;
const SkScalar mzx, mzy, mzz, mzt;
const SkScalar mwx, mwy, mwz, mwt;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.transformFullPerspective(mxx, mxy, mxz, mxt, //
myx, myy, myz, myt, //
mzx, mzy, mzz, mzt, //
mwx, mwy, mwz, mwt);
}
};
// 4 byte header with no payload.
struct TransformResetOp final : DLOp {
static const auto kType = DisplayListOpType::kTransformReset;
TransformResetOp() = default;
void dispatch(Dispatcher& dispatcher) const { dispatcher.transformReset(); }
};
// 4 byte header + 4 byte common payload packs into minimum 8 bytes
// SkRect is 16 more bytes, which packs efficiently into 24 bytes total
// SkRRect is 52 more bytes, which rounds up to 56 bytes (4 bytes unused)
// which packs into 64 bytes total
// SkPath is 16 more bytes, which packs efficiently into 24 bytes total
//
// We could pack the clip_op and the bool both into the free 4 bytes after
// the header, but the Windows compiler keeps wanting to expand that
// packing into more bytes than needed (even when they are declared as
// packed bit fields!)
#define DEFINE_CLIP_SHAPE_OP(shapetype, clipop) \
struct Clip##clipop##shapetype##Op final : DLOp { \
static const auto kType = DisplayListOpType::kClip##clipop##shapetype; \
\
Clip##clipop##shapetype##Op(Sk##shapetype shape, bool is_aa) \
: is_aa(is_aa), shape(shape) {} \
\
const bool is_aa; \
const Sk##shapetype shape; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.clip##shapetype(shape, SkClipOp::k##clipop, is_aa); \
} \
};
DEFINE_CLIP_SHAPE_OP(Rect, Intersect)
DEFINE_CLIP_SHAPE_OP(RRect, Intersect)
DEFINE_CLIP_SHAPE_OP(Rect, Difference)
DEFINE_CLIP_SHAPE_OP(RRect, Difference)
#undef DEFINE_CLIP_SHAPE_OP
#define DEFINE_CLIP_PATH_OP(clipop) \
struct Clip##clipop##PathOp final : DLOp { \
static const auto kType = DisplayListOpType::kClip##clipop##Path; \
\
Clip##clipop##PathOp(SkPath path, bool is_aa) \
: is_aa(is_aa), path(path) {} \
\
const bool is_aa; \
const SkPath path; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.clipPath(path, SkClipOp::k##clipop, is_aa); \
} \
\
DisplayListCompare equals(const Clip##clipop##PathOp* other) const { \
return is_aa == other->is_aa && path == other->path \
? DisplayListCompare::kEqual \
: DisplayListCompare::kNotEqual; \
} \
};
DEFINE_CLIP_PATH_OP(Intersect)
DEFINE_CLIP_PATH_OP(Difference)
#undef DEFINE_CLIP_PATH_OP
// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused)
struct DrawPaintOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawPaint;
DrawPaintOp() {}
void dispatch(Dispatcher& dispatcher) const { dispatcher.drawPaint(); }
};
// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes
// (4 bytes unused)
struct DrawColorOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawColor;
DrawColorOp(DlColor color, DlBlendMode mode) : color(color), mode(mode) {}
const DlColor color;
const DlBlendMode mode;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.drawColor(color, mode);
}
};
// The common data is a 4 byte header with an unused 4 bytes
// SkRect is 16 more bytes, using 20 bytes which rounds up to 24 bytes total
// (4 bytes unused)
// SkOval is same as SkRect
// SkRRect is 52 more bytes, which packs efficiently into 56 bytes total
#define DEFINE_DRAW_1ARG_OP(op_name, arg_type, arg_name) \
struct Draw##op_name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kDraw##op_name; \
\
explicit Draw##op_name##Op(arg_type arg_name) : arg_name(arg_name) {} \
\
const arg_type arg_name; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.draw##op_name(arg_name); \
} \
};
DEFINE_DRAW_1ARG_OP(Rect, SkRect, rect)
DEFINE_DRAW_1ARG_OP(Oval, SkRect, oval)
DEFINE_DRAW_1ARG_OP(RRect, SkRRect, rrect)
#undef DEFINE_DRAW_1ARG_OP
// 4 byte header + 16 byte payload uses 20 bytes but is rounded up to 24 bytes
// (4 bytes unused)
struct DrawPathOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawPath;
explicit DrawPathOp(SkPath path) : path(path) {}
const SkPath path;
void dispatch(Dispatcher& dispatcher) const { dispatcher.drawPath(path); }
DisplayListCompare equals(const DrawPathOp* other) const {
return path == other->path ? DisplayListCompare::kEqual
: DisplayListCompare::kNotEqual;
}
};
// The common data is a 4 byte header with an unused 4 bytes
// 2 x SkPoint is 16 more bytes, using 20 bytes rounding up to 24 bytes total
// (4 bytes unused)
// SkPoint + SkScalar is 12 more bytes, packing efficiently into 16 bytes total
// 2 x SkRRect is 104 more bytes, using 108 and rounding up to 112 bytes total
// (4 bytes unused)
#define DEFINE_DRAW_2ARG_OP(op_name, type1, name1, type2, name2) \
struct Draw##op_name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kDraw##op_name; \
\
Draw##op_name##Op(type1 name1, type2 name2) \
: name1(name1), name2(name2) {} \
\
const type1 name1; \
const type2 name2; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.draw##op_name(name1, name2); \
} \
};
DEFINE_DRAW_2ARG_OP(Line, SkPoint, p0, SkPoint, p1)
DEFINE_DRAW_2ARG_OP(Circle, SkPoint, center, SkScalar, radius)
DEFINE_DRAW_2ARG_OP(DRRect, SkRRect, outer, SkRRect, inner)
#undef DEFINE_DRAW_2ARG_OP
// 4 byte header + 28 byte payload packs efficiently into 32 bytes
struct DrawArcOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawArc;
DrawArcOp(SkRect bounds, SkScalar start, SkScalar sweep, bool center)
: bounds(bounds), start(start), sweep(sweep), center(center) {}
const SkRect bounds;
const SkScalar start;
const SkScalar sweep;
const bool center;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.drawArc(bounds, start, sweep, center);
}
};
// 4 byte header + 4 byte fixed payload packs efficiently into 8 bytes
// But then there is a list of points following the structure which
// is guaranteed to be a multiple of 8 bytes (SkPoint is 8 bytes)
// so this op will always pack efficiently
// The point type is packed into 3 different OpTypes to avoid expanding
// the fixed payload beyond the 8 bytes
#define DEFINE_DRAW_POINTS_OP(name, mode) \
struct Draw##name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kDraw##name; \
\
explicit Draw##name##Op(uint32_t count) : count(count) {} \
\
const uint32_t count; \
\
void dispatch(Dispatcher& dispatcher) const { \
const SkPoint* pts = reinterpret_cast<const SkPoint*>(this + 1); \
dispatcher.drawPoints(SkCanvas::PointMode::mode, count, pts); \
} \
};
DEFINE_DRAW_POINTS_OP(Points, kPoints_PointMode);
DEFINE_DRAW_POINTS_OP(Lines, kLines_PointMode);
DEFINE_DRAW_POINTS_OP(Polygon, kPolygon_PointMode);
#undef DEFINE_DRAW_POINTS_OP
// 4 byte header + 4 byte payload packs efficiently into 8 bytes
// The DlVertices object will be pod-allocated after this structure
// and can take any number of bytes so the final efficiency will
// depend on the size of the DlVertices.
// Note that the DlVertices object ends with an array of 16-bit
// indices so the alignment can be up to 6 bytes off leading to
// up to 6 bytes of overhead
struct DrawVerticesOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawVertices;
DrawVerticesOp(DlBlendMode mode) : mode(mode) {}
const DlBlendMode mode;
void dispatch(Dispatcher& dispatcher) const {
const DlVertices* vertices = reinterpret_cast<const DlVertices*>(this + 1);
dispatcher.drawVertices(vertices, mode);
}
};
// 4 byte header + 12 byte payload packs efficiently into 16 bytes
struct DrawSkVerticesOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawSkVertices;
DrawSkVerticesOp(sk_sp<SkVertices> vertices, SkBlendMode mode)
: mode(mode), vertices(std::move(vertices)) {}
const SkBlendMode mode;
const sk_sp<SkVertices> vertices;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.drawSkVertices(vertices, mode);
}
};
// 4 byte header + 40 byte payload uses 44 bytes but is rounded up to 48 bytes
// (4 bytes unused)
#define DEFINE_DRAW_IMAGE_OP(name, with_attributes) \
struct name##Op final : DLOp { \
static const auto kType = DisplayListOpType::k##name; \
\
name##Op(const sk_sp<DlImage> image, \
const SkPoint& point, \
DlImageSampling sampling) \
: point(point), sampling(sampling), image(std::move(image)) {} \
\
const SkPoint point; \
const DlImageSampling sampling; \
const sk_sp<DlImage> image; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.drawImage(image, point, sampling, with_attributes); \
} \
};
DEFINE_DRAW_IMAGE_OP(DrawImage, false)
DEFINE_DRAW_IMAGE_OP(DrawImageWithAttr, true)
#undef DEFINE_DRAW_IMAGE_OP
// 4 byte header + 72 byte payload uses 76 bytes but is rounded up to 80 bytes
// (4 bytes unused)
struct DrawImageRectOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawImageRect;
DrawImageRectOp(const sk_sp<DlImage> image,
const SkRect& src,
const SkRect& dst,
DlImageSampling sampling,
bool render_with_attributes,
SkCanvas::SrcRectConstraint constraint)
: src(src),
dst(dst),
sampling(sampling),
render_with_attributes(render_with_attributes),
constraint(constraint),
image(std::move(image)) {}
const SkRect src;
const SkRect dst;
const DlImageSampling sampling;
const bool render_with_attributes;
const SkCanvas::SrcRectConstraint constraint;
const sk_sp<DlImage> image;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.drawImageRect(image, src, dst, sampling, render_with_attributes,
constraint);
}
};
// 4 byte header + 44 byte payload packs efficiently into 48 bytes
#define DEFINE_DRAW_IMAGE_NINE_OP(name, render_with_attributes) \
struct name##Op final : DLOp { \
static const auto kType = DisplayListOpType::k##name; \
\
name##Op(const sk_sp<DlImage> image, \
const SkIRect& center, \
const SkRect& dst, \
DlFilterMode filter) \
: center(center), dst(dst), filter(filter), image(std::move(image)) {} \
\
const SkIRect center; \
const SkRect dst; \
const DlFilterMode filter; \
const sk_sp<DlImage> image; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.drawImageNine(image, center, dst, filter, \
render_with_attributes); \
} \
};
DEFINE_DRAW_IMAGE_NINE_OP(DrawImageNine, false)
DEFINE_DRAW_IMAGE_NINE_OP(DrawImageNineWithAttr, true)
#undef DEFINE_DRAW_IMAGE_NINE_OP
// 4 byte header + 60 byte payload packs evenly into 64 bytes
struct DrawImageLatticeOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawImageLattice;
DrawImageLatticeOp(const sk_sp<DlImage> image,
int x_count,
int y_count,
int cell_count,
const SkIRect& src,
const SkRect& dst,
DlFilterMode filter,
bool with_paint)
: with_paint(with_paint),
x_count(x_count),
y_count(y_count),
cell_count(cell_count),
filter(filter),
src(src),
dst(dst),
image(std::move(image)) {}
const bool with_paint;
const int x_count;
const int y_count;
const int cell_count;
const DlFilterMode filter;
const SkIRect src;
const SkRect dst;
const sk_sp<DlImage> image;
void dispatch(Dispatcher& dispatcher) const {
const int* xDivs = reinterpret_cast<const int*>(this + 1);
const int* yDivs = reinterpret_cast<const int*>(xDivs + x_count);
const SkColor* colors =
(cell_count == 0) ? nullptr
: reinterpret_cast<const SkColor*>(yDivs + y_count);
const SkCanvas::Lattice::RectType* types =
(cell_count == 0)
? nullptr
: reinterpret_cast<const SkCanvas::Lattice::RectType*>(colors +
cell_count);
dispatcher.drawImageLattice(
image, {xDivs, yDivs, types, x_count, y_count, &src, colors}, dst,
filter, with_paint);
}
};
// 4 byte header + 40 byte payload uses 44 bytes but is rounded up to 48 bytes
// (4 bytes unused)
// Each of these is then followed by a number of lists.
// SkRSXform list is a multiple of 16 bytes so it is always packed well
// SkRect list is also a multiple of 16 bytes so it also packs well
// DlColor list only packs well if the count is even, otherwise there
// can be 4 unusued bytes at the end.
struct DrawAtlasBaseOp : DLOp {
DrawAtlasBaseOp(const sk_sp<DlImage> atlas,
int count,
DlBlendMode mode,
DlImageSampling sampling,
bool has_colors,
bool render_with_attributes)
: count(count),
mode_index(static_cast<uint16_t>(mode)),
has_colors(has_colors),
render_with_attributes(render_with_attributes),
sampling(sampling),
atlas(std::move(atlas)) {}
const int count;
const uint16_t mode_index;
const uint8_t has_colors;
const uint8_t render_with_attributes;
const DlImageSampling sampling;
const sk_sp<DlImage> atlas;
};
// Packs into 48 bytes as per DrawAtlasBaseOp
// with array data following the struct also as per DrawAtlasBaseOp
struct DrawAtlasOp final : DrawAtlasBaseOp {
static const auto kType = DisplayListOpType::kDrawAtlas;
DrawAtlasOp(const sk_sp<DlImage> atlas,
int count,
DlBlendMode mode,
DlImageSampling sampling,
bool has_colors,
bool render_with_attributes)
: DrawAtlasBaseOp(atlas,
count,
mode,
sampling,
has_colors,
render_with_attributes) {}
void dispatch(Dispatcher& dispatcher) const {
const SkRSXform* xform = reinterpret_cast<const SkRSXform*>(this + 1);
const SkRect* tex = reinterpret_cast<const SkRect*>(xform + count);
const DlColor* colors =
has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;
const DlBlendMode mode = static_cast<DlBlendMode>(mode_index);
dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
nullptr, render_with_attributes);
}
};
// Packs into 48 bytes as per DrawAtlasBaseOp plus
// an additional 16 bytes for the cull rect resulting in a total
// of 56 bytes for the Culled drawAtlas.
// Also with array data following the struct as per DrawAtlasBaseOp
struct DrawAtlasCulledOp final : DrawAtlasBaseOp {
static const auto kType = DisplayListOpType::kDrawAtlasCulled;
DrawAtlasCulledOp(const sk_sp<DlImage> atlas,
int count,
DlBlendMode mode,
DlImageSampling sampling,
bool has_colors,
const SkRect& cull_rect,
bool render_with_attributes)
: DrawAtlasBaseOp(atlas,
count,
mode,
sampling,
has_colors,
render_with_attributes),
cull_rect(cull_rect) {}
const SkRect cull_rect;
void dispatch(Dispatcher& dispatcher) const {
const SkRSXform* xform = reinterpret_cast<const SkRSXform*>(this + 1);
const SkRect* tex = reinterpret_cast<const SkRect*>(xform + count);
const DlColor* colors =
has_colors ? reinterpret_cast<const DlColor*>(tex + count) : nullptr;
const DlBlendMode mode = static_cast<DlBlendMode>(mode_index);
dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling,
&cull_rect, render_with_attributes);
}
};
// 4 byte header + 12 byte payload packs evenly into 16 bytes
struct DrawSkPictureOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawSkPicture;
DrawSkPictureOp(sk_sp<SkPicture> picture, bool render_with_attributes)
: render_with_attributes(render_with_attributes),
picture(std::move(picture)) {}
const bool render_with_attributes;
const sk_sp<SkPicture> picture;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.drawPicture(picture, nullptr, render_with_attributes);
}
};
// 4 byte header + 52 byte payload packs evenly into 56 bytes
struct DrawSkPictureMatrixOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawSkPictureMatrix;
DrawSkPictureMatrixOp(sk_sp<SkPicture> picture,
const SkMatrix matrix,
bool render_with_attributes)
: render_with_attributes(render_with_attributes),
picture(std::move(picture)),
matrix(matrix) {}
const bool render_with_attributes;
const sk_sp<SkPicture> picture;
const SkMatrix matrix;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.drawPicture(picture, &matrix, render_with_attributes);
}
};
// 4 byte header + ptr aligned payload uses 12 bytes round up to 16
// (4 bytes unused)
struct DrawDisplayListOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawDisplayList;
explicit DrawDisplayListOp(const sk_sp<DisplayList> display_list)
: display_list(std::move(display_list)) {}
sk_sp<DisplayList> display_list;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.drawDisplayList(display_list);
}
DisplayListCompare equals(const DrawDisplayListOp* other) const {
return display_list->Equals(other->display_list)
? DisplayListCompare::kEqual
: DisplayListCompare::kNotEqual;
}
};
// 4 byte header + 8 payload bytes + an aligned pointer take 24 bytes
// (4 unused to align the pointer)
struct DrawTextBlobOp final : DLOp {
static const auto kType = DisplayListOpType::kDrawTextBlob;
DrawTextBlobOp(const sk_sp<SkTextBlob> blob, SkScalar x, SkScalar y)
: x(x), y(y), blob(std::move(blob)) {}
const SkScalar x;
const SkScalar y;
const sk_sp<SkTextBlob> blob;
void dispatch(Dispatcher& dispatcher) const {
dispatcher.drawTextBlob(blob, x, y);
}
};
// 4 byte header + 28 byte payload packs evenly into 32 bytes
#define DEFINE_DRAW_SHADOW_OP(name, transparent_occluder) \
struct Draw##name##Op final : DLOp { \
static const auto kType = DisplayListOpType::kDraw##name; \
\
Draw##name##Op(const SkPath& path, \
DlColor color, \
SkScalar elevation, \
SkScalar dpr) \
: color(color), elevation(elevation), dpr(dpr), path(path) {} \
\
const DlColor color; \
const SkScalar elevation; \
const SkScalar dpr; \
const SkPath path; \
\
void dispatch(Dispatcher& dispatcher) const { \
dispatcher.drawShadow(path, color, elevation, transparent_occluder, \
dpr); \
} \
};
DEFINE_DRAW_SHADOW_OP(Shadow, false)
DEFINE_DRAW_SHADOW_OP(ShadowTransparentOccluder, true)
#undef DEFINE_DRAW_SHADOW_OP
#pragma pack(pop, DLOpPackLabel)
} // namespace flutter
#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_OPS_H_