| // 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_ |