• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/codec/SkAndroidCodec.h"
9 #include "include/codec/SkCodec.h"
10 #include "include/codec/SkEncodedImageFormat.h"
11 #include "include/codec/SkGifDecoder.h"
12 #include "include/codec/SkJpegDecoder.h"
13 #include "include/codec/SkPngChunkReader.h"
14 #include "include/core/SkAlphaType.h"
15 #include "include/core/SkBitmap.h"
16 #include "include/core/SkCanvas.h"
17 #include "include/core/SkColor.h"
18 #include "include/core/SkColorSpace.h"
19 #include "include/core/SkColorType.h"
20 #include "include/core/SkData.h"
21 #include "include/core/SkDataTable.h"
22 #include "include/core/SkImage.h"
23 #include "include/core/SkImageGenerator.h"
24 #include "include/core/SkImageInfo.h"
25 #include "include/core/SkPixmap.h"
26 #include "include/core/SkRect.h"
27 #include "include/core/SkRefCnt.h"
28 #include "include/core/SkSize.h"
29 #include "include/core/SkStream.h"
30 #include "include/core/SkString.h"
31 #include "include/core/SkTypes.h"
32 #include "include/encode/SkJpegEncoder.h"
33 #include "include/encode/SkPngEncoder.h"
34 #include "include/encode/SkWebpEncoder.h"
35 #include "include/private/base/SkAlign.h"
36 #include "include/private/base/SkDebug.h"
37 #include "include/private/base/SkMalloc.h"
38 #include "include/private/base/SkTemplates.h"
39 #include "modules/skcms/skcms.h"
40 #include "src/base/SkAutoMalloc.h"
41 #include "src/base/SkRandom.h"
42 #include "src/codec/SkCodecImageGenerator.h"
43 #include "src/core/SkColorSpacePriv.h"
44 #include "src/core/SkMD5.h"
45 #include "src/core/SkStreamPriv.h"
46 #include "tests/FakeStreams.h"
47 #include "tests/Test.h"
48 #include "tools/DecodeUtils.h"
49 #include "tools/Resources.h"
50 #include "tools/ToolUtils.h"
51 
52 #ifdef SK_ENABLE_ANDROID_UTILS
53 #include "client_utils/android/FrontBufferedStream.h"
54 #endif
55 
56 #include <png.h>
57 #include <pngconf.h>
58 #include <setjmp.h>
59 
60 #include <algorithm>
61 #include <cstdint>
62 #include <cstring>
63 #include <initializer_list>
64 #include <memory>
65 #include <optional>
66 #include <utility>
67 #include <vector>
68 
69 using namespace skia_private;
70 
71 #if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR < 5
72     // FIXME (scroggo): Google3 needs to be updated to use a newer version of libpng. In
73     // the meantime, we had to break some pieces of SkPngCodec in order to support Google3.
74     // The parts that are broken are likely not used by Google3.
75     #define SK_PNG_DISABLE_TESTS
76 #endif
77 
md5(const SkBitmap & bm)78 static SkMD5::Digest md5(const SkBitmap& bm) {
79     SkASSERT(bm.getPixels());
80     SkMD5 md5;
81     size_t rowLen = bm.info().bytesPerPixel() * bm.width();
82     for (int y = 0; y < bm.height(); ++y) {
83         md5.write(bm.getAddr(0, y), rowLen);
84     }
85     return md5.finish();
86 }
87 
88 /**
89  *  Compute the digest for bm and compare it to a known good digest.
90  *  @param r Reporter to assert that bm's digest matches goodDigest.
91  *  @param goodDigest The known good digest to compare to.
92  *  @param bm The bitmap to test.
93  */
compare_to_good_digest(skiatest::Reporter * r,const SkMD5::Digest & goodDigest,const SkBitmap & bm)94 static void compare_to_good_digest(skiatest::Reporter* r, const SkMD5::Digest& goodDigest,
95                            const SkBitmap& bm) {
96     SkMD5::Digest digest = md5(bm);
97     REPORTER_ASSERT(r, digest == goodDigest);
98 }
99 
100 /**
101  *  Test decoding an SkCodec to a particular SkImageInfo.
102  *
103  *  Calling getPixels(info) should return expectedResult, and if goodDigest is non nullptr,
104  *  the resulting decode should match.
105  */
106 template<typename Codec>
test_info(skiatest::Reporter * r,Codec * codec,const SkImageInfo & info,SkCodec::Result expectedResult,const SkMD5::Digest * goodDigest)107 static void test_info(skiatest::Reporter* r, Codec* codec, const SkImageInfo& info,
108                       SkCodec::Result expectedResult, const SkMD5::Digest* goodDigest) {
109     SkBitmap bm;
110     bm.allocPixels(info);
111 
112     SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
113     REPORTER_ASSERT(r, result == expectedResult);
114 
115     if (goodDigest) {
116         compare_to_good_digest(r, *goodDigest, bm);
117     }
118 }
119 
generate_random_subset(SkRandom * rand,int w,int h)120 SkIRect generate_random_subset(SkRandom* rand, int w, int h) {
121     SkIRect rect;
122     do {
123         rect.fLeft = rand->nextRangeU(0, w);
124         rect.fTop = rand->nextRangeU(0, h);
125         rect.fRight = rand->nextRangeU(0, w);
126         rect.fBottom = rand->nextRangeU(0, h);
127         rect.sort();
128     } while (rect.isEmpty());
129     return rect;
130 }
131 
test_incremental_decode(skiatest::Reporter * r,SkCodec * codec,const SkImageInfo & info,const SkMD5::Digest & goodDigest)132 static void test_incremental_decode(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info,
133         const SkMD5::Digest& goodDigest) {
134     SkBitmap bm;
135     bm.allocPixels(info);
136 
137     REPORTER_ASSERT(r, SkCodec::kSuccess == codec->startIncrementalDecode(info, bm.getPixels(),
138                                                                           bm.rowBytes()));
139 
140     REPORTER_ASSERT(r, SkCodec::kSuccess == codec->incrementalDecode());
141 
142     compare_to_good_digest(r, goodDigest, bm);
143 }
144 
145 // Test in stripes, similar to DM's kStripe_Mode
test_in_stripes(skiatest::Reporter * r,SkCodec * codec,const SkImageInfo & info,const SkMD5::Digest & goodDigest)146 static void test_in_stripes(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info,
147                             const SkMD5::Digest& goodDigest) {
148     SkBitmap bm;
149     bm.allocPixels(info);
150     bm.eraseColor(SK_ColorYELLOW);
151 
152     const int height = info.height();
153     // Note that if numStripes does not evenly divide height there will be an extra
154     // stripe.
155     const int numStripes = 4;
156 
157     if (numStripes > height) {
158         // Image is too small.
159         return;
160     }
161 
162     const int stripeHeight = height / numStripes;
163 
164     // Iterate through the image twice. Once to decode odd stripes, and once for even.
165     for (int oddEven = 1; oddEven >= 0; oddEven--) {
166         for (int y = oddEven * stripeHeight; y < height; y += 2 * stripeHeight) {
167             SkIRect subset = SkIRect::MakeLTRB(0, y, info.width(),
168                                                std::min(y + stripeHeight, height));
169             SkCodec::Options options;
170             options.fSubset = &subset;
171             if (SkCodec::kSuccess != codec->startIncrementalDecode(info, bm.getAddr(0, y),
172                         bm.rowBytes(), &options)) {
173                 ERRORF(r, "failed to start incremental decode!\ttop: %i\tbottom%i\n",
174                        subset.top(), subset.bottom());
175                 return;
176             }
177             if (SkCodec::kSuccess != codec->incrementalDecode()) {
178                 ERRORF(r, "failed incremental decode starting from line %i\n", y);
179                 return;
180             }
181         }
182     }
183 
184     compare_to_good_digest(r, goodDigest, bm);
185 }
186 
187 template<typename Codec>
test_codec(skiatest::Reporter * r,const char * path,Codec * codec,SkBitmap & bm,const SkImageInfo & info,const SkISize & size,SkCodec::Result expectedResult,SkMD5::Digest * digest,const SkMD5::Digest * goodDigest)188 static void test_codec(skiatest::Reporter* r, const char* path, Codec* codec, SkBitmap& bm,
189         const SkImageInfo& info, const SkISize& size, SkCodec::Result expectedResult,
190         SkMD5::Digest* digest, const SkMD5::Digest* goodDigest) {
191 
192     REPORTER_ASSERT(r, info.dimensions() == size);
193     bm.allocPixels(info);
194 
195     SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
196     REPORTER_ASSERT(r, result == expectedResult);
197 
198     *digest = md5(bm);
199     if (goodDigest) {
200         REPORTER_ASSERT(r, *digest == *goodDigest);
201     }
202 
203     {
204         // Test decoding to 565
205         SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType);
206         if (info.alphaType() == kOpaque_SkAlphaType) {
207             // Decoding to 565 should succeed.
208             SkBitmap bm565;
209             bm565.allocPixels(info565);
210 
211             // This will allow comparison even if the image is incomplete.
212             bm565.eraseColor(SK_ColorBLACK);
213 
214             auto actualResult = codec->getPixels(info565, bm565.getPixels(), bm565.rowBytes());
215             if (actualResult == expectedResult) {
216                 SkMD5::Digest digest565 = md5(bm565);
217 
218                 // A request for non-opaque should also succeed.
219                 for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) {
220                     info565 = info565.makeAlphaType(alpha);
221                     test_info(r, codec, info565, expectedResult, &digest565);
222                 }
223             } else {
224                 ERRORF(r, "Decoding %s to 565 failed with result \"%s\"\n\t\t\t\texpected:\"%s\"",
225                           path,
226                           SkCodec::ResultToString(actualResult),
227                           SkCodec::ResultToString(expectedResult));
228             }
229         } else {
230             test_info(r, codec, info565, SkCodec::kInvalidConversion, nullptr);
231         }
232     }
233 
234     if (codec->getInfo().colorType() == kGray_8_SkColorType) {
235         SkImageInfo grayInfo = codec->getInfo();
236         SkBitmap grayBm;
237         grayBm.allocPixels(grayInfo);
238 
239         grayBm.eraseColor(SK_ColorBLACK);
240 
241         REPORTER_ASSERT(r, expectedResult == codec->getPixels(grayInfo,
242                 grayBm.getPixels(), grayBm.rowBytes()));
243 
244         SkMD5::Digest grayDigest = md5(grayBm);
245 
246         for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) {
247             grayInfo = grayInfo.makeAlphaType(alpha);
248             test_info(r, codec, grayInfo, expectedResult, &grayDigest);
249         }
250     }
251 
252     // Verify that re-decoding gives the same result.  It is interesting to check this after
253     // a decode to 565, since choosing to decode to 565 may result in some of the decode
254     // options being modified.  These options should return to their defaults on another
255     // decode to kN32, so the new digest should match the old digest.
256     test_info(r, codec, info, expectedResult, digest);
257 
258     {
259         // Check alpha type conversions
260         if (info.alphaType() == kOpaque_SkAlphaType) {
261             test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType),
262                       expectedResult, digest);
263             test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType),
264                       expectedResult, digest);
265         } else {
266             // Decoding to opaque should fail
267             test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType),
268                       SkCodec::kInvalidConversion, nullptr);
269             SkAlphaType otherAt = info.alphaType();
270             if (kPremul_SkAlphaType == otherAt) {
271                 otherAt = kUnpremul_SkAlphaType;
272             } else {
273                 otherAt = kPremul_SkAlphaType;
274             }
275             // The other non-opaque alpha type should always succeed, but not match.
276             test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nullptr);
277         }
278     }
279 }
280 
supports_partial_scanlines(const char path[])281 static bool supports_partial_scanlines(const char path[]) {
282     static const char* const exts[] = {
283         "jpg", "jpeg", "png", "webp",
284         "JPG", "JPEG", "PNG", "WEBP"
285     };
286 
287     for (uint32_t i = 0; i < std::size(exts); i++) {
288         if (SkStrEndsWith(path, exts[i])) {
289             return true;
290         }
291     }
292     return false;
293 }
294 
check_scanline_decode(skiatest::Reporter * r,SkCodec * codec,SkMD5::Digest * codecDigest,const SkImageInfo & info,const char path[],SkISize size,bool supportsScanlineDecoding,bool supportsIncomplete,bool supportsNewScanlineDecoding)295 static void check_scanline_decode(skiatest::Reporter* r,
296                                   SkCodec* codec,
297                                   SkMD5::Digest* codecDigest,
298                                   const SkImageInfo& info,
299                                   const char path[],
300                                   SkISize size,
301                                   bool supportsScanlineDecoding,
302                                   bool supportsIncomplete,
303                                   bool supportsNewScanlineDecoding) {
304 
305     // Test full image decodes with SkCodec
306     SkBitmap bm;
307     const SkCodec::Result expectedResult = supportsIncomplete ? SkCodec::kIncompleteInput
308                                                               : SkCodec::kSuccess;
309     test_codec(r, path, codec, bm, info, size, expectedResult, codecDigest, nullptr);
310 
311     // Scanline decoding follows.
312     if (supportsNewScanlineDecoding && !supportsIncomplete) {
313         test_incremental_decode(r, codec, info, *codecDigest);
314         // This is only supported by codecs that use incremental decoding to
315         // support subset decodes - png and jpeg (once SkJpegCodec is
316         // converted).
317         if (SkStrEndsWith(path, "png") || SkStrEndsWith(path, "PNG")) {
318             test_in_stripes(r, codec, info, *codecDigest);
319         }
320     }
321 
322     // Need to call startScanlineDecode() first.
323     REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) == 0);
324     REPORTER_ASSERT(r, !codec->skipScanlines(1));
325     const SkCodec::Result startResult = codec->startScanlineDecode(info);
326     if (supportsScanlineDecoding) {
327         bm.eraseColor(SK_ColorYELLOW);
328 
329         REPORTER_ASSERT(r, startResult == SkCodec::kSuccess);
330 
331         for (int y = 0; y < info.height(); y++) {
332             const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
333             if (!supportsIncomplete) {
334                 REPORTER_ASSERT(r, 1 == lines);
335             }
336         }
337         // verify that scanline decoding gives the same result.
338         if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) {
339             compare_to_good_digest(r, *codecDigest, bm);
340         }
341 
342         // Cannot continue to decode scanlines beyond the end
343         REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
344                 == 0);
345 
346         // Interrupting a scanline decode with a full decode starts from
347         // scratch
348         {
349             REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess);
350             const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0);
351             if (!supportsIncomplete) {
352                 REPORTER_ASSERT(r, lines == 1);
353             }
354             REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes())
355                     == expectedResult);
356             REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0)
357                     == 0);
358             REPORTER_ASSERT(r, codec->skipScanlines(1)
359                     == 0);
360         }
361 
362         // Test partial scanline decodes
363         if (supports_partial_scanlines(path) && info.width() >= 3) {
364             SkCodec::Options options;
365             int width = info.width();
366             int height = info.height();
367             SkIRect subset = SkIRect::MakeXYWH(2 * (width / 3), 0, width / 3, height);
368             options.fSubset = &subset;
369 
370             const auto partialStartResult = codec->startScanlineDecode(info, &options);
371             REPORTER_ASSERT(r, partialStartResult == SkCodec::kSuccess);
372 
373             for (int y = 0; y < height; y++) {
374                 const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0);
375                 if (!supportsIncomplete) {
376                     REPORTER_ASSERT(r, 1 == lines);
377                 }
378             }
379         }
380     } else {
381         REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented);
382     }
383 }
384 
check_subset_decode(skiatest::Reporter * r,SkCodec * codec,const SkImageInfo & info,SkISize size,bool supportsSubsetDecoding,bool supportsIncomplete)385 static void check_subset_decode(skiatest::Reporter* r,
386                                 SkCodec* codec,
387                                 const SkImageInfo& info,
388                                 SkISize size,
389                                 bool supportsSubsetDecoding,
390                                 bool supportsIncomplete) {
391     // This function tests decoding subsets, and will decode a handful of randomly-sized subsets.
392     // Do not attempt to decode subsets of an image of only one pixel, since there is no
393     // meaningful subset.
394     if (size.width() * size.height() == 1) {
395         return;
396     }
397 
398     SkRandom rand;
399     SkIRect subset;
400     SkCodec::Options opts;
401     opts.fSubset = &subset;
402     for (int i = 0; i < 5; i++) {
403         subset = generate_random_subset(&rand, size.width(), size.height());
404         SkASSERT(!subset.isEmpty());
405         const bool supported = codec->getValidSubset(&subset);
406         REPORTER_ASSERT(r, supported == supportsSubsetDecoding);
407 
408         SkImageInfo subsetInfo = info.makeDimensions(subset.size());
409         SkBitmap bm;
410         bm.allocPixels(subsetInfo);
411         const auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts);
412 
413         if (supportsSubsetDecoding) {
414             if (!supportsIncomplete) {
415                 REPORTER_ASSERT(r, result == SkCodec::kSuccess);
416             }
417             // Webp is the only codec that supports subsets, and it will have modified the subset
418             // to have even left/top.
419             REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop));
420         } else {
421             // No subsets will work.
422             REPORTER_ASSERT(r, result == SkCodec::kUnimplemented);
423         }
424     }
425 }
426 
check_android_codec(skiatest::Reporter * r,std::unique_ptr<SkCodec> codec,const SkMD5::Digest & codecDigest,const SkImageInfo & info,const char path[],SkISize size,bool supportsScanlineDecoding,bool supportsSubsetDecoding,bool supportsIncomplete,bool supportsNewScanlineDecoding)427 static void check_android_codec(skiatest::Reporter* r,
428                                 std::unique_ptr<SkCodec> codec,
429                                 const SkMD5::Digest& codecDigest,
430                                 const SkImageInfo& info,
431                                 const char path[],
432                                 SkISize size,
433                                 bool supportsScanlineDecoding,
434                                 bool supportsSubsetDecoding,
435                                 bool supportsIncomplete,
436                                 bool supportsNewScanlineDecoding) {
437     if (supportsScanlineDecoding || supportsSubsetDecoding || supportsNewScanlineDecoding) {
438         auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
439         if (!androidCodec) {
440             ERRORF(r, "Unable to decode '%s'", path);
441             return;
442         }
443 
444         SkBitmap bm;
445         SkMD5::Digest androidCodecDigest;
446         const SkCodec::Result expectedResult = supportsIncomplete ? SkCodec::kIncompleteInput
447                                                                   : SkCodec::kSuccess;
448         test_codec(r, path, androidCodec.get(), bm, info, size, expectedResult, &androidCodecDigest,
449                    &codecDigest);
450     }
451 }
452 
check_codec_image_generator(skiatest::Reporter * r,const SkMD5::Digest & codecDigest,const SkImageInfo & info,const char path[],bool supportsIncomplete)453 static void check_codec_image_generator(skiatest::Reporter* r,
454                                         const SkMD5::Digest& codecDigest,
455                                         const SkImageInfo& info,
456                                         const char path[],
457                                         bool supportsIncomplete) {
458     // Test SkCodecImageGenerator
459     if (!supportsIncomplete) {
460         std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
461         sk_sp<SkData> fullData(SkData::MakeFromStream(stream.get(), stream->getLength()));
462         std::unique_ptr<SkImageGenerator> gen(
463                 SkCodecImageGenerator::MakeFromEncodedCodec(fullData));
464         SkBitmap bm;
465         bm.allocPixels(info);
466         REPORTER_ASSERT(r, gen->getPixels(info, bm.getPixels(), bm.rowBytes()));
467         compare_to_good_digest(r, codecDigest, bm);
468 
469 #if !defined(SK_PNG_DISABLE_TESTS) && defined(SK_ENABLE_ANDROID_UTILS)
470         // Test using FrontBufferedStream, as Android does
471         auto bufferedStream = android::skia::FrontBufferedStream::Make(
472                       SkMemoryStream::Make(std::move(fullData)), SkCodec::MinBufferedBytesNeeded());
473         REPORTER_ASSERT(r, bufferedStream);
474         std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(std::move(bufferedStream));
475         REPORTER_ASSERT(r, codec);
476         if (codec) {
477             test_info(r, codec.get(), info, SkCodec::kSuccess, &codecDigest);
478         }
479 #endif
480     }
481 }
482 
check(skiatest::Reporter * r,const char path[],SkISize size,bool supportsScanlineDecoding,bool supportsSubsetDecoding,bool supportsIncomplete,bool supportsNewScanlineDecoding=false)483 static void check(skiatest::Reporter* r,
484                   const char path[],
485                   SkISize size,
486                   bool supportsScanlineDecoding,
487                   bool supportsSubsetDecoding,
488                   bool supportsIncomplete,
489                   bool supportsNewScanlineDecoding = false) {
490     skiatest::ReporterContext context(r, path);
491     // If we're testing incomplete decodes, let's run the same test on full decodes.
492     if (supportsIncomplete) {
493         check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding,
494               /*supportsIncomplete=*/false, supportsNewScanlineDecoding);
495     }
496 
497     // Initialize a codec with a data stream.
498     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
499     if (!stream) {
500         return;
501     }
502 
503     std::unique_ptr<SkCodec> codec;
504     if (supportsIncomplete) {
505         size_t length = stream->getLength();
506         codec = SkCodec::MakeFromData(SkData::MakeFromStream(stream.get(), 2 * length / 3));
507     } else {
508         codec = SkCodec::MakeFromStream(std::move(stream));
509     }
510     if (!codec) {
511         ERRORF(r, "Unable to decode '%s'", path);
512         return;
513     }
514 
515     const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
516 
517     // Run tests with this codec.
518     SkMD5::Digest codecDigest;
519     check_scanline_decode(r, codec.get(), &codecDigest, info, path, size, supportsScanlineDecoding,
520                           supportsIncomplete, supportsNewScanlineDecoding);
521 
522     check_subset_decode(r, codec.get(), info, size, supportsSubsetDecoding, supportsIncomplete);
523 
524     check_android_codec(r, std::move(codec), codecDigest, info, path, size,
525                         supportsScanlineDecoding, supportsSubsetDecoding, supportsIncomplete,
526                         supportsNewScanlineDecoding);
527 
528     check_codec_image_generator(r, codecDigest, info, path, supportsIncomplete);
529 }
530 
decodeToSkImage(skiatest::Reporter * r,const char * resourcePath,SkColorType dstColorType,SkAlphaType dstAlphaType)531 static sk_sp<SkImage> decodeToSkImage(skiatest::Reporter* r,
532                                       const char* resourcePath,
533                                       SkColorType dstColorType,
534                                       SkAlphaType dstAlphaType) {
535     std::unique_ptr<SkStream> stream(GetResourceAsStream(resourcePath));
536     REPORTER_ASSERT(r, !!stream);
537     if (!stream) {
538         return nullptr;
539     }
540 
541     std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(std::move(stream));
542     REPORTER_ASSERT(r, !!codec);
543     if (!codec) {
544         return nullptr;
545     }
546 
547     SkImageInfo dstInfo = codec->getInfo()
548                                   .makeColorType(dstColorType)
549                                   .makeAlphaType(dstAlphaType)
550                                   .makeColorSpace(SkColorSpace::MakeSRGB());
551     sk_sp<SkImage> image;
552     SkCodec::Result result;
553     std::tie(image, result) = codec->getImage(dstInfo);
554     REPORTER_ASSERT(r, !!result == SkCodec::kSuccess);
555     REPORTER_ASSERT(r, !!image);
556     return image;
557 }
558 
decodeSingleRawPixelAsUint32(skiatest::Reporter * r,const char * resourcePath,SkColorType dstColorType,SkAlphaType dstAlphaType,int x=0,int y=0)559 static std::optional<uint32_t> decodeSingleRawPixelAsUint32(skiatest::Reporter* r,
560                                                             const char* resourcePath,
561                                                             SkColorType dstColorType,
562                                                             SkAlphaType dstAlphaType,
563                                                             int x = 0,
564                                                             int y = 0) {
565     SkASSERT(dstColorType == kBGRA_8888_SkColorType || dstColorType == kRGBA_8888_SkColorType);
566     sk_sp<SkImage> image = decodeToSkImage(r, resourcePath, dstColorType, dstAlphaType);
567     if (!image) {
568         return std::nullopt;  // REPORTER_ASSERT should already fire in `decodeToSkImage`.
569     }
570 
571     SkPixmap pixmap;
572     if (!image->peekPixels(&pixmap)) {
573         REPORTER_ASSERT(r, false, "peekPixels failed");
574         return std::nullopt;
575     }
576 
577     return *pixmap.addr32(x, y);
578 }
579 
DEF_TEST(Codec_wbmp,r)580 DEF_TEST(Codec_wbmp, r) {
581     check(r, "images/mandrill.wbmp", SkISize::Make(512, 512), true, false, true);
582 }
583 
DEF_TEST(Codec_webp,r)584 DEF_TEST(Codec_webp, r) {
585     check(r, "images/baby_tux.webp", SkISize::Make(386, 395), false, true, true);
586     check(r, "images/color_wheel.webp", SkISize::Make(128, 128), false, true, true);
587     check(r, "images/yellow_rose.webp", SkISize::Make(400, 301), false, true, true);
588 }
589 
DEF_TEST(Codec_bmp,r)590 DEF_TEST(Codec_bmp, r) {
591     check(r, "images/randPixels.bmp", SkISize::Make(8, 8), true, false, true);
592     check(r, "images/rle.bmp", SkISize::Make(320, 240), true, false, true);
593 }
594 
595 #if defined(SK_CODEC_DECODES_ICO)
DEF_TEST(Codec_ico,r)596 DEF_TEST(Codec_ico, r) {
597     // FIXME: We are not ready to test incomplete ICOs
598     // These two tests examine interestingly different behavior:
599     // Decodes an embedded BMP image
600     check(r, "images/color_wheel.ico", SkISize::Make(128, 128), true, false, false);
601     // Decodes an embedded PNG image
602     check(r, "images/google_chrome.ico", SkISize::Make(256, 256), false, false, false, true);
603 }
604 #endif
605 
DEF_TEST(Codec_gif,r)606 DEF_TEST(Codec_gif, r) {
607     check(r, "images/box.gif", SkISize::Make(200, 55), false, false, true, true);
608     check(r, "images/color_wheel.gif", SkISize::Make(128, 128), false, false, true, true);
609     // randPixels.gif is too small to test incomplete
610     check(r, "images/randPixels.gif", SkISize::Make(8, 8), false, false, false, true);
611 }
612 
DEF_TEST(Codec_jpg,r)613 DEF_TEST(Codec_jpg, r) {
614     check(r, "images/CMYK.jpg", SkISize::Make(642, 516), true, false, true);
615     check(r, "images/color_wheel.jpg", SkISize::Make(128, 128), true, false, true);
616     // grayscale.jpg is too small to test incomplete
617     check(r, "images/grayscale.jpg", SkISize::Make(128, 128), true, false, false);
618     check(r, "images/mandrill_512_q075.jpg", SkISize::Make(512, 512), true, false, true);
619     // randPixels.jpg is too small to test incomplete
620     check(r, "images/randPixels.jpg", SkISize::Make(8, 8), true, false, false);
621 }
622 
DEF_TEST(Codec_png,r)623 DEF_TEST(Codec_png, r) {
624     check(r, "images/arrow.png", SkISize::Make(187, 312), false, false, true, true);
625     check(r, "images/baby_tux.png", SkISize::Make(240, 246), false, false, true, true);
626     check(r, "images/color_wheel.png", SkISize::Make(128, 128), false, false, true, true);
627     // half-transparent-white-pixel.png is too small to test incomplete
628     check(r, "images/half-transparent-white-pixel.png", SkISize::Make(1, 1), false, false, false, true);
629     check(r, "images/mandrill_128.png", SkISize::Make(128, 128), false, false, true, true);
630     // mandrill_16.png is too small (relative to embedded sRGB profile) to test incomplete
631     check(r, "images/mandrill_16.png", SkISize::Make(16, 16), false, false, false, true);
632     check(r, "images/mandrill_256.png", SkISize::Make(256, 256), false, false, true, true);
633     check(r, "images/mandrill_32.png", SkISize::Make(32, 32), false, false, true, true);
634     check(r, "images/mandrill_512.png", SkISize::Make(512, 512), false, false, true, true);
635     check(r, "images/mandrill_64.png", SkISize::Make(64, 64), false, false, true, true);
636     check(r, "images/plane.png", SkISize::Make(250, 126), false, false, true, true);
637     check(r, "images/plane_interlaced.png", SkISize::Make(250, 126), false, false, true, true);
638     check(r, "images/randPixels.png", SkISize::Make(8, 8), false, false, true, true);
639     check(r, "images/yellow_rose.png", SkISize::Make(400, 301), false, false, true, true);
640 }
641 
verifyFirstFourDecodedBytes(skiatest::Reporter * r,const char * fileName,SkColorType dstColorType,SkAlphaType dstAlphaType,std::array<uint8_t,4> expected)642 static void verifyFirstFourDecodedBytes(skiatest::Reporter* r,
643                                         const char* fileName,
644                                         SkColorType dstColorType,
645                                         SkAlphaType dstAlphaType,
646                                         std::array<uint8_t, 4> expected) {
647     std::string resourcePath = "images/";
648     resourcePath += fileName;
649 
650     std::optional<uint32_t> maybeColor =
651             decodeSingleRawPixelAsUint32(r, resourcePath.c_str(), dstColorType, dstAlphaType);
652     if (!maybeColor.has_value()) {
653         // `REPORTER_ASSERT` should already fire in `decodeSingleRaw...`.
654         return;
655     }
656     const uint8_t* pixel = static_cast<const uint8_t*>(static_cast<const void*>(&*maybeColor));
657 
658     std::string testName = "";
659     switch (dstColorType) {
660         case kRGBA_8888_SkColorType:
661             testName += "RGBA";
662             break;
663         case kBGRA_8888_SkColorType:
664             testName += "BGRA";
665             break;
666         default:
667             SkUNREACHABLE;
668     }
669     switch (dstAlphaType) {
670         case kPremul_SkAlphaType:
671             testName += " (premul)";
672             break;
673         case kUnpremul_SkAlphaType:
674             testName += " (premul)";
675             break;
676         default:
677             SkUNREACHABLE;
678     }
679 
680     for (int i = 0; i < 4; i++) {
681         REPORTER_ASSERT(r,
682                         pixel[i] == expected[i],
683                         "%s: Byte #%d: actual = %d; expected = %d",
684                         testName.c_str(),
685                         i,
686                         pixel[i],
687                         expected[i]);
688     }
689 }
690 
DEF_TEST(Codec_png_plte_trns,r)691 DEF_TEST(Codec_png_plte_trns, r) {
692     auto t = verifyFirstFourDecodedBytes;  // Help with succint formatting below...
693 
694     // RGB in `PLTE` chunk is: 100 (0x64), 150 (0x96), 200 (0xC8)
695     // Alpha in `tRNS` chunk is: 64 (i.e. 25% or 0x40)
696     //
697     // After alpha premultiplication by 25% we should get: R=25, G=38, B=50.
698     t(r, "plte_trns.png", kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, {100, 150, 200, 64});
699     t(r, "plte_trns.png", kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, {200, 150, 100, 64});
700     t(r, "plte_trns.png", kRGBA_8888_SkColorType, kPremul_SkAlphaType, {25, 38, 50, 64});
701     t(r, "plte_trns.png", kBGRA_8888_SkColorType, kPremul_SkAlphaType, {50, 38, 25, 64});
702 }
703 
DEF_TEST(Codec_png_plte_trns_gama,r)704 DEF_TEST(Codec_png_plte_trns_gama, r) {
705     auto t = verifyFirstFourDecodedBytes;  // Help with succint formatting below...
706 
707     // RGB in `PLTE` chunk is: 100 (0x64), 150 (0x96), 200 (0xC8)
708     // Alpha in `tRNS` chunk is: 64 (i.e. 25% or 0x40)
709     //
710     // After `gAMA` transformation we should get: R=161, G=197, B=227.
711     //
712     // After alpha premultiplication by 25% we should get: R=40, G=49, B=57.
713     t(r, "plte_trns_gama.png", kRGBA_8888_SkColorType, kUnpremul_SkAlphaType, {161, 197, 227, 64});
714     t(r, "plte_trns_gama.png", kBGRA_8888_SkColorType, kUnpremul_SkAlphaType, {227, 197, 161, 64});
715     t(r, "plte_trns_gama.png", kRGBA_8888_SkColorType, kPremul_SkAlphaType, {40, 49, 57, 64});
716     t(r, "plte_trns_gama.png", kBGRA_8888_SkColorType, kPremul_SkAlphaType, {57, 49, 40, 64});
717 }
718 
719 // Disable RAW tests for Win32.
720 #if defined(SK_CODEC_DECODES_RAW) && !defined(_WIN32)
DEF_TEST(Codec_raw,r)721 DEF_TEST(Codec_raw, r) {
722     check(r, "images/sample_1mp.dng", SkISize::Make(600, 338), false, false, false);
723     check(r, "images/sample_1mp_rotated.dng", SkISize::Make(600, 338), false, false, false);
724     check(r, "images/dng_with_preview.dng", SkISize::Make(600, 338), true, false, false);
725 }
726 #endif
727 
test_invalid_stream(skiatest::Reporter * r,const void * stream,size_t len)728 static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) {
729     // Neither of these calls should return a codec. Bots should catch us if we leaked anything.
730     REPORTER_ASSERT(r, !SkCodec::MakeFromStream(
731                                         std::make_unique<SkMemoryStream>(stream, len, false)));
732     REPORTER_ASSERT(r, !SkAndroidCodec::MakeFromStream(
733                                         std::make_unique<SkMemoryStream>(stream, len, false)));
734 }
735 
736 // Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream,
737 // even on failure. Test some bad streams.
DEF_TEST(Codec_leaks,r)738 DEF_TEST(Codec_leaks, r) {
739     // No codec should claim this as their format, so this tests SkCodec::NewFromStream.
740     const char nonSupportedStream[] = "hello world";
741     // The other strings should look like the beginning of a file type, so we'll call some
742     // internal version of NewFromStream, which must also delete the stream on failure.
743     const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
744     const unsigned char emptyJpeg[] = { 0xFF, 0xD8, 0xFF };
745     const char emptyWebp[] = "RIFF1234WEBPVP";
746     const char emptyBmp[] = { 'B', 'M' };
747     const char emptyIco[] = { '\x00', '\x00', '\x01', '\x00' };
748     const char emptyGif[] = "GIFVER";
749 
750     test_invalid_stream(r, nonSupportedStream, sizeof(nonSupportedStream));
751     test_invalid_stream(r, emptyPng, sizeof(emptyPng));
752     test_invalid_stream(r, emptyJpeg, sizeof(emptyJpeg));
753     test_invalid_stream(r, emptyWebp, sizeof(emptyWebp));
754     test_invalid_stream(r, emptyBmp, sizeof(emptyBmp));
755     test_invalid_stream(r, emptyIco, sizeof(emptyIco));
756     test_invalid_stream(r, emptyGif, sizeof(emptyGif));
757 }
758 
DEF_TEST(Codec_null,r)759 DEF_TEST(Codec_null, r) {
760     // Attempting to create an SkCodec or an SkAndroidCodec with null should not
761     // crash.
762     REPORTER_ASSERT(r, !SkCodec::MakeFromStream(nullptr));
763     REPORTER_ASSERT(r, !SkAndroidCodec::MakeFromStream(nullptr));
764 }
765 
test_dimensions(skiatest::Reporter * r,const char path[])766 static void test_dimensions(skiatest::Reporter* r, const char path[]) {
767     // Create the codec from the resource file
768     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
769     if (!stream) {
770         return;
771     }
772     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream)));
773     if (!codec) {
774         ERRORF(r, "Unable to create codec '%s'", path);
775         return;
776     }
777 
778     // Check that the decode is successful for a variety of scales
779     for (int sampleSize = 1; sampleSize < 32; sampleSize++) {
780         // Scale the output dimensions
781         SkISize scaledDims = codec->getSampledDimensions(sampleSize);
782         SkImageInfo scaledInfo = codec->getInfo()
783                 .makeDimensions(scaledDims)
784                 .makeColorType(kN32_SkColorType);
785 
786         // Set up for the decode
787         size_t rowBytes = scaledDims.width() * sizeof(SkPMColor);
788         size_t totalBytes = scaledInfo.computeByteSize(rowBytes);
789         AutoTMalloc<SkPMColor> pixels(totalBytes);
790 
791         SkAndroidCodec::AndroidOptions options;
792         options.fSampleSize = sampleSize;
793         SkCodec::Result result =
794                 codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options);
795         if (result != SkCodec::kSuccess) {
796             ERRORF(r, "Failed to decode %s with sample size %i; error: %s", path, sampleSize,
797                     SkCodec::ResultToString(result));
798         }
799     }
800 }
801 
802 // Ensure that onGetScaledDimensions returns valid image dimensions to use for decodes
DEF_TEST(Codec_Dimensions,r)803 DEF_TEST(Codec_Dimensions, r) {
804     // JPG
805     test_dimensions(r, "images/CMYK.jpg");
806     test_dimensions(r, "images/color_wheel.jpg");
807     test_dimensions(r, "images/grayscale.jpg");
808     test_dimensions(r, "images/mandrill_512_q075.jpg");
809     test_dimensions(r, "images/randPixels.jpg");
810 
811     // Decoding small images with very large scaling factors is a potential
812     // source of bugs and crashes.  We disable these tests in Gold because
813     // tiny images are not very useful to look at.
814     // Here we make sure that we do not crash or access illegal memory when
815     // performing scaled decodes on small images.
816     test_dimensions(r, "images/1x1.png");
817     test_dimensions(r, "images/2x2.png");
818     test_dimensions(r, "images/3x3.png");
819     test_dimensions(r, "images/3x1.png");
820     test_dimensions(r, "images/1x1.png");
821     test_dimensions(r, "images/16x1.png");
822     test_dimensions(r, "images/1x16.png");
823     test_dimensions(r, "images/mandrill_16.png");
824 
825     // RAW
826 // Disable RAW tests for Win32.
827 #if defined(SK_CODEC_DECODES_RAW) && !defined(_WIN32)
828     test_dimensions(r, "images/sample_1mp.dng");
829     test_dimensions(r, "images/sample_1mp_rotated.dng");
830     test_dimensions(r, "images/dng_with_preview.dng");
831 #endif
832 }
833 
test_invalid(skiatest::Reporter * r,const char path[])834 static void test_invalid(skiatest::Reporter* r, const char path[]) {
835     auto data = GetResourceAsData(path);
836     if (!data) {
837         ERRORF(r, "Failed to get resource %s", path);
838         return;
839     }
840 
841     REPORTER_ASSERT(r, !SkCodec::MakeFromData(data));
842 }
843 
DEF_TEST(Codec_Empty,r)844 DEF_TEST(Codec_Empty, r) {
845     if (GetResourcePath().isEmpty()) {
846         return;
847     }
848 
849     // Test images that should not be able to create a codec
850     test_invalid(r, "empty_images/zero-dims.gif");
851     test_invalid(r, "empty_images/zero-embedded.ico");
852     test_invalid(r, "empty_images/zero-width.bmp");
853     test_invalid(r, "empty_images/zero-height.bmp");
854     test_invalid(r, "empty_images/zero-width.jpg");
855     test_invalid(r, "empty_images/zero-height.jpg");
856     test_invalid(r, "empty_images/zero-width.png");
857     test_invalid(r, "empty_images/zero-height.png");
858     test_invalid(r, "empty_images/zero-width.wbmp");
859     test_invalid(r, "empty_images/zero-height.wbmp");
860     // This image is an ico with an embedded mask-bmp.  This is illegal.
861     test_invalid(r, "invalid_images/mask-bmp-ico.ico");
862     // It is illegal for a webp frame to not be fully contained by the canvas.
863     test_invalid(r, "invalid_images/invalid-offset.webp");
864 #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
865     test_invalid(r, "empty_images/zero_height.tiff");
866 #endif
867     test_invalid(r, "invalid_images/b37623797.ico");
868     test_invalid(r, "invalid_images/osfuzz6295.webp");
869     test_invalid(r, "invalid_images/osfuzz6288.bmp");
870     test_invalid(r, "invalid_images/ossfuzz6347");
871 }
872 
873 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
874 
875 #ifndef SK_PNG_DISABLE_TESTS   // reading chunks does not work properly with older versions.
876                                // It does not appear that anyone in Google3 is reading chunks.
877 
codex_test_write_fn(png_structp png_ptr,png_bytep data,png_size_t len)878 static void codex_test_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) {
879     SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr);
880     if (!sk_stream->write(data, len)) {
881         png_error(png_ptr, "sk_write_fn Error!");
882     }
883 }
884 
DEF_TEST(Codec_pngChunkReader,r)885 DEF_TEST(Codec_pngChunkReader, r) {
886     // Create a bitmap for hashing. Use unpremul RGBA for libpng.
887     SkBitmap bm;
888     const int w = 1;
889     const int h = 1;
890     const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType,
891                                                  kUnpremul_SkAlphaType);
892     bm.setInfo(bmInfo);
893     bm.allocPixels();
894     bm.eraseColor(SK_ColorBLUE);
895     SkMD5::Digest goodDigest = md5(bm);
896 
897     // Write to a png file.
898     png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
899     REPORTER_ASSERT(r, png);
900     if (!png) {
901         return;
902     }
903 
904     png_infop info = png_create_info_struct(png);
905     REPORTER_ASSERT(r, info);
906     if (!info) {
907         png_destroy_write_struct(&png, nullptr);
908         return;
909     }
910 
911     if (setjmp(png_jmpbuf(png))) {
912         ERRORF(r, "failed writing png");
913         png_destroy_write_struct(&png, &info);
914         return;
915     }
916 
917     SkDynamicMemoryWStream wStream;
918     png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr);
919 
920     png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8,
921                  PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
922                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
923 
924 #define PNG_BYTES(str) reinterpret_cast<png_byte*>(const_cast<char*>(str))
925 
926     // Create some chunks that match the Android framework's use.
927     static png_unknown_chunk gUnknowns[] = {
928         { "npOl", PNG_BYTES("outline"), sizeof("outline"), PNG_HAVE_IHDR },
929         { "npLb", PNG_BYTES("layoutBounds"), sizeof("layoutBounds"), PNG_HAVE_IHDR },
930         { "npTc", PNG_BYTES("ninePatchData"), sizeof("ninePatchData"), PNG_HAVE_IHDR },
931     };
932 
933     png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, PNG_BYTES("npOl\0npLb\0npTc\0"), 3);
934     png_set_unknown_chunks(png, info, gUnknowns, std::size(gUnknowns));
935 #if PNG_LIBPNG_VER < 10600
936     /* Deal with unknown chunk location bug in 1.5.x and earlier */
937     png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR);
938     png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR);
939 #endif
940 
941     png_write_info(png, info);
942 
943     for (int j = 0; j < h; j++) {
944         png_bytep row = (png_bytep)(bm.getAddr(0, j));
945         png_write_rows(png, &row, 1);
946     }
947     png_write_end(png, info);
948     png_destroy_write_struct(&png, &info);
949 
950     class ChunkReader : public SkPngChunkReader {
951     public:
952         ChunkReader(skiatest::Reporter* r)
953             : fReporter(r)
954         {
955             this->reset();
956         }
957 
958         bool readChunk(const char tag[], const void* data, size_t length) override {
959             for (size_t i = 0; i < std::size(gUnknowns); ++i) {
960                 if (!strcmp(tag, (const char*) gUnknowns[i].name)) {
961                     // Tag matches. This should have been the first time we see it.
962                     REPORTER_ASSERT(fReporter, !fSeen[i]);
963                     fSeen[i] = true;
964 
965                     // Data and length should match
966                     REPORTER_ASSERT(fReporter, length == gUnknowns[i].size);
967                     REPORTER_ASSERT(fReporter, !strcmp((const char*) data,
968                                                        (const char*) gUnknowns[i].data));
969                     return true;
970                 }
971             }
972             ERRORF(fReporter, "Saw an unexpected unknown chunk.");
973             return true;
974         }
975 
976         bool allHaveBeenSeen() {
977             bool ret = true;
978             for (auto seen : fSeen) {
979                 ret &= seen;
980             }
981             return ret;
982         }
983 
984         void reset() {
985             sk_bzero(fSeen, sizeof(fSeen));
986         }
987 
988     private:
989         skiatest::Reporter* fReporter;  // Unowned
990         bool fSeen[3];
991     };
992 
993     ChunkReader chunkReader(r);
994 
995     // Now read the file with SkCodec.
996     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(wStream.detachAsData(), &chunkReader));
997     REPORTER_ASSERT(r, codec);
998     if (!codec) {
999         return;
1000     }
1001 
1002     // Now compare to the original.
1003     SkBitmap decodedBm;
1004     decodedBm.setInfo(codec->getInfo());
1005     decodedBm.allocPixels();
1006     SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(),
1007                                               decodedBm.rowBytes());
1008     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1009 
1010     if (decodedBm.colorType() != bm.colorType()) {
1011         SkBitmap tmp;
1012         bool     success = ToolUtils::copy_to(&tmp, bm.colorType(), decodedBm);
1013         REPORTER_ASSERT(r, success);
1014         if (!success) {
1015             return;
1016         }
1017 
1018         tmp.swap(decodedBm);
1019     }
1020 
1021     compare_to_good_digest(r, goodDigest, decodedBm);
1022     REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen());
1023 
1024     // Decoding again will read the chunks again.
1025     chunkReader.reset();
1026     REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen());
1027     result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes());
1028     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1029     REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen());
1030 }
1031 #endif // SK_PNG_DISABLE_TESTS
1032 #endif // PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
1033 
1034 // Stream that can only peek up to a limit
1035 class LimitedPeekingMemStream : public SkStream {
1036 public:
LimitedPeekingMemStream(sk_sp<SkData> data,size_t limit)1037     LimitedPeekingMemStream(sk_sp<SkData> data, size_t limit)
1038         : fStream(std::move(data))
1039         , fLimit(limit) {}
1040 
peek(void * buf,size_t bytes) const1041     size_t peek(void* buf, size_t bytes) const override {
1042         return fStream.peek(buf, std::min(bytes, fLimit));
1043     }
read(void * buf,size_t bytes)1044     size_t read(void* buf, size_t bytes) override {
1045         return fStream.read(buf, bytes);
1046     }
rewind()1047     bool rewind() override {
1048         return fStream.rewind();
1049     }
isAtEnd() const1050     bool isAtEnd() const override {
1051         return fStream.isAtEnd();
1052     }
1053 private:
1054     SkMemoryStream fStream;
1055     const size_t   fLimit;
1056 };
1057 
1058 // Disable RAW tests for Win32.
1059 #if defined(SK_CODEC_DECODES_RAW) && !defined(_WIN32)
1060 // Test that the RawCodec works also for not asset stream. This will test the code path using
1061 // SkRawBufferedStream instead of SkRawAssetStream.
DEF_TEST(Codec_raw_notseekable,r)1062 DEF_TEST(Codec_raw_notseekable, r) {
1063     constexpr char path[] = "images/dng_with_preview.dng";
1064     sk_sp<SkData> data(GetResourceAsData(path));
1065     if (!data) {
1066         SkDebugf("Missing resource '%s'\n", path);
1067         return;
1068     }
1069 
1070     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
1071                                            std::make_unique<NotAssetMemStream>(std::move(data))));
1072     REPORTER_ASSERT(r, codec);
1073 
1074     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
1075 }
1076 #endif
1077 
1078 // Test that even if webp_parse_header fails to peek enough, it will fall back to read()
1079 // + rewind() and succeed.
DEF_TEST(Codec_webp_peek,r)1080 DEF_TEST(Codec_webp_peek, r) {
1081     constexpr char path[] = "images/baby_tux.webp";
1082     auto data = GetResourceAsData(path);
1083     if (!data) {
1084         SkDebugf("Missing resource '%s'\n", path);
1085         return;
1086     }
1087 
1088     // The limit is less than webp needs to peek or read.
1089     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
1090                                            std::make_unique<LimitedPeekingMemStream>(data, 25)));
1091     REPORTER_ASSERT(r, codec);
1092 
1093     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
1094 
1095     // Similarly, a stream which does not peek should still succeed.
1096     codec = SkCodec::MakeFromStream(std::make_unique<LimitedPeekingMemStream>(data, 0));
1097     REPORTER_ASSERT(r, codec);
1098 
1099     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
1100 }
1101 
1102 // SkCodec's wbmp decoder was initially unnecessarily restrictive.
1103 // It required the second byte to be zero. The wbmp specification allows
1104 // a couple of bits to be 1 (so long as they do not overlap with 0x9F).
1105 // Test that SkCodec now supports an image with these bits set.
DEF_TEST(Codec_wbmp_restrictive,r)1106 DEF_TEST(Codec_wbmp_restrictive, r) {
1107     const char* path = "images/mandrill.wbmp";
1108     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
1109     if (!stream) {
1110         return;
1111     }
1112 
1113     // Modify the stream to contain a second byte with some bits set.
1114     auto data = SkCopyStreamToData(stream.get());
1115     uint8_t* writeableData = static_cast<uint8_t*>(data->writable_data());
1116     writeableData[1] = static_cast<uint8_t>(~0x9F);
1117 
1118     // SkCodec should support this.
1119     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
1120     REPORTER_ASSERT(r, codec);
1121     if (!codec) {
1122         return;
1123     }
1124     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr);
1125 }
1126 
1127 // wbmp images have a header that can be arbitrarily large, depending on the
1128 // size of the image. We cap the size at 65535, meaning we only need to look at
1129 // 8 bytes to determine whether we can read the image. This is important
1130 // because SkCodec only passes a limited number of bytes to SkWbmpCodec to
1131 // determine whether the image is a wbmp.
DEF_TEST(Codec_wbmp_max_size,r)1132 DEF_TEST(Codec_wbmp_max_size, r) {
1133     const unsigned char maxSizeWbmp[] = { 0x00, 0x00,           // Header
1134                                           0x83, 0xFF, 0x7F,     // W: 65535
1135                                           0x83, 0xFF, 0x7F };   // H: 65535
1136     std::unique_ptr<SkStream> stream(new SkMemoryStream(maxSizeWbmp, sizeof(maxSizeWbmp), false));
1137     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1138 
1139     REPORTER_ASSERT(r, codec);
1140     if (!codec) return;
1141 
1142     REPORTER_ASSERT(r, codec->getInfo().width() == 65535);
1143     REPORTER_ASSERT(r, codec->getInfo().height() == 65535);
1144 
1145     // Now test an image which is too big. Any image with a larger header (i.e.
1146     // has bigger width/height) is also too big.
1147     const unsigned char tooBigWbmp[] = { 0x00, 0x00,           // Header
1148                                          0x84, 0x80, 0x00,     // W: 65536
1149                                          0x84, 0x80, 0x00 };   // H: 65536
1150     stream = std::make_unique<SkMemoryStream>(tooBigWbmp, sizeof(tooBigWbmp), false);
1151     codec = SkCodec::MakeFromStream(std::move(stream));
1152 
1153     REPORTER_ASSERT(r, !codec);
1154 }
1155 
DEF_TEST(Codec_jpeg_rewind,r)1156 DEF_TEST(Codec_jpeg_rewind, r) {
1157     const char* path = "images/mandrill_512_q075.jpg";
1158     sk_sp<SkData> data(GetResourceAsData(path));
1159     if (!data) {
1160         return;
1161     }
1162 
1163     data = SkData::MakeSubset(data.get(), 0, data->size() / 2);
1164     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(data));
1165     if (!codec) {
1166         ERRORF(r, "Unable to create codec '%s'.", path);
1167         return;
1168     }
1169 
1170     const int width = codec->getInfo().width();
1171     const int height = codec->getInfo().height();
1172     size_t rowBytes = sizeof(SkPMColor) * width;
1173     SkAutoMalloc pixelStorage(height * rowBytes);
1174 
1175     // Perform a sampled decode.
1176     SkAndroidCodec::AndroidOptions opts;
1177     opts.fSampleSize = 12;
1178     auto sampledInfo = codec->getInfo().makeWH(width / 12, height / 12);
1179     auto result = codec->getAndroidPixels(sampledInfo, pixelStorage.get(), rowBytes, &opts);
1180     REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
1181 
1182     // Rewind the codec and perform a full image decode.
1183     result = codec->getPixels(codec->getInfo(), pixelStorage.get(), rowBytes);
1184     REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
1185 
1186     // Now perform a subset decode.
1187     {
1188         opts.fSampleSize = 1;
1189         SkIRect subset = SkIRect::MakeWH(100, 100);
1190         opts.fSubset = &subset;
1191         result = codec->getAndroidPixels(codec->getInfo().makeWH(100, 100), pixelStorage.get(),
1192                                          rowBytes, &opts);
1193         // Though we only have half the data, it is enough to decode this subset.
1194         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1195     }
1196 
1197     // Perform another full image decode.  ASAN will detect if we look at the subset when it is
1198     // out of scope.  This would happen if we depend on the old state in the codec.
1199     // This tests two layers of bugs: both SkJpegCodec::readRows and SkCodec::fillIncompleteImage
1200     // used to look at the old subset.
1201     opts.fSubset = nullptr;
1202     result = codec->getAndroidPixels(codec->getInfo(), pixelStorage.get(), rowBytes, &opts);
1203     REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
1204 }
1205 
check_color_xform(skiatest::Reporter * r,const char * path)1206 static void check_color_xform(skiatest::Reporter* r, const char* path) {
1207     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(GetResourceAsStream(path)));
1208 
1209     SkAndroidCodec::AndroidOptions opts;
1210     opts.fSampleSize = 3;
1211     const int subsetWidth = codec->getInfo().width() / 2;
1212     const int subsetHeight = codec->getInfo().height() / 2;
1213     SkIRect subset = SkIRect::MakeWH(subsetWidth, subsetHeight);
1214     opts.fSubset = &subset;
1215 
1216     const int dstWidth = subsetWidth / opts.fSampleSize;
1217     const int dstHeight = subsetHeight / opts.fSampleSize;
1218     auto colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB);
1219     SkImageInfo dstInfo = codec->getInfo().makeWH(dstWidth, dstHeight)
1220                                           .makeColorType(kN32_SkColorType)
1221                                           .makeColorSpace(colorSpace);
1222 
1223     size_t rowBytes = dstInfo.minRowBytes();
1224     SkAutoMalloc pixelStorage(dstInfo.computeByteSize(rowBytes));
1225     SkCodec::Result result = codec->getAndroidPixels(dstInfo, pixelStorage.get(), rowBytes, &opts);
1226     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1227 }
1228 
DEF_TEST(Codec_ColorXform,r)1229 DEF_TEST(Codec_ColorXform, r) {
1230     check_color_xform(r, "images/mandrill_512_q075.jpg");
1231     check_color_xform(r, "images/mandrill_512.png");
1232 }
1233 
color_type_match(SkColorType origColorType,SkColorType codecColorType)1234 static bool color_type_match(SkColorType origColorType, SkColorType codecColorType) {
1235     switch (origColorType) {
1236         case kRGBA_8888_SkColorType:
1237         case kBGRA_8888_SkColorType:
1238             return kRGBA_8888_SkColorType == codecColorType ||
1239                    kBGRA_8888_SkColorType == codecColorType;
1240         default:
1241             return origColorType == codecColorType;
1242     }
1243 }
1244 
alpha_type_match(SkAlphaType origAlphaType,SkAlphaType codecAlphaType)1245 static bool alpha_type_match(SkAlphaType origAlphaType, SkAlphaType codecAlphaType) {
1246     switch (origAlphaType) {
1247         case kUnpremul_SkAlphaType:
1248         case kPremul_SkAlphaType:
1249             return kUnpremul_SkAlphaType == codecAlphaType ||
1250                     kPremul_SkAlphaType == codecAlphaType;
1251         default:
1252             return origAlphaType == codecAlphaType;
1253     }
1254 }
1255 
check_round_trip(skiatest::Reporter * r,SkCodec * origCodec,const SkImageInfo & info)1256 static void check_round_trip(skiatest::Reporter* r, SkCodec* origCodec, const SkImageInfo& info) {
1257     SkBitmap bm1;
1258     bm1.allocPixels(info);
1259     SkCodec::Result result = origCodec->getPixels(info, bm1.getPixels(), bm1.rowBytes());
1260     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1261 
1262     // Encode the image to png.
1263     SkDynamicMemoryWStream stream;
1264     SkASSERT_RELEASE(SkPngEncoder::Encode(&stream, bm1.pixmap(), {}));
1265 
1266     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(stream.detachAsData()));
1267     REPORTER_ASSERT(r, color_type_match(info.colorType(), codec->getInfo().colorType()));
1268     REPORTER_ASSERT(r, alpha_type_match(info.alphaType(), codec->getInfo().alphaType()));
1269 
1270     SkBitmap bm2;
1271     bm2.allocPixels(info);
1272     result = codec->getPixels(info, bm2.getPixels(), bm2.rowBytes());
1273     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1274 
1275     REPORTER_ASSERT(r, md5(bm1) == md5(bm2));
1276 }
1277 
DEF_TEST(Codec_PngRoundTrip,r)1278 DEF_TEST(Codec_PngRoundTrip, r) {
1279     auto codec = SkCodec::MakeFromStream(GetResourceAsStream("images/mandrill_512_q075.jpg"));
1280 
1281     SkColorType colorTypesOpaque[] = {
1282             kRGB_565_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType
1283     };
1284     for (SkColorType colorType : colorTypesOpaque) {
1285         SkImageInfo newInfo = codec->getInfo().makeColorType(colorType);
1286         check_round_trip(r, codec.get(), newInfo);
1287     }
1288 
1289     codec = SkCodec::MakeFromStream(GetResourceAsStream("images/grayscale.jpg"));
1290     check_round_trip(r, codec.get(), codec->getInfo());
1291 
1292     codec = SkCodec::MakeFromStream(GetResourceAsStream("images/yellow_rose.png"));
1293 
1294     SkColorType colorTypesWithAlpha[] = {
1295             kRGBA_8888_SkColorType, kBGRA_8888_SkColorType
1296     };
1297     SkAlphaType alphaTypes[] = {
1298             kUnpremul_SkAlphaType, kPremul_SkAlphaType
1299     };
1300     for (SkColorType colorType : colorTypesWithAlpha) {
1301         for (SkAlphaType alphaType : alphaTypes) {
1302             // Set color space to nullptr because color correct premultiplies do not round trip.
1303             SkImageInfo newInfo = codec->getInfo().makeColorType(colorType)
1304                                                   .makeAlphaType(alphaType)
1305                                                   .makeColorSpace(nullptr);
1306             check_round_trip(r, codec.get(), newInfo);
1307         }
1308     }
1309 
1310     codec = SkCodec::MakeFromStream(GetResourceAsStream("images/index8.png"));
1311 
1312     for (SkAlphaType alphaType : alphaTypes) {
1313         SkImageInfo newInfo = codec->getInfo().makeAlphaType(alphaType)
1314                                               .makeColorSpace(nullptr);
1315         check_round_trip(r, codec.get(), newInfo);
1316     }
1317 }
1318 
test_conversion_possible(skiatest::Reporter * r,const char * path,bool supportsScanlineDecoder,bool supportsIncrementalDecoder)1319 static void test_conversion_possible(skiatest::Reporter* r, const char* path,
1320                                      bool supportsScanlineDecoder,
1321                                      bool supportsIncrementalDecoder) {
1322     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
1323     if (!stream) {
1324         return;
1325     }
1326 
1327     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1328     if (!codec) {
1329         ERRORF(r, "failed to create a codec for %s", path);
1330         return;
1331     }
1332 
1333     SkImageInfo infoF16 = codec->getInfo().makeColorType(kRGBA_F16_SkColorType);
1334 
1335     SkBitmap bm;
1336     bm.allocPixels(infoF16);
1337     SkCodec::Result result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
1338     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1339 
1340     result = codec->startScanlineDecode(infoF16);
1341     if (supportsScanlineDecoder) {
1342         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1343     } else {
1344         REPORTER_ASSERT(r, SkCodec::kUnimplemented == result
1345                         || SkCodec::kSuccess == result);
1346     }
1347 
1348     result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes());
1349     if (supportsIncrementalDecoder) {
1350         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1351     } else {
1352         REPORTER_ASSERT(r, SkCodec::kUnimplemented == result
1353                         || SkCodec::kSuccess == result);
1354     }
1355 
1356     infoF16 = infoF16.makeColorSpace(infoF16.colorSpace()->makeLinearGamma());
1357     result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes());
1358     REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1359     result = codec->startScanlineDecode(infoF16);
1360     if (supportsScanlineDecoder) {
1361         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1362     } else {
1363         REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
1364     }
1365 
1366     result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes());
1367     if (supportsIncrementalDecoder) {
1368         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
1369     } else {
1370         REPORTER_ASSERT(r, SkCodec::kUnimplemented == result);
1371     }
1372 }
1373 
DEF_TEST(Codec_F16ConversionPossible,r)1374 DEF_TEST(Codec_F16ConversionPossible, r) {
1375     test_conversion_possible(r, "images/color_wheel.webp", false, false);
1376     test_conversion_possible(r, "images/mandrill_512_q075.jpg", true, false);
1377     test_conversion_possible(r, "images/yellow_rose.png", false, true);
1378 }
1379 
decode_frame(skiatest::Reporter * r,SkCodec * codec,size_t frame)1380 static void decode_frame(skiatest::Reporter* r, SkCodec* codec, size_t frame) {
1381     SkBitmap bm;
1382     auto info = codec->getInfo().makeColorType(kN32_SkColorType);
1383     bm.allocPixels(info);
1384 
1385     SkCodec::Options opts;
1386     opts.fFrameIndex = frame;
1387     REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getPixels(info,
1388             bm.getPixels(), bm.rowBytes(), &opts));
1389 }
1390 
1391 // For an animated GIF, we should only read enough to decode frame 0 if the
1392 // client never calls getFrameInfo and only decodes frame 0.
DEF_TEST(Codec_skipFullParse,r)1393 DEF_TEST(Codec_skipFullParse, r) {
1394     auto path = "images/test640x479.gif";
1395     auto streamObj = GetResourceAsStream(path);
1396     if (!streamObj) {
1397         return;
1398     }
1399     SkStream* stream = streamObj.get();
1400 
1401     // Note that we cheat and hold on to the stream pointer, but SkCodec will
1402     // take ownership. We will not refer to the stream after the SkCodec
1403     // deletes it.
1404     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(streamObj)));
1405     if (!codec) {
1406         ERRORF(r, "Failed to create codec for %s", path);
1407         return;
1408     }
1409 
1410     REPORTER_ASSERT(r, stream->hasPosition());
1411     const size_t sizePosition = stream->getPosition();
1412     REPORTER_ASSERT(r, stream->hasLength() && sizePosition < stream->getLength());
1413 
1414     // This should read more of the stream, but not the whole stream.
1415     decode_frame(r, codec.get(), 0);
1416     const size_t positionAfterFirstFrame = stream->getPosition();
1417     REPORTER_ASSERT(r, positionAfterFirstFrame > sizePosition
1418                        && positionAfterFirstFrame < stream->getLength());
1419 
1420     // There is more data in the stream.
1421     auto frameInfo = codec->getFrameInfo();
1422     REPORTER_ASSERT(r, frameInfo.size() == 4);
1423     REPORTER_ASSERT(r, stream->getPosition() > positionAfterFirstFrame);
1424 }
1425 
1426 // Only rewinds up to a limit.
1427 class LimitedRewindingStream : public SkStream {
1428 public:
Make(const char path[],size_t limit)1429     static std::unique_ptr<SkStream> Make(const char path[], size_t limit) {
1430         auto stream = GetResourceAsStream(path);
1431         if (!stream) {
1432             return nullptr;
1433         }
1434         return std::unique_ptr<SkStream>(new LimitedRewindingStream(std::move(stream), limit));
1435     }
1436 
read(void * buffer,size_t size)1437     size_t read(void* buffer, size_t size) override {
1438         const size_t bytes = fStream->read(buffer, size);
1439         fPosition += bytes;
1440         return bytes;
1441     }
1442 
isAtEnd() const1443     bool isAtEnd() const override {
1444         return fStream->isAtEnd();
1445     }
1446 
rewind()1447     bool rewind() override {
1448         if (fPosition <= fLimit && fStream->rewind()) {
1449             fPosition = 0;
1450             return true;
1451         }
1452 
1453         return false;
1454     }
1455 
1456 private:
1457     std::unique_ptr<SkStream> fStream;
1458     const size_t              fLimit;
1459     size_t                    fPosition;
1460 
LimitedRewindingStream(std::unique_ptr<SkStream> stream,size_t limit)1461     LimitedRewindingStream(std::unique_ptr<SkStream> stream, size_t limit)
1462         : fStream(std::move(stream))
1463         , fLimit(limit)
1464         , fPosition(0)
1465     {
1466         SkASSERT(fStream);
1467     }
1468 };
1469 
DEF_TEST(Codec_fallBack,r)1470 DEF_TEST(Codec_fallBack, r) {
1471     // SkAndroidCodec needs to be able to fall back to scanline decoding
1472     // if incremental decoding does not work. Make sure this does not
1473     // require a rewind.
1474 
1475     // Formats that currently do not support incremental decoding
1476     auto files = {
1477         "images/CMYK.jpg",
1478         "images/mandrill.wbmp",
1479         "images/randPixels.bmp",
1480 #if defined(SK_CODEC_DECODES_ICO)
1481         "images/color_wheel.ico",
1482 #endif
1483     };
1484     for (auto file : files) {
1485         auto stream = LimitedRewindingStream::Make(file, SkCodec::MinBufferedBytesNeeded());
1486         if (!stream) {
1487             SkDebugf("Missing resources (%s). Set --resourcePath.\n", file);
1488             return;
1489         }
1490 
1491         std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1492         if (!codec) {
1493             ERRORF(r, "Failed to create codec for %s,", file);
1494             continue;
1495         }
1496 
1497         SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
1498         SkBitmap bm;
1499         bm.allocPixels(info);
1500 
1501         if (SkCodec::kUnimplemented != codec->startIncrementalDecode(info, bm.getPixels(),
1502                 bm.rowBytes())) {
1503             ERRORF(r, "Is scanline decoding now implemented for %s?", file);
1504             continue;
1505         }
1506 
1507         // Scanline decoding should not require a rewind.
1508         SkCodec::Result result = codec->startScanlineDecode(info);
1509         if (SkCodec::kSuccess != result) {
1510             ERRORF(r, "Scanline decoding failed for %s with %i", file, result);
1511         }
1512     }
1513 }
1514 
seek_and_decode(const char * file,std::unique_ptr<SkStream> stream,skiatest::Reporter * r)1515 static void seek_and_decode(const char* file, std::unique_ptr<SkStream> stream,
1516                             skiatest::Reporter* r) {
1517     if (!stream) {
1518         SkDebugf("Missing resources (%s). Set --resourcePath.\n", file);
1519         return;
1520     }
1521 
1522     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1523     if (!codec) {
1524         ERRORF(r, "Failed to create codec for %s,", file);
1525         return;
1526     }
1527 
1528     // Trigger reading through the stream, so that decoding the first frame will
1529     // require a rewind.
1530     (void) codec->getFrameCount();
1531 
1532     SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
1533     SkBitmap bm;
1534     bm.allocPixels(info);
1535     auto result = codec->getPixels(bm.pixmap());
1536     if (result != SkCodec::kSuccess) {
1537         ERRORF(r, "Failed to decode %s with error %s", file, SkCodec::ResultToString(result));
1538     }
1539 }
1540 
DEF_TEST(Wuffs_seek_and_decode,r)1541 DEF_TEST(Wuffs_seek_and_decode, r) {
1542     const char* file = "images/flightAnim.gif";
1543     auto stream = LimitedRewindingStream::Make(file, SkCodec::MinBufferedBytesNeeded());
1544     seek_and_decode(file, std::move(stream), r);
1545 
1546 #if defined(SK_ENABLE_ANDROID_UTILS)
1547     // Test using FrontBufferedStream, as Android does
1548     auto bufferedStream = android::skia::FrontBufferedStream::Make(
1549             GetResourceAsStream(file), SkCodec::MinBufferedBytesNeeded());
1550     seek_and_decode(file, std::move(bufferedStream), r);
1551 #endif
1552 }
1553 
1554 // This test verifies that we fixed an assert statement that fired when reusing a png codec
1555 // after scaling.
DEF_TEST(Codec_reusePng,r)1556 DEF_TEST(Codec_reusePng, r) {
1557     std::unique_ptr<SkStream> stream(GetResourceAsStream("images/plane.png"));
1558     if (!stream) {
1559         return;
1560     }
1561 
1562     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream)));
1563     if (!codec) {
1564         ERRORF(r, "Failed to create codec\n");
1565         return;
1566     }
1567 
1568     SkAndroidCodec::AndroidOptions opts;
1569     opts.fSampleSize = 5;
1570     auto size = codec->getSampledDimensions(opts.fSampleSize);
1571     auto info = codec->getInfo().makeDimensions(size).makeColorType(kN32_SkColorType);
1572     SkBitmap bm;
1573     bm.allocPixels(info);
1574     auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts);
1575     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1576 
1577     info = codec->getInfo().makeColorType(kN32_SkColorType);
1578     bm.allocPixels(info);
1579     opts.fSampleSize = 1;
1580     result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts);
1581     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1582 }
1583 
DEF_TEST(Codec_rowsDecoded,r)1584 DEF_TEST(Codec_rowsDecoded, r) {
1585     auto file = "images/plane_interlaced.png";
1586     std::unique_ptr<SkStream> stream(GetResourceAsStream(file));
1587     if (!stream) {
1588         return;
1589     }
1590 
1591     // This is enough to read the header etc, but no rows.
1592     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(SkData::MakeFromStream(stream.get(), 99)));
1593     if (!codec) {
1594         ERRORF(r, "Failed to create codec\n");
1595         return;
1596     }
1597 
1598     auto info = codec->getInfo().makeColorType(kN32_SkColorType);
1599     SkBitmap bm;
1600     bm.allocPixels(info);
1601     auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
1602     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
1603 
1604     // This is an arbitrary value. The important fact is that it is not zero, and rowsDecoded
1605     // should get set to zero by incrementalDecode.
1606     int rowsDecoded = 77;
1607     result = codec->incrementalDecode(&rowsDecoded);
1608     REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
1609     REPORTER_ASSERT(r, rowsDecoded == 0);
1610 }
1611 
test_invalid_images(skiatest::Reporter * r,const char * path,SkCodec::Result expectedResult)1612 static void test_invalid_images(skiatest::Reporter* r, const char* path,
1613                                 SkCodec::Result expectedResult) {
1614     auto stream = GetResourceAsStream(path);
1615     if (!stream) {
1616         return;
1617     }
1618 
1619     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1620     REPORTER_ASSERT(r, codec);
1621 
1622     test_info(r, codec.get(), codec->getInfo().makeColorType(kN32_SkColorType), expectedResult,
1623               nullptr);
1624 }
1625 
DEF_TEST(Codec_InvalidImages,r)1626 DEF_TEST(Codec_InvalidImages, r) {
1627     test_invalid_images(r, "invalid_images/b33251605.bmp", SkCodec::kIncompleteInput);
1628     test_invalid_images(r, "invalid_images/bad_palette.png", SkCodec::kInvalidInput);
1629     test_invalid_images(r, "invalid_images/many-progressive-scans.jpg", SkCodec::kInvalidInput);
1630 
1631     // An earlier revision of this test case passed kErrorInInput (instead of
1632     // kSuccess) as the third argument (expectedResult). However, after
1633     // https://skia-review.googlesource.com/c/skia/+/414417 `SkWuffsCodec:
1634     // ignore too much pixel data` combined with
1635     // https://github.com/google/wuffs/commit/e44920d3 `Let gif "ignore too
1636     // much" quirk skip lzw errors`, the codec silently accepts skbug5887.gif
1637     // (without the ASAN buffer-overflow violation that lead to that test case
1638     // in the first place), even though it's technically an invalid GIF.
1639     //
1640     // Note that, in practice, real world GIF decoders already diverge (in
1641     // different ways) from the GIF specification. For compatibility, (ad hoc)
1642     // implementation often trumps specification.
1643     // https://github.com/google/wuffs/blob/e44920d3/test/data/artificial-gif/frame-out-of-bounds.gif.make-artificial.txt#L30-L31
1644     test_invalid_images(r, "invalid_images/skbug5887.gif", SkCodec::kSuccess);
1645 }
1646 
test_invalid_header(skiatest::Reporter * r,const char * path)1647 static void test_invalid_header(skiatest::Reporter* r, const char* path) {
1648     auto data = GetResourceAsData(path);
1649     if (!data) {
1650         return;
1651     }
1652     std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(std::move(data)));
1653     if (!stream) {
1654         return;
1655     }
1656     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1657     REPORTER_ASSERT(r, !codec);
1658 }
1659 
DEF_TEST(Codec_InvalidHeader,r)1660 DEF_TEST(Codec_InvalidHeader, r) {
1661 #if defined(SK_CODEC_DECODES_ICO)
1662     test_invalid_header(r, "invalid_images/int_overflow.ico");
1663 #endif
1664 
1665     // These files report values that have caused problems with SkFILEStreams.
1666     // They are invalid, and should not create SkCodecs.
1667     test_invalid_header(r, "invalid_images/b33651913.bmp");
1668     test_invalid_header(r, "invalid_images/b34778578.bmp");
1669 }
1670 
1671 /*
1672 For the Codec_InvalidAnimated test, immediately below,
1673 resources/invalid_images/skbug6046.gif is:
1674 
1675 00000000: 4749 4638 3961 2000 0000 0000 002c ff00  GIF89a ......,..
1676 00000010: 7400 0600 0000 4001 0021 f904 0a00 0000  t.....@..!......
1677 00000020: 002c ff00 0000 ff00 7400 0606 0606 0601  .,......t.......
1678 00000030: 0021 f904 0000 0000 002c ff00 0000 ffcc  .!.......,......
1679 00000040: 1b36 5266 deba 543d                      .6Rf..T=
1680 
1681 It nominally contains 3 frames, but only the first one is valid. It came from a
1682 fuzzer doing random mutations and copies. The breakdown:
1683 
1684 @000  6 bytes magic "GIF89a"
1685 @006  7 bytes Logical Screen Descriptor: 0x20 0x00 ... 0x00
1686    - width     =    32
1687    - height    =     0
1688    - flags     =  0x00
1689    - background color index, pixel aspect ratio bytes ignored
1690 @00D 10 bytes Image Descriptor header: 0x2C 0xFF ... 0x40
1691    - origin_x  =   255
1692    - origin_y  =   116
1693    - width     =     6
1694    - height    =     0
1695    - flags     =  0x40, interlaced
1696 @017  2 bytes Image Descriptor body (pixel data): 0x01 0x00
1697    - lit_width =     1
1698    - 0x00 byte means "end of data" for this frame
1699 @019  8 bytes Graphic Control Extension: 0x21 0xF9 ... 0x00
1700    - valid, but irrelevant here.
1701 @021 10 bytes Image Descriptor header: 0x2C 0xFF ... 0x06
1702    - origin_x  =   255
1703    - origin_y  =     0
1704    - width     =   255
1705    - height    =   116
1706    - flags     =  0x06, INVALID, 0x80 BIT ZERO IMPLIES 0x07 BITS SHOULD BE ZERO
1707 @02B 14 bytes Image Descriptor body (pixel data): 0x06 0x06 ... 0x00
1708    - lit_width =     6
1709    - 0x06 precedes a 6 byte block of data
1710    - 0x04 precedes a 4 byte block of data
1711    - 0x00 byte means "end of data" for this frame
1712 @039 10 bytes Image Descriptor header: 0x2C 0xFF ... 0x06
1713    - origin_x  =   255
1714    - origin_y  =     0
1715    - width     = 52479
1716    - height    = 13851
1717    - flags     =  0x52, INVALID, 0x80 BIT ZERO IMPLIES 0x07 BITS SHOULD BE ZERO
1718 @043  5 bytes Image Descriptor body (pixel data): 0x66 0xDE ... unexpected-EOF
1719    - lit_width =   102, INVALID, GREATER THAN 8
1720    - 0xDE precedes a 222 byte block of data, INVALIDLY TRUNCATED
1721 
1722 On Image Descriptor flags INVALIDITY,
1723 https://www.w3.org/Graphics/GIF/spec-gif89a.txt section 20.c says that "Size of
1724 Local Color Table [the low 3 bits]... should be 0 if there is no Local Color
1725 Table specified [the high bit]."
1726 
1727 On LZW literal width (also known as Minimum Code Size) INVALIDITY,
1728 https://www.w3.org/Graphics/GIF/spec-gif89a.txt Appendix F says that "Normally
1729 this will be the same as the number of [palette index] bits. Because of some
1730 algorithmic constraints however, black & white images which have one color bit
1731 must be indicated as having a code size of 2." In practice, some GIF decoders,
1732 including both the old third_party/gif code and the Wuffs GIF decoder, don't
1733 enforce this "at least 2" constraint. Nonetheless, any width greater than 8 is
1734 invalid, as there are only 8 bits in a byte.
1735 */
1736 
DEF_TEST(Codec_InvalidAnimated,r)1737 DEF_TEST(Codec_InvalidAnimated, r) {
1738     // ASAN will complain if there is an issue.
1739     auto path = "invalid_images/skbug6046.gif";
1740     auto stream = GetResourceAsStream(path);
1741     if (!stream) {
1742         return;
1743     }
1744 
1745     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
1746     REPORTER_ASSERT(r, codec);
1747     if (!codec) {
1748         return;
1749     }
1750 
1751     const auto info = codec->getInfo().makeColorType(kN32_SkColorType);
1752     SkBitmap bm;
1753     bm.allocPixels(info);
1754 
1755     auto frameInfos = codec->getFrameInfo();
1756     SkCodec::Options opts;
1757     for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) {
1758         opts.fFrameIndex = i;
1759         const auto reqFrame = frameInfos[i].fRequiredFrame;
1760         opts.fPriorFrame = reqFrame == i - 1 ? reqFrame : SkCodec::kNoFrame;
1761         auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes(), &opts);
1762 
1763         if (result != SkCodec::kSuccess) {
1764             ERRORF(r, "Failed to start decoding frame %i (out of %zu) with error %i\n", i,
1765                    frameInfos.size(), result);
1766             continue;
1767         }
1768 
1769         codec->incrementalDecode();
1770     }
1771 }
1772 
encode_format(SkDynamicMemoryWStream * stream,const SkPixmap & pixmap,SkEncodedImageFormat format)1773 static void encode_format(SkDynamicMemoryWStream* stream, const SkPixmap& pixmap,
1774                           SkEncodedImageFormat format) {
1775     switch (format) {
1776         case SkEncodedImageFormat::kPNG:
1777             SkPngEncoder::Encode(stream, pixmap, SkPngEncoder::Options());
1778             break;
1779         case SkEncodedImageFormat::kJPEG:
1780             SkJpegEncoder::Encode(stream, pixmap, SkJpegEncoder::Options());
1781             break;
1782         case SkEncodedImageFormat::kWEBP:
1783             SkWebpEncoder::Encode(stream, pixmap, SkWebpEncoder::Options());
1784             break;
1785         default:
1786             SkASSERT(false);
1787             break;
1788     }
1789 }
1790 
test_encode_icc(skiatest::Reporter * r,SkEncodedImageFormat format)1791 static void test_encode_icc(skiatest::Reporter* r, SkEncodedImageFormat format) {
1792     // Test with sRGB color space.
1793     SkBitmap srgbBitmap;
1794     SkImageInfo srgbInfo = SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType);
1795     srgbBitmap.allocPixels(srgbInfo);
1796     *srgbBitmap.getAddr32(0, 0) = 0;
1797     SkPixmap pixmap;
1798     srgbBitmap.peekPixels(&pixmap);
1799     SkDynamicMemoryWStream srgbBuf;
1800     encode_format(&srgbBuf, pixmap, format);
1801     sk_sp<SkData> srgbData = srgbBuf.detachAsData();
1802     std::unique_ptr<SkCodec> srgbCodec(SkCodec::MakeFromData(srgbData));
1803     REPORTER_ASSERT(r, srgbCodec->getInfo().colorSpace() == sk_srgb_singleton());
1804 
1805     // Test with P3 color space.
1806     SkDynamicMemoryWStream p3Buf;
1807     sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
1808     pixmap.setColorSpace(p3);
1809     encode_format(&p3Buf, pixmap, format);
1810     sk_sp<SkData> p3Data = p3Buf.detachAsData();
1811     std::unique_ptr<SkCodec> p3Codec(SkCodec::MakeFromData(p3Data));
1812     REPORTER_ASSERT(r, p3Codec->getInfo().colorSpace()->gammaCloseToSRGB());
1813     skcms_Matrix3x3 mat0, mat1;
1814     bool success = p3->toXYZD50(&mat0);
1815     REPORTER_ASSERT(r, success);
1816     success = p3Codec->getInfo().colorSpace()->toXYZD50(&mat1);
1817     REPORTER_ASSERT(r, success);
1818 
1819     for (int i = 0; i < 3; i++) {
1820         for (int j = 0; j < 3; j++) {
1821             REPORTER_ASSERT(r, color_space_almost_equal(mat0.vals[i][j], mat1.vals[i][j]));
1822         }
1823     }
1824 }
1825 
DEF_TEST(Codec_EncodeICC,r)1826 DEF_TEST(Codec_EncodeICC, r) {
1827     test_encode_icc(r, SkEncodedImageFormat::kPNG);
1828     test_encode_icc(r, SkEncodedImageFormat::kJPEG);
1829     test_encode_icc(r, SkEncodedImageFormat::kWEBP);
1830 }
1831 
DEF_TEST(Codec_webp_rowsDecoded,r)1832 DEF_TEST(Codec_webp_rowsDecoded, r) {
1833     const char* path = "images/baby_tux.webp";
1834     sk_sp<SkData> data(GetResourceAsData(path));
1835     if (!data) {
1836         return;
1837     }
1838 
1839     // Truncate this file so that the header is available but no rows can be
1840     // decoded. This should create a codec but fail to decode.
1841     size_t truncatedSize = 5000;
1842     sk_sp<SkData> subset = SkData::MakeSubset(data.get(), 0, truncatedSize);
1843     std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(std::move(subset));
1844     if (!codec) {
1845         ERRORF(r, "Failed to create a codec for %s truncated to only %zu bytes",
1846                path, truncatedSize);
1847         return;
1848     }
1849 
1850     test_info(r, codec.get(), codec->getInfo(), SkCodec::kInvalidInput, nullptr);
1851 }
1852 
1853 /*
1854 For the Codec_ossfuzz6274 test, immediately below,
1855 resources/invalid_images/ossfuzz6274.gif is:
1856 
1857 00000000: 4749 4638 3961 2000 2000 f120 2020 2020  GIF89a . ..
1858 00000010: 2020 2020 2020 2020 2021 f903 ff20 2020           !...
1859 00000020: 002c 0000 0000 2000 2000 2000 00         .,.... . . ..
1860 
1861 @000  6 bytes magic "GIF89a"
1862 @006  7 bytes Logical Screen Descriptor: 0x20 0x00 ... 0x00
1863    - width     =    32
1864    - height    =    32
1865    - flags     =  0xF1, global color table, 4 RGB entries
1866    - background color index, pixel aspect ratio bytes ignored
1867 @00D 12 bytes Color Table: 0x20 0x20 ... 0x20
1868 @019 20 bytes Graphic Control Extension: 0x21 0xF9 ... unexpected-EOF
1869    - 0x03 precedes a 3 byte block of data, INVALID, MUST BE 4
1870    - 0x20 precedes a 32 byte block of data, INVALIDly truncated
1871 
1872 https://www.w3.org/Graphics/GIF/spec-gif89a.txt section 23.c says that the
1873 block size (for an 0x21 0xF9 Graphic Control Extension) must be "the fixed
1874 value 4".
1875 */
1876 
DEF_TEST(Codec_ossfuzz6274,r)1877 DEF_TEST(Codec_ossfuzz6274, r) {
1878     if (GetResourcePath().isEmpty()) {
1879         return;
1880     }
1881 
1882     const char* file = "invalid_images/ossfuzz6274.gif";
1883     auto image = ToolUtils::GetResourceAsImage(file);
1884 
1885     if (image) {
1886         ERRORF(r, "Invalid data gave non-nullptr image");
1887     }
1888 }
1889 
DEF_TEST(Codec_78329453,r)1890 DEF_TEST(Codec_78329453, r) {
1891     if (GetResourcePath().isEmpty()) {
1892         return;
1893     }
1894 
1895     const char* file = "images/b78329453.jpeg";
1896     auto data = GetResourceAsData(file);
1897     if (!data) {
1898         ERRORF(r, "Missing %s", file);
1899         return;
1900     }
1901 
1902     auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data));
1903     if (!codec) {
1904         ERRORF(r, "failed to create codec from %s", file);
1905         return;
1906     }
1907 
1908     // A bug in jpeg_skip_scanlines resulted in an infinite loop for this specific
1909     // sample size on this image. Other sample sizes could have had the same result,
1910     // but the ones tested by DM happen to not.
1911     constexpr int kSampleSize = 19;
1912     const auto size = codec->getSampledDimensions(kSampleSize);
1913     auto info = codec->getInfo().makeDimensions(size);
1914     SkBitmap bm;
1915     bm.allocPixels(info);
1916     bm.eraseColor(SK_ColorTRANSPARENT);
1917 
1918     SkAndroidCodec::AndroidOptions options;
1919     options.fSampleSize = kSampleSize;
1920     auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &options);
1921     if (result != SkCodec::kSuccess) {
1922         ERRORF(r, "failed to decode with error %s", SkCodec::ResultToString(result));
1923     }
1924 }
1925 
DEF_TEST(Codec_A8,r)1926 DEF_TEST(Codec_A8, r) {
1927     if (GetResourcePath().isEmpty()) {
1928         return;
1929     }
1930 
1931     const char* file = "images/mandrill_cmyk.jpg";
1932     auto data = GetResourceAsData(file);
1933     if (!data) {
1934         ERRORF(r, "missing %s", file);
1935         return;
1936     }
1937 
1938     auto codec = SkCodec::MakeFromData(std::move(data));
1939     auto info = codec->getInfo().makeColorType(kAlpha_8_SkColorType);
1940     SkBitmap bm;
1941     bm.allocPixels(info);
1942     REPORTER_ASSERT(r, codec->getPixels(bm.pixmap()) == SkCodec::kInvalidConversion);
1943 }
1944 
DEF_TEST(Codec_crbug807324,r)1945 DEF_TEST(Codec_crbug807324, r) {
1946     if (GetResourcePath().isEmpty()) {
1947         return;
1948     }
1949 
1950     const char* file = "images/crbug807324.png";
1951     auto image = ToolUtils::GetResourceAsImage(file);
1952     if (!image) {
1953         ERRORF(r, "Missing %s", file);
1954         return;
1955     }
1956 
1957     const int kWidth = image->width();
1958     const int kHeight = image->height();
1959 
1960     SkBitmap bm;
1961     if (!bm.tryAllocPixels(SkImageInfo::MakeN32Premul(kWidth, kHeight))) {
1962         ERRORF(r, "Could not allocate pixels (%i x %i)", kWidth, kHeight);
1963         return;
1964     }
1965 
1966     bm.eraseColor(SK_ColorTRANSPARENT);
1967 
1968     SkCanvas canvas(bm);
1969     canvas.drawImage(image, 0, 0);
1970 
1971     for (int i = 0; i < kWidth;  ++i)
1972     for (int j = 0; j < kHeight; ++j) {
1973         if (*bm.getAddr32(i, j) == SK_ColorTRANSPARENT) {
1974             ERRORF(r, "image should not be transparent! %i, %i is 0", i, j);
1975             return;
1976         }
1977     }
1978 }
1979 
DEF_TEST(Codec_F16_noColorSpace,r)1980 DEF_TEST(Codec_F16_noColorSpace, r) {
1981     const char* path = "images/color_wheel.png";
1982     auto data = GetResourceAsData(path);
1983     if (!data) {
1984         return;
1985     }
1986 
1987     auto codec = SkCodec::MakeFromData(std::move(data));
1988     SkImageInfo info = codec->getInfo().makeColorType(kRGBA_F16_SkColorType)
1989                                        .makeColorSpace(nullptr);
1990     test_info(r, codec.get(), info, SkCodec::kSuccess, nullptr);
1991 }
1992 
1993 // These test images have ICC profiles that do not map to an SkColorSpace.
1994 // Verify that decoding them with a null destination space does not perform
1995 // color space transformations.
DEF_TEST(Codec_noConversion,r)1996 DEF_TEST(Codec_noConversion, r) {
1997     const struct Rec {
1998         const char* name;
1999         SkColor color;
2000     } recs[] = {
2001       { "images/cmyk_yellow_224_224_32.jpg", 0xFFD8FC04 },
2002       { "images/wide_gamut_yellow_224_224_64.jpeg",0xFFE0E040 },
2003     };
2004 
2005     for (const auto& rec : recs) {
2006         auto data = GetResourceAsData(rec.name);
2007         if (!data) {
2008             continue;
2009         }
2010 
2011         auto codec = SkCodec::MakeFromData(std::move(data));
2012         if (!codec) {
2013             ERRORF(r, "Failed to create a codec from %s", rec.name);
2014             continue;
2015         }
2016 
2017         const auto* profile = codec->getICCProfile();
2018         if (!profile) {
2019             ERRORF(r, "Expected %s to have a profile", rec.name);
2020             continue;
2021         }
2022 
2023         auto cs = SkColorSpace::Make(*profile);
2024         REPORTER_ASSERT(r, !cs.get());
2025 
2026         SkImageInfo info = codec->getInfo().makeColorSpace(nullptr);
2027         SkBitmap bm;
2028         bm.allocPixels(info);
2029         if (codec->getPixels(info, bm.getPixels(), bm.rowBytes()) != SkCodec::kSuccess) {
2030             ERRORF(r, "Failed to decode %s", rec.name);
2031             continue;
2032         }
2033         REPORTER_ASSERT(r, bm.getColor(0, 0) == rec.color);
2034     }
2035 }
2036 
DEF_TEST(Codec_kBGR_101010x_XR_SkColorType_supported,r)2037 DEF_TEST(Codec_kBGR_101010x_XR_SkColorType_supported, r) {
2038     SkBitmap srcBm;
2039     SkImageInfo srcInfo = SkImageInfo()
2040             .makeWH(100, 100)
2041             .makeColorSpace(SkColorSpace::MakeSRGB())
2042             .makeColorType(kBGRA_8888_SkColorType)
2043             .makeAlphaType(kOpaque_SkAlphaType);
2044     SkImageInfo dstInfo = srcInfo.makeColorType(kBGR_101010x_XR_SkColorType);
2045     srcBm.allocPixels(srcInfo);
2046     SkDynamicMemoryWStream stream;
2047     SkASSERT_RELEASE(SkPngEncoder::Encode(&stream, srcBm.pixmap(), {}));
2048     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(stream.detachAsData()));
2049     SkBitmap dstBm;
2050     dstBm.allocPixels(dstInfo);
2051     bool success = codec->getPixels(dstInfo, dstBm.getPixels(), dstBm.rowBytes());
2052     REPORTER_ASSERT(r, SkCodec::kSuccess == success);
2053 }
2054 
DEF_TEST(Codec_gif_notseekable,r)2055 DEF_TEST(Codec_gif_notseekable, r) {
2056     constexpr char path[] = "images/flightAnim.gif";
2057     sk_sp<SkData> data(GetResourceAsData(path));
2058     if (!data) {
2059         SkDebugf("Missing resource '%s'\n", path);
2060         return;
2061     }
2062 
2063     // Verify that using a non-seekable stream works the same as a seekable one for
2064     // decoding the first frame.
2065     const SkMD5::Digest goodDigest = [data, &r]() {
2066         auto codec = SkCodec::MakeFromStream(std::make_unique<SkMemoryStream>(data),
2067                                              nullptr, nullptr,
2068                                              SkCodec::SelectionPolicy::kPreferAnimation);
2069         REPORTER_ASSERT(r, codec->getFrameCount() == 60);
2070         const auto info = codec->getInfo();
2071 
2072         SkBitmap bm;
2073         bm.allocPixels(info);
2074 
2075         SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
2076         REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2077         return md5(bm);
2078     }();
2079 
2080     auto codec = SkCodec::MakeFromStream(std::make_unique<NonseekableStream>(std::move(data)),
2081                                          nullptr, nullptr,
2082                                          SkCodec::SelectionPolicy::kPreferStillImage);
2083     REPORTER_ASSERT(r, codec->getFrameCount() == 1);
2084 
2085     test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, &goodDigest);
2086 }
2087 
DEF_TEST(Codec_gif_notseekable2,r)2088 DEF_TEST(Codec_gif_notseekable2, r) {
2089     constexpr char path[] = "images/flightAnim.gif";
2090     sk_sp<SkData> data(GetResourceAsData(path));
2091     if (!data) {
2092         SkDebugf("Missing resource '%s'\n", path);
2093         return;
2094     }
2095 
2096     // Verify that using a non-seekable stream works the same as a seekable one for
2097     // decoding a later frame.
2098     SkCodec::Options options;
2099     options.fFrameIndex = 5;
2100 
2101     const SkMD5::Digest goodDigest = [data, &r, &options]() {
2102         auto codec = SkCodec::MakeFromStream(std::make_unique<SkMemoryStream>(data),
2103                                              nullptr, nullptr,
2104                                              SkCodec::SelectionPolicy::kPreferAnimation);
2105         REPORTER_ASSERT(r, codec->getFrameCount() == 60);
2106         const auto info = codec->getInfo();
2107 
2108         SkBitmap bm;
2109         bm.allocPixels(info);
2110 
2111         SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), &options);
2112         REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2113         return md5(bm);
2114     }();
2115 
2116     // This should copy the non seekable stream.
2117     auto codec = SkCodec::MakeFromStream(std::make_unique<NonseekableStream>(std::move(data)),
2118                                          nullptr, nullptr,
2119                                          SkCodec::SelectionPolicy::kPreferAnimation);
2120     REPORTER_ASSERT(r, codec->getFrameCount() == 60);
2121 
2122     SkBitmap bm;
2123     bm.allocPixels(codec->getInfo());
2124 
2125     SkCodec::Result result = codec->getPixels(codec->getInfo(), bm.getPixels(), bm.rowBytes(),
2126                                               &options);
2127     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2128     compare_to_good_digest(r, goodDigest, bm);
2129 }
2130 
DEF_TEST(Codec_gif_null_param,r)2131 DEF_TEST(Codec_gif_null_param, r) {
2132     constexpr char path[] = "images/flightAnim.gif";
2133     sk_sp<SkData> data(GetResourceAsData(path));
2134     if (!data) {
2135         SkDebugf("Missing resource '%s'\n", path);
2136         return;
2137     }
2138 
2139     SkCodec::Result result;
2140     auto codec = SkGifDecoder::Decode(std::make_unique<SkMemoryStream>(std::move(data)),
2141                                       &result, nullptr);
2142     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2143     REPORTER_ASSERT(r, codec);
2144 
2145     auto [image, res] = codec->getImage();
2146     REPORTER_ASSERT(r, res == SkCodec::kSuccess);
2147     REPORTER_ASSERT(r, image);
2148     REPORTER_ASSERT(r, image->width() == 320, "width %d != 320", image->width());
2149     REPORTER_ASSERT(r, image->height() == 240, "height %d != 240", image->height());
2150 
2151     // Decoding the image this way loses the original data.
2152     REPORTER_ASSERT(r, !image->refEncodedData());
2153 }
2154 
DEF_TEST(Codec_gif_can_preserve_original_data,r)2155 DEF_TEST(Codec_gif_can_preserve_original_data, r) {
2156     constexpr char path[] = "images/flightAnim.gif";
2157     sk_sp<SkData> data(GetResourceAsData(path));
2158     if (!data) {
2159         SkDebugf("Missing resource '%s'\n", path);
2160         return;
2161     }
2162 
2163     SkCodec::Result result;
2164     auto codec = SkGifDecoder::Decode(data, &result, nullptr);
2165     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2166     REPORTER_ASSERT(r, codec);
2167 
2168     sk_sp<SkImage> image = SkCodecs::DeferredImage(std::move(codec), kPremul_SkAlphaType);
2169     REPORTER_ASSERT(r, image);
2170     REPORTER_ASSERT(r, image->width() == 320, "width %d != 320", image->width());
2171     REPORTER_ASSERT(r, image->height() == 240, "height %d != 240", image->height());
2172     REPORTER_ASSERT(r,
2173                     image->alphaType() == kPremul_SkAlphaType,
2174                     "AlphaType is wrong %d",
2175                     image->alphaType());
2176 
2177     // The whole point of DeferredFromCodec is that it allows the client
2178     // to hold onto the original image data for later.
2179     sk_sp<SkData> encodedData = image->refEncodedData();
2180     REPORTER_ASSERT(r, encodedData != nullptr);
2181     // The returned data should the same as what went in.
2182     REPORTER_ASSERT(r, encodedData->size() == data->size());
2183     REPORTER_ASSERT(r, encodedData->bytes() == data->bytes());
2184 }
2185 
DEF_TEST(Codec_jpeg_can_return_data_from_original_stream,r)2186 DEF_TEST(Codec_jpeg_can_return_data_from_original_stream, r) {
2187     constexpr char path[] = "images/dog.jpg";
2188     // stream will be an SkFILEStream, not a SkMemoryStream (exercised above)
2189     std::unique_ptr<SkStreamAsset> stream = GetResourceAsStream(path, true);
2190     if (!stream) {
2191         SkDebugf("Missing resource '%s'\n", path);
2192         return;
2193     }
2194     size_t expectedBytes = stream->getLength();
2195 
2196     SkCodec::Result result;
2197     auto codec = SkJpegDecoder::Decode(std::move(stream), &result, nullptr);
2198     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2199     REPORTER_ASSERT(r, codec);
2200 
2201     sk_sp<SkImage> image = SkCodecs::DeferredImage(std::move(codec), kUnpremul_SkAlphaType);
2202     REPORTER_ASSERT(r, image);
2203     REPORTER_ASSERT(r, image->width() == 180, "width %d != 180", image->width());
2204     REPORTER_ASSERT(r, image->height() == 180, "height %d != 180", image->height());
2205     REPORTER_ASSERT(r,
2206                     image->alphaType() == kUnpremul_SkAlphaType,
2207                     "AlphaType is wrong %d",
2208                     image->alphaType());
2209 
2210     // The whole point of DeferredFromCodec is that it allows the client
2211     // to hold onto the original image data for later.
2212     sk_sp<SkData> encodedData = image->refEncodedData();
2213     REPORTER_ASSERT(r, encodedData != nullptr);
2214     // The returned data should the same as what went in.
2215     REPORTER_ASSERT(r, encodedData->size() == expectedBytes);
2216     REPORTER_ASSERT(r, SkJpegDecoder::IsJpeg(encodedData->data(), encodedData->size()));
2217 }
2218 
DEF_TEST(Codec_jpeg_decode_progressive_truncated_stream,r)2219 DEF_TEST(Codec_jpeg_decode_progressive_truncated_stream, r) {
2220     constexpr char path[] = "images/progressive_kitten_missing_eof.jpg";
2221     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
2222     if (!stream) {
2223         SkDebugf("Missing resource '%s'\n", path);
2224         return;
2225     }
2226     std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(std::move(stream));
2227     REPORTER_ASSERT(r, codec);
2228 
2229     SkBitmap bm;
2230     SkImageInfo info = codec->getInfo();
2231     REPORTER_ASSERT(r, bm.tryAllocPixels(info));
2232     SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
2233     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
2234 }
2235 
DEF_TEST(Codec_jpeg_decode_progressive_stream_incomplete,r)2236 DEF_TEST(Codec_jpeg_decode_progressive_stream_incomplete, r) {
2237     constexpr char path[] = "images/progressive_kitten_missing_eof.jpg";
2238     std::unique_ptr<SkStream> stream(GetResourceAsStream(path));
2239     if (!stream) {
2240         SkDebugf("Missing resource '%s'\n", path);
2241         return;
2242     }
2243     size_t length = stream->getLength();
2244     std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(SkData::MakeFromStream(stream.get(), 1 * length / 10));
2245     REPORTER_ASSERT(r, codec);
2246 
2247     SkBitmap bm;
2248     SkImageInfo info = codec->getInfo();
2249     REPORTER_ASSERT(r, bm.tryAllocPixels(info));
2250     SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
2251     REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
2252 }
2253