Implement a DisplayList mechanism similar to the Skia SkLiteDL mechanism (flutter/engine#26928)

This commit is contained in:
Jim Graham 2021-06-30 17:31:02 -07:00 committed by GitHub
parent f117c5bf1e
commit ce11db413a
39 changed files with 6328 additions and 35 deletions

View File

@ -36,6 +36,14 @@ FILE: ../../../flutter/flow/compositor_context.cc
FILE: ../../../flutter/flow/compositor_context.h
FILE: ../../../flutter/flow/diff_context.cc
FILE: ../../../flutter/flow/diff_context.h
FILE: ../../../flutter/flow/display_list.cc
FILE: ../../../flutter/flow/display_list.h
FILE: ../../../flutter/flow/display_list_canvas.cc
FILE: ../../../flutter/flow/display_list_canvas.h
FILE: ../../../flutter/flow/display_list_canvas_unittests.cc
FILE: ../../../flutter/flow/display_list_unittests.cc
FILE: ../../../flutter/flow/display_list_utils.cc
FILE: ../../../flutter/flow/display_list_utils.h
FILE: ../../../flutter/flow/embedded_view_params_unittests.cc
FILE: ../../../flutter/flow/embedded_views.cc
FILE: ../../../flutter/flow/embedded_views.h
@ -67,6 +75,9 @@ FILE: ../../../flutter/flow/layers/color_filter_layer_unittests.cc
FILE: ../../../flutter/flow/layers/container_layer.cc
FILE: ../../../flutter/flow/layers/container_layer.h
FILE: ../../../flutter/flow/layers/container_layer_unittests.cc
FILE: ../../../flutter/flow/layers/display_list_layer.cc
FILE: ../../../flutter/flow/layers/display_list_layer.h
FILE: ../../../flutter/flow/layers/display_list_layer_unittests.cc
FILE: ../../../flutter/flow/layers/image_filter_layer.cc
FILE: ../../../flutter/flow/layers/image_filter_layer.h
FILE: ../../../flutter/flow/layers/image_filter_layer_unittests.cc

View File

@ -163,6 +163,9 @@ struct Settings {
// Selects the SkParagraph implementation of the text layout engine.
bool enable_skparagraph = false;
// Selects the DisplayList for storage of rendering operations.
bool enable_display_list = false;
// All shells in the process share the same VM. The last shell to shutdown
// should typically shut down the VM as well. However, applications depend on
// the behavior of "warming-up" the VM by creating a shell that does not do

View File

@ -12,6 +12,12 @@ source_set("flow") {
"compositor_context.h",
"diff_context.cc",
"diff_context.h",
"display_list.cc",
"display_list.h",
"display_list_canvas.cc",
"display_list_canvas.h",
"display_list_utils.cc",
"display_list_utils.h",
"embedded_views.cc",
"embedded_views.h",
"frame_timings.cc",
@ -30,6 +36,8 @@ source_set("flow") {
"layers/color_filter_layer.h",
"layers/container_layer.cc",
"layers/container_layer.h",
"layers/display_list_layer.cc",
"layers/display_list_layer.h",
"layers/image_filter_layer.cc",
"layers/image_filter_layer.h",
"layers/layer.cc",
@ -123,6 +131,8 @@ if (enable_unittests) {
testonly = true
sources = [
"display_list_canvas_unittests.cc",
"display_list_unittests.cc",
"embedded_view_params_unittests.cc",
"flow_run_all_unittests.cc",
"flow_test_utils.cc",
@ -136,6 +146,7 @@ if (enable_unittests) {
"layers/clip_rrect_layer_unittests.cc",
"layers/color_filter_layer_unittests.cc",
"layers/container_layer_unittests.cc",
"layers/display_list_layer_unittests.cc",
"layers/image_filter_layer_unittests.cc",
"layers/layer_tree_unittests.cc",
"layers/opacity_layer_unittests.cc",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,452 @@
// 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_H_
#define FLUTTER_FLOW_DISPLAY_LIST_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/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.
//
// This file contains the definitions for:
// DisplayList: the base class that holds the information about the
// sequence of operations and can dispatch them to a Dispatcher
// Dispatcher: a pure virtual interface which can be implemented to field
// the requests for purposes such as sending them to an SkCanvas
// or detecting various rendering optimization scenarios
// DisplayListBuilder: a class for constructing a DisplayList from the same
// calls defined in the Dispatcher
//
// Other files include various class definitions for dealing with display
// lists, such as:
// display_list_canvas.h: classes to interact between SkCanvas and DisplayList
// (SkCanvas->DisplayList adapter and vice versa)
//
// display_list_utils.h: various utility classes to ease implementing
// a Dispatcher, including NOP implementations of
// the attribute, clip, and transform methods,
// classes to track attributes, clips, and transforms
// and a class to compute the bounds of a DisplayList
// Any class implementing Dispatcher can inherit from
// these utility classes to simplify its creation
//
// The Flutter DisplayList mechanism can be used in place of the Skia
// SkPicture mechanism. The primary means of communication into and out
// of the DisplayList is through the Dispatcher virtual class which
// provides a nearly 1:1 translation between the records of the DisplayList
// to method calls.
//
// A DisplayList can be created directly using a DisplayListBuilder and
// the Dispatcher methods that it implements, or it can be created from
// a sequence of SkCanvas calls using the DisplayListCanvasRecorder class.
//
// A DisplayList can be read back by implementing the Dispatcher virtual
// methods (with help from some of the classes in the utils file) and
// passing an instance to the dispatch() method, or it can be rendered
// to Skia using a DisplayListCanvasDispatcher or simply by passing an
// SkCanvas pointer to its renderTo() method.
//
// The mechanism is inspired by the SkLiteDL class that is not directly
// supported by Skia, but has been recommended as a basis for custom
// display lists for a number of their customers.
namespace flutter {
#define FOR_EACH_DISPLAY_LIST_OP(V) \
V(SetAA) \
V(SetDither) \
V(SetInvertColors) \
\
V(SetCaps) \
V(SetJoins) \
\
V(SetDrawStyle) \
V(SetStrokeWidth) \
V(SetMiterLimit) \
\
V(SetColor) \
V(SetBlendMode) \
\
V(SetFilterQuality) \
\
V(SetShader) \
V(ClearShader) \
V(SetColorFilter) \
V(ClearColorFilter) \
V(SetImageFilter) \
V(ClearImageFilter) \
\
V(ClearMaskFilter) \
V(SetMaskFilter) \
V(SetMaskBlurFilterNormal) \
V(SetMaskBlurFilterSolid) \
V(SetMaskBlurFilterOuter) \
V(SetMaskBlurFilterInner) \
\
V(Save) \
V(SaveLayer) \
V(SaveLayerBounds) \
V(Restore) \
\
V(Translate) \
V(Scale) \
V(Rotate) \
V(Skew) \
V(Transform2x3) \
V(Transform3x3) \
\
V(ClipIntersectRect) \
V(ClipIntersectRRect) \
V(ClipIntersectPath) \
V(ClipDifferenceRect) \
V(ClipDifferenceRRect) \
V(ClipDifferencePath) \
\
V(DrawPaint) \
V(DrawColor) \
\
V(DrawLine) \
V(DrawRect) \
V(DrawOval) \
V(DrawCircle) \
V(DrawRRect) \
V(DrawDRRect) \
V(DrawArc) \
V(DrawPath) \
\
V(DrawPoints) \
V(DrawLines) \
V(DrawPolygon) \
V(DrawVertices) \
\
V(DrawImage) \
V(DrawImageRectStrict) \
V(DrawImageRectFast) \
V(DrawImageNine) \
V(DrawImageLattice) \
V(DrawAtlas) \
V(DrawAtlasColored) \
V(DrawAtlasCulled) \
V(DrawAtlasColoredCulled) \
\
V(DrawSkPicture) \
V(DrawSkPictureMatrix) \
V(DrawDisplayList) \
V(DrawTextBlob) \
\
V(DrawShadow)
#define DL_OP_TO_ENUM_VALUE(name) k##name,
enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) };
#undef DL_OP_TO_ENUM_VALUE
class Dispatcher;
class DisplayListBuilder;
// The base class that contains a sequence of rendering operations
// for dispatch to a Dispatcher. These objects must be instantiated
// through an instance of DisplayListBuilder::build().
class DisplayList : public SkRefCnt {
public:
static const SkSamplingOptions NearestSampling;
static const SkSamplingOptions LinearSampling;
static const SkSamplingOptions MipmapSampling;
static const SkSamplingOptions CubicSampling;
DisplayList()
: ptr_(nullptr),
used_(0),
op_count_(0),
unique_id_(0),
bounds_({0, 0, 0, 0}),
bounds_cull_({0, 0, 0, 0}) {}
~DisplayList();
void Dispatch(Dispatcher& ctx) const { Dispatch(ctx, ptr_, ptr_ + used_); }
void RenderTo(SkCanvas* canvas) const;
size_t bytes() const { return used_; }
int op_count() const { return op_count_; }
uint32_t unique_id() const { return unique_id_; }
const SkRect& bounds() {
if (bounds_.width() < 0.0) {
// ComputeBounds() will leave the variable with a
// non-negative width and height
ComputeBounds();
}
return bounds_;
}
bool Equals(const DisplayList& other) const;
private:
DisplayList(uint8_t* ptr, size_t used, int op_count, const SkRect& cull_rect);
uint8_t* ptr_;
size_t used_;
int op_count_;
uint32_t unique_id_;
SkRect bounds_;
// Only used for drawPaint() and drawColor()
SkRect bounds_cull_;
void ComputeBounds();
void Dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const;
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 MaxDrawPointsCount = ((1 << 29) - 1);
virtual void setAA(bool aa) = 0;
virtual void setDither(bool dither) = 0;
virtual void setInvertColors(bool invert) = 0;
virtual void setCaps(SkPaint::Cap cap) = 0;
virtual void setJoins(SkPaint::Join join) = 0;
virtual void setDrawStyle(SkPaint::Style style) = 0;
virtual void setStrokeWidth(SkScalar width) = 0;
virtual void setMiterLimit(SkScalar limit) = 0;
virtual void setColor(SkColor color) = 0;
virtual void setBlendMode(SkBlendMode mode) = 0;
virtual void setFilterQuality(SkFilterQuality quality) = 0;
virtual void setShader(const sk_sp<SkShader> shader) = 0;
virtual void setImageFilter(const sk_sp<SkImageFilter> filter) = 0;
virtual void setColorFilter(const sk_sp<SkColorFilter> filter) = 0;
virtual void setMaskFilter(const sk_sp<SkMaskFilter> filter) = 0;
virtual void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) = 0;
virtual void save() = 0;
virtual void restore() = 0;
virtual void saveLayer(const SkRect* bounds, bool restoreWithPaint) = 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;
virtual void transform2x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt) = 0;
virtual void transform3x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt,
SkScalar px,
SkScalar py,
SkScalar pt) = 0;
virtual void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) = 0;
virtual void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) = 0;
virtual void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) = 0;
virtual void drawPaint() = 0;
virtual void drawColor(SkColor color, SkBlendMode mode) = 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& bounds,
SkScalar start,
SkScalar sweep,
bool useCenter) = 0;
virtual void drawPoints(SkCanvas::PointMode mode,
uint32_t count,
const SkPoint pts[]) = 0;
virtual void drawVertices(const sk_sp<SkVertices> vertices,
SkBlendMode mode) = 0;
virtual void drawImage(const sk_sp<SkImage> image,
const SkPoint point,
const SkSamplingOptions& sampling) = 0;
virtual void drawImageRect(const sk_sp<SkImage> image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
SkCanvas::SrcRectConstraint constraint) = 0;
virtual void drawImageNine(const sk_sp<SkImage> image,
const SkIRect& center,
const SkRect& dst,
SkFilterMode filter) = 0;
virtual void drawImageLattice(const sk_sp<SkImage> image,
const SkCanvas::Lattice& lattice,
const SkRect& dst,
SkFilterMode filter,
bool with_paint) = 0;
virtual void drawAtlas(const sk_sp<SkImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const SkColor colors[],
int count,
SkBlendMode mode,
const SkSamplingOptions& sampling,
const SkRect* cullRect) = 0;
virtual void drawPicture(const sk_sp<SkPicture> picture,
const SkMatrix* matrix,
bool with_save_layer) = 0;
virtual void drawDisplayList(const sk_sp<DisplayList> display_list) = 0;
virtual void drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) = 0;
virtual void drawShadow(const SkPath& path,
const SkColor color,
const SkScalar elevation,
bool occludes) = 0;
};
// The primary class used to build a display list. The list of methods
// here matches the list of methods invoked during dispatch().
// 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 {
public:
DisplayListBuilder(const SkRect& cull = SkRect::MakeEmpty());
~DisplayListBuilder();
void setAA(bool aa) override;
void setDither(bool dither) override;
void setInvertColors(bool invert) override;
void setCaps(SkPaint::Cap cap) override;
void setJoins(SkPaint::Join join) override;
void setDrawStyle(SkPaint::Style style) override;
void setStrokeWidth(SkScalar width) override;
void setMiterLimit(SkScalar limit) override;
void setColor(SkColor color) override;
void setBlendMode(SkBlendMode mode) override;
void setFilterQuality(SkFilterQuality quality) override;
void setShader(sk_sp<SkShader> shader) override;
void setImageFilter(sk_sp<SkImageFilter> filter) override;
void setColorFilter(sk_sp<SkColorFilter> filter) override;
void setMaskFilter(sk_sp<SkMaskFilter> filter) override;
void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override;
void save() override;
void restore() override;
void saveLayer(const SkRect* bounds, bool restoreWithPaint) 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;
void transform2x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt) override;
void transform3x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt,
SkScalar px,
SkScalar py,
SkScalar pt) override;
void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override;
void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override;
void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) 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<SkVertices> vertices,
SkBlendMode mode) override;
void drawImage(const sk_sp<SkImage> image,
const SkPoint point,
const SkSamplingOptions& sampling) override;
void drawImageRect(
const sk_sp<SkImage> image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
SkCanvas::SrcRectConstraint constraint =
SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint) override;
void drawImageNine(const sk_sp<SkImage> image,
const SkIRect& center,
const SkRect& dst,
SkFilterMode filter) override;
void drawImageLattice(const sk_sp<SkImage> image,
const SkCanvas::Lattice& lattice,
const SkRect& dst,
SkFilterMode filter,
bool with_paint) override;
void drawAtlas(const sk_sp<SkImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const SkColor colors[],
int count,
SkBlendMode mode,
const SkSamplingOptions& sampling,
const SkRect* cullRect) override;
void drawPicture(const sk_sp<SkPicture> picture,
const SkMatrix* matrix,
bool with_save_layer) override;
void drawDisplayList(const sk_sp<DisplayList> display_list) override;
void drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) override;
void drawShadow(const SkPath& path,
const SkColor color,
const SkScalar elevation,
bool occludes) override;
sk_sp<DisplayList> Build();
private:
SkAutoTMalloc<uint8_t> storage_;
size_t used_ = 0;
size_t allocated_ = 0;
int op_count_ = 0;
int save_level_ = 0;
SkRect cull_;
template <typename T, typename... Args>
void* Push(size_t extra, Args&&... args);
};
} // namespace flutter
#endif // FLUTTER_FLOW_DISPLAY_LIST_H_

View File

