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