mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Implement a DisplayList mechanism similar to the Skia SkLiteDL mechanism (flutter/engine#26928)
This commit is contained in:
parent
f117c5bf1e
commit
ce11db413a
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
1368
engine/src/flutter/flow/display_list.cc
Normal file
1368
engine/src/flutter/flow/display_list.cc
Normal file
File diff suppressed because it is too large
Load Diff
452
engine/src/flutter/flow/display_list.h
Normal file
452
engine/src/flutter/flow/display_list.h
Normal 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_
|
||||
481
engine/src/flutter/flow/display_list_canvas.cc
Normal file
481
engine/src/flutter/flow/display_list_canvas.cc
Normal 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
|
||||
312
engine/src/flutter/flow/display_list_canvas.h
Normal file
312
engine/src/flutter/flow/display_list_canvas.h
Normal 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_
|
||||
1296
engine/src/flutter/flow/display_list_canvas_unittests.cc
Normal file
1296
engine/src/flutter/flow/display_list_canvas_unittests.cc
Normal file
File diff suppressed because it is too large
Load Diff
830
engine/src/flutter/flow/display_list_unittests.cc
Normal file
830
engine/src/flutter/flow/display_list_unittests.cc
Normal 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
|
||||
428
engine/src/flutter/flow/display_list_utils.cc
Normal file
428
engine/src/flutter/flow/display_list_utils.cc
Normal 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
|
||||
372
engine/src/flutter/flow/display_list_utils.h
Normal file
372
engine/src/flutter/flow/display_list_utils.h
Normal 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_
|
||||
129
engine/src/flutter/flow/layers/display_list_layer.cc
Normal file
129
engine/src/flutter/flow/layers/display_list_layer.cc
Normal 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
|
||||
61
engine/src/flutter/flow/layers/display_list_layer.h
Normal file
61
engine/src/flutter/flow/layers/display_list_layer.h
Normal 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_
|
||||
159
engine/src/flutter/flow/layers/display_list_layer_unittests.cc
Normal file
159
engine/src/flutter/flow/layers/display_list_layer_unittests.cc
Normal 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
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>();
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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}});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user