diff --git a/.cirrus.yml b/.cirrus.yml index 1b500681efe3b..7c367dd6c8e15 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,4 +1,4 @@ -gcp_credentials: ENCRYPTED[!2c88dee9c9d9805b214c9f7ad8f3bc8fae936cdb0f881d562101151c408c7e024a41222677d5831df90c60d2dd6cd80a!] +gcp_credentials: ENCRYPTED[!ebad0a1f4f7a446b77944c33651460a7ab010b4617273cb016cf354eb8fc22aa92e37a3c58bfa4a0c40a799351e027a6!] # LINUX task: diff --git a/DEPS b/DEPS index b08ecd72a6251..cb5d54826cabe 100644 --- a/DEPS +++ b/DEPS @@ -45,7 +45,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/main/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '9f52c6d20d22cad84cb23348a014b0b52b64917f', + 'dart_revision': '9ad08f1c951ae8b5ee0387306f450afaccf4eaba', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index e439bdd996a39..157a7d7844d65 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 7eb33affc611468f375b8524e2f423ba +Signature: c5829e82694b40a1a79a17ae272a02ef UNUSED LICENSES: diff --git a/display_list/display_list_color_source.cc b/display_list/display_list_color_source.cc index 6c5a5ce746b81..3fabe565f6559 100644 --- a/display_list/display_list_color_source.cc +++ b/display_list/display_list_color_source.cc @@ -6,8 +6,6 @@ namespace flutter { -static constexpr int kGradientStaticRecaptureCount = 24; - std::shared_ptr DlColorSource::From(SkShader* sk_shader) { if (sk_shader == nullptr) { return nullptr; @@ -27,61 +25,11 @@ std::shared_ptr 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); - std::shared_ptr source; - switch (type) { - case SkShader::kNone_GradientType: - source = std::make_shared(sk_ref_sp(sk_shader)); - break; - case SkShader::kColor_GradientType: - source = std::make_shared(info.fColors[0]); - break; - case SkShader::kLinear_GradientType: - source = MakeLinear(info.fPoint[0], info.fPoint[1], info.fColorCount, - info.fColors, info.fColorOffsets, mode); - break; - case SkShader::kRadial_GradientType: - source = MakeRadial(info.fPoint[0], info.fRadius[0], info.fColorCount, - info.fColors, info.fColorOffsets, mode); - break; - case SkShader::kConical_GradientType: - source = MakeConical(info.fPoint[0], info.fRadius[0], info.fPoint[1], - info.fRadius[1], info.fColorCount, info.fColors, - info.fColorOffsets, mode); - break; - case SkShader::kSweep_GradientType: - source = MakeSweep(info.fPoint[0], 0, 360, info.fColorCount, info.fColors, - 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(sk_ref_sp(sk_shader)); } static void DlGradientDeleter(void* p) { diff --git a/display_list/display_list_color_source_unittests.cc b/display_list/display_list_color_source_unittests.cc index 642e70e96e449..f6e2fa5e8dd4b 100644 --- a/display_list/display_list_color_source_unittests.cc +++ b/display_list/display_list_color_source_unittests.cc @@ -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 shader = SkShaders::Color(SK_ColorBLUE); std::shared_ptr 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); @@ -131,126 +138,78 @@ 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. sk_sp shader = SkGradientShader::MakeLinear( TestPoints, TestColors, TestStops, kTestStopCount, SkTileMode::kClamp); std::shared_ptr source = DlColorSource::From(shader); - std::shared_ptr 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. sk_sp shader = SkGradientShader::MakeRadial(TestPoints[0], 10.0, TestColors, TestStops, kTestStopCount, SkTileMode::kClamp); std::shared_ptr source = DlColorSource::From(shader); - std::shared_ptr 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. sk_sp shader = SkGradientShader::MakeTwoPointConical( TestPoints[0], 10.0, TestPoints[1], 20.0, TestColors, TestStops, kTestStopCount, SkTileMode::kClamp); std::shared_ptr source = DlColorSource::From(shader); - std::shared_ptr 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. - sk_sp shader = - SkGradientShader::MakeSweep(TestPoints[0].fX, TestPoints[0].fY, - TestColors, TestStops, kTestStopCount); + // 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(TestColors); + sk_sp shader = SkGradientShader::MakeSweep( + TestPoints[0].fX, TestPoints[0].fY, sk_colors, TestStops, kTestStopCount); std::shared_ptr source = DlColorSource::From(shader); - std::shared_ptr 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) { diff --git a/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java index e76b16fcc9a7b..0f2ebecd4bf42 100644 --- a/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java +++ b/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -315,10 +315,9 @@ public void ensureInitializationComplete( shellArgs.add("--prefetched-default-font-manager"); - if (metaData == null || metaData.getBoolean(ENABLE_SKPARAGRAPH_META_DATA_KEY, true)) { - - shellArgs.add("--enable-skparagraph"); - } + boolean enableSkParagraph = + metaData == null || metaData.getBoolean(ENABLE_SKPARAGRAPH_META_DATA_KEY, true); + shellArgs.add("--enable-skparagraph=" + enableSkParagraph); final String leakVM = isLeakVM(metaData) ? "true" : "false"; shellArgs.add("--leak-vm=" + leakVM); diff --git a/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java b/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java index d925bc420261e..a00abfb3bb5fe 100644 --- a/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java +++ b/shell/platform/android/test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java @@ -23,6 +23,7 @@ import android.content.Context; import android.os.Bundle; import android.util.DisplayMetrics; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import io.flutter.embedding.engine.FlutterJNI; import java.util.Arrays; @@ -38,6 +39,7 @@ @Config(manifest = Config.NONE) @RunWith(AndroidJUnit4.class) public class FlutterLoaderTest { + private final Context ctx = ApplicationProvider.getApplicationContext(); @Test public void itReportsUninitializedAfterCreating() { @@ -181,6 +183,44 @@ public void itUsesCorrectExecutorService() { verify(mockExecutorService, times(1)).submit(any(Callable.class)); } + @Test + public void itSetsEnableSkParagraphByDefault() { + // SkParagraph is enabled by default + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI); + FlutterLoader.Settings settings = new FlutterLoader.Settings(); + flutterLoader.startInitialization(ctx, settings); + flutterLoader.ensureInitializationComplete(ctx, null); + shadowOf(getMainLooper()).idle(); + + ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class); + verify(mockFlutterJNI, times(1)) + .init(eq(ctx), shellArgsCaptor.capture(), anyString(), anyString(), anyString(), anyLong()); + List arguments = Arrays.asList(shellArgsCaptor.getValue()); + assertTrue(arguments.contains("--enable-skparagraph=true")); + } + + @Test + public void itSetsEnableSkParagraphFromMetaData() { + // SkParagraph can be disabled using metadata. + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + FlutterLoader flutterLoader = new FlutterLoader(mockFlutterJNI); + Bundle metaData = new Bundle(); + metaData.putBoolean("io.flutter.embedding.android.EnableSkParagraph", false); + ctx.getApplicationInfo().metaData = metaData; + + FlutterLoader.Settings settings = new FlutterLoader.Settings(); + flutterLoader.startInitialization(ctx, settings); + flutterLoader.ensureInitializationComplete(ctx, null); + shadowOf(getMainLooper()).idle(); + + ArgumentCaptor shellArgsCaptor = ArgumentCaptor.forClass(String[].class); + verify(mockFlutterJNI, times(1)) + .init(eq(ctx), shellArgsCaptor.capture(), anyString(), anyString(), anyString(), anyLong()); + List arguments = Arrays.asList(shellArgsCaptor.getValue()); + assertTrue(arguments.contains("--enable-skparagraph=false")); + } + @Test @TargetApi(23) @Config(sdk = 23)