Commit 22541caf authored by Dale Curtis's avatar Dale Curtis Committed by Commit Bot

Implement support for 422 and 444 YUV decoding.

Mostly just worked already, the way that MIPS scaling was implemented
was unnecessarily over-complicated though. Simply always using the Y
plane size when scaling yields the same results.

This also fixes some typos in the AVIF test file names as well as adds
a README.md file for the ones I've updated. Some of which actually
needed to be recreated since they were incorrectly labeled.

Fixed: 915972
Test: Updated unittests.

Change-Id: I7582a41f8dce6af21aaa1b1d2dda182f640297d1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2318056Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Auto-Submit: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#792547}
parent 72ac6b34
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <stddef.h> #include <stddef.h>
#include <string> #include <string>
#include <utility>
#include "base/containers/span.h" #include "base/containers/span.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
...@@ -99,9 +100,29 @@ PaintImage CreatePaintWorkletPaintImage( ...@@ -99,9 +100,29 @@ PaintImage CreatePaintWorkletPaintImage(
return paint_image; return paint_image;
} }
SkYUVASizeInfo GetYUV420SizeInfo(const gfx::Size& image_size, bool has_alpha) { SkYUVASizeInfo GetYUVASizeInfo(const gfx::Size& image_size,
const SkISize uv_size = SkISize::Make((image_size.width() + 1) / 2, YUVSubsampling format,
bool has_alpha) {
SkISize uv_size;
switch (format) {
case YUVSubsampling::k420:
// 4:2:0 has half sized width and height.
uv_size = SkISize::Make((image_size.width() + 1) / 2,
(image_size.height() + 1) / 2); (image_size.height() + 1) / 2);
break;
case YUVSubsampling::k422:
// 4:2:2 has half sized width.
uv_size =
SkISize::Make((image_size.width() + 1) / 2, image_size.height());
break;
case YUVSubsampling::k444:
// 4:4:4 has the same size for all planes.
uv_size = SkISize::Make(image_size.width(), image_size.height());
break;
default:
NOTREACHED();
return SkYUVASizeInfo();
}
const size_t uv_width = base::checked_cast<size_t>(uv_size.width()); const size_t uv_width = base::checked_cast<size_t>(uv_size.width());
SkYUVASizeInfo yuva_size_info; SkYUVASizeInfo yuva_size_info;
yuva_size_info.fSizes[SkYUVAIndex::kY_Index].set(image_size.width(), yuva_size_info.fSizes[SkYUVAIndex::kY_Index].set(image_size.width(),
...@@ -124,12 +145,13 @@ SkYUVASizeInfo GetYUV420SizeInfo(const gfx::Size& image_size, bool has_alpha) { ...@@ -124,12 +145,13 @@ SkYUVASizeInfo GetYUV420SizeInfo(const gfx::Size& image_size, bool has_alpha) {
return yuva_size_info; return yuva_size_info;
} }
PaintImage CreateDiscardablePaintImage(const gfx::Size& size, PaintImage CreateDiscardablePaintImage(
const gfx::Size& size,
sk_sp<SkColorSpace> color_space, sk_sp<SkColorSpace> color_space,
bool allocate_encoded_data, bool allocate_encoded_data,
PaintImage::Id id, PaintImage::Id id,
SkColorType color_type, SkColorType color_type,
bool is_yuv) { base::Optional<YUVSubsampling> yuv_format) {
if (!color_space) if (!color_space)
color_space = SkColorSpace::MakeSRGB(); color_space = SkColorSpace::MakeSRGB();
if (id == PaintImage::kInvalidId) if (id == PaintImage::kInvalidId)
...@@ -138,11 +160,9 @@ PaintImage CreateDiscardablePaintImage(const gfx::Size& size, ...@@ -138,11 +160,9 @@ PaintImage CreateDiscardablePaintImage(const gfx::Size& size,
SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), color_type, SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), color_type,
kPremul_SkAlphaType, color_space); kPremul_SkAlphaType, color_space);
sk_sp<FakePaintImageGenerator> generator; sk_sp<FakePaintImageGenerator> generator;
if (is_yuv) { if (yuv_format) {
// TODO(crbug.com/915972): Remove assumption of YUV420 in tests once we
// support other subsamplings.
generator = sk_make_sp<FakePaintImageGenerator>( generator = sk_make_sp<FakePaintImageGenerator>(
info, GetYUV420SizeInfo(size), info, GetYUVASizeInfo(size, *yuv_format),
std::vector<FrameMetadata>{FrameMetadata()}, allocate_encoded_data); std::vector<FrameMetadata>{FrameMetadata()}, allocate_encoded_data);
} else { } else {
generator = sk_make_sp<FakePaintImageGenerator>( generator = sk_make_sp<FakePaintImageGenerator>(
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CC_TEST_SKIA_COMMON_H_ #define CC_TEST_SKIA_COMMON_H_
#include <memory> #include <memory>
#include <vector>
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
...@@ -42,7 +43,8 @@ sk_sp<PaintImageGenerator> CreatePaintImageGenerator(const gfx::Size& size); ...@@ -42,7 +43,8 @@ sk_sp<PaintImageGenerator> CreatePaintImageGenerator(const gfx::Size& size);
PaintImage CreatePaintWorkletPaintImage(scoped_refptr<PaintWorkletInput> input); PaintImage CreatePaintWorkletPaintImage(scoped_refptr<PaintWorkletInput> input);
SkYUVASizeInfo GetYUV420SizeInfo(const gfx::Size& image_size, SkYUVASizeInfo GetYUVASizeInfo(const gfx::Size& image_size,
YUVSubsampling yuv_format,
bool has_alpha = false); bool has_alpha = false);
PaintImage CreateDiscardablePaintImage( PaintImage CreateDiscardablePaintImage(
...@@ -51,7 +53,7 @@ PaintImage CreateDiscardablePaintImage( ...@@ -51,7 +53,7 @@ PaintImage CreateDiscardablePaintImage(
bool allocate_encoded_memory = true, bool allocate_encoded_memory = true,
PaintImage::Id id = PaintImage::kInvalidId, PaintImage::Id id = PaintImage::kInvalidId,
SkColorType color_type = kN32_SkColorType, SkColorType color_type = kN32_SkColorType,
bool is_yuv = false); base::Optional<YUVSubsampling> yuv_format = base::nullopt);
DrawImage CreateDiscardableDrawImage(const gfx::Size& size, DrawImage CreateDiscardableDrawImage(const gfx::Size& size,
sk_sp<SkColorSpace> color_space, sk_sp<SkColorSpace> color_space,
......
...@@ -213,41 +213,6 @@ void SetYuvPixmapsFromSizeInfo(SkPixmap* pixmap_y, ...@@ -213,41 +213,6 @@ void SetYuvPixmapsFromSizeInfo(SkPixmap* pixmap_y,
pixmap_v->reset(v_decode_info, planes[SkYUVAIndex::kV_Index], v_width_bytes); pixmap_v->reset(v_decode_info, planes[SkYUVAIndex::kV_Index], v_width_bytes);
} }
// Helper method to fill in |scaled_u_size| and |scaled_v_size| by computing
// the mip level for each plane given the final raster dimensions and the
// unscaled U and V plane sizes. Also takes in |draw_image| to compute the Y
// plane mip level and DCHECK that the computed mip levels for U and V are
// reasonable.
//
// TODO(crbug.com/915972): Assumes 420 subsampling and DCHECKs the size ratios.
void ComputeMippedUVPlaneSizes(const gfx::Size& target_raster_size,
const gfx::Size& unscaled_u_size,
const gfx::Size& unscaled_v_size,
const DrawImage& draw_image,
gfx::Size* scaled_u_size,
gfx::Size* scaled_v_size) {
DCHECK(scaled_u_size);
DCHECK(scaled_v_size);
DCHECK_EQ(unscaled_u_size, unscaled_v_size);
DCHECK_EQ((draw_image.paint_image().width() + 1) / 2,
unscaled_u_size.width());
const int uv_mip_level =
MipMapUtil::GetLevelForSize(unscaled_u_size, target_raster_size);
// Check that the chroma planes do not shrink *more* than the luma.
// At least for YUV420, they will shrink at most one mip level below luma,
// which avoids blurriness.
DCHECK_GE(uv_mip_level, 0);
if (CalculateUploadScaleMipLevel(draw_image) == 0) {
// If Y is not scaled, then U and V shouldn't be either.
DCHECK_EQ(uv_mip_level, 0);
} else {
DCHECK_EQ(CalculateUploadScaleMipLevel(draw_image) - 1, uv_mip_level);
}
*scaled_u_size = MipMapUtil::GetSizeForLevel(unscaled_u_size, uv_mip_level);
*scaled_v_size = *scaled_u_size;
}
// Estimates the byte size of the decoded data for an image that goes through // Estimates the byte size of the decoded data for an image that goes through
// hardware decode acceleration. The actual byte size is only known once the // hardware decode acceleration. The actual byte size is only known once the
// image is decoded in the service side because different drivers have different // image is decoded in the service side because different drivers have different
...@@ -420,23 +385,21 @@ bool DrawAndScaleImage(const DrawImage& draw_image, ...@@ -420,23 +385,21 @@ bool DrawAndScaleImage(const DrawImage& draw_image,
decode_info, decode_pixmap.writable_addr()); decode_info, decode_pixmap.writable_addr());
const SkImageInfo y_info_scaled = info.makeColorType(kGray_8_SkColorType); const SkImageInfo y_info_scaled = info.makeColorType(kGray_8_SkColorType);
// The target raster dimensions get passed through: const auto& yuva_sizes = yuva_size_info.fSizes;
// |target_pixmap|.info() -> |pixmap|->info() -> |info| -> |y_info_scaled| DCHECK(yuva_sizes[SkYUVAIndex::kU_Index] ==
const gfx::Size target_raster_size(y_info_scaled.width(), yuva_sizes[SkYUVAIndex::kV_Index]);
y_info_scaled.height());
gfx::Size unscaled_u_size(unscaled_pixmap_u.width(), // Always promote scaled images to 4:4:4 to avoid blurriness. By using the
unscaled_pixmap_u.height()); // same dimensions for the UV planes, we can avoid scaling them completely
gfx::Size unscaled_v_size(unscaled_pixmap_v.width(), // or at least avoid scaling the width.
unscaled_pixmap_v.height()); //
gfx::Size scaled_u_size; // E.g., consider an original (100, 100) image scaled to mips level 1 (50%),
gfx::Size scaled_v_size; // the Y plane size will be (50, 50), but unscaled UV planes are already
ComputeMippedUVPlaneSizes(target_raster_size, unscaled_u_size, // (50, 50) for 4:2:0, and (50, 100) for 4:2:2, so leaving them completely
unscaled_v_size, draw_image, &scaled_u_size, // unscaled or only scaling the height for 4:2:2 has superior quality.
&scaled_v_size); SkImageInfo u_info_scaled = y_info_scaled;
const SkImageInfo u_info_scaled = SkImageInfo v_info_scaled = y_info_scaled;
y_info_scaled.makeWH(scaled_u_size.width(), scaled_u_size.height());
const SkImageInfo v_info_scaled =
y_info_scaled.makeWH(scaled_v_size.width(), scaled_v_size.height());
const size_t y_plane_bytes = y_info_scaled.computeMinByteSize(); const size_t y_plane_bytes = y_info_scaled.computeMinByteSize();
const size_t u_plane_bytes = u_info_scaled.computeMinByteSize(); const size_t u_plane_bytes = u_info_scaled.computeMinByteSize();
DCHECK(!SkImageInfo::ByteSizeOverflowed(y_plane_bytes)); DCHECK(!SkImageInfo::ByteSizeOverflowed(y_plane_bytes));
...@@ -2384,19 +2347,6 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image, ...@@ -2384,19 +2347,6 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image,
mode != DecodedDataMode::kCpu && !image_larger_than_max_texture; mode != DecodedDataMode::kCpu && !image_larger_than_max_texture;
// TODO(crbug.com/910276): Change after alpha support. // TODO(crbug.com/910276): Change after alpha support.
if (is_yuv) { if (is_yuv) {
const gfx::Size target_raster_size(image_info.width(), image_info.height());
gfx::Size unscaled_u_size(
target_yuva_size_info.fSizes[SkYUVAIndex::kU_Index].width(),
target_yuva_size_info.fSizes[SkYUVAIndex::kU_Index].height());
gfx::Size unscaled_v_size(
target_yuva_size_info.fSizes[SkYUVAIndex::kV_Index].width(),
target_yuva_size_info.fSizes[SkYUVAIndex::kV_Index].height());
gfx::Size scaled_u_size;
gfx::Size scaled_v_size;
ComputeMippedUVPlaneSizes(target_raster_size, unscaled_u_size,
unscaled_v_size, draw_image, &scaled_u_size,
&scaled_v_size);
size_t y_size_bytes = size_t y_size_bytes =
target_yuva_size_info.fWidthBytes[SkYUVAIndex::kY_Index] * target_yuva_size_info.fWidthBytes[SkYUVAIndex::kY_Index] *
target_yuva_size_info.fSizes[SkYUVAIndex::kY_Index].height(); target_yuva_size_info.fSizes[SkYUVAIndex::kY_Index].height();
......
...@@ -400,9 +400,14 @@ class GpuImageDecodeCacheTest ...@@ -400,9 +400,14 @@ class GpuImageDecodeCacheTest
sk_sp<SkColorSpace> color_space = nullptr, sk_sp<SkColorSpace> color_space = nullptr,
PaintImage::Id id = PaintImage::kInvalidId) { PaintImage::Id id = PaintImage::kInvalidId) {
const bool allocate_encoded_memory = true; const bool allocate_encoded_memory = true;
if (do_yuv_decode_) {
return CreateDiscardablePaintImage(size, color_space, return CreateDiscardablePaintImage(size, color_space,
allocate_encoded_memory, id, color_type_, allocate_encoded_memory, id,
do_yuv_decode_); color_type_, yuv_format_);
}
return CreateDiscardablePaintImage(
size, color_space, allocate_encoded_memory, id, color_type_);
} }
PaintImage CreateLargePaintImageForSoftwareFallback() { PaintImage CreateLargePaintImageForSoftwareFallback() {
...@@ -421,7 +426,7 @@ class GpuImageDecodeCacheTest ...@@ -421,7 +426,7 @@ class GpuImageDecodeCacheTest
sk_sp<FakePaintImageGenerator> generator; sk_sp<FakePaintImageGenerator> generator;
if (do_yuv_decode_) { if (do_yuv_decode_) {
generator = sk_make_sp<FakePaintImageGenerator>( generator = sk_make_sp<FakePaintImageGenerator>(
info, GetYUV420SizeInfo(test_image_size)); info, GetYUVASizeInfo(test_image_size, yuv_format_));
generator->SetExpectFallbackToRGB(); generator->SetExpectFallbackToRGB();
} else { } else {
generator = sk_make_sp<FakePaintImageGenerator>(info); generator = sk_make_sp<FakePaintImageGenerator>(info);
...@@ -471,10 +476,8 @@ class GpuImageDecodeCacheTest ...@@ -471,10 +476,8 @@ class GpuImageDecodeCacheTest
} }
size_t GetBytesNeededForSingleImage(gfx::Size image_dimensions) { size_t GetBytesNeededForSingleImage(gfx::Size image_dimensions) {
// TODO(crbug.com/915972): Assumes YUV 420. if (do_yuv_decode_)
if (do_yuv_decode_) { return GetYUVASizeInfo(image_dimensions, yuv_format_).computeTotalBytes();
return GetYUV420SizeInfo(image_dimensions).computeTotalBytes();
}
const size_t test_image_area_bytes = const size_t test_image_area_bytes =
base::checked_cast<size_t>(image_dimensions.GetArea()); base::checked_cast<size_t>(image_dimensions.GetArea());
base::CheckedNumeric<size_t> bytes_for_rgb_image_safe( base::CheckedNumeric<size_t> bytes_for_rgb_image_safe(
...@@ -583,6 +586,9 @@ class GpuImageDecodeCacheTest ...@@ -583,6 +586,9 @@ class GpuImageDecodeCacheTest
TransferCacheTestHelper transfer_cache_helper_; TransferCacheTestHelper transfer_cache_helper_;
scoped_refptr<GPUImageDecodeTestMockContextProvider> context_provider_; scoped_refptr<GPUImageDecodeTestMockContextProvider> context_provider_;
// Only used when |do_yuv_decode_| is true.
YUVSubsampling yuv_format_ = YUVSubsampling::k420;
bool use_transfer_cache_; bool use_transfer_cache_;
SkColorType color_type_; SkColorType color_type_;
bool do_yuv_decode_; bool do_yuv_decode_;
...@@ -2027,8 +2033,9 @@ TEST_P(GpuImageDecodeCacheTest, CacheDecodesExpectedFrames) { ...@@ -2027,8 +2033,9 @@ TEST_P(GpuImageDecodeCacheTest, CacheDecodesExpectedFrames) {
SkImageInfo::Make(test_image_size.width(), test_image_size.height(), SkImageInfo::Make(test_image_size.width(), test_image_size.height(),
color_type_, kPremul_SkAlphaType); color_type_, kPremul_SkAlphaType);
sk_sp<FakePaintImageGenerator> generator = sk_sp<FakePaintImageGenerator> generator =
do_yuv_decode_ ? sk_make_sp<FakePaintImageGenerator>( do_yuv_decode_
info, GetYUV420SizeInfo(test_image_size), frames) ? sk_make_sp<FakePaintImageGenerator>(
info, GetYUVASizeInfo(test_image_size, yuv_format_), frames)
: sk_make_sp<FakePaintImageGenerator>(info, frames); : sk_make_sp<FakePaintImageGenerator>(info, frames);
PaintImage image = PaintImageBuilder::WithDefault() PaintImage image = PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId()) .set_id(PaintImage::GetNextId())
...@@ -2877,7 +2884,8 @@ TEST_P(GpuImageDecodeCacheTest, ...@@ -2877,7 +2884,8 @@ TEST_P(GpuImageDecodeCacheTest,
// planes. // planes.
return; return;
} }
auto cache = CreateCache(); auto owned_cache = CreateCache();
auto decode_and_check_plane_sizes = [this, cache = owned_cache.get()]() {
SkFilterQuality filter_quality = kMedium_SkFilterQuality; SkFilterQuality filter_quality = kMedium_SkFilterQuality;
SkSize requires_decode_at_original_scale = SkSize::Make(0.8f, 0.8f); SkSize requires_decode_at_original_scale = SkSize::Make(0.8f, 0.8f);
...@@ -2886,8 +2894,8 @@ TEST_P(GpuImageDecodeCacheTest, ...@@ -2886,8 +2894,8 @@ TEST_P(GpuImageDecodeCacheTest,
filter_quality, filter_quality,
CreateMatrix(requires_decode_at_original_scale), CreateMatrix(requires_decode_at_original_scale),
PaintImage::kDefaultFrameIndex, DefaultColorSpace()); PaintImage::kDefaultFrameIndex, DefaultColorSpace());
ImageDecodeCache::TaskResult result = ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(
cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); draw_image, ImageDecodeCache::TracingInfo());
EXPECT_TRUE(result.need_unref); EXPECT_TRUE(result.need_unref);
EXPECT_TRUE(result.task); EXPECT_TRUE(result.task);
...@@ -2911,15 +2919,25 @@ TEST_P(GpuImageDecodeCacheTest, ...@@ -2911,15 +2919,25 @@ TEST_P(GpuImageDecodeCacheTest,
// Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, we // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, we
// must separately request mips for each plane and compare to the original // must separately request mips for each plane and compare to the original
// uploaded planes. // uploaded planes.
CompareAllPlanesToMippedVersions(cache.get(), draw_image, CompareAllPlanesToMippedVersions(cache, draw_image, transfer_cache_entry_id,
transfer_cache_entry_id,
true /* should_have_mips */); true /* should_have_mips */);
SkYUVASizeInfo yuv_size_info = GetYUV420SizeInfo(GetNormalImageSize()); SkYUVASizeInfo yuv_size_info =
VerifyUploadedPlaneSizes(cache.get(), draw_image, transfer_cache_entry_id, GetYUVASizeInfo(GetNormalImageSize(), yuv_format_);
VerifyUploadedPlaneSizes(cache, draw_image, transfer_cache_entry_id,
yuv_size_info.fSizes); yuv_size_info.fSizes);
cache->DrawWithImageFinished(draw_image, decoded_draw_image); cache->DrawWithImageFinished(draw_image, decoded_draw_image);
cache->UnrefImage(draw_image); cache->UnrefImage(draw_image);
};
yuv_format_ = YUVSubsampling::k420;
decode_and_check_plane_sizes();
yuv_format_ = YUVSubsampling::k422;
decode_and_check_plane_sizes();
yuv_format_ = YUVSubsampling::k444;
decode_and_check_plane_sizes();
} }
TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) { TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) {
...@@ -2933,17 +2951,21 @@ TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) { ...@@ -2933,17 +2951,21 @@ TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) {
// planes. // planes.
return; return;
} }
auto cache = CreateCache(); auto owned_cache = CreateCache();
auto decode_and_check_plane_sizes =
[this, cache = owned_cache.get()](
SkSize scaled_size,
const SkISize mipped_plane_sizes[SkYUVASizeInfo::kMaxCount]) {
SkFilterQuality filter_quality = kMedium_SkFilterQuality; SkFilterQuality filter_quality = kMedium_SkFilterQuality;
SkSize less_than_half_scale = SkSize::Make(0.45f, 0.45f);
gfx::Size image_size = GetNormalImageSize(); gfx::Size image_size = GetNormalImageSize();
PaintImage image = CreatePaintImageInternal(image_size); PaintImage image = CreatePaintImageInternal(image_size);
DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), DrawImage draw_image(
filter_quality, CreateMatrix(less_than_half_scale), image, SkIRect::MakeWH(image.width(), image.height()),
filter_quality, CreateMatrix(scaled_size),
PaintImage::kDefaultFrameIndex, DefaultColorSpace()); PaintImage::kDefaultFrameIndex, DefaultColorSpace());
ImageDecodeCache::TaskResult result = ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(
cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); draw_image, ImageDecodeCache::TracingInfo());
EXPECT_TRUE(result.need_unref); EXPECT_TRUE(result.need_unref);
EXPECT_TRUE(result.task); EXPECT_TRUE(result.task);
...@@ -2952,9 +2974,10 @@ TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) { ...@@ -2952,9 +2974,10 @@ TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) {
// Must hold context lock before calling GetDecodedImageForDraw / // Must hold context lock before calling GetDecodedImageForDraw /
// DrawWithImageFinished. // DrawWithImageFinished.
viz::ContextProvider::ScopedContextLock context_lock(context_provider()); viz::ContextProvider::ScopedContextLock context_lock(
// Pull out transfer cache ID from the DecodedDrawImage while it still has context_provider());
// it attached. // Pull out transfer cache ID from the DecodedDrawImage while it still
// has it attached.
DecodedDrawImage serialized_decoded_draw_image = DecodedDrawImage serialized_decoded_draw_image =
cache->GetDecodedImageForDraw(draw_image); cache->GetDecodedImageForDraw(draw_image);
const base::Optional<uint32_t> transfer_cache_entry_id = const base::Optional<uint32_t> transfer_cache_entry_id =
...@@ -2964,30 +2987,72 @@ TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) { ...@@ -2964,30 +2987,72 @@ TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) {
EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image());
EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
// Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, we // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus,
// must separately request mips for each plane and compare to the original // we must separately request mips for each plane and compare to the
// uploaded planes. // original uploaded planes.
CompareAllPlanesToMippedVersions(cache.get(), draw_image, CompareAllPlanesToMippedVersions(cache, draw_image,
transfer_cache_entry_id, transfer_cache_entry_id,
true /* should_have_mips */); true /* should_have_mips */);
VerifyUploadedPlaneSizes(cache, draw_image, transfer_cache_entry_id,
mipped_plane_sizes);
// Because we intend to draw this image at 0.45 x 0.45 scale, we will upload cache->DrawWithImageFinished(draw_image, decoded_draw_image);
// the Y plane at mip level 1 (corresponding to half the original size). The cache->UnrefImage(draw_image);
// chroma planes (U and V) should be uploaded at the same size as the Y plane, };
// corresponding to mip level 0, because the largest dimensions greater than
// or equal to target dimensions for them is their original size. gfx::Size image_size = GetNormalImageSize();
SkISize mipped_plane_sizes[SkYUVASizeInfo::kMaxCount]; SkISize mipped_plane_sizes[SkYUVASizeInfo::kMaxCount];
SkSize less_than_half_scale = SkSize::Make(0.45f, 0.45f);
// Because we intend to draw this image at 0.45 x 0.45 scale, we will upload
// the Y plane at mip level 1 (corresponding to 1/2 the original size).
mipped_plane_sizes[SkYUVAIndex::kY_Index] = SkISize::Make( mipped_plane_sizes[SkYUVAIndex::kY_Index] = SkISize::Make(
(image_size.width() + 1) / 2, (image_size.height() + 1) / 2); (image_size.width() + 1) / 2, (image_size.height() + 1) / 2);
mipped_plane_sizes[SkYUVAIndex::kU_Index] = mipped_plane_sizes[SkYUVAIndex::kU_Index] =
mipped_plane_sizes[SkYUVAIndex::kY_Index]; mipped_plane_sizes[SkYUVAIndex::kY_Index];
mipped_plane_sizes[SkYUVAIndex::kV_Index] = mipped_plane_sizes[SkYUVAIndex::kV_Index] =
mipped_plane_sizes[SkYUVAIndex::kY_Index]; mipped_plane_sizes[SkYUVAIndex::kY_Index];
VerifyUploadedPlaneSizes(cache.get(), draw_image, transfer_cache_entry_id,
mipped_plane_sizes);
cache->DrawWithImageFinished(draw_image, decoded_draw_image); // For 4:2:0, the chroma planes (U and V) should be uploaded at the same size
cache->UnrefImage(draw_image); // as the Y plane since they get promoted to 4:4:4 to avoid blurriness from
// scaling.
yuv_format_ = YUVSubsampling::k420;
decode_and_check_plane_sizes(less_than_half_scale, mipped_plane_sizes);
// For 4:2:2, only the UV height plane should be scaled.
yuv_format_ = YUVSubsampling::k422;
decode_and_check_plane_sizes(less_than_half_scale, mipped_plane_sizes);
// For 4:4:4, all planes should be the same size.
yuv_format_ = YUVSubsampling::k444;
decode_and_check_plane_sizes(less_than_half_scale, mipped_plane_sizes);
// Now try at 1/4 scale.
SkSize one_quarter_scale = SkSize::Make(0.20f, 0.20f);
// Because we intend to draw this image at 0.20 x 0.20 scale, we will upload
// the Y plane at mip level 2 (corresponding to 1/4 the original size).
mipped_plane_sizes[SkYUVAIndex::kY_Index] = SkISize::Make(
(image_size.width() + 1) / 4, (image_size.height() + 1) / 4);
mipped_plane_sizes[SkYUVAIndex::kU_Index] =
mipped_plane_sizes[SkYUVAIndex::kY_Index];
mipped_plane_sizes[SkYUVAIndex::kV_Index] =
mipped_plane_sizes[SkYUVAIndex::kY_Index];
// For 4:2:0, the chroma planes (U and V) should be uploaded at the same size
// as the Y plane since they get promoted to 4:4:4 to avoid blurriness from
// scaling.
yuv_format_ = YUVSubsampling::k420;
decode_and_check_plane_sizes(one_quarter_scale, mipped_plane_sizes);
// For 4:2:2, only the UV height plane should be scaled.
yuv_format_ = YUVSubsampling::k422;
decode_and_check_plane_sizes(one_quarter_scale, mipped_plane_sizes);
// For 4:4:4, all planes should be the same size.
yuv_format_ = YUVSubsampling::k444;
decode_and_check_plane_sizes(one_quarter_scale, mipped_plane_sizes);
} }
TEST_P(GpuImageDecodeCacheTest, GetBorderlineLargeDecodedImageForDraw) { TEST_P(GpuImageDecodeCacheTest, GetBorderlineLargeDecodedImageForDraw) {
...@@ -3086,7 +3151,7 @@ class GpuImageDecodeCacheWithAcceleratedDecodesTest ...@@ -3086,7 +3151,7 @@ class GpuImageDecodeCacheWithAcceleratedDecodesTest
sk_sp<FakePaintImageGenerator> generator; sk_sp<FakePaintImageGenerator> generator;
if (do_yuv_decode_) { if (do_yuv_decode_) {
generator = sk_make_sp<FakePaintImageGenerator>( generator = sk_make_sp<FakePaintImageGenerator>(
info, GetYUV420SizeInfo(image_data.image_size)); info, GetYUVASizeInfo(image_data.image_size, yuv_format_));
} else { } else {
generator = sk_make_sp<FakePaintImageGenerator>(info); generator = sk_make_sp<FakePaintImageGenerator>(info);
} }
......
...@@ -642,14 +642,13 @@ bool AVIFImageDecoder::MaybeCreateDemuxer() { ...@@ -642,14 +642,13 @@ bool AVIFImageDecoder::MaybeCreateDemuxer() {
// Determine whether the image can be decoded to YUV. // Determine whether the image can be decoded to YUV.
// * Bit depths higher than 8 are not supported. // * Bit depths higher than 8 are not supported.
// * TODO(crbug.com/915972): Only YUV 4:2:0 subsampling format is supported.
// * Alpha channel is not supported. // * Alpha channel is not supported.
// * Multi-frame images (animations) are not supported. (The DecodeToYUV() // * Multi-frame images (animations) are not supported. (The DecodeToYUV()
// method does not have an 'index' parameter.) // method does not have an 'index' parameter.)
// * If ColorTransform() returns a non-null pointer, the decoder has to do a // * If ColorTransform() returns a non-null pointer, the decoder has to do a
// color space conversion, so we don't decode to YUV. // color space conversion, so we don't decode to YUV.
allow_decode_to_yuv_ = allow_decode_to_yuv_ =
!ImageIsHighBitDepth() && yuv_format == AVIF_PIXEL_FORMAT_YUV420 && !ImageIsHighBitDepth() && yuv_format != AVIF_PIXEL_FORMAT_YUV400 &&
!decoder_->alphaPresent && decoded_frame_count_ == 1 && !decoder_->alphaPresent && decoded_frame_count_ == 1 &&
(yuv_color_space_ = GetSkYUVColorSpace(container)) && !ColorTransform(); (yuv_color_space_ = GetSkYUVColorSpace(container)) && !ColorTransform();
......
...@@ -131,7 +131,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -131,7 +131,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}}, }},
{"/images/resources/avif/red-with-limited-alhpa-8bpc.avif", {"/images/resources/avif/red-with-limited-alpha-8bpc.avif",
8, 8,
ColorType::kRgbA, ColorType::kRgbA,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -143,7 +143,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -143,7 +143,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}}, }},
{"/images/resources/avif/red-full-ranged-8bpc.avif", {"/images/resources/avif/red-full-range-420-8bpc.avif",
8, 8,
ColorType::kRgb, ColorType::kRgb,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -155,7 +155,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -155,7 +155,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}}, }},
{"/images/resources/avif/alpha-mask-limited-ranged-8bpc.avif", {"/images/resources/avif/alpha-mask-limited-range-8bpc.avif",
8, 8,
ColorType::kMono, ColorType::kMono,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -167,7 +167,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -167,7 +167,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)},
}}, }},
{"/images/resources/avif/alpha-mask-full-ranged-8bpc.avif", {"/images/resources/avif/alpha-mask-full-range-8bpc.avif",
8, 8,
ColorType::kMono, ColorType::kMono,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -235,7 +235,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -235,7 +235,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}}, }},
{"/images/resources/avif/red-with-limited-alhpa-10bpc.avif", {"/images/resources/avif/red-with-limited-alpha-10bpc.avif",
10, 10,
ColorType::kRgbA, ColorType::kRgbA,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -259,7 +259,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -259,7 +259,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}}, }},
{"/images/resources/avif/red-full-ranged-10bpc.avif", {"/images/resources/avif/red-full-range-420-10bpc.avif",
10, 10,
ColorType::kRgb, ColorType::kRgb,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -271,7 +271,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -271,7 +271,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}}, }},
{"/images/resources/avif/alpha-mask-limited-ranged-10bpc.avif", {"/images/resources/avif/alpha-mask-limited-range-10bpc.avif",
10, 10,
ColorType::kMono, ColorType::kMono,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -283,7 +283,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -283,7 +283,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)},
}}, }},
{"/images/resources/avif/alpha-mask-full-ranged-10bpc.avif", {"/images/resources/avif/alpha-mask-full-range-10bpc.avif",
10, 10,
ColorType::kMono, ColorType::kMono,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -339,7 +339,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -339,7 +339,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}}, }},
{"/images/resources/avif/red-with-limited-alhpa-12bpc.avif", {"/images/resources/avif/red-with-limited-alpha-12bpc.avif",
12, 12,
ColorType::kRgbA, ColorType::kRgbA,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -363,7 +363,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -363,7 +363,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}}, }},
{"/images/resources/avif/red-full-ranged-12bpc.avif", {"/images/resources/avif/red-full-range-420-12bpc.avif",
12, 12,
ColorType::kRgb, ColorType::kRgb,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -375,7 +375,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -375,7 +375,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
}}, }},
{"/images/resources/avif/alpha-mask-limited-ranged-12bpc.avif", {"/images/resources/avif/alpha-mask-limited-range-12bpc.avif",
12, 12,
ColorType::kMono, ColorType::kMono,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -387,7 +387,7 @@ StaticColorCheckParam kTestParams[] = { ...@@ -387,7 +387,7 @@ StaticColorCheckParam kTestParams[] = {
{gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)},
{gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)},
}}, }},
{"/images/resources/avif/alpha-mask-full-ranged-12bpc.avif", {"/images/resources/avif/alpha-mask-full-range-12bpc.avif",
12, 12,
ColorType::kMono, ColorType::kMono,
ImageDecoder::kLosslessFormat, ImageDecoder::kLosslessFormat,
...@@ -585,9 +585,17 @@ TEST(StaticAVIFTests, ValidImages) { ...@@ -585,9 +585,17 @@ TEST(StaticAVIFTests, ValidImages) {
} }
TEST(StaticAVIFTests, YUV) { TEST(StaticAVIFTests, YUV) {
const char* avif_file = // 3x3, YUV 4:2:0
"/images/resources/avif/red-full-ranged-8bpc.avif"; // 3x3, YUV 4:2:0 ReadYUV("/images/resources/avif/red-limited-range-420-8bpc.avif",
ReadYUV(avif_file, IntSize(3, 3), IntSize(2, 2)); IntSize(3, 3), IntSize(2, 2));
// 3x3, YUV 4:2:2
ReadYUV("/images/resources/avif/red-limited-range-422-8bpc.avif",
IntSize(3, 3), IntSize(2, 3));
// 3x3, YUV 4:4:4
ReadYUV("/images/resources/avif/red-limited-range-444-8bpc.avif",
IntSize(3, 3), IntSize(3, 3));
} }
using StaticAVIFColorTests = ::testing::TestWithParam<StaticColorCheckParam>; using StaticAVIFColorTests = ::testing::TestWithParam<StaticColorCheckParam>;
......
# AVIF Test Files
[TOC]
## Instructions
To add, update or remove a test file, please update the list below.
Please provide full reference and steps to generate the test file so that
any people can regenerate or update the file in the future.
## List of Test Files
### red-(full|limited)-range-(420|422|444)-(8|10|12)bpc.avif
These are all generated from red.png with the appropriate avifenc command line:
Limited:
```
avifenc -r l -d 8 -y 420 -s 0 red.png red-limited-range-420-8bpc.avif
avifenc -r l -d 10 -y 420 -s 0 red.png red-limited-range-420-10bpc.avif
avifenc -r l -d 12 -y 420 -s 0 red.png red-limited-range-420-12bpc.avif
avifenc -r l -d 8 -y 422 -s 0 red.png red-limited-range-422-8bpc.avif
avifenc -r l -d 10 -y 422 -s 0 red.png red-limited-range-422-10bpc.avif
avifenc -r l -d 12 -y 422 -s 0 red.png red-limited-range-422-12bpc.avif
avifenc -r l -d 8 -y 444 -s 0 red.png red-limited-range-444-8bpc.avif
avifenc -r l -d 10 -y 444 -s 0 red.png red-limited-range-444-10bpc.avif
avifenc -r l -d 12 -y 444 -s 0 red.png red-limited-range-444-12bpc.avif
```
Full:
```
avifenc -r f -d 8 -y 420 -s 0 red.png red-full-range-420-8bpc.avif
avifenc -r f -d 10 -y 420 -s 0 red.png red-full-range-420-10bpc.avif
avifenc -r f -d 12 -y 420 -s 0 red.png red-full-range-420-12bpc.avif
avifenc -r f -d 8 -y 422 -s 0 red.png red-full-range-422-8bpc.avif
avifenc -r f -d 10 -y 422 -s 0 red.png red-full-range-422-10bpc.avif
avifenc -r f -d 12 -y 422 -s 0 red.png red-full-range-422-12bpc.avif
avifenc -r f -d 8 -y 444 -s 0 red.png red-full-range-444-8bpc.avif
avifenc -r f -d 10 -y 444 -s 0 red.png red-full-range-444-10bpc.avif
avifenc -r f -d 12 -y 444 -s 0 red.png red-full-range-444-12bpc.avif
```
### TODO(crbug.com/960620): Figure out how the rest of files were generated.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment