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