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 "FakeStreams.h"
9#include "Resources.h"
10#include "SkAndroidCodec.h"
11#include "SkAutoMalloc.h"
12#include "SkBitmap.h"
13#include "SkCodec.h"
14#include "SkCodecImageGenerator.h"
15#include "SkColorSpace_XYZ.h"
16#include "SkColorSpacePriv.h"
17#include "SkData.h"
18#include "SkFrontBufferedStream.h"
19#include "SkImageEncoder.h"
20#include "SkImageEncoderPriv.h"
21#include "SkMakeUnique.h"
22#include "SkMD5.h"
23#include "SkOSPath.h"
24#include "SkJpegEncoder.h"
25#include "SkPngChunkReader.h"
26#include "SkPngEncoder.h"
27#include "SkRandom.h"
28#include "SkStream.h"
29#include "SkStreamPriv.h"
30#include "SkWebpEncoder.h"
31#include "Test.h"
32
33#include "png.h"
34
35#include "sk_tool_utils.h"
36
37#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR < 5
38    // FIXME (scroggo): Google3 needs to be updated to use a newer version of libpng. In
39    // the meantime, we had to break some pieces of SkPngCodec in order to support Google3.
40    // The parts that are broken are likely not used by Google3.
41    #define SK_PNG_DISABLE_TESTS
42#endif
43
44static void md5(const SkBitmap& bm, SkMD5::Digest* digest) {
45    SkASSERT(bm.getPixels());
46    SkMD5 md5;
47    size_t rowLen = bm.info().bytesPerPixel() * bm.width();
48    for (int y = 0; y < bm.height(); ++y) {
49        md5.write(bm.getAddr(0, y), rowLen);
50    }
51    md5.finish(*digest);
52}
53
54/**
55 *  Compute the digest for bm and compare it to a known good digest.
56 *  @param r Reporter to assert that bm's digest matches goodDigest.
57 *  @param goodDigest The known good digest to compare to.
58 *  @param bm The bitmap to test.
59 */
60static void compare_to_good_digest(skiatest::Reporter* r, const SkMD5::Digest& goodDigest,
61                           const SkBitmap& bm) {
62    SkMD5::Digest digest;
63    md5(bm, &digest);
64    REPORTER_ASSERT(r, digest == goodDigest);
65}
66
67/**
68 *  Test decoding an SkCodec to a particular SkImageInfo.
69 *
70 *  Calling getPixels(info) should return expectedResult, and if goodDigest is non nullptr,
71 *  the resulting decode should match.
72 */
73template<typename Codec>
74static void test_info(skiatest::Reporter* r, Codec* codec, const SkImageInfo& info,
75                      SkCodec::Result expectedResult, const SkMD5::Digest* goodDigest) {
76    SkBitmap bm;
77    bm.allocPixels(info);
78
79    SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
80    REPORTER_ASSERT(r, result == expectedResult);
81
82    if (goodDigest) {
83        compare_to_good_digest(r, *goodDigest, bm);
84    }
85}
86
87SkIRect generate_random_subset(SkRandom* rand, int w, int h) {
88    SkIRect rect;
89    do {
90        rect.fLeft = rand->nextRangeU(0, w);
91        rect.fTop = rand->nextRangeU(0, h);
92        rect.fRight = rand->nextRangeU(0, w);
93        rect.fBottom = rand->nextRangeU(0, h);
94        rect.sort();
95    } while (rect.isEmpty());
96    return rect;
97}
98
99static void test_incremental_decode(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info,
100        const SkMD5::Digest& goodDigest) {
101    SkBitmap bm;
102    bm.allocPixels(info);
103
104    REPORTER_ASSERT(r, SkCodec::kSuccess == codec->startIncrementalDecode(info, bm.getPixels(),
105                                                                          bm.rowBytes()));
106
107    REPORTER_ASSERT(r, SkCodec::kSuccess == codec->incrementalDecode());
108
109    compare_to_good_digest(r, goodDigest, bm);
110}
111
112// Test in stripes, similar to DM's kStripe_Mode
113static void test_in_stripes(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info,
114                            const SkMD5::Digest& goodDigest) {
115    SkBitmap bm;
116    bm.allocPixels(info);
117    bm.eraseColor(SK_ColorYELLOW);
118
119    const int height = info.height();
120    // Note that if numStripes does not evenly divide height there will be an extra
121    // stripe.
122    const int numStripes = 4;
123
124    if (numStripes > height) {
125        // Image is too small.
126        return;
127    }
128
129    const int stripeHeight = height / numStripes;
130
131    // Iterate through the image twice. Once to decode odd stripes, and once for even.
132    for (int oddEven = 1; oddEven >= 0; oddEven--) {
133        for (int y = oddEven * stripeHeight; y < height; y += 2 * stripeHeight) {
134            SkIRect subset = SkIRect::MakeLTRB(0, y, info.width(),
135                                               SkTMin(y + stripeHeight, height));
136            SkCodec::Options options;
137            options.fSubset = &subset;
138            if (SkCodec::kSuccess != codec->startIncrementalDecode(info, bm.getAddr(0, y),
139                        bm.rowBytes(), &options)) {
140                ERRORF(r, "failed to start incremental decode!\ttop: %i\tbottom%i\n",
141                       subset.top(), subset.bottom());
142                return;
143            }
144            if (SkCodec::kSuccess != codec->incrementalDecode()) {
145                ERRORF(r, "failed incremental decode starting from line %i\n", y);
146                return;
147            }
148        }
149    }
150
151    compare_to_good_digest(r, goodDigest, bm);
152}
153
154template<typename Codec>
155static void test_codec(skiatest::Reporter* r, Codec* codec, SkBitmap& bm, const SkImageInfo& info,
156        const SkISize& size, SkCodec::Result expectedResult, SkMD5::Digest* digest,
157        const SkMD5::Digest* goodDigest) {
158
159    REPORTER_ASSERT(r, info.dimensions() == size);
160    bm.allocPixels(info);
161
162    SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
163    REPORTER_ASSERT(r, result == expectedResult);
164
165    md5(bm, digest);
166    if (goodDigest) {
167        REPORTER_ASSERT(r, *digest == *goodDigest);
168    }
169
170    {
171        // Test decoding to 565
172        SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType);
173        if (info.alphaType() == kOpaque_SkAlphaType) {
174            // Decoding to 565 should succeed.
175            SkBitmap bm565;
176            bm565.allocPixels(info565);
177
178            // This will allow comparison even if the image is incomplete.
179            bm565.eraseColor(SK_ColorBLACK);
180
181            REPORTER_ASSERT(r, expectedResult == codec->getPixels(info565,
182                    bm565.getPixels(), bm565.rowBytes()));
183
184            SkMD5::Digest digest565;
185            md5(bm565, &digest565);
186
187            // A dumb client's request for non-opaque should also succeed.
188            for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) {
189                info565 = info565.makeAlphaType(alpha);
190                test_info(r, codec, info565, expectedResult, &digest565);
191            }
192        } else {
193            test_info(r, codec, info565, SkCodec::kInvalidConversion, nullptr);
194        }
195    }
196
197    if (codec->getInfo().colorType() == kGray_8_SkColorType) {
198        SkImageInfo grayInfo = codec->getInfo();
199        SkBitmap grayBm;
200        grayBm.allocPixels(grayInfo);
201
202        grayBm.eraseColor(SK_ColorBLACK);
203
204        REPORTER_ASSERT(r, expectedResult == codec->getPixels(grayInfo,
205                grayBm.getPixels(), grayBm.rowBytes()));
206
207        SkMD5::Digest grayDigest;
208        md5(grayBm, &grayDigest);
209
210        for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) {
211            grayInfo = grayInfo.makeAlphaType(alpha);
212            test_info(r, codec, grayInfo, expectedResult, &grayDigest);
213        }
214    }
215
216    // Verify that re-decoding gives the same result.  It is interesting to check this after
217    // a decode to 565, since choosing to decode to 565 may result in some of the decode
218    // options being modified.  These options should return to their defaults on another
219    // decode to kN32, so the new digest should match the old digest.
220    test_info(r, codec, info, expectedResult, digest);
221
222    {
223        // Check alpha type conversions
224        if (info.alphaType() == kOpaque_SkAlphaType) {
225            test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType),
226                      expectedResult, digest);
227            test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType),
228                      expectedResult, digest);
229        } else {
230            // Decoding to opaque should fail
231            test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType),
232                      SkCodec::kInvalidConversion, nullptr);
233            SkAlphaType otherAt = info.alphaType();
234            if (kPremul_SkAlphaType == otherAt) {
235                otherAt = kUnpremul_SkAlphaType;
236            } else {
237                otherAt = kPremul_SkAlphaType;
238            }
239            // The other non-opaque alpha type should always succeed, but not match.
240            test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nullptr);
241        }
242    }
243}
244
245static bool supports_partial_scanlines(const char path[]) {
246    static const char* const exts[] = {
247        "jpg", "jpeg", "png", "webp"
248        "JPG", "JPEG", "PNG", "WEBP"
249    };
250
251    for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) {
252        if (SkStrEndsWith(path, exts[i])) {
253            return true;
254        }
255    }
256    return false;
257}
258
259// FIXME: Break up this giant function
260static void check(skiatest::Reporter* r,
261                  const char path[],
262                  SkISize size,
263                  bool supportsScanlineDecoding,
264                  bool supportsSubsetDecoding,
265                  bool supportsIncomplete,
266                  bool supportsNewScanlineDecoding = false) {
267
268    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
269    if (!stream) {
270        return;
271    }
272
273    std::unique_ptr<SkCodec> codec(nullptr);
274    bool isIncomplete = supportsIncomplete;
275    if (isIncomplete) {
276        size_t size = stream->getLength();
277        codec = SkCodec::MakeFromData(SkData::MakeFromStream(stream.get(), 2 * size / 3));
278    } else {
279        codec = SkCodec::MakeFromStream(std::move(stream));
280    }
281    if (!codec) {
282        ERRORF(r, "Unable to decode '%s'", path);
283        return;
284    }
285
286    // Test full image decodes with SkCodec
287    SkMD5::Digest codecDigest;
288    const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
289    SkBitmap bm;
290    SkCodec::Result expectedResult = isIncomplete ? SkCodec::kIncompleteInput : SkCodec::kSuccess;
291    test_codec(r, codec.get(), bm, info, size, expectedResult, &codecDigest, nullptr);
292
293    // Scanline decoding follows.
294
295    if (supportsNewScanlineDecoding && !isIncomplete) {
296        test_incremental_decode(r, codec.get(), info, codecDigest);
297        // This is only supported by codecs that use incremental decoding to
298        // support subset decodes - png and jpeg (once SkJpegCodec is
299        // converted).
300        if (SkStrEndsWith(path, "png") || SkStrEndsWith(path, "PNG")) {
301            test_in_stripes(r, codec.get(), info, codecDigest);
302        }
303    }
304
305    // Need to call startScanlineDecode() first.
306    REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) == 0);
307    REPORTER_ASSERT(r, !codec->skipScanlines(1));
308    const SkCodec::Result startResult = codec->startScanlineDecode(info);
309    if (supportsScanlineDecoding) {
310        bm.eraseColor(SK_ColorYELLOW);
311
312        REPORTER_ASSERT(r, startResult == SkCodec::kSuccess);
313
314        for (int y = 0; y < info.height(); y++) {
315            const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
316            if (!isIncomplete) {
317                REPORTER_ASSERT(r, 1 == lines);
318            }
319        }
320        // verify that scanline decoding gives the same result.
321        if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) {
322            compare_to_good_digest(r, codecDigest, bm);
323        }
324
325        // Cannot continue to decode scanlines beyond the end
326        REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
327                == 0);
328
329        // Interrupting a scanline decode with a full decode starts from
330        // scratch
331        REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess);
332        const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0);
333        if (!isIncomplete) {
334            REPORTER_ASSERT(r, lines == 1);
335        }
336        REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes())
337                == expectedResult);
338        REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
339                == 0);
340        REPORTER_ASSERT(r, codec->skipScanlines(1)
341                == 0);
342
343        // Test partial scanline decodes
344        if (supports_partial_scanlines(path) && info.width() >= 3) {
345            SkCodec::Options options;
346            int width = info.width();
347            int height = info.height();
348            SkIRect subset = SkIRect::MakeXYWH(2 * (width / 3), 0, width / 3, height);
349            options.fSubset = &subset;
350
351            const auto partialStartResult = codec->startScanlineDecode(info, &options);
352            REPORTER_ASSERT(r, partialStartResult == SkCodec::kSuccess);
353
354            for (int y = 0; y < height; y++) {
355                const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
356                if (!isIncomplete) {
357                    REPORTER_ASSERT(r, 1 == lines);
358                }
359            }
360        }
361    } else {
362        REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented);
363    }
364
365    // The rest of this function tests decoding subsets, and will decode an arbitrary number of
366    // random subsets.
367    // Do not attempt to decode subsets of an image of only once pixel, since there is no
368    // meaningful subset.
369    if (size.width() * size.height() == 1) {
370        return;
371    }
372
373    SkRandom rand;
374    SkIRect subset;
375    SkCodec::Options opts;
376    opts.fSubset = &subset;
377    for (int i = 0; i < 5; i++) {
378        subset = generate_random_subset(&rand, size.width(), size.height());
379        SkASSERT(!subset.isEmpty());
380        const bool supported = codec->getValidSubset(&subset);
381        REPORTER_ASSERT(r, supported == supportsSubsetDecoding);
382
383        SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height());
384        SkBitmap bm;
385        bm.allocPixels(subsetInfo);
386        const auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts);
387
388        if (supportsSubsetDecoding) {
389            if (expectedResult == SkCodec::kSuccess) {
390                REPORTER_ASSERT(r, result == expectedResult);
391            }
392            // Webp is the only codec that supports subsets, and it will have modified the subset
393            // to have even left/top.
394            REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
395        } else {
396            // No subsets will work.
397            REPORTER_ASSERT(r, result == SkCodec::kUnimplemented);
398        }
399    }
400
401    // SkAndroidCodec tests
402    if (supportsScanlineDecoding || supportsSubsetDecoding || supportsNewScanlineDecoding) {
403
404        std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
405        if (!stream) {
406            return;
407        }
408
409        auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
410        if (!androidCodec) {
411            ERRORF(r, "Unable to decode '%s'", path);
412            return;
413        }
414
415        SkBitmap bm;
416        SkMD5::Digest androidCodecDigest;
417        test_codec(r, androidCodec.get(), bm, info, size, expectedResult, &androidCodecDigest,
418                   &codecDigest);
419    }
420
421    if (!isIncomplete) {
422        // Test SkCodecImageGenerator
423        std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
424        sk_sp<SkData> fullData(SkData::MakeFromStream(stream.get(), stream->getLength()));
425        std::unique_ptr<SkImageGenerator> gen(
426                SkCodecImageGenerator::MakeFromEncodedCodec(fullData));
427        SkBitmap bm;
428        bm.allocPixels(info);
429        REPORTER_ASSERT(r, gen->getPixels(info, bm.getPixels(), bm.rowBytes()));
430        compare_to_good_digest(r, codecDigest, bm);
431
432#ifndef SK_PNG_DISABLE_TESTS
433        // Test using SkFrontBufferedStream, as Android does
434        auto bufferedStream = SkFrontBufferedStream::Make(
435                      SkMemoryStream::Make(std::move(fullData)), SkCodec::MinBufferedBytesNeeded());
436        REPORTER_ASSERT(r, bufferedStream);
437        codec = SkCodec::MakeFromStream(std::move(bufferedStream));
438        REPORTER_ASSERT(r, codec);
439        if (codec) {
440            test_info(r, codec.get(), info, SkCodec::kSuccess, &codecDigest);
441        }
442#endif
443    }
444
445    // If we've just tested incomplete decodes, let's run the same test again on full decodes.
446    if (isIncomplete) {
447        check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, false,
448              supportsNewScanlineDecoding);
449    }
450}
451
452DEF_TEST(Codec_wbmp, r) {
453    check(r, "images/mandrill.wbmp", SkISize::Make(512, 512), true, false, true);
454}
455
456DEF_TEST(Codec_webp, r) {
457    check(r, "images/baby_tux.webp", SkISize::Make(386, 395), false, true, true);
458    check(r, "images/color_wheel.webp", SkISize::Make(128, 128), false, true, true);
459    check(r, "images/yellow_rose.webp", SkISize::Make(400, 301), false, true, true);
460}
461
462DEF_TEST(Codec_bmp, r) {
463    check(r, "images/randPixels.bmp", SkISize::Make(8, 8), true, false, true);
464    check(r, "images/rle.bmp", SkISize::Make(320, 240), true, false, true);
465}
466
467DEF_TEST(Codec_ico, r) {
468    // FIXME: We are not ready to test incomplete ICOs
469    // These two tests examine interestingly different behavior:
470    // Decodes an embedded BMP image
471    check(r, "images/color_wheel.ico", SkISize::Make(128, 128), true, false, false);
472    // Decodes an embedded PNG image
473    check(r, "images/google_chrome.ico", SkISize::Make(256, 256), false, false, false, true);
474}
475
476DEF_TEST(Codec_gif, r) {
477    check(r, "images/box.gif", SkISize::Make(200, 55), false, false, true, true);
478    check(r, "images/color_wheel.gif", SkISize::Make(128, 128), false, false, true, true);
479    // randPixels.gif is too small to test incomplete
480    check(r, "images/randPixels.gif", SkISize::Make(8, 8), false, false, false, true);
481}
482
483DEF_TEST(Codec_jpg, r) {
484    check(r, "images/CMYK.jpg", SkISize::Make(642, 516), true, false, true);
485    check(r, "images/color_wheel.jpg", SkISize::Make(128, 128), true, false, true);
486    // grayscale.jpg is too small to test incomplete
487    check(r, "images/grayscale.jpg", SkISize::Make(128, 128), true, false, false);
488    check(r, "images/mandrill_512_q075.jpg", SkISize::Make(512, 512), true, false, true);
489    // randPixels.jpg is too small to test incomplete
490    check(r, "images/randPixels.jpg", SkISize::Make(8, 8), true, false, false);
491}
492
493DEF_TEST(Codec_png, r) {
494    check(r, "images/arrow.png", SkISize::Make(187, 312), false, false, true, true);
495    check(r, "images/baby_tux.png", SkISize::Make(240, 246), false, false, true, true);
496    check(r, "images/color_wheel.png", SkISize::Make(128, 128), false, false, true, true);
497    // half-transparent-white-pixel.png is too small to test incomplete
498    check(r, "images/half-transparent-white-pixel.png", SkISize::Make(1, 1), false, false, false, true);
499    check(r, "images/mandrill_128.png", SkISize::Make(128, 128), false, false, true, true);
500    check(r, "images/mandrill_16.png", SkISize::Make(16, 16), false, false, true, true);
501    check(r, "images/mandrill_256.png", SkISize::Make(256, 256), false, false, true, true);
502    check(r, "images/mandrill_32.png", SkISize::Make(32, 32), false, false, true, true);
503    check(r, "images/mandrill_512.png", SkISize::Make(512, 512), false, false, true, true);
504    check(r, "images/mandrill_64.png", SkISize::Make(64, 64), false, false, true, true);
505    check(r, "images/plane.png", SkISize::Make(250, 126), false, false, true, true);
506    check(r, "images/plane_interlaced.png", SkISize::Make(250, 126), false, false, true, true);
507    check(r, "images/randPixels.png", SkISize::Make(8, 8), false, false, true, true);
508    check(r, "images/yellow_rose.png", SkISize::Make(400, 301), false, false, true, true);
509}
510
511// Disable RAW tests for Win32.
512#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
513DEF_TEST(Codec_raw, r) {
514    check(r, "images/sample_1mp.dng", SkISize::Make(600, 338), false, false, false);
515    check(r, "images/sample_1mp_rotated.dng", SkISize::Make(600, 338), false, false, false);
516    check(r, "images/dng_with_preview.dng", SkISize::Make(600, 338), true, false, false);
517}
518#endif
519
520static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) {
521    // Neither of these calls should return a codec. Bots should catch us if we leaked anything.
522    REPORTER_ASSERT(r, !SkCodec::MakeFromStream(
523                                        skstd::make_unique<SkMemoryStream>(stream, len, false)));
524    REPORTER_ASSERT(r, !SkAndroidCodec::MakeFromStream(
525                                        skstd::make_unique<SkMemoryStream>(stream, len, false)));
526}
527
528// Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream,
529// even on failure. Test some bad streams.
530DEF_TEST(Codec_leaks, r) {
531    // No codec should claim this as their format, so this tests SkCodec::NewFromStream.
532    const char nonSupportedStream[] = "hello world";
533    // The other strings should look like the beginning of a file type, so we'll call some
534    // internal version of NewFromStream, which must also delete the stream on failure.
535    const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
536    const unsigned char emptyJpeg[] = { 0xFF, 0xD8, 0xFF };
537    const char emptyWebp[] = "RIFF1234WEBPVP";
538    const char emptyBmp[] = { 'B', 'M' };
539    const char emptyIco[] = { '\x00', '\x00', '\x01', '\x00' };
540    const char emptyGif[] = "GIFVER";
541
542    test_invalid_stream(r, nonSupportedStream, sizeof(nonSupportedStream));
543    test_invalid_stream(r, emptyPng, sizeof(emptyPng));
544    test_invalid_stream(r, emptyJpeg, sizeof(emptyJpeg));
545    test_invalid_stream(r, emptyWebp, sizeof(emptyWebp));
546    test_invalid_stream(r, emptyBmp, sizeof(emptyBmp));
547    test_invalid_stream(r, emptyIco, sizeof(emptyIco));
548    test_invalid_stream(r, emptyGif, sizeof(emptyGif));
549}
550
551DEF_TEST(Codec_null, r) {
552    // Attempting to create an SkCodec or an SkAndroidCodec with null should not
553    // crash.
554    REPORTER_ASSERT(r, !SkCodec::MakeFromStream(nullptr));
555    REPORTER_ASSERT(r, !SkAndroidCodec::MakeFromStream(nullptr));
556}
557
558static void test_dimensions(skiatest::Reporter* r, const char path[]) {
559    // Create the codec from the resource file
560    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
561    if (!stream) {
562        return;
563    }
564    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream)));
565    if (!codec) {
566        ERRORF(r, "Unable to create codec '%s'", path);
567        return;
568    }
569
570    // Check that the decode is successful for a variety of scales
571    for (int sampleSize = 1; sampleSize < 32; sampleSize++) {
572        // Scale the output dimensions
573        SkISize scaledDims = codec->getSampledDimensions(sampleSize);
574        SkImageInfo scaledInfo = codec->getInfo()
575                .makeWH(scaledDims.width(), scaledDims.height())
576                .makeColorType(kN32_SkColorType);
577
578        // Set up for the decode
579        size_t rowBytes = scaledDims.width() * sizeof(SkPMColor);
580        size_t totalBytes = scaledInfo.computeByteSize(rowBytes);
581        SkAutoTMalloc<SkPMColor> pixels(totalBytes);
582
583        SkAndroidCodec::AndroidOptions options;
584        options.fSampleSize = sampleSize;
585        SkCodec::Result result =
586                codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options);
587        REPORTER_ASSERT(r, SkCodec::kSuccess == result);
588    }
589}
590
591// Ensure that onGetScaledDimensions returns valid image dimensions to use for decodes
592DEF_TEST(Codec_Dimensions, r) {
593    // JPG
594    test_dimensions(r, "images/CMYK.jpg");
595    test_dimensions(r, "images/color_wheel.jpg");
596    test_dimensions(r, "images/grayscale.jpg");
597    test_dimensions(r, "images/mandrill_512_q075.jpg");
598    test_dimensions(r, "images/randPixels.jpg");
599
600    // Decoding small images with very large scaling factors is a potential
601    // source of bugs and crashes.  We disable these tests in Gold because
602    // tiny images are not very useful to look at.
603    // Here we make sure that we do not crash or access illegal memory when
604    // performing scaled decodes on small images.
605    test_dimensions(r, "images/1x1.png");
606    test_dimensions(r, "images/2x2.png");
607    test_dimensions(r, "images/3x3.png");
608    test_dimensions(r, "images/3x1.png");
609    test_dimensions(r, "images/1x1.png");
610    test_dimensions(r, "images/16x1.png");
611    test_dimensions(r, "images/1x16.png");
612    test_dimensions(r, "images/mandrill_16.png");
613
614    // RAW
615// Disable RAW tests for Win32.
616#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
617    test_dimensions(r, "images/sample_1mp.dng");
618    test_dimensions(r, "images/sample_1mp_rotated.dng");
619    test_dimensions(r, "images/dng_with_preview.dng");
620#endif
621}
622
623static void test_invalid(skiatest::Reporter* r, const char path[]) {
624    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
625    if (!stream) {
626        return;
627    }
628    REPORTER_ASSERT(r, !SkCodec::MakeFromStream(std::move(stream)));
629}
630
631DEF_TEST(Codec_Empty, r) {
632    // Test images that should not be able to create a codec
633    test_invalid(r, "empty_images/zero-dims.gif");
634    test_invalid(r, "empty_images/zero-embedded.ico");
635    test_invalid(r, "empty_images/zero-width.bmp");
636    test_invalid(r, "empty_images/zero-height.bmp");
637    test_invalid(r, "empty_images/zero-width.jpg");
638    test_invalid(r, "empty_images/zero-height.jpg");
639    test_invalid(r, "empty_images/zero-width.png");
640    test_invalid(r, "empty_images/zero-height.png");
641    test_invalid(r, "empty_images/zero-width.wbmp");
642    test_invalid(r, "empty_images/zero-height.wbmp");
643    // This image is an ico with an embedded mask-bmp.  This is illegal.
644    test_invalid(r, "invalid_images/mask-bmp-ico.ico");
645    // It is illegal for a webp frame to not be fully contained by the canvas.
646    test_invalid(r, "invalid_images/invalid-offset.webp");
647#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
648    test_invalid(r, "empty_images/zero_height.tiff");
649#endif
650    test_invalid(r, "invalid_images/b37623797.ico");
651}
652
653#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
654
655#ifndef SK_PNG_DISABLE_TESTS   // reading chunks does not work properly with older versions.
656                               // It does not appear that anyone in Google3 is reading chunks.
657
658static void codex_test_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
659    SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
660    if (!sk_stream->write(data, len)) {
661        png_error(png_ptr, "sk_write_fn Error!");
662    }
663}
664
665DEF_TEST(Codec_pngChunkReader, r) {
666    // Create a dummy bitmap. Use unpremul RGBA for libpng.
667    SkBitmap bm;
668    const int w = 1;
669    const int h = 1;
670    const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType,
671                                                 kUnpremul_SkAlphaType);
672    bm.setInfo(bmInfo);
673    bm.allocPixels();
674    bm.eraseColor(SK_ColorBLUE);
675    SkMD5::Digest goodDigest;
676    md5(bm, &goodDigest);
677
678    // Write to a png file.
679    png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
680    REPORTER_ASSERT(r, png);
681    if (!png) {
682        return;
683    }
684
685    png_infop info = png_create_info_struct(png);
686    REPORTER_ASSERT(r, info);
687    if (!info) {
688        png_destroy_write_struct(&png, nullptr);
689        return;
690    }
691
692    if (setjmp(png_jmpbuf(png))) {
693        ERRORF(r, "failed writing png");
694        png_destroy_write_struct(&png, &info);
695        return;
696    }
697
698    SkDynamicMemoryWStream wStream;
699    png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr);
700
701    png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8,
702                 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
703                 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
704
705    // Create some chunks that match the Android framework's use.
706    static png_unknown_chunk gUnknowns[] = {
707        { "npOl", (png_byte*)"outline", sizeof("outline"), PNG_HAVE_IHDR },
708        { "npLb", (png_byte*)"layoutBounds", sizeof("layoutBounds"), PNG_HAVE_IHDR },
709        { "npTc", (png_byte*)"ninePatchData", sizeof("ninePatchData"), PNG_HAVE_IHDR },
710    };
711
712    png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"npOl\0npLb\0npTc\0", 3);
713    png_set_unknown_chunks(png, info, gUnknowns, SK_ARRAY_COUNT(gUnknowns));
714#if PNG_LIBPNG_VER < 10600
715    /* Deal with unknown chunk location bug in 1.5.x and earlier */
716    png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR);
717    png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR);
718#endif
719
720    png_write_info(png, info);
721
722    for (int j = 0; j < h; j++) {
723        png_bytep row = (png_bytep)(bm.getAddr(0, j));
724        png_write_rows(png, &row, 1);
725    }
726    png_write_end(png, info);
727    png_destroy_write_struct(&png, &info);
728
729    class ChunkReader : public SkPngChunkReader {
730    public:
731        ChunkReader(skiatest::Reporter* r)
732            : fReporter(r)
733        {
734            this->reset();
735        }
736
737        bool readChunk(const char tag[], const void* data, size_t length) override {
738            for (size_t i = 0; i < SK_ARRAY_COUNT(gUnknowns); ++i) {
739                if (!strcmp(tag, (const char*) gUnknowns[i].name)) {
740                    // Tag matches. This should have been the first time we see it.
741                    REPORTER_ASSERT(fReporter, !fSeen[i]);
742                    fSeen[i] = true;
743
744                    // Data and length should match
745                    REPORTER_ASSERT(fReporter, length == gUnknowns[i].size);
746                    REPORTER_ASSERT(fReporter, !strcmp((const char*) data,
747                                                       (const char*) gUnknowns[i].data));
748                    return true;
749                }
750            }
751            ERRORF(fReporter, "Saw an unexpected unknown chunk.");
752            return true;
753        }
754
755        bool allHaveBeenSeen() {
756            bool ret = true;
757            for (auto seen : fSeen) {
758                ret &= seen;
759            }
760            return ret;
761        }
762
763        void reset() {
764            sk_bzero(fSeen, sizeof(fSeen));
765        }
766
767    private:
768        skiatest::Reporter* fReporter;  // Unowned
769        bool fSeen[3];
770    };
771
772    ChunkReader chunkReader(r);
773
774    // Now read the file with SkCodec.
775    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(wStream.detachAsData(), &chunkReader));
776    REPORTER_ASSERT(r, codec);
777    if (!codec) {
778        return;
779    }
780
781    // Now compare to the original.
782    SkBitmap decodedBm;
783    decodedBm.setInfo(codec->getInfo());
784    decodedBm.allocPixels();
785    SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(),
786                                              decodedBm.rowBytes());
787    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
788
789    if (decodedBm.colorType() != bm.colorType()) {
790        SkBitmap tmp;
791        bool success = sk_tool_utils::copy_to(&tmp, bm.colorType(), decodedBm);
792        REPORTER_ASSERT(r, success);
793        if (!success) {
794            return;
795        }
796
797        tmp.swap(decodedBm);
798    }
799
800    compare_to_good_digest(r, goodDigest, decodedBm);
801    REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen());
802
803    // Decoding again will read the chunks again.
804    chunkReader.reset();
805    REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen());
806    result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes());
807    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
808    REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen());
809}
810#endif // SK_PNG_DISABLE_TESTS
811#endif // PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
812
813// Stream that can only peek up to a limit
814class LimitedPeekingMemStream : public SkStream {
815public:
816    LimitedPeekingMemStream(sk_sp<SkData> data, size_t limit)
817        : fStream(std::move(data))
818        , fLimit(limit) {}
819
820    size_t peek(void* buf, size_t bytes) const override {
821        return fStream.peek(buf, SkTMin(bytes, fLimit));
822    }
823    size_t read(void* buf, size_t bytes) override {
824        return fStream.read(buf, bytes);
825    }
826    bool rewind() override {
827        return fStream.rewind();
828    }
829    bool isAtEnd() const override {
830        return fStream.isAtEnd();
831    }
832private:
833    SkMemoryStream fStream;
834    const size_t   fLimit;
835};
836
837// Disable RAW tests for Win32.
838#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
839// Test that the RawCodec works also for not asset stream. This will test the code path using
840// SkRawBufferedStream instead of SkRawAssetStream.
841DEF_TEST(Codec_raw_notseekable, r) {
842    constexpr char path[] = "images/dng_with_preview.dng";
843    sk_sp<SkData> data(GetResourceAsData(path));
844    if (!data) {
845        SkDebugf("Missing resource '%s'\n", path);
846        return;
847    }
848
849    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
850                                           skstd::make_unique<NotAssetMemStream>(std::move(data))));
851    REPORTER_ASSERT(r, codec);
852
853    test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
854}
855#endif
856
857// Test that even if webp_parse_header fails to peek enough, it will fall back to read()
858// + rewind() and succeed.
859DEF_TEST(Codec_webp_peek, r) {
860    constexpr char path[] = "images/baby_tux.webp";
861    auto data = GetResourceAsData(path);
862    if (!data) {
863        SkDebugf("Missing resource '%s'\n", path);
864        return;
865    }
866
867    // The limit is less than webp needs to peek or read.
868    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
869                                           skstd::make_unique<LimitedPeekingMemStream>(data, 25)));
870    REPORTER_ASSERT(r, codec);
871
872    test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
873
874    // Similarly, a stream which does not peek should still succeed.
875    codec = SkCodec::MakeFromStream(skstd::make_unique<LimitedPeekingMemStream>(data, 0));
876    REPORTER_ASSERT(r, codec);
877
878    test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
879}
880
881// SkCodec's wbmp decoder was initially unnecessarily restrictive.
882// It required the second byte to be zero. The wbmp specification allows
883// a couple of bits to be 1 (so long as they do not overlap with 0x9F).
884// Test that SkCodec now supports an image with these bits set.
885DEF_TEST(Codec_wbmp_restrictive, r) {
886    const char* path = "images/mandrill.wbmp";
887    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
888    if (!stream) {
889        return;
890    }
891
892    // Modify the stream to contain a second byte with some bits set.
893    auto data = SkCopyStreamToData(stream.get());
894    uint8_t* writeableData = static_cast<uint8_t*>(data->writable_data());
895    writeableData[1] = static_cast<uint8_t>(~0x9F);
896
897    // SkCodec should support this.
898    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
899    REPORTER_ASSERT(r, codec);
900    if (!codec) {
901        return;
902    }
903    test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
904}
905
906// wbmp images have a header that can be arbitrarily large, depending on the
907// size of the image. We cap the size at 65535, meaning we only need to look at
908// 8 bytes to determine whether we can read the image. This is important
909// because SkCodec only passes a limited number of bytes to SkWbmpCodec to
910// determine whether the image is a wbmp.
911DEF_TEST(Codec_wbmp_max_size, r) {
912    const unsigned char maxSizeWbmp[] = { 0x00, 0x00,           // Header
913                                          0x83, 0xFF, 0x7F,     // W: 65535
914                                          0x83, 0xFF, 0x7F };   // H: 65535
915    std::unique_ptr<SkStream> stream(new SkMemoryStream(maxSizeWbmp, sizeof(maxSizeWbmp), false));
916    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
917
918    REPORTER_ASSERT(r, codec);
919    if (!codec) return;
920
921    REPORTER_ASSERT(r, codec->getInfo().width() == 65535);
922    REPORTER_ASSERT(r, codec->getInfo().height() == 65535);
923
924    // Now test an image which is too big. Any image with a larger header (i.e.
925    // has bigger width/height) is also too big.
926    const unsigned char tooBigWbmp[] = { 0x00, 0x00,           // Header
927                                         0x84, 0x80, 0x00,     // W: 65536
928                                         0x84, 0x80, 0x00 };   // H: 65536
929    stream.reset(new SkMemoryStream(tooBigWbmp, sizeof(tooBigWbmp), false));
930    codec = SkCodec::MakeFromStream(std::move(stream));
931
932    REPORTER_ASSERT(r, !codec);
933}
934
935DEF_TEST(Codec_jpeg_rewind, r) {
936    const char* path = "images/mandrill_512_q075.jpg";
937    sk_sp<SkData> data(GetResourceAsData(path));
938    if (!data) {
939        return;
940    }
941
942    data = SkData::MakeSubset(data.get(), 0, data->size() / 2);
943    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(data));
944    if (!codec) {
945        ERRORF(r, "Unable to create codec '%s'.", path);
946        return;
947    }
948
949    const int width = codec->getInfo().width();
950    const int height = codec->getInfo().height();
951    size_t rowBytes = sizeof(SkPMColor) * width;
952    SkAutoMalloc pixelStorage(height * rowBytes);
953
954    // Perform a sampled decode.
955    SkAndroidCodec::AndroidOptions opts;
956    opts.fSampleSize = 12;
957    auto sampledInfo = codec->getInfo().makeWH(width / 12, height / 12);
958    auto result = codec->getAndroidPixels(sampledInfo, pixelStorage.get(), rowBytes, &opts);
959    REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
960
961    // Rewind the codec and perform a full image decode.
962    result = codec->getPixels(codec->getInfo(), pixelStorage.get(), rowBytes);
963    REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
964
965    // Now perform a subset decode.
966    {
967        opts.fSampleSize = 1;
968        SkIRect subset = SkIRect::MakeWH(100, 100);
969        opts.fSubset = &subset;
970        result = codec->getAndroidPixels(codec->getInfo().makeWH(100, 100), pixelStorage.get(),
971                                         rowBytes, &opts);
972        // Though we only have half the data, it is enough to decode this subset.
973        REPORTER_ASSERT(r, SkCodec::kSuccess == result);
974    }
975
976    // Perform another full image decode.  ASAN will detect if we look at the subset when it is
977    // out of scope.  This would happen if we depend on the old state in the codec.
978    // This tests two layers of bugs: both SkJpegCodec::readRows and SkCodec::fillIncompleteImage
979    // used to look at the old subset.
980    opts.fSubset = nullptr;
981    result = codec->getAndroidPixels(codec->getInfo(), pixelStorage.get(), rowBytes, &opts);
982    REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
983}
984
985static void check_color_xform(skiatest::Reporter* r, const char* path) {
986    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(GetResourceAsStream(path)));
987
988    SkAndroidCodec::AndroidOptions opts;
989    opts.fSampleSize = 3;
990    const int subsetWidth = codec->getInfo().width() / 2;
991    const int subsetHeight = codec->getInfo().height() / 2;
992    SkIRect subset = SkIRect::MakeWH(subsetWidth, subsetHeight);
993    opts.fSubset = &subset;
994
995    const int dstWidth = subsetWidth / opts.fSampleSize;
996    const int dstHeight = subsetHeight / opts.fSampleSize;
997    sk_sp<SkData> data = GetResourceAsData("icc_profiles/HP_ZR30w.icc");
998    sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(data->data(), data->size());
999    SkImageInfo dstInfo = codec->getInfo().makeWH(dstWidth, dstHeight)
1000                                          .makeColorType(kN32_SkColorType)
1001                                          .makeColorSpace(colorSpace);
1002
1003    size_t rowBytes = dstInfo.minRowBytes();
1004    SkAutoMalloc pixelStorage(dstInfo.computeByteSize(rowBytes));
1005    SkCodec::Result result = codec->getAndroidPixels(dstInfo, pixelStorage.get(), rowBytes, &opts);
1006    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1007}
1008
1009DEF_TEST(Codec_ColorXform, r) {
1010    check_color_xform(r, "images/mandrill_512_q075.jpg");
1011    check_color_xform(r, "images/mandrill_512.png");
1012}
1013
1014static bool color_type_match(SkColorType origColorType, SkColorType codecColorType) {
1015    switch (origColorType) {
1016        case kRGBA_8888_SkColorType:
1017        case kBGRA_8888_SkColorType:
1018            return kRGBA_8888_SkColorType == codecColorType ||
1019                   kBGRA_8888_SkColorType == codecColorType;
1020        default:
1021            return origColorType == codecColorType;
1022    }
1023}
1024
1025static bool alpha_type_match(SkAlphaType origAlphaType, SkAlphaType codecAlphaType) {
1026    switch (origAlphaType) {
1027        case kUnpremul_SkAlphaType:
1028        case kPremul_SkAlphaType:
1029            return kUnpremul_SkAlphaType == codecAlphaType ||
1030                    kPremul_SkAlphaType == codecAlphaType;
1031        default:
1032            return origAlphaType == codecAlphaType;
1033    }
1034}
1035
1036static void check_round_trip(skiatest::Reporter* r, SkCodec* origCodec, const SkImageInfo& info) {
1037    SkBitmap bm1;
1038    bm1.allocPixels(info);
1039    SkCodec::Result result = origCodec->getPixels(info, bm1.getPixels(), bm1.rowBytes());
1040    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1041
1042    // Encode the image to png.
1043    sk_sp<SkData> data =
1044            sk_sp<SkData>(sk_tool_utils::EncodeImageToData(bm1, SkEncodedImageFormat::kPNG, 100));
1045
1046    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
1047    REPORTER_ASSERT(r, color_type_match(info.colorType(), codec->getInfo().colorType()));
1048    REPORTER_ASSERT(r, alpha_type_match(info.alphaType(), codec->getInfo().alphaType()));
1049
1050    SkBitmap bm2;
1051    bm2.allocPixels(info);
1052    result = codec->getPixels(info, bm2.getPixels(), bm2.rowBytes());
1053    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1054
1055    SkMD5::Digest d1, d2;
1056    md5(bm1, &d1);
1057    md5(bm2, &d2);
1058    REPORTER_ASSERT(r, d1 == d2);
1059}
1060
1061DEF_TEST(Codec_PngRoundTrip, r) {
1062    auto codec = SkCodec::MakeFromStream(GetResourceAsStream("images/mandrill_512_q075.jpg"));
1063
1064    SkColorType colorTypesOpaque[] = {
1065            kRGB_565_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType
1066    };
1067    for (SkColorType colorType : colorTypesOpaque) {
1068        SkImageInfo newInfo = codec->getInfo().makeColorType(colorType);
1069        check_round_trip(r, codec.get(), newInfo);
1070    }
1071
1072    codec = SkCodec::MakeFromStream(GetResourceAsStream("images/grayscale.jpg"));
1073    check_round_trip(r, codec.get(), codec->getInfo());
1074
1075    codec = SkCodec::MakeFromStream(GetResourceAsStream("images/yellow_rose.png"));
1076
1077    SkColorType colorTypesWithAlpha[] = {
1078            kRGBA_8888_SkColorType, kBGRA_8888_SkColorType
1079    };
1080    SkAlphaType alphaTypes[] = {
1081            kUnpremul_SkAlphaType, kPremul_SkAlphaType
1082    };
1083    for (SkColorType colorType : colorTypesWithAlpha) {
1084        for (SkAlphaType alphaType : alphaTypes) {
1085            // Set color space to nullptr because color correct premultiplies do not round trip.
1086            SkImageInfo newInfo = codec->getInfo().makeColorType(colorType)
1087                                                  .makeAlphaType(alphaType)
1088                                                  .makeColorSpace(nullptr);
1089            check_round_trip(r, codec.get(), newInfo);
1090        }
1091    }
1092
1093    codec = SkCodec::MakeFromStream(GetResourceAsStream("images/index8.png"));
1094
1095    for (SkAlphaType alphaType : alphaTypes) {
1096        SkImageInfo newInfo = codec->getInfo().makeAlphaType(alphaType)
1097                                              .makeColorSpace(nullptr);
1098        check_round_trip(r, codec.get(), newInfo);
1099    }
1100}
1101
1102static void test_conversion_possible(skiatest::Reporter* r, const char* path,
1103                                     bool supportsScanlineDecoder,
1104                                     bool supportsIncrementalDecoder) {
1105    std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
1106    if (!stream) {
1107        return;
1108    }
1109
1110    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1111    if (!codec) {
1112        ERRORF(r, "failed to create a codec for %s", path);
1113        return;
1114    }
1115
1116    SkImageInfo infoF16 = codec->getInfo().makeColorType(kRGBA_F16_SkColorType);
1117
1118    SkBitmap bm;
1119    bm.allocPixels(infoF16);
1120    SkCodec::Result result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
1121    REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
1122
1123    result = codec->startScanlineDecode(infoF16);
1124    if (supportsScanlineDecoder) {
1125        REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
1126    } else {
1127        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result
1128                        || SkCodec::kInvalidConversion == result);
1129    }
1130
1131    result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes());
1132    if (supportsIncrementalDecoder) {
1133        REPORTER_ASSERT(r, SkCodec::kInvalidConversion == result);
1134    } else {
1135        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result
1136                        || SkCodec::kInvalidConversion == result);
1137    }
1138
1139    infoF16 = infoF16.makeColorSpace(infoF16.colorSpace()->makeLinearGamma());
1140    result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
1141    REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1142    result = codec->startScanlineDecode(infoF16);
1143    if (supportsScanlineDecoder) {
1144        REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1145    } else {
1146        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
1147    }
1148
1149    result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes());
1150    if (supportsIncrementalDecoder) {
1151        REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1152    } else {
1153        REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
1154    }
1155}
1156
1157DEF_TEST(Codec_F16ConversionPossible, r) {
1158    test_conversion_possible(r, "images/color_wheel.webp", false, false);
1159    test_conversion_possible(r, "images/mandrill_512_q075.jpg", true, false);
1160    test_conversion_possible(r, "images/yellow_rose.png", false, true);
1161}
1162
1163static void decode_frame(skiatest::Reporter* r, SkCodec* codec, size_t frame) {
1164    SkBitmap bm;
1165    auto info = codec->getInfo().makeColorType(kN32_SkColorType);
1166    bm.allocPixels(info);
1167
1168    SkCodec::Options opts;
1169    opts.fFrameIndex = frame;
1170    REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getPixels(info,
1171            bm.getPixels(), bm.rowBytes(), &opts));
1172}
1173
1174// For an animated GIF, we should only read enough to decode frame 0 if the
1175// client never calls getFrameInfo and only decodes frame 0.
1176DEF_TEST(Codec_skipFullParse, r) {
1177    auto path = "images/test640x479.gif";
1178    auto streamObj = GetResourceAsStream(path);
1179    if (!streamObj) {
1180        return;
1181    }
1182    SkStream* stream = streamObj.get();
1183
1184    // Note that we cheat and hold on to the stream pointer, but SkCodec will
1185    // take ownership. We will not refer to the stream after the SkCodec
1186    // deletes it.
1187    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(streamObj)));
1188    if (!codec) {
1189        ERRORF(r, "Failed to create codec for %s", path);
1190        return;
1191    }
1192
1193    REPORTER_ASSERT(r, stream->hasPosition());
1194    const size_t sizePosition = stream->getPosition();
1195    REPORTER_ASSERT(r, stream->hasLength() && sizePosition < stream->getLength());
1196
1197    // This should read more of the stream, but not the whole stream.
1198    decode_frame(r, codec.get(), 0);
1199    const size_t positionAfterFirstFrame = stream->getPosition();
1200    REPORTER_ASSERT(r, positionAfterFirstFrame > sizePosition
1201                       && positionAfterFirstFrame < stream->getLength());
1202
1203    // There is more data in the stream.
1204    auto frameInfo = codec->getFrameInfo();
1205    REPORTER_ASSERT(r, frameInfo.size() == 4);
1206    REPORTER_ASSERT(r, stream->getPosition() > positionAfterFirstFrame);
1207}
1208
1209// Only rewinds up to a limit.
1210class LimitedRewindingStream : public SkStream {
1211public:
1212    static std::unique_ptr<SkStream> Make(const char path[], size_t limit) {
1213        auto stream = GetResourceAsStream(path);
1214        if (!stream) {
1215            return nullptr;
1216        }
1217        return std::unique_ptr<SkStream>(new LimitedRewindingStream(std::move(stream), limit));
1218    }
1219
1220    size_t read(void* buffer, size_t size) override {
1221        const size_t bytes = fStream->read(buffer, size);
1222        fPosition += bytes;
1223        return bytes;
1224    }
1225
1226    bool isAtEnd() const override {
1227        return fStream->isAtEnd();
1228    }
1229
1230    bool rewind() override {
1231        if (fPosition <= fLimit && fStream->rewind()) {
1232            fPosition = 0;
1233            return true;
1234        }
1235
1236        return false;
1237    }
1238
1239private:
1240    std::unique_ptr<SkStream> fStream;
1241    const size_t              fLimit;
1242    size_t                    fPosition;
1243
1244    LimitedRewindingStream(std::unique_ptr<SkStream> stream, size_t limit)
1245        : fStream(std::move(stream))
1246        , fLimit(limit)
1247        , fPosition(0)
1248    {
1249        SkASSERT(fStream);
1250    }
1251};
1252
1253DEF_TEST(Codec_fallBack, r) {
1254    // SkAndroidCodec needs to be able to fall back to scanline decoding
1255    // if incremental decoding does not work. Make sure this does not
1256    // require a rewind.
1257
1258    // Formats that currently do not support incremental decoding
1259    auto files = {
1260            "images/CMYK.jpg",
1261            "images/color_wheel.ico",
1262            "images/mandrill.wbmp",
1263            "images/randPixels.bmp",
1264            };
1265    for (auto file : files) {
1266        auto stream = LimitedRewindingStream::Make(file, SkCodec::MinBufferedBytesNeeded());
1267        if (!stream) {
1268            SkDebugf("Missing resources (%s). Set --resourcePath.\n", file);
1269            return;
1270        }
1271
1272        std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1273        if (!codec) {
1274            ERRORF(r, "Failed to create codec for %s,", file);
1275            continue;
1276        }
1277
1278        SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
1279        SkBitmap bm;
1280        bm.allocPixels(info);
1281
1282        if (SkCodec::kUnimplemented != codec->startIncrementalDecode(info, bm.getPixels(),
1283                bm.rowBytes())) {
1284            ERRORF(r, "Is scanline decoding now implemented for %s?", file);
1285            continue;
1286        }
1287
1288        // Scanline decoding should not require a rewind.
1289        SkCodec::Result result = codec->startScanlineDecode(info);
1290        if (SkCodec::kSuccess != result) {
1291            ERRORF(r, "Scanline decoding failed for %s with %i", file, result);
1292        }
1293    }
1294}
1295
1296// This test verifies that we fixed an assert statement that fired when reusing a png codec
1297// after scaling.
1298DEF_TEST(Codec_reusePng, r) {
1299    std::unique_ptr<SkStream> stream(GetResourceAsStream("images/plane.png"));
1300    if (!stream) {
1301        return;
1302    }
1303
1304    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream)));
1305    if (!codec) {
1306        ERRORF(r, "Failed to create codec\n");
1307        return;
1308    }
1309
1310    SkAndroidCodec::AndroidOptions opts;
1311    opts.fSampleSize = 5;
1312    auto size = codec->getSampledDimensions(opts.fSampleSize);
1313    auto info = codec->getInfo().makeWH(size.fWidth, size.fHeight).makeColorType(kN32_SkColorType);
1314    SkBitmap bm;
1315    bm.allocPixels(info);
1316    auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts);
1317    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1318
1319    info = codec->getInfo().makeColorType(kN32_SkColorType);
1320    bm.allocPixels(info);
1321    opts.fSampleSize = 1;
1322    result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts);
1323    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1324}
1325
1326DEF_TEST(Codec_rowsDecoded, r) {
1327    auto file = "images/plane_interlaced.png";
1328    std::unique_ptr<SkStream> stream(GetResourceAsStream(file));
1329    if (!stream) {
1330        return;
1331    }
1332
1333    // This is enough to read the header etc, but no rows.
1334    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(SkData::MakeFromStream(stream.get(), 99)));
1335    if (!codec) {
1336        ERRORF(r, "Failed to create codec\n");
1337        return;
1338    }
1339
1340    auto info = codec->getInfo().makeColorType(kN32_SkColorType);
1341    SkBitmap bm;
1342    bm.allocPixels(info);
1343    auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
1344    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1345
1346    // This is an arbitrary value. The important fact is that it is not zero, and rowsDecoded
1347    // should get set to zero by incrementalDecode.
1348    int rowsDecoded = 77;
1349    result = codec->incrementalDecode(&rowsDecoded);
1350    REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
1351    REPORTER_ASSERT(r, rowsDecoded == 0);
1352}
1353
1354static void test_invalid_images(skiatest::Reporter* r, const char* path,
1355                                SkCodec::Result expectedResult) {
1356    auto stream = GetResourceAsStream(path);
1357    if (!stream) {
1358        return;
1359    }
1360
1361    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1362    REPORTER_ASSERT(r, codec);
1363
1364    test_info(r, codec.get(), codec->getInfo().makeColorType(kN32_SkColorType), expectedResult,
1365              nullptr);
1366}
1367
1368DEF_TEST(Codec_InvalidImages, r) {
1369    // ASAN will complain if there is an issue.
1370    test_invalid_images(r, "invalid_images/skbug5887.gif", SkCodec::kErrorInInput);
1371    test_invalid_images(r, "invalid_images/many-progressive-scans.jpg", SkCodec::kInvalidInput);
1372    test_invalid_images(r, "invalid_images/b33251605.bmp", SkCodec::kIncompleteInput);
1373    test_invalid_images(r, "invalid_images/bad_palette.png", SkCodec::kInvalidInput);
1374}
1375
1376static void test_invalid_header(skiatest::Reporter* r, const char* path) {
1377    auto data = GetResourceAsData(path);
1378    if (!data) {
1379        return;
1380    }
1381    std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(std::move(data)));
1382    if (!stream) {
1383        return;
1384    }
1385    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1386    REPORTER_ASSERT(r, !codec);
1387}
1388
1389DEF_TEST(Codec_InvalidHeader, r) {
1390    test_invalid_header(r, "invalid_images/int_overflow.ico");
1391
1392    // These files report values that have caused problems with SkFILEStreams.
1393    // They are invalid, and should not create SkCodecs.
1394    test_invalid_header(r, "invalid_images/b33651913.bmp");
1395    test_invalid_header(r, "invalid_images/b34778578.bmp");
1396}
1397
1398DEF_TEST(Codec_InvalidAnimated, r) {
1399    // ASAN will complain if there is an issue.
1400    auto path = "invalid_images/skbug6046.gif";
1401    auto stream = GetResourceAsStream(path);
1402    if (!stream) {
1403        return;
1404    }
1405
1406    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1407    REPORTER_ASSERT(r, codec);
1408    if (!codec) {
1409        return;
1410    }
1411
1412    const auto info = codec->getInfo().makeColorType(kN32_SkColorType);
1413    SkBitmap bm;
1414    bm.allocPixels(info);
1415
1416    auto frameInfos = codec->getFrameInfo();
1417    SkCodec::Options opts;
1418    for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) {
1419        opts.fFrameIndex = i;
1420        const auto reqFrame = frameInfos[i].fRequiredFrame;
1421        opts.fPriorFrame = reqFrame == i - 1 ? reqFrame : SkCodec::kNone;
1422        auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes(), &opts);
1423        if (result != SkCodec::kSuccess) {
1424            ERRORF(r, "Failed to start decoding frame %i (out of %i) with error %i\n", i,
1425                   frameInfos.size(), result);
1426            continue;
1427        }
1428
1429        codec->incrementalDecode();
1430    }
1431}
1432
1433static void encode_format(SkDynamicMemoryWStream* stream, const SkPixmap& pixmap,
1434                          SkTransferFunctionBehavior unpremulBehavior,
1435                          SkEncodedImageFormat format) {
1436    SkPngEncoder::Options pngOptions;
1437    SkWebpEncoder::Options webpOptions;
1438    pngOptions.fUnpremulBehavior = unpremulBehavior;
1439    webpOptions.fUnpremulBehavior = unpremulBehavior;
1440    switch (format) {
1441        case SkEncodedImageFormat::kPNG:
1442            SkPngEncoder::Encode(stream, pixmap, pngOptions);
1443            break;
1444        case SkEncodedImageFormat::kJPEG:
1445            SkJpegEncoder::Encode(stream, pixmap, SkJpegEncoder::Options());
1446            break;
1447        case SkEncodedImageFormat::kWEBP:
1448            SkWebpEncoder::Encode(stream, pixmap, webpOptions);
1449            break;
1450        default:
1451            SkASSERT(false);
1452            break;
1453    }
1454}
1455
1456static void test_encode_icc(skiatest::Reporter* r, SkEncodedImageFormat format,
1457                            SkTransferFunctionBehavior unpremulBehavior) {
1458    // Test with sRGB color space.
1459    SkBitmap srgbBitmap;
1460    SkImageInfo srgbInfo = SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType);
1461    srgbBitmap.allocPixels(srgbInfo);
1462    *srgbBitmap.getAddr32(0, 0) = 0;
1463    SkPixmap pixmap;
1464    srgbBitmap.peekPixels(&pixmap);
1465    SkDynamicMemoryWStream srgbBuf;
1466    encode_format(&srgbBuf, pixmap, unpremulBehavior, format);
1467    sk_sp<SkData> srgbData = srgbBuf.detachAsData();
1468    std::unique_ptr<SkCodec> srgbCodec(SkCodec::MakeFromData(srgbData));
1469    REPORTER_ASSERT(r, srgbCodec->getInfo().colorSpace() == SkColorSpace::MakeSRGB().get());
1470
1471    // Test with P3 color space.
1472    SkDynamicMemoryWStream p3Buf;
1473    sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
1474                                                   SkColorSpace::kDCIP3_D65_Gamut);
1475    pixmap.setColorSpace(p3);
1476    encode_format(&p3Buf, pixmap, unpremulBehavior, format);
1477    sk_sp<SkData> p3Data = p3Buf.detachAsData();
1478    std::unique_ptr<SkCodec> p3Codec(SkCodec::MakeFromData(p3Data));
1479    REPORTER_ASSERT(r, p3Codec->getInfo().colorSpace()->gammaCloseToSRGB());
1480    SkMatrix44 mat0(SkMatrix44::kUninitialized_Constructor);
1481    SkMatrix44 mat1(SkMatrix44::kUninitialized_Constructor);
1482    bool success = p3->toXYZD50(&mat0);
1483    REPORTER_ASSERT(r, success);
1484    success = p3Codec->getInfo().colorSpace()->toXYZD50(&mat1);
1485    REPORTER_ASSERT(r, success);
1486
1487    for (int i = 0; i < 4; i++) {
1488        for (int j = 0; j < 4; j++) {
1489            REPORTER_ASSERT(r, color_space_almost_equal(mat0.get(i, j), mat1.get(i, j)));
1490        }
1491    }
1492}
1493
1494DEF_TEST(Codec_EncodeICC, r) {
1495    test_encode_icc(r, SkEncodedImageFormat::kPNG, SkTransferFunctionBehavior::kRespect);
1496    test_encode_icc(r, SkEncodedImageFormat::kJPEG, SkTransferFunctionBehavior::kRespect);
1497    test_encode_icc(r, SkEncodedImageFormat::kWEBP, SkTransferFunctionBehavior::kRespect);
1498    test_encode_icc(r, SkEncodedImageFormat::kPNG, SkTransferFunctionBehavior::kIgnore);
1499    test_encode_icc(r, SkEncodedImageFormat::kJPEG, SkTransferFunctionBehavior::kIgnore);
1500    test_encode_icc(r, SkEncodedImageFormat::kWEBP, SkTransferFunctionBehavior::kIgnore);
1501}
1502
1503DEF_TEST(Codec_webp_rowsDecoded, r) {
1504    const char* path = "images/baby_tux.webp";
1505    sk_sp<SkData> data(GetResourceAsData(path));
1506    if (!data) {
1507        return;
1508    }
1509
1510    // Truncate this file so that the header is available but no rows can be
1511    // decoded. This should create a codec but fail to decode.
1512    size_t truncatedSize = 5000;
1513    sk_sp<SkData> subset = SkData::MakeSubset(data.get(), 0, truncatedSize);
1514    std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(std::move(subset));
1515    if (!codec) {
1516        ERRORF(r, "Failed to create a codec for %s truncated to only %lu bytes",
1517               path, truncatedSize);
1518        return;
1519    }
1520
1521    test_info(r, codec.get(), codec->getInfo(), SkCodec::kInvalidInput, nullptr);
1522}
1523