Revert "Reland "add non-rendering operation culling to DisplayListBuilder" (#41463)" (flutter/engine#43721)

Reverts flutter/engine#43698

A framework tree test started failing on the engine roll with this PR: https://github.com/flutter/flutter/pull/130643

The test failure is in https://ci.chromium.org/ui/p/flutter/builders/prod/Linux_android%20hybrid_android_views_integration_test/8517/overview

```
[2023-07-14 19:33:21.980926] [STDOUT] stdout: [   +6 ms] I/PlatformViewsController( 9988): Using hybrid composition for platform view: 5
[2023-07-14 19:33:22.767236] [STDOUT] stdout: [ +786 ms] 00:19 +4 -1: Flutter surface with hybrid composition Uses FlutterImageView when Android view is on the screen [E]
[2023-07-14 19:33:22.767765] [STDOUT] stdout: [        ]   Expected: '|-FlutterView\n'
[2023-07-14 19:33:22.767815] [STDOUT] stdout: [        ]               '  |-FlutterSurfaceView\n'
[2023-07-14 19:33:22.767924] [STDOUT] stdout: [        ]               '  |-FlutterImageView\n'
[2023-07-14 19:33:22.768084] [STDOUT] stdout: [        ]               '  |-ViewGroup\n'
[2023-07-14 19:33:22.768162] [STDOUT] stdout: [        ]               '    |-ViewGroup\n'
[2023-07-14 19:33:22.768800] [STDOUT] stdout: [        ]               '  |-FlutterImageView\n'
[2023-07-14 19:33:22.768835] [STDOUT] stdout: [        ]               ''
[2023-07-14 19:33:22.768853] [STDOUT] stdout: [        ]     Actual: '|-FlutterView\n'
[2023-07-14 19:33:22.768882] [STDOUT] stdout: [        ]               '  |-FlutterSurfaceView\n'
[2023-07-14 19:33:22.768900] [STDOUT] stdout: [        ]               '  |-FlutterImageView\n'
[2023-07-14 19:33:22.768917] [STDOUT] stdout: [        ]               '  |-ViewGroup\n'
[2023-07-14 19:33:22.768956] [STDOUT] stdout: [        ]               '    |-ViewGroup\n'
[2023-07-14 19:33:22.769119] [STDOUT] stdout: [        ]               ''
[2023-07-14 19:33:22.769156] [STDOUT] stdout: [        ]      Which: is different. Both strings start the same, but the actual value is missing the following trailing characters:   |-Flutte ...
[2023-07-14 19:33:22.779280] [STDOUT] stdout: [  +10 ms]   package:matcher/src/expect/expect.dart 149:31     fail
[2023-07-14 19:33:22.779326] [STDOUT] stdout: [        ]   package:matcher/src/expect/expect.dart 144:3      _expect
[2023-07-14 19:33:22.780315] [STDOUT] stdout: [        ]   package:matcher/src/expect/expect.dart 56:3       expect
[2023-07-14 19:33:22.780345] [STDOUT] stdout: [        ]   test_driver/main_test.dart 124:7                  main.<fn>.<fn>
[2023-07-14 19:33:22.780356] [STDOUT] stdout: [        ]   ===== asynchronous gap ===========================
[2023-07-14 19:33:22.780365] [STDOUT] stdout: [        ]   package:test_api/src/backend/declarer.dart 215:9  Declarer.test.<fn>.<fn>
[2023-07-14 19:33:22.780376] [STDOUT] stdout: [        ]   ===== asynchronous gap ===========================
[2023-07-14 19:33:22.780385] [STDOUT] stdout: [        ]   package:test_api/src/backend/declarer.dart 213:7  Declarer.test.<fn>
[2023-07-14 19:33:22.780395] [STDOUT] stdout: [        ]   ===== asynchronous gap ===========================
[2023-07-14 19:33:22.780405] [STDOUT] stdout: [        ]   package:test_api/src/backend/invoker.dart 258:9   Invoker._waitForOutstandingCallbacks.<fn>
[2023-07-14 19:33:22.780415] [STDOUT] stdout: [        ] 00:19 +4 -1: Flutter surface with hybrid composition (tearDownAll)
[2023-07-14 19:33:22.907295] [STDOUT] stdout: [ +126 ms] 00:19 +4 -1: (tearDownAll)
[2023-07-14 19:33:22.947855] [STDOUT] stdout: [  +41 ms] 00:19 +4 -1: Some tests failed.
```

This change in that roll looks like it may be related.
This commit is contained in:
Zachary Anderson
2023-07-15 07:14:10 -07:00
committed by GitHub
parent a7a6ecc923
commit 6237207218
20 changed files with 320 additions and 1276 deletions

View File

@@ -423,7 +423,7 @@ TEST(DisplayListComplexity, DrawAtlas) {
std::vector<SkRSXform> xforms;
for (int i = 0; i < 10; i++) {
rects.push_back(SkRect::MakeXYWH(0, 0, 10, 10));
xforms.push_back(SkRSXform::Make(1, 0, 0, 0));
xforms.push_back(SkRSXform::Make(0, 0, 0, 0));
}
DisplayListBuilder builder;

View File

@@ -22,8 +22,7 @@ DisplayList::DisplayList()
unique_id_(0),
bounds_({0, 0, 0, 0}),
can_apply_group_opacity_(true),
is_ui_thread_safe_(true),
modifies_transparent_black_(false) {}
is_ui_thread_safe_(true) {}
DisplayList::DisplayList(DisplayListStorage&& storage,
size_t byte_count,
@@ -33,7 +32,6 @@ DisplayList::DisplayList(DisplayListStorage&& storage,
const SkRect& bounds,
bool can_apply_group_opacity,
bool is_ui_thread_safe,
bool modifies_transparent_black,
sk_sp<const DlRTree> rtree)
: storage_(std::move(storage)),
byte_count_(byte_count),
@@ -44,7 +42,6 @@ DisplayList::DisplayList(DisplayListStorage&& storage,
bounds_(bounds),
can_apply_group_opacity_(can_apply_group_opacity),
is_ui_thread_safe_(is_ui_thread_safe),
modifies_transparent_black_(modifies_transparent_black),
rtree_(std::move(rtree)) {}
DisplayList::~DisplayList() {

View File

@@ -265,19 +265,6 @@ class DisplayList : public SkRefCnt {
bool can_apply_group_opacity() const { return can_apply_group_opacity_; }
bool isUIThreadSafe() const { return is_ui_thread_safe_; }
/// @brief Indicates if there are any rendering operations in this
/// DisplayList that will modify a surface of transparent black
/// pixels.
///
/// This condition can be used to determine whether to create a cleared
/// surface, render a DisplayList into it, and then composite the
/// result into a scene. It is not uncommon for code in the engine to
/// come across such degenerate DisplayList objects when slicing up a
/// frame between platform views.
bool modifies_transparent_black() const {
return modifies_transparent_black_;
}
private:
DisplayList(DisplayListStorage&& ptr,
size_t byte_count,
@@ -287,7 +274,6 @@ class DisplayList : public SkRefCnt {
const SkRect& bounds,
bool can_apply_group_opacity,
bool is_ui_thread_safe,
bool modifies_transparent_black,
sk_sp<const DlRTree> rtree);
static uint32_t next_unique_id();
@@ -306,8 +292,6 @@ class DisplayList : public SkRefCnt {
const bool can_apply_group_opacity_;
const bool is_ui_thread_safe_;
const bool modifies_transparent_black_;
const sk_sp<const DlRTree> rtree_;
void Dispatch(DlOpReceiver& ctx,

View File

@@ -24,7 +24,6 @@
#include "third_party/skia/include/core/SkBBHFactory.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkRSXform.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace flutter {
@@ -3019,164 +3018,5 @@ TEST_F(DisplayListTest, DrawUnorderedRoundRectPathCCW) {
check_inverted_bounds(renderer, "DrawRoundRectPath Counter-Clockwise");
}
TEST_F(DisplayListTest, NopOperationsOmittedFromRecords) {
auto run_tests = [](const std::string& name,
void init(DisplayListBuilder & builder, DlPaint & paint),
uint32_t expected_op_count = 0u) {
auto run_one_test =
[init](const std::string& name,
void build(DisplayListBuilder & builder, DlPaint & paint),
uint32_t expected_op_count = 0u) {
DisplayListBuilder builder;
DlPaint paint;
init(builder, paint);
build(builder, paint);
auto list = builder.Build();
if (list->op_count() != expected_op_count) {
FML_LOG(ERROR) << *list;
}
ASSERT_EQ(list->op_count(), expected_op_count) << name;
ASSERT_TRUE(list->bounds().isEmpty()) << name;
};
run_one_test(
name + " DrawColor",
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.DrawColor(paint.getColor(), paint.getBlendMode());
},
expected_op_count);
run_one_test(
name + " DrawPaint",
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.DrawPaint(paint);
},
expected_op_count);
run_one_test(
name + " DrawRect",
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.DrawRect({10, 10, 20, 20}, paint);
},
expected_op_count);
run_one_test(
name + " Other Draw Ops",
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.DrawLine({10, 10}, {20, 20}, paint);
builder.DrawOval({10, 10, 20, 20}, paint);
builder.DrawCircle({50, 50}, 20, paint);
builder.DrawRRect(SkRRect::MakeRectXY({10, 10, 20, 20}, 5, 5), paint);
builder.DrawDRRect(SkRRect::MakeRectXY({5, 5, 100, 100}, 5, 5),
SkRRect::MakeRectXY({10, 10, 20, 20}, 5, 5),
paint);
builder.DrawPath(kTestPath1, paint);
builder.DrawArc({10, 10, 20, 20}, 45, 90, true, paint);
SkPoint pts[] = {{10, 10}, {20, 20}};
builder.DrawPoints(PointMode::kLines, 2, pts, paint);
builder.DrawVertices(TestVertices1, DlBlendMode::kSrcOver, paint);
builder.DrawImage(TestImage1, {10, 10}, DlImageSampling::kLinear,
&paint);
builder.DrawImageRect(TestImage1, SkRect{0.0f, 0.0f, 10.0f, 10.0f},
SkRect{10.0f, 10.0f, 25.0f, 25.0f},
DlImageSampling::kLinear, &paint);
builder.DrawImageNine(TestImage1, {10, 10, 20, 20},
{10, 10, 100, 100}, DlFilterMode::kLinear,
&paint);
SkRSXform xforms[] = {{1, 0, 10, 10}, {0, 1, 10, 10}};
SkRect rects[] = {{10, 10, 20, 20}, {10, 20, 30, 20}};
builder.DrawAtlas(TestImage1, xforms, rects, nullptr, 2,
DlBlendMode::kSrcOver, DlImageSampling::kLinear,
nullptr, &paint);
builder.DrawTextBlob(TestBlob1, 10, 10, paint);
// Dst mode eliminates most rendering ops except for
// the following two, so we'll prune those manually...
if (paint.getBlendMode() != DlBlendMode::kDst) {
builder.DrawDisplayList(TestDisplayList1, paint.getOpacity());
builder.DrawShadow(kTestPath1, paint.getColor(), 1, true, 1);
}
},
expected_op_count);
run_one_test(
name + " SaveLayer",
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.SaveLayer(nullptr, &paint, nullptr);
builder.DrawRect({10, 10, 20, 20}, DlPaint());
builder.Restore();
},
expected_op_count);
run_one_test(
name + " inside Save",
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.Save();
builder.DrawRect({10, 10, 20, 20}, paint);
builder.Restore();
},
expected_op_count);
};
run_tests("transparent color", //
[](DisplayListBuilder& builder, DlPaint& paint) {
paint.setColor(DlColor::kTransparent());
});
run_tests("0 alpha", //
[](DisplayListBuilder& builder, DlPaint& paint) {
// The transparent test above already tested transparent
// black (all 0s), we set White color here so we can test
// the case of all 1s with a 0 alpha
paint.setColor(DlColor::kWhite());
paint.setAlpha(0);
});
run_tests("BlendMode::kDst", //
[](DisplayListBuilder& builder, DlPaint& paint) {
paint.setBlendMode(DlBlendMode::kDst);
});
run_tests("Empty rect clip", //
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.ClipRect(SkRect::MakeEmpty(), ClipOp::kIntersect, false);
});
run_tests("Empty rrect clip", //
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.ClipRRect(SkRRect::MakeEmpty(), ClipOp::kIntersect,
false);
});
run_tests("Empty path clip", //
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.ClipPath(SkPath(), ClipOp::kIntersect, false);
});
run_tests("Transparent SaveLayer", //
[](DisplayListBuilder& builder, DlPaint& paint) {
DlPaint save_paint;
save_paint.setColor(DlColor::kTransparent());
builder.SaveLayer(nullptr, &save_paint);
});
run_tests("0 alpha SaveLayer", //
[](DisplayListBuilder& builder, DlPaint& paint) {
DlPaint save_paint;
// The transparent test above already tested transparent
// black (all 0s), we set White color here so we can test
// the case of all 1s with a 0 alpha
save_paint.setColor(DlColor::kWhite());
save_paint.setAlpha(0);
builder.SaveLayer(nullptr, &save_paint);
});
run_tests("Dst blended SaveLayer", //
[](DisplayListBuilder& builder, DlPaint& paint) {
DlPaint save_paint;
save_paint.setBlendMode(DlBlendMode::kDst);
builder.SaveLayer(nullptr, &save_paint);
});
run_tests(
"Nop inside SaveLayer",
[](DisplayListBuilder& builder, DlPaint& paint) {
builder.SaveLayer(nullptr, nullptr);
paint.setBlendMode(DlBlendMode::kDst);
},
2u);
run_tests("DrawImage inside Culled SaveLayer", //
[](DisplayListBuilder& builder, DlPaint& paint) {
DlPaint save_paint;
save_paint.setColor(DlColor::kTransparent());
builder.SaveLayer(nullptr, &save_paint);
builder.DrawImage(TestImage1, {10, 10}, DlImageSampling::kLinear);
});
}
} // namespace testing
} // namespace flutter

