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#include "SkBitmap.h"
9#include "SkCodecPriv.h"
10#include "SkColorData.h"
11#include "SkColorSpace.h"
12#include "SkColorSpacePriv.h"
13#include "SkColorTable.h"
14#include "SkMath.h"
15#include "SkOpts.h"
16#include "SkPngCodec.h"
17#include "SkPngPriv.h"
18#include "SkPoint3.h"
19#include "SkSize.h"
20#include "SkStream.h"
21#include "SkSwizzler.h"
22#include "SkTemplates.h"
23#include "SkUtils.h"
24
25#include "png.h"
26#include <algorithm>
27
28// This warning triggers false postives way too often in here.
29#if defined(__GNUC__) && !defined(__clang__)
30    #pragma GCC diagnostic ignored "-Wclobbered"
31#endif
32
33// FIXME (scroggo): We can use png_jumpbuf directly once Google3 is on 1.6
34#define PNG_JMPBUF(x) png_jmpbuf((png_structp) x)
35
36///////////////////////////////////////////////////////////////////////////////
37// Callback functions
38///////////////////////////////////////////////////////////////////////////////
39
40// When setjmp is first called, it returns 0, meaning longjmp was not called.
41constexpr int kSetJmpOkay   = 0;
42// An error internal to libpng.
43constexpr int kPngError     = 1;
44// Passed to longjmp when we have decoded as many lines as we need.
45constexpr int kStopDecoding = 2;
46
47static void sk_error_fn(png_structp png_ptr, png_const_charp msg) {
48    SkCodecPrintf("------ png error %s\n", msg);
49    longjmp(PNG_JMPBUF(png_ptr), kPngError);
50}
51
52void sk_warning_fn(png_structp, png_const_charp msg) {
53    SkCodecPrintf("----- png warning %s\n", msg);
54}
55
56#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
57static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) {
58    SkPngChunkReader* chunkReader = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr);
59    // readChunk() returning true means continue decoding
60    return chunkReader->readChunk((const char*)chunk->name, chunk->data, chunk->size) ? 1 : -1;
61}
62#endif
63
64///////////////////////////////////////////////////////////////////////////////
65// Helpers
66///////////////////////////////////////////////////////////////////////////////
67
68class AutoCleanPng : public SkNoncopyable {
69public:
70    /*
71     *  This class does not take ownership of stream or reader, but if codecPtr
72     *  is non-NULL, and decodeBounds succeeds, it will have created a new
73     *  SkCodec (pointed to by *codecPtr) which will own/ref them, as well as
74     *  the png_ptr and info_ptr.
75     */
76    AutoCleanPng(png_structp png_ptr, SkStream* stream, SkPngChunkReader* reader,
77            SkCodec** codecPtr)
78        : fPng_ptr(png_ptr)
79        , fInfo_ptr(nullptr)
80        , fStream(stream)
81        , fChunkReader(reader)
82        , fOutCodec(codecPtr)
83    {}
84
85    ~AutoCleanPng() {
86        // fInfo_ptr will never be non-nullptr unless fPng_ptr is.
87        if (fPng_ptr) {
88            png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr;
89            png_destroy_read_struct(&fPng_ptr, info_pp, nullptr);
90        }
91    }
92
93    void setInfoPtr(png_infop info_ptr) {
94        SkASSERT(nullptr == fInfo_ptr);
95        fInfo_ptr = info_ptr;
96    }
97
98    /**
99     *  Reads enough of the input stream to decode the bounds.
100     *  @return false if the stream is not a valid PNG (or too short).
101     *          true if it read enough of the stream to determine the bounds.
102     *          In the latter case, the stream may have been read beyond the
103     *          point to determine the bounds, and the png_ptr will have saved
104     *          any extra data. Further, if the codecPtr supplied to the
105     *          constructor was not NULL, it will now point to a new SkCodec,
106     *          which owns (or refs, in the case of the SkPngChunkReader) the
107     *          inputs. If codecPtr was NULL, the png_ptr and info_ptr are
108     *          unowned, and it is up to the caller to destroy them.
109     */
110    bool decodeBounds();
111
112private:
113    png_structp         fPng_ptr;
114    png_infop           fInfo_ptr;
115    SkStream*           fStream;
116    SkPngChunkReader*   fChunkReader;
117    SkCodec**           fOutCodec;
118
119    void infoCallback(size_t idatLength);
120
121    void releasePngPtrs() {
122        fPng_ptr = nullptr;
123        fInfo_ptr = nullptr;
124    }
125};
126#define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng)
127
128static inline bool is_chunk(const png_byte* chunk, const char* tag) {
129    return memcmp(chunk + 4, tag, 4) == 0;
130}
131
132static inline bool process_data(png_structp png_ptr, png_infop info_ptr,
133        SkStream* stream, void* buffer, size_t bufferSize, size_t length) {
134    while (length > 0) {
135        const size_t bytesToProcess = std::min(bufferSize, length);
136        const size_t bytesRead = stream->read(buffer, bytesToProcess);
137        png_process_data(png_ptr, info_ptr, (png_bytep) buffer, bytesRead);
138        if (bytesRead < bytesToProcess) {
139            return false;
140        }
141        length -= bytesToProcess;
142    }
143    return true;
144}
145
146bool AutoCleanPng::decodeBounds() {
147    if (setjmp(PNG_JMPBUF(fPng_ptr))) {
148        return false;
149    }
150
151    png_set_progressive_read_fn(fPng_ptr, nullptr, nullptr, nullptr, nullptr);
152
153    // Arbitrary buffer size, though note that it matches (below)
154    // SkPngCodec::processData(). FIXME: Can we better suit this to the size of
155    // the PNG header?
156    constexpr size_t kBufferSize = 4096;
157    char buffer[kBufferSize];
158
159    {
160        // Parse the signature.
161        if (fStream->read(buffer, 8) < 8) {
162            return false;
163        }
164
165        png_process_data(fPng_ptr, fInfo_ptr, (png_bytep) buffer, 8);
166    }
167
168    while (true) {
169        // Parse chunk length and type.
170        if (fStream->read(buffer, 8) < 8) {
171            // We have read to the end of the input without decoding bounds.
172            break;
173        }
174
175        png_byte* chunk = reinterpret_cast<png_byte*>(buffer);
176        const size_t length = png_get_uint_32(chunk);
177
178        if (is_chunk(chunk, "IDAT")) {
179            this->infoCallback(length);
180            return true;
181        }
182
183        png_process_data(fPng_ptr, fInfo_ptr, chunk, 8);
184        // Process the full chunk + CRC.
185        if (!process_data(fPng_ptr, fInfo_ptr, fStream, buffer, kBufferSize, length + 4)) {
186            return false;
187        }
188    }
189
190    return false;
191}
192
193bool SkPngCodec::processData() {
194    switch (setjmp(PNG_JMPBUF(fPng_ptr))) {
195        case kPngError:
196            // There was an error. Stop processing data.
197            // FIXME: Do we need to discard png_ptr?
198            return false;;
199        case kStopDecoding:
200            // We decoded all the lines we want.
201            return true;
202        case kSetJmpOkay:
203            // Everything is okay.
204            break;
205        default:
206            // No other values should be passed to longjmp.
207            SkASSERT(false);
208    }
209
210    // Arbitrary buffer size
211    constexpr size_t kBufferSize = 4096;
212    char buffer[kBufferSize];
213
214    bool iend = false;
215    while (true) {
216        size_t length;
217        if (fDecodedIdat) {
218            // Parse chunk length and type.
219            if (this->stream()->read(buffer, 8) < 8) {
220                break;
221            }
222
223            png_byte* chunk = reinterpret_cast<png_byte*>(buffer);
224            png_process_data(fPng_ptr, fInfo_ptr, chunk, 8);
225            if (is_chunk(chunk, "IEND")) {
226                iend = true;
227            }
228
229            length = png_get_uint_32(chunk);
230        } else {
231            length = fIdatLength;
232            png_byte idat[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'};
233            png_save_uint_32(idat, length);
234            png_process_data(fPng_ptr, fInfo_ptr, idat, 8);
235            fDecodedIdat = true;
236        }
237
238        // Process the full chunk + CRC.
239        if (!process_data(fPng_ptr, fInfo_ptr, this->stream(), buffer, kBufferSize, length + 4)
240                || iend) {
241            break;
242        }
243    }
244
245    return true;
246}
247
248static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
249
250// Note: SkColorTable claims to store SkPMColors, which is not necessarily the case here.
251bool SkPngCodec::createColorTable(const SkImageInfo& dstInfo) {
252
253    int numColors;
254    png_color* palette;
255    if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) {
256        return false;
257    }
258
259    // Contents depend on tableColorType and our choice of if/when to premultiply:
260    // { kPremul, kUnpremul, kOpaque } x { RGBA, BGRA }
261    SkPMColor colorTable[256];
262    SkColorType tableColorType = this->colorXform() ? kXformSrcColorType : dstInfo.colorType();
263
264    png_bytep alphas;
265    int numColorsWithAlpha = 0;
266    if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) {
267        // If we are performing a color xform, it will handle the premultiply.  Otherwise,
268        // we'll do it here.
269        bool premultiply = !this->colorXform() && needs_premul(dstInfo.alphaType(),
270                                                               this->getEncodedInfo().alpha());
271
272        // Choose which function to use to create the color table. If the final destination's
273        // colortype is unpremultiplied, the color table will store unpremultiplied colors.
274        PackColorProc proc = choose_pack_color_proc(premultiply, tableColorType);
275
276        for (int i = 0; i < numColorsWithAlpha; i++) {
277            // We don't have a function in SkOpts that combines a set of alphas with a set
278            // of RGBs.  We could write one, but it's hardly worth it, given that this
279            // is such a small fraction of the total decode time.
280            colorTable[i] = proc(alphas[i], palette->red, palette->green, palette->blue);
281            palette++;
282        }
283    }
284
285    if (numColorsWithAlpha < numColors) {
286        // The optimized code depends on a 3-byte png_color struct with the colors
287        // in RGB order.  These checks make sure it is safe to use.
288        static_assert(3 == sizeof(png_color), "png_color struct has changed.  Opts are broken.");
289#ifdef SK_DEBUG
290        SkASSERT(&palette->red < &palette->green);
291        SkASSERT(&palette->green < &palette->blue);
292#endif
293
294        if (is_rgba(tableColorType)) {
295            SkOpts::RGB_to_RGB1(colorTable + numColorsWithAlpha, palette,
296                    numColors - numColorsWithAlpha);
297        } else {
298            SkOpts::RGB_to_BGR1(colorTable + numColorsWithAlpha, palette,
299                    numColors - numColorsWithAlpha);
300        }
301    }
302
303    if (this->colorXform() && !this->xformOnDecode()) {
304        this->applyColorXform(colorTable, colorTable, numColors);
305    }
306
307    // Pad the color table with the last color in the table (or black) in the case that
308    // invalid pixel indices exceed the number of colors in the table.
309    const int maxColors = 1 << fBitDepth;
310    if (numColors < maxColors) {
311        SkPMColor lastColor = numColors > 0 ? colorTable[numColors - 1] : SK_ColorBLACK;
312        sk_memset32(colorTable + numColors, lastColor, maxColors - numColors);
313    }
314
315    fColorTable.reset(new SkColorTable(colorTable, maxColors));
316    return true;
317}
318
319///////////////////////////////////////////////////////////////////////////////
320// Creation
321///////////////////////////////////////////////////////////////////////////////
322
323bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) {
324    return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead);
325}
326
327#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
328
329static float png_fixed_point_to_float(png_fixed_point x) {
330    // We multiply by the same factor that libpng used to convert
331    // fixed point -> double.  Since we want floats, we choose to
332    // do the conversion ourselves rather than convert
333    // fixed point -> double -> float.
334    return ((float) x) * 0.00001f;
335}
336
337static float png_inverted_fixed_point_to_float(png_fixed_point x) {
338    // This is necessary because the gAMA chunk actually stores 1/gamma.
339    return 1.0f / png_fixed_point_to_float(x);
340}
341
342#endif // LIBPNG >= 1.6
343
344// Returns a colorSpace object that represents any color space information in
345// the encoded data.  If the encoded data contains an invalid/unsupported color space,
346// this will return NULL. If there is no color space information, it will guess sRGB
347sk_sp<SkColorSpace> read_color_space(png_structp png_ptr, png_infop info_ptr) {
348
349#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6)
350
351    // First check for an ICC profile
352    png_bytep profile;
353    png_uint_32 length;
354    // The below variables are unused, however, we need to pass them in anyway or
355    // png_get_iCCP() will return nothing.
356    // Could knowing the |name| of the profile ever be interesting?  Maybe for debugging?
357    png_charp name;
358    // The |compression| is uninteresting since:
359    //   (1) libpng has already decompressed the profile for us.
360    //   (2) "deflate" is the only mode of decompression that libpng supports.
361    int compression;
362    if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile,
363            &length)) {
364        return SkColorSpace::MakeICC(profile, length);
365    }
366
367    // Second, check for sRGB.
368    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
369
370        // sRGB chunks also store a rendering intent: Absolute, Relative,
371        // Perceptual, and Saturation.
372        // FIXME (msarett): Extract this information from the sRGB chunk once
373        //                  we are able to handle this information in
374        //                  SkColorSpace.
375        return SkColorSpace::MakeSRGB();
376    }
377
378    // Next, check for chromaticities.
379    png_fixed_point chrm[8];
380    png_fixed_point gamma;
381    if (png_get_cHRM_fixed(png_ptr, info_ptr, &chrm[0], &chrm[1], &chrm[2], &chrm[3], &chrm[4],
382                           &chrm[5], &chrm[6], &chrm[7]))
383    {
384        SkColorSpacePrimaries primaries;
385        primaries.fRX = png_fixed_point_to_float(chrm[2]);
386        primaries.fRY = png_fixed_point_to_float(chrm[3]);
387        primaries.fGX = png_fixed_point_to_float(chrm[4]);
388        primaries.fGY = png_fixed_point_to_float(chrm[5]);
389        primaries.fBX = png_fixed_point_to_float(chrm[6]);
390        primaries.fBY = png_fixed_point_to_float(chrm[7]);
391        primaries.fWX = png_fixed_point_to_float(chrm[0]);
392        primaries.fWY = png_fixed_point_to_float(chrm[1]);
393
394        SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
395        if (!primaries.toXYZD50(&toXYZD50)) {
396            toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
397        }
398
399        if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
400            SkColorSpaceTransferFn fn;
401            fn.fA = 1.0f;
402            fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
403            fn.fG = png_inverted_fixed_point_to_float(gamma);
404
405            return SkColorSpace::MakeRGB(fn, toXYZD50);
406        }
407
408        // Default to sRGB gamma if the image has color space information,
409        // but does not specify gamma.
410        return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
411    }
412
413    // Last, check for gamma.
414    if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
415        SkColorSpaceTransferFn fn;
416        fn.fA = 1.0f;
417        fn.fB = fn.fC = fn.fD = fn.fE = fn.fF = 0.0f;
418        fn.fG = png_inverted_fixed_point_to_float(gamma);
419
420        // Since there is no cHRM, we will guess sRGB gamut.
421        SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
422        toXYZD50.set3x3RowMajorf(gSRGB_toXYZD50);
423
424        return SkColorSpace::MakeRGB(fn, toXYZD50);
425    }
426
427#endif // LIBPNG >= 1.6
428
429    // Report that there is no color space information in the PNG.
430    // Guess sRGB in this case.
431    return SkColorSpace::MakeSRGB();
432}
433
434void SkPngCodec::allocateStorage(const SkImageInfo& dstInfo) {
435    switch (fXformMode) {
436        case kSwizzleOnly_XformMode:
437            break;
438        case kColorOnly_XformMode:
439            // Intentional fall through.  A swizzler hasn't been created yet, but one will
440            // be created later if we are sampling.  We'll go ahead and allocate
441            // enough memory to swizzle if necessary.
442        case kSwizzleColor_XformMode: {
443            const int bitsPerPixel = this->getEncodedInfo().bitsPerPixel();
444
445            // If we have more than 8-bits (per component) of precision, we will keep that
446            // extra precision.  Otherwise, we will swizzle to RGBA_8888 before transforming.
447            const size_t bytesPerPixel = (bitsPerPixel > 32) ? bitsPerPixel / 8 : 4;
448            const size_t colorXformBytes = dstInfo.width() * bytesPerPixel;
449            fStorage.reset(colorXformBytes);
450            fColorXformSrcRow = fStorage.get();
451            break;
452        }
453    }
454}
455
456static SkColorSpaceXform::ColorFormat png_select_xform_format(const SkEncodedInfo& info) {
457    // We use kRGB and kRGBA formats because color PNGs are always RGB or RGBA.
458    if (16 == info.bitsPerComponent()) {
459        if (SkEncodedInfo::kRGBA_Color == info.color()) {
460            return SkColorSpaceXform::kRGBA_U16_BE_ColorFormat;
461        } else if (SkEncodedInfo::kRGB_Color == info.color()) {
462            return SkColorSpaceXform::kRGB_U16_BE_ColorFormat;
463        }
464    }
465
466    return SkColorSpaceXform::kRGBA_8888_ColorFormat;
467}
468
469void SkPngCodec::applyXformRow(void* dst, const void* src) {
470    switch (fXformMode) {
471        case kSwizzleOnly_XformMode:
472            fSwizzler->swizzle(dst, (const uint8_t*) src);
473            break;
474        case kColorOnly_XformMode:
475            this->applyColorXform(dst, src, fXformWidth);
476            break;
477        case kSwizzleColor_XformMode:
478            fSwizzler->swizzle(fColorXformSrcRow, (const uint8_t*) src);
479            this->applyColorXform(dst, fColorXformSrcRow, fXformWidth);
480            break;
481    }
482}
483
484class SkPngNormalDecoder : public SkPngCodec {
485public:
486    SkPngNormalDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
487                       std::unique_ptr<SkStream> stream, SkPngChunkReader* reader,
488                       png_structp png_ptr, png_infop info_ptr, int bitDepth)
489        : INHERITED(info, imageInfo, std::move(stream), reader, png_ptr, info_ptr, bitDepth)
490        , fRowsWrittenToOutput(0)
491        , fDst(nullptr)
492        , fRowBytes(0)
493        , fFirstRow(0)
494        , fLastRow(0)
495    {}
496
497    static void AllRowsCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
498        GetDecoder(png_ptr)->allRowsCallback(row, rowNum);
499    }
500
501    static void RowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int /*pass*/) {
502        GetDecoder(png_ptr)->rowCallback(row, rowNum);
503    }
504
505private:
506    int                         fRowsWrittenToOutput;
507    void*                       fDst;
508    size_t                      fRowBytes;
509
510    // Variables for partial decode
511    int                         fFirstRow;  // FIXME: Move to baseclass?
512    int                         fLastRow;
513    int                         fRowsNeeded;
514
515    typedef SkPngCodec INHERITED;
516
517    static SkPngNormalDecoder* GetDecoder(png_structp png_ptr) {
518        return static_cast<SkPngNormalDecoder*>(png_get_progressive_ptr(png_ptr));
519    }
520
521    Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
522        const int height = this->getInfo().height();
523        png_set_progressive_read_fn(this->png_ptr(), this, nullptr, AllRowsCallback, nullptr);
524        fDst = dst;
525        fRowBytes = rowBytes;
526
527        fRowsWrittenToOutput = 0;
528        fFirstRow = 0;
529        fLastRow = height - 1;
530
531        if (!this->processData()) {
532            return kErrorInInput;
533        }
534
535        if (fRowsWrittenToOutput == height) {
536            return SkCodec::kSuccess;
537        }
538
539        if (rowsDecoded) {
540            *rowsDecoded = fRowsWrittenToOutput;
541        }
542
543        return SkCodec::kIncompleteInput;
544    }
545
546    void allRowsCallback(png_bytep row, int rowNum) {
547        SkASSERT(rowNum == fRowsWrittenToOutput);
548        fRowsWrittenToOutput++;
549        this->applyXformRow(fDst, row);
550        fDst = SkTAddOffset<void>(fDst, fRowBytes);
551    }
552
553    void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
554        png_set_progressive_read_fn(this->png_ptr(), this, nullptr, RowCallback, nullptr);
555        fFirstRow = firstRow;
556        fLastRow = lastRow;
557        fDst = dst;
558        fRowBytes = rowBytes;
559        fRowsWrittenToOutput = 0;
560        fRowsNeeded = fLastRow - fFirstRow + 1;
561    }
562
563    SkCodec::Result decode(int* rowsDecoded) override {
564        if (this->swizzler()) {
565            const int sampleY = this->swizzler()->sampleY();
566            fRowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY);
567        }
568
569        if (!this->processData()) {
570            return kErrorInInput;
571        }
572
573        if (fRowsWrittenToOutput == fRowsNeeded) {
574            return SkCodec::kSuccess;
575        }
576
577        if (rowsDecoded) {
578            *rowsDecoded = fRowsWrittenToOutput;
579        }
580
581        return SkCodec::kIncompleteInput;
582    }
583
584    void rowCallback(png_bytep row, int rowNum) {
585        if (rowNum < fFirstRow) {
586            // Ignore this row.
587            return;
588        }
589
590        SkASSERT(rowNum <= fLastRow);
591        SkASSERT(fRowsWrittenToOutput < fRowsNeeded);
592
593        // If there is no swizzler, all rows are needed.
594        if (!this->swizzler() || this->swizzler()->rowNeeded(rowNum - fFirstRow)) {
595            this->applyXformRow(fDst, row);
596            fDst = SkTAddOffset<void>(fDst, fRowBytes);
597            fRowsWrittenToOutput++;
598        }
599
600        if (fRowsWrittenToOutput == fRowsNeeded) {
601            // Fake error to stop decoding scanlines.
602            longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
603        }
604    }
605};
606
607class SkPngInterlacedDecoder : public SkPngCodec {
608public:
609    SkPngInterlacedDecoder(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
610            std::unique_ptr<SkStream> stream, SkPngChunkReader* reader, png_structp png_ptr,
611            png_infop info_ptr, int bitDepth, int numberPasses)
612        : INHERITED(info, imageInfo, std::move(stream), reader, png_ptr, info_ptr, bitDepth)
613        , fNumberPasses(numberPasses)
614        , fFirstRow(0)
615        , fLastRow(0)
616        , fLinesDecoded(0)
617        , fInterlacedComplete(false)
618        , fPng_rowbytes(0)
619    {}
620
621    static void InterlacedRowCallback(png_structp png_ptr, png_bytep row, png_uint_32 rowNum, int pass) {
622        auto decoder = static_cast<SkPngInterlacedDecoder*>(png_get_progressive_ptr(png_ptr));
623        decoder->interlacedRowCallback(row, rowNum, pass);
624    }
625
626private:
627    const int               fNumberPasses;
628    int                     fFirstRow;
629    int                     fLastRow;
630    void*                   fDst;
631    size_t                  fRowBytes;
632    int                     fLinesDecoded;
633    bool                    fInterlacedComplete;
634    size_t                  fPng_rowbytes;
635    SkAutoTMalloc<png_byte> fInterlaceBuffer;
636
637    typedef SkPngCodec INHERITED;
638
639    // FIXME: Currently sharing interlaced callback for all rows and subset. It's not
640    // as expensive as the subset version of non-interlaced, but it still does extra
641    // work.
642    void interlacedRowCallback(png_bytep row, int rowNum, int pass) {
643        if (rowNum < fFirstRow || rowNum > fLastRow || fInterlacedComplete) {
644            // Ignore this row
645            return;
646        }
647
648        png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_rowbytes;
649        png_progressive_combine_row(this->png_ptr(), oldRow, row);
650
651        if (0 == pass) {
652            // The first pass initializes all rows.
653            SkASSERT(row);
654            SkASSERT(fLinesDecoded == rowNum - fFirstRow);
655            fLinesDecoded++;
656        } else {
657            SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1);
658            if (fNumberPasses - 1 == pass && rowNum == fLastRow) {
659                // Last pass, and we have read all of the rows we care about.
660                fInterlacedComplete = true;
661                if (fLastRow != this->getInfo().height() - 1 ||
662                        (this->swizzler() && this->swizzler()->sampleY() != 1)) {
663                    // Fake error to stop decoding scanlines. Only stop if we're not decoding the
664                    // whole image, in which case processing the rest of the image might be
665                    // expensive. When decoding the whole image, read through the IEND chunk to
666                    // preserve Android behavior of leaving the input stream in the right place.
667                    longjmp(PNG_JMPBUF(this->png_ptr()), kStopDecoding);
668                }
669            }
670        }
671    }
672
673    SkCodec::Result decodeAllRows(void* dst, size_t rowBytes, int* rowsDecoded) override {
674        const int height = this->getInfo().height();
675        this->setUpInterlaceBuffer(height);
676        png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback,
677                                    nullptr);
678
679        fFirstRow = 0;
680        fLastRow = height - 1;
681        fLinesDecoded = 0;
682
683        if (!this->processData()) {
684            return kErrorInInput;
685        }
686
687        png_bytep srcRow = fInterlaceBuffer.get();
688        // FIXME: When resuming, this may rewrite rows that did not change.
689        for (int rowNum = 0; rowNum < fLinesDecoded; rowNum++) {
690            this->applyXformRow(dst, srcRow);
691            dst = SkTAddOffset<void>(dst, rowBytes);
692            srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes);
693        }
694        if (fInterlacedComplete) {
695            return SkCodec::kSuccess;
696        }
697
698        if (rowsDecoded) {
699            *rowsDecoded = fLinesDecoded;
700        }
701
702        return SkCodec::kIncompleteInput;
703    }
704
705    void setRange(int firstRow, int lastRow, void* dst, size_t rowBytes) override {
706        // FIXME: We could skip rows in the interlace buffer that we won't put in the output.
707        this->setUpInterlaceBuffer(lastRow - firstRow + 1);
708        png_set_progressive_read_fn(this->png_ptr(), this, nullptr, InterlacedRowCallback, nullptr);
709        fFirstRow = firstRow;
710        fLastRow = lastRow;
711        fDst = dst;
712        fRowBytes = rowBytes;
713        fLinesDecoded = 0;
714    }
715
716    SkCodec::Result decode(int* rowsDecoded) override {
717        if (this->processData() == false) {
718            return kErrorInInput;
719        }
720
721        // Now apply Xforms on all the rows that were decoded.
722        if (!fLinesDecoded) {
723            if (rowsDecoded) {
724                *rowsDecoded = 0;
725            }
726            return SkCodec::kIncompleteInput;
727        }
728
729        const int sampleY = this->swizzler() ? this->swizzler()->sampleY() : 1;
730        const int rowsNeeded = get_scaled_dimension(fLastRow - fFirstRow + 1, sampleY);
731        int rowsWrittenToOutput = 0;
732
733        // FIXME: For resuming interlace, we may swizzle a row that hasn't changed. But it
734        // may be too tricky/expensive to handle that correctly.
735
736        // Offset srcRow by get_start_coord rows. We do not need to account for fFirstRow,
737        // since the first row in fInterlaceBuffer corresponds to fFirstRow.
738        png_bytep srcRow = SkTAddOffset<png_byte>(fInterlaceBuffer.get(),
739                                                  fPng_rowbytes * get_start_coord(sampleY));
740        void* dst = fDst;
741        for (; rowsWrittenToOutput < rowsNeeded; rowsWrittenToOutput++) {
742            this->applyXformRow(dst, srcRow);
743            dst = SkTAddOffset<void>(dst, fRowBytes);
744            srcRow = SkTAddOffset<png_byte>(srcRow, fPng_rowbytes * sampleY);
745        }
746
747        if (fInterlacedComplete) {
748            return SkCodec::kSuccess;
749        }
750
751        if (rowsDecoded) {
752            *rowsDecoded = rowsWrittenToOutput;
753        }
754        return SkCodec::kIncompleteInput;
755    }
756
757    void setUpInterlaceBuffer(int height) {
758        fPng_rowbytes = png_get_rowbytes(this->png_ptr(), this->info_ptr());
759        fInterlaceBuffer.reset(fPng_rowbytes * height);
760        fInterlacedComplete = false;
761    }
762};
763
764// Reads the header and initializes the output fields, if not NULL.
765//
766// @param stream Input data. Will be read to get enough information to properly
767//      setup the codec.
768// @param chunkReader SkPngChunkReader, for reading unknown chunks. May be NULL.
769//      If not NULL, png_ptr will hold an *unowned* pointer to it. The caller is
770//      expected to continue to own it for the lifetime of the png_ptr.
771// @param outCodec Optional output variable.  If non-NULL, will be set to a new
772//      SkPngCodec on success.
773// @param png_ptrp Optional output variable. If non-NULL, will be set to a new
774//      png_structp on success.
775// @param info_ptrp Optional output variable. If non-NULL, will be set to a new
776//      png_infop on success;
777// @return if kSuccess, the caller is responsible for calling
778//      png_destroy_read_struct(png_ptrp, info_ptrp).
779//      Otherwise, the passed in fields (except stream) are unchanged.
780static SkCodec::Result read_header(SkStream* stream, SkPngChunkReader* chunkReader,
781                                   SkCodec** outCodec,
782                                   png_structp* png_ptrp, png_infop* info_ptrp) {
783    // The image is known to be a PNG. Decode enough to know the SkImageInfo.
784    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
785                                                 sk_error_fn, sk_warning_fn);
786    if (!png_ptr) {
787        return SkCodec::kInternalError;
788    }
789
790#ifdef PNG_SET_OPTION_SUPPORTED
791    // This setting ensures that we display images with incorrect CMF bytes.
792    // See crbug.com/807324.
793    png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
794#endif
795
796    AutoCleanPng autoClean(png_ptr, stream, chunkReader, outCodec);
797
798    png_infop info_ptr = png_create_info_struct(png_ptr);
799    if (info_ptr == nullptr) {
800        return SkCodec::kInternalError;
801    }
802
803    autoClean.setInfoPtr(info_ptr);
804
805    if (setjmp(PNG_JMPBUF(png_ptr))) {
806        return SkCodec::kInvalidInput;
807    }
808
809#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
810    // Hookup our chunkReader so we can see any user-chunks the caller may be interested in.
811    // This needs to be installed before we read the png header.  Android may store ninepatch
812    // chunks in the header.
813    if (chunkReader) {
814        png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);
815        png_set_read_user_chunk_fn(png_ptr, (png_voidp) chunkReader, sk_read_user_chunk);
816    }
817#endif
818
819    const bool decodedBounds = autoClean.decodeBounds();
820
821    if (!decodedBounds) {
822        return SkCodec::kIncompleteInput;
823    }
824
825    // On success, decodeBounds releases ownership of png_ptr and info_ptr.
826    if (png_ptrp) {
827        *png_ptrp = png_ptr;
828    }
829    if (info_ptrp) {
830        *info_ptrp = info_ptr;
831    }
832
833    // decodeBounds takes care of setting outCodec
834    if (outCodec) {
835        SkASSERT(*outCodec);
836    }
837    return SkCodec::kSuccess;
838}
839
840void AutoCleanPng::infoCallback(size_t idatLength) {
841    png_uint_32 origWidth, origHeight;
842    int bitDepth, encodedColorType;
843    png_get_IHDR(fPng_ptr, fInfo_ptr, &origWidth, &origHeight, &bitDepth,
844                 &encodedColorType, nullptr, nullptr, nullptr);
845
846    // TODO: Should we support 16-bits of precision for gray images?
847    if (bitDepth == 16 && (PNG_COLOR_TYPE_GRAY == encodedColorType ||
848                           PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType)) {
849        bitDepth = 8;
850        png_set_strip_16(fPng_ptr);
851    }
852
853    // Now determine the default colorType and alphaType and set the required transforms.
854    // Often, we depend on SkSwizzler to perform any transforms that we need.  However, we
855    // still depend on libpng for many of the rare and PNG-specific cases.
856    SkEncodedInfo::Color color;
857    SkEncodedInfo::Alpha alpha;
858    switch (encodedColorType) {
859        case PNG_COLOR_TYPE_PALETTE:
860            // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
861            // byte into separate bytes (useful for paletted and grayscale images).
862            if (bitDepth < 8) {
863                // TODO: Should we use SkSwizzler here?
864                bitDepth = 8;
865                png_set_packing(fPng_ptr);
866            }
867
868            color = SkEncodedInfo::kPalette_Color;
869            // Set the alpha depending on if a transparency chunk exists.
870            alpha = png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS) ?
871                    SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha;
872            break;
873        case PNG_COLOR_TYPE_RGB:
874            if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
875                // Convert to RGBA if transparency chunk exists.
876                png_set_tRNS_to_alpha(fPng_ptr);
877                color = SkEncodedInfo::kRGBA_Color;
878                alpha = SkEncodedInfo::kBinary_Alpha;
879            } else {
880                color = SkEncodedInfo::kRGB_Color;
881                alpha = SkEncodedInfo::kOpaque_Alpha;
882            }
883            break;
884        case PNG_COLOR_TYPE_GRAY:
885            // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel.
886            if (bitDepth < 8) {
887                // TODO: Should we use SkSwizzler here?
888                bitDepth = 8;
889                png_set_expand_gray_1_2_4_to_8(fPng_ptr);
890            }
891
892            if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) {
893                png_set_tRNS_to_alpha(fPng_ptr);
894                color = SkEncodedInfo::kGrayAlpha_Color;
895                alpha = SkEncodedInfo::kBinary_Alpha;
896            } else {
897                color = SkEncodedInfo::kGray_Color;
898                alpha = SkEncodedInfo::kOpaque_Alpha;
899            }
900            break;
901        case PNG_COLOR_TYPE_GRAY_ALPHA:
902            color = SkEncodedInfo::kGrayAlpha_Color;
903            alpha = SkEncodedInfo::kUnpremul_Alpha;
904            break;
905        case PNG_COLOR_TYPE_RGBA:
906            color = SkEncodedInfo::kRGBA_Color;
907            alpha = SkEncodedInfo::kUnpremul_Alpha;
908            break;
909        default:
910            // All the color types have been covered above.
911            SkASSERT(false);
912            color = SkEncodedInfo::kRGBA_Color;
913            alpha = SkEncodedInfo::kUnpremul_Alpha;
914    }
915
916    const int numberPasses = png_set_interlace_handling(fPng_ptr);
917
918    if (fOutCodec) {
919        SkASSERT(nullptr == *fOutCodec);
920        sk_sp<SkColorSpace> colorSpace = read_color_space(fPng_ptr, fInfo_ptr);
921        if (colorSpace) {
922            switch (colorSpace->type()) {
923                case SkColorSpace::kCMYK_Type:
924                    colorSpace = nullptr;
925                    break;
926                case SkColorSpace::kGray_Type:
927                    if (SkEncodedInfo::kGray_Color != color &&
928                        SkEncodedInfo::kGrayAlpha_Color != color)
929                    {
930                        colorSpace = nullptr;
931                    }
932                    break;
933                case SkColorSpace::kRGB_Type:
934                    break;
935            }
936        }
937        if (!colorSpace) {
938            // Treat unsupported/invalid color spaces as sRGB.
939            colorSpace = SkColorSpace::MakeSRGB();
940        }
941
942        SkEncodedInfo encodedInfo = SkEncodedInfo::Make(color, alpha, bitDepth);
943        SkImageInfo imageInfo = encodedInfo.makeImageInfo(origWidth, origHeight, colorSpace);
944
945        if (encodedColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
946            png_color_8p sigBits;
947            if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
948                if (8 == sigBits->alpha && kGraySigBit_GrayAlphaIsJustAlpha == sigBits->gray) {
949                    imageInfo = imageInfo.makeColorType(kAlpha_8_SkColorType);
950                }
951            }
952        } else if (SkEncodedInfo::kOpaque_Alpha == alpha) {
953            png_color_8p sigBits;
954            if (png_get_sBIT(fPng_ptr, fInfo_ptr, &sigBits)) {
955                if (5 == sigBits->red && 6 == sigBits->green && 5 == sigBits->blue) {
956                    // Recommend a decode to 565 if the sBIT indicates 565.
957                    imageInfo = imageInfo.makeColorType(kRGB_565_SkColorType);
958                }
959            }
960        }
961
962        if (1 == numberPasses) {
963            *fOutCodec = new SkPngNormalDecoder(encodedInfo, imageInfo,
964                   std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth);
965        } else {
966            *fOutCodec = new SkPngInterlacedDecoder(encodedInfo, imageInfo,
967                    std::unique_ptr<SkStream>(fStream), fChunkReader, fPng_ptr, fInfo_ptr, bitDepth,
968                    numberPasses);
969        }
970        static_cast<SkPngCodec*>(*fOutCodec)->setIdatLength(idatLength);
971    }
972
973    // Release the pointers, which are now owned by the codec or the caller is expected to
974    // take ownership.
975    this->releasePngPtrs();
976}
977
978SkPngCodec::SkPngCodec(const SkEncodedInfo& encodedInfo, const SkImageInfo& imageInfo,
979                       std::unique_ptr<SkStream> stream, SkPngChunkReader* chunkReader,
980                       void* png_ptr, void* info_ptr, int bitDepth)
981    : INHERITED(encodedInfo, imageInfo, png_select_xform_format(encodedInfo), std::move(stream))
982    , fPngChunkReader(SkSafeRef(chunkReader))
983    , fPng_ptr(png_ptr)
984    , fInfo_ptr(info_ptr)
985    , fColorXformSrcRow(nullptr)
986    , fBitDepth(bitDepth)
987    , fIdatLength(0)
988    , fDecodedIdat(false)
989{}
990
991SkPngCodec::~SkPngCodec() {
992    this->destroyReadStruct();
993}
994
995void SkPngCodec::destroyReadStruct() {
996    if (fPng_ptr) {
997        // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr
998        SkASSERT(fInfo_ptr);
999        png_destroy_read_struct((png_struct**)&fPng_ptr, (png_info**)&fInfo_ptr, nullptr);
1000        fPng_ptr = nullptr;
1001        fInfo_ptr = nullptr;
1002    }
1003}
1004
1005///////////////////////////////////////////////////////////////////////////////
1006// Getting the pixels
1007///////////////////////////////////////////////////////////////////////////////
1008
1009SkCodec::Result SkPngCodec::initializeXforms(const SkImageInfo& dstInfo, const Options& options) {
1010    if (setjmp(PNG_JMPBUF((png_struct*)fPng_ptr))) {
1011        SkCodecPrintf("Failed on png_read_update_info.\n");
1012        return kInvalidInput;
1013    }
1014    png_read_update_info(fPng_ptr, fInfo_ptr);
1015
1016    // Reset fSwizzler and this->colorXform().  We can't do this in onRewind() because the
1017    // interlaced scanline decoder may need to rewind.
1018    fSwizzler.reset(nullptr);
1019
1020    // If SkColorSpaceXform directly supports the encoded PNG format, we should skip format
1021    // conversion in the swizzler (or skip swizzling altogether).
1022    bool skipFormatConversion = false;
1023    switch (this->getEncodedInfo().color()) {
1024        case SkEncodedInfo::kRGB_Color:
1025            if (this->getEncodedInfo().bitsPerComponent() != 16) {
1026                break;
1027            }
1028
1029            // Fall through
1030        case SkEncodedInfo::kRGBA_Color:
1031            skipFormatConversion = this->colorXform();
1032            break;
1033        default:
1034            break;
1035    }
1036    if (skipFormatConversion && !options.fSubset) {
1037        fXformMode = kColorOnly_XformMode;
1038        return kSuccess;
1039    }
1040
1041    if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) {
1042        if (!this->createColorTable(dstInfo)) {
1043            return kInvalidInput;
1044        }
1045    }
1046
1047    this->initializeSwizzler(dstInfo, options, skipFormatConversion);
1048    return kSuccess;
1049}
1050
1051void SkPngCodec::initializeXformParams() {
1052    switch (fXformMode) {
1053        case kColorOnly_XformMode:
1054            fXformWidth = this->dstInfo().width();
1055            break;
1056        case kSwizzleColor_XformMode:
1057            fXformWidth = this->swizzler()->swizzleWidth();
1058            break;
1059        default:
1060            break;
1061    }
1062}
1063
1064void SkPngCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options,
1065                                    bool skipFormatConversion) {
1066    SkImageInfo swizzlerInfo = dstInfo;
1067    Options swizzlerOptions = options;
1068    fXformMode = kSwizzleOnly_XformMode;
1069    if (this->colorXform() && this->xformOnDecode()) {
1070        swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
1071        if (kPremul_SkAlphaType == dstInfo.alphaType()) {
1072            swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
1073        }
1074
1075        fXformMode = kSwizzleColor_XformMode;
1076
1077        // Here, we swizzle into temporary memory, which is not zero initialized.
1078        // FIXME (msarett):
1079        // Is this a problem?
1080        swizzlerOptions.fZeroInitialized = kNo_ZeroInitialized;
1081    }
1082
1083    const SkPMColor* colors = get_color_ptr(fColorTable.get());
1084    fSwizzler.reset(SkSwizzler::CreateSwizzler(this->getEncodedInfo(), colors, swizzlerInfo,
1085                                               swizzlerOptions, nullptr, skipFormatConversion));
1086    SkASSERT(fSwizzler);
1087}
1088
1089SkSampler* SkPngCodec::getSampler(bool createIfNecessary) {
1090    if (fSwizzler || !createIfNecessary) {
1091        return fSwizzler.get();
1092    }
1093
1094    this->initializeSwizzler(this->dstInfo(), this->options(), true);
1095    return fSwizzler.get();
1096}
1097
1098bool SkPngCodec::onRewind() {
1099    // This sets fPng_ptr and fInfo_ptr to nullptr. If read_header
1100    // succeeds, they will be repopulated, and if it fails, they will
1101    // remain nullptr. Any future accesses to fPng_ptr and fInfo_ptr will
1102    // come through this function which will rewind and again attempt
1103    // to reinitialize them.
1104    this->destroyReadStruct();
1105
1106    png_structp png_ptr;
1107    png_infop info_ptr;
1108    if (kSuccess != read_header(this->stream(), fPngChunkReader.get(), nullptr,
1109                                &png_ptr, &info_ptr)) {
1110        return false;
1111    }
1112
1113    fPng_ptr = png_ptr;
1114    fInfo_ptr = info_ptr;
1115    fDecodedIdat = false;
1116    return true;
1117}
1118
1119SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst,
1120                                        size_t rowBytes, const Options& options,
1121                                        int* rowsDecoded) {
1122    Result result = this->initializeXforms(dstInfo, options);
1123    if (kSuccess != result) {
1124        return result;
1125    }
1126
1127    if (options.fSubset) {
1128        return kUnimplemented;
1129    }
1130
1131    this->allocateStorage(dstInfo);
1132    this->initializeXformParams();
1133    return this->decodeAllRows(dst, rowBytes, rowsDecoded);
1134}
1135
1136SkCodec::Result SkPngCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
1137        void* dst, size_t rowBytes, const SkCodec::Options& options) {
1138    Result result = this->initializeXforms(dstInfo, options);
1139    if (kSuccess != result) {
1140        return result;
1141    }
1142
1143    this->allocateStorage(dstInfo);
1144
1145    int firstRow, lastRow;
1146    if (options.fSubset) {
1147        firstRow = options.fSubset->top();
1148        lastRow = options.fSubset->bottom() - 1;
1149    } else {
1150        firstRow = 0;
1151        lastRow = dstInfo.height() - 1;
1152    }
1153    this->setRange(firstRow, lastRow, dst, rowBytes);
1154    return kSuccess;
1155}
1156
1157SkCodec::Result SkPngCodec::onIncrementalDecode(int* rowsDecoded) {
1158    // FIXME: Only necessary on the first call.
1159    this->initializeXformParams();
1160
1161    return this->decode(rowsDecoded);
1162}
1163
1164uint64_t SkPngCodec::onGetFillValue(const SkImageInfo& dstInfo) const {
1165    const SkPMColor* colorPtr = get_color_ptr(fColorTable.get());
1166    if (colorPtr) {
1167        SkAlphaType alphaType = select_xform_alpha(dstInfo.alphaType(),
1168                                                   this->getInfo().alphaType());
1169        return get_color_table_fill_value(dstInfo.colorType(), alphaType, colorPtr, 0,
1170                                          this->colorXform(), true);
1171    }
1172    return INHERITED::onGetFillValue(dstInfo);
1173}
1174
1175std::unique_ptr<SkCodec> SkPngCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
1176                                                    Result* result, SkPngChunkReader* chunkReader) {
1177    SkCodec* outCodec = nullptr;
1178    *result = read_header(stream.get(), chunkReader, &outCodec, nullptr, nullptr);
1179    if (kSuccess == *result) {
1180        // Codec has taken ownership of the stream.
1181        SkASSERT(outCodec);
1182        stream.release();
1183    }
1184    return std::unique_ptr<SkCodec>(outCodec);
1185}
1186