diff --git a/engine/src/flutter/common/graphics/texture.h b/engine/src/flutter/common/graphics/texture.h index 794194ae47..5b5d6bd6d6 100644 --- a/engine/src/flutter/common/graphics/texture.h +++ b/engine/src/flutter/common/graphics/texture.h @@ -10,6 +10,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/synchronization/waitable_event.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkSamplingOptions.h" class GrDirectContext; @@ -25,7 +26,7 @@ class Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality quality) = 0; + const SkSamplingOptions& sampling) = 0; // Called from raster thread. virtual void OnGrContextCreated() = 0; diff --git a/engine/src/flutter/flow/layers/texture_layer.cc b/engine/src/flutter/flow/layers/texture_layer.cc index 953f9f0670..2ddc8a7b40 100644 --- a/engine/src/flutter/flow/layers/texture_layer.cc +++ b/engine/src/flutter/flow/layers/texture_layer.cc @@ -12,12 +12,12 @@ TextureLayer::TextureLayer(const SkPoint& offset, const SkSize& size, int64_t texture_id, bool freeze, - SkFilterQuality filter_quality) + const SkSamplingOptions& sampling) : offset_(offset), size_(size), texture_id_(texture_id), freeze_(freeze), - filter_quality_(filter_quality) {} + sampling_(sampling) {} void TextureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "TextureLayer::Preroll"); @@ -42,7 +42,7 @@ void TextureLayer::Paint(PaintContext& context) const { return; } texture->Paint(*context.leaf_nodes_canvas, paint_bounds(), freeze_, - context.gr_context, filter_quality_); + context.gr_context, sampling_); } } // namespace flutter diff --git a/engine/src/flutter/flow/layers/texture_layer.h b/engine/src/flutter/flow/layers/texture_layer.h index 250cefd10e..ed6f027064 100644 --- a/engine/src/flutter/flow/layers/texture_layer.h +++ b/engine/src/flutter/flow/layers/texture_layer.h @@ -18,7 +18,7 @@ class TextureLayer : public Layer { const SkSize& size, int64_t texture_id, bool freeze, - SkFilterQuality filter_quality); + const SkSamplingOptions& sampling); void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void Paint(PaintContext& context) const override; @@ -28,7 +28,7 @@ class TextureLayer : public Layer { SkSize size_; int64_t texture_id_; bool freeze_; - SkFilterQuality filter_quality_; + SkSamplingOptions sampling_; FML_DISALLOW_COPY_AND_ASSIGN(TextureLayer); }; diff --git a/engine/src/flutter/flow/layers/texture_layer_unittests.cc b/engine/src/flutter/flow/layers/texture_layer_unittests.cc index 23115052e1..3c79737be4 100644 --- a/engine/src/flutter/flow/layers/texture_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/texture_layer_unittests.cc @@ -19,7 +19,7 @@ TEST_F(TextureLayerTest, InvalidTexture) { const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); const SkSize layer_size = SkSize::Make(8.0f, 8.0f); auto layer = std::make_shared(layer_offset, layer_size, 0, - false, kNone_SkFilterQuality); + false, SkSamplingOptions()); layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), @@ -38,7 +38,7 @@ TEST_F(TextureLayerTest, PaintingEmptyLayerDies) { const int64_t texture_id = 0; auto mock_texture = std::make_shared(texture_id); auto layer = std::make_shared( - layer_offset, layer_size, texture_id, false, kNone_SkFilterQuality); + layer_offset, layer_size, texture_id, false, SkSamplingOptions()); // Ensure the texture is located by the Layer. preroll_context()->texture_registry.RegisterTexture(mock_texture); @@ -57,7 +57,8 @@ TEST_F(TextureLayerTest, PaintBeforePreollDies) { const int64_t texture_id = 0; auto mock_texture = std::make_shared(texture_id); auto layer = std::make_shared( - layer_offset, layer_size, texture_id, false, kLow_SkFilterQuality); + layer_offset, layer_size, texture_id, false, + SkSamplingOptions(SkFilterMode::kLinear)); // Ensure the texture is located by the Layer. preroll_context()->texture_registry.RegisterTexture(mock_texture); @@ -73,7 +74,8 @@ TEST_F(TextureLayerTest, PaintingWithLowFilterQuality) { const int64_t texture_id = 0; auto mock_texture = std::make_shared(texture_id); auto layer = std::make_shared( - layer_offset, layer_size, texture_id, false, kLow_SkFilterQuality); + layer_offset, layer_size, texture_id, false, + SkSamplingOptions(SkFilterMode::kLinear)); // Ensure the texture is located by the Layer. preroll_context()->texture_registry.RegisterTexture(mock_texture); @@ -88,7 +90,7 @@ TEST_F(TextureLayerTest, PaintingWithLowFilterQuality) { EXPECT_EQ(mock_texture->paint_calls(), std::vector({MockTexture::PaintCall{ mock_canvas(), layer->paint_bounds(), false, nullptr, - kLow_SkFilterQuality}})); + SkSamplingOptions(SkFilterMode::kLinear)}})); EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); } diff --git a/engine/src/flutter/flow/raster_cache.cc b/engine/src/flutter/flow/raster_cache.cc index bd228bc5e3..e748520006 100644 --- a/engine/src/flutter/flow/raster_cache.cc +++ b/engine/src/flutter/flow/raster_cache.cc @@ -32,7 +32,8 @@ void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { std::abs(bounds.size().width() - image_->dimensions().width()) <= 1 && std::abs(bounds.size().height() - image_->dimensions().height()) <= 1); canvas.resetMatrix(); - canvas.drawImage(image_, bounds.fLeft, bounds.fTop, paint); + canvas.drawImage(image_, bounds.fLeft, bounds.fTop, SkSamplingOptions(), + paint); } RasterCache::RasterCache(size_t access_threshold, diff --git a/engine/src/flutter/flow/testing/mock_texture.cc b/engine/src/flutter/flow/testing/mock_texture.cc index fddb6e8586..cb5b92c8ca 100644 --- a/engine/src/flutter/flow/testing/mock_texture.cc +++ b/engine/src/flutter/flow/testing/mock_texture.cc @@ -13,21 +13,21 @@ void MockTexture::Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) { + const SkSamplingOptions& sampling) { paint_calls_.emplace_back( - PaintCall{canvas, bounds, freeze, context, filter_quality}); + PaintCall{canvas, bounds, freeze, context, sampling}); } bool operator==(const MockTexture::PaintCall& a, const MockTexture::PaintCall& b) { return &a.canvas == &b.canvas && a.bounds == b.bounds && a.context == b.context && a.freeze == b.freeze && - a.filter_quality == b.filter_quality; + a.sampling == b.sampling; } std::ostream& operator<<(std::ostream& os, const MockTexture::PaintCall& data) { return os << &data.canvas << " " << data.bounds << " " << data.context << " " - << data.freeze << " " << data.filter_quality; + << data.freeze << " " << data.sampling; } } // namespace testing diff --git a/engine/src/flutter/flow/testing/mock_texture.h b/engine/src/flutter/flow/testing/mock_texture.h index 8120d933f3..09ce4c6542 100644 --- a/engine/src/flutter/flow/testing/mock_texture.h +++ b/engine/src/flutter/flow/testing/mock_texture.h @@ -21,7 +21,7 @@ class MockTexture : public Texture { SkRect bounds; bool freeze; GrDirectContext* context; - SkFilterQuality filter_quality; + SkSamplingOptions sampling; }; explicit MockTexture(int64_t textureId); @@ -31,7 +31,7 @@ class MockTexture : public Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) override; + const SkSamplingOptions& sampling) override; void OnGrContextCreated() override { gr_context_created_ = true; } void OnGrContextDestroyed() override { gr_context_destroyed_ = true; } diff --git a/engine/src/flutter/flow/testing/mock_texture_unittests.cc b/engine/src/flutter/flow/testing/mock_texture_unittests.cc index 9b53892aaf..16a3dfb4b1 100644 --- a/engine/src/flutter/flow/testing/mock_texture_unittests.cc +++ b/engine/src/flutter/flow/testing/mock_texture_unittests.cc @@ -29,15 +29,14 @@ TEST(MockTextureTest, PaintCalls) { SkCanvas canvas; const SkRect paint_bounds1 = SkRect::MakeWH(1.0f, 1.0f); const SkRect paint_bounds2 = SkRect::MakeWH(2.0f, 2.0f); - const auto expected_paint_calls = - std::vector{MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr, - kNone_SkFilterQuality}, - MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr, - kNone_SkFilterQuality}}; + const SkSamplingOptions sampling; + const auto expected_paint_calls = std::vector{ + MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr, sampling}, + MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr, sampling}}; auto texture = std::make_shared(0); - texture->Paint(canvas, paint_bounds1, false, nullptr, kNone_SkFilterQuality); - texture->Paint(canvas, paint_bounds2, true, nullptr, kNone_SkFilterQuality); + texture->Paint(canvas, paint_bounds1, false, nullptr, sampling); + texture->Paint(canvas, paint_bounds2, true, nullptr, sampling); EXPECT_EQ(texture->paint_calls(), expected_paint_calls); } @@ -45,15 +44,14 @@ TEST(MockTextureTest, PaintCallsWithLowFilterQuality) { SkCanvas canvas; const SkRect paint_bounds1 = SkRect::MakeWH(1.0f, 1.0f); const SkRect paint_bounds2 = SkRect::MakeWH(2.0f, 2.0f); - const auto expected_paint_calls = - std::vector{MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr, - kLow_SkFilterQuality}, - MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr, - kLow_SkFilterQuality}}; + const auto sampling = SkSamplingOptions(SkFilterMode::kLinear); + const auto expected_paint_calls = std::vector{ + MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr, sampling}, + MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr, sampling}}; auto texture = std::make_shared(0); - texture->Paint(canvas, paint_bounds1, false, nullptr, kLow_SkFilterQuality); - texture->Paint(canvas, paint_bounds2, true, nullptr, kLow_SkFilterQuality); + texture->Paint(canvas, paint_bounds1, false, nullptr, sampling); + texture->Paint(canvas, paint_bounds2, true, nullptr, sampling); EXPECT_EQ(texture->paint_calls(), expected_paint_calls); } diff --git a/engine/src/flutter/lib/ui/compositing/scene_builder.cc b/engine/src/flutter/lib/ui/compositing/scene_builder.cc index 1c0fa9bd55..b9d96ead94 100644 --- a/engine/src/flutter/lib/ui/compositing/scene_builder.cc +++ b/engine/src/flutter/lib/ui/compositing/scene_builder.cc @@ -231,9 +231,12 @@ void SceneBuilder::addTexture(double dx, int64_t textureId, bool freeze, int filterQuality) { + // TODO: take sampling directly from caller: filter-quality is deprecated + auto sampling = SkSamplingOptions(static_cast(filterQuality), + SkSamplingOptions::kMedium_asMipmapLinear); auto layer = std::make_unique( SkPoint::Make(dx, dy), SkSize::Make(width, height), textureId, freeze, - static_cast(filterQuality)); + sampling); AddLayer(std::move(layer)); } diff --git a/engine/src/flutter/lib/ui/painting/canvas.cc b/engine/src/flutter/lib/ui/painting/canvas.cc index fafb5ae3df..d15296307f 100644 --- a/engine/src/flutter/lib/ui/painting/canvas.cc +++ b/engine/src/flutter/lib/ui/painting/canvas.cc @@ -311,6 +311,12 @@ void Canvas::drawPath(const CanvasPath* path, canvas_->drawPath(path->path(), *paint.paint()); } +static SkSamplingOptions paint_to_sampling(const SkPaint* paint) { + return SkSamplingOptions( + paint ? paint->getFilterQuality() : kNone_SkFilterQuality, + SkSamplingOptions::kMedium_asMipmapLinear); +} + void Canvas::drawImage(const CanvasImage* image, double x, double y, @@ -324,7 +330,9 @@ void Canvas::drawImage(const CanvasImage* image, ToDart("Canvas.drawImage called with non-genuine Image.")); return; } - canvas_->drawImage(image->image(), x, y, paint.paint()); + // TODO: add filtering to public API, since paint's quality is deprecated + SkSamplingOptions sampling = paint_to_sampling(paint.paint()); + canvas_->drawImage(image->image(), x, y, sampling, paint.paint()); } void Canvas::drawImageRect(const CanvasImage* image, @@ -348,7 +356,9 @@ void Canvas::drawImageRect(const CanvasImage* image, } SkRect src = SkRect::MakeLTRB(src_left, src_top, src_right, src_bottom); SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom); - canvas_->drawImageRect(image->image(), src, dst, paint.paint(), + // TODO: add filtering to public API, since paint's quality is deprecated + SkSamplingOptions sampling = paint_to_sampling(paint.paint()); + canvas_->drawImageRect(image->image(), src, dst, sampling, paint.paint(), SkCanvas::kFast_SrcRectConstraint); } @@ -358,12 +368,6 @@ static SkFilterMode paint_to_filter(const SkPaint* paint) { : SkFilterMode::kNearest; } -static SkSamplingOptions paint_to_sampling(const SkPaint* paint) { - return SkSamplingOptions( - paint ? paint->getFilterQuality() : kNone_SkFilterQuality, - SkSamplingOptions::kMedium_asMipmapLinear); -} - void Canvas::drawImageNine(const CanvasImage* image, double center_left, double center_top, diff --git a/engine/src/flutter/shell/common/shell_unittests.cc b/engine/src/flutter/shell/common/shell_unittests.cc index 0b9a98f211..d072714a97 100644 --- a/engine/src/flutter/shell/common/shell_unittests.cc +++ b/engine/src/flutter/shell/common/shell_unittests.cc @@ -1650,7 +1650,7 @@ class MockTexture : public Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) override {} + const SkSamplingOptions&) override {} void OnGrContextCreated() override {} diff --git a/engine/src/flutter/shell/platform/android/android_external_texture_gl.cc b/engine/src/flutter/shell/platform/android/android_external_texture_gl.cc index ac4f73eca7..63b52d76b3 100644 --- a/engine/src/flutter/shell/platform/android/android_external_texture_gl.cc +++ b/engine/src/flutter/shell/platform/android/android_external_texture_gl.cc @@ -38,7 +38,7 @@ void AndroidExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) { + const SkSamplingOptions& sampling) { if (state_ == AttachmentState::detached) { return; } @@ -69,9 +69,7 @@ void AndroidExternalTextureGL::Paint(SkCanvas& canvas, transformAroundCenter.postTranslate(0.5, 0.5); canvas.concat(transformAroundCenter); } - SkPaint paint; - paint.setFilterQuality(filter_quality); - canvas.drawImage(image, 0, 0, &paint); + canvas.drawImage(image, 0, 0, sampling, nullptr); } } diff --git a/engine/src/flutter/shell/platform/android/android_external_texture_gl.h b/engine/src/flutter/shell/platform/android/android_external_texture_gl.h index ef4c3d0d45..a3127bf0b5 100644 --- a/engine/src/flutter/shell/platform/android/android_external_texture_gl.h +++ b/engine/src/flutter/shell/platform/android/android_external_texture_gl.h @@ -26,7 +26,7 @@ class AndroidExternalTextureGL : public flutter::Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) override; + const SkSamplingOptions& sampling) override; void OnGrContextCreated() override; diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.h b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.h index e708504419..172210c900 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.h +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.h @@ -34,7 +34,7 @@ class IOSExternalTextureGL final : public Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) override; + const SkSamplingOptions& sampling) override; // |Texture| void OnGrContextCreated() override; diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm index 685c3cbdf4..8f93c8afaf 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm @@ -142,7 +142,7 @@ void IOSExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) { + const SkSamplingOptions& sampling) { EnsureTextureCacheExists(); if (NeedUpdateTexture(freeze)) { auto pixelBuffer = [external_texture_.get() copyPixelBuffer]; @@ -167,9 +167,7 @@ void IOSExternalTextureGL::Paint(SkCanvas& canvas, FML_DCHECK(image) << "Failed to create SkImage from Texture."; if (image) { - SkPaint paint; - paint.setFilterQuality(filter_quality); - canvas.drawImage(image, bounds.x(), bounds.y(), &paint); + canvas.drawImage(image, bounds.x(), bounds.y(), sampling, nullptr); } } diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_metal.h b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_metal.h index dcd8a48170..dba9ffcaf4 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_metal.h +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_metal.h @@ -40,7 +40,7 @@ class IOSExternalTextureMetal final : public Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) override; + const SkSamplingOptions& sampling) override; // |Texture| void OnGrContextCreated() override; diff --git a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_metal.mm b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_metal.mm index 48ccc27f25..a6380807da 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_metal.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/ios_external_texture_metal.mm @@ -30,7 +30,7 @@ void IOSExternalTextureMetal::Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) { + const SkSamplingOptions& sampling) { const bool needs_updated_texture = (!freeze && texture_frame_available_) || !external_image_; if (needs_updated_texture) { @@ -51,12 +51,11 @@ void IOSExternalTextureMetal::Paint(SkCanvas& canvas, } if (external_image_) { - SkPaint paint; - paint.setFilterQuality(filter_quality); - canvas.drawImageRect(external_image_, // image - external_image_->bounds(), // source rect - bounds, // destination rect - &paint, // paint + canvas.drawImageRect(external_image_, // image + SkRect::Make(external_image_->bounds()), // source rect + bounds, // destination rect + sampling, + nullptr, // paint SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint // constraint ); } diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc index 11432d104c..e7839e1ddc 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc @@ -22,7 +22,7 @@ void EmbedderExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) { + const SkSamplingOptions& sampling) { if (auto image = external_texture_callback_( Id(), // context, // @@ -32,12 +32,10 @@ void EmbedderExternalTextureGL::Paint(SkCanvas& canvas, } if (last_image_) { - SkPaint paint; - paint.setFilterQuality(filter_quality); if (bounds != SkRect::Make(last_image_->bounds())) { - canvas.drawImageRect(last_image_, bounds, &paint); + canvas.drawImageRect(last_image_, bounds, sampling); } else { - canvas.drawImage(last_image_, bounds.x(), bounds.y(), &paint); + canvas.drawImage(last_image_, bounds.x(), bounds.y(), sampling, nullptr); } } } diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h index 8d99140807..68d96cf4f3 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h @@ -33,7 +33,7 @@ class EmbedderExternalTextureGL : public flutter::Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - SkFilterQuality filter_quality) override; + const SkSamplingOptions& sampling) override; // |flutter::Texture| void OnGrContextCreated() override; diff --git a/engine/src/flutter/testing/assertions_skia.cc b/engine/src/flutter/testing/assertions_skia.cc index 9adb1ac9eb..71f98aeafb 100644 --- a/engine/src/flutter/testing/assertions_skia.cc +++ b/engine/src/flutter/testing/assertions_skia.cc @@ -110,5 +110,14 @@ std::ostream& operator<<(std::ostream& os, const SkPaint& r) { << ", AA: " << r.isAntiAlias() << ", Shader: " << r.getShader(); } +std::ostream& operator<<(std::ostream& os, const SkSamplingOptions& s) { + if (s.useCubic) { + return os << "CubicResampler: " << s.cubic.B << ", " << s.cubic.C; + } else { + return os << "Filter: " << static_cast(s.filter) + << ", Mipmap: " << static_cast(s.mipmap); + } +} + } // namespace testing } // namespace flutter diff --git a/engine/src/flutter/testing/assertions_skia.h b/engine/src/flutter/testing/assertions_skia.h index 2e083bc863..a48a70d35c 100644 --- a/engine/src/flutter/testing/assertions_skia.h +++ b/engine/src/flutter/testing/assertions_skia.h @@ -14,6 +14,7 @@ #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPoint3.h" #include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkSamplingOptions.h" namespace flutter { namespace testing { @@ -29,6 +30,7 @@ extern std::ostream& operator<<(std::ostream& os, const SkPoint& r); extern std::ostream& operator<<(std::ostream& os, const SkISize& size); extern std::ostream& operator<<(std::ostream& os, const SkColor4f& r); extern std::ostream& operator<<(std::ostream& os, const SkPaint& r); +extern std::ostream& operator<<(std::ostream& os, const SkSamplingOptions& s); } // namespace testing } // namespace flutter