@ -0,0 +1,481 @@
// 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/flow/display_list_canvas.h"
#include "flutter/flow/layers/physical_shape_layer.h"
#include "third_party/skia/include/core/SkMaskFilter.h"
#include "third_party/skia/include/core/SkTextBlob.h"
namespace flutter {
void DisplayListCanvasDispatcher::save() {
canvas_->save();
}
void DisplayListCanvasDispatcher::restore() {
canvas_->restore();
}
void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds,
bool restore_with_paint) {
canvas_->saveLayer(bounds, restore_with_paint ? &paint() : nullptr);
}
void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) {
canvas_->translate(tx, ty);
}
void DisplayListCanvasDispatcher::scale(SkScalar sx, SkScalar sy) {
canvas_->scale(sx, sy);
}
void DisplayListCanvasDispatcher::rotate(SkScalar degrees) {
canvas_->rotate(degrees);
}
void DisplayListCanvasDispatcher::skew(SkScalar sx, SkScalar sy) {
canvas_->skew(sx, sy);
}
void DisplayListCanvasDispatcher::transform2x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt) {
canvas_->concat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, 0, 0, 1));
}
void DisplayListCanvasDispatcher::transform3x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt,
SkScalar px,
SkScalar py,
SkScalar pt) {
canvas_->concat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, px, py, pt));
}
void DisplayListCanvasDispatcher::clipRect(const SkRect& rect,
bool isAA,
SkClipOp clip_op) {
canvas_->clipRect(rect, clip_op, isAA);
}
void DisplayListCanvasDispatcher::clipRRect(const SkRRect& rrect,
bool isAA,
SkClipOp clip_op) {
canvas_->clipRRect(rrect, clip_op, isAA);
}
void DisplayListCanvasDispatcher::clipPath(const SkPath& path,
bool isAA,
SkClipOp clip_op) {
canvas_->clipPath(path, clip_op, isAA);
}
void DisplayListCanvasDispatcher::drawPaint() {
canvas_->drawPaint(paint());
}
void DisplayListCanvasDispatcher::drawColor(SkColor color, SkBlendMode mode) {
canvas_->drawColor(color, mode);
}
void DisplayListCanvasDispatcher::drawLine(const SkPoint& p0,
const SkPoint& p1) {
canvas_->drawLine(p0, p1, paint());
}
void DisplayListCanvasDispatcher::drawRect(const SkRect& rect) {
canvas_->drawRect(rect, paint());
}
void DisplayListCanvasDispatcher::drawOval(const SkRect& bounds) {
canvas_->drawOval(bounds, paint());
}
void DisplayListCanvasDispatcher::drawCircle(const SkPoint& center,
SkScalar radius) {
canvas_->drawCircle(center, radius, paint());
}
void DisplayListCanvasDispatcher::drawRRect(const SkRRect& rrect) {
canvas_->drawRRect(rrect, paint());
}
void DisplayListCanvasDispatcher::drawDRRect(const SkRRect& outer,
const SkRRect& inner) {
canvas_->drawDRRect(outer, inner, paint());
}
void DisplayListCanvasDispatcher::drawPath(const SkPath& path) {
canvas_->drawPath(path, paint());
}
void DisplayListCanvasDispatcher::drawArc(const SkRect& bounds,
SkScalar start,
SkScalar sweep,
bool useCenter) {
canvas_->drawArc(bounds, start, sweep, useCenter, paint());
}
void DisplayListCanvasDispatcher::drawPoints(SkCanvas::PointMode mode,
uint32_t count,
const SkPoint pts[]) {
canvas_->drawPoints(mode, count, pts, paint());
}
void DisplayListCanvasDispatcher::drawVertices(const sk_sp<SkVertices> vertices,
SkBlendMode mode) {
canvas_->drawVertices(vertices, mode, paint());
}
void DisplayListCanvasDispatcher::drawImage(const sk_sp<SkImage> image,
const SkPoint point,
const SkSamplingOptions& sampling) {
canvas_->drawImage(image, point.fX, point.fY, sampling, &paint());
}
void DisplayListCanvasDispatcher::drawImageRect(
const sk_sp<SkImage> image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
SkCanvas::SrcRectConstraint constraint) {
canvas_->drawImageRect(image, src, dst, sampling, &paint(), constraint);
}
void DisplayListCanvasDispatcher::drawImageNine(const sk_sp<SkImage> image,
const SkIRect& center,
const SkRect& dst,
SkFilterMode filter) {
canvas_->drawImageNine(image.get(), center, dst, filter, &paint());
}
void DisplayListCanvasDispatcher::drawImageLattice(
const sk_sp<SkImage> image,
const SkCanvas::Lattice& lattice,
const SkRect& dst,
SkFilterMode filter,
bool with_paint) {
canvas_->drawImageLattice(image.get(), lattice, dst, filter,
with_paint ? &paint() : nullptr);
}
void DisplayListCanvasDispatcher::drawAtlas(const sk_sp<SkImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const SkColor colors[],
int count,
SkBlendMode mode,
const SkSamplingOptions& sampling,
const SkRect* cullRect) {
canvas_->drawAtlas(atlas.get(), xform, tex, colors, count, mode, sampling,
cullRect, &paint());
}
void DisplayListCanvasDispatcher::drawPicture(const sk_sp<SkPicture> picture,
const SkMatrix* matrix,
bool with_save_layer) {
canvas_->drawPicture(picture, matrix, with_save_layer ? &paint() : nullptr);
}
void DisplayListCanvasDispatcher::drawDisplayList(
const sk_sp<DisplayList> display_list) {
int save_count = canvas_->save();
{
DisplayListCanvasDispatcher dispatcher(canvas_);
display_list->Dispatch(dispatcher);
}
canvas_->restoreToCount(save_count);
}
void DisplayListCanvasDispatcher::drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) {
canvas_->drawTextBlob(blob, x, y, paint());
}
void DisplayListCanvasDispatcher::drawShadow(const SkPath& path,
const SkColor color,
const SkScalar elevation,
bool occludes) {
flutter::PhysicalShapeLayer::DrawShadow(canvas_, path, color, elevation,
occludes, 1.0);
}
DisplayListCanvasRecorder::DisplayListCanvasRecorder(const SkRect& bounds)
: SkCanvasVirtualEnforcer(bounds.width(), bounds.height()),
builder_(sk_make_sp<DisplayListBuilder>(bounds)) {}
sk_sp<DisplayList> DisplayListCanvasRecorder::Build() {
sk_sp<DisplayList> display_list = builder_->Build();
builder_.reset();
return display_list;
}
void DisplayListCanvasRecorder::didConcat44(const SkM44& m44) {
SkMatrix m = m44.asM33();
if (m.hasPerspective()) {
builder_->transform3x3(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7],
m[8]);
} else {
builder_->transform2x3(m[0], m[1], m[2], m[3], m[4], m[5]);
}
}
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 edgeStyle) {
builder_->clipRect(rect, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle,
clip_op);
}
void DisplayListCanvasRecorder::onClipRRect(const SkRRect& rrect,
SkClipOp clip_op,
ClipEdgeStyle edgeStyle) {
builder_->clipRRect(rrect, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle,
clip_op);
}
void DisplayListCanvasRecorder::onClipPath(const SkPath& path,
SkClipOp clip_op,
ClipEdgeStyle edgeStyle) {
builder_->clipPath(path, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle,
clip_op);
}
void DisplayListCanvasRecorder::willSave() {
builder_->save();
}
SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy(
const SaveLayerRec& rec) {
if (rec.fPaint) {
RecordPaintAttributes(rec.fPaint, DrawType::kSaveLayerOpType);
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) {
RecordPaintAttributes(&paint, DrawType::kFillOpType);
builder_->drawPaint();
}
void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect,
const SkPaint& paint) {
RecordPaintAttributes(&paint, DrawType::kDrawOpType);
builder_->drawRect(rect);
}
void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect,
const SkPaint& paint) {
RecordPaintAttributes(&paint, DrawType::kDrawOpType);
builder_->drawRRect(rrect);
}
void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer,
const SkRRect& inner,
const SkPaint& paint) {
RecordPaintAttributes(&paint, DrawType::kDrawOpType);
builder_->drawDRRect(outer, inner);
}
void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect,
const SkPaint& paint) {
RecordPaintAttributes(&paint, DrawType::kDrawOpType);
builder_->drawOval(rect);
}
void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect,
SkScalar startAngle,
SkScalar sweepAngle,
bool useCenter,
const SkPaint& paint) {
RecordPaintAttributes(&paint, DrawType::kDrawOpType);
builder_->drawArc(rect, startAngle, sweepAngle, useCenter);
}
void DisplayListCanvasRecorder::onDrawPath(const SkPath& path,
const SkPaint& paint) {
RecordPaintAttributes(&paint, DrawType::kDrawOpType);
builder_->drawPath(path);
}
void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode,
size_t count,
const SkPoint pts[],
const SkPaint& paint) {
RecordPaintAttributes(&paint, DrawType::kStrokeOpType);
if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) {
builder_->drawLine(pts[0], pts[1]);
} else {
uint32_t count32 = static_cast<uint32_t>(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) {
RecordPaintAttributes(&paint, DrawType::kDrawOpType);
builder_->drawVertices(sk_ref_sp(vertices), mode);
}
void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image,
SkScalar dx,
SkScalar dy,
const SkSamplingOptions& sampling,
const SkPaint* paint) {
RecordPaintAttributes(paint, DrawType::kImageOpType);
builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling);
}
void DisplayListCanvasRecorder::onDrawImageRect2(
const SkImage* image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
const SkPaint* paint,
SrcRectConstraint constraint) {
RecordPaintAttributes(paint, DrawType::kImageRectOpType);
builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling, 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 {
RecordPaintAttributes(paint, DrawType::kImageOpType);
}
}
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) {
RecordPaintAttributes(paint, DrawType::kImageOpType);
builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode,
sampling, cull);
}
void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob,
SkScalar x,
SkScalar y,
const SkPaint& paint) {
RecordPaintAttributes(&paint, DrawType::kDrawOpType);
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) {
RecordPaintAttributes(paint, DrawType::kSaveLayerOpType);
}
builder_->drawPicture(sk_ref_sp(picture), matrix, paint != nullptr);
}
void DisplayListCanvasRecorder::RecordPaintAttributes(const SkPaint* paint,
DrawType type) {
int dataNeeded;
switch (type) {
case DrawType::kDrawOpType:
dataNeeded = kDrawMask_;
break;
case DrawType::kFillOpType:
dataNeeded = kPaintMask_;
break;
case DrawType::kStrokeOpType:
dataNeeded = kStrokeMask_;
break;
case DrawType::kImageOpType:
dataNeeded = kImageMask_;
break;
case DrawType::kImageRectOpType:
dataNeeded = kImageRectMask_;
break;
case DrawType::kSaveLayerOpType:
dataNeeded = kSaveLayerMask_;
break;
default:
FML_DCHECK(false);
return;
}
if (paint == nullptr) {
paint = new SkPaint();
}
if ((dataNeeded & kAaNeeded_) != 0 && current_aa_ != paint->isAntiAlias()) {
builder_->setAA(current_aa_ = paint->isAntiAlias());
}
if ((dataNeeded & kDitherNeeded_) != 0 &&
current_dither_ != paint->isDither()) {
builder_->setDither(current_dither_ = paint->isDither());
}
if ((dataNeeded & kColorNeeded_) != 0 &&
current_color_ != paint->getColor()) {
builder_->setColor(current_color_ = paint->getColor());
}
if ((dataNeeded & kBlendNeeded_) != 0 &&
current_blend_ != paint->getBlendMode()) {
builder_->setBlendMode(current_blend_ = paint->getBlendMode());
}
// invert colors is a Flutter::Paint thing, not an SkPaint thing
// if ((dataNeeded & invertColorsNeeded_) != 0 &&
// currentInvertColors_ != paint->???) {
// currentInvertColors_ = paint->invertColors;
// addOp_(currentInvertColors_
// ? _CanvasOp.setInvertColors
// : _CanvasOp.clearInvertColors, 0);
// }
if ((dataNeeded & kPaintStyleNeeded_) != 0) {
if (current_style_ != paint->getStyle()) {
builder_->setDrawStyle(current_style_ = paint->getStyle());
}
if (current_style_ == SkPaint::Style::kStroke_Style) {
dataNeeded |= kStrokeStyleNeeded_;
}
}
if ((dataNeeded & kStrokeStyleNeeded_) != 0) {
if (current_stroke_width_ != paint->getStrokeWidth()) {
builder_->setStrokeWidth(current_stroke_width_ = paint->getStrokeWidth());
}
if (current_cap_ != paint->getStrokeCap()) {
builder_->setCaps(current_cap_ = paint->getStrokeCap());
}
if (current_join_ != paint->getStrokeJoin()) {
builder_->setJoins(current_join_ = paint->getStrokeJoin());
}
if (current_miter_limit_ != paint->getStrokeMiter()) {
builder_->setMiterLimit(current_miter_limit_ = paint->getStrokeMiter());
}
}
if ((dataNeeded & kFilterQualityNeeded_) != 0 &&
current_fq_ != paint->getFilterQuality()) {
builder_->setFilterQuality(current_fq_ = paint->getFilterQuality());
}
if ((dataNeeded & kShaderNeeded_) != 0 &&
current_shader_.get() != paint->getShader()) {
builder_->setShader(current_shader_ = sk_ref_sp(paint->getShader()));
}
if ((dataNeeded & kColorFilterNeeded_) != 0 &&
current_color_filter_.get() != paint->getColorFilter()) {
builder_->setColorFilter(current_color_filter_ =
sk_ref_sp(paint->getColorFilter()));
}
if ((dataNeeded & kImageFilterNeeded_) != 0 &&
current_image_filter_.get() != paint->getImageFilter()) {
builder_->setImageFilter(current_image_filter_ =
sk_ref_sp(paint->getImageFilter()));
}
if ((dataNeeded & kMaskFilterNeeded_) != 0 &&
current_mask_filter_.get() != paint->getMaskFilter()) {
builder_->setMaskFilter(current_mask_filter_ =
sk_ref_sp(paint->getMaskFilter()));
}
}
} // namespace flutter

View File

