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