Skip to content

Commit

Permalink
Do not create incomplete DlColorSource objects from Skia gradients
Browse files Browse the repository at this point in the history
  • Loading branch information
flar committed May 5, 2022
1 parent d4d2afd commit 8a8398a
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 134 deletions.
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

0 comments on commit 8a8398a

Please sign in to comment.