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