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 "Resources.h" 9 #include "SkAndroidCodec.h" 10 #include "SkBitmap.h" 11 #include "SkCodec.h" 12 #include "SkCodecImageGenerator.h" 13 #include "SkData.h" 14 #include "SkFrontBufferedStream.h" 15 #include "SkMD5.h" 16 #include "SkRandom.h" 17 #include "SkStream.h" 18 #include "SkStreamPriv.h" 19 #include "SkPngChunkReader.h" 20 #include "Test.h" 21 22 #include "png.h" 23 24 static SkStreamAsset* resource(const char path[]) { 25 SkString fullPath = GetResourcePath(path); 26 return SkStream::NewFromFile(fullPath.c_str()); 27 } 28 29 static void md5(const SkBitmap& bm, SkMD5::Digest* digest) { 30 SkAutoLockPixels autoLockPixels(bm); 31 SkASSERT(bm.getPixels()); 32 SkMD5 md5; 33 size_t rowLen = bm.info().bytesPerPixel() * bm.width(); 34 for (int y = 0; y < bm.height(); ++y) { 35 md5.update(static_cast<uint8_t*>(bm.getAddr(0, y)), rowLen); 36 } 37 md5.finish(*digest); 38 } 39 40 /** 41 * Compute the digest for bm and compare it to a known good digest. 42 * @param r Reporter to assert that bm's digest matches goodDigest. 43 * @param goodDigest The known good digest to compare to. 44 * @param bm The bitmap to test. 45 */ 46 static void compare_to_good_digest(skiatest::Reporter* r, const SkMD5::Digest& goodDigest, 47 const SkBitmap& bm) { 48 SkMD5::Digest digest; 49 md5(bm, &digest); 50 REPORTER_ASSERT(r, digest == goodDigest); 51 } 52 53 /** 54 * Test decoding an SkCodec to a particular SkImageInfo. 55 * 56 * Calling getPixels(info) should return expectedResult, and if goodDigest is non nullptr, 57 * the resulting decode should match. 58 */ 59 template<typename Codec> 60 static void test_info(skiatest::Reporter* r, Codec* codec, const SkImageInfo& info, 61 SkCodec::Result expectedResult, const SkMD5::Digest* goodDigest) { 62 SkBitmap bm; 63 bm.allocPixels(info); 64 SkAutoLockPixels autoLockPixels(bm); 65 66 SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); 67 REPORTER_ASSERT(r, result == expectedResult); 68 69 if (goodDigest) { 70 compare_to_good_digest(r, *goodDigest, bm); 71 } 72 } 73 74 SkIRect generate_random_subset(SkRandom* rand, int w, int h) { 75 SkIRect rect; 76 do { 77 rect.fLeft = rand->nextRangeU(0, w); 78 rect.fTop = rand->nextRangeU(0, h); 79 rect.fRight = rand->nextRangeU(0, w); 80 rect.fBottom = rand->nextRangeU(0, h); 81 rect.sort(); 82 } while (rect.isEmpty()); 83 return rect; 84 } 85 86 template<typename Codec> 87 static void test_codec(skiatest::Reporter* r, Codec* codec, SkBitmap& bm, const SkImageInfo& info, 88 const SkISize& size, SkCodec::Result expectedResult, SkMD5::Digest* digest, 89 const SkMD5::Digest* goodDigest) { 90 91 REPORTER_ASSERT(r, info.dimensions() == size); 92 bm.allocPixels(info); 93 SkAutoLockPixels autoLockPixels(bm); 94 95 SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); 96 REPORTER_ASSERT(r, result == expectedResult); 97 98 md5(bm, digest); 99 if (goodDigest) { 100 REPORTER_ASSERT(r, *digest == *goodDigest); 101 } 102 103 { 104 // Test decoding to 565 105 SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType); 106 SkCodec::Result expected565 = info.alphaType() == kOpaque_SkAlphaType ? 107 expectedResult : SkCodec::kInvalidConversion; 108 test_info(r, codec, info565, expected565, nullptr); 109 } 110 111 // Verify that re-decoding gives the same result. It is interesting to check this after 112 // a decode to 565, since choosing to decode to 565 may result in some of the decode 113 // options being modified. These options should return to their defaults on another 114 // decode to kN32, so the new digest should match the old digest. 115 test_info(r, codec, info, expectedResult, digest); 116 117 { 118 // Check alpha type conversions 119 if (info.alphaType() == kOpaque_SkAlphaType) { 120 test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType), 121 expectedResult, digest); 122 test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType), 123 expectedResult, digest); 124 } else { 125 // Decoding to opaque should fail 126 test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType), 127 SkCodec::kInvalidConversion, nullptr); 128 SkAlphaType otherAt = info.alphaType(); 129 if (kPremul_SkAlphaType == otherAt) { 130 otherAt = kUnpremul_SkAlphaType; 131 } else { 132 otherAt = kPremul_SkAlphaType; 133 } 134 // The other non-opaque alpha type should always succeed, but not match. 135 test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nullptr); 136 } 137 } 138 } 139 140 static bool supports_partial_scanlines(const char path[]) { 141 static const char* const exts[] = { 142 "jpg", "jpeg", "png", "webp" 143 "JPG", "JPEG", "PNG", "WEBP" 144 }; 145 146 for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) { 147 if (SkStrEndsWith(path, exts[i])) { 148 return true; 149 } 150 } 151 return false; 152 } 153 154 static void check(skiatest::Reporter* r, 155 const char path[], 156 SkISize size, 157 bool supportsScanlineDecoding, 158 bool supportsSubsetDecoding, 159 bool supportsIncomplete = true) { 160 161 SkAutoTDelete<SkStream> stream(resource(path)); 162 if (!stream) { 163 SkDebugf("Missing resource '%s'\n", path); 164 return; 165 } 166 167 SkAutoTDelete<SkCodec> codec(nullptr); 168 bool isIncomplete = supportsIncomplete; 169 if (isIncomplete) { 170 size_t size = stream->getLength(); 171 SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3))); 172 codec.reset(SkCodec::NewFromData(data)); 173 } else { 174 codec.reset(SkCodec::NewFromStream(stream.detach())); 175 } 176 if (!codec) { 177 ERRORF(r, "Unable to decode '%s'", path); 178 return; 179 } 180 181 // Test full image decodes with SkCodec 182 SkMD5::Digest codecDigest; 183 const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); 184 SkBitmap bm; 185 SkCodec::Result expectedResult = isIncomplete ? SkCodec::kIncompleteInput : SkCodec::kSuccess; 186 test_codec(r, codec.get(), bm, info, size, expectedResult, &codecDigest, nullptr); 187 188 // Scanline decoding follows. 189 // Need to call startScanlineDecode() first. 190 REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) 191 == 0); 192 REPORTER_ASSERT(r, codec->skipScanlines(1) 193 == 0); 194 195 const SkCodec::Result startResult = codec->startScanlineDecode(info); 196 if (supportsScanlineDecoding) { 197 bm.eraseColor(SK_ColorYELLOW); 198 199 REPORTER_ASSERT(r, startResult == SkCodec::kSuccess); 200 201 for (int y = 0; y < info.height(); y++) { 202 const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0); 203 if (!isIncomplete) { 204 REPORTER_ASSERT(r, 1 == lines); 205 } 206 } 207 // verify that scanline decoding gives the same result. 208 if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) { 209 compare_to_good_digest(r, codecDigest, bm); 210 } 211 212 // Cannot continue to decode scanlines beyond the end 213 REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) 214 == 0); 215 216 // Interrupting a scanline decode with a full decode starts from 217 // scratch 218 REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess); 219 const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0); 220 if (!isIncomplete) { 221 REPORTER_ASSERT(r, lines == 1); 222 } 223 REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()) 224 == expectedResult); 225 REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) 226 == 0); 227 REPORTER_ASSERT(r, codec->skipScanlines(1) 228 == 0); 229 230 // Test partial scanline decodes 231 if (supports_partial_scanlines(path) && info.width() >= 3) { 232 SkCodec::Options options; 233 int width = info.width(); 234 int height = info.height(); 235 SkIRect subset = SkIRect::MakeXYWH(2 * (width / 3), 0, width / 3, height); 236 options.fSubset = ⊂ 237 238 const SkCodec::Result partialStartResult = codec->startScanlineDecode(info, &options, 239 nullptr, nullptr); 240 REPORTER_ASSERT(r, partialStartResult == SkCodec::kSuccess); 241 242 for (int y = 0; y < height; y++) { 243 const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0); 244 if (!isIncomplete) { 245 REPORTER_ASSERT(r, 1 == lines); 246 } 247 } 248 } 249 } else { 250 REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented); 251 } 252 253 // The rest of this function tests decoding subsets, and will decode an arbitrary number of 254 // random subsets. 255 // Do not attempt to decode subsets of an image of only once pixel, since there is no 256 // meaningful subset. 257 if (size.width() * size.height() == 1) { 258 return; 259 } 260 261 SkRandom rand; 262 SkIRect subset; 263 SkCodec::Options opts; 264 opts.fSubset = ⊂ 265 for (int i = 0; i < 5; i++) { 266 subset = generate_random_subset(&rand, size.width(), size.height()); 267 SkASSERT(!subset.isEmpty()); 268 const bool supported = codec->getValidSubset(&subset); 269 REPORTER_ASSERT(r, supported == supportsSubsetDecoding); 270 271 SkImageInfo subsetInfo = info.makeWH(subset.width(), subset.height()); 272 SkBitmap bm; 273 bm.allocPixels(subsetInfo); 274 const SkCodec::Result result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), 275 &opts, nullptr, nullptr); 276 277 if (supportsSubsetDecoding) { 278 REPORTER_ASSERT(r, result == expectedResult); 279 // Webp is the only codec that supports subsets, and it will have modified the subset 280 // to have even left/top. 281 REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); 282 } else { 283 // No subsets will work. 284 REPORTER_ASSERT(r, result == SkCodec::kUnimplemented); 285 } 286 } 287 288 // SkAndroidCodec tests 289 if (supportsScanlineDecoding || supportsSubsetDecoding) { 290 291 SkAutoTDelete<SkStream> stream(resource(path)); 292 if (!stream) { 293 SkDebugf("Missing resource '%s'\n", path); 294 return; 295 } 296 297 SkAutoTDelete<SkAndroidCodec> androidCodec(nullptr); 298 if (isIncomplete) { 299 size_t size = stream->getLength(); 300 SkAutoTUnref<SkData> data((SkData::NewFromStream(stream, 2 * size / 3))); 301 androidCodec.reset(SkAndroidCodec::NewFromData(data)); 302 } else { 303 androidCodec.reset(SkAndroidCodec::NewFromStream(stream.detach())); 304 } 305 if (!androidCodec) { 306 ERRORF(r, "Unable to decode '%s'", path); 307 return; 308 } 309 310 SkBitmap bm; 311 SkMD5::Digest androidCodecDigest; 312 test_codec(r, androidCodec.get(), bm, info, size, expectedResult, &androidCodecDigest, 313 &codecDigest); 314 } 315 316 if (!isIncomplete) { 317 // Test SkCodecImageGenerator 318 SkAutoTDelete<SkStream> stream(resource(path)); 319 SkAutoTUnref<SkData> fullData(SkData::NewFromStream(stream, stream->getLength())); 320 SkAutoTDelete<SkImageGenerator> gen(SkCodecImageGenerator::NewFromEncodedCodec(fullData)); 321 SkBitmap bm; 322 bm.allocPixels(info); 323 SkAutoLockPixels autoLockPixels(bm); 324 REPORTER_ASSERT(r, gen->getPixels(info, bm.getPixels(), bm.rowBytes())); 325 compare_to_good_digest(r, codecDigest, bm); 326 327 // Test using SkFrontBufferedStream, as Android does 328 SkStream* bufferedStream = SkFrontBufferedStream::Create(new SkMemoryStream(fullData), 329 SkCodec::MinBufferedBytesNeeded()); 330 REPORTER_ASSERT(r, bufferedStream); 331 codec.reset(SkCodec::NewFromStream(bufferedStream)); 332 REPORTER_ASSERT(r, codec); 333 if (codec) { 334 test_info(r, codec.get(), info, SkCodec::kSuccess, &codecDigest); 335 } 336 } 337 338 // If we've just tested incomplete decodes, let's run the same test again on full decodes. 339 if (isIncomplete) { 340 check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, false); 341 } 342 } 343 344 DEF_TEST(Codec, r) { 345 // WBMP 346 check(r, "mandrill.wbmp", SkISize::Make(512, 512), true, false); 347 348 // WEBP 349 check(r, "baby_tux.webp", SkISize::Make(386, 395), false, true); 350 check(r, "color_wheel.webp", SkISize::Make(128, 128), false, true); 351 check(r, "yellow_rose.webp", SkISize::Make(400, 301), false, true); 352 353 // BMP 354 check(r, "randPixels.bmp", SkISize::Make(8, 8), true, false); 355 check(r, "rle.bmp", SkISize::Make(320, 240), true, false); 356 357 // ICO 358 // FIXME: We are not ready to test incomplete ICOs 359 // These two tests examine interestingly different behavior: 360 // Decodes an embedded BMP image 361 check(r, "color_wheel.ico", SkISize::Make(128, 128), true, false, false); 362 // Decodes an embedded PNG image 363 check(r, "google_chrome.ico", SkISize::Make(256, 256), true, false, false); 364 365 // GIF 366 // FIXME: We are not ready to test incomplete GIFs 367 check(r, "box.gif", SkISize::Make(200, 55), true, false, false); 368 check(r, "color_wheel.gif", SkISize::Make(128, 128), true, false, false); 369 // randPixels.gif is too small to test incomplete 370 check(r, "randPixels.gif", SkISize::Make(8, 8), true, false, false); 371 372 // JPG 373 check(r, "CMYK.jpg", SkISize::Make(642, 516), true, false); 374 check(r, "color_wheel.jpg", SkISize::Make(128, 128), true, false); 375 // grayscale.jpg is too small to test incomplete 376 check(r, "grayscale.jpg", SkISize::Make(128, 128), true, false, false); 377 check(r, "mandrill_512_q075.jpg", SkISize::Make(512, 512), true, false); 378 // randPixels.jpg is too small to test incomplete 379 check(r, "randPixels.jpg", SkISize::Make(8, 8), true, false, false); 380 381 // PNG 382 check(r, "arrow.png", SkISize::Make(187, 312), true, false, false); 383 check(r, "baby_tux.png", SkISize::Make(240, 246), true, false, false); 384 check(r, "color_wheel.png", SkISize::Make(128, 128), true, false, false); 385 check(r, "half-transparent-white-pixel.png", SkISize::Make(1, 1), true, false, false); 386 check(r, "mandrill_128.png", SkISize::Make(128, 128), true, false, false); 387 check(r, "mandrill_16.png", SkISize::Make(16, 16), true, false, false); 388 check(r, "mandrill_256.png", SkISize::Make(256, 256), true, false, false); 389 check(r, "mandrill_32.png", SkISize::Make(32, 32), true, false, false); 390 check(r, "mandrill_512.png", SkISize::Make(512, 512), true, false, false); 391 check(r, "mandrill_64.png", SkISize::Make(64, 64), true, false, false); 392 check(r, "plane.png", SkISize::Make(250, 126), true, false, false); 393 // FIXME: We are not ready to test incomplete interlaced pngs 394 check(r, "plane_interlaced.png", SkISize::Make(250, 126), true, false, false); 395 check(r, "randPixels.png", SkISize::Make(8, 8), true, false, false); 396 check(r, "yellow_rose.png", SkISize::Make(400, 301), true, false, false); 397 398 // RAW 399 // Disable RAW tests for Win32. 400 #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) 401 check(r, "sample_1mp.dng", SkISize::Make(600, 338), false, false, false); 402 check(r, "sample_1mp_rotated.dng", SkISize::Make(600, 338), false, false, false); 403 check(r, "dng_with_preview.dng", SkISize::Make(600, 338), true, false, false); 404 #endif 405 } 406 407 // Test interlaced PNG in stripes, similar to DM's kStripe_Mode 408 DEF_TEST(Codec_stripes, r) { 409 const char * path = "plane_interlaced.png"; 410 SkAutoTDelete<SkStream> stream(resource(path)); 411 if (!stream) { 412 SkDebugf("Missing resource '%s'\n", path); 413 } 414 415 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); 416 REPORTER_ASSERT(r, codec); 417 418 if (!codec) { 419 return; 420 } 421 422 switch (codec->getScanlineOrder()) { 423 case SkCodec::kBottomUp_SkScanlineOrder: 424 case SkCodec::kOutOfOrder_SkScanlineOrder: 425 ERRORF(r, "This scanline order will not match the original."); 426 return; 427 default: 428 break; 429 } 430 431 // Baseline for what the image should look like, using N32. 432 const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); 433 434 SkBitmap bm; 435 bm.allocPixels(info); 436 SkAutoLockPixels autoLockPixels(bm); 437 SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); 438 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 439 440 SkMD5::Digest digest; 441 md5(bm, &digest); 442 443 // Now decode in stripes 444 const int height = info.height(); 445 const int numStripes = 4; 446 int stripeHeight; 447 int remainingLines; 448 SkTDivMod(height, numStripes, &stripeHeight, &remainingLines); 449 450 bm.eraseColor(SK_ColorYELLOW); 451 452 result = codec->startScanlineDecode(info); 453 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 454 455 // Odd stripes 456 for (int i = 1; i < numStripes; i += 2) { 457 // Skip the even stripes 458 bool skipResult = codec->skipScanlines(stripeHeight); 459 REPORTER_ASSERT(r, skipResult); 460 461 int linesDecoded = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight, 462 bm.rowBytes()); 463 REPORTER_ASSERT(r, linesDecoded == stripeHeight); 464 } 465 466 // Even stripes 467 result = codec->startScanlineDecode(info); 468 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 469 470 for (int i = 0; i < numStripes; i += 2) { 471 int linesDecoded = codec->getScanlines(bm.getAddr(0, i * stripeHeight), stripeHeight, 472 bm.rowBytes()); 473 REPORTER_ASSERT(r, linesDecoded == stripeHeight); 474 475 // Skip the odd stripes 476 if (i + 1 < numStripes) { 477 bool skipResult = codec->skipScanlines(stripeHeight); 478 REPORTER_ASSERT(r, skipResult); 479 } 480 } 481 482 // Remainder at the end 483 if (remainingLines > 0) { 484 result = codec->startScanlineDecode(info); 485 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 486 487 bool skipResult = codec->skipScanlines(height - remainingLines); 488 REPORTER_ASSERT(r, skipResult); 489 490 int linesDecoded = codec->getScanlines(bm.getAddr(0, height - remainingLines), 491 remainingLines, bm.rowBytes()); 492 REPORTER_ASSERT(r, linesDecoded == remainingLines); 493 } 494 495 compare_to_good_digest(r, digest, bm); 496 } 497 498 static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) { 499 // Neither of these calls should return a codec. Bots should catch us if we leaked anything. 500 SkCodec* codec = SkCodec::NewFromStream(new SkMemoryStream(stream, len, false)); 501 REPORTER_ASSERT(r, !codec); 502 503 SkAndroidCodec* androidCodec = 504 SkAndroidCodec::NewFromStream(new SkMemoryStream(stream, len, false)); 505 REPORTER_ASSERT(r, !androidCodec); 506 } 507 508 // Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream, 509 // even on failure. Test some bad streams. 510 DEF_TEST(Codec_leaks, r) { 511 // No codec should claim this as their format, so this tests SkCodec::NewFromStream. 512 const char nonSupportedStream[] = "hello world"; 513 // The other strings should look like the beginning of a file type, so we'll call some 514 // internal version of NewFromStream, which must also delete the stream on failure. 515 const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; 516 const unsigned char emptyJpeg[] = { 0xFF, 0xD8, 0xFF }; 517 const char emptyWebp[] = "RIFF1234WEBPVP"; 518 const char emptyBmp[] = { 'B', 'M' }; 519 const char emptyIco[] = { '\x00', '\x00', '\x01', '\x00' }; 520 const char emptyGif[] = "GIFVER"; 521 522 test_invalid_stream(r, nonSupportedStream, sizeof(nonSupportedStream)); 523 test_invalid_stream(r, emptyPng, sizeof(emptyPng)); 524 test_invalid_stream(r, emptyJpeg, sizeof(emptyJpeg)); 525 test_invalid_stream(r, emptyWebp, sizeof(emptyWebp)); 526 test_invalid_stream(r, emptyBmp, sizeof(emptyBmp)); 527 test_invalid_stream(r, emptyIco, sizeof(emptyIco)); 528 test_invalid_stream(r, emptyGif, sizeof(emptyGif)); 529 } 530 531 DEF_TEST(Codec_null, r) { 532 // Attempting to create an SkCodec or an SkAndroidCodec with null should not 533 // crash. 534 SkCodec* codec = SkCodec::NewFromStream(nullptr); 535 REPORTER_ASSERT(r, !codec); 536 537 SkAndroidCodec* androidCodec = SkAndroidCodec::NewFromStream(nullptr); 538 REPORTER_ASSERT(r, !androidCodec); 539 } 540 541 static void test_dimensions(skiatest::Reporter* r, const char path[]) { 542 // Create the codec from the resource file 543 SkAutoTDelete<SkStream> stream(resource(path)); 544 if (!stream) { 545 SkDebugf("Missing resource '%s'\n", path); 546 return; 547 } 548 SkAutoTDelete<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.detach())); 549 if (!codec) { 550 ERRORF(r, "Unable to create codec '%s'", path); 551 return; 552 } 553 554 // Check that the decode is successful for a variety of scales 555 for (int sampleSize = 1; sampleSize < 32; sampleSize++) { 556 // Scale the output dimensions 557 SkISize scaledDims = codec->getSampledDimensions(sampleSize); 558 SkImageInfo scaledInfo = codec->getInfo() 559 .makeWH(scaledDims.width(), scaledDims.height()) 560 .makeColorType(kN32_SkColorType); 561 562 // Set up for the decode 563 size_t rowBytes = scaledDims.width() * sizeof(SkPMColor); 564 size_t totalBytes = scaledInfo.getSafeSize(rowBytes); 565 SkAutoTMalloc<SkPMColor> pixels(totalBytes); 566 567 SkAndroidCodec::AndroidOptions options; 568 options.fSampleSize = sampleSize; 569 SkCodec::Result result = 570 codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options); 571 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 572 } 573 } 574 575 // Ensure that onGetScaledDimensions returns valid image dimensions to use for decodes 576 DEF_TEST(Codec_Dimensions, r) { 577 // JPG 578 test_dimensions(r, "CMYK.jpg"); 579 test_dimensions(r, "color_wheel.jpg"); 580 test_dimensions(r, "grayscale.jpg"); 581 test_dimensions(r, "mandrill_512_q075.jpg"); 582 test_dimensions(r, "randPixels.jpg"); 583 584 // Decoding small images with very large scaling factors is a potential 585 // source of bugs and crashes. We disable these tests in Gold because 586 // tiny images are not very useful to look at. 587 // Here we make sure that we do not crash or access illegal memory when 588 // performing scaled decodes on small images. 589 test_dimensions(r, "1x1.png"); 590 test_dimensions(r, "2x2.png"); 591 test_dimensions(r, "3x3.png"); 592 test_dimensions(r, "3x1.png"); 593 test_dimensions(r, "1x1.png"); 594 test_dimensions(r, "16x1.png"); 595 test_dimensions(r, "1x16.png"); 596 test_dimensions(r, "mandrill_16.png"); 597 598 // RAW 599 // Disable RAW tests for Win32. 600 #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) 601 test_dimensions(r, "sample_1mp.dng"); 602 test_dimensions(r, "sample_1mp_rotated.dng"); 603 test_dimensions(r, "dng_with_preview.dng"); 604 #endif 605 } 606 607 static void test_invalid(skiatest::Reporter* r, const char path[]) { 608 SkAutoTDelete<SkStream> stream(resource(path)); 609 if (!stream) { 610 SkDebugf("Missing resource '%s'\n", path); 611 return; 612 } 613 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); 614 REPORTER_ASSERT(r, nullptr == codec); 615 } 616 617 DEF_TEST(Codec_Empty, r) { 618 // Test images that should not be able to create a codec 619 test_invalid(r, "empty_images/zero-dims.gif"); 620 test_invalid(r, "empty_images/zero-embedded.ico"); 621 test_invalid(r, "empty_images/zero-width.bmp"); 622 test_invalid(r, "empty_images/zero-height.bmp"); 623 test_invalid(r, "empty_images/zero-width.jpg"); 624 test_invalid(r, "empty_images/zero-height.jpg"); 625 test_invalid(r, "empty_images/zero-width.png"); 626 test_invalid(r, "empty_images/zero-height.png"); 627 test_invalid(r, "empty_images/zero-width.wbmp"); 628 test_invalid(r, "empty_images/zero-height.wbmp"); 629 // This image is an ico with an embedded mask-bmp. This is illegal. 630 test_invalid(r, "invalid_images/mask-bmp-ico.ico"); 631 } 632 633 static void test_invalid_parameters(skiatest::Reporter* r, const char path[]) { 634 SkAutoTDelete<SkStream> stream(resource(path)); 635 if (!stream) { 636 SkDebugf("Missing resource '%s'\n", path); 637 return; 638 } 639 SkAutoTDelete<SkCodec> decoder(SkCodec::NewFromStream(stream.detach())); 640 641 // This should return kSuccess because kIndex8 is supported. 642 SkPMColor colorStorage[256]; 643 int colorCount; 644 SkCodec::Result result = decoder->startScanlineDecode( 645 decoder->getInfo().makeColorType(kIndex_8_SkColorType), nullptr, colorStorage, &colorCount); 646 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 647 // The rest of the test is uninteresting if kIndex8 is not supported 648 if (SkCodec::kSuccess != result) { 649 return; 650 } 651 652 // This should return kInvalidParameters because, in kIndex_8 mode, we must pass in a valid 653 // colorPtr and a valid colorCountPtr. 654 result = decoder->startScanlineDecode( 655 decoder->getInfo().makeColorType(kIndex_8_SkColorType), nullptr, nullptr, nullptr); 656 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); 657 result = decoder->startScanlineDecode( 658 decoder->getInfo().makeColorType(kIndex_8_SkColorType)); 659 REPORTER_ASSERT(r, SkCodec::kInvalidParameters == result); 660 } 661 662 DEF_TEST(Codec_Params, r) { 663 test_invalid_parameters(r, "index8.png"); 664 test_invalid_parameters(r, "mandrill.wbmp"); 665 } 666 667 static void codex_test_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { 668 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); 669 if (!sk_stream->write(data, len)) { 670 png_error(png_ptr, "sk_write_fn Error!"); 671 } 672 } 673 674 #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED 675 DEF_TEST(Codec_pngChunkReader, r) { 676 // Create a dummy bitmap. Use unpremul RGBA for libpng. 677 SkBitmap bm; 678 const int w = 1; 679 const int h = 1; 680 const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, 681 kUnpremul_SkAlphaType); 682 bm.setInfo(bmInfo); 683 bm.allocPixels(); 684 bm.eraseColor(SK_ColorBLUE); 685 SkMD5::Digest goodDigest; 686 md5(bm, &goodDigest); 687 688 // Write to a png file. 689 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 690 REPORTER_ASSERT(r, png); 691 if (!png) { 692 return; 693 } 694 695 png_infop info = png_create_info_struct(png); 696 REPORTER_ASSERT(r, info); 697 if (!info) { 698 png_destroy_write_struct(&png, nullptr); 699 return; 700 } 701 702 if (setjmp(png_jmpbuf(png))) { 703 ERRORF(r, "failed writing png"); 704 png_destroy_write_struct(&png, &info); 705 return; 706 } 707 708 SkDynamicMemoryWStream wStream; 709 png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr); 710 711 png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8, 712 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, 713 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 714 715 // Create some chunks that match the Android framework's use. 716 static png_unknown_chunk gUnknowns[] = { 717 { "npOl", (png_byte*)"outline", sizeof("outline"), PNG_HAVE_IHDR }, 718 { "npLb", (png_byte*)"layoutBounds", sizeof("layoutBounds"), PNG_HAVE_IHDR }, 719 { "npTc", (png_byte*)"ninePatchData", sizeof("ninePatchData"), PNG_HAVE_IHDR }, 720 }; 721 722 png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"npOl\0npLb\0npTc\0", 3); 723 png_set_unknown_chunks(png, info, gUnknowns, SK_ARRAY_COUNT(gUnknowns)); 724 #if PNG_LIBPNG_VER < 10600 725 /* Deal with unknown chunk location bug in 1.5.x and earlier */ 726 png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR); 727 png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR); 728 #endif 729 730 png_write_info(png, info); 731 732 for (int j = 0; j < h; j++) { 733 png_bytep row = (png_bytep)(bm.getAddr(0, j)); 734 png_write_rows(png, &row, 1); 735 } 736 png_write_end(png, info); 737 png_destroy_write_struct(&png, &info); 738 739 class ChunkReader : public SkPngChunkReader { 740 public: 741 ChunkReader(skiatest::Reporter* r) 742 : fReporter(r) 743 { 744 this->reset(); 745 } 746 747 bool readChunk(const char tag[], const void* data, size_t length) override { 748 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnknowns); ++i) { 749 if (!strcmp(tag, (const char*) gUnknowns[i].name)) { 750 // Tag matches. This should have been the first time we see it. 751 REPORTER_ASSERT(fReporter, !fSeen[i]); 752 fSeen[i] = true; 753 754 // Data and length should match 755 REPORTER_ASSERT(fReporter, length == gUnknowns[i].size); 756 REPORTER_ASSERT(fReporter, !strcmp((const char*) data, 757 (const char*) gUnknowns[i].data)); 758 return true; 759 } 760 } 761 ERRORF(fReporter, "Saw an unexpected unknown chunk."); 762 return true; 763 } 764 765 bool allHaveBeenSeen() { 766 bool ret = true; 767 for (auto seen : fSeen) { 768 ret &= seen; 769 } 770 return ret; 771 } 772 773 void reset() { 774 sk_bzero(fSeen, sizeof(fSeen)); 775 } 776 777 private: 778 skiatest::Reporter* fReporter; // Unowned 779 bool fSeen[3]; 780 }; 781 782 ChunkReader chunkReader(r); 783 784 // Now read the file with SkCodec. 785 SkAutoTUnref<SkData> data(wStream.copyToData()); 786 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(data, &chunkReader)); 787 REPORTER_ASSERT(r, codec); 788 if (!codec) { 789 return; 790 } 791 792 // Now compare to the original. 793 SkBitmap decodedBm; 794 decodedBm.setInfo(codec->getInfo()); 795 decodedBm.allocPixels(); 796 SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), 797 decodedBm.rowBytes()); 798 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 799 800 if (decodedBm.colorType() != bm.colorType()) { 801 SkBitmap tmp; 802 bool success = decodedBm.copyTo(&tmp, bm.colorType()); 803 REPORTER_ASSERT(r, success); 804 if (!success) { 805 return; 806 } 807 808 tmp.swap(decodedBm); 809 } 810 811 compare_to_good_digest(r, goodDigest, decodedBm); 812 REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); 813 814 // Decoding again will read the chunks again. 815 chunkReader.reset(); 816 REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen()); 817 result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes()); 818 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 819 REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); 820 } 821 #endif // PNG_READ_UNKNOWN_CHUNKS_SUPPORTED 822 823 // Stream that can only peek up to a limit 824 class LimitedPeekingMemStream : public SkStream { 825 public: 826 LimitedPeekingMemStream(SkData* data, size_t limit) 827 : fStream(data) 828 , fLimit(limit) {} 829 830 size_t peek(void* buf, size_t bytes) const override { 831 return fStream.peek(buf, SkTMin(bytes, fLimit)); 832 } 833 size_t read(void* buf, size_t bytes) override { 834 return fStream.read(buf, bytes); 835 } 836 bool rewind() override { 837 return fStream.rewind(); 838 } 839 bool isAtEnd() const override { 840 return false; 841 } 842 private: 843 SkMemoryStream fStream; 844 const size_t fLimit; 845 }; 846 847 // Stream that is not an asset stream (!hasPosition() or !hasLength()) 848 class NotAssetMemStream : public SkStream { 849 public: 850 NotAssetMemStream(SkData* data) : fStream(data) {} 851 852 bool hasPosition() const override { 853 return false; 854 } 855 856 bool hasLength() const override { 857 return false; 858 } 859 860 size_t peek(void* buf, size_t bytes) const override { 861 return fStream.peek(buf, bytes); 862 } 863 size_t read(void* buf, size_t bytes) override { 864 return fStream.read(buf, bytes); 865 } 866 bool rewind() override { 867 return fStream.rewind(); 868 } 869 bool isAtEnd() const override { 870 return fStream.isAtEnd(); 871 } 872 private: 873 SkMemoryStream fStream; 874 }; 875 876 // Disable RAW tests for Win32. 877 #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) 878 // Test that the RawCodec works also for not asset stream. This will test the code path using 879 // SkRawBufferedStream instead of SkRawAssetStream. 880 DEF_TEST(Codec_raw_notseekable, r) { 881 const char* path = "dng_with_preview.dng"; 882 SkString fullPath(GetResourcePath(path)); 883 SkAutoTUnref<SkData> data(SkData::NewFromFileName(fullPath.c_str())); 884 if (!data) { 885 SkDebugf("Missing resource '%s'\n", path); 886 return; 887 } 888 889 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(new NotAssetMemStream(data))); 890 REPORTER_ASSERT(r, codec); 891 892 test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); 893 } 894 #endif 895 896 // Test that even if webp_parse_header fails to peek enough, it will fall back to read() 897 // + rewind() and succeed. 898 DEF_TEST(Codec_webp_peek, r) { 899 const char* path = "baby_tux.webp"; 900 SkString fullPath(GetResourcePath(path)); 901 SkAutoTUnref<SkData> data(SkData::NewFromFileName(fullPath.c_str())); 902 if (!data) { 903 SkDebugf("Missing resource '%s'\n", path); 904 return; 905 } 906 907 // The limit is less than webp needs to peek or read. 908 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(new LimitedPeekingMemStream(data, 25))); 909 REPORTER_ASSERT(r, codec); 910 911 test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); 912 913 // Similarly, a stream which does not peek should still succeed. 914 codec.reset(SkCodec::NewFromStream(new LimitedPeekingMemStream(data, 0))); 915 REPORTER_ASSERT(r, codec); 916 917 test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); 918 } 919 920 // SkCodec's wbmp decoder was initially unnecessarily restrictive. 921 // It required the second byte to be zero. The wbmp specification allows 922 // a couple of bits to be 1 (so long as they do not overlap with 0x9F). 923 // Test that SkCodec now supports an image with these bits set. 924 DEF_TEST(Codec_wbmp, r) { 925 const char* path = "mandrill.wbmp"; 926 SkAutoTDelete<SkStream> stream(resource(path)); 927 if (!stream) { 928 SkDebugf("Missing resource '%s'\n", path); 929 return; 930 } 931 932 // Modify the stream to contain a second byte with some bits set. 933 SkAutoTUnref<SkData> data(SkCopyStreamToData(stream)); 934 uint8_t* writeableData = static_cast<uint8_t*>(data->writable_data()); 935 writeableData[1] = static_cast<uint8_t>(~0x9F); 936 937 // SkCodec should support this. 938 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(data)); 939 REPORTER_ASSERT(r, codec); 940 if (!codec) { 941 return; 942 } 943 test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); 944 } 945 946 // wbmp images have a header that can be arbitrarily large, depending on the 947 // size of the image. We cap the size at 65535, meaning we only need to look at 948 // 8 bytes to determine whether we can read the image. This is important 949 // because SkCodec only passes 14 bytes to SkWbmpCodec to determine whether the 950 // image is a wbmp. 951 DEF_TEST(Codec_wbmp_max_size, r) { 952 const unsigned char maxSizeWbmp[] = { 0x00, 0x00, // Header 953 0x83, 0xFF, 0x7F, // W: 65535 954 0x83, 0xFF, 0x7F }; // H: 65535 955 SkAutoTDelete<SkStream> stream(new SkMemoryStream(maxSizeWbmp, sizeof(maxSizeWbmp), false)); 956 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); 957 958 REPORTER_ASSERT(r, codec); 959 if (!codec) return; 960 961 REPORTER_ASSERT(r, codec->getInfo().width() == 65535); 962 REPORTER_ASSERT(r, codec->getInfo().height() == 65535); 963 964 // Now test an image which is too big. Any image with a larger header (i.e. 965 // has bigger width/height) is also too big. 966 const unsigned char tooBigWbmp[] = { 0x00, 0x00, // Header 967 0x84, 0x80, 0x00, // W: 65536 968 0x84, 0x80, 0x00 }; // H: 65536 969 stream.reset(new SkMemoryStream(tooBigWbmp, sizeof(tooBigWbmp), false)); 970 codec.reset(SkCodec::NewFromStream(stream.detach())); 971 972 REPORTER_ASSERT(r, !codec); 973 } 974