未验证 提交 f9eeee31 编写于 作者: Z zljj0818 提交者: GitHub

Poor video scaling quality #53080 (#18814)

Use bilinear instead of nearest filter to draw surface texture

Related Issues:
https://github.com/flutter/flutter/issues/53080

Tests:
- TextureLayerTest
上级 29b739a5
......@@ -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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册