xref: /aosp_15_r20/external/skia/src/core/SkYUVAInfo.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2020 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkYUVAInfo.h"
9 
10 #include "include/core/SkColor.h"
11 #include "src/base/SkSafeMath.h"
12 #include "src/core/SkYUVAInfoLocation.h"
13 
14 #include <algorithm>
15 
is_plane_config_compatible_with_subsampling(SkYUVAInfo::PlaneConfig config,SkYUVAInfo::Subsampling subsampling)16 static bool is_plane_config_compatible_with_subsampling(SkYUVAInfo::PlaneConfig config,
17                                                         SkYUVAInfo::Subsampling subsampling) {
18     if (config      == SkYUVAInfo::PlaneConfig::kUnknown ||
19         subsampling == SkYUVAInfo::Subsampling::kUnknown) {
20         return false;
21     }
22     return subsampling == SkYUVAInfo::Subsampling::k444 ||
23            (config != SkYUVAInfo::PlaneConfig::kYUV  &&
24             config != SkYUVAInfo::PlaneConfig::kYUVA &&
25             config != SkYUVAInfo::PlaneConfig::kUYV  &&
26             config != SkYUVAInfo::PlaneConfig::kUYVA);
27 }
28 
SubsamplingFactors(Subsampling subsampling)29 std::tuple<int, int> SkYUVAInfo::SubsamplingFactors(Subsampling subsampling) {
30     switch (subsampling) {
31         case Subsampling::kUnknown: return {0, 0};
32         case Subsampling::k444:     return {1, 1};
33         case Subsampling::k422:     return {2, 1};
34         case Subsampling::k420:     return {2, 2};
35         case Subsampling::k440:     return {1, 2};
36         case Subsampling::k411:     return {4, 1};
37         case Subsampling::k410:     return {4, 2};
38     }
39     SkUNREACHABLE;
40 }
41 
PlaneSubsamplingFactors(PlaneConfig planeConfig,Subsampling subsampling,int planeIdx)42 std::tuple<int, int> SkYUVAInfo::PlaneSubsamplingFactors(PlaneConfig planeConfig,
43                                                          Subsampling subsampling,
44                                                          int planeIdx) {
45     if (!is_plane_config_compatible_with_subsampling(planeConfig, subsampling) ||
46         planeIdx < 0                                                           ||
47         planeIdx > NumPlanes(planeConfig)) {
48         return {0, 0};
49     }
50     bool isSubsampledPlane = false;
51     switch (planeConfig) {
52         case PlaneConfig::kUnknown:     SkUNREACHABLE;
53 
54         case PlaneConfig::kY_U_V:
55         case PlaneConfig::kY_V_U:
56         case PlaneConfig::kY_U_V_A:
57         case PlaneConfig::kY_V_U_A:
58             isSubsampledPlane = planeIdx == 1 || planeIdx == 2;
59             break;
60 
61         case PlaneConfig::kY_UV:
62         case PlaneConfig::kY_VU:
63         case PlaneConfig::kY_UV_A:
64         case PlaneConfig::kY_VU_A:
65             isSubsampledPlane = planeIdx == 1;
66             break;
67 
68         case PlaneConfig::kYUV:
69         case PlaneConfig::kUYV:
70         case PlaneConfig::kYUVA:
71         case PlaneConfig::kUYVA:
72             break;
73     }
74     return isSubsampledPlane ? SubsamplingFactors(subsampling) : std::make_tuple(1, 1);
75 }
76 
PlaneDimensions(SkISize imageDimensions,PlaneConfig planeConfig,Subsampling subsampling,SkEncodedOrigin origin,SkISize planeDimensions[SkYUVAInfo::kMaxPlanes])77 int SkYUVAInfo::PlaneDimensions(SkISize imageDimensions,
78                                 PlaneConfig planeConfig,
79                                 Subsampling subsampling,
80                                 SkEncodedOrigin origin,
81                                 SkISize planeDimensions[SkYUVAInfo::kMaxPlanes]) {
82     std::fill_n(planeDimensions, SkYUVAInfo::kMaxPlanes, SkISize{0, 0});
83     if (!is_plane_config_compatible_with_subsampling(planeConfig, subsampling)) {
84         return 0;
85     }
86 
87     int w = imageDimensions.width();
88     int h = imageDimensions.height();
89     if (origin >= kLeftTop_SkEncodedOrigin) {
90         using std::swap;
91         swap(w, h);
92     }
93     auto down2 = [](int x) { return (x + 1)/2; };
94     auto down4 = [](int x) { return (x + 3)/4; };
95     SkISize uvSize;
96     switch (subsampling) {
97         case Subsampling::kUnknown: SkUNREACHABLE;
98 
99         case Subsampling::k444: uvSize = {      w ,       h }; break;
100         case Subsampling::k422: uvSize = {down2(w),       h }; break;
101         case Subsampling::k420: uvSize = {down2(w), down2(h)}; break;
102         case Subsampling::k440: uvSize = {      w , down2(h)}; break;
103         case Subsampling::k411: uvSize = {down4(w),       h }; break;
104         case Subsampling::k410: uvSize = {down4(w), down2(h)}; break;
105     }
106     switch (planeConfig) {
107         case PlaneConfig::kUnknown: SkUNREACHABLE;
108 
109         case PlaneConfig::kY_U_V:
110         case PlaneConfig::kY_V_U:
111             planeDimensions[0] = {w, h};
112             planeDimensions[1] = planeDimensions[2] = uvSize;
113             return 3;
114 
115         case PlaneConfig::kY_UV:
116         case PlaneConfig::kY_VU:
117             planeDimensions[0] = {w, h};
118             planeDimensions[1] = uvSize;
119             return 2;
120 
121         case PlaneConfig::kY_U_V_A:
122         case PlaneConfig::kY_V_U_A:
123             planeDimensions[0] = planeDimensions[3] = {w, h};
124             planeDimensions[1] = planeDimensions[2] = uvSize;
125             return 4;
126 
127         case PlaneConfig::kY_UV_A:
128         case PlaneConfig::kY_VU_A:
129             planeDimensions[0] = planeDimensions[2] = {w, h};
130             planeDimensions[1] = uvSize;
131             return 3;
132 
133         case PlaneConfig::kYUV:
134         case PlaneConfig::kUYV:
135         case PlaneConfig::kYUVA:
136         case PlaneConfig::kUYVA:
137             planeDimensions[0] = {w, h};
138             SkASSERT(planeDimensions[0] == uvSize);
139             return 1;
140     }
141     SkUNREACHABLE;
142 }
143 
channel_index_to_channel(uint32_t channelFlags,int channelIdx,SkColorChannel * channel)144 static bool channel_index_to_channel(uint32_t channelFlags,
145                                      int channelIdx,
146                                      SkColorChannel* channel) {
147     switch (channelFlags) {
148         case kGray_SkColorChannelFlag:  // For gray returning any of R, G, or B for index 0 is ok.
149         case kRed_SkColorChannelFlag:
150             if (channelIdx == 0) {
151                 *channel = SkColorChannel::kR;
152                 return true;
153             }
154             return false;
155         case kGrayAlpha_SkColorChannelFlags:
156             switch (channelIdx) {
157                 case 0: *channel = SkColorChannel::kR; return true;
158                 case 1: *channel = SkColorChannel::kA; return true;
159 
160                 default: return false;
161             }
162         case kAlpha_SkColorChannelFlag:
163             if (channelIdx == 0) {
164                 *channel = SkColorChannel::kA;
165                 return true;
166             }
167             return false;
168         case kRG_SkColorChannelFlags:
169             if (channelIdx == 0 || channelIdx == 1) {
170                 *channel = static_cast<SkColorChannel>(channelIdx);
171                 return true;
172             }
173             return false;
174         case kRGB_SkColorChannelFlags:
175             if (channelIdx >= 0 && channelIdx <= 2) {
176                 *channel = static_cast<SkColorChannel>(channelIdx);
177                 return true;
178             }
179             return false;
180         case kRGBA_SkColorChannelFlags:
181             if (channelIdx >= 0 && channelIdx <= 3) {
182                 *channel = static_cast<SkColorChannel>(channelIdx);
183                 return true;
184             }
185             return false;
186         default:
187             return false;
188     }
189 }
190 
GetYUVALocations(PlaneConfig config,const uint32_t * planeChannelFlags)191 SkYUVAInfo::YUVALocations SkYUVAInfo::GetYUVALocations(PlaneConfig config,
192                                                        const uint32_t* planeChannelFlags) {
193     // Like YUVALocation but chanIdx refers to channels by index rather than absolute channel, e.g.
194     // A is the 0th channel of an alpha-only texture. We'll use this plus planeChannelFlags to get
195     // the actual channel.
196     struct PlaneAndIndex {int plane, chanIdx;};
197     const PlaneAndIndex* planesAndIndices = nullptr;
198     switch (config) {
199         case PlaneConfig::kUnknown:
200             return {};
201 
202         case PlaneConfig::kY_U_V: {
203             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 0}, {2, 0}, {-1, -1}};
204             planesAndIndices = kPlanesAndIndices;
205             break;
206         }
207         case PlaneConfig::kY_V_U: {
208             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {2, 0}, {1, 0}, {-1, -1}};
209             planesAndIndices = kPlanesAndIndices;
210             break;
211         }
212         case PlaneConfig::kY_UV: {
213             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 0}, {1, 1}, {-1, -1}};
214             planesAndIndices = kPlanesAndIndices;
215             break;
216         }
217         case PlaneConfig::kY_VU: {
218             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 1}, {1, 0}, {-1, -1}};
219             planesAndIndices = kPlanesAndIndices;
220             break;
221         }
222         case PlaneConfig::kYUV: {
223             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {0, 1}, {0, 2}, {-1, -1}};
224             planesAndIndices = kPlanesAndIndices;
225             break;
226         }
227         case PlaneConfig::kUYV: {
228             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 1}, {0, 0}, {0, 2}, {-1, -1}};
229             planesAndIndices = kPlanesAndIndices;
230             break;
231         }
232         case PlaneConfig::kY_U_V_A: {
233             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}};
234             planesAndIndices = kPlanesAndIndices;
235             break;
236         }
237         case PlaneConfig::kY_V_U_A: {
238             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {2, 0}, {1, 0}, {3, 0}};
239             planesAndIndices = kPlanesAndIndices;
240             break;
241         }
242         case PlaneConfig::kY_UV_A: {
243             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}};
244             planesAndIndices = kPlanesAndIndices;
245             break;
246         }
247         case PlaneConfig::kY_VU_A: {
248             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {1, 1}, {1, 0}, {2, 0}};
249             planesAndIndices = kPlanesAndIndices;
250             break;
251         }
252         case PlaneConfig::kYUVA: {
253             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 0}, {0, 1}, {0, 2}, {0, 3}};
254             planesAndIndices = kPlanesAndIndices;
255             break;
256         }
257         case PlaneConfig::kUYVA: {
258             static constexpr PlaneAndIndex kPlanesAndIndices[] = {{0, 1}, {0, 0}, {0, 2}, {0, 3}};
259             planesAndIndices = kPlanesAndIndices;
260             break;
261         }
262     }
263     SkASSERT(planesAndIndices);
264     YUVALocations yuvaLocations;
265     for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
266         auto [plane, chanIdx] = planesAndIndices[i];
267         SkColorChannel channel;
268         if (plane >= 0) {
269             if (!channel_index_to_channel(planeChannelFlags[plane], chanIdx, &channel)) {
270                 return {};
271             }
272             yuvaLocations[i] = {plane, channel};
273         } else {
274             SkASSERT(i == 3);
275             yuvaLocations[i] = {-1, SkColorChannel::kR};
276         }
277     }
278     return yuvaLocations;
279 }
280 
HasAlpha(PlaneConfig planeConfig)281 bool SkYUVAInfo::HasAlpha(PlaneConfig planeConfig) {
282     switch (planeConfig) {
283         case PlaneConfig::kUnknown: return false;
284 
285         case PlaneConfig::kY_U_V:   return false;
286         case PlaneConfig::kY_V_U:   return false;
287         case PlaneConfig::kY_UV:    return false;
288         case PlaneConfig::kY_VU:    return false;
289         case PlaneConfig::kYUV:     return false;
290         case PlaneConfig::kUYV:     return false;
291 
292         case PlaneConfig::kY_U_V_A: return true;
293         case PlaneConfig::kY_V_U_A: return true;
294         case PlaneConfig::kY_UV_A:  return true;
295         case PlaneConfig::kY_VU_A:  return true;
296         case PlaneConfig::kYUVA:    return true;
297         case PlaneConfig::kUYVA:    return true;
298     }
299     SkUNREACHABLE;
300 }
301 
SkYUVAInfo(SkISize dimensions,PlaneConfig planeConfig,Subsampling subsampling,SkYUVColorSpace yuvColorSpace,SkEncodedOrigin origin,Siting sitingX,Siting sitingY)302 SkYUVAInfo::SkYUVAInfo(SkISize dimensions,
303                        PlaneConfig planeConfig,
304                        Subsampling subsampling,
305                        SkYUVColorSpace yuvColorSpace,
306                        SkEncodedOrigin origin,
307                        Siting sitingX,
308                        Siting sitingY)
309         : fDimensions(dimensions)
310         , fPlaneConfig(planeConfig)
311         , fSubsampling(subsampling)
312         , fYUVColorSpace(yuvColorSpace)
313         , fOrigin(origin)
314         , fSitingX(sitingX)
315         , fSitingY(sitingY) {
316     if (fDimensions.isEmpty() ||
317         !is_plane_config_compatible_with_subsampling(planeConfig, subsampling)) {
318         *this = {};
319         SkASSERT(!this->isValid());
320         return;
321     }
322     SkASSERT(this->isValid());
323 }
324 
computeTotalBytes(const size_t rowBytes[kMaxPlanes],size_t planeSizes[kMaxPlanes]) const325 size_t SkYUVAInfo::computeTotalBytes(const size_t rowBytes[kMaxPlanes],
326                                      size_t planeSizes[kMaxPlanes]) const {
327     if (!this->isValid()) {
328         return 0;
329     }
330     SkSafeMath safe;
331     size_t totalBytes = 0;
332     SkISize planeDimensions[kMaxPlanes];
333     int n = this->planeDimensions(planeDimensions);
334     for (int i = 0; i < n; ++i) {
335         SkASSERT(!planeDimensions[i].isEmpty());
336         SkASSERT(rowBytes[i]);
337         size_t size = safe.mul(rowBytes[i], planeDimensions[i].height());
338         if (planeSizes) {
339             planeSizes[i] = size;
340         }
341         totalBytes = safe.add(totalBytes, size);
342     }
343     if (planeSizes) {
344         if (safe.ok()) {
345             for (int i = n; i < kMaxPlanes; ++i) {
346                 planeSizes[i] = 0;
347             }
348         } else {
349             for (int i = 0; n < kMaxPlanes; ++i) {
350                 planeSizes[i] = SIZE_MAX;
351             }
352         }
353     }
354 
355     return safe.ok() ? totalBytes : SIZE_MAX;
356 }
357 
toYUVALocations(const uint32_t * channelFlags) const358 SkYUVAInfo::YUVALocations SkYUVAInfo::toYUVALocations(const uint32_t* channelFlags) const {
359     return GetYUVALocations(fPlaneConfig, channelFlags);
360 }
361 
makeSubsampling(SkYUVAInfo::Subsampling subsampling) const362 SkYUVAInfo SkYUVAInfo::makeSubsampling(SkYUVAInfo::Subsampling subsampling) const {
363     return {fDimensions, fPlaneConfig, subsampling, fYUVColorSpace, fOrigin, fSitingX, fSitingY};
364 }
365 
makeDimensions(SkISize dimensions) const366 SkYUVAInfo SkYUVAInfo::makeDimensions(SkISize dimensions) const {
367     return {dimensions, fPlaneConfig, fSubsampling, fYUVColorSpace, fOrigin, fSitingX, fSitingY};
368 }
369 
operator ==(const SkYUVAInfo & that) const370 bool SkYUVAInfo::operator==(const SkYUVAInfo& that) const {
371     return fPlaneConfig   == that.fPlaneConfig   &&
372            fSubsampling   == that.fSubsampling  &&
373            fYUVColorSpace == that.fYUVColorSpace &&
374            fDimensions    == that.fDimensions    &&
375            fSitingX       == that.fSitingX       &&
376            fSitingY       == that.fSitingY       &&
377            fOrigin        == that.fOrigin;
378 }
379