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