@ -0,0 +1,312 @@
// 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/flow/display_list.h"
#include "flutter/flow/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:
DisplayListCanvasDispatcher(SkCanvas* canvas) : canvas_(canvas) {}
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;
void transform2x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt) override;
void transform3x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt,
SkScalar px,
SkScalar py,
SkScalar pt) override;
void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override;
void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override;
void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) 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<SkVertices> vertices,
SkBlendMode mode) override;
void drawImage(const sk_sp<SkImage> image,
const SkPoint point,
const SkSamplingOptions& sampling) override;
void drawImageRect(const sk_sp<SkImage> image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
SkCanvas::SrcRectConstraint constraint) override;
void drawImageNine(const sk_sp<SkImage> image,
const SkIRect& center,
const SkRect& dst,
SkFilterMode filter) override;
void drawImageLattice(const sk_sp<SkImage> image,
const SkCanvas::Lattice& lattice,
const SkRect& dst,
SkFilterMode filter,
bool with_paint) override;
void drawAtlas(const sk_sp<SkImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const SkColor colors[],
int count,
SkBlendMode mode,
const SkSamplingOptions& sampling,
const SkRect* cullRect) override;
void drawPicture(const sk_sp<SkPicture> picture,
const SkMatrix* matrix,
bool with_save_layer) override;
void drawDisplayList(const sk_sp<DisplayList> display_list) override;
void drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) override;
void drawShadow(const SkPath& path,
const SkColor color,
const SkScalar elevation,
bool occludes) override;
private:
SkCanvas* canvas_;
};
// Receives all methods on SkCanvas and sends them to a DisplayListBuilder
class DisplayListCanvasRecorder
: public SkCanvasVirtualEnforcer<SkNoDrawCanvas>,
public SkRefCnt {
public:
DisplayListCanvasRecorder(const SkRect& bounds);
const sk_sp<DisplayListBuilder> builder() { return builder_; }
sk_sp<DisplayList> 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;
enum class DrawType {
// The operation will be an image operation
kImageOpType,
// The operation will be an imageRect operation
kImageRectOpType,
// The operation will be a fill or stroke depending on the paint.style
kDrawOpType,
// The operation will be a fill (ignoring paint.style)
kFillOpType,
// The operation will be a stroke (ignoring paint.style)
kStrokeOpType,
// The operation will be a saveLayer with a paint object
kSaveLayerOpType,
};
void RecordPaintAttributes(const SkPaint* paint, DrawType type);
private:
sk_sp<DisplayListBuilder> builder_;
// Mask bits for the various attributes that might be needed for a given
// operation.
// clang-format off
static constexpr int kAaNeeded_ = 1 << 0;
static constexpr int kColorNeeded_ = 1 << 1;
static constexpr int kBlendNeeded_ = 1 << 2;
static constexpr int kInvertColorsNeeded_ = 1 << 3;
static constexpr int kFilterQualityNeeded_ = 1 << 4;
static constexpr int kPaintStyleNeeded_ = 1 << 5;
static constexpr int kStrokeStyleNeeded_ = 1 << 6;
static constexpr int kShaderNeeded_ = 1 << 7;
static constexpr int kColorFilterNeeded_ = 1 << 8;
static constexpr int kImageFilterNeeded_ = 1 << 9;
static constexpr int kMaskFilterNeeded_ = 1 << 10;
static constexpr int kDitherNeeded_ = 1 << 11;
// clang-format on
// Combinations of the above mask bits that are common to typical "draw"
// calls.
// Note that the strokeStyle_ is handled conditionally depending on whether
// the paintStyle_ attribute value is synchronized. It can also be manually
// specified for operations that will be always stroking, like [drawLine].
static constexpr int kPaintMask_ = kAaNeeded_ | kColorNeeded_ |
kBlendNeeded_ | kInvertColorsNeeded_ |
kColorFilterNeeded_ | kShaderNeeded_ |
kDitherNeeded_ | kImageFilterNeeded_;
static constexpr int kDrawMask_ =
kPaintMask_ | kPaintStyleNeeded_ | kMaskFilterNeeded_;
static constexpr int kStrokeMask_ =
kPaintMask_ | kStrokeStyleNeeded_ | kMaskFilterNeeded_;
static constexpr int kImageMask_ =
kColorNeeded_ | kBlendNeeded_ | kInvertColorsNeeded_ |
kColorFilterNeeded_ | kDitherNeeded_ | kImageFilterNeeded_ |
kFilterQualityNeeded_ | kMaskFilterNeeded_;
static constexpr int kImageRectMask_ = kImageMask_ | kAaNeeded_;
static constexpr int kSaveLayerMask_ =
kColorNeeded_ | kBlendNeeded_ | kInvertColorsNeeded_ |
kColorFilterNeeded_ | kImageFilterNeeded_;
bool current_aa_ = false;
bool current_dither_ = false;
SkColor current_color_ = 0xFF000000;
SkBlendMode current_blend_ = SkBlendMode::kSrcOver;
SkPaint::Style current_style_ = SkPaint::Style::kFill_Style;
SkScalar current_stroke_width_ = 0.0;
SkScalar current_miter_limit_ = 4.0;
SkPaint::Cap current_cap_ = SkPaint::Cap::kButt_Cap;
SkPaint::Join current_join_ = SkPaint::Join::kMiter_Join;
SkFilterQuality current_fq_ = SkFilterQuality::kNone_SkFilterQuality;
sk_sp<SkShader> current_shader_;
sk_sp<SkColorFilter> current_color_filter_;
sk_sp<SkImageFilter> current_image_filter_;
sk_sp<SkMaskFilter> current_mask_filter_;
};
} // namespace flutter
#endif // FLUTTER_FLOW_DISPLAY_LIST_CANVAS_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,830 @@
// 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/flow/display_list_canvas.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/SkGradientShader.h"
#include "third_party/skia/include/effects/SkImageFilters.h"
#include <cmath>
#include "gtest/gtest.h"
namespace flutter {
namespace testing {
constexpr SkPoint end_points[] = {
{0, 0},
{100, 100},
};
constexpr SkColor colors[] = {
SK_ColorGREEN,
SK_ColorYELLOW,
SK_ColorBLUE,
};
constexpr float stops[] = {
0.0,
0.5,
1.0,
};
constexpr float rotate_color_matrix[20] = {
0, 1, 0, 0, 0, //
0, 0, 1, 0, 0, //
1, 0, 0, 0, 0, //
0, 0, 0, 1, 0, //
};
constexpr SkPoint TestPoints[] = {
{10, 10},
{20, 20},
{10, 20},
{20, 10},
};
#define TestPointCount sizeof(TestPoints) / (sizeof(TestPoints[0]))
static const sk_sp<SkShader> TestShader1 =
SkGradientShader::MakeLinear(end_points,
colors,
stops,
3,
SkTileMode::kMirror,
0,
nullptr);
// TestShader2 is identical to TestShader1 and points out that we cannot
// perform a deep compare over our various sk_sp objects because the
// DisplayLists constructed with the two do not compare == below.
static const sk_sp<SkShader> TestShader2 =
SkGradientShader::MakeLinear(end_points,
colors,
stops,
3,
SkTileMode::kMirror,
0,
nullptr);
static const sk_sp<SkShader> TestShader3 =
SkGradientShader::MakeLinear(end_points,
colors,
stops,
3,
SkTileMode::kDecal,
0,
nullptr);
static const sk_sp<SkImageFilter> TestImageFilter =
SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr);
static const sk_sp<SkColorFilter> TestColorFilter =
SkColorFilters::Matrix(rotate_color_matrix);
static const sk_sp<SkMaskFilter> TestMaskFilter =
SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0);
constexpr SkRect TestBounds = SkRect::MakeLTRB(10, 10, 50, 60);
static const SkRRect TestRRect = SkRRect::MakeRectXY(TestBounds, 5, 5);
static const SkRRect TestRRectRect = SkRRect::MakeRect(TestBounds);
static const SkRRect TestInnerRRect =
SkRRect::MakeRectXY(TestBounds.makeInset(5, 5), 2, 2);
static const SkPath TestPathRect = SkPath::Rect(TestBounds);
static const SkPath TestPathOval = SkPath::Oval(TestBounds);
static const SkPath TestPath1 =
SkPath::Polygon({{0, 0}, {10, 10}, {10, 0}, {0, 10}}, true);
static const SkPath TestPath2 =
SkPath::Polygon({{0, 0}, {10, 10}, {0, 10}, {10, 0}}, true);
static const SkPath TestPath3 =
SkPath::Polygon({{0, 0}, {10, 10}, {10, 0}, {0, 10}}, false);
static const SkMatrix TestMatrix1 = SkMatrix::Scale(2, 2);
static const SkMatrix TestMatrix2 = SkMatrix::RotateDeg(45);
static sk_sp<SkImage> MakeTestImage(int w, int h, int checker_size) {
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(w, h);
SkCanvas* canvas = surface->getCanvas();
SkPaint p0, p1;
p0.setStyle(SkPaint::kFill_Style);
p0.setColor(SK_ColorGREEN);
p1.setStyle(SkPaint::kFill_Style);
p1.setColor(SK_ColorBLUE);
p1.setAlpha(128);
for (int y = 0; y < w; y += checker_size) {
for (int x = 0; x < h; x += checker_size) {
SkPaint& cellp = ((x + y) & 1) == 0 ? p0 : p1;
canvas->drawRect(SkRect::MakeXYWH(x, y, checker_size, checker_size),
cellp);
}
}
return surface->makeImageSnapshot();
}
static sk_sp<SkImage> TestImage1 = MakeTestImage(40, 40, 5);
static sk_sp<SkImage> TestImage2 = MakeTestImage(50, 50, 5);
static sk_sp<SkVertices> TestVertices1 =
SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
3,
TestPoints,
nullptr,
colors);
static sk_sp<SkVertices> TestVertices2 =
SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode,
3,
TestPoints,
nullptr,
colors);
static constexpr int TestDivs1[] = {10, 20, 30};
static constexpr int TestDivs2[] = {15, 20, 25};
static constexpr int TestDivs3[] = {15, 25};
static constexpr SkCanvas::Lattice::RectType TestRTypes[] = {
SkCanvas::Lattice::RectType::kDefault,
SkCanvas::Lattice::RectType::kTransparent,
SkCanvas::Lattice::RectType::kFixedColor,
SkCanvas::Lattice::RectType::kDefault,
SkCanvas::Lattice::RectType::kTransparent,
SkCanvas::Lattice::RectType::kFixedColor,
SkCanvas::Lattice::RectType::kDefault,
SkCanvas::Lattice::RectType::kTransparent,
SkCanvas::Lattice::RectType::kFixedColor,
};
static constexpr SkColor TestLatticeColors[] = {
SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW,
SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW,
SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW,
};
static constexpr SkIRect TestLatticeSrcRect = {1, 1, 39, 39};
static sk_sp<SkPicture> MakeTestPicture(int w, int h, SkColor color) {
SkPictureRecorder recorder;
SkCanvas* cv = recorder.beginRecording(TestBounds);
SkPaint paint;
paint.setColor(color);
paint.setStyle(SkPaint::kFill_Style);
cv->drawRect(SkRect::MakeWH(w, h), paint);
return recorder.finishRecordingAsPicture();
}
static sk_sp<SkPicture> TestPicture1 = MakeTestPicture(20, 20, SK_ColorGREEN);
static sk_sp<SkPicture> TestPicture2 = MakeTestPicture(25, 25, SK_ColorBLUE);
static sk_sp<DisplayList> MakeTestDisplayList(int w, int h, SkColor color) {
DisplayListBuilder builder;
builder.setColor(color);
builder.drawRect(SkRect::MakeWH(w, h));
return builder.Build();
}
static sk_sp<DisplayList> TestDisplayList1 =
MakeTestDisplayList(20, 20, SK_ColorGREEN);
static sk_sp<DisplayList> TestDisplayList2 =
MakeTestDisplayList(25, 25, SK_ColorBLUE);
static sk_sp<SkTextBlob> MakeTextBlob(std::string string) {
return SkTextBlob::MakeFromText(string.c_str(), string.size(), SkFont(),
SkTextEncoding::kUTF8);
}
static sk_sp<SkTextBlob> TestBlob1 = MakeTextBlob("TestBlob1");
static sk_sp<SkTextBlob> TestBlob2 = MakeTextBlob("TestBlob2");
// ---------------
// Test Suite data
// ---------------
typedef const std::function<void(DisplayListBuilder&)> DlInvoker;
struct DisplayListInvocation {
int op_count;
size_t byte_count;
// in some cases, running the sequence through an SkCanvas will result
// in fewer ops/bytes. Attribute invocations are recorded in an SkPaint
// and not forwarded on, and SkCanvas culls unused save/restore/transforms.
int sk_op_count;
size_t sk_byte_count;
DlInvoker invoker;
bool sk_version_matches() {
return (op_count == sk_op_count && byte_count == sk_byte_count);
}
sk_sp<DisplayList> Build() {
DisplayListBuilder builder;
invoker(builder);
return builder.Build();
}
};
struct DisplayListInvocationGroup {
std::string op_name;
std::vector<DisplayListInvocation> variants;
};
std::vector<DisplayListInvocationGroup> allGroups = {
{ "SetAA", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setAA(false);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setAA(true);}},
}
},
{ "SetDither", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDither(false);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDither(true);}},
}
},
{ "SetInvertColors", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setInvertColors(false);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setInvertColors(true);}},
}
},
{ "SetStrokeCap", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kButt_Cap);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kRound_Cap);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kSquare_Cap);}},
}
},
{ "SetStrokeJoin", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kBevel_Join);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kRound_Join);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kMiter_Join);}},
}
},
{ "SetDrawStyle", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kFill_Style);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kStroke_Style);}},
}
},
{ "SetStrokeWidth", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(0.0);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(5.0);}},
}
},
{ "SetMiterLimit", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMiterLimit(0.0);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMiterLimit(5.0);}},
}
},
{ "SetColor", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setColor(SK_ColorGREEN);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setColor(SK_ColorBLUE);}},
}
},
{ "SetBlendMode", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kSrcIn);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kDstIn);}},
}
},
{ "SetFilterQuality", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setFilterQuality(kNone_SkFilterQuality);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setFilterQuality(kLow_SkFilterQuality);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setFilterQuality(kMedium_SkFilterQuality);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setFilterQuality(kHigh_SkFilterQuality);}},
}
},
{ "SetShader", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setShader(nullptr);}},
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader1);}},
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader2);}},
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader3);}},
}
},
{ "SetImageFilter", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(nullptr);}},
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(TestImageFilter);}},
}
},
{ "SetColorFilter", {
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(nullptr);}},
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter);}},
}
},
{ "SetMaskFilter", {
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.setMaskFilter(nullptr);}},
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.setMaskFilter(TestMaskFilter);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 3.0);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kSolid_SkBlurStyle, 3.0);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kInner_SkBlurStyle, 3.0);}},
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kOuter_SkBlurStyle, 3.0);}},
}
},
{ "Save(Layer)+Restore", {
// cv.save/restore are ignored if there are no draw calls between them
{2, 16, 0, 0, [](DisplayListBuilder& b) {b.save(); b.restore();}},
{2, 16, 2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, false); b.restore(); }},
{2, 16, 2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, true); b.restore(); }},
{2, 32, 2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, false); b.restore(); }},
{2, 32, 2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, true); b.restore(); }},
}
},
{ "Translate", {
// cv.translate(0, 0) is ignored
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.translate(0, 0);}},
{1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(10, 10);}},
{1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(10, 15);}},
{1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(15, 10);}},
}
},
{ "Scale", {
// cv.scale(1, 1) is ignored
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.scale(1, 1);}},
{1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(2, 2);}},
{1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(2, 3);}},
{1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(3, 2);}},
}
},
{ "Rotate", {
// cv.rotate(0) is ignored, otherwise expressed as concat(rotmatrix)
{1, 8, 0, 0, [](DisplayListBuilder& b) {b.rotate(0);}},
{1, 8, 1, 32, [](DisplayListBuilder& b) {b.rotate(30);}},
{1, 8, 1, 32, [](DisplayListBuilder& b) {b.rotate(45);}},
}
},
{ "Skew", {
// cv.skew(0, 0) is ignored, otherwise expressed as concat(skewmatrix)
{1, 16, 0, 0, [](DisplayListBuilder& b) {b.skew(0, 0);}},
{1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.1, 0.1);}},
{1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.1, 0.2);}},
{1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.2, 0.1);}},
}
},
{ "Transform2x3", {
// cv.transform(identity) is ignored
{1, 32, 0, 0, [](DisplayListBuilder& b) {b.transform2x3(1, 0, 0, 0, 1, 0);}},
{1, 32, 1, 32, [](DisplayListBuilder& b) {b.transform2x3(0, 1, 12, 1, 0, 33);}},
}
},
{ "Transform3x3", {
// cv.transform(identity) is ignored
{1, 40, 0, 0, [](DisplayListBuilder& b) {b.transform3x3(1, 0, 0, 0, 1, 0, 0, 0, 1);}},
{1, 40, 1, 40, [](DisplayListBuilder& b) {b.transform3x3(0, 1, 12, 1, 0, 33, 0, 0, 12);}},
}
},
{ "ClipRect", {
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kIntersect);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds.makeOffset(1, 1),
true, SkClipOp::kIntersect);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kIntersect);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kDifference);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kDifference);}},
}
},
{ "ClipRRect", {
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kIntersect);}},
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect.makeOffset(1, 1),
true, SkClipOp::kIntersect);}},
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kIntersect);}},
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kDifference);}},
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kDifference);}},
}
},
{ "ClipPath", {
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kIntersect);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath2, true, SkClipOp::kIntersect);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath3, true, SkClipOp::kIntersect);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kIntersect);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kDifference);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kDifference);}},
// clipPath(rect) becomes clipRect
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPathRect, true, SkClipOp::kIntersect);}},
// clipPath(oval) becomes clipRRect
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipPath(TestPathOval, true, SkClipOp::kIntersect);}},
}
},
{ "DrawPaint", {
{1, 8, 1, 8, [](DisplayListBuilder& b) {b.drawPaint();}},
}
},
{ "DrawColor", {
// cv.drawColor becomes cv.drawPaint(paint)
{1, 16, 3, 24, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kSrcIn);}},
{1, 16, 3, 24, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kDstIn);}},
{1, 16, 3, 24, [](DisplayListBuilder& b) {b.drawColor(SK_ColorCYAN, SkBlendMode::kSrcIn);}},
}
},
{ "DrawLine", {
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 10});}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 1}, {10, 10});}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {20, 10});}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 20});}},
}
},
{ "DrawRect", {
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 10});}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 1, 10, 10});}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 20, 10});}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 20});}},
}
},
{ "DrawOval", {
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 10});}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 1, 10, 10});}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 20, 10});}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 20});}},
}
},
{ "DrawCircle", {
// cv.drawCircle becomes cv.drawOval
{1, 16, 1, 24, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 10);}},
{1, 16, 1, 24, [](DisplayListBuilder& b) {b.drawCircle({0, 5}, 10);}},
{1, 16, 1, 24, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 20);}},
}
},
{ "DrawRRect", {
{1, 56, 1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect);}},
{1, 56, 1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect.makeOffset(5, 5));}},
}
},
{ "DrawDRRect", {
{1, 112, 1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect, TestInnerRRect);}},
{1, 112, 1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect.makeOffset(5, 5),
TestInnerRRect.makeOffset(4, 4));}},
}
},
{ "DrawPath", {
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath1);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath2);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath3);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPathRect);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPathOval);}},
}
},
{ "DrawArc", {
{1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, false);}},
{1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds.makeOffset(1, 1),
45, 270, false);}},
{1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 30, 270, false);}},
{1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 260, false);}},
{1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, true);}},
}
},
{ "DrawPoints", {
{1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8,
[](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPoints_PointMode,
TestPointCount,
TestPoints);}},
{1, 8 + (TestPointCount - 1) * 8, 1, 8 + (TestPointCount - 1) * 8,
[](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPoints_PointMode,
TestPointCount - 1,
TestPoints);}},
{1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8,
[](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kLines_PointMode,
TestPointCount,
TestPoints);}},
{1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8,
[](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPolygon_PointMode,
TestPointCount,
TestPoints);}},
}
},
{ "DrawVertices", {
{1, 16, 1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kSrcIn);}},
{1, 16, 1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kDstIn);}},
{1, 16, 1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices2, SkBlendMode::kSrcIn);}},
}
},
{ "DrawImage", {
{1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::NearestSampling);}},
{1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {20, 10}, DisplayList::NearestSampling);}},
{1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 20}, DisplayList::NearestSampling);}},
{1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::LinearSampling);}},
{1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage2, {10, 10}, DisplayList::NearestSampling);}},
}
},
{ "DrawImageRect", {
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
DisplayList::NearestSampling);}},
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
DisplayList::NearestSampling,
SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint);}},
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80},
DisplayList::NearestSampling);}},
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80},
DisplayList::NearestSampling);}},
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
DisplayList::LinearSampling);}},
{1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80},
DisplayList::NearestSampling);}},
}
},
{ "DrawImageNine", {
// SkVanvas::drawImageNine is immediately converted to drawImageLattice
{1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
SkFilterMode::kNearest);}},
{1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80},
SkFilterMode::kNearest);}},
{1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80},
SkFilterMode::kNearest);}},
{1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80},
SkFilterMode::kLinear);}},
{1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80},
SkFilterMode::kNearest);}},
}
},
{ "DrawImageLattice", {
// Lattice:
// const int* fXDivs; //!< x-axis values dividing bitmap
// const int* fYDivs; //!< y-axis values dividing bitmap
// const RectType* fRectTypes; //!< array of fill types
// int fXCount; //!< number of x-coordinates
// int fYCount; //!< number of y-coordinates
// const SkIRect* fBounds; //!< source bounds to draw from
// const SkColor* fColors; //!< array of colors
// size = 64 + fXCount * 4 + fYCount * 4
// if fColors and fRectTypes are not null, add (fXCount + 1) * (fYCount + 1) * 5
{1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1,
{TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr},
{10, 10, 40, 40}, SkFilterMode::kNearest, false);}},
{1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1,
{TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr},
{10, 10, 40, 45}, SkFilterMode::kNearest, false);}},
{1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1,
{TestDivs2, TestDivs1, nullptr, 3, 3, nullptr, nullptr},
{10, 10, 40, 40}, SkFilterMode::kNearest, false);}},
// One less yDiv does not change the allocation due to 8-byte alignment
{1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1,
{TestDivs1, TestDivs1, nullptr, 3, 2, nullptr, nullptr},
{10, 10, 40, 40}, SkFilterMode::kNearest, false);}},
{1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1,
{TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr},
{10, 10, 40, 40}, SkFilterMode::kLinear, false);}},
{2, 96, 2, 96, [](DisplayListBuilder& b) {b.setColor(SK_ColorMAGENTA);
b.drawImageLattice(TestImage1,
{TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr},
{10, 10, 40, 40}, SkFilterMode::kNearest, true);}},
{1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage2,
{TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr},
{10, 10, 40, 40}, SkFilterMode::kNearest, false);}},
// Supplying fBounds does not change size because the Op record always includes it
{1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1,
{TestDivs1, TestDivs1, nullptr, 3, 3, &TestLatticeSrcRect, nullptr},
{10, 10, 40, 40}, SkFilterMode::kNearest, false);}},
{1, 128, 1, 128, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1,
{TestDivs3, TestDivs3, TestRTypes, 2, 2, nullptr, TestLatticeColors},
{10, 10, 40, 40}, SkFilterMode::kNearest, false);}},
}
},
{ "DrawAtlas", {
{1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) {
static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} };
static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} };
b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn,
DisplayList::NearestSampling, nullptr);}},
{1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) {
static SkRSXform xforms[] = { {0, 1, 0, 0}, {0, 1, 0, 0} };
static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} };
b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn,
DisplayList::NearestSampling, nullptr);}},
{1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) {
static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} };
static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 25, 30, 30} };
b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn,
DisplayList::NearestSampling, nullptr);}},
{1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) {
static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} };
static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} };
b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn,
DisplayList::LinearSampling, nullptr);}},
{1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) {
static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} };
static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} };
b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kDstIn,
DisplayList::NearestSampling, nullptr);}},
{1, 56 + 32 + 32, 1, 56 + 32 + 32, [](DisplayListBuilder& b) {
static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} };
static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} };
static SkRect cullRect = { 0, 0, 200, 200 };
b.drawAtlas(TestImage2, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn,
DisplayList::NearestSampling, &cullRect);}},
{1, 40 + 32 + 32 + 8, 1, 40 + 32 + 32 + 8, [](DisplayListBuilder& b) {
static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} };
static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} };
static SkColor colors[] = { SK_ColorBLUE, SK_ColorGREEN };
b.drawAtlas(TestImage1, xforms, texs, colors, 2, SkBlendMode::kSrcIn,
DisplayList::NearestSampling, nullptr);}},
{1, 56 + 32 + 32 + 8, 1, 56 + 32 + 32 + 8, [](DisplayListBuilder& b) {
static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} };
static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} };
static SkColor colors[] = { SK_ColorBLUE, SK_ColorGREEN };
static SkRect cullRect = { 0, 0, 200, 200 };
b.drawAtlas(TestImage1, xforms, texs, colors, 2, SkBlendMode::kSrcIn,
DisplayList::NearestSampling, &cullRect);}},
}
},
{ "DrawPicture", {
// cv.drawPicture cannot be compared as SkCanvas may inline it
{1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, false);}},
{1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture2, nullptr, false);}},
{1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, true);}},
{1, 56, -1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, false);}},
{1, 56, -1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix2, false);}},
{1, 56, -1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, true);}},
}
},
{ "DrawDisplayList", {
// cv.drawDL does not exist
{1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList1);}},
{1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList2);}},
}
},
{ "DrawTextBlob", {
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 10);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 20, 10);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 20);}},
{1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob2, 10, 10);}},
}
},
// The -1 op counts below are to indicate to the framework not to test
// SkCanvas conversion of these ops as it converts the operation into a
// format that is not exposed publicly and so we cannot recapture the
// operation.
// See: https://bugs.chromium.org/p/skia/issues/detail?id=12125
{ "DrawShadow", {
// cv shadows are turned into an opaque ShadowRec which is not exposed
{1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, false);}},
{1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath2, SK_ColorGREEN, 1.0, false);}},
{1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorBLUE, 1.0, false);}},
{1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 2.0, false);}},
{1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, true);}},
}
},
};
TEST(DisplayList, SingleOpSizes) {
for (auto& group : allGroups) {
for (size_t i = 0; i < group.variants.size(); i++) {
auto& invocation = group.variants[i];
sk_sp<DisplayList> dl = invocation.Build();
auto desc = group.op_name + "(variant " + std::to_string(i + 1) + ")";
ASSERT_EQ(dl->op_count(), invocation.op_count) << desc;
EXPECT_EQ(dl->bytes(), invocation.byte_count) << desc;
}
}
}
TEST(DisplayList, SingleOpDisplayListsNotEqualEmpty) {
sk_sp<DisplayList> empty = DisplayListBuilder().Build();
for (auto& group : allGroups) {
for (size_t i = 0; i < group.variants.size(); i++) {
sk_sp<DisplayList> dl = group.variants[i].Build();
auto desc =
group.op_name + "(variant " + std::to_string(i + 1) + " != empty)";
ASSERT_FALSE(dl->Equals(*empty)) << desc;
ASSERT_FALSE(empty->Equals(*dl)) << desc;
}
}
}
TEST(DisplayList, SingleOpDisplayListsRecapturedAreEqual) {
for (auto& group : allGroups) {
for (size_t i = 0; i < group.variants.size(); i++) {
sk_sp<DisplayList> dl = group.variants[i].Build();
// Verify recapturing the replay of the display list is Equals()
// when dispatching directly from the DL to another builder
DisplayListBuilder builder;
dl->Dispatch(builder);
sk_sp<DisplayList> copy = builder.Build();
auto desc =
group.op_name + "(variant " + std::to_string(i + 1) + " == copy)";
ASSERT_EQ(copy->op_count(), dl->op_count()) << desc;
ASSERT_EQ(copy->bytes(), dl->bytes()) << desc;
ASSERT_EQ(copy->bounds(), dl->bounds()) << desc;
ASSERT_TRUE(copy->Equals(*dl)) << desc;
ASSERT_TRUE(dl->Equals(*copy)) << desc;
}
}
}
TEST(DisplayList, SingleOpDisplayListsRecapturedViaSkCanvasAreEqual) {
for (auto& group : allGroups) {
for (size_t i = 0; i < group.variants.size(); i++) {
if (group.variants[i].sk_op_count < 0) {
// A negative sk_op_count means "do not test this op".
// Used mainly for these cases:
// - we cannot encode a DrawShadowRec (Skia private header)
// - SkCanvas cannot receive a DisplayList
// - SkCanvas may or may not inline an SkPicture
continue;
}
// Verify a DisplayList (re)built by "rendering" it to an
// [SkCanvas->DisplayList] recorder recaptures an equivalent
// sequence.
// Note that sometimes the rendering ops can be optimized out by
// SkCanvas so the transfer is not always 1:1. We control for
// this by having separate op counts and sizes for the sk results
// and changing our expectation of Equals() results accordingly.
sk_sp<DisplayList> dl = group.variants[i].Build();
DisplayListCanvasRecorder recorder(dl->bounds());
dl->RenderTo(&recorder);
sk_sp<DisplayList> sk_copy = recorder.Build();
auto desc = group.op_name + "[variant " + std::to_string(i + 1) + "]";
EXPECT_EQ(sk_copy->op_count(), group.variants[i].sk_op_count) << desc;
EXPECT_EQ(sk_copy->bytes(), group.variants[i].sk_byte_count) << desc;
if (group.variants[i].sk_version_matches()) {
EXPECT_EQ(sk_copy->bounds(), dl->bounds()) << desc;
EXPECT_TRUE(dl->Equals(*sk_copy)) << desc << " == sk_copy";
EXPECT_TRUE(sk_copy->Equals(*dl)) << "sk_copy == " << desc;
} else {
// No assertion on bounds
// they could be equal, hard to tell
EXPECT_FALSE(dl->Equals(*sk_copy)) << desc << " != sk_copy";
EXPECT_FALSE(sk_copy->Equals(*dl)) << "sk_copy != " << desc;
}
}
}
}
TEST(DisplayList, SingleOpDisplayListsCompareToEachOther) {
for (auto& group : allGroups) {
std::vector<sk_sp<DisplayList>> listsA;
std::vector<sk_sp<DisplayList>> listsB;
for (size_t i = 0; i < group.variants.size(); i++) {
listsA.push_back(group.variants[i].Build());
listsB.push_back(group.variants[i].Build());
}
for (size_t i = 0; i < listsA.size(); i++) {
sk_sp<DisplayList> listA = listsA[i];
for (size_t j = 0; j < listsB.size(); j++) {
sk_sp<DisplayList> listB = listsB[j];
auto desc = group.op_name + "(variant " + std::to_string(i + 1) +
" ==? variant " + std::to_string(j + 1) + ")";
if (i == j) {
ASSERT_EQ(listA->op_count(), listB->op_count()) << desc;
ASSERT_EQ(listA->bytes(), listB->bytes()) << desc;
ASSERT_EQ(listA->bounds(), listB->bounds()) << desc;
ASSERT_TRUE(listA->Equals(*listB)) << desc;
ASSERT_TRUE(listB->Equals(*listA)) << desc;
} else {
// No assertion on op/byte counts or bounds
// they may or may not be equal between variants
ASSERT_FALSE(listA->Equals(*listB)) << desc;
ASSERT_FALSE(listB->Equals(*listA)) << desc;
}
}
}
}
}
static sk_sp<DisplayList> Build(size_t g_index, size_t v_index) {
DisplayListBuilder builder;
int op_count = 0;
size_t byte_count = 0;
for (size_t i = 0; i < allGroups.size(); i++) {
DisplayListInvocationGroup& group = allGroups[i];
size_t j = (i == g_index ? v_index : 0);
if (j >= group.variants.size())
continue;
DisplayListInvocation& invocation = group.variants[j];
op_count += invocation.op_count;
byte_count += invocation.byte_count;
invocation.invoker(builder);
}
sk_sp<DisplayList> dl = builder.Build();
std::string name;
if (g_index >= allGroups.size()) {
name = "Default";
} else {
name = allGroups[g_index].op_name;
if (v_index < 0) {
name += " skipped";
} else {
name += " variant " + std::to_string(v_index + 1);
}
}
EXPECT_EQ(dl->op_count(), op_count) << name;
EXPECT_EQ(dl->bytes(), byte_count) << name;
return dl;
}
TEST(DisplayList, DisplayListsWithVaryingOpComparisons) {
sk_sp<DisplayList> default_dl = Build(allGroups.size(), 0);
ASSERT_TRUE(default_dl->Equals(*default_dl)) << "Default == itself";
for (size_t gi = 0; gi < allGroups.size(); gi++) {
DisplayListInvocationGroup& group = allGroups[gi];
sk_sp<DisplayList> missing_dl = Build(gi, group.variants.size());
auto desc = "[Group " + std::to_string(gi + 1) + " omitted]";
ASSERT_TRUE(missing_dl->Equals(*missing_dl)) << desc << " == itself";
ASSERT_FALSE(missing_dl->Equals(*default_dl)) << desc << " != Default";
ASSERT_FALSE(default_dl->Equals(*missing_dl)) << "Default != " << desc;
for (size_t vi = 0; vi < group.variants.size(); vi++) {
auto desc = "[Group " + std::to_string(gi + 1) + " variant " +
std::to_string(vi + 1) + "]";
sk_sp<DisplayList> variant_dl = Build(gi, vi);
ASSERT_TRUE(variant_dl->Equals(*variant_dl)) << desc << " == itself";
if (vi == 0) {
ASSERT_TRUE(variant_dl->Equals(*default_dl)) << desc << " == Default";
ASSERT_TRUE(default_dl->Equals(*variant_dl)) << "Default == " << desc;
} else {
ASSERT_FALSE(variant_dl->Equals(*default_dl)) << desc << " != Default";
ASSERT_FALSE(default_dl->Equals(*variant_dl)) << "Default != " << desc;
}
ASSERT_FALSE(variant_dl->Equals(*missing_dl)) << desc << " != omitted";
ASSERT_FALSE(missing_dl->Equals(*variant_dl)) << "omitted != " << desc;
}
}
}
} // namespace testing
} // namespace flutter

