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