/src/skia/include/codec/SkCodec.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2015 Google Inc. |
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 SkCodec_DEFINED |
9 | | #define SkCodec_DEFINED |
10 | | |
11 | | #include "include/codec/SkEncodedOrigin.h" |
12 | | #include "include/core/SkImageInfo.h" |
13 | | #include "include/core/SkPixmap.h" |
14 | | #include "include/core/SkRect.h" |
15 | | #include "include/core/SkRefCnt.h" |
16 | | #include "include/core/SkSize.h" |
17 | | #include "include/core/SkSpan.h" |
18 | | #include "include/core/SkTypes.h" |
19 | | #include "include/core/SkYUVAPixmaps.h" |
20 | | #include "include/private/SkEncodedInfo.h" |
21 | | #include "include/private/base/SkNoncopyable.h" |
22 | | #include "modules/skcms/skcms.h" |
23 | | |
24 | | #include <cstddef> |
25 | | #include <functional> |
26 | | #include <memory> |
27 | | #include <optional> |
28 | | #include <string_view> |
29 | | #include <tuple> |
30 | | #include <vector> |
31 | | |
32 | | class SkData; |
33 | | class SkFrameHolder; |
34 | | class SkImage; |
35 | | class SkPngChunkReader; |
36 | | class SkSampler; |
37 | | class SkStream; |
38 | | struct SkGainmapInfo; |
39 | | enum SkAlphaType : int; |
40 | | enum class SkEncodedImageFormat; |
41 | | |
42 | | namespace SkCodecAnimation { |
43 | | enum class Blend; |
44 | | enum class DisposalMethod; |
45 | | } |
46 | | |
47 | | namespace DM { |
48 | | class CodecSrc; |
49 | | } // namespace DM |
50 | | |
51 | | namespace SkCodecs { |
52 | | struct Decoder; |
53 | | } |
54 | | |
55 | | /** |
56 | | * Abstraction layer directly on top of an image codec. |
57 | | */ |
58 | | class SK_API SkCodec : SkNoncopyable { |
59 | | public: |
60 | | /** |
61 | | * Minimum number of bytes that must be buffered in SkStream input. |
62 | | * |
63 | | * An SkStream passed to NewFromStream must be able to use this many |
64 | | * bytes to determine the image type. Then the same SkStream must be |
65 | | * passed to the correct decoder to read from the beginning. |
66 | | * |
67 | | * This can be accomplished by implementing peek() to support peeking |
68 | | * this many bytes, or by implementing rewind() to be able to rewind() |
69 | | * after reading this many bytes. |
70 | | */ |
71 | 0 | static constexpr size_t MinBufferedBytesNeeded() { return 32; } |
72 | | |
73 | | /** |
74 | | * Error codes for various SkCodec methods. |
75 | | */ |
76 | | enum Result { |
77 | | /** |
78 | | * General return value for success. |
79 | | */ |
80 | | kSuccess, |
81 | | /** |
82 | | * The input is incomplete. A partial image was generated. |
83 | | */ |
84 | | kIncompleteInput, |
85 | | /** |
86 | | * Like kIncompleteInput, except the input had an error. |
87 | | * |
88 | | * If returned from an incremental decode, decoding cannot continue, |
89 | | * even with more data. |
90 | | */ |
91 | | kErrorInInput, |
92 | | /** |
93 | | * The generator cannot convert to match the request, ignoring |
94 | | * dimensions. |
95 | | */ |
96 | | kInvalidConversion, |
97 | | /** |
98 | | * The generator cannot scale to requested size. |
99 | | */ |
100 | | kInvalidScale, |
101 | | /** |
102 | | * Parameters (besides info) are invalid. e.g. NULL pixels, rowBytes |
103 | | * too small, etc. |
104 | | */ |
105 | | kInvalidParameters, |
106 | | /** |
107 | | * The input did not contain a valid image. |
108 | | */ |
109 | | kInvalidInput, |
110 | | /** |
111 | | * Fulfilling this request requires rewinding the input, which is not |
112 | | * supported for this input. |
113 | | */ |
114 | | kCouldNotRewind, |
115 | | /** |
116 | | * An internal error, such as OOM. |
117 | | */ |
118 | | kInternalError, |
119 | | /** |
120 | | * This method is not implemented by this codec. |
121 | | * FIXME: Perhaps this should be kUnsupported? |
122 | | */ |
123 | | kUnimplemented, |
124 | | }; |
125 | | |
126 | | /** |
127 | | * Readable string representing the error code. |
128 | | */ |
129 | | static const char* ResultToString(Result); |
130 | | |
131 | | /** |
132 | | * For container formats that contain both still images and image sequences, |
133 | | * instruct the decoder how the output should be selected. (Refer to comments |
134 | | * for each value for more details.) |
135 | | */ |
136 | | enum class SelectionPolicy { |
137 | | /** |
138 | | * If the container format contains both still images and image sequences, |
139 | | * SkCodec should choose one of the still images. This is the default. |
140 | | * Note that kPreferStillImage may prevent use of the animation features |
141 | | * if the input is not rewindable. |
142 | | */ |
143 | | kPreferStillImage, |
144 | | /** |
145 | | * If the container format contains both still images and image sequences, |
146 | | * SkCodec should choose one of the image sequences for animation. |
147 | | */ |
148 | | kPreferAnimation, |
149 | | }; |
150 | | |
151 | | /** |
152 | | * If this stream represents an encoded image that we know how to decode, |
153 | | * return an SkCodec that can decode it. Otherwise return NULL. |
154 | | * |
155 | | * As stated above, this call must be able to peek or read |
156 | | * MinBufferedBytesNeeded to determine the correct format, and then start |
157 | | * reading from the beginning. First it will attempt to peek, and it |
158 | | * assumes that if less than MinBufferedBytesNeeded bytes (but more than |
159 | | * zero) are returned, this is because the stream is shorter than this, |
160 | | * so falling back to reading would not provide more data. If peek() |
161 | | * returns zero bytes, this call will instead attempt to read(). This |
162 | | * will require that the stream can be rewind()ed. |
163 | | * |
164 | | * If Result is not NULL, it will be set to either kSuccess if an SkCodec |
165 | | * is returned or a reason for the failure if NULL is returned. |
166 | | * |
167 | | * If SkPngChunkReader is not NULL, take a ref and pass it to libpng if |
168 | | * the image is a png. |
169 | | * |
170 | | * If the SkPngChunkReader is not NULL then: |
171 | | * If the image is not a PNG, the SkPngChunkReader will be ignored. |
172 | | * If the image is a PNG, the SkPngChunkReader will be reffed. |
173 | | * If the PNG has unknown chunks, the SkPngChunkReader will be used |
174 | | * to handle these chunks. SkPngChunkReader will be called to read |
175 | | * any unknown chunk at any point during the creation of the codec |
176 | | * or the decode. Note that if SkPngChunkReader fails to read a |
177 | | * chunk, this could result in a failure to create the codec or a |
178 | | * failure to decode the image. |
179 | | * If the PNG does not contain unknown chunks, the SkPngChunkReader |
180 | | * will not be used or modified. |
181 | | * |
182 | | * If NULL is returned, the stream is deleted immediately. Otherwise, the |
183 | | * SkCodec takes ownership of it, and will delete it when done with it. |
184 | | */ |
185 | | static std::unique_ptr<SkCodec> MakeFromStream( |
186 | | std::unique_ptr<SkStream>, |
187 | | SkSpan<const SkCodecs::Decoder> decoders, |
188 | | Result* = nullptr, |
189 | | SkPngChunkReader* = nullptr, |
190 | | SelectionPolicy selectionPolicy = SelectionPolicy::kPreferStillImage); |
191 | | // deprecated |
192 | | static std::unique_ptr<SkCodec> MakeFromStream( |
193 | | std::unique_ptr<SkStream>, |
194 | | Result* = nullptr, |
195 | | SkPngChunkReader* = nullptr, |
196 | | SelectionPolicy selectionPolicy = SelectionPolicy::kPreferStillImage); |
197 | | |
198 | | /** |
199 | | * If this data represents an encoded image that we know how to decode, |
200 | | * return an SkCodec that can decode it. Otherwise return NULL. |
201 | | * |
202 | | * If the SkPngChunkReader is not NULL then: |
203 | | * If the image is not a PNG, the SkPngChunkReader will be ignored. |
204 | | * If the image is a PNG, the SkPngChunkReader will be reffed. |
205 | | * If the PNG has unknown chunks, the SkPngChunkReader will be used |
206 | | * to handle these chunks. SkPngChunkReader will be called to read |
207 | | * any unknown chunk at any point during the creation of the codec |
208 | | * or the decode. Note that if SkPngChunkReader fails to read a |
209 | | * chunk, this could result in a failure to create the codec or a |
210 | | * failure to decode the image. |
211 | | * If the PNG does not contain unknown chunks, the SkPngChunkReader |
212 | | * will not be used or modified. |
213 | | */ |
214 | | static std::unique_ptr<SkCodec> MakeFromData(sk_sp<SkData>, |
215 | | SkSpan<const SkCodecs::Decoder> decoders, |
216 | | SkPngChunkReader* = nullptr); |
217 | | // deprecated |
218 | | static std::unique_ptr<SkCodec> MakeFromData(sk_sp<SkData>, SkPngChunkReader* = nullptr); |
219 | | |
220 | | virtual ~SkCodec(); |
221 | | |
222 | | /** |
223 | | * Return a reasonable SkImageInfo to decode into. |
224 | | * |
225 | | * If the image has an ICC profile that does not map to an SkColorSpace, |
226 | | * the returned SkImageInfo will use SRGB. |
227 | | */ |
228 | 78.2k | SkImageInfo getInfo() const { return fEncodedInfo.makeImageInfo(); } |
229 | | |
230 | 340k | SkISize dimensions() const { return {fEncodedInfo.width(), fEncodedInfo.height()}; } |
231 | 22.3k | SkIRect bounds() const { |
232 | 22.3k | return SkIRect::MakeWH(fEncodedInfo.width(), fEncodedInfo.height()); |
233 | 22.3k | } |
234 | | |
235 | | /** |
236 | | * Return the ICC profile of the encoded data. |
237 | | */ |
238 | 0 | const skcms_ICCProfile* getICCProfile() const { |
239 | 0 | return this->getEncodedInfo().profile(); |
240 | 0 | } |
241 | | |
242 | | /** |
243 | | * Returns the image orientation stored in the EXIF data. |
244 | | * If there is no EXIF data, or if we cannot read the EXIF data, returns kTopLeft. |
245 | | */ |
246 | 79.6k | SkEncodedOrigin getOrigin() const { return fOrigin; } |
247 | | |
248 | | /** |
249 | | * Return a size that approximately supports the desired scale factor. |
250 | | * The codec may not be able to scale efficiently to the exact scale |
251 | | * factor requested, so return a size that approximates that scale. |
252 | | * The returned value is the codec's suggestion for the closest valid |
253 | | * scale that it can natively support |
254 | | */ |
255 | 9.25k | SkISize getScaledDimensions(float desiredScale) const { |
256 | | // Negative and zero scales are errors. |
257 | 9.25k | SkASSERT(desiredScale > 0.0f); |
258 | 9.25k | if (desiredScale <= 0.0f) { |
259 | 0 | return SkISize::Make(0, 0); |
260 | 0 | } |
261 | | |
262 | | // Upscaling is not supported. Return the original size if the client |
263 | | // requests an upscale. |
264 | 9.25k | if (desiredScale >= 1.0f) { |
265 | 0 | return this->dimensions(); |
266 | 0 | } |
267 | 9.25k | return this->onGetScaledDimensions(desiredScale); |
268 | 9.25k | } SkCodec::getScaledDimensions(float) const Line | Count | Source | 255 | 9.25k | SkISize getScaledDimensions(float desiredScale) const { | 256 | | // Negative and zero scales are errors. | 257 | 9.25k | SkASSERT(desiredScale > 0.0f); | 258 | 9.25k | if (desiredScale <= 0.0f) { | 259 | 0 | return SkISize::Make(0, 0); | 260 | 0 | } | 261 | | | 262 | | // Upscaling is not supported. Return the original size if the client | 263 | | // requests an upscale. | 264 | 9.25k | if (desiredScale >= 1.0f) { | 265 | 0 | return this->dimensions(); | 266 | 0 | } | 267 | 9.25k | return this->onGetScaledDimensions(desiredScale); | 268 | 9.25k | } |
Unexecuted instantiation: SkCodec::getScaledDimensions(float) const |
269 | | |
270 | | /** |
271 | | * Return (via desiredSubset) a subset which can decoded from this codec, |
272 | | * or false if this codec cannot decode subsets or anything similar to |
273 | | * desiredSubset. |
274 | | * |
275 | | * @param desiredSubset In/out parameter. As input, a desired subset of |
276 | | * the original bounds (as specified by getInfo). If true is returned, |
277 | | * desiredSubset may have been modified to a subset which is |
278 | | * supported. Although a particular change may have been made to |
279 | | * desiredSubset to create something supported, it is possible other |
280 | | * changes could result in a valid subset. |
281 | | * If false is returned, desiredSubset's value is undefined. |
282 | | * @return true if this codec supports decoding desiredSubset (as |
283 | | * returned, potentially modified) |
284 | | */ |
285 | 0 | bool getValidSubset(SkIRect* desiredSubset) const { |
286 | 0 | return this->onGetValidSubset(desiredSubset); |
287 | 0 | } |
288 | | |
289 | | /** |
290 | | * Format of the encoded data. |
291 | | */ |
292 | 67.8k | SkEncodedImageFormat getEncodedFormat() const { return this->onGetEncodedFormat(); } |
293 | | |
294 | | /** |
295 | | * Return the underlying encoded data stream. This may be nullptr if the original |
296 | | * stream could not be duplicated. |
297 | | */ |
298 | | virtual std::unique_ptr<SkStream> getEncodedData() const; |
299 | | |
300 | | /** |
301 | | * Whether or not the memory passed to getPixels is zero initialized. |
302 | | */ |
303 | | enum ZeroInitialized { |
304 | | /** |
305 | | * The memory passed to getPixels is zero initialized. The SkCodec |
306 | | * may take advantage of this by skipping writing zeroes. |
307 | | */ |
308 | | kYes_ZeroInitialized, |
309 | | /** |
310 | | * The memory passed to getPixels has not been initialized to zero, |
311 | | * so the SkCodec must write all zeroes to memory. |
312 | | * |
313 | | * This is the default. It will be used if no Options struct is used. |
314 | | */ |
315 | | kNo_ZeroInitialized, |
316 | | }; |
317 | | |
318 | | /** |
319 | | * Additional options to pass to getPixels. |
320 | | */ |
321 | | struct Options { |
322 | | Options() |
323 | | : fZeroInitialized(kNo_ZeroInitialized) |
324 | | , fSubset(nullptr) |
325 | | , fFrameIndex(0) |
326 | | , fPriorFrame(kNoFrame) |
327 | 293k | {} |
328 | | |
329 | | ZeroInitialized fZeroInitialized; |
330 | | /** |
331 | | * If not NULL, represents a subset of the original image to decode. |
332 | | * Must be within the bounds returned by getInfo(). |
333 | | * If the EncodedFormat is SkEncodedImageFormat::kWEBP (the only one which |
334 | | * currently supports subsets), the top and left values must be even. |
335 | | * |
336 | | * In getPixels and incremental decode, we will attempt to decode the |
337 | | * exact rectangular subset specified by fSubset. |
338 | | * |
339 | | * In a scanline decode, it does not make sense to specify a subset |
340 | | * top or subset height, since the client already controls which rows |
341 | | * to get and which rows to skip. During scanline decodes, we will |
342 | | * require that the subset top be zero and the subset height be equal |
343 | | * to the full height. We will, however, use the values of |
344 | | * subset left and subset width to decode partial scanlines on calls |
345 | | * to getScanlines(). |
346 | | */ |
347 | | const SkIRect* fSubset; |
348 | | |
349 | | /** |
350 | | * The frame to decode. |
351 | | * |
352 | | * Only meaningful for multi-frame images. |
353 | | */ |
354 | | int fFrameIndex; |
355 | | |
356 | | /** |
357 | | * If not kNoFrame, the dst already contains the prior frame at this index. |
358 | | * |
359 | | * Only meaningful for multi-frame images. |
360 | | * |
361 | | * If fFrameIndex needs to be blended with a prior frame (as reported by |
362 | | * getFrameInfo[fFrameIndex].fRequiredFrame), the client can set this to |
363 | | * any non-kRestorePrevious frame in [fRequiredFrame, fFrameIndex) to |
364 | | * indicate that that frame is already in the dst. Options.fZeroInitialized |
365 | | * is ignored in this case. |
366 | | * |
367 | | * If set to kNoFrame, the codec will decode any necessary required frame(s) first. |
368 | | */ |
369 | | int fPriorFrame; |
370 | | }; |
371 | | |
372 | | /** |
373 | | * Decode into the given pixels, a block of memory of size at |
374 | | * least (info.fHeight - 1) * rowBytes + (info.fWidth * |
375 | | * bytesPerPixel) |
376 | | * |
377 | | * Repeated calls to this function should give the same results, |
378 | | * allowing the PixelRef to be immutable. |
379 | | * |
380 | | * @param info A description of the format (config, size) |
381 | | * expected by the caller. This can simply be identical |
382 | | * to the info returned by getInfo(). |
383 | | * |
384 | | * This contract also allows the caller to specify |
385 | | * different output-configs, which the implementation can |
386 | | * decide to support or not. |
387 | | * |
388 | | * A size that does not match getInfo() implies a request |
389 | | * to scale. If the generator cannot perform this scale, |
390 | | * it will return kInvalidScale. |
391 | | * |
392 | | * If the info contains a non-null SkColorSpace, the codec |
393 | | * will perform the appropriate color space transformation. |
394 | | * |
395 | | * If the caller passes in the SkColorSpace that maps to the |
396 | | * ICC profile reported by getICCProfile(), the color space |
397 | | * transformation is a no-op. |
398 | | * |
399 | | * If the caller passes a null SkColorSpace, no color space |
400 | | * transformation will be done. |
401 | | * |
402 | | * If a scanline decode is in progress, scanline mode will end, requiring the client to call |
403 | | * startScanlineDecode() in order to return to decoding scanlines. |
404 | | * |
405 | | * @return Result kSuccess, or another value explaining the type of failure. |
406 | | */ |
407 | | Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*); |
408 | | |
409 | | /** |
410 | | * Simplified version of getPixels() that uses the default Options. |
411 | | */ |
412 | 0 | Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) { |
413 | 0 | return this->getPixels(info, pixels, rowBytes, nullptr); |
414 | 0 | } |
415 | | |
416 | 25.5k | Result getPixels(const SkPixmap& pm, const Options* opts = nullptr) { |
417 | 25.5k | return this->getPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), opts); |
418 | 25.5k | } |
419 | | |
420 | | /** |
421 | | * Return an image containing the pixels. If the codec's origin is not "upper left", |
422 | | * This will rotate the output image accordingly. |
423 | | */ |
424 | | std::tuple<sk_sp<SkImage>, SkCodec::Result> getImage(const SkImageInfo& info, |
425 | | const Options* opts = nullptr); |
426 | | std::tuple<sk_sp<SkImage>, SkCodec::Result> getImage(); |
427 | | |
428 | | /** |
429 | | * If decoding to YUV is supported, this returns true. Otherwise, this |
430 | | * returns false and the caller will ignore output parameter yuvaPixmapInfo. |
431 | | * |
432 | | * @param supportedDataTypes Indicates the data type/planar config combinations that are |
433 | | * supported by the caller. If the generator supports decoding to |
434 | | * YUV(A), but not as a type in supportedDataTypes, this method |
435 | | * returns false. |
436 | | * @param yuvaPixmapInfo Output parameter that specifies the planar configuration, subsampling, |
437 | | * orientation, chroma siting, plane color types, and row bytes. |
438 | | */ |
439 | | bool queryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& supportedDataTypes, |
440 | | SkYUVAPixmapInfo* yuvaPixmapInfo) const; |
441 | | |
442 | | /** |
443 | | * Returns kSuccess, or another value explaining the type of failure. |
444 | | * This always attempts to perform a full decode. To get the planar |
445 | | * configuration without decoding use queryYUVAInfo(). |
446 | | * |
447 | | * @param yuvaPixmaps Contains preallocated pixmaps configured according to a successful call |
448 | | * to queryYUVAInfo(). |
449 | | */ |
450 | | Result getYUVAPlanes(const SkYUVAPixmaps& yuvaPixmaps); |
451 | | |
452 | | /** |
453 | | * Prepare for an incremental decode with the specified options. |
454 | | * |
455 | | * This may require a rewind. |
456 | | * |
457 | | * If kIncompleteInput is returned, may be called again after more data has |
458 | | * been provided to the source SkStream. |
459 | | * |
460 | | * @param dstInfo Info of the destination. If the dimensions do not match |
461 | | * those of getInfo, this implies a scale. |
462 | | * @param dst Memory to write to. Needs to be large enough to hold the subset, |
463 | | * if present, or the full image as described in dstInfo. |
464 | | * @param options Contains decoding options, including if memory is zero |
465 | | * initialized and whether to decode a subset. |
466 | | * @return Enum representing success or reason for failure. |
467 | | */ |
468 | | Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, |
469 | | const Options*); |
470 | | |
471 | 4.34k | Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes) { |
472 | 4.34k | return this->startIncrementalDecode(dstInfo, dst, rowBytes, nullptr); |
473 | 4.34k | } |
474 | | |
475 | | /** |
476 | | * Start/continue the incremental decode. |
477 | | * |
478 | | * Not valid to call before a call to startIncrementalDecode() returns |
479 | | * kSuccess. |
480 | | * |
481 | | * If kIncompleteInput is returned, may be called again after more data has |
482 | | * been provided to the source SkStream. |
483 | | * |
484 | | * Unlike getPixels and getScanlines, this does not do any filling. This is |
485 | | * left up to the caller, since they may be skipping lines or continuing the |
486 | | * decode later. In the latter case, they may choose to initialize all lines |
487 | | * first, or only initialize the remaining lines after the first call. |
488 | | * |
489 | | * @param rowsDecoded Optional output variable returning the total number of |
490 | | * lines initialized. Only meaningful if this method returns kIncompleteInput. |
491 | | * Otherwise the implementation may not set it. |
492 | | * Note that some implementations may have initialized this many rows, but |
493 | | * not necessarily finished those rows (e.g. interlaced PNG). This may be |
494 | | * useful for determining what rows the client needs to initialize. |
495 | | * @return kSuccess if all lines requested in startIncrementalDecode have |
496 | | * been completely decoded. kIncompleteInput otherwise. |
497 | | */ |
498 | 3.64k | Result incrementalDecode(int* rowsDecoded = nullptr) { |
499 | 3.64k | if (!fStartedIncrementalDecode) { |
500 | 0 | return kInvalidParameters; |
501 | 0 | } |
502 | 3.64k | return this->onIncrementalDecode(rowsDecoded); |
503 | 3.64k | } |
504 | | |
505 | | /** |
506 | | * The remaining functions revolve around decoding scanlines. |
507 | | */ |
508 | | |
509 | | /** |
510 | | * Prepare for a scanline decode with the specified options. |
511 | | * |
512 | | * After this call, this class will be ready to decode the first scanline. |
513 | | * |
514 | | * This must be called in order to call getScanlines or skipScanlines. |
515 | | * |
516 | | * This may require rewinding the stream. |
517 | | * |
518 | | * Not all SkCodecs support this. |
519 | | * |
520 | | * @param dstInfo Info of the destination. If the dimensions do not match |
521 | | * those of getInfo, this implies a scale. |
522 | | * @param options Contains decoding options, including if memory is zero |
523 | | * initialized. |
524 | | * @return Enum representing success or reason for failure. |
525 | | */ |
526 | | Result startScanlineDecode(const SkImageInfo& dstInfo, const Options* options); |
527 | | |
528 | | /** |
529 | | * Simplified version of startScanlineDecode() that uses the default Options. |
530 | | */ |
531 | 542 | Result startScanlineDecode(const SkImageInfo& dstInfo) { |
532 | 542 | return this->startScanlineDecode(dstInfo, nullptr); |
533 | 542 | } |
534 | | |
535 | | /** |
536 | | * Write the next countLines scanlines into dst. |
537 | | * |
538 | | * Not valid to call before calling startScanlineDecode(). |
539 | | * |
540 | | * @param dst Must be non-null, and large enough to hold countLines |
541 | | * scanlines of size rowBytes. |
542 | | * @param countLines Number of lines to write. |
543 | | * @param rowBytes Number of bytes per row. Must be large enough to hold |
544 | | * a scanline based on the SkImageInfo used to create this object. |
545 | | * @return the number of lines successfully decoded. If this value is |
546 | | * less than countLines, this will fill the remaining lines with a |
547 | | * default value. |
548 | | */ |
549 | | int getScanlines(void* dst, int countLines, size_t rowBytes); |
550 | | |
551 | | /** |
552 | | * Skip count scanlines. |
553 | | * |
554 | | * Not valid to call before calling startScanlineDecode(). |
555 | | * |
556 | | * The default version just calls onGetScanlines and discards the dst. |
557 | | * NOTE: If skipped lines are the only lines with alpha, this default |
558 | | * will make reallyHasAlpha return true, when it could have returned |
559 | | * false. |
560 | | * |
561 | | * @return true if the scanlines were successfully skipped |
562 | | * false on failure, possible reasons for failure include: |
563 | | * An incomplete input image stream. |
564 | | * Calling this function before calling startScanlineDecode(). |
565 | | * If countLines is less than zero or so large that it moves |
566 | | * the current scanline past the end of the image. |
567 | | */ |
568 | | bool skipScanlines(int countLines); |
569 | | |
570 | | /** |
571 | | * The order in which rows are output from the scanline decoder is not the |
572 | | * same for all variations of all image types. This explains the possible |
573 | | * output row orderings. |
574 | | */ |
575 | | enum SkScanlineOrder { |
576 | | /* |
577 | | * By far the most common, this indicates that the image can be decoded |
578 | | * reliably using the scanline decoder, and that rows will be output in |
579 | | * the logical order. |
580 | | */ |
581 | | kTopDown_SkScanlineOrder, |
582 | | |
583 | | /* |
584 | | * This indicates that the scanline decoder reliably outputs rows, but |
585 | | * they will be returned in reverse order. If the scanline format is |
586 | | * kBottomUp, the nextScanline() API can be used to determine the actual |
587 | | * y-coordinate of the next output row, but the client is not forced |
588 | | * to take advantage of this, given that it's not too tough to keep |
589 | | * track independently. |
590 | | * |
591 | | * For full image decodes, it is safe to get all of the scanlines at |
592 | | * once, since the decoder will handle inverting the rows as it |
593 | | * decodes. |
594 | | * |
595 | | * For subset decodes and sampling, it is simplest to get and skip |
596 | | * scanlines one at a time, using the nextScanline() API. It is |
597 | | * possible to ask for larger chunks at a time, but this should be used |
598 | | * with caution. As with full image decodes, the decoder will handle |
599 | | * inverting the requested rows, but rows will still be delivered |
600 | | * starting from the bottom of the image. |
601 | | * |
602 | | * Upside down bmps are an example. |
603 | | */ |
604 | | kBottomUp_SkScanlineOrder, |
605 | | }; |
606 | | |
607 | | /** |
608 | | * An enum representing the order in which scanlines will be returned by |
609 | | * the scanline decoder. |
610 | | * |
611 | | * This is undefined before startScanlineDecode() is called. |
612 | | */ |
613 | 4.20M | SkScanlineOrder getScanlineOrder() const { return this->onGetScanlineOrder(); } |
614 | | |
615 | | /** |
616 | | * Returns the y-coordinate of the next row to be returned by the scanline |
617 | | * decoder. |
618 | | * |
619 | | * This will equal fCurrScanline, except in the case of strangely |
620 | | * encoded image types (bottom-up bmps). |
621 | | * |
622 | | * Results are undefined when not in scanline decoding mode. |
623 | | */ |
624 | 28.5k | int nextScanline() const { return this->outputScanline(fCurrScanline); } |
625 | | |
626 | | /** |
627 | | * Returns the output y-coordinate of the row that corresponds to an input |
628 | | * y-coordinate. The input y-coordinate represents where the scanline |
629 | | * is located in the encoded data. |
630 | | * |
631 | | * This will equal inputScanline, except in the case of strangely |
632 | | * encoded image types (bottom-up bmps, interlaced gifs). |
633 | | */ |
634 | | int outputScanline(int inputScanline) const; |
635 | | |
636 | | /** |
637 | | * Return the number of frames in the image. |
638 | | * |
639 | | * May require reading through the stream. |
640 | | */ |
641 | 7.82k | int getFrameCount() { |
642 | 7.82k | return this->onGetFrameCount(); |
643 | 7.82k | } |
644 | | |
645 | | // Sentinel value used when a frame index implies "no frame": |
646 | | // - FrameInfo::fRequiredFrame set to this value means the frame |
647 | | // is independent. |
648 | | // - Options::fPriorFrame set to this value means no (relevant) prior frame |
649 | | // is residing in dst's memory. |
650 | | static constexpr int kNoFrame = -1; |
651 | | |
652 | | // This transitional definition was added in August 2018, and will eventually be removed. |
653 | | #ifdef SK_LEGACY_SKCODEC_NONE_ENUM |
654 | | static constexpr int kNone = kNoFrame; |
655 | | #endif |
656 | | |
657 | | /** |
658 | | * Information about individual frames in a multi-framed image. |
659 | | */ |
660 | | struct FrameInfo { |
661 | | /** |
662 | | * The frame that this frame needs to be blended with, or |
663 | | * kNoFrame if this frame is independent (so it can be |
664 | | * drawn over an uninitialized buffer). |
665 | | * |
666 | | * Note that this is the *earliest* frame that can be used |
667 | | * for blending. Any frame from [fRequiredFrame, i) can be |
668 | | * used, unless its fDisposalMethod is kRestorePrevious. |
669 | | */ |
670 | | int fRequiredFrame; |
671 | | |
672 | | /** |
673 | | * Number of milliseconds to show this frame. |
674 | | */ |
675 | | int fDuration; |
676 | | |
677 | | /** |
678 | | * Whether the end marker for this frame is contained in the stream. |
679 | | * |
680 | | * Note: this does not guarantee that an attempt to decode will be complete. |
681 | | * There could be an error in the stream. |
682 | | */ |
683 | | bool fFullyReceived; |
684 | | |
685 | | /** |
686 | | * This is conservative; it will still return non-opaque if e.g. a |
687 | | * color index-based frame has a color with alpha but does not use it. |
688 | | */ |
689 | | SkAlphaType fAlphaType; |
690 | | |
691 | | /** |
692 | | * Whether the updated rectangle contains alpha. |
693 | | * |
694 | | * This is conservative; it will still be set to true if e.g. a color |
695 | | * index-based frame has a color with alpha but does not use it. In |
696 | | * addition, it may be set to true, even if the final frame, after |
697 | | * blending, is opaque. |
698 | | */ |
699 | | bool fHasAlphaWithinBounds; |
700 | | |
701 | | /** |
702 | | * How this frame should be modified before decoding the next one. |
703 | | */ |
704 | | SkCodecAnimation::DisposalMethod fDisposalMethod; |
705 | | |
706 | | /** |
707 | | * How this frame should blend with the prior frame. |
708 | | */ |
709 | | SkCodecAnimation::Blend fBlend; |
710 | | |
711 | | /** |
712 | | * The rectangle updated by this frame. |
713 | | * |
714 | | * It may be empty, if the frame does not change the image. It will |
715 | | * always be contained by SkCodec::dimensions(). |
716 | | */ |
717 | | SkIRect fFrameRect; |
718 | | }; |
719 | | |
720 | | /** |
721 | | * Return info about a single frame. |
722 | | * |
723 | | * Does not read through the stream, so it should be called after |
724 | | * getFrameCount() to parse any frames that have not already been parsed. |
725 | | * |
726 | | * Only supported by animated (multi-frame) codecs. Note that this is a |
727 | | * property of the codec (the SkCodec subclass), not the image. |
728 | | * |
729 | | * To elaborate, some codecs support animation (e.g. GIF). Others do not |
730 | | * (e.g. BMP). Animated codecs can still represent single frame images. |
731 | | * Calling getFrameInfo(0, etc) will return true for a single frame GIF |
732 | | * even if the overall image is not animated (in that the pixels on screen |
733 | | * do not change over time). When incrementally decoding a GIF image, we |
734 | | * might only know that there's a single frame *so far*. |
735 | | * |
736 | | * For non-animated SkCodec subclasses, it's sufficient but not necessary |
737 | | * for this method to always return false. |
738 | | */ |
739 | 62.2k | bool getFrameInfo(int index, FrameInfo* info) const { |
740 | 62.2k | if (index < 0) { |
741 | 0 | return false; |
742 | 0 | } |
743 | 62.2k | return this->onGetFrameInfo(index, info); |
744 | 62.2k | } |
745 | | |
746 | | /** |
747 | | * Return info about all the frames in the image. |
748 | | * |
749 | | * May require reading through the stream to determine info about the |
750 | | * frames (including the count). |
751 | | * |
752 | | * As such, future decoding calls may require a rewind. |
753 | | * |
754 | | * This may return an empty vector for non-animated codecs. See the |
755 | | * getFrameInfo(int, FrameInfo*) comment. |
756 | | */ |
757 | | std::vector<FrameInfo> getFrameInfo(); |
758 | | |
759 | | static constexpr int kRepetitionCountInfinite = -1; |
760 | | |
761 | | /** |
762 | | * Return the number of times to repeat, if this image is animated. This number does not |
763 | | * include the first play through of each frame. For example, a repetition count of 4 means |
764 | | * that each frame is played 5 times and then the animation stops. |
765 | | * |
766 | | * It can return kRepetitionCountInfinite, a negative number, meaning that the animation |
767 | | * should loop forever. |
768 | | * |
769 | | * May require reading the stream to find the repetition count. |
770 | | * |
771 | | * As such, future decoding calls may require a rewind. |
772 | | * |
773 | | * For still (non-animated) image codecs, this will return 0. |
774 | | */ |
775 | 7.82k | int getRepetitionCount() { |
776 | 7.82k | return this->onGetRepetitionCount(); |
777 | 7.82k | } |
778 | | |
779 | | // Register a decoder at runtime by passing two function pointers: |
780 | | // - peek() to return true if the span of bytes appears to be your encoded format; |
781 | | // - make() to attempt to create an SkCodec from the given stream. |
782 | | // Not thread safe. |
783 | | static void Register( |
784 | | bool (*peek)(const void*, size_t), |
785 | | std::unique_ptr<SkCodec> (*make)(std::unique_ptr<SkStream>, SkCodec::Result*)); |
786 | | |
787 | | protected: |
788 | 115k | const SkEncodedInfo& getEncodedInfo() const { return fEncodedInfo; } |
789 | | |
790 | | using XformFormat = skcms_PixelFormat; |
791 | | |
792 | | SkCodec(SkEncodedInfo&&, |
793 | | XformFormat srcFormat, |
794 | | std::unique_ptr<SkStream>, |
795 | | SkEncodedOrigin = kTopLeft_SkEncodedOrigin); |
796 | | |
797 | | void setSrcXformFormat(XformFormat pixelFormat); |
798 | | |
799 | 0 | XformFormat getSrcXformFormat() const { |
800 | 0 | return fSrcXformFormat; |
801 | 0 | } |
802 | | |
803 | 3.13k | virtual bool onGetGainmapInfo(SkGainmapInfo*, std::unique_ptr<SkStream>*) { return false; } |
804 | | |
805 | 0 | virtual SkISize onGetScaledDimensions(float /*desiredScale*/) const { |
806 | | // By default, scaling is not supported. |
807 | 0 | return this->dimensions(); |
808 | 0 | } |
809 | | |
810 | | // FIXME: What to do about subsets?? |
811 | | /** |
812 | | * Subclasses should override if they support dimensions other than the |
813 | | * srcInfo's. |
814 | | */ |
815 | 938 | virtual bool onDimensionsSupported(const SkISize&) { |
816 | 938 | return false; |
817 | 938 | } |
818 | | |
819 | | virtual SkEncodedImageFormat onGetEncodedFormat() const = 0; |
820 | | |
821 | | /** |
822 | | * @param rowsDecoded When the encoded image stream is incomplete, this function |
823 | | * will return kIncompleteInput and rowsDecoded will be set to |
824 | | * the number of scanlines that were successfully decoded. |
825 | | * This will allow getPixels() to fill the uninitialized memory. |
826 | | */ |
827 | | virtual Result onGetPixels(const SkImageInfo& info, |
828 | | void* pixels, size_t rowBytes, const Options&, |
829 | | int* rowsDecoded) = 0; |
830 | | |
831 | | virtual bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes&, |
832 | 0 | SkYUVAPixmapInfo*) const { return false; } |
833 | | |
834 | 0 | virtual Result onGetYUVAPlanes(const SkYUVAPixmaps&) { return kUnimplemented; } |
835 | | |
836 | 0 | virtual bool onGetValidSubset(SkIRect* /*desiredSubset*/) const { |
837 | | // By default, subsets are not supported. |
838 | 0 | return false; |
839 | 0 | } |
840 | | |
841 | | /** |
842 | | * If the stream was previously read, attempt to rewind. |
843 | | * |
844 | | * If the stream needed to be rewound, call onRewind. |
845 | | * @returns true if the codec is at the right position and can be used. |
846 | | * false if there was a failure to rewind. |
847 | | * |
848 | | * This is called by getPixels(), getYUV8Planes(), startIncrementalDecode() and |
849 | | * startScanlineDecode(). Subclasses may call if they need to rewind at another time. |
850 | | */ |
851 | | [[nodiscard]] bool rewindIfNeeded(); |
852 | | |
853 | | /** |
854 | | * Called by rewindIfNeeded, if the stream needed to be rewound. |
855 | | * |
856 | | * Subclasses should do any set up needed after a rewind. |
857 | | */ |
858 | 40.2k | virtual bool onRewind() { |
859 | 40.2k | return true; |
860 | 40.2k | } |
861 | | |
862 | | /** |
863 | | * Get method for the input stream |
864 | | */ |
865 | 96.6k | SkStream* stream() { |
866 | 96.6k | return fStream.get(); |
867 | 96.6k | } |
868 | | |
869 | | /** |
870 | | * The remaining functions revolve around decoding scanlines. |
871 | | */ |
872 | | |
873 | | /** |
874 | | * Most images types will be kTopDown and will not need to override this function. |
875 | | */ |
876 | 20.3k | virtual SkScanlineOrder onGetScanlineOrder() const { return kTopDown_SkScanlineOrder; } |
877 | | |
878 | 433k | const SkImageInfo& dstInfo() const { return fDstInfo; } |
879 | | |
880 | 408k | const Options& options() const { return fOptions; } |
881 | | |
882 | | /** |
883 | | * Returns the number of scanlines that have been decoded so far. |
884 | | * This is unaffected by the SkScanlineOrder. |
885 | | * |
886 | | * Returns -1 if we have not started a scanline decode. |
887 | | */ |
888 | 1.58k | int currScanline() const { return fCurrScanline; } |
889 | | |
890 | | virtual int onOutputScanline(int inputScanline) const; |
891 | | |
892 | | /** |
893 | | * Return whether we can convert to dst. |
894 | | * |
895 | | * Will be called for the appropriate frame, prior to initializing the colorXform. |
896 | | */ |
897 | | virtual bool conversionSupported(const SkImageInfo& dst, bool srcIsOpaque, |
898 | | bool needsColorXform); |
899 | | |
900 | | // Some classes never need a colorXform e.g. |
901 | | // - ICO uses its embedded codec's colorXform |
902 | | // - WBMP is just Black/White |
903 | 88.0k | virtual bool usesColorXform() const { return true; } |
904 | | void applyColorXform(void* dst, const void* src, int count) const; |
905 | | |
906 | 6.24M | bool colorXform() const { return fXformTime != kNo_XformTime; } |
907 | 16.5k | bool xformOnDecode() const { return fXformTime == kDecodeRow_XformTime; } |
908 | | |
909 | 4.51k | virtual int onGetFrameCount() { |
910 | 4.51k | return 1; |
911 | 4.51k | } |
912 | | |
913 | 4.19k | virtual bool onGetFrameInfo(int, FrameInfo*) const { |
914 | 4.19k | return false; |
915 | 4.19k | } |
916 | | |
917 | 4.51k | virtual int onGetRepetitionCount() { |
918 | 4.51k | return 0; |
919 | 4.51k | } |
920 | | |
921 | | private: |
922 | | const SkEncodedInfo fEncodedInfo; |
923 | | XformFormat fSrcXformFormat; |
924 | | std::unique_ptr<SkStream> fStream; |
925 | | bool fNeedsRewind = false; |
926 | | const SkEncodedOrigin fOrigin; |
927 | | |
928 | | SkImageInfo fDstInfo; |
929 | | Options fOptions; |
930 | | |
931 | | enum XformTime { |
932 | | kNo_XformTime, |
933 | | kPalette_XformTime, |
934 | | kDecodeRow_XformTime, |
935 | | }; |
936 | | XformTime fXformTime; |
937 | | XformFormat fDstXformFormat; // Based on fDstInfo. |
938 | | skcms_ICCProfile fDstProfile; |
939 | | skcms_AlphaFormat fDstXformAlphaFormat; |
940 | | |
941 | | // Only meaningful during scanline decodes. |
942 | | int fCurrScanline = -1; |
943 | | |
944 | | bool fStartedIncrementalDecode = false; |
945 | | |
946 | | // Allows SkAndroidCodec to call handleFrameIndex (potentially decoding a prior frame and |
947 | | // clearing to transparent) without SkCodec itself calling it, too. |
948 | | bool fUsingCallbackForHandleFrameIndex = false; |
949 | | |
950 | | bool initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha, bool srcIsOpaque); |
951 | | |
952 | | /** |
953 | | * Return whether these dimensions are supported as a scale. |
954 | | * |
955 | | * The codec may choose to cache the information about scale and subset. |
956 | | * Either way, the same information will be passed to onGetPixels/onStart |
957 | | * on success. |
958 | | * |
959 | | * This must return true for a size returned from getScaledDimensions. |
960 | | */ |
961 | 114k | bool dimensionsSupported(const SkISize& dim) { |
962 | 114k | return dim == this->dimensions() || this->onDimensionsSupported(dim); |
963 | 114k | } |
964 | | |
965 | | /** |
966 | | * For multi-framed images, return the object with information about the frames. |
967 | | */ |
968 | 0 | virtual const SkFrameHolder* getFrameHolder() const { |
969 | 0 | return nullptr; |
970 | 0 | } |
971 | | |
972 | | // Callback for decoding a prior frame. The `Options::fFrameIndex` is ignored, |
973 | | // being replaced by frameIndex. This allows opts to actually be a subclass of |
974 | | // SkCodec::Options which SkCodec itself does not know how to copy or modify, |
975 | | // but just passes through to the caller (where it can be reinterpret_cast'd). |
976 | | using GetPixelsCallback = std::function<Result(const SkImageInfo&, void* pixels, |
977 | | size_t rowBytes, const Options& opts, |
978 | | int frameIndex)>; |
979 | | |
980 | | /** |
981 | | * Check for a valid Options.fFrameIndex, and decode prior frames if necessary. |
982 | | * |
983 | | * If GetPixelsCallback is not null, it will be used to decode a prior frame instead |
984 | | * of using this SkCodec directly. It may also be used recursively, if that in turn |
985 | | * depends on a prior frame. This is used by SkAndroidCodec. |
986 | | */ |
987 | | Result handleFrameIndex(const SkImageInfo&, void* pixels, size_t rowBytes, const Options&, |
988 | | GetPixelsCallback = nullptr); |
989 | | |
990 | | // Methods for scanline decoding. |
991 | | virtual Result onStartScanlineDecode(const SkImageInfo& /*dstInfo*/, |
992 | 0 | const Options& /*options*/) { |
993 | 0 | return kUnimplemented; |
994 | 0 | } |
995 | | |
996 | | virtual Result onStartIncrementalDecode(const SkImageInfo& /*dstInfo*/, void*, size_t, |
997 | 6.72k | const Options&) { |
998 | 6.72k | return kUnimplemented; |
999 | 6.72k | } |
1000 | | |
1001 | 0 | virtual Result onIncrementalDecode(int*) { |
1002 | 0 | return kUnimplemented; |
1003 | 0 | } |
1004 | | |
1005 | | |
1006 | 0 | virtual bool onSkipScanlines(int /*countLines*/) { return false; } |
1007 | | |
1008 | 0 | virtual int onGetScanlines(void* /*dst*/, int /*countLines*/, size_t /*rowBytes*/) { return 0; } |
1009 | | |
1010 | | /** |
1011 | | * On an incomplete decode, getPixels() and getScanlines() will call this function |
1012 | | * to fill any uinitialized memory. |
1013 | | * |
1014 | | * @param dstInfo Contains the destination color type |
1015 | | * Contains the destination alpha type |
1016 | | * Contains the destination width |
1017 | | * The height stored in this info is unused |
1018 | | * @param dst Pointer to the start of destination pixel memory |
1019 | | * @param rowBytes Stride length in destination pixel memory |
1020 | | * @param zeroInit Indicates if memory is zero initialized |
1021 | | * @param linesRequested Number of lines that the client requested |
1022 | | * @param linesDecoded Number of lines that were successfully decoded |
1023 | | */ |
1024 | | void fillIncompleteImage(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, |
1025 | | ZeroInitialized zeroInit, int linesRequested, int linesDecoded); |
1026 | | |
1027 | | /** |
1028 | | * Return an object which will allow forcing scanline decodes to sample in X. |
1029 | | * |
1030 | | * May create a sampler, if one is not currently being used. Otherwise, does |
1031 | | * not affect ownership. |
1032 | | * |
1033 | | * Only valid during scanline decoding or incremental decoding. |
1034 | | */ |
1035 | 2.08k | virtual SkSampler* getSampler(bool /*createIfNecessary*/) { return nullptr; } |
1036 | | |
1037 | | friend class DM::CodecSrc; // for fillIncompleteImage |
1038 | | friend class PNGCodecGM; // for fillIncompleteImage |
1039 | | friend class SkSampledCodec; |
1040 | | friend class SkIcoCodec; |
1041 | | friend class SkAndroidCodec; // for fEncodedInfo |
1042 | | friend class SkPDFBitmap; // for fEncodedInfo |
1043 | | }; |
1044 | | |
1045 | | namespace SkCodecs { |
1046 | | |
1047 | | using DecodeContext = void*; |
1048 | | using IsFormatCallback = bool (*)(const void* data, size_t len); |
1049 | | using MakeFromStreamCallback = std::unique_ptr<SkCodec> (*)(std::unique_ptr<SkStream>, |
1050 | | SkCodec::Result*, |
1051 | | DecodeContext); |
1052 | | |
1053 | | struct SK_API Decoder { |
1054 | | // By convention, we use all lowercase letters and go with the primary filename extension. |
1055 | | // For example "png", "jpg", "ico", "webp", etc |
1056 | | std::string_view id; |
1057 | | IsFormatCallback isFormat; |
1058 | | MakeFromStreamCallback makeFromStream; |
1059 | | }; |
1060 | | |
1061 | | // Add the decoder to the end of a linked list of decoders, which will be used to identify calls to |
1062 | | // SkCodec::MakeFromStream. If a decoder with the same id already exists, this new decoder |
1063 | | // will replace the existing one (in the same position). This is not thread-safe, so make sure all |
1064 | | // initialization is done before the first call. |
1065 | | void SK_API Register(Decoder d); |
1066 | | |
1067 | | /** |
1068 | | * Return a SkImage produced by the codec, but attempts to defer image allocation until the |
1069 | | * image is actually used/drawn. This deferral allows the system to cache the result, either on the |
1070 | | * CPU or on the GPU, depending on where the image is drawn. If memory is low, the cache may |
1071 | | * be purged, causing the next draw of the image to have to re-decode. |
1072 | | * |
1073 | | * If alphaType is nullopt, the image's alpha type will be chosen automatically based on the |
1074 | | * image format. Transparent images will default to kPremul_SkAlphaType. If alphaType contains |
1075 | | * kPremul_SkAlphaType or kUnpremul_SkAlphaType, that alpha type will be used. Forcing opaque |
1076 | | * (passing kOpaque_SkAlphaType) is not allowed, and will return nullptr. |
1077 | | * |
1078 | | * @param codec A non-null codec (e.g. from SkPngDecoder::Decode) |
1079 | | * @return created SkImage, or nullptr |
1080 | | */ |
1081 | | SK_API sk_sp<SkImage> DeferredImage(std::unique_ptr<SkCodec> codec, |
1082 | | std::optional<SkAlphaType> alphaType = std::nullopt); |
1083 | | } |
1084 | | |
1085 | | #endif // SkCodec_DEFINED |