View File

@ -0,0 +1,428 @@
// 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 <math.h>
#include <type_traits>
#include "flutter/flow/display_list_utils.h"
#include "flutter/flow/layers/physical_shape_layer.h"
#include "flutter/fml/logging.h"
#include "third_party/skia/include/core/SkMaskFilter.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"
#include "third_party/skia/include/utils/SkShadowUtils.h"
namespace flutter {
// clang-format off
constexpr float invert_color_matrix[20] = {
-1.0, 0, 0, 1.0, 0,
0, -1.0, 0, 1.0, 0,
0, 0, -1.0, 1.0, 0,
1.0, 1.0, 1.0, 1.0, 0
};
// clang-format on
void SkPaintDispatchHelper::setAA(bool aa) {
paint_.setAntiAlias(aa);
}
void SkPaintDispatchHelper::setDither(bool dither) {
paint_.setDither(dither);
}
void SkPaintDispatchHelper::setInvertColors(bool invert) {
invert_colors_ = invert;
paint_.setColorFilter(makeColorFilter());
}
void SkPaintDispatchHelper::setCaps(SkPaint::Cap cap) {
paint_.setStrokeCap(cap);
}
void SkPaintDispatchHelper::setJoins(SkPaint::Join join) {
paint_.setStrokeJoin(join);
}
void SkPaintDispatchHelper::setDrawStyle(SkPaint::Style style) {
paint_.setStyle(style);
}
void SkPaintDispatchHelper::setStrokeWidth(SkScalar width) {
paint_.setStrokeWidth(width);
}
void SkPaintDispatchHelper::setMiterLimit(SkScalar limit) {
paint_.setStrokeMiter(limit);
}
void SkPaintDispatchHelper::setColor(SkColor color) {
paint_.setColor(color);
}
void SkPaintDispatchHelper::setBlendMode(SkBlendMode mode) {
paint_.setBlendMode(mode);
}
void SkPaintDispatchHelper::setFilterQuality(SkFilterQuality quality) {
paint_.setFilterQuality(quality);
}
void SkPaintDispatchHelper::setShader(sk_sp<SkShader> shader) {
paint_.setShader(shader);
}
void SkPaintDispatchHelper::setImageFilter(sk_sp<SkImageFilter> filter) {
paint_.setImageFilter(filter);
}
void SkPaintDispatchHelper::setColorFilter(sk_sp<SkColorFilter> filter) {
color_filter_ = filter;
paint_.setColorFilter(makeColorFilter());
}
void SkPaintDispatchHelper::setMaskFilter(sk_sp<SkMaskFilter> filter) {
paint_.setMaskFilter(filter);
}
void SkPaintDispatchHelper::setMaskBlurFilter(SkBlurStyle style,
SkScalar sigma) {
paint_.setMaskFilter(SkMaskFilter::MakeBlur(style, sigma));
}
sk_sp<SkColorFilter> SkPaintDispatchHelper::makeColorFilter() {
if (!invert_colors_) {
return color_filter_;
}
sk_sp<SkColorFilter> invert_filter =
SkColorFilters::Matrix(invert_color_matrix);
if (color_filter_) {
invert_filter = invert_filter->makeComposed(color_filter_);
}
return invert_filter;
}
void SkMatrixDispatchHelper::translate(SkScalar tx, SkScalar ty) {
matrix_.preTranslate(tx, ty);
}
void SkMatrixDispatchHelper::scale(SkScalar sx, SkScalar sy) {
matrix_.preScale(sx, sy);
}
void SkMatrixDispatchHelper::rotate(SkScalar degrees) {
matrix_.preRotate(degrees);
}
void SkMatrixDispatchHelper::skew(SkScalar sx, SkScalar sy) {
matrix_.preSkew(sx, sy);
}
void SkMatrixDispatchHelper::transform2x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt) {
matrix_.preConcat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, 0, 0, 1));
}
void SkMatrixDispatchHelper::transform3x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt,
SkScalar px,
SkScalar py,
SkScalar pt) {
matrix_.preConcat(
SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, px, py, pt));
}
void SkMatrixDispatchHelper::save() {
saved_.push_back(matrix_);
}
void SkMatrixDispatchHelper::restore() {
matrix_ = saved_.back();
saved_.pop_back();
}
void SkMatrixDispatchHelper::reset() {
matrix_.reset();
}
void ClipBoundsDispatchHelper::clipRect(const SkRect& rect,
bool isAA,
SkClipOp clip_op) {
if (clip_op == SkClipOp::kIntersect) {
intersect(rect);
}
}
void ClipBoundsDispatchHelper::clipRRect(const SkRRect& rrect,
bool isAA,
SkClipOp clip_op) {
if (clip_op == SkClipOp::kIntersect) {
intersect(rrect.getBounds());
}
}
void ClipBoundsDispatchHelper::clipPath(const SkPath& path,
bool isAA,
SkClipOp clip_op) {
if (clip_op == SkClipOp::kIntersect) {
intersect(path.getBounds());
}
}
void ClipBoundsDispatchHelper::intersect(const SkRect& rect) {
SkRect devClipBounds = matrix().mapRect(rect);
if (!bounds_.intersect(devClipBounds)) {
bounds_.setEmpty();
}
}
void ClipBoundsDispatchHelper::save() {
saved_.push_back(bounds_);
}
void ClipBoundsDispatchHelper::restore() {
bounds_ = saved_.back();
saved_.pop_back();
}
void DisplayListBoundsCalculator::saveLayer(const SkRect* bounds,
bool with_paint) {
SkMatrixDispatchHelper::save();
ClipBoundsDispatchHelper::save();
SaveInfo info =
with_paint ? SaveLayerWithPaintInfo(this, accumulator_, matrix(), paint())
: SaveLayerInfo(accumulator_, matrix());
saved_infos_.push_back(info);
accumulator_ = info.save();
SkMatrixDispatchHelper::reset();
}
void DisplayListBoundsCalculator::save() {
SkMatrixDispatchHelper::save();
ClipBoundsDispatchHelper::save();
SaveInfo info = SaveInfo(accumulator_);
saved_infos_.push_back(info);
accumulator_ = info.save();
}
void DisplayListBoundsCalculator::restore() {
if (!saved_infos_.empty()) {
SkMatrixDispatchHelper::restore();
ClipBoundsDispatchHelper::restore();
SaveInfo info = saved_infos_.back();
saved_infos_.pop_back();
accumulator_ = info.restore();
}
}
void DisplayListBoundsCalculator::drawPaint() {
if (!bounds_cull_.isEmpty()) {
root_accumulator_.accumulate(bounds_cull_);
}
}
void DisplayListBoundsCalculator::drawColor(SkColor color, SkBlendMode mode) {
if (!bounds_cull_.isEmpty()) {
root_accumulator_.accumulate(bounds_cull_);
}
}
void DisplayListBoundsCalculator::drawLine(const SkPoint& p0,
const SkPoint& p1) {
SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted();
accumulateRect(bounds, true);
}
void DisplayListBoundsCalculator::drawRect(const SkRect& rect) {
accumulateRect(rect);
}
void DisplayListBoundsCalculator::drawOval(const SkRect& bounds) {
accumulateRect(bounds);
}
void DisplayListBoundsCalculator::drawCircle(const SkPoint& center,
SkScalar radius) {
accumulateRect(SkRect::MakeLTRB(center.fX - radius, center.fY - radius,
center.fX + radius, center.fY + radius));
}
void DisplayListBoundsCalculator::drawRRect(const SkRRect& rrect) {
accumulateRect(rrect.getBounds());
}
void DisplayListBoundsCalculator::drawDRRect(const SkRRect& outer,
const SkRRect& inner) {
accumulateRect(outer.getBounds());
}
void DisplayListBoundsCalculator::drawPath(const SkPath& path) {
accumulateRect(path.getBounds());
}
void DisplayListBoundsCalculator::drawArc(const SkRect& bounds,
SkScalar start,
SkScalar sweep,
bool useCenter) {
// This could be tighter if we compute where the start and end
// angles are and then also consider the quadrants swept and
// the center if specified.
accumulateRect(bounds);
}
void DisplayListBoundsCalculator::drawPoints(SkCanvas::PointMode mode,
uint32_t count,
const SkPoint pts[]) {
if (count > 0) {
BoundsAccumulator ptBounds;
for (size_t i = 0; i < count; i++) {
ptBounds.accumulate(pts[i]);
}
accumulateRect(ptBounds.getBounds(), true);
}
}
void DisplayListBoundsCalculator::drawVertices(const sk_sp<SkVertices> vertices,
SkBlendMode mode) {
accumulateRect(vertices->bounds());
}
void DisplayListBoundsCalculator::drawImage(const sk_sp<SkImage> image,
const SkPoint point,
const SkSamplingOptions& sampling) {
SkRect bounds = SkRect::Make(image->bounds());
bounds.offset(point);
accumulateRect(bounds);
}
void DisplayListBoundsCalculator::drawImageRect(
const sk_sp<SkImage> image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
SkCanvas::SrcRectConstraint constraint) {
accumulateRect(dst);
}
void DisplayListBoundsCalculator::drawImageNine(const sk_sp<SkImage> image,
const SkIRect& center,
const SkRect& dst,
SkFilterMode filter) {
accumulateRect(dst);
}
void DisplayListBoundsCalculator::drawImageLattice(
const sk_sp<SkImage> image,
const SkCanvas::Lattice& lattice,
const SkRect& dst,
SkFilterMode filter,
bool with_paint) {
accumulateRect(dst);
}
void DisplayListBoundsCalculator::drawAtlas(const sk_sp<SkImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const SkColor colors[],
int count,
SkBlendMode mode,
const SkSamplingOptions& sampling,
const SkRect* cullRect) {
SkPoint quad[4];
BoundsAccumulator atlasBounds;
for (int i = 0; i < count; i++) {
const SkRect& src = tex[i];
xform[i].toQuad(src.width(), src.height(), quad);
for (int j = 0; j < 4; j++) {
atlasBounds.accumulate(quad[j]);
}
}
if (atlasBounds.isNotEmpty()) {
accumulateRect(atlasBounds.getBounds());
}
}
void DisplayListBoundsCalculator::drawPicture(const sk_sp<SkPicture> picture,
const SkMatrix* pic_matrix,
bool with_save_layer) {
// TODO(flar) cull rect really cannot be trusted in general, but it will
// work for SkPictures generated from our own PictureRecorder or any
// picture captured with an SkRTreeFactory or accurate bounds estimate.
SkRect bounds = picture->cullRect();
if (pic_matrix) {
pic_matrix->mapRect(&bounds);
}
if (with_save_layer) {
accumulateRect(bounds);
} else {
matrix().mapRect(&bounds);
accumulator_->accumulate(bounds);
}
}
void DisplayListBoundsCalculator::drawDisplayList(
const sk_sp<DisplayList> display_list) {
accumulateRect(display_list->bounds());
}
void DisplayListBoundsCalculator::drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) {
accumulateRect(blob->bounds().makeOffset(x, y));
}
void DisplayListBoundsCalculator::drawShadow(const SkPath& path,
const SkColor color,
const SkScalar elevation,
bool occludes) {
// Constants from physical_shape_layer.cc
const SkScalar kLightHeight = 600;
const SkScalar kLightRadius = 800;
SkShadowFlags flags = occludes
? SkShadowFlags::kTransparentOccluder_ShadowFlag
: SkShadowFlags::kNone_ShadowFlag;
const SkRect& bounds = path.getBounds();
SkScalar shadow_x = (bounds.left() + bounds.right()) / 2;
SkScalar shadow_y = bounds.top() - 600.0f;
SkRect shadow_bounds;
SkShadowUtils::GetLocalBounds(
matrix(), path, SkPoint3::Make(0, 0, elevation),
SkPoint3::Make(shadow_x, shadow_y, kLightHeight), kLightRadius, flags,
&shadow_bounds);
accumulateRect(shadow_bounds);
}
void DisplayListBoundsCalculator::accumulateRect(const SkRect& rect,
bool forceStroke) {
SkRect dstRect = rect;
const SkPaint& p = paint();
if (forceStroke) {
if (p.getStyle() == SkPaint::kFill_Style) {
setDrawStyle(SkPaint::kStroke_Style);
} else {
forceStroke = false;
}
}
if (p.canComputeFastBounds()) {
dstRect = p.computeFastBounds(rect, &dstRect);
matrix().mapRect(&dstRect);
accumulator_->accumulate(dstRect);
} else {
root_accumulator_.accumulate(bounds_cull_);
}
if (forceStroke) {
setDrawStyle(SkPaint::kFill_Style);
}
}
DisplayListBoundsCalculator::SaveInfo::SaveInfo(BoundsAccumulator* accumulator)
: saved_accumulator_(accumulator) {}
BoundsAccumulator* DisplayListBoundsCalculator::SaveInfo::save() {
// No need to swap out the accumulator for a normal save
return saved_accumulator_;
}
BoundsAccumulator* DisplayListBoundsCalculator::SaveInfo::restore() {
return saved_accumulator_;
}
DisplayListBoundsCalculator::SaveLayerInfo::SaveLayerInfo(
BoundsAccumulator* accumulator,
const SkMatrix& matrix)
: SaveInfo(accumulator), matrix_(matrix) {}
BoundsAccumulator* DisplayListBoundsCalculator::SaveLayerInfo::save() {
// Use the local layerAccumulator until restore is called and
// then transform (and adjust with paint if necessary) on restore()
return &layer_accumulator_;
}
BoundsAccumulator* DisplayListBoundsCalculator::SaveLayerInfo::restore() {
SkRect layer_bounds = layer_accumulator_.getBounds();
matrix_.mapRect(&layer_bounds);
saved_accumulator_->accumulate(layer_bounds);
return saved_accumulator_;
}
DisplayListBoundsCalculator::SaveLayerWithPaintInfo::SaveLayerWithPaintInfo(
DisplayListBoundsCalculator* calculator,
BoundsAccumulator* accumulator,
const SkMatrix& saveMatrix,
const SkPaint& savePaint)
: SaveLayerInfo(accumulator, saveMatrix),
calculator_(calculator),
paint_(savePaint) {}
BoundsAccumulator*
DisplayListBoundsCalculator::SaveLayerWithPaintInfo::restore() {
SkRect layer_bounds = layer_accumulator_.getBounds();
if (paint_.canComputeFastBounds()) {
layer_bounds = paint_.computeFastBounds(layer_bounds, &layer_bounds);
matrix_.mapRect(&layer_bounds);
saved_accumulator_->accumulate(layer_bounds);
} else {
calculator_->root_accumulator_.accumulate(calculator_->bounds_cull_);
}
return saved_accumulator_;
}
} // namespace flutter

