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// Make sure SkUserConfig.h is included so #defines are available on 9// Android. 10#include "include/core/SkTypes.h" 11#ifdef SK_ENABLE_ANDROID_UTILS 12#include "client_utils/android/FrontBufferedStream.h" 13#endif 14#include "include/codec/SkAndroidCodec.h" 15#include "include/codec/SkCodec.h" 16#include "include/core/SkBitmap.h" 17#include "include/core/SkCanvas.h" 18#include "include/core/SkColor.h" 19#include "include/core/SkColorSpace.h" 20#include "include/core/SkData.h" 21#include "include/core/SkEncodedImageFormat.h" 22#include "include/core/SkImage.h" 23#include "include/core/SkImageEncoder.h" 24#include "include/core/SkImageGenerator.h" 25#include "include/core/SkImageInfo.h" 26#include "include/core/SkPixmap.h" 27#include "include/core/SkPngChunkReader.h" 28#include "include/core/SkRect.h" 29#include "include/core/SkRefCnt.h" 30#include "include/core/SkSize.h" 31#include "include/core/SkStream.h" 32#include "include/core/SkString.h" 33#include "include/core/SkTypes.h" 34#include "include/core/SkUnPreMultiply.h" 35#include "include/encode/SkJpegEncoder.h" 36#include "include/encode/SkPngEncoder.h" 37#include "include/encode/SkWebpEncoder.h" 38#include "include/private/SkMalloc.h" 39#include "include/private/SkTemplates.h" 40#include "include/third_party/skcms/skcms.h" 41#include "include/utils/SkRandom.h" 42#include "src/codec/SkCodecImageGenerator.h" 43#include "src/core/SkAutoMalloc.h" 44#include "src/core/SkColorSpacePriv.h" 45#include "src/core/SkMD5.h" 46#include "src/core/SkStreamPriv.h" 47#include "tests/FakeStreams.h" 48#include "tests/Test.h" 49#include "tools/Resources.h" 50#include "tools/ToolUtils.h" 51 52#include "png.h" 53 54#include <setjmp.h> 55#include <cstring> 56#include <initializer_list> 57#include <memory> 58#include <utility> 59#include <vector> 60 61#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR < 5 62 // FIXME (scroggo): Google3 needs to be updated to use a newer version of libpng. In 63 // the meantime, we had to break some pieces of SkPngCodec in order to support Google3. 64 // The parts that are broken are likely not used by Google3. 65 #define SK_PNG_DISABLE_TESTS 66#endif 67 68static SkMD5::Digest md5(const SkBitmap& bm) { 69 SkASSERT(bm.getPixels()); 70 SkMD5 md5; 71 size_t rowLen = bm.info().bytesPerPixel() * bm.width(); 72 for (int y = 0; y < bm.height(); ++y) { 73 md5.write(bm.getAddr(0, y), rowLen); 74 } 75 return md5.finish(); 76} 77 78/** 79 * Compute the digest for bm and compare it to a known good digest. 80 * @param r Reporter to assert that bm's digest matches goodDigest. 81 * @param goodDigest The known good digest to compare to. 82 * @param bm The bitmap to test. 83 */ 84static void compare_to_good_digest(skiatest::Reporter* r, const SkMD5::Digest& goodDigest, 85 const SkBitmap& bm) { 86 SkMD5::Digest digest = md5(bm); 87 REPORTER_ASSERT(r, digest == goodDigest); 88} 89 90/** 91 * Test decoding an SkCodec to a particular SkImageInfo. 92 * 93 * Calling getPixels(info) should return expectedResult, and if goodDigest is non nullptr, 94 * the resulting decode should match. 95 */ 96template<typename Codec> 97static void test_info(skiatest::Reporter* r, Codec* codec, const SkImageInfo& info, 98 SkCodec::Result expectedResult, const SkMD5::Digest* goodDigest) { 99 SkBitmap bm; 100 bm.allocPixels(info); 101 102 SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); 103 REPORTER_ASSERT(r, result == expectedResult); 104 105 if (goodDigest) { 106 compare_to_good_digest(r, *goodDigest, bm); 107 } 108} 109 110SkIRect generate_random_subset(SkRandom* rand, int w, int h) { 111 SkIRect rect; 112 do { 113 rect.fLeft = rand->nextRangeU(0, w); 114 rect.fTop = rand->nextRangeU(0, h); 115 rect.fRight = rand->nextRangeU(0, w); 116 rect.fBottom = rand->nextRangeU(0, h); 117 rect.sort(); 118 } while (rect.isEmpty()); 119 return rect; 120} 121 122static void test_incremental_decode(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info, 123 const SkMD5::Digest& goodDigest) { 124 SkBitmap bm; 125 bm.allocPixels(info); 126 127 REPORTER_ASSERT(r, SkCodec::kSuccess == codec->startIncrementalDecode(info, bm.getPixels(), 128 bm.rowBytes())); 129 130 REPORTER_ASSERT(r, SkCodec::kSuccess == codec->incrementalDecode()); 131 132 compare_to_good_digest(r, goodDigest, bm); 133} 134 135// Test in stripes, similar to DM's kStripe_Mode 136static void test_in_stripes(skiatest::Reporter* r, SkCodec* codec, const SkImageInfo& info, 137 const SkMD5::Digest& goodDigest) { 138 SkBitmap bm; 139 bm.allocPixels(info); 140 bm.eraseColor(SK_ColorYELLOW); 141 142 const int height = info.height(); 143 // Note that if numStripes does not evenly divide height there will be an extra 144 // stripe. 145 const int numStripes = 4; 146 147 if (numStripes > height) { 148 // Image is too small. 149 return; 150 } 151 152 const int stripeHeight = height / numStripes; 153 154 // Iterate through the image twice. Once to decode odd stripes, and once for even. 155 for (int oddEven = 1; oddEven >= 0; oddEven--) { 156 for (int y = oddEven * stripeHeight; y < height; y += 2 * stripeHeight) { 157 SkIRect subset = SkIRect::MakeLTRB(0, y, info.width(), 158 std::min(y + stripeHeight, height)); 159 SkCodec::Options options; 160 options.fSubset = ⊂ 161 if (SkCodec::kSuccess != codec->startIncrementalDecode(info, bm.getAddr(0, y), 162 bm.rowBytes(), &options)) { 163 ERRORF(r, "failed to start incremental decode!\ttop: %i\tbottom%i\n", 164 subset.top(), subset.bottom()); 165 return; 166 } 167 if (SkCodec::kSuccess != codec->incrementalDecode()) { 168 ERRORF(r, "failed incremental decode starting from line %i\n", y); 169 return; 170 } 171 } 172 } 173 174 compare_to_good_digest(r, goodDigest, bm); 175} 176 177template<typename Codec> 178static void test_codec(skiatest::Reporter* r, const char* path, Codec* codec, SkBitmap& bm, 179 const SkImageInfo& info, const SkISize& size, SkCodec::Result expectedResult, 180 SkMD5::Digest* digest, const SkMD5::Digest* goodDigest) { 181 182 REPORTER_ASSERT(r, info.dimensions() == size); 183 bm.allocPixels(info); 184 185 SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes()); 186 REPORTER_ASSERT(r, result == expectedResult); 187 188 *digest = md5(bm); 189 if (goodDigest) { 190 REPORTER_ASSERT(r, *digest == *goodDigest); 191 } 192 193 { 194 // Test decoding to 565 195 SkImageInfo info565 = info.makeColorType(kRGB_565_SkColorType); 196 if (info.alphaType() == kOpaque_SkAlphaType) { 197 // Decoding to 565 should succeed. 198 SkBitmap bm565; 199 bm565.allocPixels(info565); 200 201 // This will allow comparison even if the image is incomplete. 202 bm565.eraseColor(SK_ColorBLACK); 203 204 auto actualResult = codec->getPixels(info565, bm565.getPixels(), bm565.rowBytes()); 205 if (actualResult == expectedResult) { 206 SkMD5::Digest digest565 = md5(bm565); 207 208 // A request for non-opaque should also succeed. 209 for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) { 210 info565 = info565.makeAlphaType(alpha); 211 test_info(r, codec, info565, expectedResult, &digest565); 212 } 213 } else { 214 ERRORF(r, "Decoding %s to 565 failed with result \"%s\"\n\t\t\t\texpected:\"%s\"", 215 path, 216 SkCodec::ResultToString(actualResult), 217 SkCodec::ResultToString(expectedResult)); 218 } 219 } else { 220 test_info(r, codec, info565, SkCodec::kInvalidConversion, nullptr); 221 } 222 } 223 224 if (codec->getInfo().colorType() == kGray_8_SkColorType) { 225 SkImageInfo grayInfo = codec->getInfo(); 226 SkBitmap grayBm; 227 grayBm.allocPixels(grayInfo); 228 229 grayBm.eraseColor(SK_ColorBLACK); 230 231 REPORTER_ASSERT(r, expectedResult == codec->getPixels(grayInfo, 232 grayBm.getPixels(), grayBm.rowBytes())); 233 234 SkMD5::Digest grayDigest = md5(grayBm); 235 236 for (auto alpha : { kPremul_SkAlphaType, kUnpremul_SkAlphaType }) { 237 grayInfo = grayInfo.makeAlphaType(alpha); 238 test_info(r, codec, grayInfo, expectedResult, &grayDigest); 239 } 240 } 241 242 // Verify that re-decoding gives the same result. It is interesting to check this after 243 // a decode to 565, since choosing to decode to 565 may result in some of the decode 244 // options being modified. These options should return to their defaults on another 245 // decode to kN32, so the new digest should match the old digest. 246 test_info(r, codec, info, expectedResult, digest); 247 248 { 249 // Check alpha type conversions 250 if (info.alphaType() == kOpaque_SkAlphaType) { 251 test_info(r, codec, info.makeAlphaType(kUnpremul_SkAlphaType), 252 expectedResult, digest); 253 test_info(r, codec, info.makeAlphaType(kPremul_SkAlphaType), 254 expectedResult, digest); 255 } else { 256 // Decoding to opaque should fail 257 test_info(r, codec, info.makeAlphaType(kOpaque_SkAlphaType), 258 SkCodec::kInvalidConversion, nullptr); 259 SkAlphaType otherAt = info.alphaType(); 260 if (kPremul_SkAlphaType == otherAt) { 261 otherAt = kUnpremul_SkAlphaType; 262 } else { 263 otherAt = kPremul_SkAlphaType; 264 } 265 // The other non-opaque alpha type should always succeed, but not match. 266 test_info(r, codec, info.makeAlphaType(otherAt), expectedResult, nullptr); 267 } 268 } 269} 270 271static bool supports_partial_scanlines(const char path[]) { 272 static const char* const exts[] = { 273 "jpg", "jpeg", "png", "webp", 274 "JPG", "JPEG", "PNG", "WEBP" 275 }; 276 277 for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) { 278 if (SkStrEndsWith(path, exts[i])) { 279 return true; 280 } 281 } 282 return false; 283} 284 285static void check_scanline_decode(skiatest::Reporter* r, 286 SkCodec* codec, 287 SkMD5::Digest* codecDigest, 288 const SkImageInfo& info, 289 const char path[], 290 SkISize size, 291 bool supportsScanlineDecoding, 292 bool supportsIncomplete, 293 bool supportsNewScanlineDecoding) { 294 295 // Test full image decodes with SkCodec 296 SkBitmap bm; 297 const SkCodec::Result expectedResult = supportsIncomplete ? SkCodec::kIncompleteInput 298 : SkCodec::kSuccess; 299 test_codec(r, path, codec, bm, info, size, expectedResult, codecDigest, nullptr); 300 301 // Scanline decoding follows. 302 if (supportsNewScanlineDecoding && !supportsIncomplete) { 303 test_incremental_decode(r, codec, info, *codecDigest); 304 // This is only supported by codecs that use incremental decoding to 305 // support subset decodes - png and jpeg (once SkJpegCodec is 306 // converted). 307 if (SkStrEndsWith(path, "png") || SkStrEndsWith(path, "PNG")) { 308 test_in_stripes(r, codec, info, *codecDigest); 309 } 310 } 311 312 // Need to call startScanlineDecode() first. 313 REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) == 0); 314 REPORTER_ASSERT(r, !codec->skipScanlines(1)); 315 const SkCodec::Result startResult = codec->startScanlineDecode(info); 316 if (supportsScanlineDecoding) { 317 bm.eraseColor(SK_ColorYELLOW); 318 319 REPORTER_ASSERT(r, startResult == SkCodec::kSuccess); 320 321 for (int y = 0; y < info.height(); y++) { 322 const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0); 323 if (!supportsIncomplete) { 324 REPORTER_ASSERT(r, 1 == lines); 325 } 326 } 327 // verify that scanline decoding gives the same result. 328 if (SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder()) { 329 compare_to_good_digest(r, *codecDigest, bm); 330 } 331 332 // Cannot continue to decode scanlines beyond the end 333 REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) 334 == 0); 335 336 // Interrupting a scanline decode with a full decode starts from 337 // scratch 338 { 339 REPORTER_ASSERT(r, codec->startScanlineDecode(info) == SkCodec::kSuccess); 340 const int lines = codec->getScanlines(bm.getAddr(0, 0), 1, 0); 341 if (!supportsIncomplete) { 342 REPORTER_ASSERT(r, lines == 1); 343 } 344 REPORTER_ASSERT(r, codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes()) 345 == expectedResult); 346 REPORTER_ASSERT(r, codec->getScanlines(bm.getAddr(0, 0), 1, 0) 347 == 0); 348 REPORTER_ASSERT(r, codec->skipScanlines(1) 349 == 0); 350 } 351 352 // Test partial scanline decodes 353 if (supports_partial_scanlines(path) && info.width() >= 3) { 354 SkCodec::Options options; 355 int width = info.width(); 356 int height = info.height(); 357 SkIRect subset = SkIRect::MakeXYWH(2 * (width / 3), 0, width / 3, height); 358 options.fSubset = ⊂ 359 360 const auto partialStartResult = codec->startScanlineDecode(info, &options); 361 REPORTER_ASSERT(r, partialStartResult == SkCodec::kSuccess); 362 363 for (int y = 0; y < height; y++) { 364 const int lines = codec->getScanlines(bm.getAddr(0, y), 1, 0); 365 if (!supportsIncomplete) { 366 REPORTER_ASSERT(r, 1 == lines); 367 } 368 } 369 } 370 } else { 371 REPORTER_ASSERT(r, startResult == SkCodec::kUnimplemented); 372 } 373} 374 375static void check_subset_decode(skiatest::Reporter* r, 376 SkCodec* codec, 377 const SkImageInfo& info, 378 SkISize size, 379 bool supportsSubsetDecoding, 380 bool supportsIncomplete) { 381 // This function tests decoding subsets, and will decode a handful of randomly-sized subsets. 382 // Do not attempt to decode subsets of an image of only one pixel, since there is no 383 // meaningful subset. 384 if (size.width() * size.height() == 1) { 385 return; 386 } 387 388 SkRandom rand; 389 SkIRect subset; 390 SkCodec::Options opts; 391 opts.fSubset = ⊂ 392 for (int i = 0; i < 5; i++) { 393 subset = generate_random_subset(&rand, size.width(), size.height()); 394 SkASSERT(!subset.isEmpty()); 395 const bool supported = codec->getValidSubset(&subset); 396 REPORTER_ASSERT(r, supported == supportsSubsetDecoding); 397 398 SkImageInfo subsetInfo = info.makeDimensions(subset.size()); 399 SkBitmap bm; 400 bm.allocPixels(subsetInfo); 401 const auto result = codec->getPixels(bm.info(), bm.getPixels(), bm.rowBytes(), &opts); 402 403 if (supportsSubsetDecoding) { 404 if (!supportsIncomplete) { 405 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 406 } 407 // Webp is the only codec that supports subsets, and it will have modified the subset 408 // to have even left/top. 409 REPORTER_ASSERT(r, SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); 410 } else { 411 // No subsets will work. 412 REPORTER_ASSERT(r, result == SkCodec::kUnimplemented); 413 } 414 } 415} 416 417static void check_android_codec(skiatest::Reporter* r, 418 std::unique_ptr<SkCodec> codec, 419 const SkMD5::Digest& codecDigest, 420 const SkImageInfo& info, 421 const char path[], 422 SkISize size, 423 bool supportsScanlineDecoding, 424 bool supportsSubsetDecoding, 425 bool supportsIncomplete, 426 bool supportsNewScanlineDecoding) { 427 if (supportsScanlineDecoding || supportsSubsetDecoding || supportsNewScanlineDecoding) { 428 auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec)); 429 if (!androidCodec) { 430 ERRORF(r, "Unable to decode '%s'", path); 431 return; 432 } 433 434 SkBitmap bm; 435 SkMD5::Digest androidCodecDigest; 436 const SkCodec::Result expectedResult = supportsIncomplete ? SkCodec::kIncompleteInput 437 : SkCodec::kSuccess; 438 test_codec(r, path, androidCodec.get(), bm, info, size, expectedResult, &androidCodecDigest, 439 &codecDigest); 440 } 441} 442 443static void check_codec_image_generator(skiatest::Reporter* r, 444 const SkMD5::Digest& codecDigest, 445 const SkImageInfo& info, 446 const char path[], 447 bool supportsIncomplete) { 448 // Test SkCodecImageGenerator 449 if (!supportsIncomplete) { 450 std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); 451 sk_sp<SkData> fullData(SkData::MakeFromStream(stream.get(), stream->getLength())); 452 std::unique_ptr<SkImageGenerator> gen( 453 SkCodecImageGenerator::MakeFromEncodedCodec(fullData)); 454 SkBitmap bm; 455 bm.allocPixels(info); 456 REPORTER_ASSERT(r, gen->getPixels(info, bm.getPixels(), bm.rowBytes())); 457 compare_to_good_digest(r, codecDigest, bm); 458 459#if !defined(SK_PNG_DISABLE_TESTS) && defined(SK_ENABLE_ANDROID_UTILS) 460 // Test using FrontBufferedStream, as Android does 461 auto bufferedStream = android::skia::FrontBufferedStream::Make( 462 SkMemoryStream::Make(std::move(fullData)), SkCodec::MinBufferedBytesNeeded()); 463 REPORTER_ASSERT(r, bufferedStream); 464 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(std::move(bufferedStream)); 465 REPORTER_ASSERT(r, codec); 466 if (codec) { 467 test_info(r, codec.get(), info, SkCodec::kSuccess, &codecDigest); 468 } 469#endif 470 } 471} 472 473static void check(skiatest::Reporter* r, 474 const char path[], 475 SkISize size, 476 bool supportsScanlineDecoding, 477 bool supportsSubsetDecoding, 478 bool supportsIncomplete, 479 bool supportsNewScanlineDecoding = false) { 480 // If we're testing incomplete decodes, let's run the same test on full decodes. 481 if (supportsIncomplete) { 482 check(r, path, size, supportsScanlineDecoding, supportsSubsetDecoding, 483 /*supportsIncomplete=*/false, supportsNewScanlineDecoding); 484 } 485 486 // Initialize a codec with a data stream. 487 std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); 488 if (!stream) { 489 return; 490 } 491 492 std::unique_ptr<SkCodec> codec; 493 if (supportsIncomplete) { 494 size_t length = stream->getLength(); 495 codec = SkCodec::MakeFromData(SkData::MakeFromStream(stream.get(), 2 * length / 3)); 496 } else { 497 codec = SkCodec::MakeFromStream(std::move(stream)); 498 } 499 if (!codec) { 500 ERRORF(r, "Unable to decode '%s'", path); 501 return; 502 } 503 504 const SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); 505 506 // Run tests with this codec. 507 SkMD5::Digest codecDigest; 508 check_scanline_decode(r, codec.get(), &codecDigest, info, path, size, supportsScanlineDecoding, 509 supportsIncomplete, supportsNewScanlineDecoding); 510 511 check_subset_decode(r, codec.get(), info, size, supportsSubsetDecoding, supportsIncomplete); 512 513 check_android_codec(r, std::move(codec), codecDigest, info, path, size, 514 supportsScanlineDecoding, supportsSubsetDecoding, supportsIncomplete, 515 supportsNewScanlineDecoding); 516 517 check_codec_image_generator(r, codecDigest, info, path, supportsIncomplete); 518} 519 520DEF_TEST(Codec_wbmp, r) { 521 check(r, "images/mandrill.wbmp", SkISize::Make(512, 512), true, false, true); 522} 523 524DEF_TEST(Codec_webp, r) { 525 check(r, "images/baby_tux.webp", SkISize::Make(386, 395), false, true, true); 526 check(r, "images/color_wheel.webp", SkISize::Make(128, 128), false, true, true); 527 check(r, "images/yellow_rose.webp", SkISize::Make(400, 301), false, true, true); 528} 529 530DEF_TEST(Codec_bmp, r) { 531 check(r, "images/randPixels.bmp", SkISize::Make(8, 8), true, false, true); 532 check(r, "images/rle.bmp", SkISize::Make(320, 240), true, false, true); 533} 534 535DEF_TEST(Codec_ico, r) { 536 // FIXME: We are not ready to test incomplete ICOs 537 // These two tests examine interestingly different behavior: 538 // Decodes an embedded BMP image 539 check(r, "images/color_wheel.ico", SkISize::Make(128, 128), true, false, false); 540 // Decodes an embedded PNG image 541 check(r, "images/google_chrome.ico", SkISize::Make(256, 256), false, false, false, true); 542} 543 544DEF_TEST(Codec_gif, r) { 545 check(r, "images/box.gif", SkISize::Make(200, 55), false, false, true, true); 546 check(r, "images/color_wheel.gif", SkISize::Make(128, 128), false, false, true, true); 547 // randPixels.gif is too small to test incomplete 548 check(r, "images/randPixels.gif", SkISize::Make(8, 8), false, false, false, true); 549} 550 551DEF_TEST(Codec_jpg, r) { 552 check(r, "images/CMYK.jpg", SkISize::Make(642, 516), true, false, true); 553 check(r, "images/color_wheel.jpg", SkISize::Make(128, 128), true, false, true); 554 // grayscale.jpg is too small to test incomplete 555 check(r, "images/grayscale.jpg", SkISize::Make(128, 128), true, false, false); 556 check(r, "images/mandrill_512_q075.jpg", SkISize::Make(512, 512), true, false, true); 557 // randPixels.jpg is too small to test incomplete 558 check(r, "images/randPixels.jpg", SkISize::Make(8, 8), true, false, false); 559} 560 561DEF_TEST(Codec_png, r) { 562 check(r, "images/arrow.png", SkISize::Make(187, 312), false, false, true, true); 563 check(r, "images/baby_tux.png", SkISize::Make(240, 246), false, false, true, true); 564 check(r, "images/color_wheel.png", SkISize::Make(128, 128), false, false, true, true); 565 // half-transparent-white-pixel.png is too small to test incomplete 566 check(r, "images/half-transparent-white-pixel.png", SkISize::Make(1, 1), false, false, false, true); 567 check(r, "images/mandrill_128.png", SkISize::Make(128, 128), false, false, true, true); 568 // mandrill_16.png is too small (relative to embedded sRGB profile) to test incomplete 569 check(r, "images/mandrill_16.png", SkISize::Make(16, 16), false, false, false, true); 570 check(r, "images/mandrill_256.png", SkISize::Make(256, 256), false, false, true, true); 571 check(r, "images/mandrill_32.png", SkISize::Make(32, 32), false, false, true, true); 572 check(r, "images/mandrill_512.png", SkISize::Make(512, 512), false, false, true, true); 573 check(r, "images/mandrill_64.png", SkISize::Make(64, 64), false, false, true, true); 574 check(r, "images/plane.png", SkISize::Make(250, 126), false, false, true, true); 575 check(r, "images/plane_interlaced.png", SkISize::Make(250, 126), false, false, true, true); 576 check(r, "images/randPixels.png", SkISize::Make(8, 8), false, false, true, true); 577 check(r, "images/yellow_rose.png", SkISize::Make(400, 301), false, false, true, true); 578} 579 580// Disable RAW tests for Win32. 581#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) 582DEF_TEST(Codec_raw, r) { 583 check(r, "images/sample_1mp.dng", SkISize::Make(600, 338), false, false, false); 584 check(r, "images/sample_1mp_rotated.dng", SkISize::Make(600, 338), false, false, false); 585 check(r, "images/dng_with_preview.dng", SkISize::Make(600, 338), true, false, false); 586} 587#endif 588 589static void test_invalid_stream(skiatest::Reporter* r, const void* stream, size_t len) { 590 // Neither of these calls should return a codec. Bots should catch us if we leaked anything. 591 REPORTER_ASSERT(r, !SkCodec::MakeFromStream( 592 std::make_unique<SkMemoryStream>(stream, len, false))); 593 REPORTER_ASSERT(r, !SkAndroidCodec::MakeFromStream( 594 std::make_unique<SkMemoryStream>(stream, len, false))); 595} 596 597// Ensure that SkCodec::NewFromStream handles freeing the passed in SkStream, 598// even on failure. Test some bad streams. 599DEF_TEST(Codec_leaks, r) { 600 // No codec should claim this as their format, so this tests SkCodec::NewFromStream. 601 const char nonSupportedStream[] = "hello world"; 602 // The other strings should look like the beginning of a file type, so we'll call some 603 // internal version of NewFromStream, which must also delete the stream on failure. 604 const unsigned char emptyPng[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; 605 const unsigned char emptyJpeg[] = { 0xFF, 0xD8, 0xFF }; 606 const char emptyWebp[] = "RIFF1234WEBPVP"; 607 const char emptyBmp[] = { 'B', 'M' }; 608 const char emptyIco[] = { '\x00', '\x00', '\x01', '\x00' }; 609 const char emptyGif[] = "GIFVER"; 610 611 test_invalid_stream(r, nonSupportedStream, sizeof(nonSupportedStream)); 612 test_invalid_stream(r, emptyPng, sizeof(emptyPng)); 613 test_invalid_stream(r, emptyJpeg, sizeof(emptyJpeg)); 614 test_invalid_stream(r, emptyWebp, sizeof(emptyWebp)); 615 test_invalid_stream(r, emptyBmp, sizeof(emptyBmp)); 616 test_invalid_stream(r, emptyIco, sizeof(emptyIco)); 617 test_invalid_stream(r, emptyGif, sizeof(emptyGif)); 618} 619 620DEF_TEST(Codec_null, r) { 621 // Attempting to create an SkCodec or an SkAndroidCodec with null should not 622 // crash. 623 REPORTER_ASSERT(r, !SkCodec::MakeFromStream(nullptr)); 624 REPORTER_ASSERT(r, !SkAndroidCodec::MakeFromStream(nullptr)); 625} 626 627static void test_dimensions(skiatest::Reporter* r, const char path[]) { 628 // Create the codec from the resource file 629 std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); 630 if (!stream) { 631 return; 632 } 633 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream))); 634 if (!codec) { 635 ERRORF(r, "Unable to create codec '%s'", path); 636 return; 637 } 638 639 // Check that the decode is successful for a variety of scales 640 for (int sampleSize = 1; sampleSize < 32; sampleSize++) { 641 // Scale the output dimensions 642 SkISize scaledDims = codec->getSampledDimensions(sampleSize); 643 SkImageInfo scaledInfo = codec->getInfo() 644 .makeDimensions(scaledDims) 645 .makeColorType(kN32_SkColorType); 646 647 // Set up for the decode 648 size_t rowBytes = scaledDims.width() * sizeof(SkPMColor); 649 size_t totalBytes = scaledInfo.computeByteSize(rowBytes); 650 SkAutoTMalloc<SkPMColor> pixels(totalBytes); 651 652 SkAndroidCodec::AndroidOptions options; 653 options.fSampleSize = sampleSize; 654 SkCodec::Result result = 655 codec->getAndroidPixels(scaledInfo, pixels.get(), rowBytes, &options); 656 if (result != SkCodec::kSuccess) { 657 ERRORF(r, "Failed to decode %s with sample size %i; error: %s", path, sampleSize, 658 SkCodec::ResultToString(result)); 659 } 660 } 661} 662 663// Ensure that onGetScaledDimensions returns valid image dimensions to use for decodes 664DEF_TEST(Codec_Dimensions, r) { 665 // JPG 666 test_dimensions(r, "images/CMYK.jpg"); 667 test_dimensions(r, "images/color_wheel.jpg"); 668 test_dimensions(r, "images/grayscale.jpg"); 669 test_dimensions(r, "images/mandrill_512_q075.jpg"); 670 test_dimensions(r, "images/randPixels.jpg"); 671 672 // Decoding small images with very large scaling factors is a potential 673 // source of bugs and crashes. We disable these tests in Gold because 674 // tiny images are not very useful to look at. 675 // Here we make sure that we do not crash or access illegal memory when 676 // performing scaled decodes on small images. 677 test_dimensions(r, "images/1x1.png"); 678 test_dimensions(r, "images/2x2.png"); 679 test_dimensions(r, "images/3x3.png"); 680 test_dimensions(r, "images/3x1.png"); 681 test_dimensions(r, "images/1x1.png"); 682 test_dimensions(r, "images/16x1.png"); 683 test_dimensions(r, "images/1x16.png"); 684 test_dimensions(r, "images/mandrill_16.png"); 685 686 // RAW 687// Disable RAW tests for Win32. 688#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) 689 test_dimensions(r, "images/sample_1mp.dng"); 690 test_dimensions(r, "images/sample_1mp_rotated.dng"); 691 test_dimensions(r, "images/dng_with_preview.dng"); 692#endif 693} 694 695static void test_invalid(skiatest::Reporter* r, const char path[]) { 696 auto data = GetResourceAsData(path); 697 if (!data) { 698 ERRORF(r, "Failed to get resource %s", path); 699 return; 700 } 701 702 REPORTER_ASSERT(r, !SkCodec::MakeFromData(data)); 703} 704 705DEF_TEST(Codec_Empty, r) { 706 if (GetResourcePath().isEmpty()) { 707 return; 708 } 709 710 // Test images that should not be able to create a codec 711 test_invalid(r, "empty_images/zero-dims.gif"); 712 test_invalid(r, "empty_images/zero-embedded.ico"); 713 test_invalid(r, "empty_images/zero-width.bmp"); 714 test_invalid(r, "empty_images/zero-height.bmp"); 715 test_invalid(r, "empty_images/zero-width.jpg"); 716 test_invalid(r, "empty_images/zero-height.jpg"); 717 test_invalid(r, "empty_images/zero-width.png"); 718 test_invalid(r, "empty_images/zero-height.png"); 719 test_invalid(r, "empty_images/zero-width.wbmp"); 720 test_invalid(r, "empty_images/zero-height.wbmp"); 721 // This image is an ico with an embedded mask-bmp. This is illegal. 722 test_invalid(r, "invalid_images/mask-bmp-ico.ico"); 723 // It is illegal for a webp frame to not be fully contained by the canvas. 724 test_invalid(r, "invalid_images/invalid-offset.webp"); 725#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) 726 test_invalid(r, "empty_images/zero_height.tiff"); 727#endif 728 test_invalid(r, "invalid_images/b37623797.ico"); 729 test_invalid(r, "invalid_images/osfuzz6295.webp"); 730 test_invalid(r, "invalid_images/osfuzz6288.bmp"); 731 test_invalid(r, "invalid_images/ossfuzz6347"); 732} 733 734#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED 735 736#ifndef SK_PNG_DISABLE_TESTS // reading chunks does not work properly with older versions. 737 // It does not appear that anyone in Google3 is reading chunks. 738 739static void codex_test_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { 740 SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); 741 if (!sk_stream->write(data, len)) { 742 png_error(png_ptr, "sk_write_fn Error!"); 743 } 744} 745 746DEF_TEST(Codec_pngChunkReader, r) { 747 // Create a bitmap for hashing. Use unpremul RGBA for libpng. 748 SkBitmap bm; 749 const int w = 1; 750 const int h = 1; 751 const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, 752 kUnpremul_SkAlphaType); 753 bm.setInfo(bmInfo); 754 bm.allocPixels(); 755 bm.eraseColor(SK_ColorBLUE); 756 SkMD5::Digest goodDigest = md5(bm); 757 758 // Write to a png file. 759 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 760 REPORTER_ASSERT(r, png); 761 if (!png) { 762 return; 763 } 764 765 png_infop info = png_create_info_struct(png); 766 REPORTER_ASSERT(r, info); 767 if (!info) { 768 png_destroy_write_struct(&png, nullptr); 769 return; 770 } 771 772 if (setjmp(png_jmpbuf(png))) { 773 ERRORF(r, "failed writing png"); 774 png_destroy_write_struct(&png, &info); 775 return; 776 } 777 778 SkDynamicMemoryWStream wStream; 779 png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr); 780 781 png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8, 782 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, 783 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 784 785 // Create some chunks that match the Android framework's use. 786 static png_unknown_chunk gUnknowns[] = { 787 { "npOl", (png_byte*)"outline", sizeof("outline"), PNG_HAVE_IHDR }, 788 { "npLb", (png_byte*)"layoutBounds", sizeof("layoutBounds"), PNG_HAVE_IHDR }, 789 { "npTc", (png_byte*)"ninePatchData", sizeof("ninePatchData"), PNG_HAVE_IHDR }, 790 }; 791 792 png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"npOl\0npLb\0npTc\0", 3); 793 png_set_unknown_chunks(png, info, gUnknowns, SK_ARRAY_COUNT(gUnknowns)); 794#if PNG_LIBPNG_VER < 10600 795 /* Deal with unknown chunk location bug in 1.5.x and earlier */ 796 png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR); 797 png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR); 798#endif 799 800 png_write_info(png, info); 801 802 for (int j = 0; j < h; j++) { 803 png_bytep row = (png_bytep)(bm.getAddr(0, j)); 804 png_write_rows(png, &row, 1); 805 } 806 png_write_end(png, info); 807 png_destroy_write_struct(&png, &info); 808 809 class ChunkReader : public SkPngChunkReader { 810 public: 811 ChunkReader(skiatest::Reporter* r) 812 : fReporter(r) 813 { 814 this->reset(); 815 } 816 817 bool readChunk(const char tag[], const void* data, size_t length) override { 818 for (size_t i = 0; i < SK_ARRAY_COUNT(gUnknowns); ++i) { 819 if (!strcmp(tag, (const char*) gUnknowns[i].name)) { 820 // Tag matches. This should have been the first time we see it. 821 REPORTER_ASSERT(fReporter, !fSeen[i]); 822 fSeen[i] = true; 823 824 // Data and length should match 825 REPORTER_ASSERT(fReporter, length == gUnknowns[i].size); 826 REPORTER_ASSERT(fReporter, !strcmp((const char*) data, 827 (const char*) gUnknowns[i].data)); 828 return true; 829 } 830 } 831 ERRORF(fReporter, "Saw an unexpected unknown chunk."); 832 return true; 833 } 834 835 bool allHaveBeenSeen() { 836 bool ret = true; 837 for (auto seen : fSeen) { 838 ret &= seen; 839 } 840 return ret; 841 } 842 843 void reset() { 844 sk_bzero(fSeen, sizeof(fSeen)); 845 } 846 847 private: 848 skiatest::Reporter* fReporter; // Unowned 849 bool fSeen[3]; 850 }; 851 852 ChunkReader chunkReader(r); 853 854 // Now read the file with SkCodec. 855 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(wStream.detachAsData(), &chunkReader)); 856 REPORTER_ASSERT(r, codec); 857 if (!codec) { 858 return; 859 } 860 861 // Now compare to the original. 862 SkBitmap decodedBm; 863 decodedBm.setInfo(codec->getInfo()); 864 decodedBm.allocPixels(); 865 SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), 866 decodedBm.rowBytes()); 867 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 868 869 if (decodedBm.colorType() != bm.colorType()) { 870 SkBitmap tmp; 871 bool success = ToolUtils::copy_to(&tmp, bm.colorType(), decodedBm); 872 REPORTER_ASSERT(r, success); 873 if (!success) { 874 return; 875 } 876 877 tmp.swap(decodedBm); 878 } 879 880 compare_to_good_digest(r, goodDigest, decodedBm); 881 REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); 882 883 // Decoding again will read the chunks again. 884 chunkReader.reset(); 885 REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen()); 886 result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes()); 887 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 888 REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); 889} 890#endif // SK_PNG_DISABLE_TESTS 891#endif // PNG_READ_UNKNOWN_CHUNKS_SUPPORTED 892 893// Stream that can only peek up to a limit 894class LimitedPeekingMemStream : public SkStream { 895public: 896 LimitedPeekingMemStream(sk_sp<SkData> data, size_t limit) 897 : fStream(std::move(data)) 898 , fLimit(limit) {} 899 900 size_t peek(void* buf, size_t bytes) const override { 901 return fStream.peek(buf, std::min(bytes, fLimit)); 902 } 903 size_t read(void* buf, size_t bytes) override { 904 return fStream.read(buf, bytes); 905 } 906 bool rewind() override { 907 return fStream.rewind(); 908 } 909 bool isAtEnd() const override { 910 return fStream.isAtEnd(); 911 } 912private: 913 SkMemoryStream fStream; 914 const size_t fLimit; 915}; 916 917// Disable RAW tests for Win32. 918#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32)) 919// Test that the RawCodec works also for not asset stream. This will test the code path using 920// SkRawBufferedStream instead of SkRawAssetStream. 921DEF_TEST(Codec_raw_notseekable, r) { 922 constexpr char path[] = "images/dng_with_preview.dng"; 923 sk_sp<SkData> data(GetResourceAsData(path)); 924 if (!data) { 925 SkDebugf("Missing resource '%s'\n", path); 926 return; 927 } 928 929 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream( 930 std::make_unique<NotAssetMemStream>(std::move(data)))); 931 REPORTER_ASSERT(r, codec); 932 933 test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); 934} 935#endif 936 937// Test that even if webp_parse_header fails to peek enough, it will fall back to read() 938// + rewind() and succeed. 939DEF_TEST(Codec_webp_peek, r) { 940 constexpr char path[] = "images/baby_tux.webp"; 941 auto data = GetResourceAsData(path); 942 if (!data) { 943 SkDebugf("Missing resource '%s'\n", path); 944 return; 945 } 946 947 // The limit is less than webp needs to peek or read. 948 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream( 949 std::make_unique<LimitedPeekingMemStream>(data, 25))); 950 REPORTER_ASSERT(r, codec); 951 952 test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); 953 954 // Similarly, a stream which does not peek should still succeed. 955 codec = SkCodec::MakeFromStream(std::make_unique<LimitedPeekingMemStream>(data, 0)); 956 REPORTER_ASSERT(r, codec); 957 958 test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); 959} 960 961// SkCodec's wbmp decoder was initially unnecessarily restrictive. 962// It required the second byte to be zero. The wbmp specification allows 963// a couple of bits to be 1 (so long as they do not overlap with 0x9F). 964// Test that SkCodec now supports an image with these bits set. 965DEF_TEST(Codec_wbmp_restrictive, r) { 966 const char* path = "images/mandrill.wbmp"; 967 std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); 968 if (!stream) { 969 return; 970 } 971 972 // Modify the stream to contain a second byte with some bits set. 973 auto data = SkCopyStreamToData(stream.get()); 974 uint8_t* writeableData = static_cast<uint8_t*>(data->writable_data()); 975 writeableData[1] = static_cast<uint8_t>(~0x9F); 976 977 // SkCodec should support this. 978 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); 979 REPORTER_ASSERT(r, codec); 980 if (!codec) { 981 return; 982 } 983 test_info(r, codec.get(), codec->getInfo(), SkCodec::kSuccess, nullptr); 984} 985 986// wbmp images have a header that can be arbitrarily large, depending on the 987// size of the image. We cap the size at 65535, meaning we only need to look at 988// 8 bytes to determine whether we can read the image. This is important 989// because SkCodec only passes a limited number of bytes to SkWbmpCodec to 990// determine whether the image is a wbmp. 991DEF_TEST(Codec_wbmp_max_size, r) { 992 const unsigned char maxSizeWbmp[] = { 0x00, 0x00, // Header 993 0x83, 0xFF, 0x7F, // W: 65535 994 0x83, 0xFF, 0x7F }; // H: 65535 995 std::unique_ptr<SkStream> stream(new SkMemoryStream(maxSizeWbmp, sizeof(maxSizeWbmp), false)); 996 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream))); 997 998 REPORTER_ASSERT(r, codec); 999 if (!codec) return; 1000 1001 REPORTER_ASSERT(r, codec->getInfo().width() == 65535); 1002 REPORTER_ASSERT(r, codec->getInfo().height() == 65535); 1003 1004 // Now test an image which is too big. Any image with a larger header (i.e. 1005 // has bigger width/height) is also too big. 1006 const unsigned char tooBigWbmp[] = { 0x00, 0x00, // Header 1007 0x84, 0x80, 0x00, // W: 65536 1008 0x84, 0x80, 0x00 }; // H: 65536 1009 stream = std::make_unique<SkMemoryStream>(tooBigWbmp, sizeof(tooBigWbmp), false); 1010 codec = SkCodec::MakeFromStream(std::move(stream)); 1011 1012 REPORTER_ASSERT(r, !codec); 1013} 1014 1015DEF_TEST(Codec_jpeg_rewind, r) { 1016 const char* path = "images/mandrill_512_q075.jpg"; 1017 sk_sp<SkData> data(GetResourceAsData(path)); 1018 if (!data) { 1019 return; 1020 } 1021 1022 data = SkData::MakeSubset(data.get(), 0, data->size() / 2); 1023 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(data)); 1024 if (!codec) { 1025 ERRORF(r, "Unable to create codec '%s'.", path); 1026 return; 1027 } 1028 1029 const int width = codec->getInfo().width(); 1030 const int height = codec->getInfo().height(); 1031 size_t rowBytes = sizeof(SkPMColor) * width; 1032 SkAutoMalloc pixelStorage(height * rowBytes); 1033 1034 // Perform a sampled decode. 1035 SkAndroidCodec::AndroidOptions opts; 1036 opts.fSampleSize = 12; 1037 auto sampledInfo = codec->getInfo().makeWH(width / 12, height / 12); 1038 auto result = codec->getAndroidPixels(sampledInfo, pixelStorage.get(), rowBytes, &opts); 1039 REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result); 1040 1041 // Rewind the codec and perform a full image decode. 1042 result = codec->getPixels(codec->getInfo(), pixelStorage.get(), rowBytes); 1043 REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result); 1044 1045 // Now perform a subset decode. 1046 { 1047 opts.fSampleSize = 1; 1048 SkIRect subset = SkIRect::MakeWH(100, 100); 1049 opts.fSubset = ⊂ 1050 result = codec->getAndroidPixels(codec->getInfo().makeWH(100, 100), pixelStorage.get(), 1051 rowBytes, &opts); 1052 // Though we only have half the data, it is enough to decode this subset. 1053 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1054 } 1055 1056 // Perform another full image decode. ASAN will detect if we look at the subset when it is 1057 // out of scope. This would happen if we depend on the old state in the codec. 1058 // This tests two layers of bugs: both SkJpegCodec::readRows and SkCodec::fillIncompleteImage 1059 // used to look at the old subset. 1060 opts.fSubset = nullptr; 1061 result = codec->getAndroidPixels(codec->getInfo(), pixelStorage.get(), rowBytes, &opts); 1062 REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result); 1063} 1064 1065static void check_color_xform(skiatest::Reporter* r, const char* path) { 1066 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(GetResourceAsStream(path))); 1067 1068 SkAndroidCodec::AndroidOptions opts; 1069 opts.fSampleSize = 3; 1070 const int subsetWidth = codec->getInfo().width() / 2; 1071 const int subsetHeight = codec->getInfo().height() / 2; 1072 SkIRect subset = SkIRect::MakeWH(subsetWidth, subsetHeight); 1073 opts.fSubset = ⊂ 1074 1075 const int dstWidth = subsetWidth / opts.fSampleSize; 1076 const int dstHeight = subsetHeight / opts.fSampleSize; 1077 auto colorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB); 1078 SkImageInfo dstInfo = codec->getInfo().makeWH(dstWidth, dstHeight) 1079 .makeColorType(kN32_SkColorType) 1080 .makeColorSpace(colorSpace); 1081 1082 size_t rowBytes = dstInfo.minRowBytes(); 1083 SkAutoMalloc pixelStorage(dstInfo.computeByteSize(rowBytes)); 1084 SkCodec::Result result = codec->getAndroidPixels(dstInfo, pixelStorage.get(), rowBytes, &opts); 1085 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1086} 1087 1088DEF_TEST(Codec_ColorXform, r) { 1089 check_color_xform(r, "images/mandrill_512_q075.jpg"); 1090 check_color_xform(r, "images/mandrill_512.png"); 1091} 1092 1093static bool color_type_match(SkColorType origColorType, SkColorType codecColorType) { 1094 switch (origColorType) { 1095 case kRGBA_8888_SkColorType: 1096 case kBGRA_8888_SkColorType: 1097 return kRGBA_8888_SkColorType == codecColorType || 1098 kBGRA_8888_SkColorType == codecColorType; 1099 default: 1100 return origColorType == codecColorType; 1101 } 1102} 1103 1104static bool alpha_type_match(SkAlphaType origAlphaType, SkAlphaType codecAlphaType) { 1105 switch (origAlphaType) { 1106 case kUnpremul_SkAlphaType: 1107 case kPremul_SkAlphaType: 1108 return kUnpremul_SkAlphaType == codecAlphaType || 1109 kPremul_SkAlphaType == codecAlphaType; 1110 default: 1111 return origAlphaType == codecAlphaType; 1112 } 1113} 1114 1115static void check_round_trip(skiatest::Reporter* r, SkCodec* origCodec, const SkImageInfo& info) { 1116 SkBitmap bm1; 1117 bm1.allocPixels(info); 1118 SkCodec::Result result = origCodec->getPixels(info, bm1.getPixels(), bm1.rowBytes()); 1119 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1120 1121 // Encode the image to png. 1122 auto data = SkEncodeBitmap(bm1, SkEncodedImageFormat::kPNG, 100); 1123 1124 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); 1125 REPORTER_ASSERT(r, color_type_match(info.colorType(), codec->getInfo().colorType())); 1126 REPORTER_ASSERT(r, alpha_type_match(info.alphaType(), codec->getInfo().alphaType())); 1127 1128 SkBitmap bm2; 1129 bm2.allocPixels(info); 1130 result = codec->getPixels(info, bm2.getPixels(), bm2.rowBytes()); 1131 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1132 1133 REPORTER_ASSERT(r, md5(bm1) == md5(bm2)); 1134} 1135 1136DEF_TEST(Codec_PngRoundTrip, r) { 1137 auto codec = SkCodec::MakeFromStream(GetResourceAsStream("images/mandrill_512_q075.jpg")); 1138 1139 SkColorType colorTypesOpaque[] = { 1140 kRGB_565_SkColorType, kRGBA_8888_SkColorType, kBGRA_8888_SkColorType 1141 }; 1142 for (SkColorType colorType : colorTypesOpaque) { 1143 SkImageInfo newInfo = codec->getInfo().makeColorType(colorType); 1144 check_round_trip(r, codec.get(), newInfo); 1145 } 1146 1147 codec = SkCodec::MakeFromStream(GetResourceAsStream("images/grayscale.jpg")); 1148 check_round_trip(r, codec.get(), codec->getInfo()); 1149 1150 codec = SkCodec::MakeFromStream(GetResourceAsStream("images/yellow_rose.png")); 1151 1152 SkColorType colorTypesWithAlpha[] = { 1153 kRGBA_8888_SkColorType, kBGRA_8888_SkColorType 1154 }; 1155 SkAlphaType alphaTypes[] = { 1156 kUnpremul_SkAlphaType, kPremul_SkAlphaType 1157 }; 1158 for (SkColorType colorType : colorTypesWithAlpha) { 1159 for (SkAlphaType alphaType : alphaTypes) { 1160 // Set color space to nullptr because color correct premultiplies do not round trip. 1161 SkImageInfo newInfo = codec->getInfo().makeColorType(colorType) 1162 .makeAlphaType(alphaType) 1163 .makeColorSpace(nullptr); 1164 check_round_trip(r, codec.get(), newInfo); 1165 } 1166 } 1167 1168 codec = SkCodec::MakeFromStream(GetResourceAsStream("images/index8.png")); 1169 1170 for (SkAlphaType alphaType : alphaTypes) { 1171 SkImageInfo newInfo = codec->getInfo().makeAlphaType(alphaType) 1172 .makeColorSpace(nullptr); 1173 check_round_trip(r, codec.get(), newInfo); 1174 } 1175} 1176 1177static void test_conversion_possible(skiatest::Reporter* r, const char* path, 1178 bool supportsScanlineDecoder, 1179 bool supportsIncrementalDecoder) { 1180 std::unique_ptr<SkStream> stream(GetResourceAsStream(path)); 1181 if (!stream) { 1182 return; 1183 } 1184 1185 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream))); 1186 if (!codec) { 1187 ERRORF(r, "failed to create a codec for %s", path); 1188 return; 1189 } 1190 1191 SkImageInfo infoF16 = codec->getInfo().makeColorType(kRGBA_F16_SkColorType); 1192 1193 SkBitmap bm; 1194 bm.allocPixels(infoF16); 1195 SkCodec::Result result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes()); 1196 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1197 1198 result = codec->startScanlineDecode(infoF16); 1199 if (supportsScanlineDecoder) { 1200 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1201 } else { 1202 REPORTER_ASSERT(r, SkCodec::kUnimplemented == result 1203 || SkCodec::kSuccess == result); 1204 } 1205 1206 result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes()); 1207 if (supportsIncrementalDecoder) { 1208 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1209 } else { 1210 REPORTER_ASSERT(r, SkCodec::kUnimplemented == result 1211 || SkCodec::kSuccess == result); 1212 } 1213 1214 infoF16 = infoF16.makeColorSpace(infoF16.colorSpace()->makeLinearGamma()); 1215 result = codec->getPixels(infoF16, bm.getPixels(), bm.rowBytes()); 1216 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1217 result = codec->startScanlineDecode(infoF16); 1218 if (supportsScanlineDecoder) { 1219 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1220 } else { 1221 REPORTER_ASSERT(r, SkCodec::kUnimplemented == result); 1222 } 1223 1224 result = codec->startIncrementalDecode(infoF16, bm.getPixels(), bm.rowBytes()); 1225 if (supportsIncrementalDecoder) { 1226 REPORTER_ASSERT(r, SkCodec::kSuccess == result); 1227 } else { 1228 REPORTER_ASSERT(r, SkCodec::kUnimplemented == result); 1229 } 1230} 1231 1232DEF_TEST(Codec_F16ConversionPossible, r) { 1233 test_conversion_possible(r, "images/color_wheel.webp", false, false); 1234 test_conversion_possible(r, "images/mandrill_512_q075.jpg", true, false); 1235 test_conversion_possible(r, "images/yellow_rose.png", false, true); 1236} 1237 1238static void decode_frame(skiatest::Reporter* r, SkCodec* codec, size_t frame) { 1239 SkBitmap bm; 1240 auto info = codec->getInfo().makeColorType(kN32_SkColorType); 1241 bm.allocPixels(info); 1242 1243 SkCodec::Options opts; 1244 opts.fFrameIndex = frame; 1245 REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getPixels(info, 1246 bm.getPixels(), bm.rowBytes(), &opts)); 1247} 1248 1249// For an animated GIF, we should only read enough to decode frame 0 if the 1250// client never calls getFrameInfo and only decodes frame 0. 1251DEF_TEST(Codec_skipFullParse, r) { 1252 auto path = "images/test640x479.gif"; 1253 auto streamObj = GetResourceAsStream(path); 1254 if (!streamObj) { 1255 return; 1256 } 1257 SkStream* stream = streamObj.get(); 1258 1259 // Note that we cheat and hold on to the stream pointer, but SkCodec will 1260 // take ownership. We will not refer to the stream after the SkCodec 1261 // deletes it. 1262 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(streamObj))); 1263 if (!codec) { 1264 ERRORF(r, "Failed to create codec for %s", path); 1265 return; 1266 } 1267 1268 REPORTER_ASSERT(r, stream->hasPosition()); 1269 const size_t sizePosition = stream->getPosition(); 1270 REPORTER_ASSERT(r, stream->hasLength() && sizePosition < stream->getLength()); 1271 1272 // This should read more of the stream, but not the whole stream. 1273 decode_frame(r, codec.get(), 0); 1274 const size_t positionAfterFirstFrame = stream->getPosition(); 1275 REPORTER_ASSERT(r, positionAfterFirstFrame > sizePosition 1276 && positionAfterFirstFrame < stream->getLength()); 1277 1278 // There is more data in the stream. 1279 auto frameInfo = codec->getFrameInfo(); 1280 REPORTER_ASSERT(r, frameInfo.size() == 4); 1281 REPORTER_ASSERT(r, stream->getPosition() > positionAfterFirstFrame); 1282} 1283 1284// Only rewinds up to a limit. 1285class LimitedRewindingStream : public SkStream { 1286public: 1287 static std::unique_ptr<SkStream> Make(const char path[], size_t limit) { 1288 auto stream = GetResourceAsStream(path); 1289 if (!stream) { 1290 return nullptr; 1291 } 1292 return std::unique_ptr<SkStream>(new LimitedRewindingStream(std::move(stream), limit)); 1293 } 1294 1295 size_t read(void* buffer, size_t size) override { 1296 const size_t bytes = fStream->read(buffer, size); 1297 fPosition += bytes; 1298 return bytes; 1299 } 1300 1301 bool isAtEnd() const override { 1302 return fStream->isAtEnd(); 1303 } 1304 1305 bool rewind() override { 1306 if (fPosition <= fLimit && fStream->rewind()) { 1307 fPosition = 0; 1308 return true; 1309 } 1310 1311 return false; 1312 } 1313 1314private: 1315 std::unique_ptr<SkStream> fStream; 1316 const size_t fLimit; 1317 size_t fPosition; 1318 1319 LimitedRewindingStream(std::unique_ptr<SkStream> stream, size_t limit) 1320 : fStream(std::move(stream)) 1321 , fLimit(limit) 1322 , fPosition(0) 1323 { 1324 SkASSERT(fStream); 1325 } 1326}; 1327 1328DEF_TEST(Codec_fallBack, r) { 1329 // SkAndroidCodec needs to be able to fall back to scanline decoding 1330 // if incremental decoding does not work. Make sure this does not 1331 // require a rewind. 1332 1333 // Formats that currently do not support incremental decoding 1334 auto files = { 1335 "images/CMYK.jpg", 1336 "images/color_wheel.ico", 1337 "images/mandrill.wbmp", 1338 "images/randPixels.bmp", 1339 }; 1340 for (auto file : files) { 1341 auto stream = LimitedRewindingStream::Make(file, SkCodec::MinBufferedBytesNeeded()); 1342 if (!stream) { 1343 SkDebugf("Missing resources (%s). Set --resourcePath.\n", file); 1344 return; 1345 } 1346 1347 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream))); 1348 if (!codec) { 1349 ERRORF(r, "Failed to create codec for %s,", file); 1350 continue; 1351 } 1352 1353 SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); 1354 SkBitmap bm; 1355 bm.allocPixels(info); 1356 1357 if (SkCodec::kUnimplemented != codec->startIncrementalDecode(info, bm.getPixels(), 1358 bm.rowBytes())) { 1359 ERRORF(r, "Is scanline decoding now implemented for %s?", file); 1360 continue; 1361 } 1362 1363 // Scanline decoding should not require a rewind. 1364 SkCodec::Result result = codec->startScanlineDecode(info); 1365 if (SkCodec::kSuccess != result) { 1366 ERRORF(r, "Scanline decoding failed for %s with %i", file, result); 1367 } 1368 } 1369} 1370 1371// This test verifies that we fixed an assert statement that fired when reusing a png codec 1372// after scaling. 1373DEF_TEST(Codec_reusePng, r) { 1374 std::unique_ptr<SkStream> stream(GetResourceAsStream("images/plane.png")); 1375 if (!stream) { 1376 return; 1377 } 1378 1379 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream))); 1380 if (!codec) { 1381 ERRORF(r, "Failed to create codec\n"); 1382 return; 1383 } 1384 1385 SkAndroidCodec::AndroidOptions opts; 1386 opts.fSampleSize = 5; 1387 auto size = codec->getSampledDimensions(opts.fSampleSize); 1388 auto info = codec->getInfo().makeDimensions(size).makeColorType(kN32_SkColorType); 1389 SkBitmap bm; 1390 bm.allocPixels(info); 1391 auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts); 1392 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 1393 1394 info = codec->getInfo().makeColorType(kN32_SkColorType); 1395 bm.allocPixels(info); 1396 opts.fSampleSize = 1; 1397 result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &opts); 1398 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 1399} 1400 1401DEF_TEST(Codec_rowsDecoded, r) { 1402 auto file = "images/plane_interlaced.png"; 1403 std::unique_ptr<SkStream> stream(GetResourceAsStream(file)); 1404 if (!stream) { 1405 return; 1406 } 1407 1408 // This is enough to read the header etc, but no rows. 1409 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(SkData::MakeFromStream(stream.get(), 99))); 1410 if (!codec) { 1411 ERRORF(r, "Failed to create codec\n"); 1412 return; 1413 } 1414 1415 auto info = codec->getInfo().makeColorType(kN32_SkColorType); 1416 SkBitmap bm; 1417 bm.allocPixels(info); 1418 auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes()); 1419 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 1420 1421 // This is an arbitrary value. The important fact is that it is not zero, and rowsDecoded 1422 // should get set to zero by incrementalDecode. 1423 int rowsDecoded = 77; 1424 result = codec->incrementalDecode(&rowsDecoded); 1425 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); 1426 REPORTER_ASSERT(r, rowsDecoded == 0); 1427} 1428 1429static void test_invalid_images(skiatest::Reporter* r, const char* path, 1430 SkCodec::Result expectedResult) { 1431 auto stream = GetResourceAsStream(path); 1432 if (!stream) { 1433 return; 1434 } 1435 1436 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream))); 1437 REPORTER_ASSERT(r, codec); 1438 1439 test_info(r, codec.get(), codec->getInfo().makeColorType(kN32_SkColorType), expectedResult, 1440 nullptr); 1441} 1442 1443DEF_TEST(Codec_InvalidImages, r) { 1444 // ASAN will complain if there is an issue. 1445 test_invalid_images(r, "invalid_images/skbug5887.gif", SkCodec::kErrorInInput); 1446 test_invalid_images(r, "invalid_images/many-progressive-scans.jpg", SkCodec::kInvalidInput); 1447 test_invalid_images(r, "invalid_images/b33251605.bmp", SkCodec::kIncompleteInput); 1448 test_invalid_images(r, "invalid_images/bad_palette.png", SkCodec::kInvalidInput); 1449} 1450 1451static void test_invalid_header(skiatest::Reporter* r, const char* path) { 1452 auto data = GetResourceAsData(path); 1453 if (!data) { 1454 return; 1455 } 1456 std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(std::move(data))); 1457 if (!stream) { 1458 return; 1459 } 1460 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream))); 1461 REPORTER_ASSERT(r, !codec); 1462} 1463 1464DEF_TEST(Codec_InvalidHeader, r) { 1465 test_invalid_header(r, "invalid_images/int_overflow.ico"); 1466 1467 // These files report values that have caused problems with SkFILEStreams. 1468 // They are invalid, and should not create SkCodecs. 1469 test_invalid_header(r, "invalid_images/b33651913.bmp"); 1470 test_invalid_header(r, "invalid_images/b34778578.bmp"); 1471} 1472 1473/* 1474For the Codec_InvalidAnimated test, immediately below, 1475resources/invalid_images/skbug6046.gif is: 1476 147700000000: 4749 4638 3961 2000 0000 0000 002c ff00 GIF89a ......,.. 147800000010: 7400 0600 0000 4001 0021 f904 0a00 0000 t.....@..!...... 147900000020: 002c ff00 0000 ff00 7400 0606 0606 0601 .,......t....... 148000000030: 0021 f904 0000 0000 002c ff00 0000 ffcc .!.......,...... 148100000040: 1b36 5266 deba 543d .6Rf..T= 1482 1483It nominally contains 3 frames, but only the first one is valid. It came from a 1484fuzzer doing random mutations and copies. The breakdown: 1485 1486@000 6 bytes magic "GIF89a" 1487@006 7 bytes Logical Screen Descriptor: 0x20 0x00 ... 0x00 1488 - width = 32 1489 - height = 0 1490 - flags = 0x00 1491 - background color index, pixel aspect ratio bytes ignored 1492@00D 10 bytes Image Descriptor header: 0x2C 0xFF ... 0x40 1493 - origin_x = 255 1494 - origin_y = 116 1495 - width = 6 1496 - height = 0 1497 - flags = 0x40, interlaced 1498@017 2 bytes Image Descriptor body (pixel data): 0x01 0x00 1499 - lit_width = 1 1500 - 0x00 byte means "end of data" for this frame 1501@019 8 bytes Graphic Control Extension: 0x21 0xF9 ... 0x00 1502 - valid, but irrelevant here. 1503@021 10 bytes Image Descriptor header: 0x2C 0xFF ... 0x06 1504 - origin_x = 255 1505 - origin_y = 0 1506 - width = 255 1507 - height = 116 1508 - flags = 0x06, INVALID, 0x80 BIT ZERO IMPLIES 0x07 BITS SHOULD BE ZERO 1509@02B 14 bytes Image Descriptor body (pixel data): 0x06 0x06 ... 0x00 1510 - lit_width = 6 1511 - 0x06 precedes a 6 byte block of data 1512 - 0x04 precedes a 4 byte block of data 1513 - 0x00 byte means "end of data" for this frame 1514@039 10 bytes Image Descriptor header: 0x2C 0xFF ... 0x06 1515 - origin_x = 255 1516 - origin_y = 0 1517 - width = 52479 1518 - height = 13851 1519 - flags = 0x52, INVALID, 0x80 BIT ZERO IMPLIES 0x07 BITS SHOULD BE ZERO 1520@043 5 bytes Image Descriptor body (pixel data): 0x66 0xDE ... unexpected-EOF 1521 - lit_width = 102, INVALID, GREATER THAN 8 1522 - 0xDE precedes a 222 byte block of data, INVALIDLY TRUNCATED 1523 1524On Image Descriptor flags INVALIDITY, 1525https://www.w3.org/Graphics/GIF/spec-gif89a.txt section 20.c says that "Size of 1526Local Color Table [the low 3 bits]... should be 0 if there is no Local Color 1527Table specified [the high bit]." 1528 1529On LZW literal width (also known as Minimum Code Size) INVALIDITY, 1530https://www.w3.org/Graphics/GIF/spec-gif89a.txt Appendix F says that "Normally 1531this will be the same as the number of [palette index] bits. Because of some 1532algorithmic constraints however, black & white images which have one color bit 1533must be indicated as having a code size of 2." In practice, some GIF decoders, 1534including both the old third_party/gif code and the Wuffs GIF decoder, don't 1535enforce this "at least 2" constraint. Nonetheless, any width greater than 8 is 1536invalid, as there are only 8 bits in a byte. 1537*/ 1538 1539DEF_TEST(Codec_InvalidAnimated, r) { 1540 // ASAN will complain if there is an issue. 1541 auto path = "invalid_images/skbug6046.gif"; 1542 auto stream = GetResourceAsStream(path); 1543 if (!stream) { 1544 return; 1545 } 1546 1547 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream))); 1548 REPORTER_ASSERT(r, codec); 1549 if (!codec) { 1550 return; 1551 } 1552 1553 const auto info = codec->getInfo().makeColorType(kN32_SkColorType); 1554 SkBitmap bm; 1555 bm.allocPixels(info); 1556 1557 auto frameInfos = codec->getFrameInfo(); 1558 SkCodec::Options opts; 1559 for (int i = 0; static_cast<size_t>(i) < frameInfos.size(); i++) { 1560 opts.fFrameIndex = i; 1561 const auto reqFrame = frameInfos[i].fRequiredFrame; 1562 opts.fPriorFrame = reqFrame == i - 1 ? reqFrame : SkCodec::kNoFrame; 1563 auto result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes(), &opts); 1564 1565 if (result != SkCodec::kSuccess) { 1566 ERRORF(r, "Failed to start decoding frame %i (out of %zu) with error %i\n", i, 1567 frameInfos.size(), result); 1568 continue; 1569 } 1570 1571 codec->incrementalDecode(); 1572 } 1573} 1574 1575static void encode_format(SkDynamicMemoryWStream* stream, const SkPixmap& pixmap, 1576 SkEncodedImageFormat format) { 1577 switch (format) { 1578 case SkEncodedImageFormat::kPNG: 1579 SkPngEncoder::Encode(stream, pixmap, SkPngEncoder::Options()); 1580 break; 1581 case SkEncodedImageFormat::kJPEG: 1582 SkJpegEncoder::Encode(stream, pixmap, SkJpegEncoder::Options()); 1583 break; 1584 case SkEncodedImageFormat::kWEBP: 1585 SkWebpEncoder::Encode(stream, pixmap, SkWebpEncoder::Options()); 1586 break; 1587 default: 1588 SkASSERT(false); 1589 break; 1590 } 1591} 1592 1593static void test_encode_icc(skiatest::Reporter* r, SkEncodedImageFormat format) { 1594 // Test with sRGB color space. 1595 SkBitmap srgbBitmap; 1596 SkImageInfo srgbInfo = SkImageInfo::MakeS32(1, 1, kOpaque_SkAlphaType); 1597 srgbBitmap.allocPixels(srgbInfo); 1598 *srgbBitmap.getAddr32(0, 0) = 0; 1599 SkPixmap pixmap; 1600 srgbBitmap.peekPixels(&pixmap); 1601 SkDynamicMemoryWStream srgbBuf; 1602 encode_format(&srgbBuf, pixmap, format); 1603 sk_sp<SkData> srgbData = srgbBuf.detachAsData(); 1604 std::unique_ptr<SkCodec> srgbCodec(SkCodec::MakeFromData(srgbData)); 1605 REPORTER_ASSERT(r, srgbCodec->getInfo().colorSpace() == sk_srgb_singleton()); 1606 1607 // Test with P3 color space. 1608 SkDynamicMemoryWStream p3Buf; 1609 sk_sp<SkColorSpace> p3 = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3); 1610 pixmap.setColorSpace(p3); 1611 encode_format(&p3Buf, pixmap, format); 1612 sk_sp<SkData> p3Data = p3Buf.detachAsData(); 1613 std::unique_ptr<SkCodec> p3Codec(SkCodec::MakeFromData(p3Data)); 1614 REPORTER_ASSERT(r, p3Codec->getInfo().colorSpace()->gammaCloseToSRGB()); 1615 skcms_Matrix3x3 mat0, mat1; 1616 bool success = p3->toXYZD50(&mat0); 1617 REPORTER_ASSERT(r, success); 1618 success = p3Codec->getInfo().colorSpace()->toXYZD50(&mat1); 1619 REPORTER_ASSERT(r, success); 1620 1621 for (int i = 0; i < 3; i++) { 1622 for (int j = 0; j < 3; j++) { 1623 REPORTER_ASSERT(r, color_space_almost_equal(mat0.vals[i][j], mat1.vals[i][j])); 1624 } 1625 } 1626} 1627 1628DEF_TEST(Codec_EncodeICC, r) { 1629 test_encode_icc(r, SkEncodedImageFormat::kPNG); 1630 test_encode_icc(r, SkEncodedImageFormat::kJPEG); 1631 test_encode_icc(r, SkEncodedImageFormat::kWEBP); 1632} 1633 1634DEF_TEST(Codec_webp_rowsDecoded, r) { 1635 const char* path = "images/baby_tux.webp"; 1636 sk_sp<SkData> data(GetResourceAsData(path)); 1637 if (!data) { 1638 return; 1639 } 1640 1641 // Truncate this file so that the header is available but no rows can be 1642 // decoded. This should create a codec but fail to decode. 1643 size_t truncatedSize = 5000; 1644 sk_sp<SkData> subset = SkData::MakeSubset(data.get(), 0, truncatedSize); 1645 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(std::move(subset)); 1646 if (!codec) { 1647 ERRORF(r, "Failed to create a codec for %s truncated to only %lu bytes", 1648 path, truncatedSize); 1649 return; 1650 } 1651 1652 test_info(r, codec.get(), codec->getInfo(), SkCodec::kInvalidInput, nullptr); 1653} 1654 1655/* 1656For the Codec_ossfuzz6274 test, immediately below, 1657resources/invalid_images/ossfuzz6274.gif is: 1658 165900000000: 4749 4638 3961 2000 2000 f120 2020 2020 GIF89a . .. 166000000010: 2020 2020 2020 2020 2021 f903 ff20 2020 !... 166100000020: 002c 0000 0000 2000 2000 2000 00 .,.... . . .. 1662 1663@000 6 bytes magic "GIF89a" 1664@006 7 bytes Logical Screen Descriptor: 0x20 0x00 ... 0x00 1665 - width = 32 1666 - height = 32 1667 - flags = 0xF1, global color table, 4 RGB entries 1668 - background color index, pixel aspect ratio bytes ignored 1669@00D 12 bytes Color Table: 0x20 0x20 ... 0x20 1670@019 20 bytes Graphic Control Extension: 0x21 0xF9 ... unexpected-EOF 1671 - 0x03 precedes a 3 byte block of data, INVALID, MUST BE 4 1672 - 0x20 precedes a 32 byte block of data, INVALIDly truncated 1673 1674https://www.w3.org/Graphics/GIF/spec-gif89a.txt section 23.c says that the 1675block size (for an 0x21 0xF9 Graphic Control Extension) must be "the fixed 1676value 4". 1677*/ 1678 1679DEF_TEST(Codec_ossfuzz6274, r) { 1680 if (GetResourcePath().isEmpty()) { 1681 return; 1682 } 1683 1684 const char* file = "invalid_images/ossfuzz6274.gif"; 1685 auto image = GetResourceAsImage(file); 1686 1687#ifdef SK_HAS_WUFFS_LIBRARY 1688 // We are transitioning from an old GIF implementation to a new (Wuffs) GIF 1689 // implementation. 1690 // 1691 // This test (without SK_HAS_WUFFS_LIBRARY) is overly specific to the old 1692 // implementation. In the new implementation, the MakeFromStream factory 1693 // method returns a nullptr SkImage*, instead of returning a non-null but 1694 // otherwise all-transparent SkImage*. 1695 // 1696 // Either way, the end-to-end result is the same - the source input is 1697 // rejected as an invalid GIF image - but the two implementations differ in 1698 // how that's represented. 1699 // 1700 // Once the transition is complete, we can remove the #ifdef and delete the 1701 // rest of the test function. 1702 // 1703 // See Codec_GifTruncated3 for the equivalent of the rest of the test 1704 // function, on different (but still truncated) source data. 1705 if (image) { 1706 ERRORF(r, "Invalid data gave non-nullptr image"); 1707 } 1708 return; 1709#endif 1710 1711 if (!image) { 1712 ERRORF(r, "Missing %s", file); 1713 return; 1714 } 1715 1716 REPORTER_ASSERT(r, image->width() == 32); 1717 REPORTER_ASSERT(r, image->height() == 32); 1718 1719 SkBitmap bm; 1720 if (!bm.tryAllocPixels(SkImageInfo::MakeN32Premul(32, 32))) { 1721 ERRORF(r, "Failed to allocate pixels"); 1722 return; 1723 } 1724 1725 bm.eraseColor(SK_ColorTRANSPARENT); 1726 1727 SkCanvas canvas(bm); 1728 canvas.drawImage(image, 0, 0); 1729 1730 for (int i = 0; i < image->width(); ++i) 1731 for (int j = 0; j < image->height(); ++j) { 1732 SkColor actual = SkUnPreMultiply::PMColorToColor(*bm.getAddr32(i, j)); 1733 if (actual != SK_ColorTRANSPARENT) { 1734 ERRORF(r, "did not initialize pixels! %i, %i is %x", i, j, actual); 1735 } 1736 } 1737} 1738 1739DEF_TEST(Codec_78329453, r) { 1740 if (GetResourcePath().isEmpty()) { 1741 return; 1742 } 1743 1744 const char* file = "images/b78329453.jpeg"; 1745 auto data = GetResourceAsData(file); 1746 if (!data) { 1747 ERRORF(r, "Missing %s", file); 1748 return; 1749 } 1750 1751 auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data)); 1752 if (!codec) { 1753 ERRORF(r, "failed to create codec from %s", file); 1754 return; 1755 } 1756 1757 // A bug in jpeg_skip_scanlines resulted in an infinite loop for this specific 1758 // sample size on this image. Other sample sizes could have had the same result, 1759 // but the ones tested by DM happen to not. 1760 constexpr int kSampleSize = 19; 1761 const auto size = codec->getSampledDimensions(kSampleSize); 1762 auto info = codec->getInfo().makeDimensions(size); 1763 SkBitmap bm; 1764 bm.allocPixels(info); 1765 bm.eraseColor(SK_ColorTRANSPARENT); 1766 1767 SkAndroidCodec::AndroidOptions options; 1768 options.fSampleSize = kSampleSize; 1769 auto result = codec->getAndroidPixels(info, bm.getPixels(), bm.rowBytes(), &options); 1770 if (result != SkCodec::kSuccess) { 1771 ERRORF(r, "failed to decode with error %s", SkCodec::ResultToString(result)); 1772 } 1773} 1774 1775DEF_TEST(Codec_A8, r) { 1776 if (GetResourcePath().isEmpty()) { 1777 return; 1778 } 1779 1780 const char* file = "images/mandrill_cmyk.jpg"; 1781 auto data = GetResourceAsData(file); 1782 if (!data) { 1783 ERRORF(r, "missing %s", file); 1784 return; 1785 } 1786 1787 auto codec = SkCodec::MakeFromData(std::move(data)); 1788 auto info = codec->getInfo().makeColorType(kAlpha_8_SkColorType); 1789 SkBitmap bm; 1790 bm.allocPixels(info); 1791 REPORTER_ASSERT(r, codec->getPixels(bm.pixmap()) == SkCodec::kInvalidConversion); 1792} 1793 1794DEF_TEST(Codec_crbug807324, r) { 1795 if (GetResourcePath().isEmpty()) { 1796 return; 1797 } 1798 1799 const char* file = "images/crbug807324.png"; 1800 auto image = GetResourceAsImage(file); 1801 if (!image) { 1802 ERRORF(r, "Missing %s", file); 1803 return; 1804 } 1805 1806 const int kWidth = image->width(); 1807 const int kHeight = image->height(); 1808 1809 SkBitmap bm; 1810 if (!bm.tryAllocPixels(SkImageInfo::MakeN32Premul(kWidth, kHeight))) { 1811 ERRORF(r, "Could not allocate pixels (%i x %i)", kWidth, kHeight); 1812 return; 1813 } 1814 1815 bm.eraseColor(SK_ColorTRANSPARENT); 1816 1817 SkCanvas canvas(bm); 1818 canvas.drawImage(image, 0, 0); 1819 1820 for (int i = 0; i < kWidth; ++i) 1821 for (int j = 0; j < kHeight; ++j) { 1822 if (*bm.getAddr32(i, j) == SK_ColorTRANSPARENT) { 1823 ERRORF(r, "image should not be transparent! %i, %i is 0", i, j); 1824 return; 1825 } 1826 } 1827} 1828 1829DEF_TEST(Codec_F16_noColorSpace, r) { 1830 const char* path = "images/color_wheel.png"; 1831 auto data = GetResourceAsData(path); 1832 if (!data) { 1833 return; 1834 } 1835 1836 auto codec = SkCodec::MakeFromData(std::move(data)); 1837 SkImageInfo info = codec->getInfo().makeColorType(kRGBA_F16_SkColorType) 1838 .makeColorSpace(nullptr); 1839 test_info(r, codec.get(), info, SkCodec::kSuccess, nullptr); 1840} 1841 1842// These test images have ICC profiles that do not map to an SkColorSpace. 1843// Verify that decoding them with a null destination space does not perform 1844// color space transformations. 1845DEF_TEST(Codec_noConversion, r) { 1846 const struct Rec { 1847 const char* name; 1848 SkColor color; 1849 } recs[] = { 1850 { "images/cmyk_yellow_224_224_32.jpg", 0xFFD8FC04 }, 1851 { "images/wide_gamut_yellow_224_224_64.jpeg",0xFFE0E040 }, 1852 }; 1853 1854 for (const auto& rec : recs) { 1855 auto data = GetResourceAsData(rec.name); 1856 if (!data) { 1857 continue; 1858 } 1859 1860 auto codec = SkCodec::MakeFromData(std::move(data)); 1861 if (!codec) { 1862 ERRORF(r, "Failed to create a codec from %s", rec.name); 1863 continue; 1864 } 1865 1866 const auto* profile = codec->getICCProfile(); 1867 if (!profile) { 1868 ERRORF(r, "Expected %s to have a profile", rec.name); 1869 continue; 1870 } 1871 1872 auto cs = SkColorSpace::Make(*profile); 1873 REPORTER_ASSERT(r, !cs.get()); 1874 1875 SkImageInfo info = codec->getInfo().makeColorSpace(nullptr); 1876 SkBitmap bm; 1877 bm.allocPixels(info); 1878 if (codec->getPixels(info, bm.getPixels(), bm.rowBytes()) != SkCodec::kSuccess) { 1879 ERRORF(r, "Failed to decode %s", rec.name); 1880 continue; 1881 } 1882 REPORTER_ASSERT(r, bm.getColor(0, 0) == rec.color); 1883 } 1884} 1885