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