/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/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 | 0 | 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 | | |
156 | 0 | PlaneConfig planeConfig() const { return fPlaneConfig; } |
157 | 0 | Subsampling subsampling() const { return fSubsampling; } |
158 | | |
159 | 0 | std::tuple<int, int> planeSubsamplingFactors(int planeIdx) const { |
160 | 0 | return PlaneSubsamplingFactors(fPlaneConfig, fSubsampling, planeIdx); |
161 | 0 | } |
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 | | */ |
167 | 0 | SkISize dimensions() const { return fDimensions; } |
168 | 0 | int width() const { return fDimensions.width(); } |
169 | 0 | int height() const { return fDimensions.height(); } |
170 | | |
171 | 0 | SkYUVColorSpace yuvColorSpace() const { return fYUVColorSpace; } |
172 | 0 | Siting sitingX() const { return fSitingX; } |
173 | 0 | Siting sitingY() const { return fSitingY; } |
174 | | |
175 | 0 | SkEncodedOrigin origin() const { return fOrigin; } |
176 | | |
177 | 0 | SkMatrix originMatrix() const { |
178 | 0 | return SkEncodedOriginToMatrix(fOrigin, this->width(), this->height()); |
179 | 0 | } |
180 | | |
181 | 0 | 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 | | */ |
188 | 0 | int planeDimensions(SkISize planeDimensions[kMaxPlanes]) const { |
189 | 0 | return PlaneDimensions(fDimensions, fPlaneConfig, fSubsampling, fOrigin, planeDimensions); |
190 | 0 | } |
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 | | |
200 | 0 | int numPlanes() const { return NumPlanes(fPlaneConfig); } |
201 | | |
202 | 0 | 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 | 0 | bool operator!=(const SkYUVAInfo& that) const { return !(*this == that); } |
228 | | |
229 | 0 | 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 | | |
249 | 0 | constexpr int SkYUVAInfo::NumPlanes(PlaneConfig planeConfig) { |
250 | 0 | switch (planeConfig) { |
251 | 0 | case PlaneConfig::kUnknown: return 0; |
252 | 0 | case PlaneConfig::kY_U_V: return 3; |
253 | 0 | case PlaneConfig::kY_V_U: return 3; |
254 | 0 | case PlaneConfig::kY_UV: return 2; |
255 | 0 | case PlaneConfig::kY_VU: return 2; |
256 | 0 | case PlaneConfig::kYUV: return 1; |
257 | 0 | case PlaneConfig::kUYV: return 1; |
258 | 0 | case PlaneConfig::kY_U_V_A: return 4; |
259 | 0 | case PlaneConfig::kY_V_U_A: return 4; |
260 | 0 | case PlaneConfig::kY_UV_A: return 3; |
261 | 0 | case PlaneConfig::kY_VU_A: return 3; |
262 | 0 | case PlaneConfig::kYUVA: return 1; |
263 | 0 | case PlaneConfig::kUYVA: return 1; |
264 | 0 | } |
265 | 0 | SkUNREACHABLE; |
266 | 0 | } |
267 | | |
268 | 0 | constexpr int SkYUVAInfo::NumChannelsInPlane(PlaneConfig config, int i) { |
269 | 0 | switch (config) { |
270 | 0 | case PlaneConfig::kUnknown: |
271 | 0 | return 0; |
272 | | |
273 | 0 | case SkYUVAInfo::PlaneConfig::kY_U_V: |
274 | 0 | case SkYUVAInfo::PlaneConfig::kY_V_U: |
275 | 0 | return i >= 0 && i < 3 ? 1 : 0; |
276 | 0 | case SkYUVAInfo::PlaneConfig::kY_UV: |
277 | 0 | case SkYUVAInfo::PlaneConfig::kY_VU: |
278 | 0 | switch (i) { |
279 | 0 | case 0: return 1; |
280 | 0 | case 1: return 2; |
281 | 0 | default: return 0; |
282 | 0 | } |
283 | 0 | case SkYUVAInfo::PlaneConfig::kYUV: |
284 | 0 | case SkYUVAInfo::PlaneConfig::kUYV: |
285 | 0 | return i == 0 ? 3 : 0; |
286 | 0 | case SkYUVAInfo::PlaneConfig::kY_U_V_A: |
287 | 0 | case SkYUVAInfo::PlaneConfig::kY_V_U_A: |
288 | 0 | return i >= 0 && i < 4 ? 1 : 0; |
289 | 0 | case SkYUVAInfo::PlaneConfig::kY_UV_A: |
290 | 0 | case SkYUVAInfo::PlaneConfig::kY_VU_A: |
291 | 0 | switch (i) { |
292 | 0 | case 0: return 1; |
293 | 0 | case 1: return 2; |
294 | 0 | case 2: return 1; |
295 | 0 | default: return 0; |
296 | 0 | } |
297 | 0 | case SkYUVAInfo::PlaneConfig::kYUVA: |
298 | 0 | case SkYUVAInfo::PlaneConfig::kUYVA: |
299 | 0 | return i == 0 ? 4 : 0; |
300 | 0 | } |
301 | 0 | return 0; |
302 | 0 | } |
303 | | |
304 | | #endif |