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 SkYUVAPixmaps_DEFINED
9 #define SkYUVAPixmaps_DEFINED
10
11 #include "include/core/SkData.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkPixmap.h"
14 #include "include/core/SkYUVAInfo.h"
15 #include "include/private/base/SkTo.h"
16
17 #include <array>
18 #include <bitset>
19
20 class GrImageContext;
21
22 /**
23 * SkYUVAInfo combined with per-plane SkColorTypes and row bytes. Fully specifies the SkPixmaps
24 * for a YUVA image without the actual pixel memory and data.
25 */
26 class SK_API SkYUVAPixmapInfo {
27 public:
28 static constexpr auto kMaxPlanes = SkYUVAInfo::kMaxPlanes;
29
30 using PlaneConfig = SkYUVAInfo::PlaneConfig;
31 using Subsampling = SkYUVAInfo::Subsampling;
32
33 /**
34 * Data type for Y, U, V, and possibly A channels independent of how values are packed into
35 * planes.
36 **/
37 enum class DataType {
38 kUnorm8, ///< 8 bit unsigned normalized
39 kUnorm16, ///< 16 bit unsigned normalized
40 kFloat16, ///< 16 bit (half) floating point
41 kUnorm10_Unorm2, ///< 10 bit unorm for Y, U, and V. 2 bit unorm for alpha (if present).
42
43 kLast = kUnorm10_Unorm2
44 };
45 static constexpr int kDataTypeCnt = static_cast<int>(DataType::kLast) + 1;
46
47 class SK_API SupportedDataTypes {
48 public:
49 /** Defaults to nothing supported. */
50 constexpr SupportedDataTypes() = default;
51
52 /** Init based on texture formats supported by the context. */
53 SupportedDataTypes(const GrImageContext&);
54
55 /** All legal combinations of PlaneConfig and DataType are supported. */
56 static constexpr SupportedDataTypes All();
57
58 /**
59 * Checks whether there is a supported combination of color types for planes structured
60 * as indicated by PlaneConfig with channel data types as indicated by DataType.
61 */
62 constexpr bool supported(PlaneConfig, DataType) const;
63
64 /**
65 * Update to add support for pixmaps with numChannel channels where each channel is
66 * represented as DataType.
67 */
68 void enableDataType(DataType, int numChannels);
69
70 private:
71 // The bit for DataType dt with n channels is at index kDataTypeCnt*(n-1) + dt.
72 std::bitset<kDataTypeCnt*4> fDataTypeSupport = {};
73 };
74
75 /**
76 * Gets the default SkColorType to use with numChannels channels, each represented as DataType.
77 * Returns kUnknown_SkColorType if no such color type.
78 */
79 static constexpr SkColorType DefaultColorTypeForDataType(DataType dataType, int numChannels);
80
81 /**
82 * If the SkColorType is supported for YUVA pixmaps this will return the number of YUVA channels
83 * that can be stored in a plane of this color type and what the DataType is of those channels.
84 * If the SkColorType is not supported as a YUVA plane the number of channels is reported as 0
85 * and the DataType returned should be ignored.
86 */
87 static std::tuple<int, DataType> NumChannelsAndDataType(SkColorType);
88
89 /** Default SkYUVAPixmapInfo is invalid. */
90 SkYUVAPixmapInfo() = default;
91
92 /**
93 * Initializes the SkYUVAPixmapInfo from a SkYUVAInfo with per-plane color types and row bytes.
94 * This will be invalid if the colorTypes aren't compatible with the SkYUVAInfo or if a
95 * rowBytes entry is not valid for the plane dimensions and color type. Color type and
96 * row byte values beyond the number of planes in SkYUVAInfo are ignored. All SkColorTypes
97 * must have the same DataType or this will be invalid.
98 *
99 * If rowBytes is nullptr then bpp*width is assumed for each plane.
100 */
101 SkYUVAPixmapInfo(const SkYUVAInfo&,
102 const SkColorType[kMaxPlanes],
103 const size_t rowBytes[kMaxPlanes]);
104 /**
105 * Like above but uses DefaultColorTypeForDataType to determine each plane's SkColorType. If
106 * rowBytes is nullptr then bpp*width is assumed for each plane.
107 */
108 SkYUVAPixmapInfo(const SkYUVAInfo&, DataType, const size_t rowBytes[kMaxPlanes]);
109
110 SkYUVAPixmapInfo(const SkYUVAPixmapInfo&) = default;
111
112 SkYUVAPixmapInfo& operator=(const SkYUVAPixmapInfo&) = default;
113
114 bool operator==(const SkYUVAPixmapInfo&) const;
115 bool operator!=(const SkYUVAPixmapInfo& that) const { return !(*this == that); }
116
yuvaInfo()117 const SkYUVAInfo& yuvaInfo() const { return fYUVAInfo; }
118
yuvColorSpace()119 SkYUVColorSpace yuvColorSpace() const { return fYUVAInfo.yuvColorSpace(); }
120
121 /** The number of SkPixmap planes, 0 if this SkYUVAPixmapInfo is invalid. */
numPlanes()122 int numPlanes() const { return fYUVAInfo.numPlanes(); }
123
124 /** The per-YUV[A] channel data type. */
dataType()125 DataType dataType() const { return fDataType; }
126
127 /**
128 * Row bytes for the ith plane. Returns zero if i >= numPlanes() or this SkYUVAPixmapInfo is
129 * invalid.
130 */
rowBytes(int i)131 size_t rowBytes(int i) const { return fRowBytes[static_cast<size_t>(i)]; }
132
133 /** Image info for the ith plane, or default SkImageInfo if i >= numPlanes() */
planeInfo(int i)134 const SkImageInfo& planeInfo(int i) const { return fPlaneInfos[static_cast<size_t>(i)]; }
135
136 /**
137 * Determine size to allocate for all planes. Optionally retrieves the per-plane sizes in
138 * planeSizes if not null. If total size overflows will return SIZE_MAX and set all planeSizes
139 * to SIZE_MAX. Returns 0 and fills planesSizes with 0 if this SkYUVAPixmapInfo is not valid.
140 */
141 size_t computeTotalBytes(size_t planeSizes[kMaxPlanes] = nullptr) const;
142
143 /**
144 * Takes an allocation that is assumed to be at least computeTotalBytes() in size and configures
145 * the first numPlanes() entries in pixmaps array to point into that memory. The remaining
146 * entries of pixmaps are default initialized. Fails if this SkYUVAPixmapInfo not valid.
147 */
148 bool initPixmapsFromSingleAllocation(void* memory, SkPixmap pixmaps[kMaxPlanes]) const;
149
150 /**
151 * Returns true if this has been configured with a non-empty dimensioned SkYUVAInfo with
152 * compatible color types and row bytes.
153 */
isValid()154 bool isValid() const { return fYUVAInfo.isValid(); }
155
156 /** Is this valid and does it use color types allowed by the passed SupportedDataTypes? */
157 bool isSupported(const SupportedDataTypes&) const;
158
159 private:
160 SkYUVAInfo fYUVAInfo;
161 std::array<SkImageInfo, kMaxPlanes> fPlaneInfos = {};
162 std::array<size_t, kMaxPlanes> fRowBytes = {};
163 DataType fDataType = DataType::kUnorm8;
164 static_assert(kUnknown_SkColorType == 0, "default init isn't kUnknown");
165 };
166
167 /**
168 * Helper to store SkPixmap planes as described by a SkYUVAPixmapInfo. Can be responsible for
169 * allocating/freeing memory for pixmaps or use external memory.
170 */
171 class SK_API SkYUVAPixmaps {
172 public:
173 using DataType = SkYUVAPixmapInfo::DataType;
174 static constexpr auto kMaxPlanes = SkYUVAPixmapInfo::kMaxPlanes;
175
176 static SkColorType RecommendedRGBAColorType(DataType);
177
178 /** Allocate space for pixmaps' pixels in the SkYUVAPixmaps. */
179 static SkYUVAPixmaps Allocate(const SkYUVAPixmapInfo& yuvaPixmapInfo);
180
181 /**
182 * Use storage in SkData as backing store for pixmaps' pixels. SkData is retained by the
183 * SkYUVAPixmaps.
184 */
185 static SkYUVAPixmaps FromData(const SkYUVAPixmapInfo&, sk_sp<SkData>);
186
187 /**
188 * Makes a deep copy of the src SkYUVAPixmaps. The returned SkYUVAPixmaps owns its planes'
189 * backing stores.
190 */
191 static SkYUVAPixmaps MakeCopy(const SkYUVAPixmaps& src);
192
193 /**
194 * Use passed in memory as backing store for pixmaps' pixels. Caller must ensure memory remains
195 * allocated while pixmaps are in use. There must be at least
196 * SkYUVAPixmapInfo::computeTotalBytes() allocated starting at memory.
197 */
198 static SkYUVAPixmaps FromExternalMemory(const SkYUVAPixmapInfo&, void* memory);
199
200 /**
201 * Wraps existing SkPixmaps. The SkYUVAPixmaps will have no ownership of the SkPixmaps' pixel
202 * memory so the caller must ensure it remains valid. Will return an invalid SkYUVAPixmaps if
203 * the SkYUVAInfo isn't compatible with the SkPixmap array (number of planes, plane dimensions,
204 * sufficient color channels in planes, ...).
205 */
206 static SkYUVAPixmaps FromExternalPixmaps(const SkYUVAInfo&, const SkPixmap[kMaxPlanes]);
207
208 /** Default SkYUVAPixmaps is invalid. */
209 SkYUVAPixmaps() = default;
210 ~SkYUVAPixmaps() = default;
211
212 SkYUVAPixmaps(SkYUVAPixmaps&& that) = default;
213 SkYUVAPixmaps& operator=(SkYUVAPixmaps&& that) = default;
214 SkYUVAPixmaps(const SkYUVAPixmaps&) = default;
215 SkYUVAPixmaps& operator=(const SkYUVAPixmaps& that) = default;
216
217 /** Does have initialized pixmaps compatible with its SkYUVAInfo. */
isValid()218 bool isValid() const { return !fYUVAInfo.dimensions().isEmpty(); }
219
yuvaInfo()220 const SkYUVAInfo& yuvaInfo() const { return fYUVAInfo; }
221
dataType()222 DataType dataType() const { return fDataType; }
223
224 SkYUVAPixmapInfo pixmapsInfo() const;
225
226 /** Number of pixmap planes or 0 if this SkYUVAPixmaps is invalid. */
numPlanes()227 int numPlanes() const { return this->isValid() ? fYUVAInfo.numPlanes() : 0; }
228
229 /**
230 * Access the SkPixmap planes. They are default initialized if this is not a valid
231 * SkYUVAPixmaps.
232 */
planes()233 const std::array<SkPixmap, kMaxPlanes>& planes() const { return fPlanes; }
234
235 /**
236 * Get the ith SkPixmap plane. SkPixmap will be default initialized if i >= numPlanes or this
237 * SkYUVAPixmaps is invalid.
238 */
plane(int i)239 const SkPixmap& plane(int i) const { return fPlanes[SkToSizeT(i)]; }
240
241 /**
242 * Computes a YUVALocations representation of the planar layout. The result is guaranteed to be
243 * valid if this->isValid().
244 */
245 SkYUVAInfo::YUVALocations toYUVALocations() const;
246
247 /** Does this SkPixmaps own the backing store of the planes? */
ownsStorage()248 bool ownsStorage() const { return SkToBool(fData); }
249
250 private:
251 SkYUVAPixmaps(const SkYUVAPixmapInfo&, sk_sp<SkData>);
252 SkYUVAPixmaps(const SkYUVAInfo&, DataType, const SkPixmap[kMaxPlanes]);
253
254 std::array<SkPixmap, kMaxPlanes> fPlanes = {};
255 sk_sp<SkData> fData;
256 SkYUVAInfo fYUVAInfo;
257 DataType fDataType;
258 };
259
260 //////////////////////////////////////////////////////////////////////////////
261
All()262 constexpr SkYUVAPixmapInfo::SupportedDataTypes SkYUVAPixmapInfo::SupportedDataTypes::All() {
263 using ULL = unsigned long long; // bitset cons. takes this.
264 ULL bits = 0;
265 for (ULL c = 1; c <= 4; ++c) {
266 for (ULL dt = 0; dt <= ULL(kDataTypeCnt); ++dt) {
267 if (DefaultColorTypeForDataType(static_cast<DataType>(dt),
268 static_cast<int>(c)) != kUnknown_SkColorType) {
269 bits |= ULL(1) << (dt + static_cast<ULL>(kDataTypeCnt)*(c - 1));
270 }
271 }
272 }
273 SupportedDataTypes combinations;
274 combinations.fDataTypeSupport = bits;
275 return combinations;
276 }
277
supported(PlaneConfig config,DataType type)278 constexpr bool SkYUVAPixmapInfo::SupportedDataTypes::supported(PlaneConfig config,
279 DataType type) const {
280 int n = SkYUVAInfo::NumPlanes(config);
281 for (int i = 0; i < n; ++i) {
282 auto c = static_cast<size_t>(SkYUVAInfo::NumChannelsInPlane(config, i));
283 SkASSERT(c >= 1 && c <= 4);
284 if (!fDataTypeSupport[static_cast<size_t>(type) +
285 (c - 1)*static_cast<size_t>(kDataTypeCnt)]) {
286 return false;
287 }
288 }
289 return true;
290 }
291
DefaultColorTypeForDataType(DataType dataType,int numChannels)292 constexpr SkColorType SkYUVAPixmapInfo::DefaultColorTypeForDataType(DataType dataType,
293 int numChannels) {
294 switch (numChannels) {
295 case 1:
296 switch (dataType) {
297 case DataType::kUnorm8: return kGray_8_SkColorType;
298 case DataType::kUnorm16: return kA16_unorm_SkColorType;
299 case DataType::kFloat16: return kA16_float_SkColorType;
300 case DataType::kUnorm10_Unorm2: return kUnknown_SkColorType;
301 }
302 break;
303 case 2:
304 switch (dataType) {
305 case DataType::kUnorm8: return kR8G8_unorm_SkColorType;
306 case DataType::kUnorm16: return kR16G16_unorm_SkColorType;
307 case DataType::kFloat16: return kR16G16_float_SkColorType;
308 case DataType::kUnorm10_Unorm2: return kUnknown_SkColorType;
309 }
310 break;
311 case 3:
312 // None of these are tightly packed. The intended use case is for interleaved YUVA
313 // planes where we're forcing opaqueness by ignoring the alpha values.
314 // There are "x" rather than "A" variants for Unorm8 and Unorm10_Unorm2 but we don't
315 // choose them because 1) there is no inherent advantage and 2) there is better support
316 // in the GPU backend for the "A" versions.
317 switch (dataType) {
318 case DataType::kUnorm8: return kRGBA_8888_SkColorType;
319 case DataType::kUnorm16: return kR16G16B16A16_unorm_SkColorType;
320 case DataType::kFloat16: return kRGBA_F16_SkColorType;
321 case DataType::kUnorm10_Unorm2: return kRGBA_1010102_SkColorType;
322 }
323 break;
324 case 4:
325 switch (dataType) {
326 case DataType::kUnorm8: return kRGBA_8888_SkColorType;
327 case DataType::kUnorm16: return kR16G16B16A16_unorm_SkColorType;
328 case DataType::kFloat16: return kRGBA_F16_SkColorType;
329 case DataType::kUnorm10_Unorm2: return kRGBA_1010102_SkColorType;
330 }
331 break;
332 }
333 return kUnknown_SkColorType;
334 }
335
336 #endif
337