mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Use bilinear instead of nearest filter to draw surface texture Related Issues: https://github.com/flutter/flutter/issues/53080 Tests: - TextureLayerTest
This commit is contained in:
parent
29b739a514
commit
f9eeee310c
@ -11,8 +11,13 @@ namespace flutter {
|
||||
TextureLayer::TextureLayer(const SkPoint& offset,
|
||||
const SkSize& size,
|
||||
int64_t texture_id,
|
||||
bool freeze)
|
||||
: offset_(offset), size_(size), texture_id_(texture_id), freeze_(freeze) {}
|
||||
bool freeze,
|
||||
SkFilterQuality filter_quality)
|
||||
: offset_(offset),
|
||||
size_(size),
|
||||
texture_id_(texture_id),
|
||||
freeze_(freeze),
|
||||
filter_quality_(filter_quality) {}
|
||||
|
||||
void TextureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
TRACE_EVENT0("flutter", "TextureLayer::Preroll");
|
||||
@ -35,7 +40,7 @@ void TextureLayer::Paint(PaintContext& context) const {
|
||||
return;
|
||||
}
|
||||
texture->Paint(*context.leaf_nodes_canvas, paint_bounds(), freeze_,
|
||||
context.gr_context);
|
||||
context.gr_context, filter_quality_);
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#define FLUTTER_FLOW_LAYERS_TEXTURE_LAYER_H_
|
||||
|
||||
#include "flutter/flow/layers/layer.h"
|
||||
#include "third_party/skia/include/core/SkFilterQuality.h"
|
||||
#include "third_party/skia/include/core/SkPoint.h"
|
||||
#include "third_party/skia/include/core/SkSize.h"
|
||||
|
||||
@ -16,7 +17,8 @@ class TextureLayer : public Layer {
|
||||
TextureLayer(const SkPoint& offset,
|
||||
const SkSize& size,
|
||||
int64_t texture_id,
|
||||
bool freeze);
|
||||
bool freeze,
|
||||
SkFilterQuality filter_quality);
|
||||
|
||||
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
|
||||
void Paint(PaintContext& context) const override;
|
||||
@ -26,6 +28,7 @@ class TextureLayer : public Layer {
|
||||
SkSize size_;
|
||||
int64_t texture_id_;
|
||||
bool freeze_;
|
||||
SkFilterQuality filter_quality_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(TextureLayer);
|
||||
};
|
||||
|
||||
@ -18,8 +18,8 @@ using TextureLayerTest = LayerTest;
|
||||
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<TextureLayer>(layer_offset, layer_size, 0, false);
|
||||
auto layer = std::make_shared<TextureLayer>(layer_offset, layer_size, 0,
|
||||
false, kNone_SkFilterQuality);
|
||||
|
||||
layer->Preroll(preroll_context(), SkMatrix());
|
||||
EXPECT_EQ(layer->paint_bounds(),
|
||||
@ -36,8 +36,8 @@ TEST_F(TextureLayerTest, PaintingEmptyLayerDies) {
|
||||
const SkSize layer_size = SkSize::Make(0.0f, 0.0f);
|
||||
const int64_t texture_id = 0;
|
||||
auto mock_texture = std::make_shared<MockTexture>(texture_id);
|
||||
auto layer = std::make_shared<TextureLayer>(layer_offset, layer_size,
|
||||
texture_id, false);
|
||||
auto layer = std::make_shared<TextureLayer>(
|
||||
layer_offset, layer_size, texture_id, false, kNone_SkFilterQuality);
|
||||
|
||||
// Ensure the texture is located by the Layer.
|
||||
preroll_context()->texture_registry.RegisterTexture(mock_texture);
|
||||
@ -49,7 +49,33 @@ TEST_F(TextureLayerTest, PaintingEmptyLayerDies) {
|
||||
layer->Paint(paint_context());
|
||||
EXPECT_EQ(mock_texture->paint_calls(),
|
||||
std::vector({MockTexture::PaintCall{
|
||||
mock_canvas(), layer->paint_bounds(), false, nullptr}}));
|
||||
mock_canvas(), layer->paint_bounds(), false, nullptr,
|
||||
kNone_SkFilterQuality}}));
|
||||
EXPECT_EQ(mock_canvas().draw_calls(), std::vector<MockCanvas::DrawCall>());
|
||||
}
|
||||
|
||||
TEST_F(TextureLayerTest, PaintingWithLowFilterQuality) {
|
||||
const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f);
|
||||
const SkSize layer_size = SkSize::Make(8.0f, 8.0f);
|
||||
const int64_t texture_id = 0;
|
||||
auto mock_texture = std::make_shared<MockTexture>(texture_id);
|
||||
auto layer = std::make_shared<TextureLayer>(
|
||||
layer_offset, layer_size, texture_id, false, kLow_SkFilterQuality);
|
||||
|
||||
// Ensure the texture is located by the Layer.
|
||||
preroll_context()->texture_registry.RegisterTexture(mock_texture);
|
||||
|
||||
layer->Preroll(preroll_context(), SkMatrix());
|
||||
EXPECT_EQ(layer->paint_bounds(),
|
||||
(SkRect::MakeSize(layer_size)
|
||||
.makeOffset(layer_offset.fX, layer_offset.fY)));
|
||||
EXPECT_TRUE(layer->needs_painting());
|
||||
|
||||
layer->Paint(paint_context());
|
||||
EXPECT_EQ(mock_texture->paint_calls(),
|
||||
std::vector({MockTexture::PaintCall{
|
||||
mock_canvas(), layer->paint_bounds(), false, nullptr,
|
||||
kLow_SkFilterQuality}}));
|
||||
EXPECT_EQ(mock_canvas().draw_calls(), std::vector<MockCanvas::DrawCall>());
|
||||
}
|
||||
|
||||
|
||||
@ -12,19 +12,22 @@ MockTexture::MockTexture(int64_t textureId) : Texture(textureId) {}
|
||||
void MockTexture::Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) {
|
||||
paint_calls_.emplace_back(PaintCall{canvas, bounds, freeze, context});
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) {
|
||||
paint_calls_.emplace_back(
|
||||
PaintCall{canvas, bounds, freeze, context, filter_quality});
|
||||
}
|
||||
|
||||
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.context == b.context && a.freeze == b.freeze &&
|
||||
a.filter_quality == b.filter_quality;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const MockTexture::PaintCall& data) {
|
||||
return os << &data.canvas << " " << data.bounds << " " << data.context << " "
|
||||
<< data.freeze;
|
||||
<< data.freeze << " " << data.filter_quality;
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
|
||||
@ -21,6 +21,7 @@ class MockTexture : public Texture {
|
||||
SkRect bounds;
|
||||
bool freeze;
|
||||
GrContext* context;
|
||||
SkFilterQuality filter_quality;
|
||||
};
|
||||
|
||||
explicit MockTexture(int64_t textureId);
|
||||
@ -29,7 +30,8 @@ class MockTexture : public Texture {
|
||||
void Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) override;
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) override;
|
||||
|
||||
void OnGrContextCreated() override { gr_context_created_ = true; }
|
||||
void OnGrContextDestroyed() override { gr_context_destroyed_ = true; }
|
||||
|
||||
@ -30,12 +30,30 @@ TEST(MockTextureTest, PaintCalls) {
|
||||
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},
|
||||
MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr}};
|
||||
std::vector{MockTexture::PaintCall{canvas, paint_bounds1, false, nullptr,
|
||||
kNone_SkFilterQuality},
|
||||
MockTexture::PaintCall{canvas, paint_bounds2, true, nullptr,
|
||||
kNone_SkFilterQuality}};
|
||||
auto texture = std::make_shared<MockTexture>(0);
|
||||
|
||||
texture->Paint(canvas, paint_bounds1, false, nullptr);
|
||||
texture->Paint(canvas, paint_bounds2, true, nullptr);
|
||||
texture->Paint(canvas, paint_bounds1, false, nullptr, kNone_SkFilterQuality);
|
||||
texture->Paint(canvas, paint_bounds2, true, nullptr, kNone_SkFilterQuality);
|
||||
EXPECT_EQ(texture->paint_calls(), expected_paint_calls);
|
||||
}
|
||||
|
||||
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}};
|
||||
auto texture = std::make_shared<MockTexture>(0);
|
||||
|
||||
texture->Paint(canvas, paint_bounds1, false, nullptr, kLow_SkFilterQuality);
|
||||
texture->Paint(canvas, paint_bounds2, true, nullptr, kLow_SkFilterQuality);
|
||||
EXPECT_EQ(texture->paint_calls(), expected_paint_calls);
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,8 @@ class Texture {
|
||||
virtual void Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) = 0;
|
||||
GrContext* context,
|
||||
SkFilterQuality quality) = 0;
|
||||
|
||||
// Called from raster thread.
|
||||
virtual void OnGrContextCreated() = 0;
|
||||
|
||||
@ -699,18 +699,19 @@ class SceneBuilder extends NativeFieldWrapperClass2 {
|
||||
/// texture just before resizing the Android view and un-freezes it when it is
|
||||
/// certain that a frame with the new size is ready.
|
||||
void addTexture(
|
||||
int textureId, {
|
||||
Offset offset = Offset.zero,
|
||||
double width = 0.0,
|
||||
double height = 0.0,
|
||||
bool freeze = false,
|
||||
int/*!*/ textureId, {
|
||||
Offset/*!*/ offset = Offset.zero,
|
||||
double/*!*/ width = 0.0,
|
||||
double/*!*/ height = 0.0,
|
||||
bool/*!*/ freeze = false,
|
||||
FilterQuality/*!*/ filterQuality = FilterQuality.low,
|
||||
}) {
|
||||
assert(offset != null, 'Offset argument was null'); // ignore: unnecessary_null_comparison
|
||||
_addTexture(offset.dx, offset.dy, width, height, textureId, freeze);
|
||||
_addTexture(offset.dx, offset.dy, width, height, textureId, freeze, filterQuality.index);
|
||||
}
|
||||
|
||||
void _addTexture(double dx, double dy, double width, double height, int textureId, bool freeze)
|
||||
native 'SceneBuilder_addTexture';
|
||||
void _addTexture(double dx, double dy, double width, double height, int textureId, bool freeze,
|
||||
int filterQuality) native 'SceneBuilder_addTexture';
|
||||
|
||||
/// Adds a platform view (e.g an iOS UIView) to the scene.
|
||||
///
|
||||
|
||||
@ -229,9 +229,11 @@ void SceneBuilder::addTexture(double dx,
|
||||
double width,
|
||||
double height,
|
||||
int64_t textureId,
|
||||
bool freeze) {
|
||||
bool freeze,
|
||||
int filterQuality) {
|
||||
auto layer = std::make_unique<flutter::TextureLayer>(
|
||||
SkPoint::Make(dx, dy), SkSize::Make(width, height), textureId, freeze);
|
||||
SkPoint::Make(dx, dy), SkSize::Make(width, height), textureId, freeze,
|
||||
static_cast<SkFilterQuality>(filterQuality));
|
||||
AddLayer(std::move(layer));
|
||||
}
|
||||
|
||||
|
||||
@ -92,7 +92,8 @@ class SceneBuilder : public RefCountedDartWrappable<SceneBuilder> {
|
||||
double width,
|
||||
double height,
|
||||
int64_t textureId,
|
||||
bool freeze);
|
||||
bool freeze,
|
||||
int filterQuality);
|
||||
|
||||
void addPlatformView(double dx,
|
||||
double dy,
|
||||
|
||||
@ -67,6 +67,7 @@ class LayerSceneBuilder implements ui.SceneBuilder {
|
||||
double width = 0.0,
|
||||
double height = 0.0,
|
||||
bool freeze = false,
|
||||
ui.FilterQuality filterQuality = ui.FilterQuality.low,
|
||||
}) {
|
||||
// TODO(b/128315641): implement addTexture.
|
||||
}
|
||||
|
||||
@ -377,13 +377,14 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
|
||||
double width = 0.0,
|
||||
double height = 0.0,
|
||||
bool freeze = false,
|
||||
ui.FilterQuality filterQuality = ui.FilterQuality.low,
|
||||
}) {
|
||||
assert(offset != null, 'Offset argument was null');
|
||||
_addTexture(offset.dx, offset.dy, width, height, textureId);
|
||||
_addTexture(offset.dx, offset.dy, width, height, textureId, filterQuality.index);
|
||||
}
|
||||
|
||||
void _addTexture(
|
||||
double dx, double dy, double width, double height, int textureId) {
|
||||
double dx, double dy, double width, double height, int textureId, int filterQuality) {
|
||||
// In test mode, allow this to be a no-op.
|
||||
if (!ui.debugEmulateFlutterTesterEnvironment) {
|
||||
throw UnimplementedError('Textures are not supported in Flutter Web');
|
||||
|
||||
@ -327,6 +327,7 @@ abstract class SceneBuilder {
|
||||
double width = 0.0,
|
||||
double height = 0.0,
|
||||
bool freeze = false,
|
||||
FilterQuality filterQuality = FilterQuality.low,
|
||||
});
|
||||
|
||||
/// Adds a platform view (e.g an iOS UIView) to the scene.
|
||||
|
||||
@ -937,7 +937,8 @@ class MockTexture : public Texture {
|
||||
void Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) override {}
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) override {}
|
||||
|
||||
void OnGrContextCreated() override {}
|
||||
|
||||
|
||||
@ -36,7 +36,8 @@ void AndroidExternalTextureGL::MarkNewFrameAvailable() {
|
||||
void AndroidExternalTextureGL::Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) {
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) {
|
||||
if (state_ == AttachmentState::detached) {
|
||||
return;
|
||||
}
|
||||
@ -67,7 +68,9 @@ void AndroidExternalTextureGL::Paint(SkCanvas& canvas,
|
||||
transformAroundCenter.postTranslate(0.5, 0.5);
|
||||
canvas.concat(transformAroundCenter);
|
||||
}
|
||||
canvas.drawImage(image, 0, 0);
|
||||
SkPaint paint;
|
||||
paint.setFilterQuality(filter_quality);
|
||||
canvas.drawImage(image, 0, 0, &paint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,8 @@ class AndroidExternalTextureGL : public flutter::Texture {
|
||||
void Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) override;
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) override;
|
||||
|
||||
void OnGrContextCreated() override;
|
||||
|
||||
|
||||
@ -27,7 +27,11 @@ class IOSExternalTextureGL final : public Texture {
|
||||
fml::CFRef<CVPixelBufferRef> buffer_ref_;
|
||||
|
||||
// |Texture|
|
||||
void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrContext* context) override;
|
||||
void Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) override;
|
||||
|
||||
// |Texture|
|
||||
void OnGrContextCreated() override;
|
||||
|
||||
@ -63,7 +63,8 @@ bool IOSExternalTextureGL::NeedUpdateTexture(bool freeze) {
|
||||
void IOSExternalTextureGL::Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) {
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) {
|
||||
EnsureTextureCacheExists();
|
||||
if (NeedUpdateTexture(freeze)) {
|
||||
auto pixelBuffer = [external_texture_.get() copyPixelBuffer];
|
||||
@ -84,7 +85,9 @@ void IOSExternalTextureGL::Paint(SkCanvas& canvas,
|
||||
kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
|
||||
FML_DCHECK(image) << "Failed to create SkImage from Texture.";
|
||||
if (image) {
|
||||
canvas.drawImage(image, bounds.x(), bounds.y());
|
||||
SkPaint paint;
|
||||
paint.setFilterQuality(filter_quality);
|
||||
canvas.drawImage(image, bounds.x(), bounds.y(), &paint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,11 @@ class IOSExternalTextureMetal final : public Texture {
|
||||
sk_sp<SkImage> external_image_;
|
||||
|
||||
// |Texture|
|
||||
void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrContext* context) override;
|
||||
void Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) override;
|
||||
|
||||
// |Texture|
|
||||
void OnGrContextCreated() override;
|
||||
|
||||
@ -26,7 +26,8 @@ IOSExternalTextureMetal::~IOSExternalTextureMetal() = default;
|
||||
void IOSExternalTextureMetal::Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) {
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) {
|
||||
const bool needs_updated_texture = (!freeze && texture_frame_available_) || !external_image_;
|
||||
|
||||
if (needs_updated_texture) {
|
||||
@ -45,10 +46,12 @@ 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
|
||||
nullptr, // paint
|
||||
&paint, // paint
|
||||
SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint // constraint
|
||||
);
|
||||
}
|
||||
|
||||
@ -21,7 +21,8 @@ EmbedderExternalTextureGL::~EmbedderExternalTextureGL() = default;
|
||||
void EmbedderExternalTextureGL::Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) {
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) {
|
||||
if (auto image = external_texture_callback_(
|
||||
Id(), //
|
||||
canvas.getGrContext(), //
|
||||
@ -31,10 +32,12 @@ 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, nullptr);
|
||||
canvas.drawImageRect(last_image_, bounds, &paint);
|
||||
} else {
|
||||
canvas.drawImage(last_image_, bounds.x(), bounds.y());
|
||||
canvas.drawImage(last_image_, bounds.x(), bounds.y(), &paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,8 @@ class EmbedderExternalTextureGL : public flutter::Texture {
|
||||
void Paint(SkCanvas& canvas,
|
||||
const SkRect& bounds,
|
||||
bool freeze,
|
||||
GrContext* context) override;
|
||||
GrContext* context,
|
||||
SkFilterQuality filter_quality) override;
|
||||
|
||||
// |flutter::Texture|
|
||||
void OnGrContextCreated() override;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user