/src/skia/include/core/SkYUVAInfo.h
Line | Count | Source (jump to first uncovered line) |
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 | 0 | 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 | | |
160 | 0 | PlaneConfig planeConfig() const { return fPlaneConfig; } |
161 | 0 | Subsampling subsampling() const { return fSubsampling; } |
162 | | |
163 | 0 | std::tuple<int, int> planeSubsamplingFactors(int planeIdx) const { |
164 | 0 | return PlaneSubsamplingFactors(fPlaneConfig, fSubsampling, planeIdx); |
165 | 0 | } |
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 | | */ |
171 | 0 | SkISize dimensions() const { return fDimensions; } |
172 | 0 | int width() const { return fDimensions.width(); } |
173 | 0 | int height() const { return fDimensions.height(); } |
174 | | |
175 | 0 | SkYUVColorSpace yuvColorSpace() const { return fYUVColorSpace; } |
176 | 0 | Siting sitingX() const { return fSitingX; } |
177 | 0 | Siting sitingY() const { return fSitingY; } |
178 | | |
179 | 0 | SkEncodedOrigin origin() const { return fOrigin; } |
180 | | |
181 | 0 | SkMatrix originMatrix() const { |
182 | 0 | return SkEncodedOriginToMatrix(fOrigin, this->width(), this->height()); |
183 | 0 | } |
184 | | |
185 | 0 | 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 | | */ |
192 | 0 | int planeDimensions(SkISize planeDimensions[kMaxPlanes]) const { |
193 | 0 | return PlaneDimensions(fDimensions, fPlaneConfig, fSubsampling, fOrigin, planeDimensions); |
194 | 0 | } |
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 | | |
204 | 0 | int numPlanes() const { return NumPlanes(fPlaneConfig); } |
205 | | |
206 | 0 | 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 | 0 | bool operator!=(const SkYUVAInfo& that) const { return !(*this == that); } |
232 | | |
233 | 0 | 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 | | |
253 | 0 | constexpr int SkYUVAInfo::NumPlanes(PlaneConfig planeConfig) { |
254 | 0 | switch (planeConfig) { |
255 | 0 | case PlaneConfig::kUnknown: return 0; |
256 | 0 | case PlaneConfig::kY_U_V: return 3; |
257 | 0 | case PlaneConfig::kY_V_U: return 3; |
258 | 0 | case PlaneConfig::kY_UV: return 2; |
259 | 0 | case PlaneConfig::kY_VU: return 2; |
260 | 0 | case PlaneConfig::kYUV: return 1; |
261 | 0 | case PlaneConfig::kUYV: return 1; |
262 | 0 | case PlaneConfig::kY_U_V_A: return 4; |
263 | 0 | case PlaneConfig::kY_V_U_A: return 4; |
264 | 0 | case PlaneConfig::kY_UV_A: return 3; |
265 | 0 | case PlaneConfig::kY_VU_A: return 3; |
266 | 0 | case PlaneConfig::kYUVA: return 1; |
267 | 0 | case PlaneConfig::kUYVA: return 1; |
268 | 0 | } |
269 | 0 | SkUNREACHABLE; |
270 | 0 | } |
271 | | |
272 | 0 | constexpr int SkYUVAInfo::NumChannelsInPlane(PlaneConfig config, int i) { |
273 | 0 | switch (config) { |
274 | 0 | case PlaneConfig::kUnknown: |
275 | 0 | return 0; |
276 | | |
277 | 0 | case SkYUVAInfo::PlaneConfig::kY_U_V: |
278 | 0 | case SkYUVAInfo::PlaneConfig::kY_V_U: |
279 | 0 | return i >= 0 && i < 3 ? 1 : 0; |
280 | 0 | case SkYUVAInfo::PlaneConfig::kY_UV: |
281 | 0 | case SkYUVAInfo::PlaneConfig::kY_VU: |
282 | 0 | switch (i) { |
283 | 0 | case 0: return 1; |
284 | 0 | case 1: return 2; |
285 | 0 | default: return 0; |
286 | 0 | } |
287 | 0 | case SkYUVAInfo::PlaneConfig::kYUV: |
288 | 0 | case SkYUVAInfo::PlaneConfig::kUYV: |
289 | 0 | return i == 0 ? 3 : 0; |
290 | 0 | case SkYUVAInfo::PlaneConfig::kY_U_V_A: |
291 | 0 | case SkYUVAInfo::PlaneConfig::kY_V_U_A: |
292 | 0 | return i >= 0 && i < 4 ? 1 : 0; |
293 | 0 | case SkYUVAInfo::PlaneConfig::kY_UV_A: |
294 | 0 | case SkYUVAInfo::PlaneConfig::kY_VU_A: |
295 | 0 | switch (i) { |
296 | 0 | case 0: return 1; |
297 | 0 | case 1: return 2; |
298 | 0 | case 2: return 1; |
299 | 0 | default: return 0; |
300 | 0 | } |
301 | 0 | case SkYUVAInfo::PlaneConfig::kYUVA: |
302 | 0 | case SkYUVAInfo::PlaneConfig::kUYVA: |
303 | 0 | return i == 0 ? 4 : 0; |
304 | 0 | } |
305 | 0 | return 0; |
306 | 0 | } |
307 | | |
308 | | #endif |