View File

@@ -6,12 +6,10 @@
#include "flutter/display_list/display_list.h"
#include "flutter/display_list/dl_blend_mode.h"
#include "flutter/display_list/dl_op_flags.h"
#include "flutter/display_list/dl_op_records.h"
#include "flutter/display_list/effects/dl_color_source.h"
#include "flutter/display_list/utils/dl_bounds_accumulator.h"
#include "fml/logging.h"
#include "third_party/skia/include/core/SkScalar.h"
namespace flutter {
@@ -72,22 +70,20 @@ sk_sp<DisplayList> DisplayListBuilder::Build() {
int count = render_op_count_;
size_t nested_bytes = nested_bytes_;
int nested_count = nested_op_count_;
bool compatible = current_layer_->is_group_opacity_compatible();
bool compatible = layer_stack_.back().is_group_opacity_compatible();
bool is_safe = is_ui_thread_safe_;
bool affects_transparency = current_layer_->affects_transparent_layer();
used_ = allocated_ = render_op_count_ = op_index_ = 0;
nested_bytes_ = nested_op_count_ = 0;
is_ui_thread_safe_ = true;
storage_.realloc(bytes);
layer_stack_.pop_back();
layer_stack_.emplace_back();
tracker_.reset();
current_ = DlPaint();
return sk_sp<DisplayList>(new DisplayList(
std::move(storage_), bytes, count, nested_bytes, nested_count, bounds(),
compatible, is_safe, affects_transparency, rtree()));
return sk_sp<DisplayList>(
new DisplayList(std::move(storage_), bytes, count, nested_bytes,
nested_count, bounds(), compatible, is_safe, rtree()));
}
DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect,
@@ -393,11 +389,9 @@ void DisplayListBuilder::checkForDeferredSave() {
}
void DisplayListBuilder::Save() {
bool is_nop = current_layer_->is_nop_;
layer_stack_.emplace_back();
current_layer_ = &layer_stack_.back();
current_layer_->has_deferred_save_op_ = true;
current_layer_->is_nop_ = is_nop;
tracker_.save();
accumulator()->save();
}
@@ -484,16 +478,18 @@ void DisplayListBuilder::saveLayer(const SkRect* bounds,
const SaveLayerOptions in_options,
const DlImageFilter* backdrop) {
SaveLayerOptions options = in_options.without_optimizations();
DisplayListAttributeFlags flags = options.renders_with_attributes()
? kSaveLayerWithPaintFlags
: kSaveLayerFlags;
OpResult result = PaintResult(current_, flags);
if (result == OpResult::kNoEffect) {
save();
current_layer_->is_nop_ = true;
return;
}
size_t save_layer_offset = used_;
if (backdrop) {
bounds //
? Push<SaveLayerBackdropBoundsOp>(0, 1, options, *bounds, backdrop)
: Push<SaveLayerBackdropOp>(0, 1, options, backdrop);
} else {
bounds //
? Push<SaveLayerBoundsOp>(0, 1, options, *bounds)
: Push<SaveLayerOp>(0, 1, options);
}
CheckLayerOpacityCompatibility(options.renders_with_attributes());
if (options.renders_with_attributes()) {
// The actual flood of the outer layer clip will occur after the
// (eventual) corresponding restore is called, but rather than
@@ -504,41 +500,17 @@ void DisplayListBuilder::saveLayer(const SkRect* bounds,
// with its full bounds and the right op_index so that it doesn't
// get culled during rendering.
if (!paint_nops_on_transparency()) {
// We will fill the clip of the outer layer when we restore.
// Accumulate should always return true here because if the
// clip was empty then that would have been caught up above
// when we tested the PaintResult.
[[maybe_unused]] bool unclipped = AccumulateUnbounded();
FML_DCHECK(unclipped);
// We will fill the clip of the outer layer when we restore
AccumulateUnbounded();
}
CheckLayerOpacityCompatibility(true);
layer_stack_.emplace_back(save_layer_offset, true,
current_.getImageFilter());
} else {
CheckLayerOpacityCompatibility(false);
layer_stack_.emplace_back(save_layer_offset, true, nullptr);
}
current_layer_ = &layer_stack_.back();
tracker_.save();
accumulator()->save();
if (backdrop) {
// A backdrop will affect up to the entire surface, bounded by the clip
// Accumulate should always return true here because if the
// clip was empty then that would have been caught up above
// when we tested the PaintResult.
[[maybe_unused]] bool unclipped = AccumulateUnbounded();
FML_DCHECK(unclipped);
bounds //
? Push<SaveLayerBackdropBoundsOp>(0, 1, options, *bounds, backdrop)
: Push<SaveLayerBackdropOp>(0, 1, options, backdrop);
} else {
bounds //
? Push<SaveLayerBoundsOp>(0, 1, options, *bounds)
: Push<SaveLayerOp>(0, 1, options);
}
current_layer_ = &layer_stack_.back();
if (options.renders_with_attributes()) {
// |current_opacity_compatibility_| does not take an ImageFilter into
// account because an individual primitive with an ImageFilter can apply
@@ -549,7 +521,6 @@ void DisplayListBuilder::saveLayer(const SkRect* bounds,
UpdateLayerOpacityCompatibility(false);
}
}
UpdateLayerResult(result);
// Even though Skia claims that the bounds are only a hint, they actually
// use them as the temporary layer bounds during rendering the layer, so
@@ -557,6 +528,10 @@ void DisplayListBuilder::saveLayer(const SkRect* bounds,
if (bounds) {
tracker_.clipRect(*bounds, ClipOp::kIntersect, false);
}
if (backdrop) {
// A backdrop will affect up to the entire surface, bounded by the clip
AccumulateUnbounded();
}
}
void DisplayListBuilder::SaveLayer(const SkRect* bounds,
const DlPaint* paint,
@@ -679,11 +654,6 @@ void DisplayListBuilder::ClipRect(const SkRect& rect,
if (!rect.isFinite()) {
return;
}
tracker_.clipRect(rect, clip_op, is_aa);
if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) {
current_layer_->is_nop_ = true;
return;
}
checkForDeferredSave();
switch (clip_op) {
case ClipOp::kIntersect:
@@ -693,6 +663,7 @@ void DisplayListBuilder::ClipRect(const SkRect& rect,
Push<ClipDifferenceRectOp>(0, 1, rect, is_aa);
break;
}
tracker_.clipRect(rect, clip_op, is_aa);
}
void DisplayListBuilder::ClipRRect(const SkRRect& rrect,
ClipOp clip_op,
@@ -700,11 +671,6 @@ void DisplayListBuilder::ClipRRect(const SkRRect& rrect,
if (rrect.isRect()) {
clipRect(rrect.rect(), clip_op, is_aa);
} else {
tracker_.clipRRect(rrect, clip_op, is_aa);
if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) {
current_layer_->is_nop_ = true;
return;
}
checkForDeferredSave();
switch (clip_op) {
case ClipOp::kIntersect:
@@ -714,6 +680,7 @@ void DisplayListBuilder::ClipRRect(const SkRRect& rrect,
Push<ClipDifferenceRRectOp>(0, 1, rrect, is_aa);
break;
}
tracker_.clipRRect(rrect, clip_op, is_aa);
}
}
void DisplayListBuilder::ClipPath(const SkPath& path,
@@ -736,11 +703,6 @@ void DisplayListBuilder::ClipPath(const SkPath& path,
return;
}
}
tracker_.clipPath(path, clip_op, is_aa);
if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) {
current_layer_->is_nop_ = true;
return;
}
checkForDeferredSave();
switch (clip_op) {
case ClipOp::kIntersect:
@@ -750,6 +712,7 @@ void DisplayListBuilder::ClipPath(const SkPath& path,
Push<ClipDifferencePathOp>(0, 1, path, is_aa);
break;
}
tracker_.clipPath(path, clip_op, is_aa);
}
bool DisplayListBuilder::QuickReject(const SkRect& bounds) const {
@@ -757,36 +720,27 @@ bool DisplayListBuilder::QuickReject(const SkRect& bounds) const {
}
void DisplayListBuilder::drawPaint() {
OpResult result = PaintResult(current_, kDrawPaintFlags);
if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
Push<DrawPaintOp>(0, 1);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
}
Push<DrawPaintOp>(0, 1);
CheckLayerOpacityCompatibility();
AccumulateUnbounded();
}
void DisplayListBuilder::DrawPaint(const DlPaint& paint) {
SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawPaintFlags);
drawPaint();
}
void DisplayListBuilder::DrawColor(DlColor color, DlBlendMode mode) {
OpResult result = PaintResult(DlPaint(color).setBlendMode(mode));
if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
Push<DrawColorOp>(0, 1, color, mode);
CheckLayerOpacityCompatibility(mode);
UpdateLayerResult(result);
}
Push<DrawColorOp>(0, 1, color, mode);
CheckLayerOpacityCompatibility(mode);
AccumulateUnbounded();
}
void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) {
Push<DrawLineOp>(0, 1, p0, p1);
CheckLayerOpacityCompatibility();
SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted();
DisplayListAttributeFlags flags =
(bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags
: kDrawHVLineFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
Push<DrawLineOp>(0, 1, p0, p1);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
}
AccumulateOpBounds(bounds, flags);
}
void DisplayListBuilder::DrawLine(const SkPoint& p0,
const SkPoint& p1,
@@ -795,45 +749,29 @@ void DisplayListBuilder::DrawLine(const SkPoint& p0,
drawLine(p0, p1);
}
void DisplayListBuilder::drawRect(const SkRect& rect) {
DisplayListAttributeFlags flags = kDrawRectFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect &&
AccumulateOpBounds(rect.makeSorted(), flags)) {
Push<DrawRectOp>(0, 1, rect);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
}
Push<DrawRectOp>(0, 1, rect);
CheckLayerOpacityCompatibility();
AccumulateOpBounds(rect.makeSorted(), kDrawRectFlags);
}
void DisplayListBuilder::DrawRect(const SkRect& rect, const DlPaint& paint) {
SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawRectFlags);
drawRect(rect);
}
void DisplayListBuilder::drawOval(const SkRect& bounds) {
DisplayListAttributeFlags flags = kDrawOvalFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect &&
AccumulateOpBounds(bounds.makeSorted(), flags)) {
Push<DrawOvalOp>(0, 1, bounds);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
}
Push<DrawOvalOp>(0, 1, bounds);
CheckLayerOpacityCompatibility();
AccumulateOpBounds(bounds.makeSorted(), kDrawOvalFlags);
}
void DisplayListBuilder::DrawOval(const SkRect& bounds, const DlPaint& paint) {
SetAttributesFromPaint(paint, DisplayListOpFlags::kDrawOvalFlags);
drawOval(bounds);
}
void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) {
DisplayListAttributeFlags flags = kDrawCircleFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect) {
SkRect bounds = SkRect::MakeLTRB(center.fX - radius, center.fY - radius,
center.fX + radius, center.fY + radius);
if (AccumulateOpBounds(bounds, flags)) {
Push<DrawCircleOp>(0, 1, center, radius);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
}
}
Push<DrawCircleOp>(0, 1, center, radius);
CheckLayerOpacityCompatibility();
AccumulateOpBounds(SkRect::MakeLTRB(center.fX - radius, center.fY - radius,
center.fX + radius, center.fY + radius),
kDrawCircleFlags);
}
void DisplayListBuilder::DrawCircle(const SkPoint& center,
SkScalar radius,
@@ -847,14 +785,9 @@ void DisplayListBuilder::drawRRect(const SkRRect& rrect) {
} else if (rrect.isOval()) {
drawOval(rrect.rect());
} else {
DisplayListAttributeFlags flags = kDrawRRectFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect &&
AccumulateOpBounds(rrect.getBounds(), flags)) {
Push<DrawRRectOp>(0, 1, rrect);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
}
Push<DrawRRectOp>(0, 1, rrect);
CheckLayerOpacityCompatibility();
AccumulateOpBounds(rrect.getBounds(), kDrawRRectFlags);
}
}
void DisplayListBuilder::DrawRRect(const SkRRect& rrect, const DlPaint& paint) {
@@ -863,14 +796,9 @@ void DisplayListBuilder::DrawRRect(const SkRRect& rrect, const DlPaint& paint) {
}
void DisplayListBuilder::drawDRRect(const SkRRect& outer,
const SkRRect& inner) {
DisplayListAttributeFlags flags = kDrawDRRectFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect &&
AccumulateOpBounds(outer.getBounds(), flags)) {
Push<DrawDRRectOp>(0, 1, outer, inner);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
}
Push<DrawDRRectOp>(0, 1, outer, inner);
CheckLayerOpacityCompatibility();
AccumulateOpBounds(outer.getBounds(), kDrawDRRectFlags);
}
void DisplayListBuilder::DrawDRRect(const SkRRect& outer,
const SkRRect& inner,
@@ -879,17 +807,12 @@ void DisplayListBuilder::DrawDRRect(const SkRRect& outer,
drawDRRect(outer, inner);
}
void DisplayListBuilder::drawPath(const SkPath& path) {
DisplayListAttributeFlags flags = kDrawPathFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect) {
bool is_visible = path.isInverseFillType()
? AccumulateUnbounded()
: AccumulateOpBounds(path.getBounds(), flags);
if (is_visible) {
Push<DrawPathOp>(0, 1, path);
CheckLayerOpacityHairlineCompatibility();
UpdateLayerResult(result);
}
Push<DrawPathOp>(0, 1, path);
CheckLayerOpacityHairlineCompatibility();
if (path.isInverseFillType()) {
AccumulateUnbounded();
} else {
AccumulateOpBounds(path.getBounds(), kDrawPathFlags);
}
}
void DisplayListBuilder::DrawPath(const SkPath& path, const DlPaint& paint) {
@@ -901,23 +824,19 @@ void DisplayListBuilder::drawArc(const SkRect& bounds,
SkScalar start,
SkScalar sweep,
bool useCenter) {
DisplayListAttributeFlags flags = //
useCenter //
? kDrawArcWithCenterFlags
: kDrawArcNoCenterFlags;
OpResult result = PaintResult(current_, flags);
Push<DrawArcOp>(0, 1, bounds, start, sweep, useCenter);
if (useCenter) {
CheckLayerOpacityHairlineCompatibility();
} else {
CheckLayerOpacityCompatibility();
}
// 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.
if (result != OpResult::kNoEffect && AccumulateOpBounds(bounds, flags)) {
Push<DrawArcOp>(0, 1, bounds, start, sweep, useCenter);
if (useCenter) {
CheckLayerOpacityHairlineCompatibility();
} else {
CheckLayerOpacityCompatibility();
}
UpdateLayerResult(result);
}
AccumulateOpBounds(bounds,
useCenter //
? kDrawArcWithCenterFlags
: kDrawArcNoCenterFlags);
}
void DisplayListBuilder::DrawArc(const SkRect& bounds,
SkScalar start,
@@ -928,31 +847,14 @@ void DisplayListBuilder::DrawArc(const SkRect& bounds,
paint, useCenter ? kDrawArcWithCenterFlags : kDrawArcNoCenterFlags);
drawArc(bounds, start, sweep, useCenter);
}
DisplayListAttributeFlags DisplayListBuilder::FlagsForPointMode(
PointMode mode) {
switch (mode) {
case DlCanvas::PointMode::kPoints:
return kDrawPointsAsPointsFlags;
case PointMode::kLines:
return kDrawPointsAsLinesFlags;
case PointMode::kPolygon:
return kDrawPointsAsPolygonFlags;
}
FML_UNREACHABLE();
}
void DisplayListBuilder::drawPoints(PointMode mode,
uint32_t count,
const SkPoint pts[]) {
if (count == 0) {
return;
}
DisplayListAttributeFlags flags = FlagsForPointMode(mode);
OpResult result = PaintResult(current_, flags);
if (result == OpResult::kNoEffect) {
return;
}
void* data_ptr;
FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount);
int bytes = count * sizeof(SkPoint);
RectBoundsAccumulator ptBounds;
@@ -960,23 +862,21 @@ void DisplayListBuilder::drawPoints(PointMode mode,
ptBounds.accumulate(pts[i]);
}
SkRect point_bounds = ptBounds.bounds();
if (!AccumulateOpBounds(point_bounds, flags)) {
return;
}
void* data_ptr;
switch (mode) {
case PointMode::kPoints:
data_ptr = Push<DrawPointsOp>(bytes, 1, count);
AccumulateOpBounds(point_bounds, kDrawPointsAsPointsFlags);
break;
case PointMode::kLines:
data_ptr = Push<DrawLinesOp>(bytes, 1, count);
AccumulateOpBounds(point_bounds, kDrawPointsAsLinesFlags);
break;
case PointMode::kPolygon:
data_ptr = Push<DrawPolygonOp>(bytes, 1, count);
AccumulateOpBounds(point_bounds, kDrawPointsAsPolygonFlags);
break;
default:
FML_UNREACHABLE();
FML_DCHECK(false);
return;
}
CopyV(data_ptr, pts, count);
@@ -986,30 +886,39 @@ void DisplayListBuilder::drawPoints(PointMode mode,
// bounds of every sub-primitive.
// See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c
UpdateLayerOpacityCompatibility(false);
UpdateLayerResult(result);
}
void DisplayListBuilder::DrawPoints(PointMode mode,
uint32_t count,
const SkPoint pts[],
const DlPaint& paint) {
SetAttributesFromPaint(paint, FlagsForPointMode(mode));
const DisplayListAttributeFlags* flags;
switch (mode) {
case PointMode::kPoints:
flags = &DisplayListOpFlags::kDrawPointsAsPointsFlags;
break;
case PointMode::kLines:
flags = &DisplayListOpFlags::kDrawPointsAsLinesFlags;
break;
case PointMode::kPolygon:
flags = &DisplayListOpFlags::kDrawPointsAsPolygonFlags;
break;
default:
FML_DCHECK(false);
return;
}
SetAttributesFromPaint(paint, *flags);
drawPoints(mode, count, pts);
}
void DisplayListBuilder::drawVertices(const DlVertices* vertices,
DlBlendMode mode) {
DisplayListAttributeFlags flags = kDrawVerticesFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect &&
AccumulateOpBounds(vertices->bounds(), flags)) {
void* pod = Push<DrawVerticesOp>(vertices->size(), 1, mode);
new (pod) DlVertices(vertices);
// DrawVertices applies its colors to the paint so we have no way
// of controlling opacity using the current paint attributes.
// Although, examination of the |mode| might find some predictable
// cases.
UpdateLayerOpacityCompatibility(false);
UpdateLayerResult(result);
}
void* pod = Push<DrawVerticesOp>(vertices->size(), 1, mode);
new (pod) DlVertices(vertices);
// DrawVertices applies its colors to the paint so we have no way
// of controlling opacity using the current paint attributes.
// Although, examination of the |mode| might find some predictable
// cases.
UpdateLayerOpacityCompatibility(false);
AccumulateOpBounds(vertices->bounds(), kDrawVerticesFlags);
}
void DisplayListBuilder::DrawVertices(const DlVertices* vertices,
DlBlendMode mode,
@@ -1022,23 +931,17 @@ void DisplayListBuilder::drawImage(const sk_sp<DlImage> image,
const SkPoint point,
DlImageSampling sampling,
bool render_with_attributes) {
render_with_attributes
? Push<DrawImageWithAttrOp>(0, 1, image, point, sampling)
: Push<DrawImageOp>(0, 1, image, point, sampling);
CheckLayerOpacityCompatibility(render_with_attributes);
is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, //
image->width(), image->height());
DisplayListAttributeFlags flags = render_with_attributes //
? kDrawImageWithPaintFlags
: kDrawImageFlags;
OpResult result = PaintResult(current_, flags);
if (result == OpResult::kNoEffect) {
return;
}
SkRect bounds = SkRect::MakeXYWH(point.fX, point.fY, //
image->width(), image->height());
if (AccumulateOpBounds(bounds, flags)) {
render_with_attributes
? Push<DrawImageWithAttrOp>(0, 1, image, point, sampling)
: Push<DrawImageOp>(0, 1, image, point, sampling);
CheckLayerOpacityCompatibility(render_with_attributes);
UpdateLayerResult(result);
is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
}
AccumulateOpBounds(bounds, flags);
}
void DisplayListBuilder::DrawImage(const sk_sp<DlImage>& image,
const SkPoint point,
@@ -1058,17 +961,14 @@ void DisplayListBuilder::drawImageRect(const sk_sp<DlImage> image,
DlImageSampling sampling,
bool render_with_attributes,
SrcRectConstraint constraint) {
Push<DrawImageRectOp>(0, 1, image, src, dst, sampling, render_with_attributes,
constraint);
CheckLayerOpacityCompatibility(render_with_attributes);
is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
DisplayListAttributeFlags flags = render_with_attributes
? kDrawImageRectWithPaintFlags
: kDrawImageRectFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
Push<DrawImageRectOp>(0, 1, image, src, dst, sampling,
render_with_attributes, constraint);
CheckLayerOpacityCompatibility(render_with_attributes);
UpdateLayerResult(result);
is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
}
AccumulateOpBounds(dst, flags);
}
void DisplayListBuilder::DrawImageRect(const sk_sp<DlImage>& image,
const SkRect& src,
@@ -1089,18 +989,15 @@ void DisplayListBuilder::drawImageNine(const sk_sp<DlImage> image,
const SkRect& dst,
DlFilterMode filter,
bool render_with_attributes) {
render_with_attributes
? Push<DrawImageNineWithAttrOp>(0, 1, image, center, dst, filter)
: Push<DrawImageNineOp>(0, 1, image, center, dst, filter);
CheckLayerOpacityCompatibility(render_with_attributes);
is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
DisplayListAttributeFlags flags = render_with_attributes
? kDrawImageNineWithPaintFlags
: kDrawImageNineFlags;
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect && AccumulateOpBounds(dst, flags)) {
render_with_attributes
? Push<DrawImageNineWithAttrOp>(0, 1, image, center, dst, filter)
: Push<DrawImageNineOp>(0, 1, image, center, dst, filter);
CheckLayerOpacityCompatibility(render_with_attributes);
UpdateLayerResult(result);
is_ui_thread_safe_ = is_ui_thread_safe_ && image->isUIThreadSafe();
}
AccumulateOpBounds(dst, flags);
}
void DisplayListBuilder::DrawImageNine(const sk_sp<DlImage>& image,
const SkIRect& center,
@@ -1124,27 +1021,6 @@ void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
DlImageSampling sampling,
const SkRect* cull_rect,
bool render_with_attributes) {
DisplayListAttributeFlags flags = render_with_attributes //
? kDrawAtlasWithPaintFlags
: kDrawAtlasFlags;
OpResult result = PaintResult(current_, flags);
if (result == OpResult::kNoEffect) {
return;
}
SkPoint quad[4];
RectBoundsAccumulator 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.is_empty() ||
!AccumulateOpBounds(atlasBounds.bounds(), flags)) {
return;
}
int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect));
void* data_ptr;
if (colors != nullptr) {
@@ -1173,8 +1049,23 @@ void DisplayListBuilder::drawAtlas(const sk_sp<DlImage> atlas,
// on it to distribute the opacity without overlap without checking all
// of the transforms and texture rectangles.
UpdateLayerOpacityCompatibility(false);
UpdateLayerResult(result);
is_ui_thread_safe_ = is_ui_thread_safe_ && atlas->isUIThreadSafe();
SkPoint quad[4];
RectBoundsAccumulator 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.is_not_empty()) {
DisplayListAttributeFlags flags = render_with_attributes //
? kDrawAtlasWithPaintFlags
: kDrawAtlasFlags;
AccumulateOpBounds(atlasBounds.bounds(), flags);
}
}
void DisplayListBuilder::DrawAtlas(const sk_sp<DlImage>& atlas,
const SkRSXform xform[],
@@ -1198,42 +1089,8 @@ void DisplayListBuilder::DrawAtlas(const sk_sp<DlImage>& atlas,
void DisplayListBuilder::DrawDisplayList(const sk_sp<DisplayList> display_list,
SkScalar opacity) {
if (!SkScalarIsFinite(opacity) || opacity <= SK_ScalarNearlyZero ||
display_list->op_count() == 0 || display_list->bounds().isEmpty() ||
current_layer_->is_nop_) {
return;
}
const SkRect bounds = display_list->bounds();
bool accumulated;
switch (accumulator()->type()) {
case BoundsAccumulatorType::kRect:
accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags);
break;
case BoundsAccumulatorType::kRTree:
auto rtree = display_list->rtree();
if (rtree) {
std::list<SkRect> rects =
rtree->searchAndConsolidateRects(bounds, false);
accumulated = false;
for (const SkRect& rect : rects) {
// TODO (https://github.com/flutter/flutter/issues/114919): Attributes
// are not necessarily `kDrawDisplayListFlags`.
if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) {
accumulated = true;
}
}
} else {
accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags);
}
break;
}
if (!accumulated) {
return;
}
DlPaint current_paint = current_;
Push<DrawDisplayListOp>(0, 1, display_list,
opacity < SK_Scalar1 ? opacity : SK_Scalar1);
Push<DrawDisplayListOp>(0, 1, display_list, opacity);
is_ui_thread_safe_ = is_ui_thread_safe_ && display_list->isUIThreadSafe();
// Not really necessary if the developer is interacting with us via
// our attribute-state-less DlCanvas methods, but this avoids surprises
@@ -1241,6 +1098,26 @@ void DisplayListBuilder::DrawDisplayList(const sk_sp<DisplayList> display_list,
SetAttributesFromPaint(current_paint,
DisplayListOpFlags::kSaveLayerWithPaintFlags);
const SkRect bounds = display_list->bounds();
switch (accumulator()->type()) {
case BoundsAccumulatorType::kRect:
AccumulateOpBounds(bounds, kDrawDisplayListFlags);
break;
case BoundsAccumulatorType::kRTree:
auto rtree = display_list->rtree();
if (rtree) {
std::list<SkRect> rects =
rtree->searchAndConsolidateRects(bounds, false);
for (const SkRect& rect : rects) {
// TODO (https://github.com/flutter/flutter/issues/114919): Attributes
// are not necessarily `kDrawDisplayListFlags`.
AccumulateOpBounds(rect, kDrawDisplayListFlags);
}
} else {
AccumulateOpBounds(bounds, kDrawDisplayListFlags);
}
break;
}
// The non-nested op count accumulated in the |Push| method will include
// this call to |drawDisplayList| for non-nested op count metrics.
// But, for nested op count metrics we want the |drawDisplayList| call itself
@@ -1250,38 +1127,18 @@ void DisplayListBuilder::DrawDisplayList(const sk_sp<DisplayList> display_list,
nested_op_count_ += display_list->op_count(true) - 1;
nested_bytes_ += display_list->bytes(true);
UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity());
// Nop DisplayLists are eliminated above so we either affect transparent
// pixels or we do not. We should not have [kNoEffect].
UpdateLayerResult(display_list->modifies_transparent_black()
? OpResult::kAffectsAll
: OpResult::kPreservesTransparency);
}
void DisplayListBuilder::drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) {
DisplayListAttributeFlags flags = kDrawTextBlobFlags;
OpResult result = PaintResult(current_, flags);
if (result == OpResult::kNoEffect) {
return;
}
bool unclipped = AccumulateOpBounds(blob->bounds().makeOffset(x, y), flags);
// TODO(https://github.com/flutter/flutter/issues/82202): Remove once the
// unit tests can use Fuchsia's font manager instead of the empty default.
// Until then we might encounter empty bounds for otherwise valid text and
// thus we ignore the results from AccumulateOpBounds.
#if defined(OS_FUCHSIA)
unclipped = true;
#endif // OS_FUCHSIA
if (unclipped) {
Push<DrawTextBlobOp>(0, 1, blob, x, y);
// There is no way to query if the glyphs of a text blob overlap and
// there are no current guarantees from either Skia or Impeller that
// they will protect overlapping glyphs from the effects of overdraw
// so we must make the conservative assessment that this DL layer is
// not compatible with group opacity inheritance.
UpdateLayerOpacityCompatibility(false);
UpdateLayerResult(result);
}
Push<DrawTextBlobOp>(0, 1, blob, x, y);
AccumulateOpBounds(blob->bounds().makeOffset(x, y), kDrawTextBlobFlags);
// There is no way to query if the glyphs of a text blob overlap and
// there are no current guarantees from either Skia or Impeller that
// they will protect overlapping glyphs from the effects of overdraw
// so we must make the conservative assessment that this DL layer is
// not compatible with group opacity inheritance.
UpdateLayerOpacityCompatibility(false);
}
void DisplayListBuilder::DrawTextBlob(const sk_sp<SkTextBlob>& blob,
SkScalar x,
@@ -1295,19 +1152,14 @@ void DisplayListBuilder::DrawShadow(const SkPath& path,
const SkScalar elevation,
bool transparent_occluder,
SkScalar dpr) {
OpResult result = PaintResult(DlPaint(color));
if (result != OpResult::kNoEffect) {
SkRect shadow_bounds =
DlCanvas::ComputeShadowBounds(path, elevation, dpr, GetTransform());
if (AccumulateOpBounds(shadow_bounds, kDrawShadowFlags)) {
transparent_occluder //
? Push<DrawShadowTransparentOccluderOp>(0, 1, path, color, elevation,
dpr)
: Push<DrawShadowOp>(0, 1, path, color, elevation, dpr);
UpdateLayerOpacityCompatibility(false);
UpdateLayerResult(result);
}
}
transparent_occluder //
? Push<DrawShadowTransparentOccluderOp>(0, 1, path, color, elevation, dpr)
: Push<DrawShadowOp>(0, 1, path, color, elevation, dpr);
SkRect shadow_bounds =
DlCanvas::ComputeShadowBounds(path, elevation, dpr, GetTransform());
AccumulateOpBounds(shadow_bounds, kDrawShadowFlags);
UpdateLayerOpacityCompatibility(false);
}
bool DisplayListBuilder::ComputeFilteredBounds(SkRect& bounds,
@@ -1377,40 +1229,31 @@ bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds,
return true;
}
bool DisplayListBuilder::AccumulateUnbounded() {
SkRect clip = tracker_.device_cull_rect();
if (clip.isEmpty()) {
return false;
}
accumulator()->accumulate(clip, op_index_);
return true;
void DisplayListBuilder::AccumulateUnbounded() {
accumulator()->accumulate(tracker_.device_cull_rect(), op_index_ - 1);
}
bool DisplayListBuilder::AccumulateOpBounds(SkRect& bounds,
void DisplayListBuilder::AccumulateOpBounds(SkRect& bounds,
DisplayListAttributeFlags flags) {
if (AdjustBoundsForPaint(bounds, flags)) {
return AccumulateBounds(bounds);
AccumulateBounds(bounds);
} else {
return AccumulateUnbounded();
AccumulateUnbounded();
}
}
bool DisplayListBuilder::AccumulateBounds(SkRect& bounds) {
if (!bounds.isEmpty()) {
tracker_.mapRect(&bounds);
if (bounds.intersect(tracker_.device_cull_rect())) {
accumulator()->accumulate(bounds, op_index_);
return true;
}
void DisplayListBuilder::AccumulateBounds(SkRect& bounds) {
tracker_.mapRect(&bounds);
if (bounds.intersect(tracker_.device_cull_rect())) {
accumulator()->accumulate(bounds, op_index_ - 1);
}
return false;
}
bool DisplayListBuilder::paint_nops_on_transparency() {
// SkImageFilter::canComputeFastBounds tests for transparency behavior
// This test assumes that the blend mode checked down below will
// NOP on transparent black.
if (current_.getImageFilterPtr() &&
current_.getImageFilterPtr()->modifies_transparent_black()) {
if (current_.getImageFilter() &&
current_.getImageFilter()->modifies_transparent_black()) {
return false;
}
@@ -1420,8 +1263,8 @@ bool DisplayListBuilder::paint_nops_on_transparency() {
// save layer untouched out to the edge of the output surface.
// This test assumes that the blend mode checked down below will
// NOP on transparent black.
if (current_.getColorFilterPtr() &&
current_.getColorFilterPtr()->modifies_transparent_black()) {
if (current_.getColorFilter() &&
current_.getColorFilter()->modifies_transparent_black()) {
return false;
}
@@ -1478,130 +1321,4 @@ bool DisplayListBuilder::paint_nops_on_transparency() {
break;
}
}
DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint,
DisplayListAttributeFlags flags) {
DlColor color;
if (flags.applies_color()) {
const DlColorSource* source = paint.getColorSourcePtr();
if (source) {
if (source->asColor()) {
color = source->asColor()->color();
} else {
color = source->is_opaque() ? DlColor::kBlack() : kAnyColor;
}
} else {
color = paint.getColor();
}
} else if (flags.applies_alpha()) {
// If the operation applies alpha, but not color, then the only impact
// of the alpha is to modulate the output towards transparency.
// We can not guarantee an opaque source even if the alpha is opaque
// since that would require knowing something about the colors that
// the alpha is modulating, but we can guarantee a transparent source
// if the alpha is 0.
color = (paint.getAlpha() == 0) ? DlColor::kTransparent() : kAnyColor;
} else {
color = kAnyColor;
}
if (flags.applies_image_filter()) {
auto filter = paint.getImageFilterPtr();
if (filter) {
if (!color.isTransparent() || filter->modifies_transparent_black()) {
color = kAnyColor;
}
}
}
if (flags.applies_color_filter()) {
auto filter = paint.getColorFilterPtr();
if (filter) {
if (!color.isTransparent() || filter->modifies_transparent_black()) {
color = kAnyColor;
}
}
}
return color;
}
DisplayListBuilder::OpResult DisplayListBuilder::PaintResult(
const DlPaint& paint,
DisplayListAttributeFlags flags) {
if (current_layer_->is_nop_) {
return OpResult::kNoEffect;
}
if (flags.applies_blend()) {
switch (paint.getBlendMode()) {
// Nop blend mode (singular, there is only one)
case DlBlendMode::kDst:
return OpResult::kNoEffect;
// Always clears pixels blend mode (singular, there is only one)
case DlBlendMode::kClear:
return OpResult::kPreservesTransparency;
case DlBlendMode::kHue:
case DlBlendMode::kSaturation:
case DlBlendMode::kColor:
case DlBlendMode::kLuminosity:
case DlBlendMode::kColorBurn:
return GetEffectiveColor(paint, flags).isTransparent()
? OpResult::kNoEffect
: OpResult::kAffectsAll;
// kSrcIn modifies pixels towards transparency
case DlBlendMode::kSrcIn:
return OpResult::kPreservesTransparency;
// These blend modes preserve destination alpha
case DlBlendMode::kSrcATop:
case DlBlendMode::kDstOut:
return GetEffectiveColor(paint, flags).isTransparent()
? OpResult::kNoEffect
: OpResult::kPreservesTransparency;
// Always destructive blend modes, potentially not affecting transparency
case DlBlendMode::kSrc:
case DlBlendMode::kSrcOut:
case DlBlendMode::kDstATop:
return GetEffectiveColor(paint, flags).isTransparent()
? OpResult::kPreservesTransparency
: OpResult::kAffectsAll;
// The kDstIn blend mode modifies the destination unless the
// source color is opaque.
case DlBlendMode::kDstIn:
return GetEffectiveColor(paint, flags).isOpaque()
? OpResult::kNoEffect
: OpResult::kPreservesTransparency;
// The next group of blend modes modifies the destination unless the
// source color is transparent.
case DlBlendMode::kSrcOver:
case DlBlendMode::kDstOver:
case DlBlendMode::kXor:
case DlBlendMode::kPlus:
case DlBlendMode::kScreen:
case DlBlendMode::kMultiply:
case DlBlendMode::kOverlay:
case DlBlendMode::kDarken:
case DlBlendMode::kLighten:
case DlBlendMode::kColorDodge:
case DlBlendMode::kHardLight:
case DlBlendMode::kSoftLight:
case DlBlendMode::kDifference:
case DlBlendMode::kExclusion:
return GetEffectiveColor(paint, flags).isTransparent()
? OpResult::kNoEffect
: OpResult::kAffectsAll;
// Modulate only leaves the pixel alone when the source is white.
case DlBlendMode::kModulate:
return GetEffectiveColor(paint, flags) == DlColor::kWhite()
? OpResult::kNoEffect
: OpResult::kPreservesTransparency;
}
}
return OpResult::kAffectsAll;
}
} // namespace flutter

View File

@@ -506,13 +506,15 @@ class DisplayListBuilder final : public virtual DlCanvas,
class LayerInfo {
public:
explicit LayerInfo(
size_t save_offset = 0,
bool has_layer = false,
const std::shared_ptr<const DlImageFilter>& filter = nullptr)
explicit LayerInfo(size_t save_offset = 0,
bool has_layer = false,
std::shared_ptr<const DlImageFilter> filter = nullptr)
: save_offset_(save_offset),
has_layer_(has_layer),
filter_(filter) {}
cannot_inherit_opacity_(false),
has_compatible_op_(false),
filter_(filter),
is_unbounded_(false) {}
// The offset into the memory buffer where the saveLayer DLOp record
// for this saveLayer() call is placed. This may be needed if the
@@ -525,9 +527,6 @@ class DisplayListBuilder final : public virtual DlCanvas,
bool has_layer() const { return has_layer_; }
bool cannot_inherit_opacity() const { return cannot_inherit_opacity_; }
bool has_compatible_op() const { return has_compatible_op_; }
bool affects_transparent_layer() const {
return affects_transparent_layer_;
}
bool is_group_opacity_compatible() const {
return !cannot_inherit_opacity_;
@@ -550,12 +549,6 @@ class DisplayListBuilder final : public virtual DlCanvas,
}
}
// Records that the current layer contains an op that produces visible
// output on a transparent surface.
void add_visible_op() {
affects_transparent_layer_ = true;
}
// The filter to apply to the layer bounds when it is restored
std::shared_ptr<const DlImageFilter> filter() { return filter_; }
@@ -590,13 +583,11 @@ class DisplayListBuilder final : public virtual DlCanvas,
private:
size_t save_offset_;
bool has_layer_;
bool cannot_inherit_opacity_ = false;
bool has_compatible_op_ = false;
bool cannot_inherit_opacity_;
bool has_compatible_op_;
std::shared_ptr<const DlImageFilter> filter_;
bool is_unbounded_ = false;
bool is_unbounded_;
bool has_deferred_save_op_ = false;
bool is_nop_ = false;
bool affects_transparent_layer_ = false;
friend class DisplayListBuilder;
};
@@ -710,40 +701,9 @@ class DisplayListBuilder final : public virtual DlCanvas,
return accumulator_->rtree();
}
static DisplayListAttributeFlags FlagsForPointMode(PointMode mode);
enum class OpResult {
kNoEffect,
kPreservesTransparency,
kAffectsAll,
};
bool paint_nops_on_transparency();
OpResult PaintResult(const DlPaint& paint,
DisplayListAttributeFlags flags = kDrawPaintFlags);
void UpdateLayerResult(OpResult result) {
switch (result) {
case OpResult::kNoEffect:
case OpResult::kPreservesTransparency:
break;
case OpResult::kAffectsAll:
current_layer_->add_visible_op();
break;
}
}
// kAnyColor is a non-opaque and non-transparent color that will not
// trigger any short-circuit tests about the results of a blend.
static constexpr DlColor kAnyColor = DlColor::kMidGrey().withAlpha(0x80);
static_assert(!kAnyColor.isOpaque());
static_assert(!kAnyColor.isTransparent());
static DlColor GetEffectiveColor(const DlPaint& paint,
DisplayListAttributeFlags flags);
// Computes the bounds of an operation adjusted for a given ImageFilter
// and returns whether the computation was possible. If the method
// returns false then the caller should assume the worst about the bounds.
static bool ComputeFilteredBounds(SkRect& bounds,
const DlImageFilter* filter);
@@ -753,24 +713,24 @@ class DisplayListBuilder final : public virtual DlCanvas,
// Records the fact that we encountered an op that either could not
// estimate its bounds or that fills all of the destination space.
bool AccumulateUnbounded();
void AccumulateUnbounded();
// Records the bounds for an op after modifying them according to the
// supplied attribute flags and transforming by the current matrix.
bool AccumulateOpBounds(const SkRect& bounds,
void AccumulateOpBounds(const SkRect& bounds,
DisplayListAttributeFlags flags) {
SkRect safe_bounds = bounds;
return AccumulateOpBounds(safe_bounds, flags);
AccumulateOpBounds(safe_bounds, flags);
}
// Records the bounds for an op after modifying them according to the
// supplied attribute flags and transforming by the current matrix
// and clipping against the current clip.
bool AccumulateOpBounds(SkRect& bounds, DisplayListAttributeFlags flags);
void AccumulateOpBounds(SkRect& bounds, DisplayListAttributeFlags flags);
// Records the given bounds after transforming by the current matrix
// and clipping against the current clip.
bool AccumulateBounds(SkRect& bounds);
void AccumulateBounds(SkRect& bounds);
DlPaint current_;
};

View File

@@ -34,20 +34,20 @@ struct DlColor {
uint32_t argb;
constexpr bool isOpaque() const { return getAlpha() == 0xFF; }
constexpr bool isTransparent() const { return getAlpha() == 0; }
bool isOpaque() const { return getAlpha() == 0xFF; }
bool isTransparent() const { return getAlpha() == 0; }
constexpr int getAlpha() const { return argb >> 24; }
constexpr int getRed() const { return (argb >> 16) & 0xFF; }
constexpr int getGreen() const { return (argb >> 8) & 0xFF; }
constexpr int getBlue() const { return argb & 0xFF; }
int getAlpha() const { return argb >> 24; }
int getRed() const { return (argb >> 16) & 0xFF; }
int getGreen() const { return (argb >> 8) & 0xFF; }
int getBlue() const { return argb & 0xFF; }
constexpr float getAlphaF() const { return toF(getAlpha()); }
constexpr float getRedF() const { return toF(getRed()); }
constexpr float getGreenF() const { return toF(getGreen()); }
constexpr float getBlueF() const { return toF(getBlue()); }
float getAlphaF() const { return toF(getAlpha()); }
float getRedF() const { return toF(getRed()); }
float getGreenF() const { return toF(getGreen()); }
float getBlueF() const { return toF(getBlue()); }
constexpr uint32_t premultipliedArgb() const {
uint32_t premultipliedArgb() const {
if (isOpaque()) {
return argb;
}
@@ -58,20 +58,20 @@ struct DlColor {
toC(getBlueF() * f);
}
constexpr DlColor withAlpha(uint8_t alpha) const { //
DlColor withAlpha(uint8_t alpha) const { //
return (argb & 0x00FFFFFF) | (alpha << 24);
}
constexpr DlColor withRed(uint8_t red) const { //
DlColor withRed(uint8_t red) const { //
return (argb & 0xFF00FFFF) | (red << 16);
}
constexpr DlColor withGreen(uint8_t green) const { //
DlColor withGreen(uint8_t green) const { //
return (argb & 0xFFFF00FF) | (green << 8);
}
constexpr DlColor withBlue(uint8_t blue) const { //
DlColor withBlue(uint8_t blue) const { //
return (argb & 0xFFFFFF00) | (blue << 0);
}
constexpr DlColor modulateOpacity(float opacity) const {
DlColor modulateOpacity(float opacity) const {
return opacity <= 0 ? withAlpha(0)
: opacity >= 1 ? *this
: withAlpha(round(getAlpha() * opacity));

View File

@@ -83,7 +83,6 @@ class DlPaint {
color_.argb = alpha << 24 | (color_.argb & 0x00FFFFFF);
return *this;
}
SkScalar getOpacity() const { return color_.getAlphaF(); }
DlPaint& setOpacity(SkScalar opacity) {
setAlpha(SkScalarRoundToInt(opacity * 0xff));
return *this;

View File

@@ -9,7 +9,6 @@
#include "flutter/display_list/dl_op_flags.h"
#include "flutter/display_list/dl_sampling_options.h"
#include "flutter/display_list/skia/dl_sk_canvas.h"
#include "flutter/display_list/skia/dl_sk_conversions.h"
#include "flutter/display_list/skia/dl_sk_dispatcher.h"
#include "flutter/display_list/testing/dl_test_surface_provider.h"
#include "flutter/display_list/utils/dl_comparable.h"
@@ -284,26 +283,20 @@ using BackendType = DlSurfaceProvider::BackendType;
class RenderResult {
public:
explicit RenderResult(const sk_sp<SkSurface>& surface,
bool take_snapshot = false) {
explicit RenderResult(const sk_sp<SkSurface>& surface) {
SkImageInfo info = surface->imageInfo();
info = SkImageInfo::MakeN32Premul(info.dimensions());
addr_ = malloc(info.computeMinByteSize() * info.height());
pixmap_.reset(info, addr_, info.minRowBytes());
surface->readPixels(pixmap_, 0, 0);
if (take_snapshot) {
image_ = surface->makeImageSnapshot();
}
EXPECT_TRUE(surface->readPixels(pixmap_, 0, 0));
}
~RenderResult() { free(addr_); }
sk_sp<SkImage> image() const { return image_; }
int width() const { return pixmap_.width(); }
int height() const { return pixmap_.height(); }
const uint32_t* addr32(int x, int y) const { return pixmap_.addr32(x, y); }
private:
sk_sp<SkImage> image_;
SkPixmap pixmap_;
void* addr_ = nullptr;
};
@@ -919,14 +912,7 @@ class CanvasCompareTester {
};
DlRenderer dl_safe_restore = [=](DlCanvas* cv, const DlPaint& p) {
// Draw another primitive to disable peephole optimizations
// As the rendering op rejection in the DisplayList Builder
// gets smarter and smarter, this operation has had to get
// sneakier and sneakier about specifying an operation that
// won't practically show up in the output, but technically
// can't be culled.
cv->DrawRect(
SkRect::MakeXYWH(kRenderCenterX, kRenderCenterY, 0.0001, 0.0001),
DlPaint());
cv->DrawRect(kRenderBounds.makeOffset(500, 500), DlPaint());
cv->Restore();
};
SkRenderer sk_opt_restore = [=](SkCanvas* cv, const SkPaint& p) {
@@ -3799,6 +3785,7 @@ TEST_F(DisplayListCanvas, MatrixColorFilterOpacityCommuteCheck) {
}
#define FOR_EACH_BLEND_MODE_ENUM(FUNC) \
FUNC(kSrc) \
FUNC(kClear) \
FUNC(kSrc) \
FUNC(kDst) \
@@ -3829,18 +3816,6 @@ TEST_F(DisplayListCanvas, MatrixColorFilterOpacityCommuteCheck) {
FUNC(kColor) \
FUNC(kLuminosity)
// This function serves both to enhance error output below and to double
// check that the macro supplies all modes (otherwise it won't compile)
static std::string BlendModeToString(DlBlendMode mode) {
switch (mode) {
#define MODE_CASE(m) \
case DlBlendMode::m: \
return #m;
FOR_EACH_BLEND_MODE_ENUM(MODE_CASE)
#undef MODE_CASE
}
}
TEST_F(DisplayListCanvas, BlendColorFilterModifyTransparencyCheck) {
std::vector<std::unique_ptr<RenderEnvironment>> environments;
for (auto& provider : CanvasCompareTester::kTestProviders) {
@@ -3851,8 +3826,7 @@ TEST_F(DisplayListCanvas, BlendColorFilterModifyTransparencyCheck) {
auto test_mode_color = [&environments](DlBlendMode mode, DlColor color) {
std::stringstream desc_str;
std::string mode_string = BlendModeToString(mode);
desc_str << "blend[" << mode_string << ", " << color << "]";
desc_str << "blend[" << mode << ", " << color << "]";
std::string desc = desc_str.str();
DlBlendColorFilter filter(color, mode);
if (filter.modifies_transparent_black()) {
@@ -3913,8 +3887,7 @@ TEST_F(DisplayListCanvas, BlendColorFilterOpacityCommuteCheck) {
auto test_mode_color = [&environments](DlBlendMode mode, DlColor color) {
std::stringstream desc_str;
std::string mode_string = BlendModeToString(mode);
desc_str << "blend[" << mode_string << ", " << color << "]";
desc_str << "blend[" << mode << ", " << color << "]";
std::string desc = desc_str.str();
DlBlendColorFilter filter(color, mode);
if (filter.can_commute_with_opacity()) {
@@ -3973,359 +3946,7 @@ TEST_F(DisplayListCanvas, BlendColorFilterOpacityCommuteCheck) {
#undef TEST_MODE
}
class DisplayListNopTest : public DisplayListCanvas {
// The following code uses the acronym MTB for "modifies_transparent_black"
protected:
DisplayListNopTest() : DisplayListCanvas() {
test_src_colors = {
DlColor::kBlack().withAlpha(0), // transparent black
DlColor::kBlack().withAlpha(0x7f), // half transparent black
DlColor::kWhite().withAlpha(0x7f), // half transparent white
DlColor::kBlack(), // opaque black
DlColor::kWhite(), // opaque white
DlColor::kRed(), // opaque red
DlColor::kGreen(), // opaque green
DlColor::kBlue(), // opaque blue
DlColor::kDarkGrey(), // dark grey
DlColor::kLightGrey(), // light grey
};
// We test against a color cube of 3x3x3 colors [55,aa,ff]
// plus transparency as the first color/pixel
test_dst_colors.push_back(DlColor::kTransparent());
const int step = 0x55;
static_assert(step * 3 == 255);
for (int a = step; a < 256; a += step) {
for (int r = step; r < 256; r += step) {
for (int g = step; g < 256; g += step) {
for (int b = step; b < 256; b += step) {
test_dst_colors.push_back(DlColor(a << 24 | r << 16 | g << 8 | b));
}
}
}
}
static constexpr float color_filter_matrix_nomtb[] = {
0.0001, 0.0001, 0.0001, 0.9997, 0.0, //
0.0001, 0.0001, 0.0001, 0.9997, 0.0, //
0.0001, 0.0001, 0.0001, 0.9997, 0.0, //
0.0001, 0.0001, 0.0001, 0.9997, 0.0, //
};
static constexpr float color_filter_matrix_mtb[] = {
0.0001, 0.0001, 0.0001, 0.9997, 0.0, //
0.0001, 0.0001, 0.0001, 0.9997, 0.0, //
0.0001, 0.0001, 0.0001, 0.9997, 0.0, //
0.0001, 0.0001, 0.0001, 0.9997, 0.1, //
};
color_filter_nomtb = DlMatrixColorFilter::Make(color_filter_matrix_nomtb);
color_filter_mtb = DlMatrixColorFilter::Make(color_filter_matrix_mtb);
EXPECT_FALSE(color_filter_nomtb->modifies_transparent_black());
EXPECT_TRUE(color_filter_mtb->modifies_transparent_black());
test_data =
get_output(test_dst_colors.size(), 1, true, [this](SkCanvas* canvas) {
int x = 0;
for (DlColor color : test_dst_colors) {
SkPaint paint;
paint.setColor(color);
paint.setBlendMode(SkBlendMode::kSrc);
canvas->drawRect(SkRect::MakeXYWH(x, 0, 1, 1), paint);
x++;
}
});
// For image-on-image tests, the src and dest images will have repeated
// rows/columns that have every color, but laid out at right angles to
// each other so we see an interaction with every test color against
// every other test color.
int data_count = test_data->image()->width();
test_image_dst_data = get_output(
data_count, data_count, true, [this, data_count](SkCanvas* canvas) {
ASSERT_EQ(test_data->width(), data_count);
ASSERT_EQ(test_data->height(), 1);
for (int y = 0; y < data_count; y++) {
canvas->drawImage(test_data->image().get(), 0, y);
}
});
test_image_src_data = get_output(
data_count, data_count, true, [this, data_count](SkCanvas* canvas) {
ASSERT_EQ(test_data->width(), data_count);
ASSERT_EQ(test_data->height(), 1);
canvas->translate(data_count, 0);
canvas->rotate(90);
for (int y = 0; y < data_count; y++) {
canvas->drawImage(test_data->image().get(), 0, y);
}
});
// Double check that the pixel data is laid out in orthogonal stripes
for (int y = 0; y < data_count; y++) {
for (int x = 0; x < data_count; x++) {
EXPECT_EQ(*test_image_dst_data->addr32(x, y), *test_data->addr32(x, 0));
EXPECT_EQ(*test_image_src_data->addr32(x, y), *test_data->addr32(y, 0));
}
}
}
// These flags are 0 by default until they encounter a counter-example
// result and get set.
static constexpr int kWasNotNop = 0x1; // Some tested pixel was modified
static constexpr int kWasMTB = 0x2; // A transparent pixel was modified
std::vector<DlColor> test_src_colors;
std::vector<DlColor> test_dst_colors;
std::shared_ptr<DlColorFilter> color_filter_nomtb;
std::shared_ptr<DlColorFilter> color_filter_mtb;
// A 1-row image containing every color in test_dst_colors
std::unique_ptr<RenderResult> test_data;
// A square image containing test_data duplicated in each row
std::unique_ptr<RenderResult> test_image_dst_data;
// A square image containing test_data duplicated in each column
std::unique_ptr<RenderResult> test_image_src_data;
std::unique_ptr<RenderResult> get_output(
int w,
int h,
bool snapshot,
const std::function<void(SkCanvas*)>& renderer) {
auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(w, h));
SkCanvas* canvas = surface->getCanvas();
renderer(canvas);
canvas->flush();
surface->flushAndSubmit(true);
return std::make_unique<RenderResult>(surface, snapshot);
}
int check_color_result(DlColor dst_color,
DlColor result_color,
const sk_sp<DisplayList>& dl,
const std::string& desc) {
int ret = 0;
bool is_error = false;
if (dst_color.isTransparent() && !result_color.isTransparent()) {
ret |= kWasMTB;
is_error = !dl->modifies_transparent_black();
}
if (result_color != dst_color) {
ret |= kWasNotNop;
is_error = (dl->op_count() == 0u);
}
if (is_error) {
FML_LOG(ERROR) << std::hex << dst_color << " filters to " << result_color
<< desc;
}
return ret;
}
int check_image_result(const std::unique_ptr<RenderResult>& dst_data,
const std::unique_ptr<RenderResult>& result_data,
const sk_sp<DisplayList>& dl,
const std::string& desc) {
EXPECT_EQ(dst_data->width(), result_data->width());
EXPECT_EQ(dst_data->height(), result_data->height());
int all_flags = 0;
for (int y = 0; y < dst_data->height(); y++) {
const uint32_t* dst_pixels = dst_data->addr32(0, y);
const uint32_t* result_pixels = result_data->addr32(0, y);
for (int x = 0; x < dst_data->width(); x++) {
all_flags |=
check_color_result(dst_pixels[x], result_pixels[x], dl, desc);
}
}
return all_flags;
}
void report_results(int all_flags,
const sk_sp<DisplayList>& dl,
const std::string& desc) {
if (!dl->modifies_transparent_black()) {
EXPECT_TRUE((all_flags & kWasMTB) == 0);
} else if ((all_flags & kWasMTB) == 0) {
FML_LOG(INFO) << "combination does not affect transparency: " << desc;
}
if (dl->op_count() == 0u) {
EXPECT_TRUE((all_flags & kWasNotNop) == 0);
} else if ((all_flags & kWasNotNop) == 0) {
FML_LOG(INFO) << "combination could be classified as a nop: " << desc;
}
};
void test_mode_color_via_filter(DlBlendMode mode, DlColor color) {
std::stringstream desc_stream;
desc_stream << " using SkColorFilter::filterColor() with: ";
desc_stream << BlendModeToString(mode);
desc_stream << "/" << color;
std::string desc = desc_stream.str();
DisplayListBuilder builder({0.0f, 0.0f, 100.0f, 100.0f});
DlPaint paint = DlPaint(color).setBlendMode(mode);
builder.DrawRect({0.0f, 0.0f, 10.0f, 10.0f}, paint);
auto dl = builder.Build();
if (dl->modifies_transparent_black()) {
ASSERT_TRUE(dl->op_count() != 0u);
}
auto sk_mode = static_cast<SkBlendMode>(mode);
auto sk_color_filter = SkColorFilters::Blend(color, sk_mode);
int all_flags = 0;
if (sk_color_filter) {
for (DlColor dst_color : test_dst_colors) {
DlColor result = sk_color_filter->filterColor(dst_color);
all_flags |= check_color_result(dst_color, result, dl, desc);
}
if ((all_flags & kWasMTB) != 0) {
EXPECT_FALSE(sk_color_filter->isAlphaUnchanged());
}
}
report_results(all_flags, dl, desc);
};
void test_mode_color_via_rendering(DlBlendMode mode, DlColor color) {
std::stringstream desc_stream;
desc_stream << " rendering with: ";
desc_stream << BlendModeToString(mode);
desc_stream << "/" << color;
std::string desc = desc_stream.str();
auto test_image = test_data->image();
SkRect test_bounds =
SkRect::MakeWH(test_image->width(), test_image->height());
DisplayListBuilder builder(test_bounds);
DlPaint dl_paint = DlPaint(color).setBlendMode(mode);
builder.DrawRect(test_bounds, dl_paint);
auto dl = builder.Build();
bool dl_is_elided = dl->op_count() == 0u;
bool dl_affects_transparent_pixels = dl->modifies_transparent_black();
ASSERT_TRUE(!dl_is_elided || !dl_affects_transparent_pixels);
auto sk_mode = static_cast<SkBlendMode>(mode);
SkPaint sk_paint;
sk_paint.setBlendMode(sk_mode);
sk_paint.setColor(color);
for (auto& provider : CanvasCompareTester::kTestProviders) {
auto result_surface = provider->MakeOffscreenSurface(
test_image->width(), test_image->height(),
DlSurfaceProvider::kN32Premul_PixelFormat);
SkCanvas* result_canvas = result_surface->sk_surface()->getCanvas();
result_canvas->clear(SK_ColorTRANSPARENT);
result_canvas->drawImage(test_image.get(), 0, 0);
result_canvas->drawRect(test_bounds, sk_paint);
result_canvas->flush();
result_surface->sk_surface()->flushAndSubmit(true);
auto result_pixels =
std::make_unique<RenderResult>(result_surface->sk_surface());
int all_flags = check_image_result(test_data, result_pixels, dl, desc);
report_results(all_flags, dl, desc);
}
};
void test_attributes_image(DlBlendMode mode,
DlColor color,
DlColorFilter* color_filter,
DlImageFilter* image_filter) {
// if (true) { return; }
std::stringstream desc_stream;
desc_stream << " rendering with: ";
desc_stream << BlendModeToString(mode);
desc_stream << "/" << color;
std::string cf_mtb = color_filter
? color_filter->modifies_transparent_black()
? "modifies transparency"
: "preserves transparency"
: "no filter";
desc_stream << ", CF: " << cf_mtb;
std::string if_mtb = image_filter
? image_filter->modifies_transparent_black()
? "modifies transparency"
: "preserves transparency"
: "no filter";
desc_stream << ", IF: " << if_mtb;
std::string desc = desc_stream.str();
DisplayListBuilder builder({0.0f, 0.0f, 100.0f, 100.0f});
DlPaint paint = DlPaint(color) //
.setBlendMode(mode) //
.setColorFilter(color_filter) //
.setImageFilter(image_filter);
builder.DrawImage(DlImage::Make(test_image_src_data->image()), {0, 0},
DlImageSampling::kNearestNeighbor, &paint);
auto dl = builder.Build();
int w = test_image_src_data->width();
int h = test_image_src_data->height();
auto sk_mode = static_cast<SkBlendMode>(mode);
SkPaint sk_paint;
sk_paint.setBlendMode(sk_mode);
sk_paint.setColor(color);
sk_paint.setColorFilter(ToSk(color_filter));
sk_paint.setImageFilter(ToSk(image_filter));
for (auto& provider : CanvasCompareTester::kTestProviders) {
auto result_surface = provider->MakeOffscreenSurface(
w, h, DlSurfaceProvider::kN32Premul_PixelFormat);
SkCanvas* result_canvas = result_surface->sk_surface()->getCanvas();
result_canvas->clear(SK_ColorTRANSPARENT);
result_canvas->drawImage(test_image_dst_data->image(), 0, 0);
result_canvas->drawImage(test_image_src_data->image(), 0, 0,
SkSamplingOptions(), &sk_paint);
result_canvas->flush();
result_surface->sk_surface()->flushAndSubmit(true);
auto result_pixels =
std::make_unique<RenderResult>(result_surface->sk_surface());
int all_flags =
check_image_result(test_image_dst_data, result_pixels, dl, desc);
report_results(all_flags, dl, desc);
}
};
};
TEST_F(DisplayListNopTest, BlendModeAndColorViaColorFilter) {
auto test_mode_filter = [this](DlBlendMode mode) {
for (DlColor color : test_src_colors) {
test_mode_color_via_filter(mode, color);
}
};
#define TEST_MODE(V) test_mode_filter(DlBlendMode::V);
FOR_EACH_BLEND_MODE_ENUM(TEST_MODE)
#undef TEST_MODE
}
TEST_F(DisplayListNopTest, BlendModeAndColorByRendering) {
auto test_mode_render = [this](DlBlendMode mode) {
// First check rendering a variety of colors onto image
for (DlColor color : test_src_colors) {
test_mode_color_via_rendering(mode, color);
}
};
#define TEST_MODE(V) test_mode_render(DlBlendMode::V);
FOR_EACH_BLEND_MODE_ENUM(TEST_MODE)
#undef TEST_MODE
}
TEST_F(DisplayListNopTest, BlendModeAndColorAndFiltersByRendering) {
auto test_mode_render = [this](DlBlendMode mode) {
auto image_filter_nomtb = DlColorFilterImageFilter(color_filter_nomtb);
auto image_filter_mtb = DlColorFilterImageFilter(color_filter_mtb);
for (DlColor color : test_src_colors) {
test_attributes_image(mode, color, nullptr, nullptr);
test_attributes_image(mode, color, color_filter_nomtb.get(), nullptr);
test_attributes_image(mode, color, color_filter_mtb.get(), nullptr);
test_attributes_image(mode, color, nullptr, &image_filter_nomtb);
test_attributes_image(mode, color, nullptr, &image_filter_mtb);
}
};
#define TEST_MODE(V) test_mode_render(DlBlendMode::V);
FOR_EACH_BLEND_MODE_ENUM(TEST_MODE)
#undef TEST_MODE
}
#undef FOR_EACH_BLEND_MODE_ENUM
#undef FOR_EACH_ENUM
} // namespace testing
} // namespace flutter

View File

@@ -517,7 +517,7 @@ std::vector<DisplayListInvocationGroup> CreateAllRenderingOps() {
}},
{1, 16, 1, 24,
[](DlOpReceiver& r) {
r.drawColor(SK_ColorBLUE, DlBlendMode::kDstOut);
r.drawColor(SK_ColorBLUE, DlBlendMode::kDstIn);
}},
{1, 16, 1, 24,
[](DlOpReceiver& r) {

View File

@@ -151,9 +151,9 @@ static const DlBlurImageFilter kTestBlurImageFilter4(5.0,
static const DlDilateImageFilter kTestDilateImageFilter1(5.0, 5.0);
static const DlDilateImageFilter kTestDilateImageFilter2(6.0, 5.0);
static const DlDilateImageFilter kTestDilateImageFilter3(5.0, 6.0);
static const DlErodeImageFilter kTestErodeImageFilter1(4.0, 4.0);
static const DlErodeImageFilter kTestErodeImageFilter2(4.0, 3.0);
static const DlErodeImageFilter kTestErodeImageFilter3(3.0, 4.0);
static const DlErodeImageFilter kTestErodeImageFilter1(5.0, 5.0);
static const DlErodeImageFilter kTestErodeImageFilter2(6.0, 5.0);
static const DlErodeImageFilter kTestErodeImageFilter3(5.0, 6.0);
static const DlMatrixImageFilter kTestMatrixImageFilter1(
SkMatrix::RotateDeg(45),
kNearestSampling);

View File

@@ -40,7 +40,6 @@ class DisplayListMatrixClipTracker {
bool content_culled(const SkRect& content_bounds) const {
return current_->content_culled(content_bounds);
}
bool is_cull_rect_empty() const { return current_->is_cull_rect_empty(); }
void save();
void restore();
@@ -89,10 +88,9 @@ class DisplayListMatrixClipTracker {
virtual SkMatrix matrix_3x3() const = 0;
virtual SkM44 matrix_4x4() const = 0;
SkRect device_cull_rect() const { return cull_rect_; }
virtual SkRect device_cull_rect() const { return cull_rect_; }
virtual SkRect local_cull_rect() const = 0;
virtual bool content_culled(const SkRect& content_bounds) const;
bool is_cull_rect_empty() const { return cull_rect_.isEmpty(); }
virtual void translate(SkScalar tx, SkScalar ty) = 0;
virtual void scale(SkScalar sx, SkScalar sy) = 0;

View File

@@ -10,7 +10,7 @@ namespace testing {
TEST_F(DiffContextTest, ClipAlignment) {
MockLayerTree t1;
t1.root()->Add(CreateDisplayListLayer(
CreateDisplayList(SkRect::MakeLTRB(30, 30, 50, 50))));
CreateDisplayList(SkRect::MakeLTRB(30, 30, 50, 50), 1)));
auto damage = DiffLayerTree(t1, MockLayerTree(), SkIRect::MakeEmpty(), 0, 0);
EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(30, 30, 50, 50));
EXPECT_EQ(damage.buffer_damage, SkIRect::MakeLTRB(30, 30, 50, 50));

View File

@@ -564,9 +564,9 @@ using ContainerLayerDiffTest = DiffContextTest;
// Insert PictureLayer amongst container layers
TEST_F(ContainerLayerDiffTest, PictureLayerInsertion) {
auto pic1 = CreateDisplayList(SkRect::MakeLTRB(0, 0, 50, 50));
auto pic2 = CreateDisplayList(SkRect::MakeLTRB(100, 0, 150, 50));
auto pic3 = CreateDisplayList(SkRect::MakeLTRB(200, 0, 250, 50));
auto pic1 = CreateDisplayList(SkRect::MakeLTRB(0, 0, 50, 50), 1);
auto pic2 = CreateDisplayList(SkRect::MakeLTRB(100, 0, 150, 50), 1);
auto pic3 = CreateDisplayList(SkRect::MakeLTRB(200, 0, 250, 50), 1);
MockLayerTree t1;
@@ -616,9 +616,9 @@ TEST_F(ContainerLayerDiffTest, PictureLayerInsertion) {
// Insert picture layer amongst other picture layers
TEST_F(ContainerLayerDiffTest, PictureInsertion) {
auto pic1 = CreateDisplayList(SkRect::MakeLTRB(0, 0, 50, 50));
auto pic2 = CreateDisplayList(SkRect::MakeLTRB(100, 0, 150, 50));
auto pic3 = CreateDisplayList(SkRect::MakeLTRB(200, 0, 250, 50));
auto pic1 = CreateDisplayList(SkRect::MakeLTRB(0, 0, 50, 50), 1);
auto pic2 = CreateDisplayList(SkRect::MakeLTRB(100, 0, 150, 50), 1);
auto pic3 = CreateDisplayList(SkRect::MakeLTRB(200, 0, 250, 50), 1);
MockLayerTree t1;
t1.root()->Add(CreateDisplayListLayer(pic1));

View File

@@ -355,7 +355,7 @@ TEST_F(DisplayListLayerTest, RasterCachePreservesRTree) {
using DisplayListLayerDiffTest = DiffContextTest;
TEST_F(DisplayListLayerDiffTest, SimpleDisplayList) {
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60));
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
MockLayerTree tree1;
tree1.root()->Add(CreateDisplayListLayer(display_list));
@@ -375,7 +375,7 @@ TEST_F(DisplayListLayerDiffTest, SimpleDisplayList) {
}
TEST_F(DisplayListLayerDiffTest, FractionalTranslation) {
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60));
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
MockLayerTree tree1;
tree1.root()->Add(
@@ -388,7 +388,7 @@ TEST_F(DisplayListLayerDiffTest, FractionalTranslation) {
}
TEST_F(DisplayListLayerDiffTest, FractionalTranslationWithRasterCache) {
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60));
auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
MockLayerTree tree1;
tree1.root()->Add(
@@ -402,25 +402,21 @@ TEST_F(DisplayListLayerDiffTest, FractionalTranslationWithRasterCache) {
TEST_F(DisplayListLayerDiffTest, DisplayListCompare) {
MockLayerTree tree1;
auto display_list1 =
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), DlColor::kGreen());
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;
// same DL, same offset
auto display_list2 =
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), DlColor::kGreen());
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), DlColor::kGreen());
auto display_list3 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1);
// add offset
tree3.root()->Add(
CreateDisplayListLayer(display_list3, SkPoint::Make(10, 10)));
@@ -430,8 +426,7 @@ TEST_F(DisplayListLayerDiffTest, DisplayListCompare) {
MockLayerTree tree4;
// different color
auto display_list4 =
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), DlColor::kRed());
auto display_list4 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 2);
tree4.root()->Add(
CreateDisplayListLayer(display_list4, SkPoint::Make(10, 10)));

View File

@@ -659,7 +659,7 @@ using OpacityLayerDiffTest = DiffContextTest;
TEST_F(OpacityLayerDiffTest, FractionalTranslation) {
auto picture = CreateDisplayListLayer(
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60)));
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1));
auto layer = CreateOpacityLater({picture}, 128, SkPoint::Make(0.5, 0.5));
MockLayerTree tree1;
@@ -672,7 +672,7 @@ TEST_F(OpacityLayerDiffTest, FractionalTranslation) {
TEST_F(OpacityLayerDiffTest, FractionalTranslationWithRasterCache) {
auto picture = CreateDisplayListLayer(
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60)));
CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1));
auto layer = CreateOpacityLater({picture}, 128, SkPoint::Make(0.5, 0.5));
MockLayerTree tree1;

View File

@@ -30,7 +30,7 @@ Damage DiffContextTest::DiffLayerTree(MockLayerTree& layer_tree,
}
sk_sp<DisplayList> DiffContextTest::CreateDisplayList(const SkRect& bounds,
DlColor color) {
SkColor color) {
DisplayListBuilder builder;
builder.DrawRect(bounds, DlPaint().setColor(color));
return builder.Build();

View File

@@ -45,8 +45,7 @@ class DiffContextTest : public LayerTest {
// 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,
DlColor color = DlColor::kBlack());
sk_sp<DisplayList> CreateDisplayList(const SkRect& bounds, uint32_t color);
std::shared_ptr<DisplayListLayer> CreateDisplayListLayer(
const sk_sp<DisplayList>& display_list,

View File

@@ -856,12 +856,18 @@ TEST_P(DisplayListTest, CanDrawShadow) {
}
TEST_P(DisplayListTest, TransparentShadowProducesCorrectColor) {
flutter::DisplayListBuilder builder;
{
builder.Save();
builder.Scale(1.618, 1.618);
builder.DrawShadow(SkPath{}.addRect(SkRect::MakeXYWH(0, 0, 200, 100)),
SK_ColorTRANSPARENT, 15, false, 1);
builder.Restore();
}
auto dl = builder.Build();
DlDispatcher dispatcher;
dispatcher.save();
dispatcher.scale(1.618, 1.618);
dispatcher.drawShadow(SkPath{}.addRect(SkRect::MakeXYWH(0, 0, 200, 100)),
SK_ColorTRANSPARENT, 15, false, 1);
dispatcher.restore();
dispatcher.drawDisplayList(dl, 1);
auto picture = dispatcher.EndRecordingAsPicture();
std::shared_ptr<SolidRRectBlurContents> rrect_blur;

View File

@@ -7,40 +7,15 @@
#include "flutter/shell/common/dl_op_spy.h"
#include "flutter/testing/testing.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkRSXform.h"
namespace flutter {
namespace testing {
// The following macros demonstrate that the DlOpSpy class is equivalent
// to DisplayList::affects_transparent_surface() now that DisplayListBuilder
// implements operation culling.
// See https://github.com/flutter/flutter/issues/125403
#define ASSERT_DID_DRAW(spy, dl) \
do { \
ASSERT_TRUE(spy.did_draw()); \
ASSERT_TRUE(dl->modifies_transparent_black()); \
} while (0)
#define ASSERT_NO_DRAW(spy, dl) \
do { \
ASSERT_FALSE(spy.did_draw()); \
ASSERT_FALSE(dl->modifies_transparent_black()); \
} while (0)
TEST(DlOpSpy, DidDrawIsFalseByDefault) {
DlOpSpy dl_op_spy;
ASSERT_FALSE(dl_op_spy.did_draw());
}
TEST(DlOpSpy, EmptyDisplayList) {
DisplayListBuilder builder;
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
}
TEST(DlOpSpy, SetColor) {
{ // No Color set.
DisplayListBuilder builder;
@@ -49,7 +24,7 @@ TEST(DlOpSpy, SetColor) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // Set transparent color.
DisplayListBuilder builder;
@@ -58,7 +33,7 @@ TEST(DlOpSpy, SetColor) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // Set black color.
DisplayListBuilder builder;
@@ -67,7 +42,7 @@ TEST(DlOpSpy, SetColor) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
@@ -80,7 +55,7 @@ TEST(DlOpSpy, SetColorSource) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // Set transparent color.
DisplayListBuilder builder;
@@ -92,7 +67,7 @@ TEST(DlOpSpy, SetColorSource) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // Set black color.
DisplayListBuilder builder;
@@ -104,7 +79,7 @@ TEST(DlOpSpy, SetColorSource) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
@@ -116,25 +91,16 @@ TEST(DlOpSpy, DrawColor) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // Transparent color with kSrc.
{ // Transparent color source.
DisplayListBuilder builder;
auto color = DlColor::kTransparent();
builder.DrawColor(color, DlBlendMode::kSrc);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
}
{ // Transparent color with kSrcOver.
DisplayListBuilder builder;
auto color = DlColor::kTransparent();
builder.DrawColor(color, DlBlendMode::kSrcOver);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
@@ -146,7 +112,7 @@ TEST(DlOpSpy, DrawPaint) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // black color in paint.
DisplayListBuilder builder;
@@ -155,7 +121,7 @@ TEST(DlOpSpy, DrawPaint) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
@@ -167,7 +133,7 @@ TEST(DlOpSpy, DrawLine) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
@@ -176,7 +142,7 @@ TEST(DlOpSpy, DrawLine) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
@@ -188,7 +154,7 @@ TEST(DlOpSpy, DrawRect) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
@@ -197,11 +163,11 @@ TEST(DlOpSpy, DrawRect) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawOval) {
TEST(DlOpSpy, drawOval) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
@@ -209,7 +175,7 @@ TEST(DlOpSpy, DrawOval) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
@@ -218,11 +184,11 @@ TEST(DlOpSpy, DrawOval) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawCircle) {
TEST(DlOpSpy, drawCircle) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
@@ -230,7 +196,7 @@ TEST(DlOpSpy, DrawCircle) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
@@ -239,11 +205,11 @@ TEST(DlOpSpy, DrawCircle) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawRRect) {
TEST(DlOpSpy, drawRRect) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
@@ -251,7 +217,7 @@ TEST(DlOpSpy, DrawRRect) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
@@ -260,49 +226,34 @@ TEST(DlOpSpy, DrawRRect) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawPath) {
{ // black line
TEST(DlOpSpy, drawPath) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
paint.setDrawStyle(DlDrawStyle::kStroke);
builder.DrawPath(SkPath::Line(SkPoint::Make(0, 1), SkPoint::Make(1, 1)),
paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // triangle
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
SkPath path;
path.moveTo({0, 0});
path.lineTo({1, 0});
path.lineTo({0, 1});
builder.DrawPath(path, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
}
{ // transparent line
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
paint.setDrawStyle(DlDrawStyle::kStroke);
builder.DrawPath(SkPath::Line(SkPoint::Make(0, 1), SkPoint::Make(1, 1)),
paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawArc) {
TEST(DlOpSpy, drawArc) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
@@ -310,7 +261,7 @@ TEST(DlOpSpy, DrawArc) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
@@ -319,11 +270,11 @@ TEST(DlOpSpy, DrawArc) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawPoints) {
TEST(DlOpSpy, drawPoints) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
@@ -332,7 +283,7 @@ TEST(DlOpSpy, DrawPoints) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
@@ -342,62 +293,38 @@ TEST(DlOpSpy, DrawPoints) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawVertices) {
TEST(DlOpSpy, drawVertices) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
const SkPoint vertices[] = {
SkPoint::Make(5, 5),
SkPoint::Make(5, 15),
SkPoint::Make(15, 5),
};
const SkPoint texture_coordinates[] = {
SkPoint::Make(5, 5),
SkPoint::Make(15, 5),
SkPoint::Make(5, 15),
};
const DlColor colors[] = {
DlColor::kBlack(),
DlColor::kRed(),
DlColor::kGreen(),
};
auto dl_vertices = DlVertices::Make(DlVertexMode::kTriangles, 3, vertices,
const SkPoint vertices[] = {SkPoint::Make(5, 5)};
const SkPoint texture_coordinates[] = {SkPoint::Make(5, 5)};
const DlColor colors[] = {DlColor::kBlack()};
auto dl_vertices = DlVertices::Make(DlVertexMode::kTriangles, 1, vertices,
texture_coordinates, colors, 0);
builder.DrawVertices(dl_vertices.get(), DlBlendMode::kSrc, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
const SkPoint vertices[] = {
SkPoint::Make(5, 5),
SkPoint::Make(5, 15),
SkPoint::Make(15, 5),
};
const SkPoint texture_coordinates[] = {
SkPoint::Make(5, 5),
SkPoint::Make(15, 5),
SkPoint::Make(5, 15),
};
const DlColor colors[] = {
DlColor::kBlack(),
DlColor::kRed(),
DlColor::kGreen(),
};
auto dl_vertices = DlVertices::Make(DlVertexMode::kTriangles, 3, vertices,
const SkPoint vertices[] = {SkPoint::Make(5, 5)};
const SkPoint texture_coordinates[] = {SkPoint::Make(5, 5)};
const DlColor colors[] = {DlColor::kBlack()};
auto dl_vertices = DlVertices::Make(DlVertexMode::kTriangles, 1, vertices,
texture_coordinates, colors, 0);
builder.DrawVertices(dl_vertices.get(), DlBlendMode::kSrc, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
@@ -416,7 +343,7 @@ TEST(DlOpSpy, Images) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // DrawImageRect
DisplayListBuilder builder;
@@ -432,7 +359,7 @@ TEST(DlOpSpy, Images) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // DrawImageNine
DisplayListBuilder builder;
@@ -448,7 +375,7 @@ TEST(DlOpSpy, Images) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // DrawAtlas
DisplayListBuilder builder;
@@ -459,19 +386,20 @@ TEST(DlOpSpy, Images) {
SkBitmap bitmap;
bitmap.allocPixels(info, 0);
auto sk_image = SkImages::RasterFromBitmap(bitmap);
const SkRSXform xform[] = {SkRSXform::Make(1, 0, 0, 0)};
const SkRect tex[] = {SkRect::MakeXYWH(10, 10, 10, 10)};
const SkRSXform xform[] = {};
const SkRect tex[] = {};
const DlColor colors[] = {};
SkRect cull_rect = SkRect::MakeWH(5, 5);
builder.DrawAtlas(DlImage::Make(sk_image), xform, tex, nullptr, 1,
builder.DrawAtlas(DlImage::Make(sk_image), xform, tex, colors, 0,
DlBlendMode::kSrc, DlImageSampling::kLinear, &cull_rect);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawDisplayList) {
TEST(DlOpSpy, drawDisplayList) {
{ // Recursive Transparent DisplayList
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
@@ -486,7 +414,7 @@ TEST(DlOpSpy, DrawDisplayList) {
DlOpSpy dl_op_spy;
dl2->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl2);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // Sub non-transparent DisplayList,
DisplayListBuilder builder;
@@ -502,7 +430,7 @@ TEST(DlOpSpy, DrawDisplayList) {
DlOpSpy dl_op_spy;
dl2->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl2);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // Sub non-transparent DisplayList, 0 opacity
@@ -519,7 +447,7 @@ TEST(DlOpSpy, DrawDisplayList) {
DlOpSpy dl_op_spy;
dl2->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl2);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // Parent non-transparent DisplayList
@@ -536,11 +464,11 @@ TEST(DlOpSpy, DrawDisplayList) {
DlOpSpy dl_op_spy;
dl2->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl2);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawTextBlob) {
TEST(DlOpSpy, drawTextBlob) {
{ // Non-transparent color.
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
@@ -551,7 +479,7 @@ TEST(DlOpSpy, DrawTextBlob) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent color.
DisplayListBuilder builder;
@@ -563,11 +491,11 @@ TEST(DlOpSpy, DrawTextBlob) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawShadow) {
TEST(DlOpSpy, drawShadow) {
{ // valid shadow
DisplayListBuilder builder;
DlPaint paint;
@@ -577,7 +505,7 @@ TEST(DlOpSpy, DrawShadow) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_DID_DRAW(dl_op_spy, dl);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent color
DisplayListBuilder builder;
@@ -588,7 +516,7 @@ TEST(DlOpSpy, DrawShadow) {
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_NO_DRAW(dl_op_spy, dl);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}