blob: 4a0b8e6ecde7f715e70d12dcdeab55a5882c1ee5 [file]
/*
* Copyright 2026 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkColor.h"
#include "include/core/SkYUVAInfo.h"
#include "src/core/SkYUVAInfoLocation.h" // IWYU pragma: keep
#include "tests/Test.h"
#if defined(SK_ENABLE_YUVA_PACKED_422)
#include "include/codec/SkEncodedOrigin.h"
#endif
using PlaneConfig = SkYUVAInfo::PlaneConfig;
using Subsampling = SkYUVAInfo::Subsampling;
#if defined(SK_ENABLE_YUVA_PACKED_422)
DEF_TEST(SkYUVAInfoPackedYUYV, reporter) {
// The packed 4:2:2 config is only compatible with horizontal 2:1
// subsampling.
for (Subsampling s : {Subsampling::kUnknown, Subsampling::k444, Subsampling::k422,
Subsampling::k420, Subsampling::k440, Subsampling::k411,
Subsampling::k410}) {
SkYUVAInfo info({1920, 1080}, PlaneConfig::kYUYV, s, kRec601_SkYUVColorSpace);
REPORTER_ASSERT(reporter, info.isValid() == (s == Subsampling::k422));
}
SkYUVAInfo info({1920, 1080}, PlaneConfig::kYUYV, Subsampling::k422,
kRec601_SkYUVColorSpace);
REPORTER_ASSERT(reporter, info.isValid());
REPORTER_ASSERT(reporter, info.numPlanes() == 1);
REPORTER_ASSERT(reporter, !info.hasAlpha());
REPORTER_ASSERT(reporter, info.numChannelsInPlane(0) == 2);
REPORTER_ASSERT(reporter, info.numChannelsInPlane(1) == 0);
// The packed plane has one 2-channel texel per pixel, stored at full image
// texel dimensions even though its chroma is subsampled horizontally.
SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
REPORTER_ASSERT(reporter, info.planeDimensions(planeDimensions) == 1);
REPORTER_ASSERT(reporter, planeDimensions[0] == SkISize::Make(1920, 1080));
// The plane is sampled at full rate; the packed extraction happens in the
// YUV effect.
auto [ssx, ssy] = info.planeSubsamplingFactors(0);
REPORTER_ASSERT(reporter, ssx == 1 && ssy == 1);
// Odd plane memory widths are not representable (the last pixel would be
// unpaired) and are rejected by the constructor.
SkYUVAInfo odd({1919, 1080}, PlaneConfig::kYUYV, Subsampling::k422,
kRec601_SkYUVColorSpace);
REPORTER_ASSERT(reporter, !odd.isValid());
// Origins that swap width and height store the plane rotated 90°, so the
// plane's memory width is the image height.
SkYUVAInfo swappedOk({1919, 1080}, PlaneConfig::kYUYV, Subsampling::k422,
kRec601_SkYUVColorSpace, kLeftBottom_SkEncodedOrigin);
REPORTER_ASSERT(reporter, swappedOk.isValid());
REPORTER_ASSERT(reporter, swappedOk.planeDimensions(planeDimensions) == 1);
REPORTER_ASSERT(reporter, planeDimensions[0] == SkISize::Make(1080, 1919));
SkYUVAInfo swappedOdd({1080, 1919}, PlaneConfig::kYUYV, Subsampling::k422,
kRec601_SkYUVColorSpace, kLeftBottom_SkEncodedOrigin);
REPORTER_ASSERT(reporter, !swappedOdd.isValid());
// Channel locations: Y is channel 0 (R) of the pixel's own texel, U and V
// are both channel 1 (G) of the pair's two texels; there is no alpha.
const uint32_t rgFlags = kRG_SkColorChannelFlags;
auto locations = info.toYUVALocations(&rgFlags);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kY].fPlane == 0);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kY].fChannel ==
SkColorChannel::kR);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kU].fPlane == 0);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kU].fChannel ==
SkColorChannel::kG);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kV].fPlane == 0);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kV].fChannel ==
SkColorChannel::kG);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kA].fPlane < 0);
}
#endif // SK_ENABLE_YUVA_PACKED_422
DEF_TEST(SkYUVAInfoSinglePlaneConfigs, reporter) {
// Y410 is modeled as kUYVA with k444 subsampling: plane channel order is
// U, Y, V, A in R, G, B, A.
SkYUVAInfo y410({1920, 1080}, PlaneConfig::kUYVA, Subsampling::k444,
kRec709_SkYUVColorSpace);
REPORTER_ASSERT(reporter, y410.isValid());
REPORTER_ASSERT(reporter, y410.numPlanes() == 1);
REPORTER_ASSERT(reporter, y410.hasAlpha());
REPORTER_ASSERT(reporter, y410.numChannelsInPlane(0) == 4);
const uint32_t rgbaFlags = kRGBA_SkColorChannelFlags;
auto locations = y410.toYUVALocations(&rgbaFlags);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kY].fChannel ==
SkColorChannel::kG);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kU].fChannel ==
SkColorChannel::kR);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kV].fChannel ==
SkColorChannel::kB);
REPORTER_ASSERT(reporter, locations[SkYUVAInfo::YUVAChannels::kA].fChannel ==
SkColorChannel::kA);
}