From 63738b1350b4bf79509f8605882608747d809eba Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 23 Dec 2021 16:03:07 -0800 Subject: [PATCH] Move the major display list classes to their own translation units. No functional change. Makes the display list subsystem easier to navigate as the major classes are in their own TUs. Also avoids importing unnecessary headers when the previous kitchen sink header was imported. I've tried to remove all display list related imports and start from scratch but I may have missed some files. Minor structs and classes (like the ones in utils, ops, etc..) still don't get their own TUs though. There were [two](https://github.com/flutter/engine/pull/29562) [related](https://github.com/flutter/engine/pull/30484) changes being made to this subsystem that have since landed. So I don't think I am stepping on anyones toes with the reorganization. Happy to incorporate any work-in-progress changes being made to the this subsystem before submitting. --- ci/licenses_golden/licenses_flutter | 15 +- display_list/BUILD.gn | 13 +- display_list/display_list.cc | 1658 +---------------- display_list/display_list.h | 910 +-------- display_list/display_list_builder.cc | 760 ++++++++ display_list/display_list_builder.h | 403 ++++ display_list/display_list_canvas.h | 259 --- ...s.cc => display_list_canvas_dispatcher.cc} | 225 +-- display_list/display_list_canvas_dispatcher.h | 133 ++ display_list/display_list_canvas_recorder.cc | 231 +++ display_list/display_list_canvas_recorder.h | 146 ++ display_list/display_list_canvas_unittests.cc | 12 +- display_list/display_list_dispatcher.cc | 11 + display_list/display_list_dispatcher.h | 248 +++ display_list/display_list_flags.cc | 11 + display_list/display_list_flags.h | 295 +++ display_list/display_list_ops.cc | 11 + display_list/display_list_ops.h | 853 +++++++++ display_list/display_list_unittests.cc | 12 +- display_list/display_list_utils.cc | 2 +- display_list/display_list_utils.h | 1 + display_list/types.h | 25 + .../checkerboard_layertree_unittests.cc | 3 +- flow/layers/display_list_layer.cc | 2 +- flow/layers/display_list_layer_unittests.cc | 1 + flow/layers/physical_shape_layer.cc | 2 +- flow/layers/physical_shape_layer_unittests.cc | 2 +- flow/raster_cache_unittests.cc | 2 +- flow/testing/diff_context_test.cc | 1 + lib/ui/painting/canvas.cc | 2 + lib/ui/painting/paint.cc | 1 + lib/ui/painting/paint.h | 1 + lib/ui/painting/picture_recorder.cc | 1 + lib/ui/painting/picture_recorder.h | 2 +- 34 files changed, 3216 insertions(+), 3038 deletions(-) create mode 100644 display_list/display_list_builder.cc create mode 100644 display_list/display_list_builder.h delete mode 100644 display_list/display_list_canvas.h rename display_list/{display_list_canvas.cc => display_list_canvas_dispatcher.cc} (53%) create mode 100644 display_list/display_list_canvas_dispatcher.h create mode 100644 display_list/display_list_canvas_recorder.cc create mode 100644 display_list/display_list_canvas_recorder.h create mode 100644 display_list/display_list_dispatcher.cc create mode 100644 display_list/display_list_dispatcher.h create mode 100644 display_list/display_list_flags.cc create mode 100644 display_list/display_list_flags.h create mode 100644 display_list/display_list_ops.cc create mode 100644 display_list/display_list_ops.h create mode 100644 display_list/types.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index ac1d7935f7e55..e6a3c0997b1d6 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -34,12 +34,23 @@ FILE: ../../../flutter/common/task_runners.cc FILE: ../../../flutter/common/task_runners.h FILE: ../../../flutter/display_list/display_list.cc FILE: ../../../flutter/display_list/display_list.h -FILE: ../../../flutter/display_list/display_list_canvas.cc -FILE: ../../../flutter/display_list/display_list_canvas.h +FILE: ../../../flutter/display_list/display_list_builder.cc +FILE: ../../../flutter/display_list/display_list_builder.h +FILE: ../../../flutter/display_list/display_list_canvas_dispatcher.cc +FILE: ../../../flutter/display_list/display_list_canvas_dispatcher.h +FILE: ../../../flutter/display_list/display_list_canvas_recorder.cc +FILE: ../../../flutter/display_list/display_list_canvas_recorder.h FILE: ../../../flutter/display_list/display_list_canvas_unittests.cc +FILE: ../../../flutter/display_list/display_list_dispatcher.cc +FILE: ../../../flutter/display_list/display_list_dispatcher.h +FILE: ../../../flutter/display_list/display_list_flags.cc +FILE: ../../../flutter/display_list/display_list_flags.h +FILE: ../../../flutter/display_list/display_list_ops.cc +FILE: ../../../flutter/display_list/display_list_ops.h FILE: ../../../flutter/display_list/display_list_unittests.cc FILE: ../../../flutter/display_list/display_list_utils.cc FILE: ../../../flutter/display_list/display_list_utils.h +FILE: ../../../flutter/display_list/types.h FILE: ../../../flutter/flow/compositor_context.cc FILE: ../../../flutter/flow/compositor_context.h FILE: ../../../flutter/flow/diff_context.cc diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index dc9a457e7bd63..0e1bd03d93bc7 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -6,10 +6,19 @@ source_set("display_list") { sources = [ "display_list.cc", "display_list.h", - "display_list_canvas.cc", - "display_list_canvas.h", + "display_list_builder.cc", + "display_list_builder.h", + "display_list_canvas_dispatcher.cc", + "display_list_canvas_dispatcher.h", + "display_list_canvas_recorder.cc", + "display_list_canvas_recorder.h", + "display_list_dispatcher.cc", + "display_list_dispatcher.h", + "display_list_flags.cc", + "display_list_flags.h", "display_list_utils.cc", "display_list_utils.h", + "types.h", ] public_deps = [ diff --git a/display_list/display_list.cc b/display_list/display_list.cc index b9b6423802440..8b7540624ca5d 100644 --- a/display_list/display_list.cc +++ b/display_list/display_list.cc @@ -5,11 +5,9 @@ #include #include "flutter/display_list/display_list.h" -#include "flutter/display_list/display_list_canvas.h" +#include "flutter/display_list/display_list_canvas_dispatcher.h" +#include "flutter/display_list/display_list_ops.h" #include "flutter/display_list/display_list_utils.h" -#include "third_party/skia/include/core/SkPath.h" -#include "third_party/skia/include/core/SkRSXform.h" -#include "third_party/skia/include/core/SkTextBlob.h" namespace flutter { @@ -22,840 +20,41 @@ const SkSamplingOptions DisplayList::MipmapSampling = const SkSamplingOptions DisplayList::CubicSampling = SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f}); -// 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, -}; - -#pragma pack(push, DLOp_Alignment, 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(SkPaint::name value) : value(value) {} \ - \ - const SkPaint::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(SkPaint::Style style) : style(style) {} - - const SkPaint::Style 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(SkScalar width) : width(width) {} - - const SkScalar 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(SkScalar limit) : limit(limit) {} - - const SkScalar 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(SkColor color) : color(color) {} - - const SkColor 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(SkBlendMode mode) : mode(mode) {} - - const SkBlendMode 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 field) : field(std::move(field)) {} \ - \ - sk_sp field; \ - \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.set##name(field); \ - } \ - }; -DEFINE_SET_CLEAR_SKREF_OP(Blender, blender) -DEFINE_SET_CLEAR_SKREF_OP(Shader, shader) -DEFINE_SET_CLEAR_SKREF_OP(ImageFilter, filter) -DEFINE_SET_CLEAR_SKREF_OP(ColorFilter, filter) -DEFINE_SET_CLEAR_SKREF_OP(MaskFilter, filter) -DEFINE_SET_CLEAR_SKREF_OP(PathEffect, effect) -#undef DEFINE_SET_CLEAR_SKREF_OP - -// 4 byte header + 4 byte payload packs into minimum 8 bytes -// Note that the "blur style" is packed into the OpType to prevent -// needing an additional 8 bytes for a 4-value enum. -#define DEFINE_MASK_BLUR_FILTER_OP(name, style) \ - struct SetMaskBlurFilter##name##Op final : DLOp { \ - static const auto kType = DisplayListOpType::kSetMaskBlurFilter##name; \ - \ - explicit SetMaskBlurFilter##name##Op(SkScalar sigma) : sigma(sigma) {} \ - \ - SkScalar sigma; \ - \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.setMaskBlurFilter(style, sigma); \ - } \ - }; -DEFINE_MASK_BLUR_FILTER_OP(Normal, kNormal_SkBlurStyle) -DEFINE_MASK_BLUR_FILTER_OP(Solid, kSolid_SkBlurStyle) -DEFINE_MASK_BLUR_FILTER_OP(Inner, kInner_SkBlurStyle) -DEFINE_MASK_BLUR_FILTER_OP(Outer, kOuter_SkBlurStyle) -#undef DEFINE_MASK_BLUR_FILTER_OP - -// 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(bool with_paint) : with_paint(with_paint) {} - - bool with_paint; - - void dispatch(Dispatcher& dispatcher) const { - dispatcher.saveLayer(nullptr, with_paint); - } -}; -// 4 byte header + 20 byte payload packs evenly into 24 bytes -struct SaveLayerBoundsOp final : DLOp { - static const auto kType = DisplayListOpType::kSaveLayerBounds; - - SaveLayerBoundsOp(SkRect rect, bool with_paint) - : with_paint(with_paint), rect(rect) {} - - bool with_paint; - const SkRect rect; - - void dispatch(Dispatcher& dispatcher) const { - dispatcher.saveLayer(&rect, with_paint); - } -}; -// 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 + 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(SkColor color, SkBlendMode mode) : color(color), mode(mode) {} - - const SkColor color; - const SkBlendMode 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(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 + 12 byte payload packs efficiently into 16 bytes -struct DrawVerticesOp final : DLOp { - static const auto kType = DisplayListOpType::kDrawVertices; - - DrawVerticesOp(sk_sp vertices, SkBlendMode mode) - : mode(mode), vertices(std::move(vertices)) {} - - const SkBlendMode mode; - const sk_sp vertices; - - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawVertices(vertices, mode); - } -}; - -// 4 byte header + 36 byte payload packs efficiently into 40 bytes -#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 image, \ - const SkPoint& point, \ - const SkSamplingOptions& sampling) \ - : point(point), sampling(sampling), image(std::move(image)) {} \ - \ - const SkPoint point; \ - const SkSamplingOptions sampling; \ - const sk_sp 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 + 68 byte payload packs efficiently into 72 bytes -struct DrawImageRectOp final : DLOp { - static const auto kType = DisplayListOpType::kDrawImageRect; - - DrawImageRectOp(const sk_sp image, - const SkRect& src, - const SkRect& dst, - const SkSamplingOptions& 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 SkSamplingOptions sampling; - const bool render_with_attributes; - const SkCanvas::SrcRectConstraint constraint; - const sk_sp 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 image, \ - const SkIRect& center, \ - const SkRect& dst, \ - SkFilterMode filter) \ - : center(center), dst(dst), filter(filter), image(std::move(image)) {} \ - \ - const SkIRect center; \ - const SkRect dst; \ - const SkFilterMode filter; \ - const sk_sp 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 image, - int x_count, - int y_count, - int cell_count, - const SkIRect& src, - const SkRect& dst, - SkFilterMode 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 SkFilterMode filter; - const SkIRect src; - const SkRect dst; - const sk_sp image; - - void dispatch(Dispatcher& dispatcher) const { - const int* xDivs = reinterpret_cast(this + 1); - const int* yDivs = reinterpret_cast(xDivs + x_count); - const SkColor* colors = - (cell_count == 0) ? nullptr - : reinterpret_cast(yDivs + y_count); - const SkCanvas::Lattice::RectType* types = - (cell_count == 0) - ? nullptr - : reinterpret_cast(colors + - cell_count); - dispatcher.drawImageLattice( - image, {xDivs, yDivs, types, x_count, y_count, &src, colors}, dst, - filter, with_paint); - } -}; - -// 4 byte header + 36 byte common payload packs efficiently into 40 bytes -// 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 -// SkColor 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 atlas, - int count, - SkBlendMode mode, - const SkSamplingOptions& sampling, - bool has_colors, - bool render_with_attributes) - : count(count), - mode_index(static_cast(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 SkSamplingOptions sampling; - const sk_sp atlas; -}; - -// Packs as efficiently into 40 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 atlas, - int count, - SkBlendMode mode, - const SkSamplingOptions& 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(this + 1); - const SkRect* tex = reinterpret_cast(xform + count); - const SkColor* colors = - has_colors ? reinterpret_cast(tex + count) : nullptr; - const SkBlendMode mode = static_cast(mode_index); - dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, - nullptr, render_with_attributes); - } -}; - -// Packs efficiently into the same 40 bytes as 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 atlas, - int count, - SkBlendMode mode, - const SkSamplingOptions& 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(this + 1); - const SkRect* tex = reinterpret_cast(xform + count); - const SkColor* colors = - has_colors ? reinterpret_cast(tex + count) : nullptr; - const SkBlendMode mode = static_cast(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 picture, bool render_with_attributes) - : render_with_attributes(render_with_attributes), - picture(std::move(picture)) {} - - const bool render_with_attributes; - const sk_sp 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 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 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 rounde up to 16 -// (4 bytes unused) -struct DrawDisplayListOp final : DLOp { - static const auto kType = DisplayListOpType::kDrawDisplayList; - - explicit DrawDisplayListOp(const sk_sp display_list) - : display_list(std::move(display_list)) {} - - sk_sp display_list; - - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawDisplayList(display_list); - } -}; - -// 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 blob, SkScalar x, SkScalar y) - : x(x), y(y), blob(std::move(blob)) {} - - const SkScalar x; - const SkScalar y; - const sk_sp blob; - - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawTextBlob(blob, x, y); - } -}; +DisplayList::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}), + can_apply_group_opacity_(true) {} -// 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, \ - SkColor color, \ - SkScalar elevation, \ - SkScalar dpr) \ - : color(color), elevation(elevation), dpr(dpr), path(path) {} \ - \ - const SkColor 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 +DisplayList::DisplayList(uint8_t* ptr, + size_t byte_count, + int op_count, + size_t nested_byte_count, + int nested_op_count, + const SkRect& cull_rect, + bool can_apply_group_opacity) + : storage_(ptr), + byte_count_(byte_count), + op_count_(op_count), + nested_byte_count_(nested_byte_count), + nested_op_count_(nested_op_count), + bounds_({0, 0, -1, -1}), + bounds_cull_(cull_rect), + can_apply_group_opacity_(can_apply_group_opacity) { + static std::atomic nextID{1}; + do { + unique_id_ = nextID.fetch_add(+1, std::memory_order_relaxed); + } while (unique_id_ == 0); +} -#pragma pack(pop, DLOp_Alignment) +DisplayList::~DisplayList() { + uint8_t* ptr = storage_.get(); + DisposeOps(ptr, ptr + byte_count_); +} void DisplayList::ComputeBounds() { DisplayListBoundsCalculator calculator(&bounds_cull_); @@ -887,7 +86,7 @@ void DisplayList::Dispatch(Dispatcher& dispatcher, } } -static void DisposeOps(uint8_t* ptr, uint8_t* end) { +void DisplayList::DisposeOps(uint8_t* ptr, uint8_t* end) { while (ptr < end) { auto op = reinterpret_cast(ptr); ptr += op->size; @@ -994,789 +193,4 @@ bool DisplayList::Equals(const DisplayList& other) const { return CompareOps(ptr, ptr + byte_count_, o_ptr, o_ptr + other.byte_count_); } -DisplayList::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}), - can_apply_group_opacity_(true) {} - -DisplayList::DisplayList(uint8_t* ptr, - size_t byte_count, - int op_count, - size_t nested_byte_count, - int nested_op_count, - const SkRect& cull_rect, - bool can_apply_group_opacity) - : storage_(ptr), - byte_count_(byte_count), - op_count_(op_count), - nested_byte_count_(nested_byte_count), - nested_op_count_(nested_op_count), - bounds_({0, 0, -1, -1}), - bounds_cull_(cull_rect), - can_apply_group_opacity_(can_apply_group_opacity) { - static std::atomic nextID{1}; - do { - unique_id_ = nextID.fetch_add(+1, std::memory_order_relaxed); - } while (unique_id_ == 0); -} - -DisplayList::~DisplayList() { - uint8_t* ptr = storage_.get(); - DisposeOps(ptr, ptr + byte_count_); -} - -#define DL_BUILDER_PAGE 4096 - -// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst. -static void CopyV(void* dst) {} - -template -static void CopyV(void* dst, const S* src, int n, Rest&&... rest) { - FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0) - << "Expected " << dst << " to be aligned for at least " << alignof(S) - << " bytes."; - sk_careful_memcpy(dst, src, n * sizeof(S)); - CopyV(SkTAddOffset(dst, n * sizeof(S)), std::forward(rest)...); -} - -template -void* DisplayListBuilder::Push(size_t pod, int op_inc, Args&&... args) { - size_t size = SkAlignPtr(sizeof(T) + pod); - FML_DCHECK(size < (1 << 24)); - if (used_ + size > allocated_) { - static_assert(SkIsPow2(DL_BUILDER_PAGE), - "This math needs updating for non-pow2."); - // Next greater multiple of DL_BUILDER_PAGE. - allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1); - storage_.realloc(allocated_); - FML_DCHECK(storage_.get()); - memset(storage_.get() + used_, 0, allocated_ - used_); - } - FML_DCHECK(used_ + size <= allocated_); - auto op = reinterpret_cast(storage_.get() + used_); - used_ += size; - new (op) T{std::forward(args)...}; - op->type = T::kType; - op->size = size; - op_count_ += op_inc; - return op + 1; -} - -sk_sp DisplayListBuilder::Build() { - while (layer_stack_.size() > 1) { - restore(); - } - size_t bytes = used_; - int count = op_count_; - size_t nested_bytes = nested_bytes_; - int nested_count = nested_op_count_; - used_ = allocated_ = op_count_ = 0; - nested_bytes_ = nested_op_count_ = 0; - storage_.realloc(bytes); - bool compatible = layer_stack_.back().is_group_opacity_compatible(); - return sk_sp(new DisplayList(storage_.release(), bytes, count, - nested_bytes, nested_count, - cull_rect_, compatible)); -} - -DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect) - : cull_rect_(cull_rect) { - layer_stack_.emplace_back(); - current_layer_ = &layer_stack_.back(); -} - -DisplayListBuilder::~DisplayListBuilder() { - uint8_t* ptr = storage_.get(); - if (ptr) { - DisposeOps(ptr, ptr + used_); - } -} - -void DisplayListBuilder::onSetAntiAlias(bool aa) { - Push(0, 0, current_anti_alias_ = aa); -} -void DisplayListBuilder::onSetDither(bool dither) { - Push(0, 0, current_dither_ = dither); -} -void DisplayListBuilder::onSetInvertColors(bool invert) { - Push(0, 0, current_invert_colors_ = invert); - UpdateCurrentOpacityCompatibility(); -} -void DisplayListBuilder::onSetStrokeCap(SkPaint::Cap cap) { - Push(0, 0, current_stroke_cap_ = cap); -} -void DisplayListBuilder::onSetStrokeJoin(SkPaint::Join join) { - Push(0, 0, current_stroke_join_ = join); -} -void DisplayListBuilder::onSetStyle(SkPaint::Style style) { - Push(0, 0, current_style_ = style); -} -void DisplayListBuilder::onSetStrokeWidth(SkScalar width) { - Push(0, 0, current_stroke_width_ = width); -} -void DisplayListBuilder::onSetStrokeMiter(SkScalar limit) { - Push(0, 0, current_stroke_miter_ = limit); -} -void DisplayListBuilder::onSetColor(SkColor color) { - Push(0, 0, current_color_ = color); -} -void DisplayListBuilder::onSetBlendMode(SkBlendMode mode) { - current_blender_ = nullptr; - Push(0, 0, current_blend_mode_ = mode); - UpdateCurrentOpacityCompatibility(); -} -void DisplayListBuilder::onSetBlender(sk_sp blender) { - // setBlender(nullptr) should be redirected to setBlendMode(SrcOver) - // by the set method, if not then the following is inefficient but works - FML_DCHECK(blender); - SkPaint p; - p.setBlender(blender); - if (p.asBlendMode()) { - setBlendMode(p.asBlendMode().value()); - } else { - // |current_blender_| supersedes any value of |current_blend_mode_| - (current_blender_ = blender) // - ? Push(0, 0, std::move(blender)) - : Push(0, 0); - UpdateCurrentOpacityCompatibility(); - } -} -void DisplayListBuilder::onSetShader(sk_sp shader) { - (current_shader_ = shader) // - ? Push(0, 0, std::move(shader)) - : Push(0, 0); -} -void DisplayListBuilder::onSetImageFilter(sk_sp filter) { - (current_image_filter_ = filter) // - ? Push(0, 0, std::move(filter)) - : Push(0, 0); -} -void DisplayListBuilder::onSetColorFilter(sk_sp filter) { - (current_color_filter_ = filter) // - ? Push(0, 0, std::move(filter)) - : Push(0, 0); - UpdateCurrentOpacityCompatibility(); -} -void DisplayListBuilder::onSetPathEffect(sk_sp effect) { - (current_path_effect_ = effect) // - ? Push(0, 0, std::move(effect)) - : Push(0, 0); -} -void DisplayListBuilder::onSetMaskFilter(sk_sp filter) { - current_mask_sigma_ = kInvalidSigma; - (current_mask_filter_ = filter) // - ? Push(0, 0, std::move(filter)) - : Push(0, 0); -} -void DisplayListBuilder::onSetMaskBlurFilter(SkBlurStyle style, - SkScalar sigma) { - // Valid sigma is checked by setMaskBlurFilter - FML_DCHECK(mask_sigma_valid(sigma)); - current_mask_filter_ = nullptr; - current_mask_style_ = style; - current_mask_sigma_ = sigma; - switch (style) { - case kNormal_SkBlurStyle: - Push(0, 0, sigma); - break; - case kSolid_SkBlurStyle: - Push(0, 0, sigma); - break; - case kOuter_SkBlurStyle: - Push(0, 0, sigma); - break; - case kInner_SkBlurStyle: - Push(0, 0, sigma); - break; - } -} - -void DisplayListBuilder::setAttributesFromPaint( - const SkPaint& paint, - const DisplayListAttributeFlags flags) { - if (flags.applies_anti_alias()) { - setAntiAlias(paint.isAntiAlias()); - } - if (flags.applies_dither()) { - setDither(paint.isDither()); - } - if (flags.applies_alpha_or_color()) { - setColor(paint.getColor()); - } - if (flags.applies_blend()) { - skstd::optional mode_optional = paint.asBlendMode(); - if (mode_optional) { - setBlendMode(mode_optional.value()); - } else { - setBlender(sk_ref_sp(paint.getBlender())); - } - } - if (flags.applies_style()) { - setStyle(paint.getStyle()); - } - if (flags.is_stroked(paint.getStyle())) { - setStrokeWidth(paint.getStrokeWidth()); - setStrokeMiter(paint.getStrokeMiter()); - setStrokeCap(paint.getStrokeCap()); - setStrokeJoin(paint.getStrokeJoin()); - } - if (flags.applies_shader()) { - setShader(sk_ref_sp(paint.getShader())); - } - if (flags.applies_color_filter()) { - // invert colors is a Flutter::Paint thing, not an SkPaint thing - // we must clear it because it is a second potential color filter - // that is composed with the paint's color filter. - setInvertColors(false); - setColorFilter(sk_ref_sp(paint.getColorFilter())); - } - if (flags.applies_image_filter()) { - setImageFilter(sk_ref_sp(paint.getImageFilter())); - } - if (flags.applies_path_effect()) { - setPathEffect(sk_ref_sp(paint.getPathEffect())); - } - if (flags.applies_mask_filter()) { - setMaskFilter(sk_ref_sp(paint.getMaskFilter())); - } -} - -void DisplayListBuilder::save() { - Push(0, 1); - layer_stack_.emplace_back(); - current_layer_ = &layer_stack_.back(); -} -void DisplayListBuilder::restore() { - if (layer_stack_.size() > 1) { - // Grab the current layer info before we push the restore - // on the stack. - LayerInfo layer_info = layer_stack_.back(); - layer_stack_.pop_back(); - current_layer_ = &layer_stack_.back(); - Push(0, 1); - if (!layer_info.has_layer) { - // For regular save() ops there was no protecting layer so we have to - // accumulate the values into the enclosing layer. - if (layer_info.cannot_inherit_opacity) { - current_layer_->mark_incompatible(); - } else if (layer_info.has_compatible_op) { - current_layer_->add_compatible_op(); - } - } - } -} -void DisplayListBuilder::saveLayer(const SkRect* bounds, - bool restore_with_paint) { - bounds // - ? Push(0, 1, *bounds, restore_with_paint) - : Push(0, 1, restore_with_paint); - CheckLayerOpacityCompatibility(restore_with_paint); - layer_stack_.emplace_back(true); - current_layer_ = &layer_stack_.back(); -} - -void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { - if (SkScalarIsFinite(tx) && SkScalarIsFinite(ty) && - (tx != 0.0 || ty != 0.0)) { - Push(0, 1, tx, ty); - } -} -void DisplayListBuilder::scale(SkScalar sx, SkScalar sy) { - if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && - (sx != 1.0 || sy != 1.0)) { - Push(0, 1, sx, sy); - } -} -void DisplayListBuilder::rotate(SkScalar degrees) { - if (SkScalarMod(degrees, 360.0) != 0.0) { - Push(0, 1, degrees); - } -} -void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) { - if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && - (sx != 0.0 || sy != 0.0)) { - Push(0, 1, sx, sy); - } -} - -// clang-format off - -// 2x3 2D affine subset of a 4x4 transform in row major order -void DisplayListBuilder::transform2DAffine( - SkScalar mxx, SkScalar mxy, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myt) { - if (SkScalarsAreFinite(mxx, myx) && - SkScalarsAreFinite(mxy, myy) && - SkScalarsAreFinite(mxt, myt) && - !(mxx == 1 && mxy == 0 && mxt == 0 && - myx == 0 && myy == 1 && myt == 0)) { - Push(0, 1, - mxx, mxy, mxt, - myx, myy, myt); - } -} -// full 4x4 transform in row major order -void DisplayListBuilder::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) { - if ( mxz == 0 && - myz == 0 && - mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 && - mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) { - transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); - } else if (SkScalarsAreFinite(mxx, mxy) && SkScalarsAreFinite(mxz, mxt) && - SkScalarsAreFinite(myx, myy) && SkScalarsAreFinite(myz, myt) && - SkScalarsAreFinite(mzx, mzy) && SkScalarsAreFinite(mzz, mzt) && - SkScalarsAreFinite(mwx, mwy) && SkScalarsAreFinite(mwz, mwt)) { - Push(0, 1, - mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); - } -} - -// clang-format on - -void DisplayListBuilder::clipRect(const SkRect& rect, - SkClipOp clip_op, - bool is_aa) { - clip_op == SkClipOp::kIntersect // - ? Push(0, 1, rect, is_aa) - : Push(0, 1, rect, is_aa); -} -void DisplayListBuilder::clipRRect(const SkRRect& rrect, - SkClipOp clip_op, - bool is_aa) { - if (rrect.isRect()) { - clipRect(rrect.rect(), clip_op, is_aa); - } else { - clip_op == SkClipOp::kIntersect // - ? Push(0, 1, rrect, is_aa) - : Push(0, 1, rrect, is_aa); - } -} -void DisplayListBuilder::clipPath(const SkPath& path, - SkClipOp clip_op, - bool is_aa) { - if (!path.isInverseFillType()) { - SkRect rect; - if (path.isRect(&rect)) { - this->clipRect(rect, clip_op, is_aa); - return; - } - SkRRect rrect; - if (path.isOval(&rect)) { - rrect.setOval(rect); - this->clipRRect(rrect, clip_op, is_aa); - return; - } - if (path.isRRect(&rrect)) { - this->clipRRect(rrect, clip_op, is_aa); - return; - } - } - clip_op == SkClipOp::kIntersect // - ? Push(0, 1, path, is_aa) - : Push(0, 1, path, is_aa); -} - -void DisplayListBuilder::drawPaint() { - Push(0, 1); - CheckLayerOpacityCompatibility(); -} -void DisplayListBuilder::drawColor(SkColor color, SkBlendMode mode) { - Push(0, 1, color, mode); - CheckLayerOpacityCompatibility(mode); -} -void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) { - Push(0, 1, p0, p1); - CheckLayerOpacityCompatibility(); -} -void DisplayListBuilder::drawRect(const SkRect& rect) { - Push(0, 1, rect); - CheckLayerOpacityCompatibility(); -} -void DisplayListBuilder::drawOval(const SkRect& bounds) { - Push(0, 1, bounds); - CheckLayerOpacityCompatibility(); -} -void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) { - Push(0, 1, center, radius); - CheckLayerOpacityCompatibility(); -} -void DisplayListBuilder::drawRRect(const SkRRect& rrect) { - if (rrect.isRect()) { - drawRect(rrect.rect()); - } else if (rrect.isOval()) { - drawOval(rrect.rect()); - } else { - Push(0, 1, rrect); - CheckLayerOpacityCompatibility(); - } -} -void DisplayListBuilder::drawDRRect(const SkRRect& outer, - const SkRRect& inner) { - Push(0, 1, outer, inner); - CheckLayerOpacityCompatibility(); -} -void DisplayListBuilder::drawPath(const SkPath& path) { - Push(0, 1, path); - CheckLayerOpacityHairlineCompatibility(); -} - -void DisplayListBuilder::drawArc(const SkRect& bounds, - SkScalar start, - SkScalar sweep, - bool useCenter) { - Push(0, 1, bounds, start, sweep, useCenter); - if (useCenter) { - CheckLayerOpacityHairlineCompatibility(); - } else { - CheckLayerOpacityCompatibility(); - } -} -void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, - uint32_t count, - const SkPoint pts[]) { - void* data_ptr; - FML_DCHECK(count < kMaxDrawPointsCount); - int bytes = count * sizeof(SkPoint); - switch (mode) { - case SkCanvas::PointMode::kPoints_PointMode: - data_ptr = Push(bytes, 1, count); - break; - case SkCanvas::PointMode::kLines_PointMode: - data_ptr = Push(bytes, 1, count); - break; - case SkCanvas::PointMode::kPolygon_PointMode: - data_ptr = Push(bytes, 1, count); - break; - default: - FML_DCHECK(false); - return; - } - CopyV(data_ptr, pts, count); - // drawPoints treats every point or line (or segment of a polygon) - // as a completely separate operation meaning we cannot ensure - // distribution of group opacity without analyzing the mode and the - // bounds of every sub-primitive. - // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c - UpdateLayerOpacityCompatibility(false); -} -void DisplayListBuilder::drawVertices(const sk_sp vertices, - SkBlendMode mode) { - Push(0, 1, std::move(vertices), mode); - // DrawVertices applies its colors to the paint so we have no way - // of controlling opacity using the current paint attributes. - UpdateLayerOpacityCompatibility(false); -} - -void DisplayListBuilder::drawImage(const sk_sp image, - const SkPoint point, - const SkSamplingOptions& sampling, - bool render_with_attributes) { - render_with_attributes - ? Push(0, 1, std::move(image), point, sampling) - : Push(0, 1, std::move(image), point, sampling); - CheckLayerOpacityCompatibility(render_with_attributes); -} -void DisplayListBuilder::drawImageRect(const sk_sp image, - const SkRect& src, - const SkRect& dst, - const SkSamplingOptions& sampling, - bool render_with_attributes, - SkCanvas::SrcRectConstraint constraint) { - Push(0, 1, std::move(image), src, dst, sampling, - render_with_attributes, constraint); - CheckLayerOpacityCompatibility(render_with_attributes); -} -void DisplayListBuilder::drawImageNine(const sk_sp image, - const SkIRect& center, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) { - render_with_attributes - ? Push(0, 1, std::move(image), center, dst, - filter) - : Push(0, 1, std::move(image), center, dst, filter); - CheckLayerOpacityCompatibility(render_with_attributes); -} -void DisplayListBuilder::drawImageLattice(const sk_sp image, - const SkCanvas::Lattice& lattice, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) { - int xDivCount = lattice.fXCount; - int yDivCount = lattice.fYCount; - FML_DCHECK((lattice.fRectTypes == nullptr) || (lattice.fColors != nullptr)); - int cellCount = lattice.fRectTypes && lattice.fColors - ? (xDivCount + 1) * (yDivCount + 1) - : 0; - size_t bytes = - (xDivCount + yDivCount) * sizeof(int) + - cellCount * (sizeof(SkColor) + sizeof(SkCanvas::Lattice::RectType)); - SkIRect src = lattice.fBounds ? *lattice.fBounds : image->bounds(); - void* pod = this->Push( - bytes, 1, std::move(image), xDivCount, yDivCount, cellCount, src, dst, - filter, render_with_attributes); - CopyV(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, - lattice.fColors, cellCount, lattice.fRectTypes, cellCount); - CheckLayerOpacityCompatibility(render_with_attributes); -} -void DisplayListBuilder::drawAtlas(const sk_sp 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) { - int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); - void* data_ptr; - if (colors != nullptr) { - bytes += count * sizeof(SkColor); - if (cull_rect != nullptr) { - data_ptr = Push(bytes, 1, std::move(atlas), count, - mode, sampling, true, *cull_rect, - render_with_attributes); - } else { - data_ptr = Push(bytes, 1, std::move(atlas), count, mode, - sampling, true, render_with_attributes); - } - CopyV(data_ptr, xform, count, tex, count, colors, count); - } else { - if (cull_rect != nullptr) { - data_ptr = Push(bytes, 1, std::move(atlas), count, - mode, sampling, false, *cull_rect, - render_with_attributes); - } else { - data_ptr = Push(bytes, 1, std::move(atlas), count, mode, - sampling, false, render_with_attributes); - } - CopyV(data_ptr, xform, count, tex, count); - } - // drawAtlas treats each image as a separate operation so we cannot rely - // on it to distribute the opacity without overlap without checking all - // of the transforms and texture rectangles. - UpdateLayerOpacityCompatibility(false); -} - -void DisplayListBuilder::drawPicture(const sk_sp picture, - const SkMatrix* matrix, - bool render_with_attributes) { - matrix // - ? Push(0, 1, std::move(picture), *matrix, - render_with_attributes) - : Push(0, 1, std::move(picture), render_with_attributes); - // The non-nested op count accumulated in the |Push| method will include - // this call to |drawPicture| for non-nested op count metrics. - // But, for nested op count metrics we want the |drawPicture| call itself - // to be transparent. So we subtract 1 from our accumulated nested count to - // balance out against the 1 that was accumulated into the regular count. - // This behavior is identical to the way SkPicture computes nested op counts. - nested_op_count_ += picture->approximateOpCount(true) - 1; - nested_bytes_ += picture->approximateBytesUsed(); - CheckLayerOpacityCompatibility(render_with_attributes); -} -void DisplayListBuilder::drawDisplayList( - const sk_sp display_list) { - Push(0, 1, std::move(display_list)); - // The non-nested op count accumulated in the |Push| method will include - // this call to |drawDisplayList| for non-nested op count metrics. - // But, for nested op count metrics we want the |drawDisplayList| call itself - // to be transparent. So we subtract 1 from our accumulated nested count to - // balance out against the 1 that was accumulated into the regular count. - // This behavior is identical to the way SkPicture computes nested op counts. - nested_op_count_ += display_list->op_count(true) - 1; - nested_bytes_ += display_list->bytes(true); - UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity()); -} -void DisplayListBuilder::drawTextBlob(const sk_sp blob, - SkScalar x, - SkScalar y) { - Push(0, 1, std::move(blob), x, y); - CheckLayerOpacityCompatibility(); -} -void DisplayListBuilder::drawShadow(const SkPath& path, - const SkColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) { - transparent_occluder // - ? Push(0, 1, path, color, elevation, dpr) - : Push(0, 1, path, color, elevation, dpr); - UpdateLayerOpacityCompatibility(false); -} - -// clang-format off -// Flags common to all primitives that apply colors -#define PAINT_FLAGS (kUsesDither_ | \ - kUsesColor_ | \ - kUsesAlpha_ | \ - kUsesBlend_ | \ - kUsesShader_ | \ - kUsesColorFilter_ | \ - kUsesImageFilter_) - -// Flags common to all primitives that stroke or fill -#define STROKE_OR_FILL_FLAGS (kIsDrawnGeometry_ | \ - kUsesAntiAlias_ | \ - kUsesMaskFilter_ | \ - kUsesPathEffect_) - -// Flags common to primitives that stroke geometry -#define STROKE_FLAGS (kIsStrokedGeometry_ | \ - kUsesAntiAlias_ | \ - kUsesMaskFilter_ | \ - kUsesPathEffect_) - -// Flags common to primitives that render an image with paint attributes -#define IMAGE_FLAGS_BASE (kIsNonGeometric_ | \ - kUsesAlpha_ | \ - kUsesDither_ | \ - kUsesBlend_ | \ - kUsesColorFilter_ | \ - kUsesImageFilter_) -// clang-format on - -const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerWithPaintFlags = - DisplayListAttributeFlags(kIsNonGeometric_ | // - kUsesAlpha_ | // - kUsesBlend_ | // - kUsesColorFilter_ | // - kUsesImageFilter_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawColorFlags = - DisplayListAttributeFlags(kFloodsSurface_ | kIgnoresPaint_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawPaintFlags = - DisplayListAttributeFlags(PAINT_FLAGS | kFloodsSurface_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawHVLineFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | kMayHaveCaps_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawLineFlags = - kDrawHVLineFlags.with(kMayHaveDiagonalCaps_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawRectFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | - kMayHaveJoins_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawOvalFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawCircleFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawRRectFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawDRRectFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawPathFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | - kMayHaveCaps_ | kMayHaveDiagonalCaps_ | - kMayHaveJoins_ | kMayHaveAcuteJoins_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawArcNoCenterFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | - kMayHaveCaps_ | kMayHaveDiagonalCaps_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawArcWithCenterFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | - kMayHaveJoins_ | kMayHaveAcuteJoins_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawPointsAsPointsFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | // - kMayHaveCaps_ | kButtCapIsSquare_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawPointsAsLinesFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | // - kMayHaveCaps_ | kMayHaveDiagonalCaps_); - -// Polygon mode just draws (count-1) separate lines, no joins -const DisplayListAttributeFlags DisplayListOpFlags::kDrawPointsAsPolygonFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | // - kMayHaveCaps_ | kMayHaveDiagonalCaps_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawVerticesFlags = - DisplayListAttributeFlags(kIsNonGeometric_ | // - kUsesDither_ | // - kUsesAlpha_ | // - kUsesShader_ | // - kUsesBlend_ | // - kUsesColorFilter_ | // - kUsesImageFilter_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageWithPaintFlags = - DisplayListAttributeFlags(IMAGE_FLAGS_BASE | // - kUsesAntiAlias_ | kUsesMaskFilter_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageRectFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -const DisplayListAttributeFlags - DisplayListOpFlags::kDrawImageRectWithPaintFlags = - DisplayListAttributeFlags(IMAGE_FLAGS_BASE | // - kUsesAntiAlias_ | kUsesMaskFilter_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageNineFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -const DisplayListAttributeFlags - DisplayListOpFlags::kDrawImageNineWithPaintFlags = - DisplayListAttributeFlags(IMAGE_FLAGS_BASE); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageLatticeFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -const DisplayListAttributeFlags - DisplayListOpFlags::kDrawImageLatticeWithPaintFlags = - DisplayListAttributeFlags(IMAGE_FLAGS_BASE); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasWithPaintFlags = - DisplayListAttributeFlags(IMAGE_FLAGS_BASE); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureWithPaintFlags = - kSaveLayerWithPaintFlags; - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawDisplayListFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawTextBlobFlags = - DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | - kMayHaveJoins_) - .without(kUsesAntiAlias_); - -const DisplayListAttributeFlags DisplayListOpFlags::kDrawShadowFlags = - DisplayListAttributeFlags(kIgnoresPaint_); - -#undef PAINT_FLAGS -#undef STROKE_OR_FILL_FLAGS -#undef STROKE_FLAGS -#undef IMAGE_FLAGS_BASE - } // namespace flutter diff --git a/display_list/display_list.h b/display_list/display_list.h index 51015a4e4038f..ee97305105cce 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -7,18 +7,8 @@ #include +#include "flutter/display_list/types.h" #include "flutter/fml/logging.h" -#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" // The Flutter DisplayList mechanism encapsulates a persistent sequence of // rendering operations. @@ -171,6 +161,7 @@ class DisplayList : public SkRefCnt { static const SkSamplingOptions CubicSampling; DisplayList(); + ~DisplayList(); void Dispatch(Dispatcher& ctx) const { @@ -187,9 +178,11 @@ class DisplayList : public SkRefCnt { 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() { @@ -205,6 +198,8 @@ class DisplayList : public SkRefCnt { bool can_apply_group_opacity() { return can_apply_group_opacity_; } + static void DisposeOps(uint8_t* ptr, uint8_t* end); + private: DisplayList(uint8_t* ptr, size_t byte_count, @@ -235,899 +230,6 @@ class DisplayList : public SkRefCnt { 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 shader) = 0; - virtual void setColorFilter(sk_sp 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 blender) = 0; - virtual void setPathEffect(sk_sp effect) = 0; - virtual void setMaskFilter(sk_sp 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 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 vertices, - SkBlendMode mode) = 0; - virtual void drawImage(const sk_sp image, - const SkPoint point, - const SkSamplingOptions& sampling, - bool render_with_attributes) = 0; - virtual void drawImageRect(const sk_sp image, - const SkRect& src, - const SkRect& dst, - const SkSamplingOptions& sampling, - bool render_with_attributes, - SkCanvas::SrcRectConstraint constraint) = 0; - virtual void drawImageNine(const sk_sp image, - const SkIRect& center, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) = 0; - virtual void drawImageLattice(const sk_sp image, - const SkCanvas::Lattice& lattice, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) = 0; - virtual void drawAtlas(const sk_sp 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 picture, - const SkMatrix* matrix, - bool render_with_attributes) = 0; - virtual void drawDisplayList(const sk_sp display_list) = 0; - virtual void drawTextBlob(const sk_sp 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; - - // Some ops have an optional paint argument. If the version - // stored in the DisplayList ignores the paint, but there - // is an option to render the same op with a paint then - // both of the following flags are set to indicate that - // a default paint object can be constructed when rendering - // the op to carry information imposed from outside the - // DisplayList (for example, the opacity override). - 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: - explicit 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: - explicit 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 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: - explicit 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 DisplayListAttributeFlags(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, - 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 blender) override { - if (!blender) { - setBlendMode(SkBlendMode::kSrcOver); - } else if (current_blender_ != blender) { - onSetBlender(std::move(blender)); - } - } - void setShader(sk_sp shader) override { - if (current_shader_ != shader) { - onSetShader(std::move(shader)); - } - } - void setImageFilter(sk_sp filter) override { - if (current_image_filter_ != filter) { - onSetImageFilter(std::move(filter)); - } - } - void setColorFilter(sk_sp filter) override { - if (current_color_filter_ != filter) { - onSetColorFilter(std::move(filter)); - } - } - void setPathEffect(sk_sp effect) override { - if (current_path_effect_ != effect) { - onSetPathEffect(std::move(effect)); - } - } - void setMaskFilter(sk_sp 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 getShader() const { return current_shader_; } - sk_sp getColorFilter() const { return current_color_filter_; } - bool isInvertColors() const { return current_invert_colors_; } - std::optional getBlendMode() const { - if (current_blender_) { - // The setters will turn "Mode" style blenders into "blend_mode"s - return {}; - } - return current_blend_mode_; - } - sk_sp getBlender() const { - return current_blender_ ? current_blender_ - : SkBlender::Mode(current_blend_mode_); - } - sk_sp getPathEffect() const { return current_path_effect_; } - sk_sp 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 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 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 vertices, - SkBlendMode mode) override; - void drawImage(const sk_sp image, - const SkPoint point, - const SkSamplingOptions& sampling, - bool render_with_attributes) override; - void drawImageRect( - const sk_sp 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 image, - const SkIRect& center, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) override; - void drawImageLattice(const sk_sp image, - const SkCanvas::Lattice& lattice, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) override; - void drawAtlas(const sk_sp 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 picture, - const SkMatrix* matrix, - bool render_with_attributes) override; - void drawDisplayList(const sk_sp display_list) override; - void drawTextBlob(const sk_sp 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 Build(); - - private: - SkAutoTMalloc 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 - 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(bool has_layer = false) - : has_layer(has_layer), - cannot_inherit_opacity(false), - has_compatible_op(false) {} - - 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 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 blender); - void onSetShader(sk_sp shader); - void onSetImageFilter(sk_sp filter); - void onSetColorFilter(sk_sp filter); - void onSetPathEffect(sk_sp effect); - void onSetMaskFilter(sk_sp 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 current_blender_; - sk_sp current_shader_; - sk_sp current_color_filter_; - sk_sp current_image_filter_; - sk_sp current_path_effect_; - sk_sp current_mask_filter_; - SkBlurStyle current_mask_style_; - SkScalar current_mask_sigma_ = kInvalidSigma; -}; - } // namespace flutter #endif // FLUTTER_FLOW_DISPLAY_LIST_H_ diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc new file mode 100644 index 0000000000000..2484214ee541d --- /dev/null +++ b/display_list/display_list_builder.cc @@ -0,0 +1,760 @@ +// 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. + +#include "flutter/display_list/display_list_builder.h" + +#include "flutter/display_list/display_list_ops.h" + +namespace flutter { + +#define DL_BUILDER_PAGE 4096 + +// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst. +static void CopyV(void* dst) {} + +template +static void CopyV(void* dst, const S* src, int n, Rest&&... rest) { + FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0) + << "Expected " << dst << " to be aligned for at least " << alignof(S) + << " bytes."; + sk_careful_memcpy(dst, src, n * sizeof(S)); + CopyV(SkTAddOffset(dst, n * sizeof(S)), std::forward(rest)...); +} + +template +void* DisplayListBuilder::Push(size_t pod, int op_inc, Args&&... args) { + size_t size = SkAlignPtr(sizeof(T) + pod); + FML_DCHECK(size < (1 << 24)); + if (used_ + size > allocated_) { + static_assert(SkIsPow2(DL_BUILDER_PAGE), + "This math needs updating for non-pow2."); + // Next greater multiple of DL_BUILDER_PAGE. + allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1); + storage_.realloc(allocated_); + FML_DCHECK(storage_.get()); + memset(storage_.get() + used_, 0, allocated_ - used_); + } + FML_DCHECK(used_ + size <= allocated_); + auto op = reinterpret_cast(storage_.get() + used_); + used_ += size; + new (op) T{std::forward(args)...}; + op->type = T::kType; + op->size = size; + op_count_ += op_inc; + return op + 1; +} + +sk_sp DisplayListBuilder::Build() { + while (layer_stack_.size() > 1) { + restore(); + } + size_t bytes = used_; + int count = op_count_; + size_t nested_bytes = nested_bytes_; + int nested_count = nested_op_count_; + used_ = allocated_ = op_count_ = 0; + nested_bytes_ = nested_op_count_ = 0; + storage_.realloc(bytes); + bool compatible = layer_stack_.back().is_group_opacity_compatible(); + return sk_sp(new DisplayList(storage_.release(), bytes, count, + nested_bytes, nested_count, + cull_rect_, compatible)); +} + +DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect) + : cull_rect_(cull_rect) { + layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); +} + +DisplayListBuilder::~DisplayListBuilder() { + uint8_t* ptr = storage_.get(); + if (ptr) { + DisplayList::DisposeOps(ptr, ptr + used_); + } +} + +void DisplayListBuilder::onSetAntiAlias(bool aa) { + Push(0, 0, current_anti_alias_ = aa); +} +void DisplayListBuilder::onSetDither(bool dither) { + Push(0, 0, current_dither_ = dither); +} +void DisplayListBuilder::onSetInvertColors(bool invert) { + Push(0, 0, current_invert_colors_ = invert); + UpdateCurrentOpacityCompatibility(); +} +void DisplayListBuilder::onSetStrokeCap(SkPaint::Cap cap) { + Push(0, 0, current_stroke_cap_ = cap); +} +void DisplayListBuilder::onSetStrokeJoin(SkPaint::Join join) { + Push(0, 0, current_stroke_join_ = join); +} +void DisplayListBuilder::onSetStyle(SkPaint::Style style) { + Push(0, 0, current_style_ = style); +} +void DisplayListBuilder::onSetStrokeWidth(SkScalar width) { + Push(0, 0, current_stroke_width_ = width); +} +void DisplayListBuilder::onSetStrokeMiter(SkScalar limit) { + Push(0, 0, current_stroke_miter_ = limit); +} +void DisplayListBuilder::onSetColor(SkColor color) { + Push(0, 0, current_color_ = color); +} +void DisplayListBuilder::onSetBlendMode(SkBlendMode mode) { + current_blender_ = nullptr; + Push(0, 0, current_blend_mode_ = mode); + UpdateCurrentOpacityCompatibility(); +} +void DisplayListBuilder::onSetBlender(sk_sp blender) { + // setBlender(nullptr) should be redirected to setBlendMode(SrcOver) + // by the set method, if not then the following is inefficient but works + FML_DCHECK(blender); + SkPaint p; + p.setBlender(blender); + if (p.asBlendMode()) { + setBlendMode(p.asBlendMode().value()); + } else { + // |current_blender_| supersedes any value of |current_blend_mode_| + (current_blender_ = blender) // + ? Push(0, 0, std::move(blender)) + : Push(0, 0); + UpdateCurrentOpacityCompatibility(); + } +} +void DisplayListBuilder::onSetShader(sk_sp shader) { + (current_shader_ = shader) // + ? Push(0, 0, std::move(shader)) + : Push(0, 0); +} +void DisplayListBuilder::onSetImageFilter(sk_sp filter) { + (current_image_filter_ = filter) // + ? Push(0, 0, std::move(filter)) + : Push(0, 0); +} +void DisplayListBuilder::onSetColorFilter(sk_sp filter) { + (current_color_filter_ = filter) // + ? Push(0, 0, std::move(filter)) + : Push(0, 0); + UpdateCurrentOpacityCompatibility(); +} +void DisplayListBuilder::onSetPathEffect(sk_sp effect) { + (current_path_effect_ = effect) // + ? Push(0, 0, std::move(effect)) + : Push(0, 0); +} +void DisplayListBuilder::onSetMaskFilter(sk_sp filter) { + current_mask_sigma_ = kInvalidSigma; + (current_mask_filter_ = filter) // + ? Push(0, 0, std::move(filter)) + : Push(0, 0); +} +void DisplayListBuilder::onSetMaskBlurFilter(SkBlurStyle style, + SkScalar sigma) { + // Valid sigma is checked by setMaskBlurFilter + FML_DCHECK(mask_sigma_valid(sigma)); + current_mask_filter_ = nullptr; + current_mask_style_ = style; + current_mask_sigma_ = sigma; + switch (style) { + case kNormal_SkBlurStyle: + Push(0, 0, sigma); + break; + case kSolid_SkBlurStyle: + Push(0, 0, sigma); + break; + case kOuter_SkBlurStyle: + Push(0, 0, sigma); + break; + case kInner_SkBlurStyle: + Push(0, 0, sigma); + break; + } +} + +void DisplayListBuilder::setAttributesFromPaint( + const SkPaint& paint, + const DisplayListAttributeFlags flags) { + if (flags.applies_anti_alias()) { + setAntiAlias(paint.isAntiAlias()); + } + if (flags.applies_dither()) { + setDither(paint.isDither()); + } + if (flags.applies_alpha_or_color()) { + setColor(paint.getColor()); + } + if (flags.applies_blend()) { + skstd::optional mode_optional = paint.asBlendMode(); + if (mode_optional) { + setBlendMode(mode_optional.value()); + } else { + setBlender(sk_ref_sp(paint.getBlender())); + } + } + if (flags.applies_style()) { + setStyle(paint.getStyle()); + } + if (flags.is_stroked(paint.getStyle())) { + setStrokeWidth(paint.getStrokeWidth()); + setStrokeMiter(paint.getStrokeMiter()); + setStrokeCap(paint.getStrokeCap()); + setStrokeJoin(paint.getStrokeJoin()); + } + if (flags.applies_shader()) { + setShader(sk_ref_sp(paint.getShader())); + } + if (flags.applies_color_filter()) { + // invert colors is a Flutter::Paint thing, not an SkPaint thing + // we must clear it because it is a second potential color filter + // that is composed with the paint's color filter. + setInvertColors(false); + setColorFilter(sk_ref_sp(paint.getColorFilter())); + } + if (flags.applies_image_filter()) { + setImageFilter(sk_ref_sp(paint.getImageFilter())); + } + if (flags.applies_path_effect()) { + setPathEffect(sk_ref_sp(paint.getPathEffect())); + } + if (flags.applies_mask_filter()) { + setMaskFilter(sk_ref_sp(paint.getMaskFilter())); + } +} + +void DisplayListBuilder::save() { + Push(0, 1); + layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); +} +void DisplayListBuilder::restore() { + if (layer_stack_.size() > 1) { + // Grab the current layer info before we push the restore + // on the stack. + LayerInfo layer_info = layer_stack_.back(); + layer_stack_.pop_back(); + current_layer_ = &layer_stack_.back(); + Push(0, 1); + if (!layer_info.has_layer) { + // For regular save() ops there was no protecting layer so we have to + // accumulate the values into the enclosing layer. + if (layer_info.cannot_inherit_opacity) { + current_layer_->mark_incompatible(); + } else if (layer_info.has_compatible_op) { + current_layer_->add_compatible_op(); + } + } + } +} +void DisplayListBuilder::saveLayer(const SkRect* bounds, + bool restore_with_paint) { + bounds // + ? Push(0, 1, *bounds, restore_with_paint) + : Push(0, 1, restore_with_paint); + CheckLayerOpacityCompatibility(restore_with_paint); + layer_stack_.emplace_back(true); + current_layer_ = &layer_stack_.back(); +} + +void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { + if (SkScalarIsFinite(tx) && SkScalarIsFinite(ty) && + (tx != 0.0 || ty != 0.0)) { + Push(0, 1, tx, ty); + } +} +void DisplayListBuilder::scale(SkScalar sx, SkScalar sy) { + if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && + (sx != 1.0 || sy != 1.0)) { + Push(0, 1, sx, sy); + } +} +void DisplayListBuilder::rotate(SkScalar degrees) { + if (SkScalarMod(degrees, 360.0) != 0.0) { + Push(0, 1, degrees); + } +} +void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) { + if (SkScalarIsFinite(sx) && SkScalarIsFinite(sy) && + (sx != 0.0 || sy != 0.0)) { + Push(0, 1, sx, sy); + } +} + +// clang-format off + +// 2x3 2D affine subset of a 4x4 transform in row major order +void DisplayListBuilder::transform2DAffine( + SkScalar mxx, SkScalar mxy, SkScalar mxt, + SkScalar myx, SkScalar myy, SkScalar myt) { + if (SkScalarsAreFinite(mxx, myx) && + SkScalarsAreFinite(mxy, myy) && + SkScalarsAreFinite(mxt, myt) && + !(mxx == 1 && mxy == 0 && mxt == 0 && + myx == 0 && myy == 1 && myt == 0)) { + Push(0, 1, + mxx, mxy, mxt, + myx, myy, myt); + } +} +// full 4x4 transform in row major order +void DisplayListBuilder::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) { + if ( mxz == 0 && + myz == 0 && + mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 && + mwx == 0 && mwy == 0 && mwz == 0 && mwt == 1) { + transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); + } else if (SkScalarsAreFinite(mxx, mxy) && SkScalarsAreFinite(mxz, mxt) && + SkScalarsAreFinite(myx, myy) && SkScalarsAreFinite(myz, myt) && + SkScalarsAreFinite(mzx, mzy) && SkScalarsAreFinite(mzz, mzt) && + SkScalarsAreFinite(mwx, mwy) && SkScalarsAreFinite(mwz, mwt)) { + Push(0, 1, + mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); + } +} + +// clang-format on + +void DisplayListBuilder::clipRect(const SkRect& rect, + SkClipOp clip_op, + bool is_aa) { + clip_op == SkClipOp::kIntersect // + ? Push(0, 1, rect, is_aa) + : Push(0, 1, rect, is_aa); +} +void DisplayListBuilder::clipRRect(const SkRRect& rrect, + SkClipOp clip_op, + bool is_aa) { + if (rrect.isRect()) { + clipRect(rrect.rect(), clip_op, is_aa); + } else { + clip_op == SkClipOp::kIntersect // + ? Push(0, 1, rrect, is_aa) + : Push(0, 1, rrect, is_aa); + } +} +void DisplayListBuilder::clipPath(const SkPath& path, + SkClipOp clip_op, + bool is_aa) { + if (!path.isInverseFillType()) { + SkRect rect; + if (path.isRect(&rect)) { + this->clipRect(rect, clip_op, is_aa); + return; + } + SkRRect rrect; + if (path.isOval(&rect)) { + rrect.setOval(rect); + this->clipRRect(rrect, clip_op, is_aa); + return; + } + if (path.isRRect(&rrect)) { + this->clipRRect(rrect, clip_op, is_aa); + return; + } + } + clip_op == SkClipOp::kIntersect // + ? Push(0, 1, path, is_aa) + : Push(0, 1, path, is_aa); +} + +void DisplayListBuilder::drawPaint() { + Push(0, 1); + CheckLayerOpacityCompatibility(); +} +void DisplayListBuilder::drawColor(SkColor color, SkBlendMode mode) { + Push(0, 1, color, mode); + CheckLayerOpacityCompatibility(mode); +} +void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) { + Push(0, 1, p0, p1); + CheckLayerOpacityCompatibility(); +} +void DisplayListBuilder::drawRect(const SkRect& rect) { + Push(0, 1, rect); + CheckLayerOpacityCompatibility(); +} +void DisplayListBuilder::drawOval(const SkRect& bounds) { + Push(0, 1, bounds); + CheckLayerOpacityCompatibility(); +} +void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) { + Push(0, 1, center, radius); + CheckLayerOpacityCompatibility(); +} +void DisplayListBuilder::drawRRect(const SkRRect& rrect) { + if (rrect.isRect()) { + drawRect(rrect.rect()); + } else if (rrect.isOval()) { + drawOval(rrect.rect()); + } else { + Push(0, 1, rrect); + CheckLayerOpacityCompatibility(); + } +} +void DisplayListBuilder::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { + Push(0, 1, outer, inner); + CheckLayerOpacityCompatibility(); +} +void DisplayListBuilder::drawPath(const SkPath& path) { + Push(0, 1, path); + CheckLayerOpacityHairlineCompatibility(); +} + +void DisplayListBuilder::drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) { + Push(0, 1, bounds, start, sweep, useCenter); + if (useCenter) { + CheckLayerOpacityHairlineCompatibility(); + } else { + CheckLayerOpacityCompatibility(); + } +} +void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) { + void* data_ptr; + FML_DCHECK(count < kMaxDrawPointsCount); + int bytes = count * sizeof(SkPoint); + switch (mode) { + case SkCanvas::PointMode::kPoints_PointMode: + data_ptr = Push(bytes, 1, count); + break; + case SkCanvas::PointMode::kLines_PointMode: + data_ptr = Push(bytes, 1, count); + break; + case SkCanvas::PointMode::kPolygon_PointMode: + data_ptr = Push(bytes, 1, count); + break; + default: + FML_DCHECK(false); + return; + } + CopyV(data_ptr, pts, count); + // drawPoints treats every point or line (or segment of a polygon) + // as a completely separate operation meaning we cannot ensure + // distribution of group opacity without analyzing the mode and the + // bounds of every sub-primitive. + // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c + UpdateLayerOpacityCompatibility(false); +} +void DisplayListBuilder::drawVertices(const sk_sp vertices, + SkBlendMode mode) { + Push(0, 1, std::move(vertices), mode); + // DrawVertices applies its colors to the paint so we have no way + // of controlling opacity using the current paint attributes. + UpdateLayerOpacityCompatibility(false); +} + +void DisplayListBuilder::drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling, + bool render_with_attributes) { + render_with_attributes + ? Push(0, 1, std::move(image), point, sampling) + : Push(0, 1, std::move(image), point, sampling); + CheckLayerOpacityCompatibility(render_with_attributes); +} +void DisplayListBuilder::drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + bool render_with_attributes, + SkCanvas::SrcRectConstraint constraint) { + Push(0, 1, std::move(image), src, dst, sampling, + render_with_attributes, constraint); + CheckLayerOpacityCompatibility(render_with_attributes); +} +void DisplayListBuilder::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) { + render_with_attributes + ? Push(0, 1, std::move(image), center, dst, + filter) + : Push(0, 1, std::move(image), center, dst, filter); + CheckLayerOpacityCompatibility(render_with_attributes); +} +void DisplayListBuilder::drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) { + int xDivCount = lattice.fXCount; + int yDivCount = lattice.fYCount; + FML_DCHECK((lattice.fRectTypes == nullptr) || (lattice.fColors != nullptr)); + int cellCount = lattice.fRectTypes && lattice.fColors + ? (xDivCount + 1) * (yDivCount + 1) + : 0; + size_t bytes = + (xDivCount + yDivCount) * sizeof(int) + + cellCount * (sizeof(SkColor) + sizeof(SkCanvas::Lattice::RectType)); + SkIRect src = lattice.fBounds ? *lattice.fBounds : image->bounds(); + void* pod = this->Push( + bytes, 1, std::move(image), xDivCount, yDivCount, cellCount, src, dst, + filter, render_with_attributes); + CopyV(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, + lattice.fColors, cellCount, lattice.fRectTypes, cellCount); + CheckLayerOpacityCompatibility(render_with_attributes); +} +void DisplayListBuilder::drawAtlas(const sk_sp 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) { + int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); + void* data_ptr; + if (colors != nullptr) { + bytes += count * sizeof(SkColor); + if (cull_rect != nullptr) { + data_ptr = Push(bytes, 1, std::move(atlas), count, + mode, sampling, true, *cull_rect, + render_with_attributes); + } else { + data_ptr = Push(bytes, 1, std::move(atlas), count, mode, + sampling, true, render_with_attributes); + } + CopyV(data_ptr, xform, count, tex, count, colors, count); + } else { + if (cull_rect != nullptr) { + data_ptr = Push(bytes, 1, std::move(atlas), count, + mode, sampling, false, *cull_rect, + render_with_attributes); + } else { + data_ptr = Push(bytes, 1, std::move(atlas), count, mode, + sampling, false, render_with_attributes); + } + CopyV(data_ptr, xform, count, tex, count); + } + // drawAtlas treats each image as a separate operation so we cannot rely + // on it to distribute the opacity without overlap without checking all + // of the transforms and texture rectangles. + UpdateLayerOpacityCompatibility(false); +} + +void DisplayListBuilder::drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool render_with_attributes) { + matrix // + ? Push(0, 1, std::move(picture), *matrix, + render_with_attributes) + : Push(0, 1, std::move(picture), render_with_attributes); + // The non-nested op count accumulated in the |Push| method will include + // this call to |drawPicture| for non-nested op count metrics. + // But, for nested op count metrics we want the |drawPicture| call itself + // to be transparent. So we subtract 1 from our accumulated nested count to + // balance out against the 1 that was accumulated into the regular count. + // This behavior is identical to the way SkPicture computes nested op counts. + nested_op_count_ += picture->approximateOpCount(true) - 1; + nested_bytes_ += picture->approximateBytesUsed(); + CheckLayerOpacityCompatibility(render_with_attributes); +} +void DisplayListBuilder::drawDisplayList( + const sk_sp display_list) { + Push(0, 1, std::move(display_list)); + // The non-nested op count accumulated in the |Push| method will include + // this call to |drawDisplayList| for non-nested op count metrics. + // But, for nested op count metrics we want the |drawDisplayList| call itself + // to be transparent. So we subtract 1 from our accumulated nested count to + // balance out against the 1 that was accumulated into the regular count. + // This behavior is identical to the way SkPicture computes nested op counts. + nested_op_count_ += display_list->op_count(true) - 1; + nested_bytes_ += display_list->bytes(true); + UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity()); +} +void DisplayListBuilder::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { + Push(0, 1, std::move(blob), x, y); + CheckLayerOpacityCompatibility(); +} +void DisplayListBuilder::drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) { + transparent_occluder // + ? Push(0, 1, path, color, elevation, dpr) + : Push(0, 1, path, color, elevation, dpr); + UpdateLayerOpacityCompatibility(false); +} + +// clang-format off +// Flags common to all primitives that apply colors +#define PAINT_FLAGS (kUsesDither_ | \ + kUsesColor_ | \ + kUsesAlpha_ | \ + kUsesBlend_ | \ + kUsesShader_ | \ + kUsesColorFilter_ | \ + kUsesImageFilter_) + +// Flags common to all primitives that stroke or fill +#define STROKE_OR_FILL_FLAGS (kIsDrawnGeometry_ | \ + kUsesAntiAlias_ | \ + kUsesMaskFilter_ | \ + kUsesPathEffect_) + +// Flags common to primitives that stroke geometry +#define STROKE_FLAGS (kIsStrokedGeometry_ | \ + kUsesAntiAlias_ | \ + kUsesMaskFilter_ | \ + kUsesPathEffect_) + +// Flags common to primitives that render an image with paint attributes +#define IMAGE_FLAGS_BASE (kIsNonGeometric_ | \ + kUsesAlpha_ | \ + kUsesDither_ | \ + kUsesBlend_ | \ + kUsesColorFilter_ | \ + kUsesImageFilter_) +// clang-format on + +const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerWithPaintFlags = + DisplayListAttributeFlags(kIsNonGeometric_ | // + kUsesAlpha_ | // + kUsesBlend_ | // + kUsesColorFilter_ | // + kUsesImageFilter_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawColorFlags = + DisplayListAttributeFlags(kFloodsSurface_ | kIgnoresPaint_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPaintFlags = + DisplayListAttributeFlags(PAINT_FLAGS | kFloodsSurface_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawHVLineFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | kMayHaveCaps_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawLineFlags = + kDrawHVLineFlags.with(kMayHaveDiagonalCaps_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawRectFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveJoins_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawOvalFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawCircleFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawRRectFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawDRRectFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPathFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveCaps_ | kMayHaveDiagonalCaps_ | + kMayHaveJoins_ | kMayHaveAcuteJoins_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawArcNoCenterFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveCaps_ | kMayHaveDiagonalCaps_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawArcWithCenterFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveJoins_ | kMayHaveAcuteJoins_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPointsAsPointsFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | // + kMayHaveCaps_ | kButtCapIsSquare_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPointsAsLinesFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | // + kMayHaveCaps_ | kMayHaveDiagonalCaps_); + +// Polygon mode just draws (count-1) separate lines, no joins +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPointsAsPolygonFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_FLAGS | // + kMayHaveCaps_ | kMayHaveDiagonalCaps_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawVerticesFlags = + DisplayListAttributeFlags(kIsNonGeometric_ | // + kUsesDither_ | // + kUsesAlpha_ | // + kUsesShader_ | // + kUsesBlend_ | // + kUsesColorFilter_ | // + kUsesImageFilter_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE | // + kUsesAntiAlias_ | kUsesMaskFilter_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageRectFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags + DisplayListOpFlags::kDrawImageRectWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE | // + kUsesAntiAlias_ | kUsesMaskFilter_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageNineFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags + DisplayListOpFlags::kDrawImageNineWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageLatticeFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags + DisplayListOpFlags::kDrawImageLatticeWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasWithPaintFlags = + DisplayListAttributeFlags(IMAGE_FLAGS_BASE); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureWithPaintFlags = + kSaveLayerWithPaintFlags; + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawDisplayListFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawTextBlobFlags = + DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | + kMayHaveJoins_) + .without(kUsesAntiAlias_); + +const DisplayListAttributeFlags DisplayListOpFlags::kDrawShadowFlags = + DisplayListAttributeFlags(kIgnoresPaint_); + +#undef PAINT_FLAGS +#undef STROKE_OR_FILL_FLAGS +#undef STROKE_FLAGS +#undef IMAGE_FLAGS_BASE + +} // namespace flutter diff --git a/display_list/display_list_builder.h b/display_list/display_list_builder.h new file mode 100644 index 0000000000000..0dc3417a4a7a2 --- /dev/null +++ b/display_list/display_list_builder.h @@ -0,0 +1,403 @@ +// 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_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 blender) override { + if (!blender) { + setBlendMode(SkBlendMode::kSrcOver); + } else if (current_blender_ != blender) { + onSetBlender(std::move(blender)); + } + } + void setShader(sk_sp shader) override { + if (current_shader_ != shader) { + onSetShader(std::move(shader)); + } + } + void setImageFilter(sk_sp filter) override { + if (current_image_filter_ != filter) { + onSetImageFilter(std::move(filter)); + } + } + void setColorFilter(sk_sp filter) override { + if (current_color_filter_ != filter) { + onSetColorFilter(std::move(filter)); + } + } + void setPathEffect(sk_sp effect) override { + if (current_path_effect_ != effect) { + onSetPathEffect(std::move(effect)); + } + } + void setMaskFilter(sk_sp 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 getShader() const { return current_shader_; } + sk_sp getColorFilter() const { return current_color_filter_; } + bool isInvertColors() const { return current_invert_colors_; } + std::optional getBlendMode() const { + if (current_blender_) { + // The setters will turn "Mode" style blenders into "blend_mode"s + return {}; + } + return current_blend_mode_; + } + sk_sp getBlender() const { + return current_blender_ ? current_blender_ + : SkBlender::Mode(current_blend_mode_); + } + sk_sp getPathEffect() const { return current_path_effect_; } + sk_sp 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 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 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 vertices, + SkBlendMode mode) override; + void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling, + bool render_with_attributes) override; + void drawImageRect( + const sk_sp 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 image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) override; + void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) override; + void drawAtlas(const sk_sp 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 picture, + const SkMatrix* matrix, + bool render_with_attributes) override; + void drawDisplayList(const sk_sp display_list) override; + void drawTextBlob(const sk_sp 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 Build(); + + private: + SkAutoTMalloc 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 + 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(bool has_layer = false) + : has_layer(has_layer), + cannot_inherit_opacity(false), + has_compatible_op(false) {} + + 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 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 blender); + void onSetShader(sk_sp shader); + void onSetImageFilter(sk_sp filter); + void onSetColorFilter(sk_sp filter); + void onSetPathEffect(sk_sp effect); + void onSetMaskFilter(sk_sp 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 current_blender_; + sk_sp current_shader_; + sk_sp current_color_filter_; + sk_sp current_image_filter_; + sk_sp current_path_effect_; + sk_sp current_mask_filter_; + SkBlurStyle current_mask_style_; + SkScalar current_mask_sigma_ = kInvalidSigma; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_BUILDER_H_ diff --git a/display_list/display_list_canvas.h b/display_list/display_list_canvas.h deleted file mode 100644 index ea73b377ee160..0000000000000 --- a/display_list/display_list_canvas.h +++ /dev/null @@ -1,259 +0,0 @@ -// 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_CANVAS_H_ -#define FLUTTER_FLOW_DISPLAY_LIST_CANVAS_H_ - -#include "flutter/display_list/display_list.h" -#include "flutter/display_list/display_list_utils.h" -#include "flutter/fml/logging.h" -#include "third_party/skia/include/core/SkCanvasVirtualEnforcer.h" -#include "third_party/skia/include/utils/SkNoDrawCanvas.h" - -// Classes to interact between SkCanvas and DisplayList, including: -// DisplayListCanvasDispatcher: -// Can be fed to the dispatch() method of a DisplayList to feed -// the resulting rendering operations to an SkCanvas instance. -// DisplayListCanvasRecorder -// An adapter that implements an SkCanvas interface which can -// then be handed to code that outputs to an SkCanvas to capture -// the output into a Flutter DisplayList. - -namespace flutter { - -// Receives all methods on Dispatcher and sends them to an SkCanvas -class DisplayListCanvasDispatcher : public virtual Dispatcher, - public SkPaintDispatchHelper { - public: - explicit DisplayListCanvasDispatcher(SkCanvas* canvas, - SkScalar opacity = SK_Scalar1) - : SkPaintDispatchHelper(opacity), canvas_(canvas) {} - - const SkPaint* safe_paint(bool use_attributes); - - void save() override; - void restore() override; - void saveLayer(const SkRect* bounds, bool restore_with_paint) override; - - 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; - // 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 vertices, - SkBlendMode mode) override; - void drawImage(const sk_sp image, - const SkPoint point, - const SkSamplingOptions& sampling, - bool render_with_attributes) override; - void drawImageRect(const sk_sp image, - const SkRect& src, - const SkRect& dst, - const SkSamplingOptions& sampling, - bool render_with_attributes, - SkCanvas::SrcRectConstraint constraint) override; - void drawImageNine(const sk_sp image, - const SkIRect& center, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) override; - void drawImageLattice(const sk_sp image, - const SkCanvas::Lattice& lattice, - const SkRect& dst, - SkFilterMode filter, - bool render_with_attributes) override; - void drawAtlas(const sk_sp 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 picture, - const SkMatrix* matrix, - bool render_with_attributes) override; - void drawDisplayList(const sk_sp display_list) override; - void drawTextBlob(const sk_sp blob, - SkScalar x, - SkScalar y) override; - void drawShadow(const SkPath& path, - const SkColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) override; - - static SkRect ComputeShadowBounds(const SkPath& path, - float elevation, - SkScalar dpr, - const SkMatrix& ctm); - - static void DrawShadow(SkCanvas* canvas, - const SkPath& path, - SkColor color, - float elevation, - bool transparentOccluder, - SkScalar dpr); - - private: - SkCanvas* canvas_; - SkPaint temp_paint_; -}; - -// Receives all methods on SkCanvas and sends them to a DisplayListBuilder -class DisplayListCanvasRecorder - : public SkCanvasVirtualEnforcer, - public SkRefCnt, - DisplayListOpFlags { - public: - explicit DisplayListCanvasRecorder(const SkRect& bounds); - - const sk_sp builder() { return builder_; } - - sk_sp Build(); - - void didConcat44(const SkM44&) override; - void didSetM44(const SkM44&) override { FML_DCHECK(false); } - void didTranslate(SkScalar, SkScalar) override; - void didScale(SkScalar, SkScalar) override; - - void onClipRect(const SkRect& rect, - SkClipOp op, - ClipEdgeStyle edgeStyle) override; - void onClipRRect(const SkRRect& rrect, - SkClipOp op, - ClipEdgeStyle edgeStyle) override; - void onClipPath(const SkPath& path, - SkClipOp op, - ClipEdgeStyle edgeStyle) override; - - void willSave() override; - SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; - void didRestore() override; - - void onDrawPaint(const SkPaint& paint) override; - void onDrawBehind(const SkPaint&) override { FML_DCHECK(false); } - void onDrawRect(const SkRect& rect, const SkPaint& paint) override; - void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override; - void onDrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const SkPaint& paint) override; - void onDrawOval(const SkRect& rect, const SkPaint& paint) override; - void onDrawArc(const SkRect& rect, - SkScalar startAngle, - SkScalar sweepAngle, - bool useCenter, - const SkPaint& paint) override; - void onDrawPath(const SkPath& path, const SkPaint& paint) override; - void onDrawRegion(const SkRegion& region, const SkPaint& paint) override { - FML_DCHECK(false); - } - - void onDrawTextBlob(const SkTextBlob* blob, - SkScalar x, - SkScalar y, - const SkPaint& paint) override; - - void onDrawPatch(const SkPoint cubics[12], - const SkColor colors[4], - const SkPoint texCoords[4], - SkBlendMode mode, - const SkPaint& paint) override { - FML_DCHECK(false); - } - void onDrawPoints(SkCanvas::PointMode mode, - size_t count, - const SkPoint pts[], - const SkPaint& paint) override; - void onDrawVerticesObject(const SkVertices* vertices, - SkBlendMode mode, - const SkPaint& paint) override; - - void onDrawImage2(const SkImage*, - SkScalar dx, - SkScalar dy, - const SkSamplingOptions&, - const SkPaint*) override; - void onDrawImageRect2(const SkImage*, - const SkRect& src, - const SkRect& dst, - const SkSamplingOptions&, - const SkPaint*, - SrcRectConstraint) override; - void onDrawImageLattice2(const SkImage*, - const Lattice&, - const SkRect& dst, - SkFilterMode, - const SkPaint*) override; - void onDrawAtlas2(const SkImage*, - const SkRSXform[], - const SkRect src[], - const SkColor[], - int count, - SkBlendMode, - const SkSamplingOptions&, - const SkRect* cull, - const SkPaint*) override; - - void onDrawEdgeAAQuad(const SkRect& rect, - const SkPoint clip[4], - SkCanvas::QuadAAFlags aaFlags, - const SkColor4f& color, - SkBlendMode mode) override { - FML_DCHECK(0); - } - - void onDrawAnnotation(const SkRect& rect, - const char key[], - SkData* value) override { - FML_DCHECK(false); - } - void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; - - void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { - FML_DCHECK(false); - } - void onDrawPicture(const SkPicture* picture, - const SkMatrix* matrix, - const SkPaint* paint) override; - - private: - sk_sp builder_; -}; - -} // namespace flutter - -#endif // FLUTTER_FLOW_DISPLAY_LIST_CANVAS_H_ diff --git a/display_list/display_list_canvas.cc b/display_list/display_list_canvas_dispatcher.cc similarity index 53% rename from display_list/display_list_canvas.cc rename to display_list/display_list_canvas_dispatcher.cc index 99e0bf20ee170..ce4007f7cda6a 100644 --- a/display_list/display_list_canvas.cc +++ b/display_list/display_list_canvas_dispatcher.cc @@ -2,12 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/display_list/display_list_canvas.h" +#include "flutter/display_list/display_list_canvas_dispatcher.h" #include "flutter/fml/trace_event.h" -#include "third_party/skia/include/core/SkMaskFilter.h" -#include "third_party/skia/include/core/SkTextBlob.h" -#include "third_party/skia/include/utils/SkShadowUtils.h" namespace flutter { @@ -265,224 +262,4 @@ void DisplayListCanvasDispatcher::drawShadow(const SkPath& path, DrawShadow(canvas_, path, color, elevation, transparent_occluder, dpr); } -DisplayListCanvasRecorder::DisplayListCanvasRecorder(const SkRect& bounds) - : SkCanvasVirtualEnforcer(bounds.width(), bounds.height()), - builder_(sk_make_sp(bounds)) {} - -sk_sp DisplayListCanvasRecorder::Build() { - sk_sp display_list = builder_->Build(); - builder_.reset(); - return display_list; -} - -// clang-format off -void DisplayListCanvasRecorder::didConcat44(const SkM44& m44) { - // transform4x4 takes a full 4x4 transform in row major order - builder_->transformFullPerspective( - m44.rc(0, 0), m44.rc(0, 1), m44.rc(0, 2), m44.rc(0, 3), - m44.rc(1, 0), m44.rc(1, 1), m44.rc(1, 2), m44.rc(1, 3), - m44.rc(2, 0), m44.rc(2, 1), m44.rc(2, 2), m44.rc(2, 3), - m44.rc(3, 0), m44.rc(3, 1), m44.rc(3, 2), m44.rc(3, 3)); -} -// clang-format on -void DisplayListCanvasRecorder::didTranslate(SkScalar tx, SkScalar ty) { - builder_->translate(tx, ty); -} -void DisplayListCanvasRecorder::didScale(SkScalar sx, SkScalar sy) { - builder_->scale(sx, sy); -} - -void DisplayListCanvasRecorder::onClipRect(const SkRect& rect, - SkClipOp clip_op, - ClipEdgeStyle edge_style) { - builder_->clipRect(rect, clip_op, - edge_style == ClipEdgeStyle::kSoft_ClipEdgeStyle); -} -void DisplayListCanvasRecorder::onClipRRect(const SkRRect& rrect, - SkClipOp clip_op, - ClipEdgeStyle edge_style) { - builder_->clipRRect(rrect, clip_op, - edge_style == ClipEdgeStyle::kSoft_ClipEdgeStyle); -} -void DisplayListCanvasRecorder::onClipPath(const SkPath& path, - SkClipOp clip_op, - ClipEdgeStyle edge_style) { - builder_->clipPath(path, clip_op, - edge_style == ClipEdgeStyle::kSoft_ClipEdgeStyle); -} - -void DisplayListCanvasRecorder::willSave() { - builder_->save(); -} -SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy( - const SaveLayerRec& rec) { - if (rec.fPaint) { - builder_->setAttributesFromPaint(*rec.fPaint, kSaveLayerWithPaintFlags); - builder_->saveLayer(rec.fBounds, true); - } else { - builder_->saveLayer(rec.fBounds, false); - } - return SaveLayerStrategy::kNoLayer_SaveLayerStrategy; -} -void DisplayListCanvasRecorder::didRestore() { - builder_->restore(); -} - -void DisplayListCanvasRecorder::onDrawPaint(const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, kDrawPaintFlags); - builder_->drawPaint(); -} -void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect, - const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, kDrawRectFlags); - builder_->drawRect(rect); -} -void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect, - const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, kDrawRRectFlags); - builder_->drawRRect(rrect); -} -void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer, - const SkRRect& inner, - const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, kDrawDRRectFlags); - builder_->drawDRRect(outer, inner); -} -void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect, - const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, kDrawOvalFlags); - builder_->drawOval(rect); -} -void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, - SkScalar startAngle, - SkScalar sweepAngle, - bool useCenter, - const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, - useCenter // - ? kDrawArcWithCenterFlags - : kDrawArcNoCenterFlags); - builder_->drawArc(rect, startAngle, sweepAngle, useCenter); -} -void DisplayListCanvasRecorder::onDrawPath(const SkPath& path, - const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, kDrawPathFlags); - builder_->drawPath(path); -} - -void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, - size_t count, - const SkPoint pts[], - const SkPaint& paint) { - switch (mode) { - case SkCanvas::kPoints_PointMode: - builder_->setAttributesFromPaint(paint, kDrawPointsAsPointsFlags); - break; - case SkCanvas::kLines_PointMode: - builder_->setAttributesFromPaint(paint, kDrawPointsAsLinesFlags); - break; - case SkCanvas::kPolygon_PointMode: - builder_->setAttributesFromPaint(paint, kDrawPointsAsPolygonFlags); - break; - } - if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) { - builder_->drawLine(pts[0], pts[1]); - } else { - uint32_t count32 = static_cast(count); - // TODO(flar): depending on the mode we could break it down into - // multiple calls to drawPoints, but how much do we really want - // to support more than a couple billion points? - FML_DCHECK(count32 == count); - builder_->drawPoints(mode, count32, pts); - } -} -void DisplayListCanvasRecorder::onDrawVerticesObject(const SkVertices* vertices, - SkBlendMode mode, - const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, kDrawVerticesFlags); - builder_->drawVertices(sk_ref_sp(vertices), mode); -} - -void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image, - SkScalar dx, - SkScalar dy, - const SkSamplingOptions& sampling, - const SkPaint* paint) { - if (paint != nullptr) { - builder_->setAttributesFromPaint(*paint, kDrawImageWithPaintFlags); - } - builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling, - paint != nullptr); -} -void DisplayListCanvasRecorder::onDrawImageRect2( - const SkImage* image, - const SkRect& src, - const SkRect& dst, - const SkSamplingOptions& sampling, - const SkPaint* paint, - SrcRectConstraint constraint) { - if (paint != nullptr) { - builder_->setAttributesFromPaint(*paint, kDrawImageRectWithPaintFlags); - } - builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling, - paint != nullptr, constraint); -} -void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, - const Lattice& lattice, - const SkRect& dst, - SkFilterMode filter, - const SkPaint* paint) { - if (paint != nullptr) { - // SkCanvas will always construct a paint, - // though it is a default paint most of the time - SkPaint default_paint; - if (*paint == default_paint) { - paint = nullptr; - } else { - builder_->setAttributesFromPaint(*paint, kDrawImageLatticeWithPaintFlags); - } - } - builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter, - paint != nullptr); -} -void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, - const SkRSXform xform[], - const SkRect src[], - const SkColor colors[], - int count, - SkBlendMode mode, - const SkSamplingOptions& sampling, - const SkRect* cull, - const SkPaint* paint) { - if (paint != nullptr) { - builder_->setAttributesFromPaint(*paint, kDrawAtlasWithPaintFlags); - } - builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode, - sampling, cull, paint != nullptr); -} - -void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, - SkScalar x, - SkScalar y, - const SkPaint& paint) { - builder_->setAttributesFromPaint(paint, kDrawTextBlobFlags); - builder_->drawTextBlob(sk_ref_sp(blob), x, y); -} -void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, - const SkDrawShadowRec& rec) { - // Skia does not expose the SkDrawShadowRec structure in a public - // header file so we cannot record this operation. - // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 - FML_DCHECK(false); -} - -void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, - const SkMatrix* matrix, - const SkPaint* paint) { - if (paint != nullptr) { - builder_->setAttributesFromPaint(*paint, kDrawPictureWithPaintFlags); - } - builder_->drawPicture(sk_ref_sp(picture), matrix, paint != nullptr); -} - } // namespace flutter diff --git a/display_list/display_list_canvas_dispatcher.h b/display_list/display_list_canvas_dispatcher.h new file mode 100644 index 0000000000000..753fb6467eb0c --- /dev/null +++ b/display_list/display_list_canvas_dispatcher.h @@ -0,0 +1,133 @@ +// 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_CANVAS_DISPATCHER_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_CANVAS_DISPATCHER_H_ + +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_dispatcher.h" +#include "flutter/display_list/display_list_utils.h" +#include "flutter/fml/macros.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// Can be fed to the dispatch() method of a DisplayList to feed the resulting +/// rendering operations to an SkCanvas instance. +/// +/// Receives all methods on Dispatcher and sends them to an SkCanvas +/// +class DisplayListCanvasDispatcher : public virtual Dispatcher, + public SkPaintDispatchHelper { + public: + explicit DisplayListCanvasDispatcher(SkCanvas* canvas, + SkScalar opacity = SK_Scalar1) + : SkPaintDispatchHelper(opacity), canvas_(canvas) {} + + const SkPaint* safe_paint(bool use_attributes); + + void save() override; + void restore() override; + void saveLayer(const SkRect* bounds, bool restore_with_paint) override; + + 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; + // 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 vertices, + SkBlendMode mode) override; + void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling, + bool render_with_attributes) override; + void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + bool render_with_attributes, + SkCanvas::SrcRectConstraint constraint) override; + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) override; + void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) override; + void drawAtlas(const sk_sp 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 picture, + const SkMatrix* matrix, + bool render_with_attributes) override; + void drawDisplayList(const sk_sp display_list) override; + void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) override; + void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) override; + + static SkRect ComputeShadowBounds(const SkPath& path, + float elevation, + SkScalar dpr, + const SkMatrix& ctm); + + static void DrawShadow(SkCanvas* canvas, + const SkPath& path, + SkColor color, + float elevation, + bool transparentOccluder, + SkScalar dpr); + + private: + SkCanvas* canvas_; + SkPaint temp_paint_; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_CANVAS_DISPATCHER_H_ diff --git a/display_list/display_list_canvas_recorder.cc b/display_list/display_list_canvas_recorder.cc new file mode 100644 index 0000000000000..82185a8d6fc84 --- /dev/null +++ b/display_list/display_list_canvas_recorder.cc @@ -0,0 +1,231 @@ +// 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. + +#include "flutter/display_list/display_list_canvas_recorder.h" + +#include "flutter/display_list/display_list_builder.h" + +namespace flutter { + +DisplayListCanvasRecorder::DisplayListCanvasRecorder(const SkRect& bounds) + : SkCanvasVirtualEnforcer(bounds.width(), bounds.height()), + builder_(sk_make_sp(bounds)) {} + +sk_sp DisplayListCanvasRecorder::Build() { + sk_sp display_list = builder_->Build(); + builder_.reset(); + return display_list; +} + +// clang-format off +void DisplayListCanvasRecorder::didConcat44(const SkM44& m44) { + // transform4x4 takes a full 4x4 transform in row major order + builder_->transformFullPerspective( + m44.rc(0, 0), m44.rc(0, 1), m44.rc(0, 2), m44.rc(0, 3), + m44.rc(1, 0), m44.rc(1, 1), m44.rc(1, 2), m44.rc(1, 3), + m44.rc(2, 0), m44.rc(2, 1), m44.rc(2, 2), m44.rc(2, 3), + m44.rc(3, 0), m44.rc(3, 1), m44.rc(3, 2), m44.rc(3, 3)); +} +// clang-format on +void DisplayListCanvasRecorder::didTranslate(SkScalar tx, SkScalar ty) { + builder_->translate(tx, ty); +} +void DisplayListCanvasRecorder::didScale(SkScalar sx, SkScalar sy) { + builder_->scale(sx, sy); +} + +void DisplayListCanvasRecorder::onClipRect(const SkRect& rect, + SkClipOp clip_op, + ClipEdgeStyle edge_style) { + builder_->clipRect(rect, clip_op, + edge_style == ClipEdgeStyle::kSoft_ClipEdgeStyle); +} +void DisplayListCanvasRecorder::onClipRRect(const SkRRect& rrect, + SkClipOp clip_op, + ClipEdgeStyle edge_style) { + builder_->clipRRect(rrect, clip_op, + edge_style == ClipEdgeStyle::kSoft_ClipEdgeStyle); +} +void DisplayListCanvasRecorder::onClipPath(const SkPath& path, + SkClipOp clip_op, + ClipEdgeStyle edge_style) { + builder_->clipPath(path, clip_op, + edge_style == ClipEdgeStyle::kSoft_ClipEdgeStyle); +} + +void DisplayListCanvasRecorder::willSave() { + builder_->save(); +} +SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy( + const SaveLayerRec& rec) { + if (rec.fPaint) { + builder_->setAttributesFromPaint(*rec.fPaint, kSaveLayerWithPaintFlags); + builder_->saveLayer(rec.fBounds, true); + } else { + builder_->saveLayer(rec.fBounds, false); + } + return SaveLayerStrategy::kNoLayer_SaveLayerStrategy; +} +void DisplayListCanvasRecorder::didRestore() { + builder_->restore(); +} + +void DisplayListCanvasRecorder::onDrawPaint(const SkPaint& paint) { + builder_->setAttributesFromPaint(paint, kDrawPaintFlags); + builder_->drawPaint(); +} +void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect, + const SkPaint& paint) { + builder_->setAttributesFromPaint(paint, kDrawRectFlags); + builder_->drawRect(rect); +} +void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect, + const SkPaint& paint) { + builder_->setAttributesFromPaint(paint, kDrawRRectFlags); + builder_->drawRRect(rrect); +} +void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const SkPaint& paint) { + builder_->setAttributesFromPaint(paint, kDrawDRRectFlags); + builder_->drawDRRect(outer, inner); +} +void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect, + const SkPaint& paint) { + builder_->setAttributesFromPaint(paint, kDrawOvalFlags); + builder_->drawOval(rect); +} +void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, + SkScalar startAngle, + SkScalar sweepAngle, + bool useCenter, + const SkPaint& paint) { + builder_->setAttributesFromPaint(paint, + useCenter // + ? kDrawArcWithCenterFlags + : kDrawArcNoCenterFlags); + builder_->drawArc(rect, startAngle, sweepAngle, useCenter); +} +void DisplayListCanvasRecorder::onDrawPath(const SkPath& path, + const SkPaint& paint) { + builder_->setAttributesFromPaint(paint, kDrawPathFlags); + builder_->drawPath(path); +} + +void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, + size_t count, + const SkPoint pts[], + const SkPaint& paint) { + switch (mode) { + case SkCanvas::kPoints_PointMode: + builder_->setAttributesFromPaint(paint, kDrawPointsAsPointsFlags); + break; + case SkCanvas::kLines_PointMode: + builder_->setAttributesFromPaint(paint, kDrawPointsAsLinesFlags); + break; + case SkCanvas::kPolygon_PointMode: + builder_->setAttributesFromPaint(paint, kDrawPointsAsPolygonFlags); + break; + } + if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) { + builder_->drawLine(pts[0], pts[1]); + } else { + uint32_t count32 = static_cast(count); + // TODO(flar): depending on the mode we could break it down into + // multiple calls to drawPoints, but how much do we really want + // to support more than a couple billion points? + FML_DCHECK(count32 == count); + builder_->drawPoints(mode, count32, pts); + } +} +void DisplayListCanvasRecorder::onDrawVerticesObject(const SkVertices* vertices, + SkBlendMode mode, + const SkPaint& paint) { + builder_->setAttributesFromPaint(paint, kDrawVerticesFlags); + builder_->drawVertices(sk_ref_sp(vertices), mode); +} + +void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image, + SkScalar dx, + SkScalar dy, + const SkSamplingOptions& sampling, + const SkPaint* paint) { + if (paint != nullptr) { + builder_->setAttributesFromPaint(*paint, kDrawImageWithPaintFlags); + } + builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling, + paint != nullptr); +} +void DisplayListCanvasRecorder::onDrawImageRect2( + const SkImage* image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + const SkPaint* paint, + SrcRectConstraint constraint) { + if (paint != nullptr) { + builder_->setAttributesFromPaint(*paint, kDrawImageRectWithPaintFlags); + } + builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling, + paint != nullptr, constraint); +} +void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, + const Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + const SkPaint* paint) { + if (paint != nullptr) { + // SkCanvas will always construct a paint, + // though it is a default paint most of the time + SkPaint default_paint; + if (*paint == default_paint) { + paint = nullptr; + } else { + builder_->setAttributesFromPaint(*paint, kDrawImageLatticeWithPaintFlags); + } + } + builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter, + paint != nullptr); +} +void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, + const SkRSXform xform[], + const SkRect src[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cull, + const SkPaint* paint) { + if (paint != nullptr) { + builder_->setAttributesFromPaint(*paint, kDrawAtlasWithPaintFlags); + } + builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode, + sampling, cull, paint != nullptr); +} + +void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, + SkScalar x, + SkScalar y, + const SkPaint& paint) { + builder_->setAttributesFromPaint(paint, kDrawTextBlobFlags); + builder_->drawTextBlob(sk_ref_sp(blob), x, y); +} +void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, + const SkDrawShadowRec& rec) { + // Skia does not expose the SkDrawShadowRec structure in a public + // header file so we cannot record this operation. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 + FML_DCHECK(false); +} + +void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) { + if (paint != nullptr) { + builder_->setAttributesFromPaint(*paint, kDrawPictureWithPaintFlags); + } + builder_->drawPicture(sk_ref_sp(picture), matrix, paint != nullptr); +} + +} // namespace flutter diff --git a/display_list/display_list_canvas_recorder.h b/display_list/display_list_canvas_recorder.h new file mode 100644 index 0000000000000..2cd185d1e0ba0 --- /dev/null +++ b/display_list/display_list_canvas_recorder.h @@ -0,0 +1,146 @@ +// 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_CANVAS_RECORDER_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_CANVAS_RECORDER_H_ + +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/display_list_flags.h" +#include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkCanvasVirtualEnforcer.h" +#include "third_party/skia/include/utils/SkNoDrawCanvas.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// An adapter that implements an SkCanvas interface which can then be handed to +/// code that outputs to an SkCanvas to capture the output into a Flutter +/// DisplayList. +/// +/// Receives all methods on SkCanvas and sends them to a DisplayListBuilder +/// +class DisplayListCanvasRecorder + : public SkCanvasVirtualEnforcer, + public SkRefCnt, + DisplayListOpFlags { + public: + explicit DisplayListCanvasRecorder(const SkRect& bounds); + + const sk_sp builder() { return builder_; } + + sk_sp Build(); + + void didConcat44(const SkM44&) override; + void didSetM44(const SkM44&) override { FML_DCHECK(false); } + void didTranslate(SkScalar, SkScalar) override; + void didScale(SkScalar, SkScalar) override; + + void onClipRect(const SkRect& rect, + SkClipOp op, + ClipEdgeStyle edgeStyle) override; + void onClipRRect(const SkRRect& rrect, + SkClipOp op, + ClipEdgeStyle edgeStyle) override; + void onClipPath(const SkPath& path, + SkClipOp op, + ClipEdgeStyle edgeStyle) override; + + void willSave() override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; + void didRestore() override; + + void onDrawPaint(const SkPaint& paint) override; + void onDrawBehind(const SkPaint&) override { FML_DCHECK(false); } + void onDrawRect(const SkRect& rect, const SkPaint& paint) override; + void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override; + void onDrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const SkPaint& paint) override; + void onDrawOval(const SkRect& rect, const SkPaint& paint) override; + void onDrawArc(const SkRect& rect, + SkScalar startAngle, + SkScalar sweepAngle, + bool useCenter, + const SkPaint& paint) override; + void onDrawPath(const SkPath& path, const SkPaint& paint) override; + void onDrawRegion(const SkRegion& region, const SkPaint& paint) override { + FML_DCHECK(false); + } + + void onDrawTextBlob(const SkTextBlob* blob, + SkScalar x, + SkScalar y, + const SkPaint& paint) override; + + void onDrawPatch(const SkPoint cubics[12], + const SkColor colors[4], + const SkPoint texCoords[4], + SkBlendMode mode, + const SkPaint& paint) override { + FML_DCHECK(false); + } + void onDrawPoints(SkCanvas::PointMode mode, + size_t count, + const SkPoint pts[], + const SkPaint& paint) override; + void onDrawVerticesObject(const SkVertices* vertices, + SkBlendMode mode, + const SkPaint& paint) override; + + void onDrawImage2(const SkImage*, + SkScalar dx, + SkScalar dy, + const SkSamplingOptions&, + const SkPaint*) override; + void onDrawImageRect2(const SkImage*, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions&, + const SkPaint*, + SrcRectConstraint) override; + void onDrawImageLattice2(const SkImage*, + const Lattice&, + const SkRect& dst, + SkFilterMode, + const SkPaint*) override; + void onDrawAtlas2(const SkImage*, + const SkRSXform[], + const SkRect src[], + const SkColor[], + int count, + SkBlendMode, + const SkSamplingOptions&, + const SkRect* cull, + const SkPaint*) override; + + void onDrawEdgeAAQuad(const SkRect& rect, + const SkPoint clip[4], + SkCanvas::QuadAAFlags aaFlags, + const SkColor4f& color, + SkBlendMode mode) override { + FML_DCHECK(0); + } + + void onDrawAnnotation(const SkRect& rect, + const char key[], + SkData* value) override { + FML_DCHECK(false); + } + void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + + void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { + FML_DCHECK(false); + } + void onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) override; + + private: + sk_sp builder_; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_CANVAS_RECORDER_H_ diff --git a/display_list/display_list_canvas_unittests.cc b/display_list/display_list_canvas_unittests.cc index 674ef3bc0a3a8..f64a471ed30f3 100644 --- a/display_list/display_list_canvas_unittests.cc +++ b/display_list/display_list_canvas_unittests.cc @@ -2,18 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/display_list/display_list_canvas.h" +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_canvas_dispatcher.h" +#include "flutter/display_list/display_list_canvas_recorder.h" +#include "flutter/display_list/display_list_flags.h" #include "flutter/fml/math.h" #include "flutter/testing/testing.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkImageInfo.h" -#include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPictureRecorder.h" -#include "third_party/skia/include/core/SkRRect.h" -#include "third_party/skia/include/core/SkRSXform.h" #include "third_party/skia/include/core/SkSurface.h" -#include "third_party/skia/include/core/SkTextBlob.h" -#include "third_party/skia/include/core/SkVertices.h" #include "third_party/skia/include/effects/SkBlenders.h" #include "third_party/skia/include/effects/SkDashPathEffect.h" #include "third_party/skia/include/effects/SkDiscretePathEffect.h" diff --git a/display_list/display_list_dispatcher.cc b/display_list/display_list_dispatcher.cc new file mode 100644 index 0000000000000..e321f02ea6c7f --- /dev/null +++ b/display_list/display_list_dispatcher.cc @@ -0,0 +1,11 @@ +// 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. + +#include "flutter/display_list/display_list_dispatcher.h" + +namespace flutter { + +// + +} // namespace flutter diff --git a/display_list/display_list_dispatcher.h b/display_list/display_list_dispatcher.h new file mode 100644 index 0000000000000..e938eec6ebe91 --- /dev/null +++ b/display_list/display_list_dispatcher.h @@ -0,0 +1,248 @@ +// 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_DISPATCHER_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_DISPATCHER_H_ + +#include "flutter/display_list/types.h" +#include "flutter/fml/macros.h" + +namespace flutter { + +class DisplayList; + +//------------------------------------------------------------------------------ +/// @brief 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 shader) = 0; + virtual void setColorFilter(sk_sp 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 blender) = 0; + virtual void setPathEffect(sk_sp effect) = 0; + virtual void setMaskFilter(sk_sp 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 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 vertices, + SkBlendMode mode) = 0; + virtual void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling, + bool render_with_attributes) = 0; + virtual void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + bool render_with_attributes, + SkCanvas::SrcRectConstraint constraint) = 0; + virtual void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) = 0; + virtual void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + bool render_with_attributes) = 0; + virtual void drawAtlas(const sk_sp 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 picture, + const SkMatrix* matrix, + bool render_with_attributes) = 0; + virtual void drawDisplayList(const sk_sp display_list) = 0; + virtual void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) = 0; + virtual void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool transparent_occluder, + SkScalar dpr) = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_DISPATCHER_H_ diff --git a/display_list/display_list_flags.cc b/display_list/display_list_flags.cc new file mode 100644 index 0000000000000..36ed9ea48d871 --- /dev/null +++ b/display_list/display_list_flags.cc @@ -0,0 +1,11 @@ +// 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. + +#include "flutter/display_list/display_list_flags.h" + +namespace flutter { + +// Just exists to ensure that the header can be cleanly imported. + +} // namespace flutter diff --git a/display_list/display_list_flags.h b/display_list/display_list_flags.h new file mode 100644 index 0000000000000..c3f1e7498c27e --- /dev/null +++ b/display_list/display_list_flags.h @@ -0,0 +1,295 @@ +// 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_FLAGS_H_ +#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_FLAGS_H_ + +#include "flutter/display_list/types.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +/// 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; + + // Some ops have an optional paint argument. If the version + // stored in the DisplayList ignores the paint, but there + // is an option to render the same op with a paint then + // both of the following flags are set to indicate that + // a default paint object can be constructed when rendering + // the op to carry information imposed from outside the + // DisplayList (for example, the opacity override). + 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: + explicit 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: + explicit 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 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: + explicit 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 DisplayListAttributeFlags(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; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_FLAGS_H_ diff --git a/display_list/display_list_ops.cc b/display_list/display_list_ops.cc new file mode 100644 index 0000000000000..4e8bdb23ee264 --- /dev/null +++ b/display_list/display_list_ops.cc @@ -0,0 +1,11 @@ +// 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. + +#include "flutter/display_list/display_list_ops.h" + +namespace flutter { + +// Just exists to ensure that the header can be cleanly imported. + +} // namespace flutter diff --git a/display_list/display_list_ops.h b/display_list/display_list_ops.h new file mode 100644 index 0000000000000..4e1a007bdd212 --- /dev/null +++ b/display_list/display_list_ops.h @@ -0,0 +1,853 @@ +// 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 "flutter/display_list/display_list.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(SkPaint::name value) : value(value) {} \ + \ + const SkPaint::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(SkPaint::Style style) : style(style) {} + + const SkPaint::Style 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(SkScalar width) : width(width) {} + + const SkScalar 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(SkScalar limit) : limit(limit) {} + + const SkScalar 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(SkColor color) : color(color) {} + + const SkColor 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(SkBlendMode mode) : mode(mode) {} + + const SkBlendMode 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 field) : field(std::move(field)) {} \ + \ + sk_sp field; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name(field); \ + } \ + }; +DEFINE_SET_CLEAR_SKREF_OP(Blender, blender) +DEFINE_SET_CLEAR_SKREF_OP(Shader, shader) +DEFINE_SET_CLEAR_SKREF_OP(ImageFilter, filter) +DEFINE_SET_CLEAR_SKREF_OP(ColorFilter, filter) +DEFINE_SET_CLEAR_SKREF_OP(MaskFilter, filter) +DEFINE_SET_CLEAR_SKREF_OP(PathEffect, effect) +#undef DEFINE_SET_CLEAR_SKREF_OP + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +// Note that the "blur style" is packed into the OpType to prevent +// needing an additional 8 bytes for a 4-value enum. +#define DEFINE_MASK_BLUR_FILTER_OP(name, style) \ + struct SetMaskBlurFilter##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kSetMaskBlurFilter##name; \ + \ + explicit SetMaskBlurFilter##name##Op(SkScalar sigma) : sigma(sigma) {} \ + \ + SkScalar sigma; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.setMaskBlurFilter(style, sigma); \ + } \ + }; +DEFINE_MASK_BLUR_FILTER_OP(Normal, kNormal_SkBlurStyle) +DEFINE_MASK_BLUR_FILTER_OP(Solid, kSolid_SkBlurStyle) +DEFINE_MASK_BLUR_FILTER_OP(Inner, kInner_SkBlurStyle) +DEFINE_MASK_BLUR_FILTER_OP(Outer, kOuter_SkBlurStyle) +#undef DEFINE_MASK_BLUR_FILTER_OP + +// 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(bool with_paint) : with_paint(with_paint) {} + + bool with_paint; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.saveLayer(nullptr, with_paint); + } +}; +// 4 byte header + 20 byte payload packs evenly into 24 bytes +struct SaveLayerBoundsOp final : DLOp { + static const auto kType = DisplayListOpType::kSaveLayerBounds; + + SaveLayerBoundsOp(SkRect rect, bool with_paint) + : with_paint(with_paint), rect(rect) {} + + bool with_paint; + const SkRect rect; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.saveLayer(&rect, with_paint); + } +}; +// 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 + 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(SkColor color, SkBlendMode mode) : color(color), mode(mode) {} + + const SkColor color; + const SkBlendMode 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(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 + 12 byte payload packs efficiently into 16 bytes +struct DrawVerticesOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawVertices; + + DrawVerticesOp(sk_sp vertices, SkBlendMode mode) + : mode(mode), vertices(std::move(vertices)) {} + + const SkBlendMode mode; + const sk_sp vertices; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawVertices(vertices, mode); + } +}; + +// 4 byte header + 36 byte payload packs efficiently into 40 bytes +#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 image, \ + const SkPoint& point, \ + const SkSamplingOptions& sampling) \ + : point(point), sampling(sampling), image(std::move(image)) {} \ + \ + const SkPoint point; \ + const SkSamplingOptions sampling; \ + const sk_sp 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 + 68 byte payload packs efficiently into 72 bytes +struct DrawImageRectOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawImageRect; + + DrawImageRectOp(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& 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 SkSamplingOptions sampling; + const bool render_with_attributes; + const SkCanvas::SrcRectConstraint constraint; + const sk_sp 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 image, \ + const SkIRect& center, \ + const SkRect& dst, \ + SkFilterMode filter) \ + : center(center), dst(dst), filter(filter), image(std::move(image)) {} \ + \ + const SkIRect center; \ + const SkRect dst; \ + const SkFilterMode filter; \ + const sk_sp 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 image, + int x_count, + int y_count, + int cell_count, + const SkIRect& src, + const SkRect& dst, + SkFilterMode 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 SkFilterMode filter; + const SkIRect src; + const SkRect dst; + const sk_sp image; + + void dispatch(Dispatcher& dispatcher) const { + const int* xDivs = reinterpret_cast(this + 1); + const int* yDivs = reinterpret_cast(xDivs + x_count); + const SkColor* colors = + (cell_count == 0) ? nullptr + : reinterpret_cast(yDivs + y_count); + const SkCanvas::Lattice::RectType* types = + (cell_count == 0) + ? nullptr + : reinterpret_cast(colors + + cell_count); + dispatcher.drawImageLattice( + image, {xDivs, yDivs, types, x_count, y_count, &src, colors}, dst, + filter, with_paint); + } +}; + +// 4 byte header + 36 byte common payload packs efficiently into 40 bytes +// 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 +// SkColor 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 atlas, + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + bool has_colors, + bool render_with_attributes) + : count(count), + mode_index(static_cast(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 SkSamplingOptions sampling; + const sk_sp atlas; +}; + +// Packs as efficiently into 40 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 atlas, + int count, + SkBlendMode mode, + const SkSamplingOptions& 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(this + 1); + const SkRect* tex = reinterpret_cast(xform + count); + const SkColor* colors = + has_colors ? reinterpret_cast(tex + count) : nullptr; + const SkBlendMode mode = static_cast(mode_index); + dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, + nullptr, render_with_attributes); + } +}; + +// Packs efficiently into the same 40 bytes as 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 atlas, + int count, + SkBlendMode mode, + const SkSamplingOptions& 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(this + 1); + const SkRect* tex = reinterpret_cast(xform + count); + const SkColor* colors = + has_colors ? reinterpret_cast(tex + count) : nullptr; + const SkBlendMode mode = static_cast(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 picture, bool render_with_attributes) + : render_with_attributes(render_with_attributes), + picture(std::move(picture)) {} + + const bool render_with_attributes; + const sk_sp 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 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 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 rounde up to 16 +// (4 bytes unused) +struct DrawDisplayListOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawDisplayList; + + explicit DrawDisplayListOp(const sk_sp display_list) + : display_list(std::move(display_list)) {} + + sk_sp display_list; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawDisplayList(display_list); + } +}; + +// 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 blob, SkScalar x, SkScalar y) + : x(x), y(y), blob(std::move(blob)) {} + + const SkScalar x; + const SkScalar y; + const sk_sp 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, \ + SkColor color, \ + SkScalar elevation, \ + SkScalar dpr) \ + : color(color), elevation(elevation), dpr(dpr), path(path) {} \ + \ + const SkColor 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_ diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index 694ede58f6210..227e0a21441c4 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -2,19 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/display_list/display_list_canvas.h" +#include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/display_list_canvas_recorder.h" #include "flutter/fml/math.h" #include "flutter/testing/testing.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkImageInfo.h" -#include "third_party/skia/include/core/SkPath.h" -#include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkPictureRecorder.h" -#include "third_party/skia/include/core/SkRRect.h" -#include "third_party/skia/include/core/SkRSXform.h" #include "third_party/skia/include/core/SkSurface.h" -#include "third_party/skia/include/core/SkTextBlob.h" -#include "third_party/skia/include/core/SkVertices.h" #include "third_party/skia/include/effects/SkBlenders.h" #include "third_party/skia/include/effects/SkDashPathEffect.h" #include "third_party/skia/include/effects/SkGradientShader.h" diff --git a/display_list/display_list_utils.cc b/display_list/display_list_utils.cc index db341c73f8948..24ffa869e1997 100644 --- a/display_list/display_list_utils.cc +++ b/display_list/display_list_utils.cc @@ -7,7 +7,7 @@ #include #include -#include "flutter/display_list/display_list_canvas.h" +#include "flutter/display_list/display_list_canvas_dispatcher.h" #include "flutter/fml/logging.h" #include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/core/SkPath.h" diff --git a/display_list/display_list_utils.h b/display_list/display_list_utils.h index 2a60cadcdffbf..220098c41ac24 100644 --- a/display_list/display_list_utils.h +++ b/display_list/display_list_utils.h @@ -8,6 +8,7 @@ #include #include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" #include "third_party/skia/include/core/SkMaskFilter.h" diff --git a/display_list/types.h b/display_list/types.h new file mode 100644 index 0000000000000..5ee069c8829cf --- /dev/null +++ b/display_list/types.h @@ -0,0 +1,25 @@ +// 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_TYPES_H_ +#define FLUTTER_DISPLAY_LIST_TYPES_H_ + +#include "flutter/fml/macros.h" +#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/SkPath.h" +#include "third_party/skia/include/core/SkPathEffect.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkRSXform.h" +#include "third_party/skia/include/core/SkShader.h" +#include "third_party/skia/include/core/SkTextBlob.h" +#include "third_party/skia/include/core/SkVertices.h" +#include "third_party/skia/include/utils/SkShadowUtils.h" + +#endif // FLUTTER_DISPLAY_LIST_TYPES_H_ diff --git a/flow/layers/checkerboard_layertree_unittests.cc b/flow/layers/checkerboard_layertree_unittests.cc index c082066f67f13..4f7f4cbdc9d9c 100644 --- a/flow/layers/checkerboard_layertree_unittests.cc +++ b/flow/layers/checkerboard_layertree_unittests.cc @@ -2,12 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flutter/display_list/display_list_canvas_dispatcher.h" #include "flutter/flow/layers/clip_path_layer.h" #include "flutter/flow/layers/clip_rect_layer.h" #include "flutter/flow/layers/clip_rrect_layer.h" #include "flutter/flow/layers/physical_shape_layer.h" - -#include "flutter/display_list/display_list_canvas.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" #include "flutter/fml/macros.h" diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index de3f832f0b545..f360602cf8376 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -4,7 +4,7 @@ #include "flutter/flow/layers/display_list_layer.h" -#include "flutter/display_list/display_list_canvas.h" +#include "flutter/display_list/display_list_builder.h" namespace flutter { diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index fafd4f25dd855..229b54891250c 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -6,6 +6,7 @@ #include "flutter/flow/layers/display_list_layer.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/skia_gpu_object_layer_test.h" #include "flutter/fml/macros.h" diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 06661898cb5ba..a5bac6ad14ab6 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -4,7 +4,7 @@ #include "flutter/flow/layers/physical_shape_layer.h" -#include "flutter/display_list/display_list_canvas.h" +#include "flutter/display_list/display_list_canvas_dispatcher.h" #include "flutter/flow/paint_utils.h" namespace flutter { diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index 133ce35f33b85..5e71d61bea09c 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -4,7 +4,7 @@ #include "flutter/flow/layers/physical_shape_layer.h" -#include "flutter/display_list/display_list_canvas.h" +#include "flutter/display_list/display_list_canvas_dispatcher.h" #include "flutter/flow/testing/diff_context_test.h" #include "flutter/flow/testing/layer_test.h" #include "flutter/flow/testing/mock_layer.h" diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 5428b76604903..4ac75fe13048b 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/flow/raster_cache.h" - #include "flutter/flow/testing/mock_raster_cache.h" #include "gtest/gtest.h" #include "third_party/skia/include/core/SkCanvas.h" diff --git a/flow/testing/diff_context_test.cc b/flow/testing/diff_context_test.cc index 7276dd72c124f..54596df8cce8b 100644 --- a/flow/testing/diff_context_test.cc +++ b/flow/testing/diff_context_test.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "diff_context_test.h" +#include "flutter/display_list/display_list_builder.h" namespace flutter { namespace testing { diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index adc13a8c9e9bf..4e88270e001d6 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -7,6 +7,8 @@ #include +#include "flutter/display_list/display_list_builder.h" +#include "flutter/display_list/display_list_canvas_dispatcher.h" #include "flutter/flow/layers/physical_shape_layer.h" #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/matrix.h" diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index 25e3913cfc323..eaaa69659d8ef 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/painting/paint.h" +#include "flutter/display_list/display_list_builder.h" #include "flutter/fml/logging.h" #include "flutter/lib/ui/painting/color_filter.h" #include "flutter/lib/ui/painting/image_filter.h" diff --git a/lib/ui/painting/paint.h b/lib/ui/painting/paint.h index 4f3ea749eef0f..2e4dad7ad59d8 100644 --- a/lib/ui/painting/paint.h +++ b/lib/ui/painting/paint.h @@ -6,6 +6,7 @@ #define FLUTTER_LIB_UI_PAINTING_PAINT_H_ #include "flutter/display_list/display_list.h" +#include "flutter/display_list/display_list_flags.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/tonic/converter/dart_converter.h" diff --git a/lib/ui/painting/picture_recorder.cc b/lib/ui/painting/picture_recorder.cc index 945b20c309aef..32c888aef4d78 100644 --- a/lib/ui/painting/picture_recorder.cc +++ b/lib/ui/painting/picture_recorder.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/painting/picture_recorder.h" +#include "flutter/display_list/display_list.h" #include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/painting/picture.h" #include "third_party/tonic/converter/dart_converter.h" diff --git a/lib/ui/painting/picture_recorder.h b/lib/ui/painting/picture_recorder.h index d46f1437bcfde..f1c73f4d5a7fb 100644 --- a/lib/ui/painting/picture_recorder.h +++ b/lib/ui/painting/picture_recorder.h @@ -5,7 +5,7 @@ #ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_ #define FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_ -#include "flutter/display_list/display_list_canvas.h" +#include "flutter/display_list/display_list_canvas_recorder.h" #include "flutter/lib/ui/dart_wrapper.h" #include "third_party/skia/include/core/SkPictureRecorder.h"