| /* | |
| * 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); | |
| } |