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"