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