Defer making skshader until we know quality (#24376) (#24494)

Co-authored-by: Michael Reed <mike@reedtribe.org>
diff --git a/flow/paint_utils.cc b/flow/paint_utils.cc
index 38fc179..647ca3d 100644
--- a/flow/paint_utils.cc
+++ b/flow/paint_utils.cc
@@ -20,7 +20,8 @@
   bm.eraseColor(c1);
   bm.eraseArea(SkIRect::MakeLTRB(0, 0, size, size), c2);
   bm.eraseArea(SkIRect::MakeLTRB(size, size, 2 * size, 2 * size), c2);
-  return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat);
+  return bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
+                       SkSamplingOptions());
 }
 
 }  // anonymous namespace
diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc
index b9d96ea..d1d8f00 100644
--- a/lib/ui/compositing/scene_builder.cc
+++ b/lib/ui/compositing/scene_builder.cc
@@ -183,8 +183,10 @@
                                   int blendMode) {
   SkRect rect = SkRect::MakeLTRB(maskRectLeft, maskRectTop, maskRectRight,
                                  maskRectBottom);
+  // TODO: should this come from the caller?
+  SkFilterQuality quality = kNone_SkFilterQuality;
   auto layer = std::make_shared<flutter::ShaderMaskLayer>(
-      shader->shader(), rect, static_cast<SkBlendMode>(blendMode));
+      shader->shader(quality), rect, static_cast<SkBlendMode>(blendMode));
   PushLayer(layer);
   EngineLayer::MakeRetained(layer_handle, layer);
 }
diff --git a/lib/ui/painting/gradient.cc b/lib/ui/painting/gradient.cc
index d71e75a..0d2d2fc 100644
--- a/lib/ui/painting/gradient.cc
+++ b/lib/ui/painting/gradient.cc
@@ -58,10 +58,10 @@
     sk_matrix = ToSkMatrix(matrix4);
   }
 
-  set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeLinear(
+  sk_shader_ = UIDartState::CreateGPUObject(SkGradientShader::MakeLinear(
       reinterpret_cast<const SkPoint*>(end_points.data()),
       reinterpret_cast<const SkColor*>(colors.data()), color_stops.data(),
-      colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr)));
+      colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr));
 }
 
 void CanvasGradient::initRadial(double center_x,
@@ -83,10 +83,10 @@
     sk_matrix = ToSkMatrix(matrix4);
   }
 
-  set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeRadial(
+  sk_shader_ = UIDartState::CreateGPUObject(SkGradientShader::MakeRadial(
       SkPoint::Make(center_x, center_y), radius,
       reinterpret_cast<const SkColor*>(colors.data()), color_stops.data(),
-      colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr)));
+      colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr));
 }
 
 void CanvasGradient::initSweep(double center_x,
@@ -109,11 +109,11 @@
     sk_matrix = ToSkMatrix(matrix4);
   }
 
-  set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeSweep(
+  sk_shader_ = UIDartState::CreateGPUObject(SkGradientShader::MakeSweep(
       center_x, center_y, reinterpret_cast<const SkColor*>(colors.data()),
       color_stops.data(), colors.num_elements(), tile_mode,
       start_angle * 180.0 / M_PI, end_angle * 180.0 / M_PI, 0,
-      has_matrix ? &sk_matrix : nullptr)));
+      has_matrix ? &sk_matrix : nullptr));
 }
 
 void CanvasGradient::initTwoPointConical(double start_x,
@@ -138,11 +138,13 @@
     sk_matrix = ToSkMatrix(matrix4);
   }
 
-  set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeTwoPointConical(
-      SkPoint::Make(start_x, start_y), start_radius,
-      SkPoint::Make(end_x, end_y), end_radius,
-      reinterpret_cast<const SkColor*>(colors.data()), color_stops.data(),
-      colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr)));
+  sk_shader_ =
+      UIDartState::CreateGPUObject(SkGradientShader::MakeTwoPointConical(
+          SkPoint::Make(start_x, start_y), start_radius,
+          SkPoint::Make(end_x, end_y), end_radius,
+          reinterpret_cast<const SkColor*>(colors.data()), color_stops.data(),
+          colors.num_elements(), tile_mode, 0,
+          has_matrix ? &sk_matrix : nullptr));
 }
 
 CanvasGradient::CanvasGradient() = default;
diff --git a/lib/ui/painting/gradient.h b/lib/ui/painting/gradient.h
index a0d142a..e3dc70e 100644
--- a/lib/ui/painting/gradient.h
+++ b/lib/ui/painting/gradient.h
@@ -62,10 +62,13 @@
                            SkTileMode tile_mode,
                            const tonic::Float64List& matrix4);
 
+  sk_sp<SkShader> shader(SkFilterQuality) override { return sk_shader_.get(); }
+
   static void RegisterNatives(tonic::DartLibraryNatives* natives);
 
  private:
   CanvasGradient();
+  flutter::SkiaGPUObject<SkShader> sk_shader_;
 };
 
 }  // namespace flutter
diff --git a/lib/ui/painting/image_shader.cc b/lib/ui/painting/image_shader.cc
index 95c27a6..5e64e46 100644
--- a/lib/ui/painting/image_shader.cc
+++ b/lib/ui/painting/image_shader.cc
@@ -43,11 +43,23 @@
         ToDart("ImageShader constructor called with non-genuine Image."));
     return;
   }
-  SkMatrix sk_matrix = ToSkMatrix(matrix4);
-  set_shader(UIDartState::CreateGPUObject(
-      image->image()->makeShader(tmx, tmy, &sk_matrix)));
+  sk_image_ = image->image();
+  tmx_ = tmx;
+  tmy_ = tmy;
+  local_matrix_ = ToSkMatrix(matrix4);
 }
 
