Use DisplayListMatrixClipTracker in DisplayListBuilder (flutter/engine#38349)

* Use DisplayListMatrixClipTracker in DisplayListBuilder

* Ignore is_aa

* Revert "Ignore is_aa"

This reverts commit b201dadc773f8e726ec68ed88114df9be7b5a9b0.

* Tweak code

* Use content_culled

* getLocalClipBounds without device clip bounds roundsOut

* Tweak code and add more tests

* remove virtual
This commit is contained in:
ColdPaleLight
2022-12-20 18:05:40 +08:00
committed by GitHub
parent 013067e6e5
commit f2007cfd2b
7 changed files with 526 additions and 163 deletions

View File

@@ -67,16 +67,15 @@ sk_sp<DisplayList> DisplayListBuilder::Build() {
}
DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect,
bool prepare_rtree) {
bool prepare_rtree)
: tracker_(cull_rect, SkMatrix::I()) {
if (prepare_rtree) {
accumulator_ = std::make_unique<RTreeBoundsAccumulator>();
} else {
accumulator_ = std::make_unique<RectBoundsAccumulator>();
}
// isEmpty protects us against NaN as we normalize any empty cull rects
SkRect cull = cull_rect.isEmpty() ? SkRect::MakeEmpty() : cull_rect;
layer_stack_.emplace_back(SkM44(), SkMatrix::I(), cull);
layer_stack_.emplace_back();
current_layer_ = &layer_stack_.back();
}
@@ -440,9 +439,10 @@ void DisplayListBuilder::checkForDeferredSave() {
}
void DisplayListBuilder::save() {
layer_stack_.emplace_back(current_layer_);
layer_stack_.emplace_back();
current_layer_ = &layer_stack_.back();
current_layer_->has_deferred_save_op_ = true;
tracker_.save();
accumulator()->save();
}
@@ -455,6 +455,7 @@ void DisplayListBuilder::restore() {
// on the stack.
LayerInfo layer_info = layer_stack_.back();
tracker_.restore();
layer_stack_.pop_back();
current_layer_ = &layer_stack_.back();
bool is_unbounded = layer_info.is_unbounded();
@@ -463,7 +464,7 @@ void DisplayListBuilder::restore() {
// current accumulator and adjust it as required based on the filter.
std::shared_ptr<const DlImageFilter> filter = layer_info.filter();
if (filter) {
const SkRect* clip = &current_layer_->clip_bounds();
const SkRect clip = tracker_.device_cull_rect();
if (!accumulator()->restore(
[filter = filter, matrix = getTransform()](const SkRect& input,
SkRect& output) {
@@ -473,7 +474,7 @@ void DisplayListBuilder::restore() {
output.set(output_bounds);
return ret;
},
clip)) {
&clip)) {
is_unbounded = true;
}
} else {
@@ -544,11 +545,12 @@ void DisplayListBuilder::saveLayer(const SkRect* bounds,
// We will fill the clip of the outer layer when we restore
AccumulateUnbounded();
}
layer_stack_.emplace_back(current_layer_, save_layer_offset, true,
layer_stack_.emplace_back(save_layer_offset, true,
current_.getImageFilter());
} else {
layer_stack_.emplace_back(current_layer_, save_layer_offset, true, nullptr);
layer_stack_.emplace_back(save_layer_offset, true, nullptr);
}
tracker_.save();
accumulator()->save();
current_layer_ = &layer_stack_.back();
if (options.renders_with_attributes()) {
@@ -566,7 +568,7 @@ void DisplayListBuilder::saveLayer(const SkRect* bounds,
// use them as the temporary layer bounds during rendering the layer, so
// we set them as if a clip operation were performed.
if (bounds) {
intersect(*bounds);
tracker_.clipRect(*bounds, SkClipOp::kIntersect, false);
}
if (backdrop) {
// A backdrop will affect up to the entire surface, bounded by the clip
@@ -590,8 +592,7 @@ void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) {
(tx != 0.0 || ty != 0.0)) {
checkForDeferredSave();
Push<TranslateOp>(0, 1, tx, ty);
current_layer_->matrix().preTranslate(tx, ty);
current_layer_->update_matrix33();
tracker_.translate(tx, ty);
}
}
void DisplayListBuilder::scale(SkScalar sx, SkScalar sy) {
@@ -599,16 +600,14 @@ void DisplayListBuilder::scale(SkScalar sx, SkScalar sy) {
(sx != 1.0 || sy != 1.0)) {
checkForDeferredSave();
Push<ScaleOp>(0, 1, sx, sy);
current_layer_->matrix().preScale(sx, sy);
current_layer_->update_matrix33();
tracker_.scale(sx, sy);
}
}
void DisplayListBuilder::rotate(SkScalar degrees) {
if (SkScalarMod(degrees, 360.0) != 0.0) {
checkForDeferredSave();
Push<RotateOp>(0, 1, degrees);
current_layer_->matrix().preConcat(SkMatrix::RotateDeg(degrees));
current_layer_->update_matrix33();
tracker_.rotate(degrees);
}
}
void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) {
@@ -616,8 +615,7 @@ void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) {
(sx != 0.0 || sy != 0.0)) {
checkForDeferredSave();
Push<SkewOp>(0, 1, sx, sy);
current_layer_->matrix().preConcat(SkMatrix::Skew(sx, sy));
current_layer_->update_matrix33();
tracker_.skew(sx, sy);
}
}
@@ -636,11 +634,8 @@ void DisplayListBuilder::transform2DAffine(
Push<Transform2DAffineOp>(0, 1,
mxx, mxy, mxt,
myx, myy, myt);
current_layer_->matrix().preConcat(SkM44(mxx, mxy, 0, mxt,
myx, myy, 0, myt,
0, 0, 1, 0,
0, 0, 0, 1));
current_layer_->update_matrix33();
tracker_.transform2DAffine(mxx, mxy, mxt,
myx, myy, myt);
}
}
// full 4x4 transform in row major order
@@ -665,19 +660,17 @@ void DisplayListBuilder::transformFullPerspective(
myx, myy, myz, myt,
mzx, mzy, mzz, mzt,
mwx, mwy, mwz, mwt);
current_layer_->matrix().preConcat(SkM44(mxx, mxy, mxz, mxt,
myx, myy, myz, myt,
mzx, mzy, mzz, mzt,
mwx, mwy, mwz, mwt));
current_layer_->update_matrix33();
tracker_.transformFullPerspective(mxx, mxy, mxz, mxt,
myx, myy, myz, myt,
mzx, mzy, mzz, mzt,
mwx, mwy, mwz, mwt);
}
}
// clang-format on
void DisplayListBuilder::transformReset() {
checkForDeferredSave();
Push<TransformResetOp>(0, 0);
current_layer_->matrix().setIdentity();
current_layer_->update_matrix33();
tracker_.setIdentity();
}
void DisplayListBuilder::transform(const SkMatrix* matrix) {
if (matrix != nullptr) {
@@ -704,12 +697,12 @@ void DisplayListBuilder::clipRect(const SkRect& rect,
switch (clip_op) {
case SkClipOp::kIntersect:
Push<ClipIntersectRectOp>(0, 1, rect, is_aa);
intersect(rect);
break;
case SkClipOp::kDifference:
Push<ClipDifferenceRectOp>(0, 1, rect, is_aa);
break;
}
tracker_.clipRect(rect, clip_op, is_aa);
}
void DisplayListBuilder::clipRRect(const SkRRect& rrect,
SkClipOp clip_op,
@@ -721,12 +714,12 @@ void DisplayListBuilder::clipRRect(const SkRRect& rrect,
switch (clip_op) {
case SkClipOp::kIntersect:
Push<ClipIntersectRRectOp>(0, 1, rrect, is_aa);
intersect(rrect.getBounds());
break;
case SkClipOp::kDifference:
Push<ClipDifferenceRRectOp>(0, 1, rrect, is_aa);
break;
}
tracker_.clipRRect(rrect, clip_op, is_aa);
}
}
void DisplayListBuilder::clipPath(const SkPath& path,
@@ -753,48 +746,16 @@ void DisplayListBuilder::clipPath(const SkPath& path,
switch (clip_op) {
case SkClipOp::kIntersect:
Push<ClipIntersectPathOp>(0, 1, path, is_aa);
if (!path.isInverseFillType()) {
intersect(path.getBounds());
}
break;
case SkClipOp::kDifference:
Push<ClipDifferencePathOp>(0, 1, path, is_aa);
// Map "kDifference of inverse path" to "kIntersect of the original path".
if (path.isInverseFillType()) {
intersect(path.getBounds());
}
break;
}
}
void DisplayListBuilder::intersect(const SkRect& rect) {
SkRect dev_clip_bounds = getTransform().mapRect(rect);
if (!current_layer_->clip_bounds().intersect(dev_clip_bounds)) {
current_layer_->clip_bounds().setEmpty();
}
}
SkRect DisplayListBuilder::getLocalClipBounds() {
SkM44 inverse;
if (current_layer_->matrix().invert(&inverse)) {
SkRect dev_bounds;
current_layer_->clip_bounds().roundOut(&dev_bounds);
return inverse.asM33().mapRect(dev_bounds);
}
return kMaxCullRect;
tracker_.clipPath(path, clip_op, is_aa);
}
bool DisplayListBuilder::quickReject(const SkRect& bounds) const {
if (bounds.isEmpty()) {
return true;
}
SkMatrix matrix = getTransform();
// We don't need the inverse, but this method tells us if the matrix
// is singular in which case we can reject all rendering.
if (!matrix.invert(nullptr)) {
return true;
}
SkRect dev_bounds;
matrix.mapRect(bounds).roundOut(&dev_bounds);
return !current_layer_->clip_bounds().intersects(dev_bounds);
return tracker_.content_culled(bounds);
}
void DisplayListBuilder::drawPaint() {
@@ -1357,7 +1318,7 @@ bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds,
}
void DisplayListBuilder::AccumulateUnbounded() {
accumulator()->accumulate(current_layer_->clip_bounds());
accumulator()->accumulate(tracker_.device_cull_rect());
}
void DisplayListBuilder::AccumulateOpBounds(SkRect& bounds,
@@ -1369,8 +1330,8 @@ void DisplayListBuilder::AccumulateOpBounds(SkRect& bounds,
}
}
void DisplayListBuilder::AccumulateBounds(SkRect& bounds) {
getTransform().mapRect(&bounds);
if (bounds.intersect(current_layer_->clip_bounds())) {
tracker_.mapRect(&bounds);
if (bounds.intersect(tracker_.device_cull_rect())) {
accumulator()->accumulate(bounds);
}
}

View File

@@ -11,6 +11,7 @@
#include "flutter/display_list/display_list_dispatcher.h"
#include "flutter/display_list/display_list_flags.h"
#include "flutter/display_list/display_list_image.h"
#include "flutter/display_list/display_list_matrix_clip_tracker.h"
#include "flutter/display_list/display_list_paint.h"
#include "flutter/display_list/display_list_path_effect.h"
#include "flutter/display_list/display_list_sampling_options.h"
@@ -210,11 +211,11 @@ class DisplayListBuilder final : public virtual Dispatcher,
/// Returns the 4x4 full perspective transform representing all transform
/// operations executed so far in this DisplayList within the enclosing
/// save stack.
SkM44 getTransformFullPerspective() const { return current_layer_->matrix(); }
SkM44 getTransformFullPerspective() const { return tracker_.matrix_4x4(); }
/// Returns the 3x3 partial perspective transform representing all transform
/// operations executed so far in this DisplayList within the enclosing
/// save stack.
SkMatrix getTransform() const { return current_layer_->matrix33(); }
SkMatrix getTransform() const { return tracker_.matrix_3x3(); }
void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override;
void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override;
@@ -223,11 +224,11 @@ class DisplayListBuilder final : public virtual Dispatcher,
/// Conservative estimate of the bounds of all outstanding clip operations
/// measured in the coordinate space within which this DisplayList will
/// be rendered.
SkRect getDestinationClipBounds() { return current_layer_->clip_bounds(); }
SkRect getDestinationClipBounds() { return tracker_.device_cull_rect(); }
/// Conservative estimate of the bounds of all outstanding clip operations
/// transformed into the local coordinate space in which currently
/// recorded rendering operations are interpreted.
SkRect getLocalClipBounds();
SkRect getLocalClipBounds() { return tracker_.local_cull_rect(); }
/// Return true iff the supplied bounds are easily shown to be outside
/// of the current clip bounds. This method may conservatively return
@@ -386,33 +387,16 @@ class DisplayListBuilder final : public virtual Dispatcher,
class LayerInfo {
public:
explicit LayerInfo(const SkM44& matrix,
const SkMatrix& matrix33,
const SkRect& clip_bounds,
size_t save_layer_offset = 0,
explicit LayerInfo(size_t save_layer_offset = 0,
bool has_layer = false,
std::shared_ptr<const DlImageFilter> filter = nullptr)
: save_layer_offset_(save_layer_offset),
has_layer_(has_layer),
cannot_inherit_opacity_(false),
has_compatible_op_(false),
matrix_(matrix),
matrix33_(matrix33),
clip_bounds_(clip_bounds),
filter_(filter),
is_unbounded_(false) {}
explicit LayerInfo(const LayerInfo* current_layer,
size_t save_layer_offset = 0,
bool has_layer = false,
std::shared_ptr<const DlImageFilter> filter = nullptr)
: LayerInfo(current_layer->matrix_,
current_layer->matrix33_,
current_layer->clip_bounds_,
save_layer_offset,
has_layer,
filter) {}
// The offset into the memory buffer where the saveLayer DLOp record
// for this saveLayer() call is placed. This may be needed if the
// eventual restore() call has discovered important information about
@@ -424,11 +408,6 @@ class DisplayListBuilder final : public virtual Dispatcher,
bool has_layer() const { return has_layer_; }
bool cannot_inherit_opacity() const { return cannot_inherit_opacity_; }
bool has_compatible_op() const { return cannot_inherit_opacity_; }
SkM44& matrix() { return matrix_; }
SkMatrix& matrix33() { return matrix33_; }
SkRect& clip_bounds() { return clip_bounds_; }
void update_matrix33() { matrix33_ = matrix_.asM33(); }
bool is_group_opacity_compatible() const {
return !cannot_inherit_opacity_;
@@ -486,9 +465,6 @@ class DisplayListBuilder final : public virtual Dispatcher,
bool has_layer_;
bool cannot_inherit_opacity_;
bool has_compatible_op_;
SkM44 matrix_;
SkMatrix matrix33_;
SkRect clip_bounds_;
std::shared_ptr<const DlImageFilter> filter_;
bool is_unbounded_;
bool has_deferred_save_op_ = false;
@@ -498,6 +474,7 @@ class DisplayListBuilder final : public virtual Dispatcher,
std::vector<LayerInfo> layer_stack_;
LayerInfo* current_layer_;
DisplayListMatrixClipTracker tracker_;
std::unique_ptr<BoundsAccumulator> accumulator_;
BoundsAccumulator* accumulator() { return accumulator_.get(); }

View File

@@ -39,12 +39,13 @@ class Data4x4 : public DisplayListMatrixClipTracker::Data {
void setTransform(const SkMatrix& matrix) override { m44_ = SkM44(matrix); }
void setTransform(const SkM44& m44) override { m44_ = m44; }
void setIdentity() override { m44_.setIdentity(); }
bool mapRect(const SkRect& rect, SkRect* mapped) const override {
return m44_.asM33().mapRect(mapped, rect);
}
bool canBeInverted() const override { return m44_.asM33().invert(nullptr); }
protected:
bool has_perspective() const override;
bool map_rect(const SkRect& rect, SkRect* mapped) const override {
return m44_.asM33().mapRect(mapped, rect);
}
private:
SkM44 m44_;
@@ -80,12 +81,13 @@ class Data3x3 : public DisplayListMatrixClipTracker::Data {
FML_CHECK(false) << "SkM44 was set without upgrading Data";
}
void setIdentity() override { matrix_.setIdentity(); }
bool mapRect(const SkRect& rect, SkRect* mapped) const override {
return matrix_.mapRect(mapped, rect);
}
bool canBeInverted() const override { return matrix_.invert(nullptr); }
protected:
bool has_perspective() const override { return matrix_.hasPerspective(); }
bool map_rect(const SkRect& rect, SkRect* mapped) const override {
return matrix_.mapRect(mapped, rect);
}
private:
SkMatrix matrix_;
@@ -103,21 +105,64 @@ static bool is_3x3(const SkM44& m) {
DisplayListMatrixClipTracker::DisplayListMatrixClipTracker(
const SkRect& cull_rect,
const SkMatrix& matrix) {
saved_.emplace_back(std::make_unique<Data3x3>(matrix, cull_rect));
// isEmpty protects us against NaN as we normalize any empty cull rects
SkRect cull = cull_rect.isEmpty() ? SkRect::MakeEmpty() : cull_rect;
saved_.emplace_back(std::make_unique<Data3x3>(matrix, cull));
current_ = saved_.back().get();
}
DisplayListMatrixClipTracker::DisplayListMatrixClipTracker(
const SkRect& cull_rect,
const SkM44& m44) {
// isEmpty protects us against NaN as we normalize any empty cull rects
SkRect cull = cull_rect.isEmpty() ? SkRect::MakeEmpty() : cull_rect;
if (is_3x3(m44)) {
saved_.emplace_back(std::make_unique<Data3x3>(m44.asM33(), cull_rect));
saved_.emplace_back(std::make_unique<Data3x3>(m44.asM33(), cull));
} else {
saved_.emplace_back(std::make_unique<Data4x4>(m44, cull_rect));
saved_.emplace_back(std::make_unique<Data4x4>(m44, cull));
}
current_ = saved_.back().get();
}
// clang-format off
void DisplayListMatrixClipTracker::transform2DAffine(
SkScalar mxx, SkScalar mxy, SkScalar mxt,
SkScalar myx, SkScalar myy, SkScalar myt) {
if (!current_->is_4x4()) {
transform(SkMatrix::MakeAll(mxx, mxy, mxt,
myx, myy, myt,
0, 0, 1));
} else {
transform(SkM44(mxx, mxy, 0, mxt,
myx, myy, 0, myt,
0, 0, 1, 0,
0, 0, 0, 1));
}
}
void DisplayListMatrixClipTracker::transformFullPerspective(
SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) {
if (!current_->is_4x4()) {
if ( mxz == 0 &&
myz == 0 &&
mzx == 0 && mzy == 0 && mzz == 1 && mzt == 0 &&
mwz == 0) {
transform(SkMatrix::MakeAll(mxx, mxy, mxt,
myx, myy, myt,
mwx, mwy, mwt));
return;
}
}
transform(SkM44(mxx, mxy, mxz, mxt,
myx, myy, myz, myt,
mzx, mzy, mzz, mzt,
mwx, mwy, mwz, mwt));
}
// clang-format on
void DisplayListMatrixClipTracker::save() {
if (current_->is_4x4()) {
saved_.emplace_back(std::make_unique<Data4x4>(current_));
@@ -183,6 +228,19 @@ void DisplayListMatrixClipTracker::clipRRect(const SkRRect& rrect,
void DisplayListMatrixClipTracker::clipPath(const SkPath& path,
SkClipOp op,
bool is_aa) {
// Map "kDifference of inverse path" to "kIntersect of the original path" and
// map "kIntersect of inverse path" to "kDifference of the original path"
if (path.isInverseFillType()) {
switch (op) {
case SkClipOp::kIntersect:
op = SkClipOp::kDifference;
break;
case SkClipOp::kDifference:
op = SkClipOp::kIntersect;
break;
}
}
SkRect bounds;
switch (op) {
case SkClipOp::kIntersect:
@@ -202,11 +260,14 @@ bool DisplayListMatrixClipTracker::Data::content_culled(
if (cull_rect_.isEmpty() || content_bounds.isEmpty()) {
return true;
}
if (!canBeInverted()) {
return true;
}
if (has_perspective()) {
return false;
}
SkRect mapped;
map_rect(content_bounds, &mapped);
mapRect(content_bounds, &mapped);
return !mapped.intersects(cull_rect_);
}
@@ -228,7 +289,7 @@ void DisplayListMatrixClipTracker::Data::clipBounds(const SkRect& clip,
break;
}
SkRect rect;
map_rect(clip, &rect);
mapRect(clip, &rect);
if (is_aa) {
rect.roundOut(&rect);
}
@@ -242,7 +303,7 @@ void DisplayListMatrixClipTracker::Data::clipBounds(const SkRect& clip,
break;
}
SkRect rect;
if (map_rect(clip, &rect)) {
if (mapRect(clip, &rect)) {
// This technique only works if it is rect -> rect
if (is_aa) {
SkIRect rounded;

View File

@@ -44,9 +44,20 @@ class DisplayListMatrixClipTracker {
void rotate(SkScalar degrees) { current_->rotate(degrees); }
void transform(const SkM44& m44);
void transform(const SkMatrix& matrix) { current_->transform(matrix); }
// clang-format off
void transform2DAffine(
SkScalar mxx, SkScalar mxy, SkScalar mxt,
SkScalar myx, SkScalar myy, SkScalar myt);
void transformFullPerspective(
SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt,
SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt,
SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt,
SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt);
// clang-format on
void setTransform(const SkMatrix& matrix) { current_->setTransform(matrix); }
void setTransform(const SkM44& m44);
void setIdentity() { current_->setIdentity(); }
bool mapRect(SkRect* rect) const { return current_->mapRect(*rect, rect); }
void clipRect(const SkRect& rect, SkClipOp op, bool is_aa) {
current_->clipBounds(rect, op, is_aa);
@@ -77,6 +88,8 @@ class DisplayListMatrixClipTracker {
virtual void setTransform(const SkMatrix& matrix) = 0;
virtual void setTransform(const SkM44& m44) = 0;
virtual void setIdentity() = 0;
virtual bool mapRect(const SkRect& rect, SkRect* mapped) const = 0;
virtual bool canBeInverted() const = 0;
virtual void clipBounds(const SkRect& clip, SkClipOp op, bool is_aa);
@@ -84,7 +97,6 @@ class DisplayListMatrixClipTracker {
Data(const SkRect& rect) : cull_rect_(rect) {}
virtual bool has_perspective() const = 0;
virtual bool map_rect(const SkRect& rect, SkRect* mapped) const = 0;
SkRect cull_rect_;
};

View File

@@ -4,6 +4,7 @@
#include "flutter/display_list/display_list_matrix_clip_tracker.h"
#include "gtest/gtest.h"
#include "third_party/skia/include/core/SkPath.h"
namespace flutter {
namespace testing {
@@ -225,5 +226,127 @@ TEST(DisplayListMatrixClipTracker, Rotate) {
ASSERT_EQ(tracker2.matrix_4x4(), rotated_m44);
}
TEST(DisplayListMatrixClipTracker, Transform2DAffine) {
const SkRect cull_rect = SkRect::MakeLTRB(20, 20, 60, 60);
const SkMatrix matrix = SkMatrix::Scale(4, 4);
const SkM44 m44 = SkM44::Scale(4, 4);
const SkMatrix transformed_matrix =
SkMatrix::Concat(matrix, SkMatrix::MakeAll(2, 0, 5, //
0, 2, 6, //
0, 0, 1));
const SkM44 transformed_m44 = SkM44(transformed_matrix);
const SkRect local_cull_rect = SkRect::MakeLTRB(0, -0.5, 5, 4.5);
DisplayListMatrixClipTracker tracker1(cull_rect, matrix);
DisplayListMatrixClipTracker tracker2(cull_rect, m44);
tracker1.transform2DAffine(2, 0, 5, //
0, 2, 6);
tracker2.transform2DAffine(2, 0, 5, //
0, 2, 6);
ASSERT_FALSE(tracker1.using_4x4_matrix());
ASSERT_EQ(tracker1.device_cull_rect(), cull_rect);
ASSERT_EQ(tracker1.local_cull_rect(), local_cull_rect);
ASSERT_EQ(tracker1.matrix_3x3(), transformed_matrix);
ASSERT_EQ(tracker1.matrix_4x4(), transformed_m44);
ASSERT_FALSE(tracker2.using_4x4_matrix());
ASSERT_EQ(tracker2.device_cull_rect(), cull_rect);
ASSERT_EQ(tracker2.local_cull_rect(), local_cull_rect);
ASSERT_EQ(tracker2.matrix_3x3(), transformed_matrix);
ASSERT_EQ(tracker2.matrix_4x4(), transformed_m44);
}
TEST(DisplayListMatrixClipTracker, TransformFullPerspectiveUsing3x3Matrix) {
const SkRect cull_rect = SkRect::MakeLTRB(20, 20, 60, 60);
const SkMatrix matrix = SkMatrix::Scale(4, 4);
const SkM44 m44 = SkM44::Scale(4, 4);
const SkMatrix transformed_matrix =
SkMatrix::Concat(matrix, SkMatrix::MakeAll(2, 0, 5, //
0, 2, 6, //
0, 0, 1));
const SkM44 transformed_m44 = SkM44(transformed_matrix);
const SkRect local_cull_rect = SkRect::MakeLTRB(0, -0.5, 5, 4.5);
DisplayListMatrixClipTracker tracker1(cull_rect, matrix);
DisplayListMatrixClipTracker tracker2(cull_rect, m44);
tracker1.transformFullPerspective(2, 0, 0, 5, //
0, 2, 0, 6, //
0, 0, 1, 0, //
0, 0, 0, 1);
tracker2.transformFullPerspective(2, 0, 0, 5, //
0, 2, 0, 6, //
0, 0, 1, 0, //
0, 0, 0, 1);
ASSERT_FALSE(tracker1.using_4x4_matrix());
ASSERT_EQ(tracker1.device_cull_rect(), cull_rect);
ASSERT_EQ(tracker1.local_cull_rect(), local_cull_rect);
ASSERT_EQ(tracker1.matrix_3x3(), transformed_matrix);
ASSERT_EQ(tracker1.matrix_4x4(), transformed_m44);
ASSERT_FALSE(tracker2.using_4x4_matrix());
ASSERT_EQ(tracker2.device_cull_rect(), cull_rect);
ASSERT_EQ(tracker2.local_cull_rect(), local_cull_rect);
ASSERT_EQ(tracker2.matrix_3x3(), transformed_matrix);
ASSERT_EQ(tracker2.matrix_4x4(), transformed_m44);
}
TEST(DisplayListMatrixClipTracker, TransformFullPerspectiveUsing4x4Matrix) {
const SkRect cull_rect = SkRect::MakeLTRB(20, 20, 60, 60);
const SkMatrix matrix = SkMatrix::Scale(4, 4);
const SkM44 m44 = SkM44::Scale(4, 4);
const SkM44 transformed_m44 = SkM44(m44, SkM44(2, 0, 0, 5, //
0, 2, 0, 6, //
0, 0, 1, 7, //
0, 0, 0, 1));
const SkRect local_cull_rect = SkRect::MakeLTRB(0, -0.5, 5, 4.5);
DisplayListMatrixClipTracker tracker1(cull_rect, matrix);
DisplayListMatrixClipTracker tracker2(cull_rect, m44);
tracker1.transformFullPerspective(2, 0, 0, 5, //
0, 2, 0, 6, //
0, 0, 1, 7, //
0, 0, 0, 1);
tracker2.transformFullPerspective(2, 0, 0, 5, //
0, 2, 0, 6, //
0, 0, 1, 7, //
0, 0, 0, 1);
ASSERT_TRUE(tracker1.using_4x4_matrix());
ASSERT_EQ(tracker1.device_cull_rect(), cull_rect);
ASSERT_EQ(tracker1.local_cull_rect(), local_cull_rect);
ASSERT_EQ(tracker1.matrix_4x4(), transformed_m44);
ASSERT_TRUE(tracker2.using_4x4_matrix());
ASSERT_EQ(tracker2.device_cull_rect(), cull_rect);
ASSERT_EQ(tracker2.local_cull_rect(), local_cull_rect);
ASSERT_EQ(tracker2.matrix_4x4(), transformed_m44);
}
TEST(DisplayListMatrixClipTracker, ClipPathWithInvertFillType) {
SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0);
DisplayListMatrixClipTracker builder(cull_rect, SkMatrix::I());
SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
clip.setFillType(SkPathFillType::kInverseWinding);
builder.clipPath(clip, SkClipOp::kIntersect, false);
ASSERT_EQ(builder.local_cull_rect(), cull_rect);
ASSERT_EQ(builder.device_cull_rect(), cull_rect);
}
TEST(DisplayListMatrixClipTracker, DiffClipPathWithInvertFillType) {
SkRect cull_rect = SkRect::MakeLTRB(0, 0, 100.0, 100.0);
DisplayListMatrixClipTracker tracker(cull_rect, SkMatrix::I());
SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
clip.setFillType(SkPathFillType::kInverseWinding);
SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
tracker.clipPath(clip, SkClipOp::kDifference, false);
ASSERT_EQ(tracker.local_cull_rect(), clip_bounds);
ASSERT_EQ(tracker.device_cull_rect(), clip_bounds);
}
} // namespace testing
} // namespace flutter

View File

@@ -1361,22 +1361,21 @@ TEST(DisplayList, FullTransformAffectsCurrentTransform) {
TEST(DisplayList, ClipRectAffectsClipBounds) {
DisplayListBuilder builder;
SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
builder.clipRect(clip_bounds, SkClipOp::kIntersect, false);
// Save initial return values for testing restored values
SkRect initial_local_bounds = builder.getLocalClipBounds();
SkRect initial_destination_bounds = builder.getDestinationClipBounds();
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_local_bounds, clip_bounds);
ASSERT_EQ(initial_destination_bounds, clip_bounds);
builder.save();
builder.clipRect({0, 0, 15, 15}, SkClipOp::kIntersect, false);
// Both clip bounds have changed
ASSERT_NE(builder.getLocalClipBounds(), clip_expanded_bounds);
ASSERT_NE(builder.getLocalClipBounds(), clip_bounds);
ASSERT_NE(builder.getDestinationClipBounds(), clip_bounds);
// Previous return values have not changed
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_local_bounds, clip_bounds);
ASSERT_EQ(initial_destination_bounds, clip_bounds);
builder.restore();
@@ -1384,12 +1383,51 @@ TEST(DisplayList, ClipRectAffectsClipBounds) {
ASSERT_EQ(builder.getLocalClipBounds(), initial_local_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), initial_destination_bounds);
builder.save();
builder.scale(2, 2);
SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85);
ASSERT_EQ(builder.getLocalClipBounds(), scaled_clip_bounds);
// Destination bounds are unaffected by transform
ASSERT_EQ(builder.getDestinationClipBounds(), clip_bounds);
builder.restore();
// save/restore returned the values to their original values
ASSERT_EQ(builder.getLocalClipBounds(), initial_local_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), initial_destination_bounds);
}
TEST(DisplayList, ClipRectDoAAAffectsClipBounds) {
DisplayListBuilder builder;
SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
builder.clipRect(clip_bounds, SkClipOp::kIntersect, true);
// Save initial return values for testing restored values
SkRect initial_local_bounds = builder.getLocalClipBounds();
SkRect initial_destination_bounds = builder.getDestinationClipBounds();
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
builder.save();
builder.clipRect({0, 0, 15, 15}, SkClipOp::kIntersect, true);
// Both clip bounds have changed
ASSERT_NE(builder.getLocalClipBounds(), clip_expanded_bounds);
ASSERT_NE(builder.getDestinationClipBounds(), clip_expanded_bounds);
// Previous return values have not changed
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
builder.restore();
// save/restore returned the values to their original values
ASSERT_EQ(builder.getLocalClipBounds(), initial_local_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), initial_destination_bounds);
builder.save();
builder.scale(2, 2);
SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13);
ASSERT_EQ(builder.getLocalClipBounds(), scaled_expanded_bounds);
// Destination bounds are unaffected by transform
ASSERT_EQ(builder.getDestinationClipBounds(), clip_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), clip_expanded_bounds);
builder.restore();
// save/restore returned the values to their original values
@@ -1419,23 +1457,22 @@ TEST(DisplayList, ClipRectAffectsClipBoundsWithMatrix) {
TEST(DisplayList, ClipRRectAffectsClipBounds) {
DisplayListBuilder builder;
SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2);
builder.clipRRect(clip, SkClipOp::kIntersect, false);
// Save initial return values for testing restored values
SkRect initial_local_bounds = builder.getLocalClipBounds();
SkRect initial_destination_bounds = builder.getDestinationClipBounds();
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_local_bounds, clip_bounds);
ASSERT_EQ(initial_destination_bounds, clip_bounds);
builder.save();
builder.clipRect({0, 0, 15, 15}, SkClipOp::kIntersect, false);
// Both clip bounds have changed
ASSERT_NE(builder.getLocalClipBounds(), clip_expanded_bounds);
ASSERT_NE(builder.getLocalClipBounds(), clip_bounds);
ASSERT_NE(builder.getDestinationClipBounds(), clip_bounds);
// Previous return values have not changed
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_local_bounds, clip_bounds);
ASSERT_EQ(initial_destination_bounds, clip_bounds);
builder.restore();
@@ -1443,12 +1480,52 @@ TEST(DisplayList, ClipRRectAffectsClipBounds) {
ASSERT_EQ(builder.getLocalClipBounds(), initial_local_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), initial_destination_bounds);
builder.save();
builder.scale(2, 2);
SkRect scaled_clip_bounds = SkRect::MakeLTRB(5.1, 5.65, 10.2, 12.85);
ASSERT_EQ(builder.getLocalClipBounds(), scaled_clip_bounds);
// Destination bounds are unaffected by transform
ASSERT_EQ(builder.getDestinationClipBounds(), clip_bounds);
builder.restore();
// save/restore returned the values to their original values
ASSERT_EQ(builder.getLocalClipBounds(), initial_local_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), initial_destination_bounds);
}
TEST(DisplayList, ClipRRectDoAAAffectsClipBounds) {
DisplayListBuilder builder;
SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
SkRRect clip = SkRRect::MakeRectXY(clip_bounds, 3, 2);
builder.clipRRect(clip, SkClipOp::kIntersect, true);
// Save initial return values for testing restored values
SkRect initial_local_bounds = builder.getLocalClipBounds();
SkRect initial_destination_bounds = builder.getDestinationClipBounds();
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
builder.save();
builder.clipRect({0, 0, 15, 15}, SkClipOp::kIntersect, true);
// Both clip bounds have changed
ASSERT_NE(builder.getLocalClipBounds(), clip_expanded_bounds);
ASSERT_NE(builder.getDestinationClipBounds(), clip_expanded_bounds);
// Previous return values have not changed
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
builder.restore();
// save/restore returned the values to their original values
ASSERT_EQ(builder.getLocalClipBounds(), initial_local_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), initial_destination_bounds);
builder.save();
builder.scale(2, 2);
SkRect scaled_expanded_bounds = SkRect::MakeLTRB(5, 5.5, 10.5, 13);
ASSERT_EQ(builder.getLocalClipBounds(), scaled_expanded_bounds);
// Destination bounds are unaffected by transform
ASSERT_EQ(builder.getDestinationClipBounds(), clip_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), clip_expanded_bounds);
builder.restore();
// save/restore returned the values to their original values
@@ -1482,22 +1559,21 @@ TEST(DisplayList, ClipPathAffectsClipBounds) {
DisplayListBuilder builder;
SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28);
builder.clipPath(clip, SkClipOp::kIntersect, false);
// Save initial return values for testing restored values
SkRect initial_local_bounds = builder.getLocalClipBounds();
SkRect initial_destination_bounds = builder.getDestinationClipBounds();
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_local_bounds, clip_bounds);
ASSERT_EQ(initial_destination_bounds, clip_bounds);
builder.save();
builder.clipRect({0, 0, 15, 15}, SkClipOp::kIntersect, false);
// Both clip bounds have changed
ASSERT_NE(builder.getLocalClipBounds(), clip_expanded_bounds);
ASSERT_NE(builder.getLocalClipBounds(), clip_bounds);
ASSERT_NE(builder.getDestinationClipBounds(), clip_bounds);
// Previous return values have not changed
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_local_bounds, clip_bounds);
ASSERT_EQ(initial_destination_bounds, clip_bounds);
builder.restore();
@@ -1505,12 +1581,51 @@ TEST(DisplayList, ClipPathAffectsClipBounds) {
ASSERT_EQ(builder.getLocalClipBounds(), initial_local_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), initial_destination_bounds);
builder.save();
builder.scale(2, 2);
SkRect scaled_clip_bounds = SkRect::MakeLTRB(4.1, 4.65, 11.2, 13.85);
ASSERT_EQ(builder.getLocalClipBounds(), scaled_clip_bounds);
// Destination bounds are unaffected by transform
ASSERT_EQ(builder.getDestinationClipBounds(), clip_bounds);
builder.restore();
// save/restore returned the values to their original values
ASSERT_EQ(builder.getLocalClipBounds(), initial_local_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), initial_destination_bounds);
}
TEST(DisplayList, ClipPathDoAAAffectsClipBounds) {
DisplayListBuilder builder;
SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28);
builder.clipPath(clip, SkClipOp::kIntersect, true);
// Save initial return values for testing restored values
SkRect initial_local_bounds = builder.getLocalClipBounds();
SkRect initial_destination_bounds = builder.getDestinationClipBounds();
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
builder.save();
builder.clipRect({0, 0, 15, 15}, SkClipOp::kIntersect, true);
// Both clip bounds have changed
ASSERT_NE(builder.getLocalClipBounds(), clip_expanded_bounds);
ASSERT_NE(builder.getDestinationClipBounds(), clip_expanded_bounds);
// Previous return values have not changed
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_destination_bounds, clip_expanded_bounds);
builder.restore();
// save/restore returned the values to their original values
ASSERT_EQ(builder.getLocalClipBounds(), initial_local_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), initial_destination_bounds);
builder.save();
builder.scale(2, 2);
SkRect scaled_expanded_bounds = SkRect::MakeLTRB(4, 4.5, 11.5, 14);
ASSERT_EQ(builder.getLocalClipBounds(), scaled_expanded_bounds);
// Destination bounds are unaffected by transform
ASSERT_EQ(builder.getDestinationClipBounds(), clip_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), clip_expanded_bounds);
builder.restore();
// save/restore returned the values to their original values
@@ -1543,13 +1658,12 @@ TEST(DisplayList, DiffClipRectDoesNotAffectClipBounds) {
DisplayListBuilder builder;
SkRect diff_clip = SkRect::MakeLTRB(0, 0, 15, 15);
SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
builder.clipRect(clip_bounds, SkClipOp::kIntersect, false);
// Save initial return values for testing after kDifference clip
SkRect initial_local_bounds = builder.getLocalClipBounds();
SkRect initial_destination_bounds = builder.getDestinationClipBounds();
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_local_bounds, clip_bounds);
ASSERT_EQ(initial_destination_bounds, clip_bounds);
builder.clipRect(diff_clip, SkClipOp::kDifference, false);
@@ -1561,14 +1675,13 @@ TEST(DisplayList, DiffClipRRectDoesNotAffectClipBounds) {
DisplayListBuilder builder;
SkRRect diff_clip = SkRRect::MakeRectXY({0, 0, 15, 15}, 1, 1);
SkRect clip_bounds = SkRect::MakeLTRB(10.2, 11.3, 20.4, 25.7);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(10, 11, 21, 26);
SkRRect clip = SkRRect::MakeRectXY({10.2, 11.3, 20.4, 25.7}, 3, 2);
builder.clipRRect(clip, SkClipOp::kIntersect, false);
// Save initial return values for testing after kDifference clip
SkRect initial_local_bounds = builder.getLocalClipBounds();
SkRect initial_destination_bounds = builder.getDestinationClipBounds();
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_local_bounds, clip_bounds);
ASSERT_EQ(initial_destination_bounds, clip_bounds);
builder.clipRRect(diff_clip, SkClipOp::kDifference, false);
@@ -1581,13 +1694,12 @@ TEST(DisplayList, DiffClipPathDoesNotAffectClipBounds) {
SkPath diff_clip = SkPath().addRect({0, 0, 15, 15});
SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28);
builder.clipPath(clip, SkClipOp::kIntersect, false);
// Save initial return values for testing after kDifference clip
SkRect initial_local_bounds = builder.getLocalClipBounds();
SkRect initial_destination_bounds = builder.getDestinationClipBounds();
ASSERT_EQ(initial_local_bounds, clip_expanded_bounds);
ASSERT_EQ(initial_local_bounds, clip_bounds);
ASSERT_EQ(initial_destination_bounds, clip_bounds);
builder.clipPath(diff_clip, SkClipOp::kDifference, false);
@@ -1612,10 +1724,9 @@ TEST(DisplayList, DiffClipPathWithInvertFillTypeAffectsClipBounds) {
SkPath clip = SkPath().addCircle(10.2, 11.3, 2).addCircle(20.4, 25.7, 2);
clip.setFillType(SkPathFillType::kInverseWinding);
SkRect clip_bounds = SkRect::MakeLTRB(8.2, 9.3, 22.4, 27.7);
SkRect clip_expanded_bounds = SkRect::MakeLTRB(8, 9, 23, 28);
builder.clipPath(clip, SkClipOp::kDifference, false);
ASSERT_EQ(builder.getLocalClipBounds(), clip_expanded_bounds);
ASSERT_EQ(builder.getLocalClipBounds(), clip_bounds);
ASSERT_EQ(builder.getDestinationClipBounds(), clip_bounds);
}

View File

@@ -726,7 +726,7 @@ void main() {
Expect.fail('$value is too close to $expected');
};
test('Canvas.clipRect affects canvas.getClipBounds', () async {
test('Canvas.clipRect(doAA=true) affects canvas.getClipBounds', () async {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
@@ -737,16 +737,16 @@ void main() {
final Rect initialLocalBounds = canvas.getLocalClipBounds();
final Rect initialDestinationBounds = canvas.getDestinationClipBounds();
expect(initialLocalBounds, closeToRect(clipExpandedBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipExpandedBounds));
canvas.save();
canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15));
// Both clip bounds have changed
expect(canvas.getLocalClipBounds(), notCloseToRect(clipExpandedBounds));
expect(canvas.getDestinationClipBounds(), notCloseToRect(clipBounds));
expect(canvas.getDestinationClipBounds(), notCloseToRect(clipExpandedBounds));
// Previous return values have not changed
expect(initialLocalBounds, closeToRect(clipExpandedBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipExpandedBounds));
canvas.restore();
// save/restore returned the values to their original values
@@ -758,6 +758,45 @@ void main() {
const Rect scaledExpandedBounds = Rect.fromLTRB(5, 5.5, 10.5, 13);
expect(canvas.getLocalClipBounds(), closeToRect(scaledExpandedBounds));
// Destination bounds are unaffected by transform
expect(canvas.getDestinationClipBounds(), closeToRect(clipExpandedBounds));
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
});
test('Canvas.clipRect(doAA=false) affects canvas.getClipBounds', () async {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
canvas.clipRect(clipBounds, doAntiAlias: false);
// Save initial return values for testing restored values
final Rect initialLocalBounds = canvas.getLocalClipBounds();
final Rect initialDestinationBounds = canvas.getDestinationClipBounds();
expect(initialLocalBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
canvas.save();
canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15));
// Both clip bounds have changed
expect(canvas.getLocalClipBounds(), notCloseToRect(clipBounds));
expect(canvas.getDestinationClipBounds(), notCloseToRect(clipBounds));
// Previous return values have not changed
expect(initialLocalBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
canvas.save();
canvas.scale(2, 2);
const Rect scaledClipBounds = Rect.fromLTRB(5.1, 5.65, 10.2, 12.85);
expect(canvas.getLocalClipBounds(), closeToRect(scaledClipBounds));
// Destination bounds are unaffected by transform
expect(canvas.getDestinationClipBounds(), closeToRect(clipBounds));
canvas.restore();
@@ -773,21 +812,21 @@ void main() {
const Rect clipBounds2 = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0);
canvas.save();
canvas.clipRect(clipBounds1);
canvas.clipRect(clipBounds1, doAntiAlias: false);
canvas.translate(0, 10.0);
canvas.clipRect(clipBounds1);
canvas.clipRect(clipBounds1, doAntiAlias: false);
expect(canvas.getDestinationClipBounds().isEmpty, isTrue);
canvas.restore();
canvas.save();
canvas.clipRect(clipBounds1);
canvas.clipRect(clipBounds1, doAntiAlias: false);
canvas.translate(-10.0, -10.0);
canvas.clipRect(clipBounds2);
canvas.clipRect(clipBounds2, doAntiAlias: false);
expect(canvas.getDestinationClipBounds(), clipBounds1);
canvas.restore();
});
test('Canvas.clipRRect affects canvas.getClipBounds', () async {
test('Canvas.clipRRect(doAA=true) affects canvas.getClipBounds', () async {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
@@ -799,16 +838,16 @@ void main() {
final Rect initialLocalBounds = canvas.getLocalClipBounds();
final Rect initialDestinationBounds = canvas.getDestinationClipBounds();
expect(initialLocalBounds, closeToRect(clipExpandedBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipExpandedBounds));
canvas.save();
canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15));
// Both clip bounds have changed
expect(canvas.getLocalClipBounds(), notCloseToRect(clipExpandedBounds));
expect(canvas.getDestinationClipBounds(), notCloseToRect(clipBounds));
expect(canvas.getDestinationClipBounds(), notCloseToRect(clipExpandedBounds));
// Previous return values have not changed
expect(initialLocalBounds, closeToRect(clipExpandedBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipExpandedBounds));
canvas.restore();
// save/restore returned the values to their original values
@@ -820,6 +859,46 @@ void main() {
const Rect scaledExpandedBounds = Rect.fromLTRB(5, 5.5, 10.5, 13);
expect(canvas.getLocalClipBounds(), closeToRect(scaledExpandedBounds));
// Destination bounds are unaffected by transform
expect(canvas.getDestinationClipBounds(), closeToRect(clipExpandedBounds));
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
});
test('Canvas.clipRRect(doAA=false) affects canvas.getClipBounds', () async {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
final RRect clip = RRect.fromRectAndRadius(clipBounds, const Radius.circular(3));
canvas.clipRRect(clip, doAntiAlias: false);
// Save initial return values for testing restored values
final Rect initialLocalBounds = canvas.getLocalClipBounds();
final Rect initialDestinationBounds = canvas.getDestinationClipBounds();
expect(initialLocalBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
canvas.save();
canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15), doAntiAlias: false);
// Both clip bounds have changed
expect(canvas.getLocalClipBounds(), notCloseToRect(clipBounds));
expect(canvas.getDestinationClipBounds(), notCloseToRect(clipBounds));
// Previous return values have not changed
expect(initialLocalBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
canvas.save();
canvas.scale(2, 2);
const Rect scaledClipBounds = Rect.fromLTRB(5.1, 5.65, 10.2, 12.85);
expect(canvas.getLocalClipBounds(), closeToRect(scaledClipBounds));
// Destination bounds are unaffected by transform
expect(canvas.getDestinationClipBounds(), closeToRect(clipBounds));
canvas.restore();
@@ -837,21 +916,21 @@ void main() {
final RRect clip2 = RRect.fromRectAndRadius(clipBounds2, const Radius.circular(3));
canvas.save();
canvas.clipRRect(clip1);
canvas.clipRRect(clip1, doAntiAlias: false);
canvas.translate(0, 10.0);
canvas.clipRRect(clip1);
canvas.clipRRect(clip1, doAntiAlias: false);
expect(canvas.getDestinationClipBounds().isEmpty, isTrue);
canvas.restore();
canvas.save();
canvas.clipRRect(clip1);
canvas.clipRRect(clip1, doAntiAlias: false);
canvas.translate(-10.0, -10.0);
canvas.clipRRect(clip2);
canvas.clipRRect(clip2, doAntiAlias: false);
expect(canvas.getDestinationClipBounds(), clipBounds1);
canvas.restore();
});
test('Canvas.clipPath affects canvas.getClipBounds', () async {
test('Canvas.clipPath(doAA=true) affects canvas.getClipBounds', () async {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
@@ -863,16 +942,16 @@ void main() {
final Rect initialLocalBounds = canvas.getLocalClipBounds();
final Rect initialDestinationBounds = canvas.getDestinationClipBounds();
expect(initialLocalBounds, closeToRect(clipExpandedBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipExpandedBounds));
canvas.save();
canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15));
// Both clip bounds have changed
expect(canvas.getLocalClipBounds(), notCloseToRect(clipExpandedBounds));
expect(canvas.getDestinationClipBounds(), notCloseToRect(clipBounds));
expect(canvas.getDestinationClipBounds(), notCloseToRect(clipExpandedBounds));
// Previous return values have not changed
expect(initialLocalBounds, closeToRect(clipExpandedBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipExpandedBounds));
canvas.restore();
// save/restore returned the values to their original values
@@ -884,6 +963,46 @@ void main() {
const Rect scaledExpandedBounds = Rect.fromLTRB(5, 5.5, 10.5, 13);
expect(canvas.getLocalClipBounds(), closeToRect(scaledExpandedBounds));
// Destination bounds are unaffected by transform
expect(canvas.getDestinationClipBounds(), closeToRect(clipExpandedBounds));
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
});
test('Canvas.clipPath(doAA=false) affects canvas.getClipBounds', () async {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
final Path clip = Path()..addRect(clipBounds)..addOval(clipBounds);
canvas.clipPath(clip, doAntiAlias: false);
// Save initial return values for testing restored values
final Rect initialLocalBounds = canvas.getLocalClipBounds();
final Rect initialDestinationBounds = canvas.getDestinationClipBounds();
expect(initialLocalBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
canvas.save();
canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15), doAntiAlias: false);
// Both clip bounds have changed
expect(canvas.getLocalClipBounds(), notCloseToRect(clipBounds));
expect(canvas.getDestinationClipBounds(), notCloseToRect(clipBounds));
// Previous return values have not changed
expect(initialLocalBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
canvas.save();
canvas.scale(2, 2);
const Rect scaledClipBounds = Rect.fromLTRB(5.1, 5.65, 10.2, 12.85);
expect(canvas.getLocalClipBounds(), closeToRect(scaledClipBounds));
// Destination bounds are unaffected by transform
expect(canvas.getDestinationClipBounds(), closeToRect(clipBounds));
canvas.restore();
@@ -901,16 +1020,16 @@ void main() {
final Path clip2 = Path()..addRect(clipBounds2)..addOval(clipBounds2);
canvas.save();
canvas.clipPath(clip1);
canvas.clipPath(clip1, doAntiAlias: false);
canvas.translate(0, 10.0);
canvas.clipPath(clip1);
canvas.clipPath(clip1, doAntiAlias: false);
expect(canvas.getDestinationClipBounds().isEmpty, isTrue);
canvas.restore();
canvas.save();
canvas.clipPath(clip1);
canvas.clipPath(clip1, doAntiAlias: false);
canvas.translate(-10.0, -10.0);
canvas.clipPath(clip2);
canvas.clipPath(clip2, doAntiAlias: false);
expect(canvas.getDestinationClipBounds(), clipBounds1);
canvas.restore();
});
@@ -919,16 +1038,15 @@ void main() {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
const Rect clipExpandedBounds = Rect.fromLTRB(10, 11, 21, 26);
canvas.clipRect(clipBounds);
canvas.clipRect(clipBounds, doAntiAlias: false);
// Save initial return values for testing restored values
final Rect initialLocalBounds = canvas.getLocalClipBounds();
final Rect initialDestinationBounds = canvas.getDestinationClipBounds();
expect(initialLocalBounds, closeToRect(clipExpandedBounds));
expect(initialLocalBounds, closeToRect(clipBounds));
expect(initialDestinationBounds, closeToRect(clipBounds));
canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15), clipOp: ClipOp.difference);
canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15), clipOp: ClipOp.difference, doAntiAlias: false);
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
});