blob: d3035a7a71d8f0ec6b094d0404c1709fcb746b02 [file] [log] [blame]
scroggof24f2242015-03-03 08:59:20 -08001/*
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
bungemanf3c15b72015-08-19 11:56:48 -070011#include "../private/SkTemplates.h"
Leon Scroggins III33deb7e2017-06-07 12:31:51 -040012#include "SkCodecAnimation.h"
scroggoeb602a52015-07-09 08:16:03 -070013#include "SkColor.h"
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -040014#include "SkColorSpaceXform.h"
Hal Canarydb683012016-11-23 08:55:18 -070015#include "SkEncodedImageFormat.h"
msarettc30c4182016-04-20 11:53:35 -070016#include "SkEncodedInfo.h"
Leon Scroggins IIIb6ab10f2017-10-18 14:42:43 -040017#include "SkEncodedOrigin.h"
scroggof24f2242015-03-03 08:59:20 -080018#include "SkImageInfo.h"
Mike Reed43798692017-10-17 18:04:32 +000019#include "SkPixmap.h"
scroggof24f2242015-03-03 08:59:20 -080020#include "SkSize.h"
scroggofffeede2015-03-18 10:50:37 -070021#include "SkStream.h"
scroggof24f2242015-03-03 08:59:20 -080022#include "SkTypes.h"
msarett4984c3c2016-03-10 05:44:43 -080023#include "SkYUVSizeInfo.h"
scroggof24f2242015-03-03 08:59:20 -080024
scroggo19b91532016-10-24 09:03:26 -070025#include <vector>
26
msarettad8bcfe2016-03-07 07:09:03 -080027class SkColorSpace;
scroggof24f2242015-03-03 08:59:20 -080028class SkData;
Leon Scroggins III1f6af6b2017-06-12 16:41:09 -040029class SkFrameHolder;
scroggocf98fa92015-11-23 08:14:40 -080030class SkPngChunkReader;
scroggoe7fc14b2015-10-02 13:14:46 -070031class SkSampler;
scroggof24f2242015-03-03 08:59:20 -080032
msarett9876ac52016-06-01 14:47:18 -070033namespace DM {
scroggo8e6c7ad2016-09-16 08:20:38 -070034class CodecSrc;
msarett9876ac52016-06-01 14:47:18 -070035class ColorCodecSrc;
36}
msarett2cee9022016-06-03 08:25:21 -070037class ColorCodecBench;
msarett9876ac52016-06-01 14:47:18 -070038
scroggof24f2242015-03-03 08:59:20 -080039/**
40 * Abstraction layer directly on top of an image codec.
41 */
Florin Malita78c212a2016-12-14 13:17:01 -050042class SK_API SkCodec : SkNoncopyable {
scroggof24f2242015-03-03 08:59:20 -080043public:
44 /**
scroggodb30be22015-12-08 18:54:13 -080045 * Minimum number of bytes that must be buffered in SkStream input.
46 *
47 * An SkStream passed to NewFromStream must be able to use this many
48 * bytes to determine the image type. Then the same SkStream must be
49 * passed to the correct decoder to read from the beginning.
50 *
51 * This can be accomplished by implementing peek() to support peeking
52 * this many bytes, or by implementing rewind() to be able to rewind()
53 * after reading this many bytes.
54 */
Leon Scroggins III04be2b52017-08-17 15:13:20 -040055 static constexpr size_t MinBufferedBytesNeeded() { return 32; }
scroggodb30be22015-12-08 18:54:13 -080056
57 /**
Leon Scroggins III588fb042017-07-14 16:32:31 -040058 * Error codes for various SkCodec methods.
59 */
60 enum Result {
61 /**
62 * General return value for success.
63 */
64 kSuccess,
65 /**
66 * The input is incomplete. A partial image was generated.
67 */
68 kIncompleteInput,
69 /**
70 * Like kIncompleteInput, except the input had an error.
71 *
72 * If returned from an incremental decode, decoding cannot continue,
73 * even with more data.
74 */
75 kErrorInInput,
76 /**
77 * The generator cannot convert to match the request, ignoring
78 * dimensions.
79 */
80 kInvalidConversion,
81 /**
82 * The generator cannot scale to requested size.
83 */
84 kInvalidScale,
85 /**
86 * Parameters (besides info) are invalid. e.g. NULL pixels, rowBytes
87 * too small, etc.
88 */
89 kInvalidParameters,
90 /**
91 * The input did not contain a valid image.
92 */
93 kInvalidInput,
94 /**
95 * Fulfilling this request requires rewinding the input, which is not
96 * supported for this input.
97 */
98 kCouldNotRewind,
99 /**
100 * An internal error, such as OOM.
101 */
102 kInternalError,
103 /**
104 * This method is not implemented by this codec.
105 * FIXME: Perhaps this should be kUnsupported?
106 */
107 kUnimplemented,
108 };
109
110 /**
scroggof24f2242015-03-03 08:59:20 -0800111 * If this stream represents an encoded image that we know how to decode,
112 * return an SkCodec that can decode it. Otherwise return NULL.
113 *
scroggodb30be22015-12-08 18:54:13 -0800114 * As stated above, this call must be able to peek or read
115 * MinBufferedBytesNeeded to determine the correct format, and then start
116 * reading from the beginning. First it will attempt to peek, and it
117 * assumes that if less than MinBufferedBytesNeeded bytes (but more than
118 * zero) are returned, this is because the stream is shorter than this,
119 * so falling back to reading would not provide more data. If peek()
120 * returns zero bytes, this call will instead attempt to read(). This
121 * will require that the stream can be rewind()ed.
122 *
Leon Scroggins III588fb042017-07-14 16:32:31 -0400123 * If Result is not NULL, it will be set to either kSuccess if an SkCodec
124 * is returned or a reason for the failure if NULL is returned.
125 *
scroggodb30be22015-12-08 18:54:13 -0800126 * If SkPngChunkReader is not NULL, take a ref and pass it to libpng if
127 * the image is a png.
128 *
msarett7d5105c2015-12-02 07:02:41 -0800129 * If the SkPngChunkReader is not NULL then:
130 * If the image is not a PNG, the SkPngChunkReader will be ignored.
131 * If the image is a PNG, the SkPngChunkReader will be reffed.
132 * If the PNG has unknown chunks, the SkPngChunkReader will be used
133 * to handle these chunks. SkPngChunkReader will be called to read
134 * any unknown chunk at any point during the creation of the codec
135 * or the decode. Note that if SkPngChunkReader fails to read a
136 * chunk, this could result in a failure to create the codec or a
137 * failure to decode the image.
138 * If the PNG does not contain unknown chunks, the SkPngChunkReader
139 * will not be used or modified.
scroggocf98fa92015-11-23 08:14:40 -0800140 *
scroggof24f2242015-03-03 08:59:20 -0800141 * If NULL is returned, the stream is deleted immediately. Otherwise, the
142 * SkCodec takes ownership of it, and will delete it when done with it.
143 */
Mike Reedede7bac2017-07-23 15:30:02 -0400144 static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result* = nullptr,
145 SkPngChunkReader* = nullptr);
scroggof24f2242015-03-03 08:59:20 -0800146
147 /**
148 * If this data represents an encoded image that we know how to decode,
149 * return an SkCodec that can decode it. Otherwise return NULL.
150 *
msarett7d5105c2015-12-02 07:02:41 -0800151 * If the SkPngChunkReader is not NULL then:
152 * If the image is not a PNG, the SkPngChunkReader will be ignored.
153 * If the image is a PNG, the SkPngChunkReader will be reffed.
154 * If the PNG has unknown chunks, the SkPngChunkReader will be used
155 * to handle these chunks. SkPngChunkReader will be called to read
156 * any unknown chunk at any point during the creation of the codec
157 * or the decode. Note that if SkPngChunkReader fails to read a
158 * chunk, this could result in a failure to create the codec or a
159 * failure to decode the image.
160 * If the PNG does not contain unknown chunks, the SkPngChunkReader
161 * will not be used or modified.
scroggof24f2242015-03-03 08:59:20 -0800162 */
Mike Reedede7bac2017-07-23 15:30:02 -0400163 static std::unique_ptr<SkCodec> MakeFromData(sk_sp<SkData>, SkPngChunkReader* = nullptr);
164
scroggoeb602a52015-07-09 08:16:03 -0700165 virtual ~SkCodec();
166
167 /**
168 * Return the ImageInfo associated with this codec.
169 */
scroggo46c57472015-09-30 08:57:13 -0700170 const SkImageInfo& getInfo() const { return fSrcInfo; }
scroggoeb602a52015-07-09 08:16:03 -0700171
msarettc30c4182016-04-20 11:53:35 -0700172 const SkEncodedInfo& getEncodedInfo() const { return fEncodedInfo; }
173
msarett0e6274f2016-03-21 08:04:40 -0700174 /**
175 * Returns the image orientation stored in the EXIF data.
176 * If there is no EXIF data, or if we cannot read the EXIF data, returns kTopLeft.
177 */
Leon Scroggins IIIb6ab10f2017-10-18 14:42:43 -0400178 SkEncodedOrigin getOrigin() const { return fOrigin; }
msarett0e6274f2016-03-21 08:04:40 -0700179
msarett6a738212016-03-04 13:27:35 -0800180 /**
scroggof24f2242015-03-03 08:59:20 -0800181 * Return a size that approximately supports the desired scale factor.
182 * The codec may not be able to scale efficiently to the exact scale
183 * factor requested, so return a size that approximates that scale.
emmaleer8f4ba762015-08-14 07:44:46 -0700184 * The returned value is the codec's suggestion for the closest valid
185 * scale that it can natively support
scroggof24f2242015-03-03 08:59:20 -0800186 */
scroggofffeede2015-03-18 10:50:37 -0700187 SkISize getScaledDimensions(float desiredScale) const {
msarettb32758a2015-08-18 13:22:46 -0700188 // Negative and zero scales are errors.
189 SkASSERT(desiredScale > 0.0f);
190 if (desiredScale <= 0.0f) {
191 return SkISize::Make(0, 0);
192 }
193
194 // Upscaling is not supported. Return the original size if the client
195 // requests an upscale.
196 if (desiredScale >= 1.0f) {
197 return this->getInfo().dimensions();
198 }
scroggofffeede2015-03-18 10:50:37 -0700199 return this->onGetScaledDimensions(desiredScale);
200 }
scroggof24f2242015-03-03 08:59:20 -0800201
scroggo1dd3ea92015-03-20 11:55:55 -0700202 /**
scroggob636b452015-07-22 07:16:20 -0700203 * Return (via desiredSubset) a subset which can decoded from this codec,
204 * or false if this codec cannot decode subsets or anything similar to
205 * desiredSubset.
206 *
207 * @param desiredSubset In/out parameter. As input, a desired subset of
208 * the original bounds (as specified by getInfo). If true is returned,
209 * desiredSubset may have been modified to a subset which is
210 * supported. Although a particular change may have been made to
211 * desiredSubset to create something supported, it is possible other
212 * changes could result in a valid subset.
213 * If false is returned, desiredSubset's value is undefined.
214 * @return true if this codec supports decoding desiredSubset (as
215 * returned, potentially modified)
216 */
217 bool getValidSubset(SkIRect* desiredSubset) const {
218 return this->onGetValidSubset(desiredSubset);
219 }
220
221 /**
scroggo1dd3ea92015-03-20 11:55:55 -0700222 * Format of the encoded data.
223 */
Hal Canarydb683012016-11-23 08:55:18 -0700224 SkEncodedImageFormat getEncodedFormat() const { return this->onGetEncodedFormat(); }
scroggo1dd3ea92015-03-20 11:55:55 -0700225
scroggo05245902015-03-25 11:11:52 -0700226 /**
scroggoeb602a52015-07-09 08:16:03 -0700227 * Whether or not the memory passed to getPixels is zero initialized.
228 */
229 enum ZeroInitialized {
230 /**
231 * The memory passed to getPixels is zero initialized. The SkCodec
232 * may take advantage of this by skipping writing zeroes.
233 */
234 kYes_ZeroInitialized,
235 /**
236 * The memory passed to getPixels has not been initialized to zero,
237 * so the SkCodec must write all zeroes to memory.
238 *
239 * This is the default. It will be used if no Options struct is used.
240 */
241 kNo_ZeroInitialized,
242 };
243
244 /**
245 * Additional options to pass to getPixels.
246 */
247 struct Options {
248 Options()
scroggob636b452015-07-22 07:16:20 -0700249 : fZeroInitialized(kNo_ZeroInitialized)
scroggo19b91532016-10-24 09:03:26 -0700250 , fSubset(nullptr)
251 , fFrameIndex(0)
Leon Scroggins III33deb7e2017-06-07 12:31:51 -0400252 , fPriorFrame(kNone)
Matt Sarettcf3f2342017-03-23 15:32:25 -0400253 , fPremulBehavior(SkTransferFunctionBehavior::kRespect)
scroggob636b452015-07-22 07:16:20 -0700254 {}
scroggoeb602a52015-07-09 08:16:03 -0700255
Matt Sarettcf3f2342017-03-23 15:32:25 -0400256 ZeroInitialized fZeroInitialized;
scroggob636b452015-07-22 07:16:20 -0700257 /**
258 * If not NULL, represents a subset of the original image to decode.
scroggob636b452015-07-22 07:16:20 -0700259 * Must be within the bounds returned by getInfo().
Hal Canarydb683012016-11-23 08:55:18 -0700260 * If the EncodedFormat is SkEncodedImageFormat::kWEBP (the only one which
scroggob636b452015-07-22 07:16:20 -0700261 * currently supports subsets), the top and left values must be even.
msarettfdb47572015-10-13 12:50:14 -0700262 *
scroggo8e6c7ad2016-09-16 08:20:38 -0700263 * In getPixels and incremental decode, we will attempt to decode the
264 * exact rectangular subset specified by fSubset.
msarettfdb47572015-10-13 12:50:14 -0700265 *
266 * In a scanline decode, it does not make sense to specify a subset
267 * top or subset height, since the client already controls which rows
268 * to get and which rows to skip. During scanline decodes, we will
269 * require that the subset top be zero and the subset height be equal
270 * to the full height. We will, however, use the values of
271 * subset left and subset width to decode partial scanlines on calls
272 * to getScanlines().
scroggob636b452015-07-22 07:16:20 -0700273 */
Matt Sarettcf3f2342017-03-23 15:32:25 -0400274 const SkIRect* fSubset;
scroggo19b91532016-10-24 09:03:26 -0700275
276 /**
277 * The frame to decode.
278 *
279 * Only meaningful for multi-frame images.
280 */
Leon Scroggins III249b8e32017-04-17 12:46:33 -0400281 int fFrameIndex;
scroggo19b91532016-10-24 09:03:26 -0700282
283 /**
Leon Scroggins III33deb7e2017-06-07 12:31:51 -0400284 * If not kNone, the dst already contains the prior frame at this index.
scroggo19b91532016-10-24 09:03:26 -0700285 *
286 * Only meaningful for multi-frame images.
287 *
288 * If fFrameIndex needs to be blended with a prior frame (as reported by
289 * getFrameInfo[fFrameIndex].fRequiredFrame), the client can set this to
Leon Scroggins III33deb7e2017-06-07 12:31:51 -0400290 * any non-kRestorePrevious frame in [fRequiredFrame, fFrameIndex) to
291 * indicate that that frame is already in the dst. Options.fZeroInitialized
292 * is ignored in this case.
scroggo19b91532016-10-24 09:03:26 -0700293 *
Leon Scroggins III33deb7e2017-06-07 12:31:51 -0400294 * If set to kNone, the codec will decode any necessary required frame(s) first.
scroggo19b91532016-10-24 09:03:26 -0700295 */
Leon Scroggins III33deb7e2017-06-07 12:31:51 -0400296 int fPriorFrame;
Matt Sarettcf3f2342017-03-23 15:32:25 -0400297
298 /**
299 * Indicates whether we should do a linear premultiply or a legacy premultiply.
300 *
301 * In the case where the dst SkColorSpace is nullptr, this flag is ignored and
302 * we will always do a legacy premultiply.
303 */
304 SkTransferFunctionBehavior fPremulBehavior;
scroggoeb602a52015-07-09 08:16:03 -0700305 };
306
307 /**
308 * Decode into the given pixels, a block of memory of size at
309 * least (info.fHeight - 1) * rowBytes + (info.fWidth *
310 * bytesPerPixel)
311 *
312 * Repeated calls to this function should give the same results,
313 * allowing the PixelRef to be immutable.
314 *
315 * @param info A description of the format (config, size)
316 * expected by the caller. This can simply be identical
317 * to the info returned by getInfo().
318 *
319 * This contract also allows the caller to specify
320 * different output-configs, which the implementation can
321 * decide to support or not.
322 *
323 * A size that does not match getInfo() implies a request
324 * to scale. If the generator cannot perform this scale,
325 * it will return kInvalidScale.
326 *
msarett50ce1f22016-07-29 06:23:33 -0700327 * If the info contains a non-null SkColorSpace, the codec
328 * will perform the appropriate color space transformation.
329 * If the caller passes in the same color space that was
330 * reported by the codec, the color space transformation is
331 * a no-op.
332 *
scroggo46c57472015-09-30 08:57:13 -0700333 * If a scanline decode is in progress, scanline mode will end, requiring the client to call
334 * startScanlineDecode() in order to return to decoding scanlines.
335 *
scroggoeb602a52015-07-09 08:16:03 -0700336 * @return Result kSuccess, or another value explaining the type of failure.
337 */
Leon Scroggins571b30f2017-07-11 17:35:31 +0000338 Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options*);
scroggoeb602a52015-07-09 08:16:03 -0700339
340 /**
Leon Scroggins571b30f2017-07-11 17:35:31 +0000341 * Simplified version of getPixels() that uses the default Options.
scroggoeb602a52015-07-09 08:16:03 -0700342 */
Leon Scroggins571b30f2017-07-11 17:35:31 +0000343 Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
344 return this->getPixels(info, pixels, rowBytes, nullptr);
345 }
scroggoeb602a52015-07-09 08:16:03 -0700346
Mike Reed43798692017-10-17 18:04:32 +0000347 Result getPixels(const SkPixmap& pm, const Options* opts = nullptr) {
348 return this->getPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), opts);
349 }
350
msarettb714fb02016-01-22 14:46:42 -0800351 /**
352 * If decoding to YUV is supported, this returns true. Otherwise, this
353 * returns false and does not modify any of the parameters.
354 *
355 * @param sizeInfo Output parameter indicating the sizes and required
356 * allocation widths of the Y, U, and V planes.
357 * @param colorSpace Output parameter. If non-NULL this is set to kJPEG,
358 * otherwise this is ignored.
359 */
msarett4984c3c2016-03-10 05:44:43 -0800360 bool queryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const {
msarettb714fb02016-01-22 14:46:42 -0800361 if (nullptr == sizeInfo) {
362 return false;
363 }
364
365 return this->onQueryYUV8(sizeInfo, colorSpace);
366 }
367
368 /**
369 * Returns kSuccess, or another value explaining the type of failure.
370 * This always attempts to perform a full decode. If the client only
371 * wants size, it should call queryYUV8().
372 *
373 * @param sizeInfo Needs to exactly match the values returned by the
374 * query, except the WidthBytes may be larger than the
375 * recommendation (but not smaller).
376 * @param planes Memory for each of the Y, U, and V planes.
377 */
msarett4984c3c2016-03-10 05:44:43 -0800378 Result getYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) {
msarettb714fb02016-01-22 14:46:42 -0800379 if (nullptr == planes || nullptr == planes[0] || nullptr == planes[1] ||
380 nullptr == planes[2]) {
381 return kInvalidInput;
382 }
383
384 if (!this->rewindIfNeeded()) {
385 return kCouldNotRewind;
386 }
387
388 return this->onGetYUV8Planes(sizeInfo, planes);
389 }
390
scroggoeb602a52015-07-09 08:16:03 -0700391 /**
scroggo8e6c7ad2016-09-16 08:20:38 -0700392 * Prepare for an incremental decode with the specified options.
393 *
394 * This may require a rewind.
395 *
396 * @param dstInfo Info of the destination. If the dimensions do not match
397 * those of getInfo, this implies a scale.
398 * @param dst Memory to write to. Needs to be large enough to hold the subset,
399 * if present, or the full image as described in dstInfo.
400 * @param options Contains decoding options, including if memory is zero
401 * initialized and whether to decode a subset.
scroggo8e6c7ad2016-09-16 08:20:38 -0700402 * @return Enum representing success or reason for failure.
403 */
404 Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
Leon Scroggins571b30f2017-07-11 17:35:31 +0000405 const Options*);
scroggo8e6c7ad2016-09-16 08:20:38 -0700406
407 Result startIncrementalDecode(const SkImageInfo& dstInfo, void* dst, size_t rowBytes) {
Leon Scroggins571b30f2017-07-11 17:35:31 +0000408 return this->startIncrementalDecode(dstInfo, dst, rowBytes, nullptr);
scroggo8e6c7ad2016-09-16 08:20:38 -0700409 }
410
411 /**
412 * Start/continue the incremental decode.
413 *
414 * Not valid to call before calling startIncrementalDecode().
415 *
416 * After the first call, should only be called again if more data has been
417 * provided to the source SkStream.
418 *
419 * Unlike getPixels and getScanlines, this does not do any filling. This is
420 * left up to the caller, since they may be skipping lines or continuing the
421 * decode later. In the latter case, they may choose to initialize all lines
422 * first, or only initialize the remaining lines after the first call.
423 *
424 * @param rowsDecoded Optional output variable returning the total number of
425 * lines initialized. Only meaningful if this method returns kIncompleteInput.
426 * Otherwise the implementation may not set it.
427 * Note that some implementations may have initialized this many rows, but
428 * not necessarily finished those rows (e.g. interlaced PNG). This may be
429 * useful for determining what rows the client needs to initialize.
430 * @return kSuccess if all lines requested in startIncrementalDecode have
431 * been completely decoded. kIncompleteInput otherwise.
432 */
433 Result incrementalDecode(int* rowsDecoded = nullptr) {
434 if (!fStartedIncrementalDecode) {
435 return kInvalidParameters;
436 }
437 return this->onIncrementalDecode(rowsDecoded);
438 }
439
440 /**
scroggo46c57472015-09-30 08:57:13 -0700441 * The remaining functions revolve around decoding scanlines.
442 */
443
444 /**
445 * Prepare for a scanline decode with the specified options.
446 *
447 * After this call, this class will be ready to decode the first scanline.
448 *
449 * This must be called in order to call getScanlines or skipScanlines.
450 *
451 * This may require rewinding the stream.
452 *
453 * Not all SkCodecs support this.
454 *
455 * @param dstInfo Info of the destination. If the dimensions do not match
456 * those of getInfo, this implies a scale.
457 * @param options Contains decoding options, including if memory is zero
458 * initialized.
scroggo46c57472015-09-30 08:57:13 -0700459 * @return Enum representing success or reason for failure.
460 */
Leon Scroggins571b30f2017-07-11 17:35:31 +0000461 Result startScanlineDecode(const SkImageInfo& dstInfo, const Options* options);
scroggo46c57472015-09-30 08:57:13 -0700462
463 /**
Leon Scroggins571b30f2017-07-11 17:35:31 +0000464 * Simplified version of startScanlineDecode() that uses the default Options.
scroggo46c57472015-09-30 08:57:13 -0700465 */
Leon Scroggins571b30f2017-07-11 17:35:31 +0000466 Result startScanlineDecode(const SkImageInfo& dstInfo) {
467 return this->startScanlineDecode(dstInfo, nullptr);
468 }
scroggo46c57472015-09-30 08:57:13 -0700469
470 /**
471 * Write the next countLines scanlines into dst.
472 *
473 * Not valid to call before calling startScanlineDecode().
474 *
475 * @param dst Must be non-null, and large enough to hold countLines
476 * scanlines of size rowBytes.
477 * @param countLines Number of lines to write.
478 * @param rowBytes Number of bytes per row. Must be large enough to hold
479 * a scanline based on the SkImageInfo used to create this object.
msarette6dd0042015-10-09 11:07:34 -0700480 * @return the number of lines successfully decoded. If this value is
481 * less than countLines, this will fill the remaining lines with a
482 * default value.
scroggo46c57472015-09-30 08:57:13 -0700483 */
msarette6dd0042015-10-09 11:07:34 -0700484 int getScanlines(void* dst, int countLines, size_t rowBytes);
scroggo46c57472015-09-30 08:57:13 -0700485
486 /**
487 * Skip count scanlines.
488 *
489 * Not valid to call before calling startScanlineDecode().
490 *
491 * The default version just calls onGetScanlines and discards the dst.
492 * NOTE: If skipped lines are the only lines with alpha, this default
493 * will make reallyHasAlpha return true, when it could have returned
494 * false.
msarette6dd0042015-10-09 11:07:34 -0700495 *
496 * @return true if the scanlines were successfully skipped
497 * false on failure, possible reasons for failure include:
498 * An incomplete input image stream.
499 * Calling this function before calling startScanlineDecode().
500 * If countLines is less than zero or so large that it moves
501 * the current scanline past the end of the image.
scroggo46c57472015-09-30 08:57:13 -0700502 */
msarette6dd0042015-10-09 11:07:34 -0700503 bool skipScanlines(int countLines);
scroggo46c57472015-09-30 08:57:13 -0700504
505 /**
506 * The order in which rows are output from the scanline decoder is not the
507 * same for all variations of all image types. This explains the possible
508 * output row orderings.
509 */
510 enum SkScanlineOrder {
511 /*
512 * By far the most common, this indicates that the image can be decoded
513 * reliably using the scanline decoder, and that rows will be output in
514 * the logical order.
515 */
516 kTopDown_SkScanlineOrder,
517
518 /*
519 * This indicates that the scanline decoder reliably outputs rows, but
520 * they will be returned in reverse order. If the scanline format is
521 * kBottomUp, the nextScanline() API can be used to determine the actual
522 * y-coordinate of the next output row, but the client is not forced
523 * to take advantage of this, given that it's not too tough to keep
524 * track independently.
525 *
526 * For full image decodes, it is safe to get all of the scanlines at
527 * once, since the decoder will handle inverting the rows as it
528 * decodes.
529 *
530 * For subset decodes and sampling, it is simplest to get and skip
531 * scanlines one at a time, using the nextScanline() API. It is
532 * possible to ask for larger chunks at a time, but this should be used
533 * with caution. As with full image decodes, the decoder will handle
534 * inverting the requested rows, but rows will still be delivered
535 * starting from the bottom of the image.
536 *
537 * Upside down bmps are an example.
538 */
539 kBottomUp_SkScanlineOrder,
scroggo46c57472015-09-30 08:57:13 -0700540 };
541
542 /**
543 * An enum representing the order in which scanlines will be returned by
544 * the scanline decoder.
msarettbe8216a2015-12-04 08:00:50 -0800545 *
546 * This is undefined before startScanlineDecode() is called.
scroggo46c57472015-09-30 08:57:13 -0700547 */
548 SkScanlineOrder getScanlineOrder() const { return this->onGetScanlineOrder(); }
549
550 /**
551 * Returns the y-coordinate of the next row to be returned by the scanline
msarette6dd0042015-10-09 11:07:34 -0700552 * decoder.
553 *
554 * This will equal fCurrScanline, except in the case of strangely
scroggo19b91532016-10-24 09:03:26 -0700555 * encoded image types (bottom-up bmps).
scroggo46c57472015-09-30 08:57:13 -0700556 *
557 * Results are undefined when not in scanline decoding mode.
558 */
msarette6dd0042015-10-09 11:07:34 -0700559 int nextScanline() const { return this->outputScanline(fCurrScanline); }
560
561 /**
msarettcb0d5c92015-12-03 12:23:43 -0800562 * Returns the output y-coordinate of the row that corresponds to an input
563 * y-coordinate. The input y-coordinate represents where the scanline
564 * is located in the encoded data.
msarette6dd0042015-10-09 11:07:34 -0700565 *
566 * This will equal inputScanline, except in the case of strangely
567 * encoded image types (bottom-up bmps, interlaced gifs).
568 */
569 int outputScanline(int inputScanline) const;
scroggo46c57472015-09-30 08:57:13 -0700570
Leon Scroggins IIIe132e7b2017-04-12 10:49:52 -0400571 /**
572 * Return the number of frames in the image.
573 *
574 * May require reading through the stream.
575 */
Leon Scroggins III249b8e32017-04-17 12:46:33 -0400576 int getFrameCount() {
Leon Scroggins IIIe132e7b2017-04-12 10:49:52 -0400577 return this->onGetFrameCount();
578 }
579
scroggo19b91532016-10-24 09:03:26 -0700580 // The required frame for an independent frame is marked as
581 // kNone.
Leon Scroggins III249b8e32017-04-17 12:46:33 -0400582 static constexpr int kNone = -1;
scroggo19b91532016-10-24 09:03:26 -0700583
584 /**
585 * Information about individual frames in a multi-framed image.
586 */
587 struct FrameInfo {
588 /**
589 * The frame that this frame needs to be blended with, or
Leon Scroggins III33deb7e2017-06-07 12:31:51 -0400590 * kNone if this frame is independent.
591 *
592 * Note that this is the *earliest* frame that can be used
593 * for blending. Any frame from [fRequiredFrame, i) can be
594 * used, unless its fDisposalMethod is kRestorePrevious.
scroggo19b91532016-10-24 09:03:26 -0700595 */
Leon Scroggins III249b8e32017-04-17 12:46:33 -0400596 int fRequiredFrame;
scroggo19b91532016-10-24 09:03:26 -0700597
598 /**
599 * Number of milliseconds to show this frame.
600 */
Leon Scroggins III249b8e32017-04-17 12:46:33 -0400601 int fDuration;
Leon Scroggins III3639faa2016-12-08 11:38:58 -0500602
603 /**
604 * Whether the end marker for this frame is contained in the stream.
605 *
606 * Note: this does not guarantee that an attempt to decode will be complete.
607 * There could be an error in the stream.
608 */
609 bool fFullyReceived;
Leon Scroggins IIIa4db9be2017-04-11 10:32:02 -0400610
611 /**
612 * This is conservative; it will still return non-opaque if e.g. a
613 * color index-based frame has a color with alpha but does not use it.
614 */
Leon Scroggins IIIae79f322017-08-18 10:53:24 -0400615 SkEncodedInfo::Alpha fAlpha;
Leon Scroggins III33deb7e2017-06-07 12:31:51 -0400616
617 /**
618 * How this frame should be modified before decoding the next one.
619 */
620 SkCodecAnimation::DisposalMethod fDisposalMethod;
scroggo19b91532016-10-24 09:03:26 -0700621 };
622
623 /**
Leon Scroggins IIIe132e7b2017-04-12 10:49:52 -0400624 * Return info about a single frame.
625 *
626 * Only supported by multi-frame images. Does not read through the stream,
627 * so it should be called after getFrameCount() to parse any frames that
628 * have not already been parsed.
629 */
Leon Scroggins III249b8e32017-04-17 12:46:33 -0400630 bool getFrameInfo(int index, FrameInfo* info) const {
Leon Scroggins III557fbbe2017-05-23 09:37:21 -0400631 if (index < 0) {
632 return false;
633 }
Leon Scroggins IIIe132e7b2017-04-12 10:49:52 -0400634 return this->onGetFrameInfo(index, info);
635 }
636
637 /**
638 * Return info about all the frames in the image.
scroggo19b91532016-10-24 09:03:26 -0700639 *
scroggoe71b1a12016-11-01 08:28:28 -0700640 * May require reading through the stream to determine info about the
641 * frames (including the count).
scroggo19b91532016-10-24 09:03:26 -0700642 *
643 * As such, future decoding calls may require a rewind.
644 *
645 * For single-frame images, this will return an empty vector.
646 */
Leon Scroggins IIIe132e7b2017-04-12 10:49:52 -0400647 std::vector<FrameInfo> getFrameInfo();
scroggo19b91532016-10-24 09:03:26 -0700648
scroggoe71b1a12016-11-01 08:28:28 -0700649 static constexpr int kRepetitionCountInfinite = -1;
650
651 /**
652 * Return the number of times to repeat, if this image is animated.
653 *
654 * May require reading the stream to find the repetition count.
655 *
656 * As such, future decoding calls may require a rewind.
657 *
658 * For single-frame images, this will return 0.
659 */
660 int getRepetitionCount() {
661 return this->onGetRepetitionCount();
662 }
663
scroggof24f2242015-03-03 08:59:20 -0800664protected:
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400665 using XformFormat = SkColorSpaceXform::ColorFormat;
666
msarettc30c4182016-04-20 11:53:35 -0700667 SkCodec(int width,
668 int height,
669 const SkEncodedInfo&,
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400670 XformFormat srcFormat,
Mike Reedede7bac2017-07-23 15:30:02 -0400671 std::unique_ptr<SkStream>,
Matt Sarett7f650bd2016-10-30 21:25:34 -0400672 sk_sp<SkColorSpace>,
Leon Scroggins IIIb6ab10f2017-10-18 14:42:43 -0400673 SkEncodedOrigin = kTopLeft_SkEncodedOrigin);
scroggof24f2242015-03-03 08:59:20 -0800674
msarett549ca322016-08-17 08:54:08 -0700675 /**
msarett549ca322016-08-17 08:54:08 -0700676 * Allows the subclass to set the recommended SkImageInfo
677 */
678 SkCodec(const SkEncodedInfo&,
679 const SkImageInfo&,
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400680 XformFormat srcFormat,
Mike Reedede7bac2017-07-23 15:30:02 -0400681 std::unique_ptr<SkStream>,
Leon Scroggins IIIb6ab10f2017-10-18 14:42:43 -0400682 SkEncodedOrigin = kTopLeft_SkEncodedOrigin);
msarett549ca322016-08-17 08:54:08 -0700683
msarettb714fb02016-01-22 14:46:42 -0800684 virtual SkISize onGetScaledDimensions(float /*desiredScale*/) const {
scroggof24f2242015-03-03 08:59:20 -0800685 // By default, scaling is not supported.
halcanaryb880d7f2015-03-26 06:29:03 -0700686 return this->getInfo().dimensions();
scroggof24f2242015-03-03 08:59:20 -0800687 }
688
scroggoe7fc14b2015-10-02 13:14:46 -0700689 // FIXME: What to do about subsets??
690 /**
691 * Subclasses should override if they support dimensions other than the
692 * srcInfo's.
693 */
694 virtual bool onDimensionsSupported(const SkISize&) {
695 return false;
696 }
697
Hal Canarydb683012016-11-23 08:55:18 -0700698 virtual SkEncodedImageFormat onGetEncodedFormat() const = 0;
scroggo1dd3ea92015-03-20 11:55:55 -0700699
msarette6dd0042015-10-09 11:07:34 -0700700 /**
701 * @param rowsDecoded When the encoded image stream is incomplete, this function
702 * will return kIncompleteInput and rowsDecoded will be set to
703 * the number of scanlines that were successfully decoded.
704 * This will allow getPixels() to fill the uninitialized memory.
705 */
scroggoeb602a52015-07-09 08:16:03 -0700706 virtual Result onGetPixels(const SkImageInfo& info,
707 void* pixels, size_t rowBytes, const Options&,
msarette6dd0042015-10-09 11:07:34 -0700708 int* rowsDecoded) = 0;
scroggoeb602a52015-07-09 08:16:03 -0700709
msarett4984c3c2016-03-10 05:44:43 -0800710 virtual bool onQueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const {
msarettb714fb02016-01-22 14:46:42 -0800711 return false;
712 }
713
msarett4984c3c2016-03-10 05:44:43 -0800714 virtual Result onGetYUV8Planes(const SkYUVSizeInfo&, void*[3] /*planes*/) {
msarettb714fb02016-01-22 14:46:42 -0800715 return kUnimplemented;
716 }
717
718 virtual bool onGetValidSubset(SkIRect* /*desiredSubset*/) const {
scroggob636b452015-07-22 07:16:20 -0700719 // By default, subsets are not supported.
720 return false;
721 }
722
msarett90c4d5f2015-12-10 13:09:24 -0800723 /**
scroggof24f2242015-03-03 08:59:20 -0800724 * If the stream was previously read, attempt to rewind.
scroggob427db12015-08-12 07:24:13 -0700725 *
726 * If the stream needed to be rewound, call onRewind.
727 * @returns true if the codec is at the right position and can be used.
728 * false if there was a failure to rewind.
halcanarya096d7a2015-03-27 12:16:53 -0700729 *
scroggo3a7701c2015-09-30 09:15:14 -0700730 * This is called by getPixels() and start(). Subclasses may call if they
731 * need to rewind at another time.
scroggof24f2242015-03-03 08:59:20 -0800732 */
scroggob427db12015-08-12 07:24:13 -0700733 bool SK_WARN_UNUSED_RESULT rewindIfNeeded();
734
735 /**
736 * Called by rewindIfNeeded, if the stream needed to be rewound.
737 *
738 * Subclasses should do any set up needed after a rewind.
739 */
740 virtual bool onRewind() {
741 return true;
742 }
scroggof24f2242015-03-03 08:59:20 -0800743
msarettc0e80c12015-07-01 06:50:35 -0700744 /**
msarette6dd0042015-10-09 11:07:34 -0700745 * On an incomplete input, getPixels() and getScanlines() will fill any uninitialized
746 * scanlines. This allows the subclass to indicate what value to fill with.
747 *
msarettf7eb6fc2016-09-13 09:04:11 -0700748 * @param dstInfo Describes the destination.
msarette6dd0042015-10-09 11:07:34 -0700749 * @return The value with which to fill uninitialized pixels.
750 *
msarettf7eb6fc2016-09-13 09:04:11 -0700751 * Note that we can interpret the return value as a 64-bit Float16 color, a SkPMColor,
752 * a 16-bit 565 color, an 8-bit gray color, or an 8-bit index into a color table,
753 * depending on the color type.
msarette6dd0042015-10-09 11:07:34 -0700754 */
msarettf7eb6fc2016-09-13 09:04:11 -0700755 uint64_t getFillValue(const SkImageInfo& dstInfo) const {
756 return this->onGetFillValue(dstInfo);
msarette6dd0042015-10-09 11:07:34 -0700757 }
758
759 /**
760 * Some subclasses will override this function, but this is a useful default for the color
msarettf7eb6fc2016-09-13 09:04:11 -0700761 * types that we support. Note that for color types that do not use the full 64-bits,
msarette6dd0042015-10-09 11:07:34 -0700762 * we will simply take the low bits of the fill value.
763 *
msarettf7eb6fc2016-09-13 09:04:11 -0700764 * The defaults are:
765 * kRGBA_F16_SkColorType: Transparent or Black, depending on the src alpha type
scroggoc5560be2016-02-03 09:42:42 -0800766 * kN32_SkColorType: Transparent or Black, depending on the src alpha type
msarette6dd0042015-10-09 11:07:34 -0700767 * kRGB_565_SkColorType: Black
768 * kGray_8_SkColorType: Black
msarette6dd0042015-10-09 11:07:34 -0700769 */
msarettf7eb6fc2016-09-13 09:04:11 -0700770 virtual uint64_t onGetFillValue(const SkImageInfo& dstInfo) const;
msarette6dd0042015-10-09 11:07:34 -0700771
772 /**
msarett74114382015-03-16 11:55:18 -0700773 * Get method for the input stream
msarett74114382015-03-16 11:55:18 -0700774 */
775 SkStream* stream() {
776 return fStream.get();
777 }
778
scroggo46c57472015-09-30 08:57:13 -0700779 /**
780 * The remaining functions revolve around decoding scanlines.
781 */
782
783 /**
784 * Most images types will be kTopDown and will not need to override this function.
785 */
786 virtual SkScanlineOrder onGetScanlineOrder() const { return kTopDown_SkScanlineOrder; }
787
scroggo46c57472015-09-30 08:57:13 -0700788 const SkImageInfo& dstInfo() const { return fDstInfo; }
789
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400790 const Options& options() const { return fOptions; }
scroggo46c57472015-09-30 08:57:13 -0700791
msarettcb0d5c92015-12-03 12:23:43 -0800792 /**
793 * Returns the number of scanlines that have been decoded so far.
794 * This is unaffected by the SkScanlineOrder.
795 *
796 * Returns -1 if we have not started a scanline decode.
797 */
798 int currScanline() const { return fCurrScanline; }
799
msarette6dd0042015-10-09 11:07:34 -0700800 virtual int onOutputScanline(int inputScanline) const;
801
Leon Scroggins IIIae79f322017-08-18 10:53:24 -0400802 bool initializeColorXform(const SkImageInfo& dstInfo, SkEncodedInfo::Alpha,
Matt Sarettcf3f2342017-03-23 15:32:25 -0400803 SkTransferFunctionBehavior premulBehavior);
Leon Scroggins IIIae79f322017-08-18 10:53:24 -0400804 // Some classes never need a colorXform e.g.
805 // - ICO uses its embedded codec's colorXform
806 // - WBMP is just Black/White
807 virtual bool usesColorXform() const { return true; }
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400808 void applyColorXform(void* dst, const void* src, int count, SkAlphaType) const;
809 void applyColorXform(void* dst, const void* src, int count) const;
810
Matt Sarett313c4632016-10-20 12:35:23 -0400811 SkColorSpaceXform* colorXform() const { return fColorXform.get(); }
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400812 bool xformOnDecode() const { return fXformOnDecode; }
Matt Sarett313c4632016-10-20 12:35:23 -0400813
Leon Scroggins III249b8e32017-04-17 12:46:33 -0400814 virtual int onGetFrameCount() {
Leon Scroggins IIIe132e7b2017-04-12 10:49:52 -0400815 return 1;
816 }
817
Leon Scroggins III249b8e32017-04-17 12:46:33 -0400818 virtual bool onGetFrameInfo(int, FrameInfo*) const {
Leon Scroggins IIIe132e7b2017-04-12 10:49:52 -0400819 return false;
scroggo19b91532016-10-24 09:03:26 -0700820 }
821
scroggoe71b1a12016-11-01 08:28:28 -0700822 virtual int onGetRepetitionCount() {
823 return 0;
824 }
825
Matt Sarett313c4632016-10-20 12:35:23 -0400826private:
827 const SkEncodedInfo fEncodedInfo;
828 const SkImageInfo fSrcInfo;
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400829 const XformFormat fSrcXformFormat;
bungeman6bd52842016-10-27 09:30:08 -0700830 std::unique_ptr<SkStream> fStream;
Matt Sarett313c4632016-10-20 12:35:23 -0400831 bool fNeedsRewind;
Leon Scroggins IIIb6ab10f2017-10-18 14:42:43 -0400832 const SkEncodedOrigin fOrigin;
Matt Sarett313c4632016-10-20 12:35:23 -0400833
834 SkImageInfo fDstInfo;
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400835 Options fOptions;
836 XformFormat fDstXformFormat; // Based on fDstInfo.
Matt Sarett313c4632016-10-20 12:35:23 -0400837 std::unique_ptr<SkColorSpaceXform> fColorXform;
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400838 bool fXformOnDecode;
scroggo8e6c7ad2016-09-16 08:20:38 -0700839
840 // Only meaningful during scanline decodes.
Matt Sarett313c4632016-10-20 12:35:23 -0400841 int fCurrScanline;
scroggo46c57472015-09-30 08:57:13 -0700842
Matt Sarett313c4632016-10-20 12:35:23 -0400843 bool fStartedIncrementalDecode;
scroggo8e6c7ad2016-09-16 08:20:38 -0700844
scroggoe7fc14b2015-10-02 13:14:46 -0700845 /**
Leon Scroggins III07418182017-08-15 12:24:02 -0400846 * Return whether {srcColor, srcIsOpaque, srcCS} can convert to dst.
847 *
848 * Will be called for the appropriate frame, prior to initializing the colorXform.
849 */
850 virtual bool conversionSupported(const SkImageInfo& dst, SkEncodedInfo::Color srcColor,
851 bool srcIsOpaque, const SkColorSpace* srcCS) const;
852 /**
scroggoe7fc14b2015-10-02 13:14:46 -0700853 * Return whether these dimensions are supported as a scale.
854 *
855 * The codec may choose to cache the information about scale and subset.
856 * Either way, the same information will be passed to onGetPixels/onStart
857 * on success.
858 *
859 * This must return true for a size returned from getScaledDimensions.
860 */
861 bool dimensionsSupported(const SkISize& dim) {
862 return dim == fSrcInfo.dimensions() || this->onDimensionsSupported(dim);
863 }
864
Leon Scroggins III1f6af6b2017-06-12 16:41:09 -0400865 /**
866 * For multi-framed images, return the object with information about the frames.
867 */
868 virtual const SkFrameHolder* getFrameHolder() const {
869 return nullptr;
870 }
871
872 /**
873 * Check for a valid Options.fFrameIndex, and decode prior frames if necessary.
874 */
875 Result handleFrameIndex(const SkImageInfo&, void* pixels, size_t rowBytes, const Options&);
876
scroggo46c57472015-09-30 08:57:13 -0700877 // Methods for scanline decoding.
Leon Scroggins IIIc6e6a5f2017-06-05 15:53:38 -0400878 virtual Result onStartScanlineDecode(const SkImageInfo& /*dstInfo*/,
Leon Scroggins571b30f2017-07-11 17:35:31 +0000879 const Options& /*options*/) {
scroggo46c57472015-09-30 08:57:13 -0700880 return kUnimplemented;
881 }
882
scroggo8e6c7ad2016-09-16 08:20:38 -0700883 virtual Result onStartIncrementalDecode(const SkImageInfo& /*dstInfo*/, void*, size_t,
Leon Scroggins571b30f2017-07-11 17:35:31 +0000884 const Options&) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700885 return kUnimplemented;
886 }
887
888 virtual Result onIncrementalDecode(int*) {
889 return kUnimplemented;
890 }
891
892
msarett9b9497e2016-02-11 13:29:36 -0800893 virtual bool onSkipScanlines(int /*countLines*/) { return false; }
scroggo46c57472015-09-30 08:57:13 -0700894
msarett33bee092015-11-11 12:43:07 -0800895 virtual int onGetScanlines(void* /*dst*/, int /*countLines*/, size_t /*rowBytes*/) { return 0; }
msarette6dd0042015-10-09 11:07:34 -0700896
897 /**
898 * On an incomplete decode, getPixels() and getScanlines() will call this function
899 * to fill any uinitialized memory.
900 *
901 * @param dstInfo Contains the destination color type
902 * Contains the destination alpha type
903 * Contains the destination width
904 * The height stored in this info is unused
905 * @param dst Pointer to the start of destination pixel memory
906 * @param rowBytes Stride length in destination pixel memory
907 * @param zeroInit Indicates if memory is zero initialized
908 * @param linesRequested Number of lines that the client requested
909 * @param linesDecoded Number of lines that were successfully decoded
910 */
911 void fillIncompleteImage(const SkImageInfo& dstInfo, void* dst, size_t rowBytes,
912 ZeroInitialized zeroInit, int linesRequested, int linesDecoded);
scroggo46c57472015-09-30 08:57:13 -0700913
scroggoe7fc14b2015-10-02 13:14:46 -0700914 /**
915 * Return an object which will allow forcing scanline decodes to sample in X.
916 *
917 * May create a sampler, if one is not currently being used. Otherwise, does
918 * not affect ownership.
919 *
scroggo19b91532016-10-24 09:03:26 -0700920 * Only valid during scanline decoding or incremental decoding.
scroggoe7fc14b2015-10-02 13:14:46 -0700921 */
msarett33bee092015-11-11 12:43:07 -0800922 virtual SkSampler* getSampler(bool /*createIfNecessary*/) { return nullptr; }
scroggoe7fc14b2015-10-02 13:14:46 -0700923
scroggo8e6c7ad2016-09-16 08:20:38 -0700924 friend class DM::CodecSrc; // for fillIncompleteImage
msarett3d9d7a72015-10-21 10:27:10 -0700925 friend class SkSampledCodec;
msarettbe8216a2015-12-04 08:00:50 -0800926 friend class SkIcoCodec;
scroggof24f2242015-03-03 08:59:20 -0800927};
928#endif // SkCodec_DEFINED