blob: 95807c061666b36840a3a3213b7dfea26bb93811 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/display_list/display_list_attributes_testing.h"
#include "flutter/display_list/display_list_blend_mode.h"
#include "flutter/display_list/display_list_builder.h"
#include "flutter/display_list/display_list_color.h"
#include "flutter/display_list/display_list_color_filter.h"
#include "flutter/display_list/display_list_comparable.h"
#include "flutter/display_list/display_list_image_filter.h"
#include "flutter/display_list/display_list_sampling_options.h"
#include "flutter/display_list/display_list_tile_mode.h"
#include "flutter/display_list/types.h"
#include "gtest/gtest.h"
namespace flutter {
namespace testing {
TEST(DisplayListImageFilter, BuilderSetGet) {
DlBlurImageFilter filter(5.0, 5.0, DlTileMode::kDecal);
DisplayListBuilder builder;
ASSERT_EQ(builder.getImageFilter(), nullptr);
builder.setImageFilter(&filter);
ASSERT_NE(builder.getImageFilter(), nullptr);
ASSERT_TRUE(
Equals(builder.getImageFilter(), static_cast<DlImageFilter*>(&filter)));
builder.setImageFilter(nullptr);
ASSERT_EQ(builder.getImageFilter(), nullptr);
}
TEST(DisplayListImageFilter, FromSkiaNullFilter) {
std::shared_ptr<DlImageFilter> filter = DlImageFilter::From(nullptr);
ASSERT_EQ(filter, nullptr);
ASSERT_EQ(filter.get(), nullptr);
}
TEST(DisplayListImageFilter, FromSkiaBlurImageFilter) {
sk_sp<SkImageFilter> sk_image_filter =
SkImageFilters::Blur(5.0, 5.0, SkTileMode::kRepeat, nullptr);
std::shared_ptr<DlImageFilter> filter = DlImageFilter::From(sk_image_filter);
ASSERT_EQ(filter->type(), DlImageFilterType::kUnknown);
// We cannot recapture the blur parameters from an SkBlurImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
}
TEST(DisplayListImageFilter, FromSkiaDilateImageFilter) {
sk_sp<SkImageFilter> sk_image_filter =
SkImageFilters::Dilate(5.0, 5.0, nullptr);
std::shared_ptr<DlImageFilter> filter = DlImageFilter::From(sk_image_filter);
ASSERT_EQ(filter->type(), DlImageFilterType::kUnknown);
// We cannot recapture the dilate parameters from an SkDilateImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
}
TEST(DisplayListImageFilter, FromSkiaErodeImageFilter) {
sk_sp<SkImageFilter> sk_image_filter =
SkImageFilters::Erode(5.0, 5.0, nullptr);
std::shared_ptr<DlImageFilter> filter = DlImageFilter::From(sk_image_filter);
ASSERT_EQ(filter->type(), DlImageFilterType::kUnknown);
// We cannot recapture the erode parameters from an SkErodeImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
}
TEST(DisplayListImageFilter, FromSkiaMatrixImageFilter) {
sk_sp<SkImageFilter> sk_image_filter = SkImageFilters::MatrixTransform(
SkMatrix::RotateDeg(45), ToSk(DlImageSampling::kLinear), nullptr);
std::shared_ptr<DlImageFilter> filter = DlImageFilter::From(sk_image_filter);
ASSERT_EQ(filter->type(), DlImageFilterType::kUnknown);
// We cannot recapture the blur parameters from an SkMatrixImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
}
TEST(DisplayListImageFilter, FromSkiaComposeImageFilter) {
sk_sp<SkImageFilter> sk_blur_filter =
SkImageFilters::Blur(5.0, 5.0, SkTileMode::kRepeat, nullptr);
sk_sp<SkImageFilter> sk_matrix_filter = SkImageFilters::MatrixTransform(
SkMatrix::RotateDeg(45), ToSk(DlImageSampling::kLinear), nullptr);
sk_sp<SkImageFilter> sk_image_filter =
SkImageFilters::Compose(sk_blur_filter, sk_matrix_filter);
std::shared_ptr<DlImageFilter> filter = DlImageFilter::From(sk_image_filter);
ASSERT_EQ(filter->type(), DlImageFilterType::kUnknown);
// We cannot recapture the blur parameters from an SkComposeImageFilter
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_EQ(filter->asColorFilter(), nullptr);
}
TEST(DisplayListImageFilter, FromSkiaColorFilterImageFilter) {
sk_sp<SkColorFilter> sk_color_filter =
SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcIn);
sk_sp<SkImageFilter> sk_image_filter =
SkImageFilters::ColorFilter(sk_color_filter, nullptr);
std::shared_ptr<DlImageFilter> filter = DlImageFilter::From(sk_image_filter);
DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kSrcIn);
DlColorFilterImageFilter dl_image_filter(dl_color_filter.shared());
ASSERT_EQ(filter->type(), DlImageFilterType::kColorFilter);
ASSERT_TRUE(*filter->asColorFilter() == dl_image_filter);
ASSERT_EQ(*filter.get(), dl_image_filter);
ASSERT_EQ(*filter->asColorFilter()->color_filter(), dl_color_filter);
ASSERT_EQ(filter->asBlur(), nullptr);
ASSERT_EQ(filter->asDilate(), nullptr);
ASSERT_EQ(filter->asErode(), nullptr);
ASSERT_EQ(filter->asMatrix(), nullptr);
ASSERT_EQ(filter->asCompose(), nullptr);
ASSERT_NE(filter->asColorFilter(), nullptr);
}
// SkRect::contains treats the rect as a half-open interval which is
// appropriate for so many operations. Unfortunately, we are using
// it here to test containment of the corners of a transformed quad
// so the corners of the quad that are measured against the right
// and bottom edges are contained even if they are on the right or
// bottom edge. This method does the "all sides inclusive" version
// of SkRect::contains.
static bool containsInclusive(const SkRect rect, const SkPoint p) {
// Test with a slight offset of 1E-9 to "forgive" IEEE bit-rounding
// Ending up with bounds that are off by 1E-9 (these numbers are all
// being tested in device space with this method) will be off by a
// negligible amount of a pixel that wouldn't contribute to changing
// the color of a pixel.
return (p.fX >= rect.fLeft - 1E-9 && //
p.fX <= rect.fRight + 1E-9 && //
p.fY >= rect.fTop - 1E-9 && //
p.fY <= rect.fBottom + 1E-9);
}
static bool containsInclusive(const SkRect rect, const SkPoint quad[4]) {
return (containsInclusive(rect, quad[0]) && //
containsInclusive(rect, quad[1]) && //
containsInclusive(rect, quad[2]) && //
containsInclusive(rect, quad[3]));
}
static bool containsInclusive(const SkIRect rect, const SkPoint quad[4]) {
return containsInclusive(SkRect::Make(rect), quad);
}
static bool containsInclusive(const SkIRect rect, const SkRect bounds) {
return (bounds.fLeft >= rect.fLeft - 1E-9 &&
bounds.fTop >= rect.fTop - 1E-9 &&
bounds.fRight <= rect.fRight + 1E-9 &&
bounds.fBottom <= rect.fBottom + 1E-9);
}
// Used to verify that the expected output bounds and reverse-engineered
// "input bounds for output bounds" rectangles are included in the rectangle
// returned from the various bounds computation methods under the specified
// matrix.
static void TestBoundsWithMatrix(const DlImageFilter& filter,
const SkMatrix& matrix,
const SkRect& sourceBounds,
const SkPoint expectedLocalOutputQuad[4]) {
SkRect device_input_bounds = matrix.mapRect(sourceBounds);
SkPoint expected_output_quad[4];
matrix.mapPoints(expected_output_quad, expectedLocalOutputQuad, 4);
SkIRect device_filter_ibounds;
ASSERT_EQ(filter.map_device_bounds(device_input_bounds.roundOut(), matrix,
device_filter_ibounds),
&device_filter_ibounds);
ASSERT_TRUE(containsInclusive(device_filter_ibounds, expected_output_quad));
SkIRect reverse_input_ibounds;
ASSERT_EQ(filter.get_input_device_bounds(device_filter_ibounds, matrix,
reverse_input_ibounds),
&reverse_input_ibounds);
ASSERT_TRUE(containsInclusive(reverse_input_ibounds, device_input_bounds));
}
static void TestInvalidBounds(const DlImageFilter& filter,
const SkMatrix& matrix,
const SkRect& localInputBounds) {
SkIRect device_input_bounds = matrix.mapRect(localInputBounds).roundOut();
SkRect local_filter_bounds;
ASSERT_EQ(filter.map_local_bounds(localInputBounds, local_filter_bounds),
nullptr);
ASSERT_EQ(local_filter_bounds, localInputBounds);
SkIRect device_filter_ibounds;
ASSERT_EQ(filter.map_device_bounds(device_input_bounds, matrix,
device_filter_ibounds),
nullptr);
ASSERT_EQ(device_filter_ibounds, device_input_bounds);
SkIRect reverse_input_ibounds;
ASSERT_EQ(filter.get_input_device_bounds(device_input_bounds, matrix,
reverse_input_ibounds),
nullptr);
ASSERT_EQ(reverse_input_ibounds, device_input_bounds);
}
// localInputBounds is a sample bounds for testing as input to the filter.
// localExpectOutputBounds is the theoretical output bounds for applying
// the filter to the localInputBounds.
// localExpectInputBounds is the theoretical input bounds required for the
// filter to cover the localExpectOutputBounds
// If either of the expected bounds are nullptr then the bounds methods will
// be assumed to be unable to perform their computations for the given
// image filter and will be returning null.
static void TestBounds(const DlImageFilter& filter,
const SkRect& sourceBounds,
const SkPoint expectedLocalOutputQuad[4]) {
SkRect local_filter_bounds;
ASSERT_EQ(filter.map_local_bounds(sourceBounds, local_filter_bounds),
&local_filter_bounds);
ASSERT_TRUE(containsInclusive(local_filter_bounds, expectedLocalOutputQuad));
for (int scale = 1; scale <= 4; scale++) {
for (int skew = 0; skew < 8; skew++) {
for (int degrees = 0; degrees <= 360; degrees += 15) {
SkMatrix matrix;
matrix.setScale(scale, scale);
matrix.postSkew(skew / 8.0, skew / 8.0);
matrix.postRotate(degrees);
ASSERT_TRUE(matrix.invert(nullptr));
TestBoundsWithMatrix(filter, matrix, sourceBounds,
expectedLocalOutputQuad);
matrix.setPerspX(0.001);
matrix.setPerspY(0.001);
ASSERT_TRUE(matrix.invert(nullptr));
TestBoundsWithMatrix(filter, matrix, sourceBounds,
expectedLocalOutputQuad);
}
}
}
}
static void TestBounds(const DlImageFilter& filter,
const SkRect& sourceBounds,
const SkRect& expectedLocalOutputBounds) {
SkPoint expected_local_output_quad[4];
expectedLocalOutputBounds.toQuad(expected_local_output_quad);
TestBounds(filter, sourceBounds, expected_local_output_quad);
}
TEST(DisplayListImageFilter, BlurConstructor) {
DlBlurImageFilter filter(5.0, 6.0, DlTileMode::kMirror);
}
TEST(DisplayListImageFilter, BlurShared) {
DlBlurImageFilter filter(5.0, 6.0, DlTileMode::kMirror);
ASSERT_NE(filter.shared().get(), &filter);
ASSERT_EQ(*filter.shared(), filter);
}
TEST(DisplayListImageFilter, BlurAsBlur) {
DlBlurImageFilter filter(5.0, 6.0, DlTileMode::kMirror);
ASSERT_NE(filter.asBlur(), nullptr);
ASSERT_EQ(filter.asBlur(), &filter);
}
TEST(DisplayListImageFilter, BlurContents) {
DlBlurImageFilter filter(5.0, 6.0, DlTileMode::kMirror);
ASSERT_EQ(filter.sigma_x(), 5.0);
ASSERT_EQ(filter.sigma_y(), 6.0);
ASSERT_EQ(filter.tile_mode(), DlTileMode::kMirror);
}
TEST(DisplayListImageFilter, BlurEquals) {
DlBlurImageFilter filter1(5.0, 6.0, DlTileMode::kMirror);
DlBlurImageFilter filter2(5.0, 6.0, DlTileMode::kMirror);
TestEquals(filter1, filter2);
}
TEST(DisplayListImageFilter, BlurNotEquals) {
DlBlurImageFilter filter1(5.0, 6.0, DlTileMode::kMirror);
DlBlurImageFilter filter2(7.0, 6.0, DlTileMode::kMirror);
DlBlurImageFilter filter3(5.0, 8.0, DlTileMode::kMirror);
DlBlurImageFilter filter4(5.0, 6.0, DlTileMode::kRepeat);
TestNotEquals(filter1, filter2, "Sigma X differs");
TestNotEquals(filter1, filter3, "Sigma Y differs");
TestNotEquals(filter1, filter4, "Tile Mode differs");
}
TEST(DisplayListImageFilter, BlurBounds) {
DlBlurImageFilter filter = DlBlurImageFilter(5, 10, DlTileMode::kDecal);
SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
SkRect expected_output_bounds = input_bounds.makeOutset(15, 30);
TestBounds(filter, input_bounds, expected_output_bounds);
}
TEST(DisplayListImageFilter, DilateConstructor) {
DlDilateImageFilter filter(5.0, 6.0);
}
TEST(DisplayListImageFilter, DilateShared) {
DlDilateImageFilter filter(5.0, 6.0);
ASSERT_NE(filter.shared().get(), &filter);
ASSERT_EQ(*filter.shared(), filter);
}
TEST(DisplayListImageFilter, DilateAsDilate) {
DlDilateImageFilter filter(5.0, 6.0);
ASSERT_NE(filter.asDilate(), nullptr);
ASSERT_EQ(filter.asDilate(), &filter);
}
TEST(DisplayListImageFilter, DilateContents) {
DlDilateImageFilter filter(5.0, 6.0);
ASSERT_EQ(filter.radius_x(), 5.0);
ASSERT_EQ(filter.radius_y(), 6.0);
}
TEST(DisplayListImageFilter, DilateEquals) {
DlDilateImageFilter filter1(5.0, 6.0);
DlDilateImageFilter filter2(5.0, 6.0);
TestEquals(filter1, filter2);
}
TEST(DisplayListImageFilter, DilateNotEquals) {
DlDilateImageFilter filter1(5.0, 6.0);
DlDilateImageFilter filter2(7.0, 6.0);
DlDilateImageFilter filter3(5.0, 8.0);
TestNotEquals(filter1, filter2, "Radius X differs");
TestNotEquals(filter1, filter3, "Radius Y differs");
}
TEST(DisplayListImageFilter, DilateBounds) {
DlDilateImageFilter filter = DlDilateImageFilter(5, 10);
SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
SkRect expected_output_bounds = input_bounds.makeOutset(5, 10);
TestBounds(filter, input_bounds, expected_output_bounds);
}
TEST(DisplayListImageFilter, ErodeConstructor) {
DlErodeImageFilter filter(5.0, 6.0);
}
TEST(DisplayListImageFilter, ErodeShared) {
DlErodeImageFilter filter(5.0, 6.0);
ASSERT_NE(filter.shared().get(), &filter);
ASSERT_EQ(*filter.shared(), filter);
}
TEST(DisplayListImageFilter, ErodeAsErode) {
DlErodeImageFilter filter(5.0, 6.0);
ASSERT_NE(filter.asErode(), nullptr);
ASSERT_EQ(filter.asErode(), &filter);
}
TEST(DisplayListImageFilter, ErodeContents) {
DlErodeImageFilter filter(5.0, 6.0);
ASSERT_EQ(filter.radius_x(), 5.0);
ASSERT_EQ(filter.radius_y(), 6.0);
}
TEST(DisplayListImageFilter, ErodeEquals) {
DlErodeImageFilter filter1(5.0, 6.0);
DlErodeImageFilter filter2(5.0, 6.0);
TestEquals(filter1, filter2);
}
TEST(DisplayListImageFilter, ErodeNotEquals) {
DlErodeImageFilter filter1(5.0, 6.0);
DlErodeImageFilter filter2(7.0, 6.0);
DlErodeImageFilter filter3(5.0, 8.0);
TestNotEquals(filter1, filter2, "Radius X differs");
TestNotEquals(filter1, filter3, "Radius Y differs");
}
TEST(DisplayListImageFilter, ErodeBounds) {
DlErodeImageFilter filter = DlErodeImageFilter(5, 10);
SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
SkRect expected_output_bounds = input_bounds.makeInset(5, 10);
TestBounds(filter, input_bounds, expected_output_bounds);
}
TEST(DisplayListImageFilter, MatrixConstructor) {
DlMatrixImageFilter filter(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
}
TEST(DisplayListImageFilter, MatrixShared) {
DlMatrixImageFilter filter(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
ASSERT_NE(filter.shared().get(), &filter);
ASSERT_EQ(*filter.shared(), filter);
}
TEST(DisplayListImageFilter, MatrixAsMatrix) {
DlMatrixImageFilter filter(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
ASSERT_NE(filter.asMatrix(), nullptr);
ASSERT_EQ(filter.asMatrix(), &filter);
}
TEST(DisplayListImageFilter, MatrixContents) {
SkMatrix matrix = SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1);
DlMatrixImageFilter filter(matrix, DlImageSampling::kLinear);
ASSERT_EQ(filter.matrix(), matrix);
ASSERT_EQ(filter.sampling(), DlImageSampling::kLinear);
}
TEST(DisplayListImageFilter, MatrixEquals) {
SkMatrix matrix = SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1);
DlMatrixImageFilter filter1(matrix, DlImageSampling::kLinear);
DlMatrixImageFilter filter2(matrix, DlImageSampling::kLinear);
TestEquals(filter1, filter2);
}
TEST(DisplayListImageFilter, MatrixNotEquals) {
SkMatrix matrix1 = SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1);
SkMatrix matrix2 = SkMatrix::MakeAll(5.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1);
DlMatrixImageFilter filter1(matrix1, DlImageSampling::kLinear);
DlMatrixImageFilter filter2(matrix2, DlImageSampling::kLinear);
DlMatrixImageFilter filter3(matrix1, DlImageSampling::kNearestNeighbor);
TestNotEquals(filter1, filter2, "Matrix differs");
TestNotEquals(filter1, filter3, "Sampling differs");
}
TEST(DisplayListImageFilter, MatrixBounds) {
SkMatrix matrix = SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 7, //
0.0, 0.0, 1);
SkMatrix inverse;
ASSERT_TRUE(matrix.invert(&inverse));
DlMatrixImageFilter filter(matrix, DlImageSampling::kLinear);
SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
SkPoint expectedOutputQuad[4] = {
{50, 77}, // (20,20) => (20*2 + 10, 20/2 + 20*3 + 7) == (50, 77)
{50, 257}, // (20,80) => (20*2 + 10, 20/2 + 80*3 + 7) == (50, 257)
{170, 287}, // (80,80) => (80*2 + 10, 80/2 + 80*3 + 7) == (170, 287)
{170, 107}, // (80,20) => (80*2 + 10, 80/2 + 20*3 + 7) == (170, 107)
};
TestBounds(filter, input_bounds, expectedOutputQuad);
}
TEST(DisplayListImageFilter, ComposeConstructor) {
DlMatrixImageFilter outer(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
DlBlurImageFilter inner(5.0, 6.0, DlTileMode::kMirror);
DlComposeImageFilter filter(outer, inner);
}
TEST(DisplayListImageFilter, ComposeShared) {
DlMatrixImageFilter outer(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
DlBlurImageFilter inner(5.0, 6.0, DlTileMode::kMirror);
DlComposeImageFilter filter(outer, inner);
ASSERT_NE(filter.shared().get(), &filter);
ASSERT_EQ(*filter.shared(), filter);
}
TEST(DisplayListImageFilter, ComposeAsCompose) {
DlMatrixImageFilter outer(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
DlBlurImageFilter inner(5.0, 6.0, DlTileMode::kMirror);
DlComposeImageFilter filter(outer, inner);
ASSERT_NE(filter.asCompose(), nullptr);
ASSERT_EQ(filter.asCompose(), &filter);
}
TEST(DisplayListImageFilter, ComposeContents) {
DlMatrixImageFilter outer(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
DlBlurImageFilter inner(5.0, 6.0, DlTileMode::kMirror);
DlComposeImageFilter filter(outer, inner);
ASSERT_EQ(*filter.outer().get(), outer);
ASSERT_EQ(*filter.inner().get(), inner);
}
TEST(DisplayListImageFilter, ComposeEquals) {
DlMatrixImageFilter outer1(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
DlBlurImageFilter inner1(5.0, 6.0, DlTileMode::kMirror);
DlComposeImageFilter filter1(outer1, inner1);
DlMatrixImageFilter outer2(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
DlBlurImageFilter inner2(5.0, 6.0, DlTileMode::kMirror);
DlComposeImageFilter filter2(outer1, inner1);
TestEquals(filter1, filter2);
}
TEST(DisplayListImageFilter, ComposeNotEquals) {
DlMatrixImageFilter outer1(SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
DlBlurImageFilter inner1(5.0, 6.0, DlTileMode::kMirror);
DlMatrixImageFilter outer2(SkMatrix::MakeAll(5.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1),
DlImageSampling::kLinear);
DlBlurImageFilter inner2(7.0, 6.0, DlTileMode::kMirror);
DlComposeImageFilter filter1(outer1, inner1);
DlComposeImageFilter filter2(outer2, inner1);
DlComposeImageFilter filter3(outer1, inner2);
TestNotEquals(filter1, filter2, "Outer differs");
TestNotEquals(filter1, filter3, "Inner differs");
}
TEST(DisplayListImageFilter, ComposeBounds) {
DlDilateImageFilter outer = DlDilateImageFilter(5, 10);
DlBlurImageFilter inner = DlBlurImageFilter(12, 5, DlTileMode::kDecal);
DlComposeImageFilter filter = DlComposeImageFilter(outer, inner);
SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
SkRect expected_output_bounds =
input_bounds.makeOutset(36, 15).makeOutset(5, 10);
TestBounds(filter, input_bounds, expected_output_bounds);
}
static void TestUnboundedBounds(DlImageFilter& filter,
const SkRect& sourceBounds,
const SkRect& expectedOutputBounds,
const SkRect& expectedInputBounds) {
SkRect bounds;
EXPECT_EQ(filter.map_local_bounds(sourceBounds, bounds), nullptr);
EXPECT_EQ(bounds, expectedOutputBounds);
SkIRect ibounds;
EXPECT_EQ(
filter.map_device_bounds(sourceBounds.roundOut(), SkMatrix::I(), ibounds),
nullptr);
EXPECT_EQ(ibounds, expectedOutputBounds.roundOut());
EXPECT_EQ(filter.get_input_device_bounds(sourceBounds.roundOut(),
SkMatrix::I(), ibounds),
nullptr);
EXPECT_EQ(ibounds, expectedInputBounds.roundOut());
}
TEST(DisplayListImageFilter, ComposeBoundsWithUnboundedInner) {
auto input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
auto expected_bounds = SkRect::MakeLTRB(5, 2, 95, 98);
DlBlendColorFilter color_filter(DlColor::kRed(), DlBlendMode::kSrcOver);
auto outer = DlBlurImageFilter(5.0, 6.0, DlTileMode::kRepeat);
auto inner = DlColorFilterImageFilter(color_filter.shared());
auto composed = DlComposeImageFilter(outer.shared(), inner.shared());
TestUnboundedBounds(composed, input_bounds, expected_bounds, expected_bounds);
}
TEST(DisplayListImageFilter, ComposeBoundsWithUnboundedOuter) {
auto input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
auto expected_bounds = SkRect::MakeLTRB(5, 2, 95, 98);
DlBlendColorFilter color_filter(DlColor::kRed(), DlBlendMode::kSrcOver);
auto outer = DlColorFilterImageFilter(color_filter.shared());
auto inner = DlBlurImageFilter(5.0, 6.0, DlTileMode::kRepeat);
auto composed = DlComposeImageFilter(outer.shared(), inner.shared());
TestUnboundedBounds(composed, input_bounds, expected_bounds, expected_bounds);
}
TEST(DisplayListImageFilter, ComposeBoundsWithUnboundedInnerAndOuter) {
auto input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
auto expected_bounds = input_bounds;
DlBlendColorFilter color_filter1(DlColor::kRed(), DlBlendMode::kSrcOver);
DlBlendColorFilter color_filter2(DlColor::kBlue(), DlBlendMode::kSrcOver);
auto outer = DlColorFilterImageFilter(color_filter1.shared());
auto inner = DlColorFilterImageFilter(color_filter2.shared());
auto composed = DlComposeImageFilter(outer.shared(), inner.shared());
TestUnboundedBounds(composed, input_bounds, expected_bounds, expected_bounds);
}
// See https://github.com/flutter/flutter/issues/108433
TEST(DisplayListImageFilter, Issue108433) {
auto input_bounds = SkIRect::MakeLTRB(20, 20, 80, 80);
auto sk_filter = SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcOver);
auto sk_outer = SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr);
auto sk_inner = SkImageFilters::ColorFilter(sk_filter, nullptr);
auto sk_compose = SkImageFilters::Compose(sk_outer, sk_inner);
DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kSrcOver);
auto dl_outer = DlBlurImageFilter(5.0, 6.0, DlTileMode::kRepeat);
auto dl_inner = DlColorFilterImageFilter(dl_color_filter.shared());
auto dl_compose = DlComposeImageFilter(dl_outer, dl_inner);
auto sk_bounds = sk_compose->filterBounds(
input_bounds, SkMatrix::I(),
SkImageFilter::MapDirection::kForward_MapDirection);
SkIRect dl_bounds;
EXPECT_EQ(
dl_compose.map_device_bounds(input_bounds, SkMatrix::I(), dl_bounds),
nullptr);
ASSERT_EQ(dl_bounds, sk_bounds);
}
TEST(DisplayListImageFilter, ColorFilterConstructor) {
DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kLighten);
DlColorFilterImageFilter filter(dl_color_filter);
}
TEST(DisplayListImageFilter, ColorFilterShared) {
DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kLighten);
DlColorFilterImageFilter filter(dl_color_filter);
ASSERT_EQ(*filter.shared(), filter);
}
TEST(DisplayListImageFilter, ColorFilterAsColorFilter) {
DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kLighten);
DlColorFilterImageFilter filter(dl_color_filter);
ASSERT_NE(filter.asColorFilter(), nullptr);
ASSERT_EQ(filter.asColorFilter(), &filter);
}
TEST(DisplayListImageFilter, ColorFilterContents) {
DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kLighten);
DlColorFilterImageFilter filter(dl_color_filter);
ASSERT_EQ(*filter.color_filter().get(), dl_color_filter);
}
TEST(DisplayListImageFilter, ColorFilterEquals) {
DlBlendColorFilter dl_color_filter1(DlColor::kRed(), DlBlendMode::kLighten);
DlColorFilterImageFilter filter1(dl_color_filter1);
DlBlendColorFilter dl_color_filter2(DlColor::kRed(), DlBlendMode::kLighten);
DlColorFilterImageFilter filter2(dl_color_filter2);
TestEquals(filter1, filter2);
}
TEST(DisplayListImageFilter, ColorFilterNotEquals) {
DlBlendColorFilter dl_color_filter1(DlColor::kRed(), DlBlendMode::kLighten);
DlColorFilterImageFilter filter1(dl_color_filter1);
DlBlendColorFilter dl_color_filter2(DlColor::kBlue(), DlBlendMode::kLighten);
DlColorFilterImageFilter filter2(dl_color_filter2);
DlBlendColorFilter dl_color_filter3(DlColor::kRed(), DlBlendMode::kDarken);
DlColorFilterImageFilter filter3(dl_color_filter3);
TestNotEquals(filter1, filter2, "Color differs");
TestNotEquals(filter1, filter3, "Blend Mode differs");
}
TEST(DisplayListImageFilter, ColorFilterBounds) {
DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kSrcIn);
DlColorFilterImageFilter filter(dl_color_filter);
SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
TestBounds(filter, input_bounds, input_bounds);
}
TEST(DisplayListImageFilter, ColorFilterModifiesTransparencyBounds) {
DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kSrcOver);
DlColorFilterImageFilter filter(dl_color_filter);
SkRect input_bounds = SkRect::MakeLTRB(20, 20, 80, 80);
TestInvalidBounds(filter, SkMatrix::I(), input_bounds);
}
TEST(DisplayListImageFilter, UnknownConstructor) {
DlUnknownImageFilter filter(
SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr));
}
TEST(DisplayListImageFilter, UnknownShared) {
DlUnknownImageFilter filter(
SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr));
ASSERT_NE(filter.shared().get(), &filter);
ASSERT_EQ(*filter.shared(), filter);
}
TEST(DisplayListImageFilter, UnknownContents) {
sk_sp<SkImageFilter> sk_filter =
SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr);
DlUnknownImageFilter filter(sk_filter);
ASSERT_EQ(filter.skia_object(), sk_filter);
ASSERT_EQ(filter.skia_object().get(), sk_filter.get());
}
TEST(DisplayListImageFilter, LocalImageFilterBounds) {
auto filter_matrix = SkMatrix::MakeAll(2.0, 0.0, 10, //
0.5, 3.0, 15, //
0.0, 0.0, 1);
std::vector<sk_sp<SkImageFilter>> sk_filters{
SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr),
SkImageFilters::ColorFilter(
SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcOver), nullptr),
SkImageFilters::Dilate(5.0, 10.0, nullptr),
SkImageFilters::MatrixTransform(filter_matrix,
ToSk(DlImageSampling::kLinear), nullptr),
SkImageFilters::Compose(
SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr),
SkImageFilters::ColorFilter(
SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcOver),
nullptr))};
DlBlendColorFilter dl_color_filter(DlColor::kRed(), DlBlendMode::kSrcOver);
std::vector<std::shared_ptr<DlImageFilter>> dl_filters{
std::make_shared<DlBlurImageFilter>(5.0, 6.0, DlTileMode::kRepeat),
std::make_shared<DlColorFilterImageFilter>(dl_color_filter.shared()),
std::make_shared<DlDilateImageFilter>(5, 10),
std::make_shared<DlMatrixImageFilter>(filter_matrix,
DlImageSampling::kLinear),
std::make_shared<DlComposeImageFilter>(
std::make_shared<DlBlurImageFilter>(5.0, 6.0, DlTileMode::kRepeat),
std::make_shared<DlColorFilterImageFilter>(
dl_color_filter.shared()))};
auto persp = SkMatrix::I();
persp.setPerspY(0.001);
std::vector<SkMatrix> matrices = {
SkMatrix::Translate(10.0, 10.0),
SkMatrix::Scale(2.0, 2.0).preTranslate(10.0, 10.0),
SkMatrix::RotateDeg(45).preTranslate(5.0, 5.0), persp};
std::vector<SkMatrix> bounds_matrices{SkMatrix::Translate(5.0, 10.0),
SkMatrix::Scale(2.0, 2.0)};
for (unsigned i = 0; i < sk_filters.size(); i++) {
for (unsigned j = 0; j < matrices.size(); j++) {
for (unsigned k = 0; k < bounds_matrices.size(); k++) {
auto& m = matrices[j];
auto& bounds_matrix = bounds_matrices[k];
auto sk_local_filter = sk_filters[i]->makeWithLocalMatrix(m);
auto dl_local_filter = dl_filters[i]->makeWithLocalMatrix(m);
if (!sk_local_filter || !dl_local_filter) {
// Temporarily relax the equivalence testing to allow Skia to expand
// their behavior. Once the Skia fixes are rolled in, the
// DlImageFilter should adapt to the new rules.
// See https://github.com/flutter/flutter/issues/114723
ASSERT_TRUE(sk_local_filter || !dl_local_filter);
continue;
}
{
auto input_bounds = SkIRect::MakeLTRB(20, 20, 80, 80);
SkIRect sk_rect, dl_rect;
sk_rect = sk_local_filter->filterBounds(
input_bounds, bounds_matrix,
SkImageFilter::MapDirection::kForward_MapDirection);
dl_local_filter->map_device_bounds(input_bounds, bounds_matrix,
dl_rect);
ASSERT_EQ(sk_rect, dl_rect);
}
{
// Test for: Know the outset bounds to get the inset bounds
// Skia have some bounds calculate error of DilateFilter and
// MatrixFilter
// Skia issue: https://bugs.chromium.org/p/skia/issues/detail?id=13444
// flutter issue: https://github.com/flutter/flutter/issues/108693
if (i == 2 || i == 3) {
continue;
}
auto outset_bounds = SkIRect::MakeLTRB(20, 20, 80, 80);
SkIRect sk_rect, dl_rect;
sk_rect = sk_local_filter->filterBounds(
outset_bounds, bounds_matrix,
SkImageFilter::MapDirection::kReverse_MapDirection);
dl_local_filter->get_input_device_bounds(outset_bounds, bounds_matrix,
dl_rect);
ASSERT_EQ(sk_rect, dl_rect);
}
}
}
}
}
TEST(DisplayListImageFilter, LocalImageSkiaNull) {
auto blur_filter =
std::make_shared<DlBlurImageFilter>(0, 0, DlTileMode::kClamp);
DlLocalMatrixImageFilter dl_local_matrix_filter(SkMatrix::RotateDeg(45),
blur_filter);
// With sigmas set to zero on the blur filter, Skia will return a null filter.
// The local matrix filter should return nullptr instead of crashing.
ASSERT_EQ(dl_local_matrix_filter.skia_object(), nullptr);
}
TEST(DisplayListImageFilter, UnknownEquals) {
sk_sp<SkImageFilter> sk_filter =
SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr);
DlUnknownImageFilter filter1(sk_filter);
DlUnknownImageFilter filter2(sk_filter);
TestEquals(filter1, filter2);
}
TEST(DisplayListImageFilter, UnknownNotEquals) {
DlUnknownImageFilter filter1(
SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr));
DlUnknownImageFilter filter2(
SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr));
// Even though the filter is the same, it is a different instance
// and we cannot currently tell them apart because the Skia
// ImageFilter objects do not implement ==
TestNotEquals(filter1, filter2, "SkImageFilter instance differs");
}
} // namespace testing
} // namespace flutter