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