Ensure raster cache doesn't bleed over to adjacent physical pixels (flutter/engine#35602)

* Ensure raster cache doesn't bleed over to adjacent physical pixels

* Only clip if necessary

* Add tests

* Reformat

* Only save/restore when necessary
This commit is contained in:
Matej Knopp 2022-08-22 19:23:38 +02:00 committed by GitHub
parent 02c259c246
commit 5ab1c6712d
2 changed files with 77 additions and 0 deletions

View File

@ -47,8 +47,23 @@ void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const {
#endif
canvas.resetMatrix();
flow_.Step();
bool exceeds_bounds = bounds.fLeft + image_->dimensions().width() >
SkScalarCeilToScalar(bounds.fRight) ||
bounds.fTop + image_->dimensions().height() >
SkScalarCeilToScalar(bounds.fBottom);
// Make sure raster cache doesn't bleed to physical pixels outside of
// original bounds. https://github.com/flutter/flutter/issues/110002
if (exceeds_bounds) {
canvas.save();
canvas.clipRect(SkRect::Make(bounds.roundOut()));
}
canvas.drawImage(image_, bounds.fLeft, bounds.fTop, SkSamplingOptions(),
paint);
if (exceeds_bounds) {
canvas.restore();
}
}
RasterCache::RasterCache(size_t access_threshold,

View File

@ -787,5 +787,67 @@ TEST_F(RasterCacheTest, RasterCacheKeyID_LayerChildrenIds) {
ASSERT_EQ(ids, expected_ids);
}
TEST_F(RasterCacheTest, RasterCacheBleedingNoClipNeeded) {
SkImageInfo info =
SkImageInfo::MakeN32(40, 40, SkAlphaType::kOpaque_SkAlphaType);
auto image = SkImage::MakeRasterData(
info, SkData::MakeUninitialized(40 * 40 * 4), 40 * 4);
auto canvas = MockCanvas();
canvas.setMatrix(SkMatrix::Scale(2, 2));
// Drawing cached image does not exceeds physical pixels of the original
// bounds and does not need to be clipped.
auto cache_result =
RasterCacheResult(image, SkRect::MakeXYWH(100.3, 100.3, 20, 20), "");
auto paint = SkPaint();
cache_result.draw(canvas, &paint);
EXPECT_EQ(canvas.draw_calls(),
std::vector({
MockCanvas::DrawCall{
0, MockCanvas::SetMatrixData{SkM44::Scale(2, 2)}},
MockCanvas::DrawCall{0, MockCanvas::SaveData{1}},
MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}},
MockCanvas::DrawCall{
1, MockCanvas::DrawImageData{image, 200.6, 200.6,
SkSamplingOptions(), paint}},
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}},
}));
}
TEST_F(RasterCacheTest, RasterCacheBleedingClipNeeded) {
SkImageInfo info =
SkImageInfo::MakeN32(40, 40, SkAlphaType::kOpaque_SkAlphaType);
auto image = SkImage::MakeRasterData(
info, SkData::MakeUninitialized(40 * 40 * 4), 40 * 4);
auto canvas = MockCanvas();
canvas.setMatrix(SkMatrix::Scale(2, 2));
auto cache_result =
RasterCacheResult(image, SkRect::MakeXYWH(100.3, 100.3, 19.6, 19.6), "");
auto paint = SkPaint();
cache_result.draw(canvas, &paint);
EXPECT_EQ(
canvas.draw_calls(),
std::vector({
MockCanvas::DrawCall{0,
MockCanvas::SetMatrixData{SkM44::Scale(2, 2)}},
MockCanvas::DrawCall{0, MockCanvas::SaveData{1}},
MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkM44()}},
MockCanvas::DrawCall{1, MockCanvas::SaveData{2}},
MockCanvas::DrawCall{
2, MockCanvas::ClipRectData{SkRect::MakeLTRB(200, 200, 240, 240),
SkClipOp::kIntersect,
MockCanvas::kHard_ClipEdgeStyle}},
MockCanvas::DrawCall{
2, MockCanvas::DrawImageData{image, 200.6, 200.6,
SkSamplingOptions(), paint}},
MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}},
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}},
}));
}
} // namespace testing
} // namespace flutter