xref: /third_party/skia/src/core/SkYUVAInfo.cpp (revision cb93a386)
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
14static 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
27std::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
40std::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
75int 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
142static 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
189SkYUVAInfo::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
279bool 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
300SkYUVAInfo::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
323size_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
356SkYUVAInfo::YUVALocations SkYUVAInfo::toYUVALocations(const uint32_t* channelFlags) const {
357    return GetYUVALocations(fPlaneConfig, channelFlags);
358}
359
360SkYUVAInfo SkYUVAInfo::makeSubsampling(SkYUVAInfo::Subsampling subsampling) const {
361    return {fDimensions, fPlaneConfig, subsampling, fYUVColorSpace, fOrigin, fSitingX, fSitingY};
362}
363
364SkYUVAInfo SkYUVAInfo::makeDimensions(SkISize dimensions) const {
365    return {dimensions, fPlaneConfig, fSubsampling, fYUVColorSpace, fOrigin, fSitingX, fSitingY};
366}
367
368bool 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