skia2/tests/YUVTest.cpp
Brian Salomon 87d42e5d12 A new way to specify YUVA planar data from SkCodec to SkImage_Lazy
Tunnels through SkImageGenerator as well.

The new SkCodec interface doesn't assume three 8 bit planes.

New SkYUVASpec more clearly defines chroma subsampling and siting of
the planes.

The intent is to use this for other YUVA APIs as well, in particular
SkImage factories in the future.

In this change we convert to the SkYUVASpec to SkYUVASizeInfo
and SkYUVAIndex[4] representation. But the intent is to use
the SkYUVASpec representation throughout the pipeline once
legacy APIs are removed.

orientation GM is replicated to test a variety of chroma
subsampling configs.

Bug: skia:10632

Change-Id: I3fad35752b87cac16c51b24824331f2ae7d458d3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309658
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
Reviewed-by: Leon Scroggins <scroggo@google.com>
2020-08-24 14:25:32 +00:00

170 lines
6.2 KiB
C++

/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/codec/SkCodec.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkStream.h"
#include "include/core/SkYUVASizeInfo.h"
#include "include/private/SkTemplates.h"
#include "src/core/SkAutoMalloc.h"
#include "tests/Test.h"
#include "tools/Resources.h"
static void codec_yuv(skiatest::Reporter* reporter,
const char path[],
const SkYUVAInfo* expectedInfo) {
std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
if (!stream) {
return;
}
std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
REPORTER_ASSERT(reporter, codec);
if (!codec) {
return;
}
// Test queryYUBAInfo()
SkYUVAInfo yuvaInfo;
static_assert(SkYUVAInfo::kMaxPlanes == 4);
SkColorType colorTypes[SkYUVAInfo::kMaxPlanes] = {kUnknown_SkColorType,
kUnknown_SkColorType,
kUnknown_SkColorType,
kUnknown_SkColorType};
size_t rowBytes[SkYUVAInfo::kMaxPlanes] = {};
// All params are required to be non-null.
bool success = codec->queryYUVAInfo(&yuvaInfo, colorTypes, nullptr);
REPORTER_ASSERT(reporter, !success);
success = codec->queryYUVAInfo(&yuvaInfo, nullptr, rowBytes);
REPORTER_ASSERT(reporter, !success);
success = codec->queryYUVAInfo(nullptr, colorTypes, rowBytes);
REPORTER_ASSERT(reporter, !success);
success = codec->queryYUVAInfo(&yuvaInfo, colorTypes, rowBytes);
REPORTER_ASSERT(reporter, SkToBool(expectedInfo) == success);
if (!success) {
return;
}
REPORTER_ASSERT(reporter, *expectedInfo == yuvaInfo);
SkImageInfo ii[SkYUVAInfo::kMaxPlanes];
SkISize planeDims[SkYUVAInfo::kMaxPlanes] = {};
int numPlanes = yuvaInfo.expectedPlaneDims(planeDims);
REPORTER_ASSERT(reporter, numPlanes <= SkYUVAInfo::kMaxPlanes);
size_t totalBytes = 0;
for (int i = 0; i < numPlanes; ++i) {
REPORTER_ASSERT(reporter, !planeDims[i].isEmpty());
REPORTER_ASSERT(reporter, colorTypes[i] != kUnknown_SkColorType);
ii[i] = SkImageInfo::Make(planeDims[i], colorTypes[i], kPremul_SkAlphaType);
REPORTER_ASSERT(reporter, ii[i].validRowBytes(rowBytes[i]));
totalBytes += ii[i].height()*rowBytes[i];
}
for (int i = numPlanes; i < SkYUVAInfo::kMaxPlanes; ++i) {
REPORTER_ASSERT(reporter, planeDims[i].isZero());
REPORTER_ASSERT(reporter, colorTypes[i] == kUnknown_SkColorType);
REPORTER_ASSERT(reporter, rowBytes[i] == 0);
}
// Allocate the memory for the YUV decode.
SkAutoMalloc storage(totalBytes);
SkPixmap planes[SkYUVAInfo::kMaxPlanes];
char* addr = static_cast<char*>(storage.get());
for (int i = 0; i < numPlanes; ++i) {
planes[i].reset(ii[i], addr, rowBytes[i]);
addr += planes[i].height()*rowBytes[i];
}
// Test getYUVAPlanes()
REPORTER_ASSERT(reporter, SkCodec::kInvalidInput == codec->getYUVAPlanes(nullptr));
REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->getYUVAPlanes(planes));
}
DEF_TEST(Jpeg_YUV_Codec, r) {
auto setExpectations = [](SkISize dims, SkYUVAInfo::PlanarConfig planarConfig) {
return SkYUVAInfo(dims,
planarConfig,
kJPEG_Full_SkYUVColorSpace,
kTopLeft_SkEncodedOrigin,
SkYUVAInfo::Siting::kCentered,
SkYUVAInfo::Siting::kCentered);
};
SkYUVAInfo expectations = setExpectations({128, 128}, SkYUVAInfo::PlanarConfig::kY_U_V_420);
codec_yuv(r, "images/color_wheel.jpg", &expectations);
// H2V2
expectations = setExpectations({512, 512}, SkYUVAInfo::PlanarConfig::kY_U_V_420);
codec_yuv(r, "images/mandrill_512_q075.jpg", &expectations);
// H1V1
expectations = setExpectations({512, 512}, SkYUVAInfo::PlanarConfig::kY_U_V_444);
codec_yuv(r, "images/mandrill_h1v1.jpg", &expectations);
// H2V1
expectations = setExpectations({512, 512}, SkYUVAInfo::PlanarConfig::kY_U_V_422);
codec_yuv(r, "images/mandrill_h2v1.jpg", &expectations);
// Non-power of two dimensions
expectations = setExpectations({439, 154}, SkYUVAInfo::PlanarConfig::kY_U_V_420);
codec_yuv(r, "images/cropped_mandrill.jpg", &expectations);
expectations = setExpectations({8, 8}, SkYUVAInfo::PlanarConfig::kY_U_V_420);
codec_yuv(r, "images/randPixels.jpg", &expectations);
// Progressive images
expectations = setExpectations({512, 512}, SkYUVAInfo::PlanarConfig::kY_U_V_444);
codec_yuv(r, "images/brickwork-texture.jpg", &expectations);
codec_yuv(r, "images/brickwork_normal-map.jpg", &expectations);
// A CMYK encoded image should fail.
codec_yuv(r, "images/CMYK.jpg", nullptr);
// A grayscale encoded image should fail.
codec_yuv(r, "images/grayscale.jpg", nullptr);
// A PNG should fail.
codec_yuv(r, "images/arrow.png", nullptr);
}
#include "include/effects/SkColorMatrix.h"
#include "src/core/SkYUVMath.h"
// Be sure that the two matrices are inverses of each other
// (i.e. rgb2yuv and yuv2rgb
DEF_TEST(YUVMath, reporter) {
const SkYUVColorSpace spaces[] = {
kJPEG_SkYUVColorSpace,
kRec601_SkYUVColorSpace,
kRec709_SkYUVColorSpace,
kBT2020_SkYUVColorSpace,
kIdentity_SkYUVColorSpace,
};
// Not sure what the theoretical precision we can hope for is, so pick a big value that
// passes (when I think we're correct).
const float tolerance = 1.0f/(1 << 18);
for (auto cs : spaces) {
float r2y[20], y2r[20];
SkColorMatrix_RGB2YUV(cs, r2y);
SkColorMatrix_YUV2RGB(cs, y2r);
SkColorMatrix r2ym, y2rm;
r2ym.setRowMajor(r2y);
y2rm.setRowMajor(y2r);
r2ym.postConcat(y2rm);
float tmp[20];
r2ym.getRowMajor(tmp);
for (int i = 0; i < 20; ++i) {
float expected = 0;
if (i % 6 == 0) { // diagonal
expected = 1;
}
REPORTER_ASSERT(reporter, SkScalarNearlyEqual(tmp[i], expected, tolerance));
}
}
}