View File

@ -0,0 +1,372 @@
// 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_UTILS_H_
#define FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_
#include "flutter/flow/display_list.h"
#include "third_party/skia/include/core/SkMaskFilter.h"
// This file contains various utility classes to ease implementing
// a Flutter DisplayList Dispatcher, including:
//
// IngoreAttributeDispatchHelper:
// IngoreClipDispatchHelper:
// IngoreTransformDispatchHelper
// Empty overrides of all of the associated methods of Dispatcher
// for dispatchers that only track some of the rendering operations
//
// SkPaintAttributeDispatchHelper:
// Tracks the attribute methods and maintains their state in an
// SkPaint object.
// SkMatrixTransformDispatchHelper:
// Tracks the transform methods and maintains their state in a
// (save/restore stack of) SkMatrix object.
// ClipBoundsDispatchHelper:
// Tracks the clip methods and maintains a culling box in a
// (save/restore stack of) SkRect culling rectangle.
//
// DisplayListBoundsCalculator:
// A class that can traverse an entire display list and compute
// a conservative estimate of the bounds of all of the rendering
// operations.
namespace flutter {
// A utility class that will ignore all Dispatcher methods relating
// to the setting of attributes.
class IngoreAttributeDispatchHelper : public virtual Dispatcher {
public:
void setAA(bool aa) override {}
void setDither(bool dither) override {}
void setInvertColors(bool invert) override {}
void setCaps(SkPaint::Cap cap) override {}
void setJoins(SkPaint::Join join) override {}
void setDrawStyle(SkPaint::Style style) override {}
void setStrokeWidth(SkScalar width) override {}
void setMiterLimit(SkScalar limit) override {}
void setColor(SkColor color) override {}
void setBlendMode(SkBlendMode mode) override {}
void setFilterQuality(SkFilterQuality quality) override {}
void setShader(sk_sp<SkShader> shader) override {}
void setImageFilter(sk_sp<SkImageFilter> filter) override {}
void setColorFilter(sk_sp<SkColorFilter> filter) override {}
void setMaskFilter(sk_sp<SkMaskFilter> filter) override {}
void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override {}
};
// A utility class that will ignore all Dispatcher methods relating
// to setting a clip.
class IngoreClipDispatchHelper : public virtual Dispatcher {
void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override {}
void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override {}
void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override {}
};
// A utility class that will ignore all Dispatcher methods relating
// to modifying the transform.
class IngoreTransformDispatchHelper : public virtual Dispatcher {
public:
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 transform2x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt) override {}
void transform3x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt,
SkScalar px,
SkScalar py,
SkScalar pt) override {}
};
// A utility class that will monitor the Dispatcher methods relating
// to the rendering attributes and accumulate them into an SkPaint
// which can be accessed at any time via paint().
class SkPaintDispatchHelper : public virtual Dispatcher {
public:
void setAA(bool aa) override;
void setDither(bool dither) override;
void setInvertColors(bool invert) override;
void setCaps(SkPaint::Cap cap) override;
void setJoins(SkPaint::Join join) override;
void setDrawStyle(SkPaint::Style style) override;
void setStrokeWidth(SkScalar width) override;
void setMiterLimit(SkScalar limit) override;
void setColor(SkColor color) override;
void setBlendMode(SkBlendMode mode) override;
void setFilterQuality(SkFilterQuality quality) override;
void setShader(sk_sp<SkShader> shader) override;
void setImageFilter(sk_sp<SkImageFilter> filter) override;
void setColorFilter(sk_sp<SkColorFilter> filter) override;
void setMaskFilter(sk_sp<SkMaskFilter> filter) override;
void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override;
const SkPaint& paint() { return paint_; }
private:
SkPaint paint_;
bool invert_colors_ = false;
sk_sp<SkColorFilter> color_filter_;
sk_sp<SkColorFilter> makeColorFilter();
};
class SkMatrixSource {
public:
virtual const SkMatrix& matrix() const = 0;
};
// A utility class that will monitor the Dispatcher methods relating
// to the transform and accumulate them into an SkMatrix which can
// be accessed at any time via getMatrix().
//
// This class also implements an appropriate stack of transforms via
// its save() and restore() methods so those methods will need to be
// forwarded if overridden in more than one super class.
class SkMatrixDispatchHelper : public virtual Dispatcher,
public virtual SkMatrixSource {
public:
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 transform2x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt) override;
void transform3x3(SkScalar mxx,
SkScalar mxy,
SkScalar mxt,
SkScalar myx,
SkScalar myy,
SkScalar myt,
SkScalar px,
SkScalar py,
SkScalar pt) override;
void save() override;
void restore() override;
const SkMatrix& matrix() const override { return matrix_; }
protected:
void reset();
private:
SkMatrix matrix_;
std::vector<SkMatrix> saved_;
};
// A utility class that will monitor the Dispatcher methods relating
// to the clip and accumulate a conservative bounds into an SkRect
// which can be accessed at any time via getCullingBounds().
//
// The subclass must implement a single virtual method matrix()
// which will happen automatically if the subclass also inherits
// from SkMatrixTransformDispatchHelper.
//
// This class also implements an appropriate stack of transforms via
// its save() and restore() methods so those methods will need to be
// forwarded if overridden in more than one super class.
class ClipBoundsDispatchHelper : public virtual Dispatcher,
private virtual SkMatrixSource {
public:
void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override;
void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override;
void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override;
void save() override;
void restore() override;
const SkRect& getCullingBounds() const { return bounds_; }
private:
SkRect bounds_;
std::vector<SkRect> saved_;
void intersect(const SkRect& clipBounds);
};
class BoundsAccumulator {
public:
void accumulate(const SkPoint& p) { accumulate(p.fX, p.fY); }
void accumulate(SkScalar x, SkScalar y) {
if (min_x_ > x)
min_x_ = x;
if (min_y_ > y)
min_y_ = y;
if (max_x_ < x)
max_x_ = x;
if (max_y_ < y)
max_y_ = y;
}
void accumulate(const SkRect& r) {
if (r.fLeft <= r.fRight && r.fTop <= r.fBottom) {
accumulate(r.fLeft, r.fTop);
accumulate(r.fRight, r.fBottom);
}
}
bool isEmpty() const { return min_x_ >= max_x_ || min_y_ >= max_y_; }
bool isNotEmpty() const { return min_x_ < max_x_ && min_y_ < max_y_; }
SkRect getBounds() const {
return (max_x_ > min_x_ && max_y_ > min_y_)
? SkRect::MakeLTRB(min_x_, min_y_, max_x_, max_y_)
: SkRect::MakeEmpty();
}
private:
SkScalar min_x_ = std::numeric_limits<SkScalar>::infinity();
SkScalar min_y_ = std::numeric_limits<SkScalar>::infinity();
SkScalar max_x_ = -std::numeric_limits<SkScalar>::infinity();
SkScalar max_y_ = -std::numeric_limits<SkScalar>::infinity();
};
// This class implements all rendering methods and computes a liberal
// bounds of the rendering operations.
class DisplayListBoundsCalculator final
: public virtual Dispatcher,
public virtual SkPaintDispatchHelper,
public virtual SkMatrixDispatchHelper,
public virtual ClipBoundsDispatchHelper {
public:
// Construct a Calculator to determine the bounds of a list of
// DisplayList dispatcher method calls. Since 2 of the method calls
// have no intrinsic size because they render to the entire available,
// the |cullRect| provides a bounds for them to include.
DisplayListBoundsCalculator(const SkRect& cull_rect = SkRect::MakeEmpty())
: accumulator_(&root_accumulator_), bounds_cull_(cull_rect) {}
void saveLayer(const SkRect* bounds, bool with_paint) override;
void save() override;
void restore() 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<SkVertices> vertices,
SkBlendMode mode) override;
void drawImage(const sk_sp<SkImage> image,
const SkPoint point,
const SkSamplingOptions& sampling) override;
void drawImageRect(const sk_sp<SkImage> image,
const SkRect& src,
const SkRect& dst,
const SkSamplingOptions& sampling,
SkCanvas::SrcRectConstraint constraint) override;
void drawImageNine(const sk_sp<SkImage> image,
const SkIRect& center,
const SkRect& dst,
SkFilterMode filter) override;
void drawImageLattice(const sk_sp<SkImage> image,
const SkCanvas::Lattice& lattice,
const SkRect& dst,
SkFilterMode filter,
bool with_paint) override;
void drawAtlas(const sk_sp<SkImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const SkColor colors[],
int count,
SkBlendMode mode,
const SkSamplingOptions& sampling,
const SkRect* cullRect) override;
void drawPicture(const sk_sp<SkPicture> picture,
const SkMatrix* matrix,
bool with_save_layer) override;
void drawDisplayList(const sk_sp<DisplayList> display_list) override;
void drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) override;
void drawShadow(const SkPath& path,
const SkColor color,
const SkScalar elevation,
bool occludes) override;
SkRect getBounds() { return accumulator_->getBounds(); }
private:
// current accumulator based on saveLayer history
BoundsAccumulator* accumulator_;
// Only used for drawColor and drawPaint and paint objects that
// cannot support fast bounds.
SkRect bounds_cull_;
BoundsAccumulator root_accumulator_;
class SaveInfo {
public:
SaveInfo(BoundsAccumulator* accumulator);
virtual ~SaveInfo() = default;
virtual BoundsAccumulator* save();
virtual BoundsAccumulator* restore();
protected:
BoundsAccumulator* saved_accumulator_;
};
class SaveLayerInfo : public SaveInfo {
public:
SaveLayerInfo(BoundsAccumulator* accumulator, const SkMatrix& matrix);
virtual ~SaveLayerInfo() = default;
BoundsAccumulator* save() override;
BoundsAccumulator* restore() override;
protected:
BoundsAccumulator layer_accumulator_;
const SkMatrix matrix_;
};
class SaveLayerWithPaintInfo : public SaveLayerInfo {
public:
SaveLayerWithPaintInfo(DisplayListBoundsCalculator* calculator,
BoundsAccumulator* accumulator,
const SkMatrix& save_matrix,
const SkPaint& save_paint);
virtual ~SaveLayerWithPaintInfo() = default;
BoundsAccumulator* restore() override;
protected:
DisplayListBoundsCalculator* calculator_;
SkPaint paint_;
};
std::vector<SaveInfo> saved_infos_;
void accumulateRect(const SkRect& rect, bool force_stroke = false);
};
} // namespace flutter
#endif // FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_

