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