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 #ifndef SkYUVAInfo_DEFINED
9 #define SkYUVAInfo_DEFINED
10 
11 #include "include/codec/SkEncodedOrigin.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkSize.h"
14 
15 #include <array>
16 #include <tuple>
17 
18 /**
19  * Specifies the structure of planes for a YUV image with optional alpha. The actual planar data
20  * is not part of this structure and depending on usage is in external textures or pixmaps.
21  */
22 class SK_API SkYUVAInfo {
23 public:
24     enum YUVAChannels { kY, kU, kV, kA, kLast = kA };
25     static constexpr int kYUVAChannelCount = static_cast<int>(YUVAChannels::kLast + 1);
26 
27     struct YUVALocation;  // For internal use.
28     using YUVALocations = std::array<YUVALocation, kYUVAChannelCount>;
29 
30     /**
31      * Specifies how YUV (and optionally A) are divided among planes. Planes are separated by
32      * underscores in the enum value names. Within each plane the pixmap/texture channels are
33      * mapped to the YUVA channels in the order specified, e.g. for kY_UV Y is in channel 0 of plane
34      * 0, U is in channel 0 of plane 1, and V is in channel 1 of plane 1. Channel ordering
35      * within a pixmap/texture given the channels it contains:
36      * A:                       0:A
37      * Luminance/Gray:          0:Gray
38      * Luminance/Gray + Alpha:  0:Gray, 1:A
39      * RG                       0:R,    1:G
40      * RGB                      0:R,    1:G, 2:B
41      * RGBA                     0:R,    1:G, 2:B, 3:A
42      */
43     enum class PlaneConfig {
44         kUnknown,
45 
46         kY_U_V,    ///< Plane 0: Y, Plane 1: U,  Plane 2: V
47         kY_V_U,    ///< Plane 0: Y, Plane 1: V,  Plane 2: U
48         kY_UV,     ///< Plane 0: Y, Plane 1: UV
49         kY_VU,     ///< Plane 0: Y, Plane 1: VU
50         kYUV,      ///< Plane 0: YUV
51         kUYV,      ///< Plane 0: UYV
52 
53         kY_U_V_A,  ///< Plane 0: Y, Plane 1: U,  Plane 2: V, Plane 3: A
54         kY_V_U_A,  ///< Plane 0: Y, Plane 1: V,  Plane 2: U, Plane 3: A
55         kY_UV_A,   ///< Plane 0: Y, Plane 1: UV, Plane 2: A
56         kY_VU_A,   ///< Plane 0: Y, Plane 1: VU, Plane 2: A
57         kYUVA,     ///< Plane 0: YUVA
58         kUYVA,     ///< Plane 0: UYVA
59 
60         kLast = kUYVA
61     };
62 
63     /**
64      * UV subsampling is also specified in the enum value names using J:a:b notation (e.g. 4:2:0 is
65      * 1/2 horizontal and 1/2 vertical resolution for U and V). If alpha is present it is not sub-
66      * sampled. Note that Subsampling values other than k444 are only valid with PlaneConfig values
67      * that have U and V in different planes than Y (and A, if present).
68      */
69     enum class Subsampling {
70         kUnknown,
71 
72         k444,    ///< No subsampling. UV values for each Y.
73         k422,    ///< 1 set of UV values for each 2x1 block of Y values.
74         k420,    ///< 1 set of UV values for each 2x2 block of Y values.
75         k440,    ///< 1 set of UV values for each 1x2 block of Y values.
76         k411,    ///< 1 set of UV values for each 4x1 block of Y values.
77         k410,    ///< 1 set of UV values for each 4x2 block of Y values.
78 
79         kLast = k410
80     };
81 
82     /**
83      * Describes how subsampled chroma values are sited relative to luma values.
84      *
85      * Currently only centered siting is supported but will expand to support additional sitings.
86      */
87     enum class Siting {
88         /**
89          * Subsampled chroma value is sited at the center of the block of corresponding luma values.
90          */
91         kCentered,
92     };
93 
94     static constexpr int kMaxPlanes = 4;
95 
96     /** ratio of Y/A values to U/V values in x and y. */
97     static std::tuple<int, int> SubsamplingFactors(Subsampling);
98 
99     /**
100      * SubsamplingFactors(Subsampling) if planedIdx refers to a U/V plane and otherwise {1, 1} if
101      * inputs are valid. Invalid inputs consist of incompatible PlaneConfig/Subsampling/planeIdx
102      * combinations. {0, 0} is returned for invalid inputs.
103      */
104     static std::tuple<int, int> PlaneSubsamplingFactors(PlaneConfig, Subsampling, int planeIdx);
105 
106     /**
107      * Given image dimensions, a planer configuration, subsampling, and origin, determine the
108      * expected size of each plane. Returns the number of expected planes. planeDimensions[0]
109      * through planeDimensions[<ret>] are written. The input image dimensions are as displayed
110      * (after the planes have been transformed to the intended display orientation). The plane
111      * dimensions are output as the planes are stored in memory (may be rotated from image
112      * dimensions).
113      */
114     static int PlaneDimensions(SkISize imageDimensions,
115                                PlaneConfig,
116                                Subsampling,
117                                SkEncodedOrigin,
118                                SkISize planeDimensions[kMaxPlanes]);
119 
120     /** Number of planes for a given PlaneConfig. */
121     static constexpr int NumPlanes(PlaneConfig);
122 
123     /**
124      * Number of Y, U, V, A channels in the ith plane for a given PlaneConfig (or 0 if i is
125      * invalid).
126      */
127     static constexpr int NumChannelsInPlane(PlaneConfig, int i);
128 
129     /**
130      * Given a PlaneConfig and a set of channel flags for each plane, convert to YUVALocations
131      * representation. Fails if channel flags aren't valid for the PlaneConfig (i.e. don't have
132      * enough channels in a plane) by returning an invalid set of locations (plane indices are -1).
133      */
134     static YUVALocations GetYUVALocations(PlaneConfig, const uint32_t* planeChannelFlags);
135 
136     /** Does the PlaneConfig have alpha values? */
137     static bool HasAlpha(PlaneConfig);
138 
139     SkYUVAInfo() = default;
140     SkYUVAInfo(const SkYUVAInfo&) = default;
141 
142     /**
143      * 'dimensions' should specify the size of the full resolution image (after planes have been
144      * oriented to how the image is displayed as indicated by 'origin').
145      */
146     SkYUVAInfo(SkISize dimensions,
147                PlaneConfig,
148                Subsampling,
149                SkYUVColorSpace,
150                SkEncodedOrigin origin = kTopLeft_SkEncodedOrigin,
151                Siting sitingX = Siting::kCentered,
152                Siting sitingY = Siting::kCentered);
153 
154     SkYUVAInfo& operator=(const SkYUVAInfo& that) = default;
155 
planeConfig()156     PlaneConfig planeConfig() const { return fPlaneConfig; }
subsampling()157     Subsampling subsampling() const { return fSubsampling; }
158 
planeSubsamplingFactors(int planeIdx)159     std::tuple<int, int> planeSubsamplingFactors(int planeIdx) const {
160         return PlaneSubsamplingFactors(fPlaneConfig, fSubsampling, planeIdx);
161     }
162 
163     /**
164      * Dimensions of the full resolution image (after planes have been oriented to how the image
165      * is displayed as indicated by fOrigin).
166      */
dimensions()167     SkISize dimensions() const { return fDimensions; }
width()168     int width() const { return fDimensions.width(); }
height()169     int height() const { return fDimensions.height(); }
170 
yuvColorSpace()171     SkYUVColorSpace yuvColorSpace() const { return fYUVColorSpace; }
sitingX()172     Siting sitingX() const { return fSitingX; }
sitingY()173     Siting sitingY() const { return fSitingY; }
174 
origin()175     SkEncodedOrigin origin() const { return fOrigin; }
176 
originMatrix()177     SkMatrix originMatrix() const {
178         return SkEncodedOriginToMatrix(fOrigin, this->width(), this->height());
179     }
180 
hasAlpha()181     bool hasAlpha() const { return HasAlpha(fPlaneConfig); }
182 
183     /**
184      * Returns the number of planes and initializes planeDimensions[0]..planeDimensions[<ret>] to
185      * the expected dimensions for each plane. Dimensions are as stored in memory, before
186      * transformation to image display space as indicated by origin().
187      */
planeDimensions(SkISize planeDimensions[kMaxPlanes])188     int planeDimensions(SkISize planeDimensions[kMaxPlanes]) const {
189         return PlaneDimensions(fDimensions, fPlaneConfig, fSubsampling, fOrigin, planeDimensions);
190     }
191 
192     /**
193      * Given a per-plane row bytes, determine size to allocate for all planes. Optionally retrieves
194      * the per-plane byte sizes in planeSizes if not null. If total size overflows will return
195      * SIZE_MAX and set all planeSizes to SIZE_MAX.
196      */
197     size_t computeTotalBytes(const size_t rowBytes[kMaxPlanes],
198                              size_t planeSizes[kMaxPlanes] = nullptr) const;
199 
numPlanes()200     int numPlanes() const { return NumPlanes(fPlaneConfig); }
201 
numChannelsInPlane(int i)202     int numChannelsInPlane(int i) const { return NumChannelsInPlane(fPlaneConfig, i); }
203 
204     /**
205      * Given a set of channel flags for each plane, converts this->planeConfig() to YUVALocations
206      * representation. Fails if the channel flags aren't valid for the PlaneConfig (i.e. don't have
207      * enough channels in a plane) by returning default initialized locations (all plane indices are
208      * -1).
209      */
210     YUVALocations toYUVALocations(const uint32_t* channelFlags) const;
211 
212     /**
213      * Makes a SkYUVAInfo that is identical to this one but with the passed Subsampling. If the
214      * passed Subsampling is not k444 and this info's PlaneConfig is not compatible with chroma
215      * subsampling (because Y is in the same plane as UV) then the result will be an invalid
216      * SkYUVAInfo.
217      */
218     SkYUVAInfo makeSubsampling(SkYUVAInfo::Subsampling) const;
219 
220     /**
221      * Makes a SkYUVAInfo that is identical to this one but with the passed dimensions. If the
222      * passed dimensions is empty then the result will be an invalid SkYUVAInfo.
223      */
224     SkYUVAInfo makeDimensions(SkISize) const;
225 
226     bool operator==(const SkYUVAInfo& that) const;
227     bool operator!=(const SkYUVAInfo& that) const { return !(*this == that); }
228 
isValid()229     bool isValid() const { return fPlaneConfig != PlaneConfig::kUnknown; }
230 
231 private:
232     SkISize fDimensions = {0, 0};
233 
234     PlaneConfig fPlaneConfig = PlaneConfig::kUnknown;
235     Subsampling fSubsampling = Subsampling::kUnknown;
236 
237     SkYUVColorSpace fYUVColorSpace = SkYUVColorSpace::kIdentity_SkYUVColorSpace;
238 
239     /**
240      * YUVA data often comes from formats like JPEG that support EXIF orientation.
241      * Code that operates on the raw YUV data often needs to know that orientation.
242      */
243     SkEncodedOrigin fOrigin = kTopLeft_SkEncodedOrigin;
244 
245     Siting fSitingX = Siting::kCentered;
246     Siting fSitingY = Siting::kCentered;
247 };
248 
NumPlanes(PlaneConfig planeConfig)249 constexpr int SkYUVAInfo::NumPlanes(PlaneConfig planeConfig) {
250     switch (planeConfig) {
251         case PlaneConfig::kUnknown: return 0;
252         case PlaneConfig::kY_U_V:   return 3;
253         case PlaneConfig::kY_V_U:   return 3;
254         case PlaneConfig::kY_UV:    return 2;
255         case PlaneConfig::kY_VU:    return 2;
256         case PlaneConfig::kYUV:     return 1;
257         case PlaneConfig::kUYV:     return 1;
258         case PlaneConfig::kY_U_V_A: return 4;
259         case PlaneConfig::kY_V_U_A: return 4;
260         case PlaneConfig::kY_UV_A:  return 3;
261         case PlaneConfig::kY_VU_A:  return 3;
262         case PlaneConfig::kYUVA:    return 1;
263         case PlaneConfig::kUYVA:    return 1;
264     }
265     SkUNREACHABLE;
266 }
267 
NumChannelsInPlane(PlaneConfig config,int i)268 constexpr int SkYUVAInfo::NumChannelsInPlane(PlaneConfig config, int i) {
269     switch (config) {
270         case PlaneConfig::kUnknown:
271             return 0;
272 
273         case SkYUVAInfo::PlaneConfig::kY_U_V:
274         case SkYUVAInfo::PlaneConfig::kY_V_U:
275             return i >= 0 && i < 3 ? 1 : 0;
276         case SkYUVAInfo::PlaneConfig::kY_UV:
277         case SkYUVAInfo::PlaneConfig::kY_VU:
278             switch (i) {
279                 case 0:  return 1;
280                 case 1:  return 2;
281                 default: return 0;
282             }
283         case SkYUVAInfo::PlaneConfig::kYUV:
284         case SkYUVAInfo::PlaneConfig::kUYV:
285             return i == 0 ? 3 : 0;
286         case SkYUVAInfo::PlaneConfig::kY_U_V_A:
287         case SkYUVAInfo::PlaneConfig::kY_V_U_A:
288             return i >= 0 && i < 4 ? 1 : 0;
289         case SkYUVAInfo::PlaneConfig::kY_UV_A:
290         case SkYUVAInfo::PlaneConfig::kY_VU_A:
291             switch (i) {
292                 case 0:  return 1;
293                 case 1:  return 2;
294                 case 2:  return 1;
295                 default: return 0;
296             }
297         case SkYUVAInfo::PlaneConfig::kYUVA:
298         case SkYUVAInfo::PlaneConfig::kUYVA:
299             return i == 0 ? 4 : 0;
300     }
301     return 0;
302 }
303 
304 #endif
305