Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not create incomplete DlColorSource objects from Skia gradients #33131

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 5 additions & 58 deletions display_list/display_list_color_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

namespace flutter {

static constexpr int kGradientStaticRecaptureCount = 24;

std::shared_ptr<DlColorSource> DlColorSource::From(SkShader* sk_shader) {
if (sk_shader == nullptr) {
return nullptr;
Expand All @@ -27,62 +25,11 @@ std::shared_ptr<DlColorSource> DlColorSource::From(SkShader* sk_shader) {
// of parameters which are missing, including the local matrix in every
// gradient, and the sweep angles in the sweep gradients.
//
// Since the matrix is a rarely used property and since most sweep
// gradients swing full circle, we will simply assume an Identity matrix
// and 0,360 for the Sweep gradient.
// Possibly the most likely "missing attribute" that might be different
// would be the sweep gradients which might be a full circle, but might
// have their starting angle in a custom direction.
SkColor colors[kGradientStaticRecaptureCount];
SkScalar stops[kGradientStaticRecaptureCount];
SkShader::GradientInfo info = {};
info.fColorCount = kGradientStaticRecaptureCount;
info.fColors = colors;
info.fColorOffsets = stops;
SkShader::GradientType type = sk_shader->asAGradient(&info);
if (type != SkShader::kNone_GradientType &&
info.fColorCount > kGradientStaticRecaptureCount) {
int count = info.fColorCount;
info.fColors = new SkColor[count];
info.fColorOffsets = new SkScalar[count];
sk_shader->asAGradient(&info);
FML_DCHECK(count == info.fColorCount);
}
DlTileMode mode = ToDl(info.fTileMode);
DlColor* dl_colors = reinterpret_cast<DlColor*>(info.fColors);
std::shared_ptr<DlColorSource> source;
switch (type) {
case SkShader::kNone_GradientType:
source = std::make_shared<DlUnknownColorSource>(sk_ref_sp(sk_shader));
break;
case SkShader::kColor_GradientType:
source = std::make_shared<DlColorColorSource>(info.fColors[0]);
break;
case SkShader::kLinear_GradientType:
source = MakeLinear(info.fPoint[0], info.fPoint[1], info.fColorCount,
dl_colors, info.fColorOffsets, mode);
break;
case SkShader::kRadial_GradientType:
source = MakeRadial(info.fPoint[0], info.fRadius[0], info.fColorCount,
dl_colors, info.fColorOffsets, mode);
break;
case SkShader::kConical_GradientType:
source = MakeConical(info.fPoint[0], info.fRadius[0], info.fPoint[1],
info.fRadius[1], info.fColorCount, dl_colors,
info.fColorOffsets, mode);
break;
case SkShader::kSweep_GradientType:
source = MakeSweep(info.fPoint[0], 0, 360, info.fColorCount, dl_colors,
info.fColorOffsets, mode);
break;
}
if (info.fColors != colors) {
delete info.fColors;
}
if (info.fColorOffsets != stops) {
delete info.fColorOffsets;
}
return source;
// Since we can't reproduce every Gradient, and customers rely on using
// gradients with matrices in text code, we have to just use an Unknown
// ColorSource to express all gradients.
// (see: https://github.com/flutter/flutter/issues/102947)
return std::make_shared<DlUnknownColorSource>(sk_ref_sp(sk_shader));
}

static void DlGradientDeleter(void* p) {
Expand Down
111 changes: 35 additions & 76 deletions display_list/display_list_color_source_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,20 @@ TEST(DisplayListColorSource, FromSkiaNullShader) {
}

TEST(DisplayListColorSource, FromSkiaColorShader) {
// We cannot read back the matrix parameter from a Skia LinearGradient
// so we conservatively use an UnknownColorSource wrapper so as to not
// lose any data. Note that the Skia Color shader end is read back from
// the Skia asAGradient() method so while this type of color source
// does not really need the matrix, we represent all of the gradient
// sources using an unknown source.
// Note that this shader should never really happen in practice as it
// represents a degenerate gradient that collapsed to a single color.
sk_sp<SkShader> shader = SkShaders::Color(SK_ColorBLUE);
std::shared_ptr<DlColorSource> source = DlColorSource::From(shader);
DlColorColorSource dl_source(SK_ColorBLUE);
ASSERT_EQ(source->type(), DlColorSourceType::kColor);
ASSERT_EQ(*source->asColor(), dl_source);
ASSERT_EQ(source->asColor()->color(), SK_ColorBLUE);
ASSERT_EQ(source->type(), DlColorSourceType::kUnknown);
ASSERT_EQ(source->skia_object(), shader);

ASSERT_EQ(source->asColor(), nullptr);
ASSERT_EQ(source->asImage(), nullptr);
ASSERT_EQ(source->asLinearGradient(), nullptr);
ASSERT_EQ(source->asRadialGradient(), nullptr);
Expand Down Expand Up @@ -131,129 +138,81 @@ TEST(DisplayListColorSource, FromSkiaImageShader) {
}

TEST(DisplayListColorSource, FromSkiaLinearGradient) {
// We can read back all of the parameters of a Linear gradient
// except for matrix.
// We cannot read back the matrix parameter from a Skia LinearGradient
// so we conservatively use an UnknownColorSource wrapper so as to not
// lose any data.
const SkColor* sk_colors = reinterpret_cast<const SkColor*>(TestColors);
sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
TestPoints, sk_colors, TestStops, kTestStopCount, SkTileMode::kClamp);
std::shared_ptr<DlColorSource> source = DlColorSource::From(shader);
std::shared_ptr<DlColorSource> dl_source =
DlColorSource::MakeLinear(TestPoints[0], TestPoints[1], kTestStopCount,
TestColors, TestStops, DlTileMode::kClamp);
ASSERT_EQ(source->type(), DlColorSourceType::kLinearGradient);
EXPECT_TRUE(*source->asLinearGradient() == *dl_source->asLinearGradient());
ASSERT_EQ(*source->asLinearGradient(), *dl_source->asLinearGradient());
ASSERT_EQ(source->asLinearGradient()->start_point(), TestPoints[0]);
ASSERT_EQ(source->asLinearGradient()->end_point(), TestPoints[1]);
ASSERT_EQ(source->asLinearGradient()->stop_count(), kTestStopCount);
for (int i = 0; i < kTestStopCount; i++) {
ASSERT_EQ(source->asLinearGradient()->colors()[i], TestColors[i]);
ASSERT_EQ(source->asLinearGradient()->stops()[i], TestStops[i]);
}
ASSERT_EQ(source->asLinearGradient()->tile_mode(), DlTileMode::kClamp);
ASSERT_EQ(source->asLinearGradient()->matrix(), SkMatrix::I());
ASSERT_EQ(source->type(), DlColorSourceType::kUnknown);
ASSERT_EQ(source->skia_object(), shader);

ASSERT_EQ(source->asColor(), nullptr);
ASSERT_EQ(source->asImage(), nullptr);
ASSERT_EQ(source->asLinearGradient(), nullptr);
ASSERT_EQ(source->asRadialGradient(), nullptr);
ASSERT_EQ(source->asConicalGradient(), nullptr);
ASSERT_EQ(source->asSweepGradient(), nullptr);
}

TEST(DisplayListColorSource, FromSkiaRadialGradient) {
// We can read back all of the parameters of a Radial gradient
// except for matrix.
// We cannot read back the matrix parameter from a Skia RadialGradient
// so we conservatively use an UnknownColorSource wrapper so as to not
// lose any data.
const SkColor* sk_colors = reinterpret_cast<const SkColor*>(TestColors);
sk_sp<SkShader> shader =
SkGradientShader::MakeRadial(TestPoints[0], 10.0, sk_colors, TestStops,
kTestStopCount, SkTileMode::kClamp);
std::shared_ptr<DlColorSource> source = DlColorSource::From(shader);
std::shared_ptr<DlColorSource> dl_source =
DlColorSource::MakeRadial(TestPoints[0], 10.0, kTestStopCount, TestColors,
TestStops, DlTileMode::kClamp);
ASSERT_EQ(source->type(), DlColorSourceType::kRadialGradient);
EXPECT_TRUE(*source->asRadialGradient() == *dl_source->asRadialGradient());
ASSERT_EQ(*source->asRadialGradient(), *dl_source->asRadialGradient());
ASSERT_EQ(source->asRadialGradient()->center(), TestPoints[0]);
ASSERT_EQ(source->asRadialGradient()->radius(), 10.0);
ASSERT_EQ(source->asRadialGradient()->stop_count(), kTestStopCount);
for (int i = 0; i < kTestStopCount; i++) {
ASSERT_EQ(source->asRadialGradient()->colors()[i], TestColors[i]);
ASSERT_EQ(source->asRadialGradient()->stops()[i], TestStops[i]);
}
ASSERT_EQ(source->asRadialGradient()->tile_mode(), DlTileMode::kClamp);
ASSERT_EQ(source->asRadialGradient()->matrix(), SkMatrix::I());
ASSERT_EQ(source->type(), DlColorSourceType::kUnknown);
ASSERT_EQ(source->skia_object(), shader);

ASSERT_EQ(source->asColor(), nullptr);
ASSERT_EQ(source->asImage(), nullptr);
ASSERT_EQ(source->asLinearGradient(), nullptr);
ASSERT_EQ(source->asRadialGradient(), nullptr);
ASSERT_EQ(source->asConicalGradient(), nullptr);
ASSERT_EQ(source->asSweepGradient(), nullptr);
}

TEST(DisplayListColorSource, FromSkiaConicalGradient) {
// We can read back all of the parameters of a Conical gradient
// except for matrix.
// We cannot read back the matrix parameter from a Skia ConicalGradient
// so we conservatively use an UnknownColorSource wrapper so as to not
// lose any data.
const SkColor* sk_colors = reinterpret_cast<const SkColor*>(TestColors);
sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(
TestPoints[0], 10.0, TestPoints[1], 20.0, sk_colors, TestStops,
kTestStopCount, SkTileMode::kClamp);
std::shared_ptr<DlColorSource> source = DlColorSource::From(shader);
std::shared_ptr<DlColorSource> dl_source = DlColorSource::MakeConical(
TestPoints[0], 10.0, TestPoints[1], 20.0, kTestStopCount, TestColors,
TestStops, DlTileMode::kClamp);
ASSERT_EQ(source->type(), DlColorSourceType::kConicalGradient);
EXPECT_TRUE(*source->asConicalGradient() == *dl_source->asConicalGradient());
ASSERT_EQ(*source->asConicalGradient(), *dl_source->asConicalGradient());
ASSERT_EQ(source->asConicalGradient()->start_center(), TestPoints[0]);
ASSERT_EQ(source->asConicalGradient()->start_radius(), 10.0);
ASSERT_EQ(source->asConicalGradient()->end_center(), TestPoints[1]);
ASSERT_EQ(source->asConicalGradient()->end_radius(), 20.0);
ASSERT_EQ(source->asConicalGradient()->stop_count(), kTestStopCount);
for (int i = 0; i < kTestStopCount; i++) {
ASSERT_EQ(source->asConicalGradient()->colors()[i], TestColors[i]);
ASSERT_EQ(source->asConicalGradient()->stops()[i], TestStops[i]);
}
ASSERT_EQ(source->asConicalGradient()->tile_mode(), DlTileMode::kClamp);
ASSERT_EQ(source->asConicalGradient()->matrix(), SkMatrix::I());
ASSERT_EQ(source->type(), DlColorSourceType::kUnknown);
ASSERT_EQ(source->skia_object(), shader);

ASSERT_EQ(source->asColor(), nullptr);
ASSERT_EQ(source->asImage(), nullptr);
ASSERT_EQ(source->asLinearGradient(), nullptr);
ASSERT_EQ(source->asRadialGradient(), nullptr);
ASSERT_EQ(source->asConicalGradient(), nullptr);
ASSERT_EQ(source->asSweepGradient(), nullptr);
}

TEST(DisplayListColorSource, FromSkiaSweepGradient) {
// We can read back all of the parameters of a Sweep gradient
// except for matrix and the start/stop angles.
// We cannot read back the matrix parameter, nor the sweep parameters from a
// Skia SweepGradient so we conservatively use an UnknownColorSource wrapper
// so as to not lose any data.
const SkColor* sk_colors = reinterpret_cast<const SkColor*>(TestColors);
sk_sp<SkShader> shader = SkGradientShader::MakeSweep(
TestPoints[0].fX, TestPoints[0].fY, sk_colors, TestStops, kTestStopCount);
std::shared_ptr<DlColorSource> source = DlColorSource::From(shader);
std::shared_ptr<DlColorSource> dl_source =
DlColorSource::MakeSweep(TestPoints[0], 0, 360, kTestStopCount,
TestColors, TestStops, DlTileMode::kClamp);
ASSERT_EQ(source->type(), DlColorSourceType::kSweepGradient);
EXPECT_TRUE(*source->asSweepGradient() == *dl_source->asSweepGradient());
ASSERT_EQ(*source->asSweepGradient(), *dl_source->asSweepGradient());
ASSERT_EQ(source->asSweepGradient()->center(), TestPoints[0]);
ASSERT_EQ(source->asSweepGradient()->start(), 0);
ASSERT_EQ(source->asSweepGradient()->end(), 360);
ASSERT_EQ(source->asSweepGradient()->stop_count(), kTestStopCount);
for (int i = 0; i < kTestStopCount; i++) {
ASSERT_EQ(source->asSweepGradient()->colors()[i], TestColors[i]);
ASSERT_EQ(source->asSweepGradient()->stops()[i], TestStops[i]);
}
ASSERT_EQ(source->asSweepGradient()->tile_mode(), DlTileMode::kClamp);
ASSERT_EQ(source->asSweepGradient()->matrix(), SkMatrix::I());
ASSERT_EQ(source->type(), DlColorSourceType::kUnknown);
ASSERT_EQ(source->skia_object(), shader);

ASSERT_EQ(source->asColor(), nullptr);
ASSERT_EQ(source->asImage(), nullptr);
ASSERT_EQ(source->asLinearGradient(), nullptr);
ASSERT_EQ(source->asRadialGradient(), nullptr);
ASSERT_EQ(source->asConicalGradient(), nullptr);
ASSERT_NE(source->asSweepGradient(), nullptr);
ASSERT_EQ(source->asSweepGradient(), nullptr);
}

TEST(DisplayListColorSource, FromSkiaUnrecognizedShader) {
Expand Down