+sk_sp<SkShader> ImageShader::shader(SkFilterQuality quality) {
+  if (!cached_shader_.get() || cached_quality_ != quality) {
+    SkSamplingOptions sampling(quality,
+                               SkSamplingOptions::kMedium_asMipmapLinear);
+
+    cached_quality_ = quality;
+    cached_shader_ = UIDartState::CreateGPUObject(
+        sk_image_->makeShader(tmx_, tmy_, sampling, &local_matrix_));
+  }
+  return cached_shader_.get();
+}
 ImageShader::ImageShader() = default;
 
 ImageShader::~ImageShader() = default;
diff --git a/lib/ui/painting/image_shader.h b/lib/ui/painting/image_shader.h
index e57a7a5..afbfe7f 100644
--- a/lib/ui/painting/image_shader.h
+++ b/lib/ui/painting/image_shader.h
@@ -33,10 +33,20 @@
                      SkTileMode tmy,
                      const tonic::Float64List& matrix4);
 
+  sk_sp<SkShader> shader(SkFilterQuality) override;
+
   static void RegisterNatives(tonic::DartLibraryNatives* natives);
 
  private:
   ImageShader();
+
+  sk_sp<SkImage> sk_image_;
+  SkTileMode tmx_;
+  SkTileMode tmy_;
+  SkMatrix local_matrix_;
+
+  SkFilterQuality cached_quality_ = kNone_SkFilterQuality;
+  flutter::SkiaGPUObject<SkShader> cached_shader_;
 };
 
 }  // namespace flutter
diff --git a/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc
index d3e49ad..c511269 100644
--- a/lib/ui/painting/paint.cc
+++ b/lib/ui/painting/paint.cc
@@ -71,6 +71,12 @@
     return;
   }
 
+  tonic::DartByteData byte_data(paint_data);
+  FML_CHECK(byte_data.length_in_bytes() == kDataByteCount);
+
+  const uint32_t* uint_data = static_cast<const uint32_t*>(byte_data.data());
+  const float* float_data = static_cast<const float*>(byte_data.data());
+
   Dart_Handle values[kObjectCount];
   if (!Dart_IsNull(paint_objects)) {
     FML_DCHECK(Dart_IsList(paint_objects));
@@ -86,7 +92,9 @@
     Dart_Handle shader = values[kShaderIndex];
     if (!Dart_IsNull(shader)) {
       Shader* decoded = tonic::DartConverter<Shader*>::FromDart(shader);
-      paint_.setShader(decoded->shader());
+      uint32_t filter_quality = uint_data[kFilterQualityIndex];
+      paint_.setShader(
+          decoded->shader(static_cast<SkFilterQuality>(filter_quality)));
     }
 
     Dart_Handle color_filter = values[kColorFilterIndex];
@@ -104,12 +112,6 @@
     }
   }
 
-  tonic::DartByteData byte_data(paint_data);
-  FML_CHECK(byte_data.length_in_bytes() == kDataByteCount);
-
-  const uint32_t* uint_data = static_cast<const uint32_t*>(byte_data.data());
-  const float* float_data = static_cast<const float*>(byte_data.data());
-
   paint_.setAntiAlias(uint_data[kIsAntiAliasIndex] == 0);
 
   uint32_t encoded_color = uint_data[kColorIndex];
@@ -149,11 +151,6 @@
     paint_.setStrokeMiter(stroke_miter_limit + kStrokeMiterLimitDefault);
   }
 
-  uint32_t filter_quality = uint_data[kFilterQualityIndex];
-  if (filter_quality) {
-    paint_.setFilterQuality(static_cast<SkFilterQuality>(filter_quality));
-  }
-
   if (uint_data[kInvertColorIndex]) {
     sk_sp<SkColorFilter> invert_filter =
         ColorFilter::MakeColorMatrixFilter255(invert_colors);
diff --git a/lib/ui/painting/shader.cc b/lib/ui/painting/shader.cc
index c6f0e6b..e350033 100644
--- a/lib/ui/painting/shader.cc
+++ b/lib/ui/painting/shader.cc
@@ -10,9 +10,6 @@
 
 IMPLEMENT_WRAPPERTYPEINFO(ui, Shader);
 
-Shader::Shader(flutter::SkiaGPUObject<SkShader> shader)
-    : shader_(std::move(shader)) {}
-
 Shader::~Shader() = default;
 
 }  // namespace flutter
diff --git a/lib/ui/painting/shader.h b/lib/ui/painting/shader.h
index cb9043a..509a06a 100644
--- a/lib/ui/painting/shader.h
+++ b/lib/ui/painting/shader.h
@@ -8,6 +8,7 @@
 #include "flutter/flow/skia_gpu_object.h"
 #include "flutter/lib/ui/dart_wrapper.h"
 #include "flutter/lib/ui/ui_dart_state.h"
+#include "third_party/skia/include/core/SkFilterQuality.h"
 #include "third_party/skia/include/core/SkShader.h"
 
 namespace flutter {
@@ -19,17 +20,13 @@
  public:
   ~Shader() override;
 
-  sk_sp<SkShader> shader() { return shader_.get(); }
-
-  void set_shader(flutter::SkiaGPUObject<SkShader> shader) {
-    shader_ = std::move(shader);
-  }
+  virtual sk_sp<SkShader> shader(SkFilterQuality) = 0;
 
  protected:
-  Shader(flutter::SkiaGPUObject<SkShader> shader = {});
+  Shader() {}
 
  private:
-  flutter::SkiaGPUObject<SkShader> shader_;
+  //  flutter::SkiaGPUObject<SkShader> shader_;
 };
 
 }  // namespace flutter