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