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