View File

@ -0,0 +1,129 @@
// 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/flow/layers/display_list_layer.h"
#include "flutter/flow/display_list_canvas.h"
namespace flutter {
DisplayListLayer::DisplayListLayer(const SkPoint& offset,
sk_sp<DisplayList> display_list,
bool is_complex,
bool will_change)
: offset_(offset),
display_list_(display_list),
is_complex_(is_complex),
will_change_(will_change) {}
#ifdef FLUTTER_ENABLE_DIFF_CONTEXT
bool DisplayListLayer::IsReplacing(DiffContext* context,
const Layer* layer) const {
// Only return true for identical display lists; This way
// ContainerLayer::DiffChildren can detect when a display list layer
// got inserted between other display list layers
auto old_layer = layer->as_display_list_layer();
return old_layer != nullptr && offset_ == old_layer->offset_ &&
Compare(context->statistics(), this, old_layer);
}
void DisplayListLayer::Diff(DiffContext* context, const Layer* old_layer) {
DiffContext::AutoSubtreeRestore subtree(context);
if (!context->IsSubtreeDirty()) {
#ifndef NDEBUG
FML_DCHECK(old_layer);
auto prev = old_layer->as_display_list_layer();
DiffContext::Statistics dummy_statistics;
// IsReplacing has already determined that the display list is same
FML_DCHECK(prev->offset_ == offset_ &&
Compare(dummy_statistics, this, prev));
#endif
}
context->PushTransform(SkMatrix::Translate(offset_.x(), offset_.y()));
context->AddLayerBounds(display_list_->bounds());
context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion());
}
bool DisplayListLayer::Compare(DiffContext::Statistics& statistics,
const DisplayListLayer* l1,
const DisplayListLayer* l2) {
const auto& dl1 = l1->display_list_;
const auto& dl2 = l2->display_list_;
if (dl1.get() == dl2.get()) {
statistics.AddSameInstancePicture();
return true;
}
const auto op_cnt_1 = dl1->op_count();
const auto op_cnt_2 = dl2->op_count();
const auto op_bytes_1 = dl1->bytes();
const auto op_bytes_2 = dl2->bytes();
if (op_cnt_1 != op_cnt_2 || op_bytes_1 != op_bytes_2 ||
dl1->bounds() != dl2->bounds()) {
statistics.AddNewPicture();
return false;
}
if (op_bytes_1 > kMaxBytesToCompare) {
statistics.AddPictureTooComplexToCompare();
return false;
}
statistics.AddDeepComparePicture();
auto res = dl1->Equals(*dl2);
if (res) {
statistics.AddDifferentInstanceButEqualPicture();
} else {
statistics.AddNewPicture();
}
return res;
}
#endif // FLUTTER_ENABLE_DIFF_CONTEXT
void DisplayListLayer::Preroll(PrerollContext* context,
const SkMatrix& matrix) {
TRACE_EVENT0("flutter", "DisplayListLayer::Preroll");
DisplayList* disp_list = display_list();
if (auto* cache = context->raster_cache) {
TRACE_EVENT0("flutter", "DisplayListLayer::RasterCache (Preroll)");
SkMatrix ctm = matrix;
ctm.preTranslate(offset_.x(), offset_.y());
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
ctm = RasterCache::GetIntegralTransCTM(ctm);
#endif
cache->Prepare(context->gr_context, disp_list, ctm,
context->dst_color_space, is_complex_, will_change_);
}
SkRect bounds = disp_list->bounds().makeOffset(offset_.x(), offset_.y());
set_paint_bounds(bounds);
}
void DisplayListLayer::Paint(PaintContext& context) const {
TRACE_EVENT0("flutter", "DisplayListLayer::Paint");
FML_DCHECK(display_list_.get());
FML_DCHECK(needs_painting(context));
SkAutoCanvasRestore save(context.leaf_nodes_canvas, true);
context.leaf_nodes_canvas->translate(offset_.x(), offset_.y());
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
context.leaf_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM(
context.leaf_nodes_canvas->getTotalMatrix()));
#endif
if (context.raster_cache &&
context.raster_cache->Draw(*display_list(), *context.leaf_nodes_canvas)) {
TRACE_EVENT_INSTANT0("flutter", "raster cache hit");
return;
}
display_list()->RenderTo(context.leaf_nodes_canvas);
}
} // namespace flutter

View File

@ -0,0 +1,61 @@
// 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_LAYERS_DISPLAY_LIST_LAYER_H_
#define FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_
#include "flutter/flow/display_list.h"
#include "flutter/flow/layers/layer.h"
namespace flutter {
class DisplayListLayer : public Layer {
public:
static constexpr size_t kMaxBytesToCompare = 10000;
DisplayListLayer(const SkPoint& offset,
sk_sp<DisplayList> display_list,
bool is_complex,
bool will_change);
DisplayList* display_list() const { return display_list_.get(); }
#ifdef FLUTTER_ENABLE_DIFF_CONTEXT
bool IsReplacing(DiffContext* context, const Layer* layer) const override;
void Diff(DiffContext* context, const Layer* old_layer) override;
const DisplayListLayer* as_display_list_layer() const override {
return this;
}
#endif // FLUTTER_ENABLE_DIFF_CONTEXT
void Preroll(PrerollContext* frame, const SkMatrix& matrix) override;
void Paint(PaintContext& context) const override;
private:
SkPoint offset_;
sk_sp<DisplayList> display_list_;
bool is_complex_ = false;
bool will_change_ = false;
#ifdef FLUTTER_ENABLE_DIFF_CONTEXT
sk_sp<SkData> SerializedPicture() const;
mutable sk_sp<SkData> cached_serialized_picture_;
static bool Compare(DiffContext::Statistics& statistics,
const DisplayListLayer* l1,
const DisplayListLayer* l2);
#endif // FLUTTER_ENABLE_DIFF_CONTEXT
FML_DISALLOW_COPY_AND_ASSIGN(DisplayListLayer);
};
} // namespace flutter
#endif // FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_

View File

@ -0,0 +1,159 @@
// 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.
#define FML_USED_ON_EMBEDDER
#include "flutter/flow/layers/display_list_layer.h"
#include "flutter/flow/testing/diff_context_test.h"
#include "flutter/fml/macros.h"
#include "flutter/testing/mock_canvas.h"
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
#include "flutter/flow/raster_cache.h"
#endif
namespace flutter {
namespace testing {
using DisplayListLayerTest = LayerTest;
#ifndef NDEBUG
TEST_F(DisplayListLayerTest, PaintBeforePrerollInvalidDisplayListDies) {
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
auto layer = std::make_shared<DisplayListLayer>(
layer_offset, sk_ref_sp<DisplayList>(nullptr), false, false);
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"display_list_\\.get\\(\\)");
}
TEST_F(DisplayListLayerTest, PaintBeforePrerollDies) {
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
auto layer = std::make_shared<DisplayListLayer>(
layer_offset, sk_make_sp<DisplayList>(), false, false);
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
TEST_F(DisplayListLayerTest, PaintingEmptyLayerDies) {
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
auto layer = std::make_shared<DisplayListLayer>(
layer_offset, sk_make_sp<DisplayList>(), false, false);
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
TEST_F(DisplayListLayerTest, InvalidDisplayListDies) {
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
auto layer = std::make_shared<DisplayListLayer>(
layer_offset, sk_ref_sp<DisplayList>(nullptr), false, false);
// Crashes reading a nullptr.
EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), "");
}
#endif
TEST_F(DisplayListLayerTest, SimpleDisplayList) {
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);
const SkMatrix layer_offset_matrix =
SkMatrix::Translate(layer_offset.fX, layer_offset.fY);
const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f);
DisplayListBuilder builder;
builder.drawRect(picture_bounds);
auto display_list = builder.Build();
auto layer = std::make_shared<DisplayListLayer>(layer_offset, display_list,
false, false);
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(),
picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY));
EXPECT_EQ(layer->display_list(), display_list.get());
EXPECT_TRUE(layer->needs_painting(paint_context()));
layer->Paint(paint_context());
auto expected_draw_calls = std::vector(
{MockCanvas::DrawCall{0, MockCanvas::SaveData{1}},
MockCanvas::DrawCall{
1, MockCanvas::ConcatMatrixData{SkM44(layer_offset_matrix)}},
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
MockCanvas::DrawCall{
1, MockCanvas::SetMatrixData{SkM44(
RasterCache::GetIntegralTransCTM(layer_offset_matrix))}},
#endif
MockCanvas::DrawCall{
1, MockCanvas::DrawRectData{picture_bounds, SkPaint()}},
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}});
EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls);
}
#ifdef FLUTTER_ENABLE_DIFF_CONTEXT
using DisplayListLayerDiffTest = DiffContextTest;
TEST_F(DisplayListLayerDiffTest, SimpleDisplayList) {
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
MockLayerTree tree1;
tree1.root()->Add(CreateDisplayListLayer(display_list));
auto damage = DiffLayerTree(tree1, MockLayerTree());
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60));
MockLayerTree tree2;
tree2.root()->Add(CreateDisplayListLayer(display_list));
damage = DiffLayerTree(tree2, tree1);
EXPECT_TRUE(damage.frame_damage.isEmpty());
MockLayerTree tree3;
damage = DiffLayerTree(tree3, tree2);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60));
}
TEST_F(DisplayListLayerDiffTest, DisplayListCompare) {
MockLayerTree tree1;
auto display_list1 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
tree1.root()->Add(CreateDisplayListLayer(display_list1));
auto damage = DiffLayerTree(tree1, MockLayerTree());
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60));
MockLayerTree tree2;
auto display_list2 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
tree2.root()->Add(CreateDisplayListLayer(display_list2));
damage = DiffLayerTree(tree2, tree1);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeEmpty());
MockLayerTree tree3;
auto display_list3 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
// add offset
tree3.root()->Add(
CreateDisplayListLayer(display_list3, SkPoint::Make(10, 10)));
damage = DiffLayerTree(tree3, tree2);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 70, 70));
MockLayerTree tree4;
// different color
auto display_list4 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 2);
tree4.root()->Add(
CreateDisplayListLayer(display_list4, SkPoint::Make(10, 10)));
damage = DiffLayerTree(tree4, tree3);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(20, 20, 70, 70));
}
#endif
} // namespace testing
} // namespace flutter

