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