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