View File

@ -64,6 +64,7 @@ struct PrerollContext {
};
class PictureLayer;
class DisplayListLayer;
class PerformanceOverlayLayer;
class TextureLayer;
@ -239,6 +240,9 @@ class Layer {
#ifdef FLUTTER_ENABLE_DIFF_CONTEXT
virtual const PictureLayer* as_picture_layer() const { return nullptr; }
virtual const DisplayListLayer* as_display_list_layer() const {
return nullptr;
}
virtual const TextureLayer* as_texture_layer() const { return nullptr; }
virtual const PerformanceOverlayLayer* as_performance_overlay_layer() const {
return nullptr;

View File

@ -57,7 +57,6 @@ TEST_F(PictureLayerTest, PaintingEmptyLayerDies) {
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
#endif
TEST_F(PictureLayerTest, InvalidPictureDies) {
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
@ -67,6 +66,7 @@ TEST_F(PictureLayerTest, InvalidPictureDies) {
// Crashes reading a nullptr.
EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), "");
}
#endif
TEST_F(PictureLayerTest, SimplePicture) {
const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f);

View File

@ -56,6 +56,28 @@ static bool CanRasterizePicture(SkPicture* picture) {
if (!cull_rect.isFinite()) {
// Cannot attempt to rasterize into an infinitely large surface.
FML_LOG(INFO) << "Attempted to raster cache non-finite picture";
return false;
}
return true;
}
static bool CanRasterizeDisplayList(DisplayList* display_list) {
if (display_list == nullptr) {
return false;
}
const SkRect cull_rect = display_list->bounds();
if (cull_rect.isEmpty()) {
// No point in ever rasterizing an empty display list.
return false;
}
if (!cull_rect.isFinite()) {
// Cannot attempt to rasterize into an infinitely large surface.
FML_LOG(INFO) << "Attempted to raster cache non-finite display list";
return false;
}
@ -88,6 +110,32 @@ static bool IsPictureWorthRasterizing(SkPicture* picture,
return picture->approximateOpCount() > 5;
}
static bool IsDisplayListWorthRasterizing(DisplayList* display_list,
bool will_change,
bool is_complex) {
if (will_change) {
// If the display list is going to change in the future, there is no point
// in doing to extra work to rasterize.
return false;
}
if (!CanRasterizeDisplayList(display_list)) {
// No point in deciding whether the display list is worth rasterizing if it
// cannot be rasterized at all.
return false;
}
if (is_complex) {
// The caller seems to have extra information about the display list and
// thinks the display list is always worth rasterizing.
return true;
}
// TODO(abarth): We should find a better heuristic here that lets us avoid
// wasting memory on trivial layers that are easy to re-rasterize every frame.
return display_list->op_count() > 5;
}
/// @note Procedure doesn't copy all closures.
static std::unique_ptr<RasterCacheResult> Rasterize(
GrDirectContext* context,
@ -136,6 +184,17 @@ std::unique_ptr<RasterCacheResult> RasterCache::RasterizePicture(
[=](SkCanvas* canvas) { canvas->drawPicture(picture); });
}
std::unique_ptr<RasterCacheResult> RasterCache::RasterizeDisplayList(
DisplayList* display_list,
GrDirectContext* context,
const SkMatrix& ctm,
SkColorSpace* dst_color_space,
bool checkerboard) const {
return Rasterize(context, ctm, dst_color_space, checkerboard,
display_list->bounds(),
[=](SkCanvas* canvas) { display_list->RenderTo(canvas); });
}
void RasterCache::Prepare(PrerollContext* context,
Layer* layer,
const SkMatrix& ctm) {
@ -223,6 +282,52 @@ bool RasterCache::Prepare(GrDirectContext* context,
return true;
}
bool RasterCache::Prepare(GrDirectContext* context,
DisplayList* display_list,
const SkMatrix& transformation_matrix,
SkColorSpace* dst_color_space,
bool is_complex,
bool will_change) {
// Disabling caching when access_threshold is zero is historic behavior.
if (access_threshold_ == 0) {
return false;
}
if (picture_cached_this_frame_ >= picture_cache_limit_per_frame_) {
return false;
}
if (!IsDisplayListWorthRasterizing(display_list, will_change, is_complex)) {
// We only deal with display lists that are worthy of rasterization.
return false;
}
// Decompose the matrix (once) for all subsequent operations. We want to make
// sure to avoid volumetric distortions while accounting for scaling.
const MatrixDecomposition matrix(transformation_matrix);
if (!matrix.IsValid()) {
// The matrix was singular. No point in going further.
return false;
}
DisplayListRasterCacheKey cache_key(display_list->unique_id(),
transformation_matrix);
// Creates an entry, if not present prior.
Entry& entry = display_list_cache_[cache_key];
if (entry.access_count < access_threshold_) {
// Frame threshold has not yet been reached.
return false;
}
if (!entry.image) {
entry.image =
RasterizeDisplayList(display_list, context, transformation_matrix,
dst_color_space, checkerboard_images_);
picture_cached_this_frame_++;
}
return true;
}
bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const {
PictureRasterCacheKey cache_key(picture.uniqueID(), canvas.getTotalMatrix());
auto it = picture_cache_.find(cache_key);
@ -242,6 +347,27 @@ bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const {
return false;
}
bool RasterCache::Draw(const DisplayList& display_list,
SkCanvas& canvas) const {
DisplayListRasterCacheKey cache_key(display_list.unique_id(),
canvas.getTotalMatrix());
auto it = display_list_cache_.find(cache_key);
if (it == display_list_cache_.end()) {
return false;
}
Entry& entry = it->second;
entry.access_count++;
entry.used_this_frame = true;
if (entry.image) {
entry.image->draw(canvas, nullptr);
return true;
}
return false;
}
bool RasterCache::Draw(const Layer* layer,
SkCanvas& canvas,
SkPaint* paint) const {
@ -265,6 +391,7 @@ bool RasterCache::Draw(const Layer* layer,
void RasterCache::SweepAfterFrame() {
SweepOneCacheAfterFrame(picture_cache_);
SweepOneCacheAfterFrame(display_list_cache_);
SweepOneCacheAfterFrame(layer_cache_);
picture_cached_this_frame_ = 0;
TraceStatsToTimeline();
@ -272,11 +399,13 @@ void RasterCache::SweepAfterFrame() {
void RasterCache::Clear() {
picture_cache_.clear();
display_list_cache_.clear();
layer_cache_.clear();
}
size_t RasterCache::GetCachedEntriesCount() const {
return layer_cache_.size() + picture_cache_.size();
return layer_cache_.size() + picture_cache_.size() +
display_list_cache_.size();
}
size_t RasterCache::GetLayerCachedEntriesCount() const {
@ -287,6 +416,10 @@ size_t RasterCache::GetPictureCachedEntriesCount() const {
return picture_cache_.size();
}
size_t RasterCache::GetDisplayListCachedEntriesCount() const {
return display_list_cache_.size();
}
void RasterCache::SetCheckboardCacheImages(bool checkerboard) {
if (checkerboard_images_ == checkerboard) {
return;
@ -305,7 +438,10 @@ void RasterCache::TraceStatsToTimeline() const {
"LayerCount", layer_cache_.size(), "LayerMBytes",
EstimateLayerCacheByteSize() / kMegaByteSizeInBytes,
"PictureCount", picture_cache_.size(), "PictureMBytes",
EstimatePictureCacheByteSize() / kMegaByteSizeInBytes);
EstimatePictureCacheByteSize() / kMegaByteSizeInBytes,
"DisplayListCount", display_list_cache_.size(),
"DisplayListMBytes",
EstimateDisplayListCacheByteSize() / kMegaByteSizeInBytes);
#endif // !FLUTTER_RELEASE
}
@ -330,4 +466,14 @@ size_t RasterCache::EstimatePictureCacheByteSize() const {
return picture_cache_bytes;
}
size_t RasterCache::EstimateDisplayListCacheByteSize() const {
size_t display_list_cache_bytes = 0;
for (const auto& item : display_list_cache_) {
if (item.second.image) {
display_list_cache_bytes += item.second.image->image_bytes();
}
}
return display_list_cache_bytes;
}
} // namespace flutter

View File

@ -8,6 +8,7 @@
#include <memory>
#include <unordered_map>
#include "flutter/flow/display_list.h"
#include "flutter/flow/raster_cache_key.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/memory/weak_ptr.h"
@ -74,6 +75,12 @@ class RasterCache {
const SkMatrix& ctm,
SkColorSpace* dst_color_space,
bool checkerboard) const;
virtual std::unique_ptr<RasterCacheResult> RasterizeDisplayList(
DisplayList* display_list,
GrDirectContext* context,
const SkMatrix& ctm,
SkColorSpace* dst_color_space,
bool checkerboard) const;
/**
* @brief Rasterize an engine Layer and produce a RasterCacheResult
@ -138,6 +145,12 @@ class RasterCache {
SkColorSpace* dst_color_space,
bool is_complex,
bool will_change);
bool Prepare(GrDirectContext* context,
DisplayList* display_list,
const SkMatrix& transformation_matrix,
SkColorSpace* dst_color_space,
bool is_complex,
bool will_change);
void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm);
@ -146,6 +159,11 @@ class RasterCache {
// Return true if it's found and drawn.
bool Draw(const SkPicture& picture, SkCanvas& canvas) const;
// Find the raster cache for the display list and draw it to the canvas.
//
// Return true if it's found and drawn.
bool Draw(const DisplayList& display_list, SkCanvas& canvas) const;
// Find the raster cache for the layer and draw it to the canvas.
//
// Additional paint can be given to change how the raster cache is drawn
@ -168,6 +186,8 @@ class RasterCache {
size_t GetPictureCachedEntriesCount() const;
size_t GetDisplayListCachedEntriesCount() const;
/**
* @brief Estimate how much memory is used by picture raster cache entries in
* bytes.
@ -178,6 +198,16 @@ class RasterCache {
*/
size_t EstimatePictureCacheByteSize() const;
/**
* @brief Estimate how much memory is used by display list raster cache
* entries in bytes.
*
* Only SkImage's memory usage is counted as other objects are often much
* smaller compared to SkImage. SkImageInfo::computeMinByteSize is used to
* estimate the SkImage memory usage.
*/
size_t EstimateDisplayListCacheByteSize() const;
/**
* @brief Estimate how much memory is used by layer raster cache entries in
* bytes.
@ -216,6 +246,7 @@ class RasterCache {
const size_t picture_cache_limit_per_frame_;
size_t picture_cached_this_frame_ = 0;
mutable PictureRasterCacheKey::Map<Entry> picture_cache_;
mutable DisplayListRasterCacheKey::Map<Entry> display_list_cache_;
mutable LayerRasterCacheKey::Map<Entry> layer_cache_;
bool checkerboard_images_;

View File

@ -52,6 +52,9 @@ class RasterCacheKey {
// The ID is the uint32_t picture uniqueID
using PictureRasterCacheKey = RasterCacheKey<uint32_t>;
// The ID is the uint32_t DisplayList uniqueID
using DisplayListRasterCacheKey = RasterCacheKey<uint32_t>;
class Layer;
// The ID is the uint64_t layer unique_id

View File

@ -42,6 +42,20 @@ std::shared_ptr<PictureLayer> DiffContextTest::CreatePictureLayer(
offset, SkiaGPUObject(picture, unref_queue()), false, false);
}
sk_sp<DisplayList> DiffContextTest::CreateDisplayList(const SkRect& bounds,
SkColor color) {
DisplayListBuilder builder;
builder.setColor(color);
builder.drawRect(bounds);
return builder.Build();
}
std::shared_ptr<DisplayListLayer> DiffContextTest::CreateDisplayListLayer(
sk_sp<DisplayList> display_list,
const SkPoint& offset) {
return std::make_shared<DisplayListLayer>(offset, display_list, false, false);
}
std::shared_ptr<ContainerLayer> DiffContextTest::CreateContainerLayer(
std::initializer_list<std::shared_ptr<Layer>> layers) {
auto res = std::make_shared<ContainerLayer>();

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "flutter/flow/layers/container_layer.h"
#include "flutter/flow/layers/display_list_layer.h"
#include "flutter/flow/layers/picture_layer.h"
#include "flutter/flow/testing/skia_gpu_object_layer_test.h"
#include "third_party/skia/include/core/SkPicture.h"
@ -48,6 +49,14 @@ class DiffContextTest : public ThreadTest {
sk_sp<SkPicture> picture,
const SkPoint& offset = SkPoint::Make(0, 0));
// Create display list consisting of filled rect with given color; Being able
// to specify different color is useful to test deep comparison of pictures
sk_sp<DisplayList> CreateDisplayList(const SkRect& bounds, uint32_t color);
std::shared_ptr<DisplayListLayer> CreateDisplayListLayer(
sk_sp<DisplayList> display_list,
const SkPoint& offset = SkPoint::Make(0, 0));
std::shared_ptr<ContainerLayer> CreateContainerLayer(
std::initializer_list<std::shared_ptr<Layer>> layers);

View File

@ -10,6 +10,7 @@
#include "flutter/flow/layers/clip_rrect_layer.h"
#include "flutter/flow/layers/color_filter_layer.h"
#include "flutter/flow/layers/container_layer.h"
#include "flutter/flow/layers/display_list_layer.h"
#include "flutter/flow/layers/image_filter_layer.h"
#include "flutter/flow/layers/layer.h"
#include "flutter/flow/layers/layer_tree.h"
@ -265,10 +266,17 @@ void SceneBuilder::addPicture(double dx,
double dy,
Picture* picture,
int hints) {
auto layer = std::make_unique<flutter::PictureLayer>(
SkPoint::Make(dx, dy), UIDartState::CreateGPUObject(picture->picture()),
!!(hints & 1), !!(hints & 2));
AddLayer(std::move(layer));
if (picture->picture()) {
auto layer = std::make_unique<flutter::PictureLayer>(
SkPoint::Make(dx, dy), UIDartState::CreateGPUObject(picture->picture()),
!!(hints & 1), !!(hints & 2));
AddLayer(std::move(layer));
} else {
auto layer = std::make_unique<flutter::DisplayListLayer>(
SkPoint::Make(dx, dy), picture->display_list(), !!(hints & 1),
!!(hints & 2));
AddLayer(std::move(layer));
}
}
void SceneBuilder::addTexture(double dx,

View File

@ -85,6 +85,7 @@ fml::RefPtr<Canvas> Canvas::Create(PictureRecorder* recorder,
fml::RefPtr<Canvas> canvas = fml::MakeRefCounted<Canvas>(
recorder->BeginRecording(SkRect::MakeLTRB(left, top, right, bottom)));
recorder->set_canvas(canvas);
canvas->display_list_recorder_ = recorder->display_list_recorder();
return canvas;
}
@ -383,8 +384,19 @@ void Canvas::drawImageNine(const CanvasImage* image,
center.round(&icenter);
SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom);
auto filter = ImageFilter::FilterModeFromIndex(bitmapSamplingIndex);
canvas_->drawImageNine(image->image().get(), icenter, dst, filter,
paint.paint());
if (display_list_recorder_) {
// SkCanvas turns a simple 2-rect DrawImageNine operation into a
// drawImageLattice operation which has arrays to allocate and
// pass along. For simplicity, we will bypass the canvas and ask
// the recorder to record our paint attributes and record a much
// simpler DrawImageNineOp record directly.
display_list_recorder_->RecordPaintAttributes(
paint.paint(), DisplayListCanvasRecorder::DrawType::kImageOpType);
builder()->drawImageNine(image->image(), icenter, dst, filter);
} else {
canvas_->drawImageNine(image->image().get(), icenter, dst, filter,
paint.paint());
}
}
void Canvas::drawPicture(Picture* picture) {
@ -396,7 +408,17 @@ void Canvas::drawPicture(Picture* picture) {
ToDart("Canvas.drawPicture called with non-genuine Picture."));
return;
}
canvas_->drawPicture(picture->picture().get());
if (picture->picture()) {
canvas_->drawPicture(picture->picture().get());
} else if (picture->display_list()) {
if (display_list_recorder_) {
builder()->drawDisplayList(picture->display_list());
} else {
picture->display_list()->RenderTo(canvas_);
}
} else {
FML_DCHECK(false);
}
}
void Canvas::drawPoints(const Paint& paint,
@ -477,13 +499,24 @@ void Canvas::drawShadow(const CanvasPath* path,
ToDart("Canvas.drawShader called with non-genuine Path."));
return;
}
SkScalar dpr = UIDartState::Current()
->platform_configuration()
->get_window(0)
->viewport_metrics()
.device_pixel_ratio;
flutter::PhysicalShapeLayer::DrawShadow(canvas_, path->path(), color,
elevation, transparentOccluder, dpr);
if (display_list_recorder_) {
// The DrawShadow mechanism results in non-public operations to be
// performed on the canvas involving an SkDrawShadowRec. Since we
// cannot include the header that defines that structure, we cannot
// record an operation that it injects into an SkCanvas. To prevent
// that situation we bypass the canvas interface and inject the
// shadow parameters directly into the underlying DisplayList.
// See: https://bugs.chromium.org/p/skia/issues/detail?id=12125
builder()->drawShadow(path->path(), color, elevation, transparentOccluder);
} else {
SkScalar dpr = UIDartState::Current()
->platform_configuration()
->get_window(0)
->viewport_metrics()
.device_pixel_ratio;
flutter::PhysicalShapeLayer::DrawShadow(
canvas_, path->path(), color, elevation, transparentOccluder, dpr);
}
}
void Canvas::Invalidate() {

View File

@ -180,6 +180,17 @@ class Canvas : public RefCountedDartWrappable<Canvas> {
// which does not transfer ownership. For this reason, we hold a raw
// pointer and manually set to null in Clear.
SkCanvas* canvas_;
// A copy of the recorder used by the SkCanvas->DisplayList adapter for cases
// where we cannot record the SkCanvas method call through the various OnOp()
// virtual methods or where we can be more efficient by talking directly in
// the DisplayList operation lexicon. The recorder has a method for recording
// paint attributes from an SkPaint and an operation type as well as access
// to the raw DisplayListBuilder for emitting custom rendering operations.
sk_sp<DisplayListCanvasRecorder> display_list_recorder_;
sk_sp<DisplayListBuilder> builder() {
return display_list_recorder_->builder();
}
};
} // namespace flutter

View File

@ -6,8 +6,10 @@
#include "flutter/common/task_runners.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/lib/ui/painting/canvas.h"
#include "flutter/lib/ui/painting/image.h"
#include "flutter/lib/ui/painting/picture.h"
#include "flutter/lib/ui/painting/picture_recorder.h"
#include "flutter/runtime/dart_vm.h"
#include "flutter/shell/common/shell_test.h"
#include "flutter/shell/common/thread_host.h"
@ -31,6 +33,7 @@ class ImageDisposeTest : public ShellTest {
fml::AutoResetWaitableEvent message_latch_;
sk_sp<SkPicture> current_picture_;
sk_sp<DisplayList> current_display_list_;
sk_sp<SkImage> current_image_;
};
@ -45,9 +48,14 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) {
CanvasImage* image = GetNativePeer<CanvasImage>(native_image_handle);
Picture* picture = GetNativePeer<Picture>(Dart_GetNativeArgument(args, 1));
ASSERT_FALSE(image->image()->unique());
ASSERT_FALSE(picture->picture()->unique());
if (picture->display_list()) {
ASSERT_FALSE(picture->display_list()->unique());
current_display_list_ = picture->display_list();
} else {
ASSERT_FALSE(picture->picture()->unique());
current_picture_ = picture->picture();
}
current_image_ = image->image();
current_picture_ = picture->picture();
};
auto native_finish = [&](Dart_NativeArguments args) {
@ -84,7 +92,7 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) {
message_latch_.Wait();
ASSERT_TRUE(current_picture_);
ASSERT_TRUE(current_display_list_ || current_picture_);
ASSERT_TRUE(current_image_);
// Force a drain the SkiaUnrefQueue. The engine does this normally as frames
@ -96,8 +104,13 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) {
});
message_latch_.Wait();
EXPECT_TRUE(current_picture_->unique());
current_picture_.reset();
if (current_display_list_) {
EXPECT_TRUE(current_display_list_->unique());
current_display_list_.reset();
} else {
EXPECT_TRUE(current_picture_->unique());
current_picture_.reset();
}
EXPECT_TRUE(current_image_->unique());
current_image_.reset();

View File

@ -37,29 +37,50 @@ fml::RefPtr<Picture> Picture::Create(
return canvas_picture;
}
fml::RefPtr<Picture> Picture::Create(Dart_Handle dart_handle,
sk_sp<DisplayList> display_list) {
auto canvas_picture = fml::MakeRefCounted<Picture>(std::move(display_list));
canvas_picture->AssociateWithDartWrapper(dart_handle);
return canvas_picture;
}
Picture::Picture(flutter::SkiaGPUObject<SkPicture> picture)
: picture_(std::move(picture)) {}
Picture::Picture(sk_sp<DisplayList> display_list)
: display_list_(std::move(display_list)) {}
Picture::~Picture() = default;
Dart_Handle Picture::toImage(uint32_t width,
uint32_t height,
Dart_Handle raw_image_callback) {
if (!picture_.get()) {
return tonic::ToDart("Picture is null");
if (display_list_) {
return RasterizeToImage(
[display_list = display_list_.get()](SkCanvas* canvas) {
display_list->RenderTo(canvas);
},
width, height, raw_image_callback);
} else {
if (!picture_.get()) {
return tonic::ToDart("Picture is null");
}
return RasterizeToImage(picture_.get(), width, height, raw_image_callback);
}
return RasterizeToImage(picture_.get(), width, height, raw_image_callback);
}
void Picture::dispose() {
picture_.reset();
display_list_.reset();
ClearDartWrapper();
}
size_t Picture::GetAllocationSize() const {
if (auto picture = picture_.get()) {
return picture->approximateBytesUsed() + sizeof(Picture);
} else if (auto display_list = display_list_.get()) {
return display_list_->bytes() + sizeof(Picture);
} else {
return sizeof(Picture);
}
@ -69,6 +90,18 @@ Dart_Handle Picture::RasterizeToImage(sk_sp<SkPicture> picture,
uint32_t width,
uint32_t height,
Dart_Handle raw_image_callback) {
return RasterizeToImage(
[sk_picture = picture.get()](SkCanvas* canvas) {
canvas->drawPicture(sk_picture);
},
width, height, raw_image_callback);
}
Dart_Handle Picture::RasterizeToImage(
std::function<void(SkCanvas*)> draw_callback,
uint32_t width,
uint32_t height,
Dart_Handle raw_image_callback) {
if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) {
return tonic::ToDart("Image callback was invalid");
}
@ -121,10 +154,10 @@ Dart_Handle Picture::RasterizeToImage(sk_sp<SkPicture> picture,
// Kick things off on the raster rask runner.
fml::TaskRunner::RunNowOrPostTask(
raster_task_runner,
[ui_task_runner, snapshot_delegate, picture, picture_bounds, ui_task] {
sk_sp<SkImage> raster_image =
snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds);
raster_task_runner, [ui_task_runner, snapshot_delegate, draw_callback,
picture_bounds, ui_task] {
sk_sp<SkImage> raster_image = snapshot_delegate->MakeRasterSnapshot(
draw_callback, picture_bounds);
fml::TaskRunner::RunNowOrPostTask(
ui_task_runner,

View File

@ -5,6 +5,7 @@
#ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_H_
#define FLUTTER_LIB_UI_PAINTING_PICTURE_H_
#include "flutter/flow/display_list.h"
#include "flutter/flow/skia_gpu_object.h"
#include "flutter/lib/ui/dart_wrapper.h"
#include "flutter/lib/ui/painting/image.h"
@ -26,8 +27,11 @@ class Picture : public RefCountedDartWrappable<Picture> {
~Picture() override;
static fml::RefPtr<Picture> Create(Dart_Handle dart_handle,
flutter::SkiaGPUObject<SkPicture> picture);
static fml::RefPtr<Picture> Create(Dart_Handle dart_handle,
sk_sp<DisplayList> display_list);
sk_sp<SkPicture> picture() const { return picture_.get(); }
sk_sp<DisplayList> display_list() const { return display_list_; }
Dart_Handle toImage(uint32_t width,
uint32_t height,
@ -44,10 +48,18 @@ class Picture : public RefCountedDartWrappable<Picture> {
uint32_t height,
Dart_Handle raw_image_callback);
static Dart_Handle RasterizeToImage(
std::function<void(SkCanvas*)> draw_callback,
uint32_t width,
uint32_t height,
Dart_Handle raw_image_callback);
private:
Picture(flutter::SkiaGPUObject<SkPicture> picture);
Picture(sk_sp<DisplayList> display_list);
flutter::SkiaGPUObject<SkPicture> picture_;
sk_sp<DisplayList> display_list_;
};
} // namespace flutter

View File

@ -39,7 +39,13 @@ PictureRecorder::PictureRecorder() {}
PictureRecorder::~PictureRecorder() {}
SkCanvas* PictureRecorder::BeginRecording(SkRect bounds) {
return picture_recorder_.beginRecording(bounds, &rtree_factory_);
bool enable_display_list = UIDartState::Current()->enable_display_list();
if (enable_display_list) {
display_list_recorder_ = sk_make_sp<DisplayListCanvasRecorder>(bounds);
return display_list_recorder_.get();
} else {
return picture_recorder_.beginRecording(bounds, &rtree_factory_);
}
}
fml::RefPtr<Picture> PictureRecorder::endRecording(Dart_Handle dart_picture) {
@ -47,9 +53,16 @@ fml::RefPtr<Picture> PictureRecorder::endRecording(Dart_Handle dart_picture) {
return nullptr;
}
fml::RefPtr<Picture> picture = Picture::Create(
dart_picture, UIDartState::CreateGPUObject(
picture_recorder_.finishRecordingAsPicture()));
fml::RefPtr<Picture> picture;
if (display_list_recorder_) {
picture = Picture::Create(dart_picture, display_list_recorder_->Build());
display_list_recorder_ = nullptr;
} else {
picture = Picture::Create(
dart_picture, UIDartState::CreateGPUObject(
picture_recorder_.finishRecordingAsPicture()));
}
canvas_->Invalidate();
canvas_ = nullptr;

View File

@ -5,6 +5,7 @@
#ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_
#define FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_
#include "flutter/flow/display_list_canvas.h"
#include "flutter/lib/ui/dart_wrapper.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
@ -28,6 +29,10 @@ class PictureRecorder : public RefCountedDartWrappable<PictureRecorder> {
SkCanvas* BeginRecording(SkRect bounds);
fml::RefPtr<Picture> endRecording(Dart_Handle dart_picture);
sk_sp<DisplayListCanvasRecorder> display_list_recorder() {
return display_list_recorder_;
}
void set_canvas(fml::RefPtr<Canvas> canvas) { canvas_ = std::move(canvas); }
static void RegisterNatives(tonic::DartLibraryNatives* natives);
@ -37,6 +42,9 @@ class PictureRecorder : public RefCountedDartWrappable<PictureRecorder> {
SkRTreeFactory rtree_factory_;
SkPictureRecorder picture_recorder_;
sk_sp<DisplayListCanvasRecorder> display_list_recorder_;
fml::RefPtr<Canvas> canvas_;
};

View File

@ -12,6 +12,10 @@ namespace flutter {
class SnapshotDelegate {
public:
virtual sk_sp<SkImage> MakeRasterSnapshot(
std::function<void(SkCanvas*)> draw_callback,
SkISize picture_size) = 0;
virtual sk_sp<SkImage> MakeRasterSnapshot(sk_sp<SkPicture> picture,
SkISize picture_size) = 0;

View File

@ -56,6 +56,7 @@ UIDartState::UIDartState(
std::shared_ptr<IsolateNameServer> isolate_name_server,
bool is_root_isolate,
bool enable_skparagraph,
bool enable_display_list,
const UIDartState::Context& context)
: add_callback_(std::move(add_callback)),
remove_callback_(std::move(remove_callback)),
@ -65,6 +66,7 @@ UIDartState::UIDartState(
log_message_callback_(log_message_callback),
isolate_name_server_(std::move(isolate_name_server)),
enable_skparagraph_(enable_skparagraph),
enable_display_list_(enable_display_list),
context_(std::move(context)) {
AddOrRemoveTaskObserver(true /* add */);
}
@ -238,4 +240,8 @@ bool UIDartState::enable_skparagraph() const {
return enable_skparagraph_;
}
bool UIDartState::enable_display_list() const {
return enable_display_list_;
}
} // namespace flutter

View File

@ -143,6 +143,8 @@ class UIDartState : public tonic::DartState {
bool enable_skparagraph() const;
bool enable_display_list() const;
template <class T>
static flutter::SkiaGPUObject<T> CreateGPUObject(sk_sp<T> object) {
if (!object) {
@ -163,6 +165,7 @@ class UIDartState : public tonic::DartState {
std::shared_ptr<IsolateNameServer> isolate_name_server,
bool is_root_isolate_,
bool enable_skparagraph,
bool enable_display_list,
const UIDartState::Context& context);
~UIDartState() override;
@ -189,6 +192,7 @@ class UIDartState : public tonic::DartState {
LogMessageCallback log_message_callback_;
const std::shared_ptr<IsolateNameServer> isolate_name_server_;
const bool enable_skparagraph_;
const bool enable_display_list_;
UIDartState::Context context_;
void AddOrRemoveTaskObserver(bool add);

View File

@ -311,6 +311,7 @@ DartIsolate::DartIsolate(const Settings& settings,
DartVMRef::GetIsolateNameServer(),
is_root_isolate,
settings.enable_skparagraph,
settings.enable_display_list,
std::move(context)),
may_insecurely_connect_to_all_domains_(
settings.may_insecurely_connect_to_all_domains),

View File

@ -313,6 +313,12 @@ sk_sp<SkImage> Rasterizer::DoMakeRasterSnapshot(
return result;
}
sk_sp<SkImage> Rasterizer::MakeRasterSnapshot(
std::function<void(SkCanvas*)> draw_callback,
SkISize picture_size) {
return DoMakeRasterSnapshot(picture_size, draw_callback);
}
sk_sp<SkImage> Rasterizer::MakeRasterSnapshot(sk_sp<SkPicture> picture,
SkISize picture_size) {
return DoMakeRasterSnapshot(picture_size,

View File

@ -449,6 +449,11 @@ class Rasterizer final : public SnapshotDelegate {
std::shared_ptr<ExternalViewEmbedder> external_view_embedder_;
bool shared_engine_block_thread_merging_ = false;
// |SnapshotDelegate|
sk_sp<SkImage> MakeRasterSnapshot(
std::function<void(SkCanvas*)> draw_callback,
SkISize picture_size) override;
// |SnapshotDelegate|
sk_sp<SkImage> MakeRasterSnapshot(sk_sp<SkPicture> picture,
SkISize picture_size) override;

View File

@ -1690,6 +1690,9 @@ bool Shell::OnServiceProtocolEstimateRasterCacheMemory(
response->AddMember<uint64_t>("pictureBytes",
raster_cache.EstimatePictureCacheByteSize(),
response->GetAllocator());
response->AddMember<uint64_t>("displayListBytes",
raster_cache.EstimateDisplayListCacheByteSize(),
response->GetAllocator());
return true;
}

View File

@ -2110,7 +2110,7 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) {
document.Accept(writer);
std::string expected_json =
"{\"type\":\"EstimateRasterCacheMemory\",\"layerBytes\":40000,\"picture"
"Bytes\":400}";
"Bytes\":400,\"displayListBytes\":0}";
std::string actual_json = buffer.GetString();
ASSERT_EQ(actual_json, expected_json);

View File

@ -66,6 +66,8 @@ static const std::string gAllowedDartFlags[] = {
"--write-service-info",
"--null_assertions",
"--strict_null_safety_checks",
"--enable-display-list",
"--no-enable-display-list",
};
// clang-format on
@ -404,6 +406,16 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
settings.dart_flags.push_back(flag);
}
}
if (std::find(settings.dart_flags.begin(), settings.dart_flags.end(),
"--enable-display-list") != settings.dart_flags.end()) {
FML_LOG(ERROR) << "Manually enabling display lists";
settings.enable_display_list = true;
} else if (std::find(settings.dart_flags.begin(), settings.dart_flags.end(),
"--no-enable-display-list") !=
settings.dart_flags.end()) {
FML_LOG(ERROR) << "Manually disabling display lists";
settings.enable_display_list = false;
}
#if !FLUTTER_RELEASE
command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag);

View File

@ -105,6 +105,7 @@ void MockCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
void MockCanvas::onDrawShadowRec(const SkPath& path,
const SkDrawShadowRec& rec) {
// See: https://bugs.chromium.org/p/skia/issues/detail?id=12125
(void)rec; // Can't use b/c Skia keeps this type anonymous.
draw_calls_.emplace_back(DrawCall{current_layer_, DrawShadowData{path}});
}