1 /* 2 * Copyright 2013 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 "include/codec/SkAndroidCodec.h" 9 #include "include/core/SkBitmap.h" 10 #include "include/core/SkCanvas.h" 11 #include "include/core/SkData.h" 12 #include "include/core/SkImage.h" 13 #include "include/core/SkStream.h" 14 #include "include/core/SkTypes.h" 15 #include "tests/CodecPriv.h" 16 #include "tests/Test.h" 17 #include "tools/Resources.h" 18 19 static unsigned char gGIFData[] = { 20 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x03, 0x00, 0x03, 0x00, 0xe3, 0x08, 21 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 22 0xff, 0x80, 0x80, 0x80, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 23 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 24 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 25 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x04, 26 0x07, 0x50, 0x1c, 0x43, 0x40, 0x41, 0x23, 0x44, 0x00, 0x3b 27 }; 28 29 static unsigned char gGIFDataNoColormap[] = { 30 // Header 31 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 32 // Screen descriptor 33 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 34 // Graphics control extension 35 0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00, 0x01, 0x00, 36 // Image descriptor 37 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 38 // Image data 39 0x02, 0x02, 0x4c, 0x01, 0x00, 40 // Trailer 41 0x3b 42 }; 43 44 static unsigned char gInterlacedGIF[] = { 45 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x09, 0x00, 0x09, 0x00, 0xe3, 0x08, 0x00, 46 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x80, 47 0x80, 0x80, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 48 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 49 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 50 0x00, 0x09, 0x00, 0x09, 0x00, 0x40, 0x04, 0x1b, 0x50, 0x1c, 0x23, 0xe9, 0x44, 51 0x23, 0x60, 0x9d, 0x09, 0x28, 0x1e, 0xf8, 0x6d, 0x64, 0x56, 0x9d, 0x53, 0xa8, 52 0x7e, 0xa8, 0x65, 0x94, 0x5c, 0xb0, 0x8a, 0x45, 0x04, 0x00, 0x3b 53 }; 54 test_gif_data_no_colormap(skiatest::Reporter * r,void * data,size_t size)55 static void test_gif_data_no_colormap(skiatest::Reporter* r, 56 void* data, 57 size_t size) { 58 SkBitmap bm; 59 bool imageDecodeSuccess = decode_memory(data, size, &bm); 60 REPORTER_ASSERT(r, imageDecodeSuccess); 61 REPORTER_ASSERT(r, bm.width() == 1); 62 REPORTER_ASSERT(r, bm.height() == 1); 63 REPORTER_ASSERT(r, !(bm.empty())); 64 if (!(bm.empty())) { 65 REPORTER_ASSERT(r, bm.getColor(0, 0) == 0x00000000); 66 } 67 } test_gif_data(skiatest::Reporter * r,void * data,size_t size)68 static void test_gif_data(skiatest::Reporter* r, void* data, size_t size) { 69 SkBitmap bm; 70 bool imageDecodeSuccess = decode_memory(data, size, &bm); 71 REPORTER_ASSERT(r, imageDecodeSuccess); 72 REPORTER_ASSERT(r, bm.width() == 3); 73 REPORTER_ASSERT(r, bm.height() == 3); 74 REPORTER_ASSERT(r, !(bm.empty())); 75 if (!(bm.empty())) { 76 REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000); 77 REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00); 78 REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff); 79 REPORTER_ASSERT(r, bm.getColor(0, 1) == 0xff808080); 80 REPORTER_ASSERT(r, bm.getColor(1, 1) == 0xff000000); 81 REPORTER_ASSERT(r, bm.getColor(2, 1) == 0xff00ff00); 82 REPORTER_ASSERT(r, bm.getColor(0, 2) == 0xffffffff); 83 REPORTER_ASSERT(r, bm.getColor(1, 2) == 0xffff00ff); 84 REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff); 85 } 86 } test_gif_data_dims(skiatest::Reporter * r,void * data,size_t size,int width,int height)87 static void test_gif_data_dims(skiatest::Reporter* r, void* data, size_t size, int width, 88 int height) { 89 SkBitmap bm; 90 bool imageDecodeSuccess = decode_memory(data, size, &bm); 91 REPORTER_ASSERT(r, imageDecodeSuccess); 92 REPORTER_ASSERT(r, bm.width() == width); 93 REPORTER_ASSERT(r, bm.height() == height); 94 REPORTER_ASSERT(r, !(bm.empty())); 95 } test_interlaced_gif_data(skiatest::Reporter * r,void * data,size_t size)96 static void test_interlaced_gif_data(skiatest::Reporter* r, 97 void* data, 98 size_t size) { 99 SkBitmap bm; 100 bool imageDecodeSuccess = decode_memory(data, size, &bm); 101 REPORTER_ASSERT(r, imageDecodeSuccess); 102 REPORTER_ASSERT(r, bm.width() == 9); 103 REPORTER_ASSERT(r, bm.height() == 9); 104 REPORTER_ASSERT(r, !(bm.empty())); 105 if (!(bm.empty())) { 106 REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000); 107 REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00); 108 REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff); 109 110 REPORTER_ASSERT(r, bm.getColor(0, 2) == 0xffffffff); 111 REPORTER_ASSERT(r, bm.getColor(1, 2) == 0xffff00ff); 112 REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff); 113 114 REPORTER_ASSERT(r, bm.getColor(0, 4) == 0xff808080); 115 REPORTER_ASSERT(r, bm.getColor(1, 4) == 0xff000000); 116 REPORTER_ASSERT(r, bm.getColor(2, 4) == 0xff00ff00); 117 118 REPORTER_ASSERT(r, bm.getColor(0, 6) == 0xffff0000); 119 REPORTER_ASSERT(r, bm.getColor(1, 6) == 0xffffff00); 120 REPORTER_ASSERT(r, bm.getColor(2, 6) == 0xff00ffff); 121 122 REPORTER_ASSERT(r, bm.getColor(0, 8) == 0xffffffff); 123 REPORTER_ASSERT(r, bm.getColor(1, 8) == 0xffff00ff); 124 REPORTER_ASSERT(r, bm.getColor(2, 8) == 0xff0000ff); 125 } 126 } 127 test_gif_data_short(skiatest::Reporter * r,void * data,size_t size)128 static void test_gif_data_short(skiatest::Reporter* r, 129 void* data, 130 size_t size) { 131 SkBitmap bm; 132 bool imageDecodeSuccess = decode_memory(data, size, &bm); 133 REPORTER_ASSERT(r, imageDecodeSuccess); 134 REPORTER_ASSERT(r, bm.width() == 3); 135 REPORTER_ASSERT(r, bm.height() == 3); 136 REPORTER_ASSERT(r, !(bm.empty())); 137 if (!(bm.empty())) { 138 REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000); 139 REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00); 140 REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff); 141 REPORTER_ASSERT(r, bm.getColor(0, 1) == 0xff808080); 142 REPORTER_ASSERT(r, bm.getColor(1, 1) == 0xff000000); 143 REPORTER_ASSERT(r, bm.getColor(2, 1) == 0xff00ff00); 144 } 145 } 146 147 /** 148 This test will test the ability of the SkCodec to deal with 149 GIF files which have been mangled somehow. We want to display as 150 much of the GIF as possible. 151 */ DEF_TEST(Gif,reporter)152 DEF_TEST(Gif, reporter) { 153 // test perfectly good images. 154 test_gif_data(reporter, static_cast<void *>(gGIFData), sizeof(gGIFData)); 155 test_interlaced_gif_data(reporter, static_cast<void *>(gInterlacedGIF), 156 sizeof(gInterlacedGIF)); 157 158 unsigned char badData[sizeof(gGIFData)]; 159 160 memcpy(badData, gGIFData, sizeof(gGIFData)); 161 badData[6] = 0x01; // image too wide 162 test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData)); 163 // "libgif warning [image too wide, expanding output to size]" 164 165 memcpy(badData, gGIFData, sizeof(gGIFData)); 166 badData[8] = 0x01; // image too tall 167 test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData)); 168 // "libgif warning [image too tall, expanding output to size]" 169 170 memcpy(badData, gGIFData, sizeof(gGIFData)); 171 badData[62] = 0x01; // image shifted right 172 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 4, 3); 173 174 memcpy(badData, gGIFData, sizeof(gGIFData)); 175 badData[64] = 0x01; // image shifted down 176 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 4); 177 178 memcpy(badData, gGIFData, sizeof(gGIFData)); 179 badData[62] = 0xff; // image shifted right 180 badData[63] = 0xff; 181 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3 + 0xFFFF, 3); 182 183 memcpy(badData, gGIFData, sizeof(gGIFData)); 184 badData[64] = 0xff; // image shifted down 185 badData[65] = 0xff; 186 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 3 + 0xFFFF); 187 188 test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap), 189 sizeof(gGIFDataNoColormap)); 190 191 #ifdef SK_HAS_WUFFS_LIBRARY 192 // We are transitioning from an old GIF implementation to a new (Wuffs) GIF 193 // implementation. 194 // 195 // This test (without SK_HAS_WUFFS_LIBRARY) is overly specific to the old 196 // implementation. It claims that, for invalid (truncated) input, we can 197 // still 'decode' all of the pixels because no matter what palette index 198 // each pixel is, they're all equivalently transparent. It's not obvious 199 // that this off-spec behavior is worth preserving. Are real world users 200 // decoding truncated all-transparent GIF images?? 201 // 202 // Once the transition is complete, we can remove the #ifdef and delete the 203 // #else branch. 204 #else 205 // Since there is no color map, we do not even need to parse the image data 206 // to know that we should draw transparent. Truncate the file before the 207 // data. This should still succeed. 208 test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap), 31); 209 210 // Likewise, incremental decoding should succeed here. 211 { 212 sk_sp<SkData> data = SkData::MakeWithoutCopy(gGIFDataNoColormap, 31); 213 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); 214 REPORTER_ASSERT(reporter, codec); 215 if (codec) { 216 auto info = codec->getInfo().makeColorType(kN32_SkColorType); 217 SkBitmap bm; 218 bm.allocPixels(info); 219 REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->startIncrementalDecode( 220 info, bm.getPixels(), bm.rowBytes())); 221 REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->incrementalDecode()); 222 REPORTER_ASSERT(reporter, bm.width() == 1); 223 REPORTER_ASSERT(reporter, bm.height() == 1); 224 REPORTER_ASSERT(reporter, !(bm.empty())); 225 if (!(bm.empty())) { 226 REPORTER_ASSERT(reporter, bm.getColor(0, 0) == 0x00000000); 227 } 228 } 229 } 230 #endif 231 232 // test short Gif. 80 is missing a few bytes. 233 test_gif_data_short(reporter, static_cast<void *>(gGIFData), 80); 234 // "libgif warning [DGifGetLine]" 235 236 test_interlaced_gif_data(reporter, static_cast<void *>(gInterlacedGIF), 237 100); // 100 is missing a few bytes 238 // "libgif warning [interlace DGifGetLine]" 239 } 240 241 // Regression test for decoding a gif image with sampleSize of 4, which was 242 // previously crashing. DEF_TEST(Gif_Sampled,r)243 DEF_TEST(Gif_Sampled, r) { 244 auto data = GetResourceAsData("images/test640x479.gif"); 245 REPORTER_ASSERT(r, data); 246 if (!data) { 247 return; 248 } 249 std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(std::move(data))); 250 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream))); 251 REPORTER_ASSERT(r, codec); 252 if (!codec) { 253 return; 254 } 255 256 SkAndroidCodec::AndroidOptions options; 257 options.fSampleSize = 4; 258 259 SkBitmap bm; 260 bm.allocPixels(codec->getInfo()); 261 const SkCodec::Result result = codec->getAndroidPixels(codec->getInfo(), bm.getPixels(), 262 bm.rowBytes(), &options); 263 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 264 } 265 266 // If a GIF file is truncated before the header for the first image is defined, 267 // we should not create an SkCodec. DEF_TEST(Codec_GifTruncated,r)268 DEF_TEST(Codec_GifTruncated, r) { 269 sk_sp<SkData> data(GetResourceAsData("images/test640x479.gif")); 270 if (!data) { 271 return; 272 } 273 274 // This is right before the header for the first image. 275 data = SkData::MakeSubset(data.get(), 0, 446); 276 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); 277 REPORTER_ASSERT(r, !codec); 278 } 279 280 /* 281 For the Codec_GifTruncated2 test, immediately below, 282 resources/images/box.gif's first 23 bytes are: 283 284 00000000: 4749 4638 3961 c800 3700 203f 002c 0000 GIF89a..7. ?.,.. 285 00000010: 0000 c800 3700 85 ....7.. 286 287 The breakdown: 288 289 @000 6 bytes magic "GIF89a" 290 @006 7 bytes Logical Screen Descriptor: 0xC8 0x00 ... 0x00 291 - width = 200 292 - height = 55 293 - flags = 0x20 294 - background color index, pixel aspect ratio bytes ignored 295 @00D 10 bytes Image Descriptor header: 0x2C 0x00 ... 0x85 296 - origin_x = 0 297 - origin_y = 0 298 - width = 200 299 - height = 55 300 - flags = 0x85, local color table, 64 RGB entries 301 302 In particular, 23 bytes is after the header, but before the color table. 303 */ 304 DEF_TEST(Codec_GifTruncated2,r)305 DEF_TEST(Codec_GifTruncated2, r) { 306 // Truncate box.gif at 21, 22 and 23 bytes. 307 // 308 // See also Codec_GifTruncated3 in this file, below. 309 // 310 // See also Codec_trunc in CodecAnimTest.cpp for this magic 23. 311 // 312 // See also Codec_GifPreMap in CodecPartialTest.cpp for this magic 23. 313 for (int i = 21; i < 24; i++) { 314 sk_sp<SkData> data(GetResourceAsData("images/box.gif")); 315 if (!data) { 316 return; 317 } 318 319 data = SkData::MakeSubset(data.get(), 0, i); 320 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); 321 322 if (i <= 21) { 323 if (codec) { 324 ERRORF(r, "Invalid data gave non-nullptr codec"); 325 } 326 return; 327 } 328 329 if (!codec) { 330 ERRORF(r, "Failed to create codec with partial data (truncated at %d)", i); 331 return; 332 } 333 334 #ifdef SK_HAS_WUFFS_LIBRARY 335 // We are transitioning from an old GIF implementation to a new (Wuffs) 336 // GIF implementation. 337 // 338 // The input is truncated in the Image Descriptor, before the local 339 // color table, and before (21) or after (22, 23) the first frame's 340 // XYWH (left / top / width / height) can be decoded. A detailed 341 // breakdown of those 23 bytes is in a comment above this function. 342 // 343 // With the old implementation, this test claimed that "no frame is 344 // complete enough that it has its metadata". In terms of the 345 // underlying file format, this claim is true for truncating at 21 346 // bytes, but not true for 22 or 23. 347 // 348 // At 21 bytes, both the old and new implementation's MakeFromStream 349 // factory method returns a nullptr SkCodec*, because creating a 350 // SkCodec requires knowing the image width and height (as its 351 // constructor takes an SkEncodedInfo argument), and specifically for 352 // GIF, decoding the image width and height requires decoding the first 353 // frame's XYWH, as per 354 // https://raw.githubusercontent.com/google/wuffs/master/test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt 355 // 356 // At 22 or 23 bytes, the first frame is complete enough that we can 357 // fill in all of a SkCodec::FrameInfo's fields (other than 358 // fFullyReceived). Specifically, we can fill in fRequiredFrame and 359 // fAlphaType, even though we haven't yet decoded the frame's RGB 360 // palette entries, as we do know the frame rectangle and that every 361 // palette entry is fully opaque, due to the lack of a Graphic Control 362 // Extension before the Image Descriptor. 363 // 364 // The new implementation correctly reports that the first frame's 365 // metadata is complete enough. The old implementation does not. 366 // 367 // Once the transition is complete, we can remove the #ifdef and delete 368 // the #else code. 369 REPORTER_ASSERT(r, codec->getFrameCount() == 1); 370 #else 371 // The old implementation claimed: 372 // 373 // Although we correctly created a codec, no frame is 374 // complete enough that it has its metadata. Returning 0 375 // ensures that Chromium will not try to create a frame 376 // too early. 377 REPORTER_ASSERT(r, codec->getFrameCount() == 0); 378 #endif 379 } 380 } 381 382 #ifdef SK_HAS_WUFFS_LIBRARY 383 // This tests that, after truncating the input, the pixels are still 384 // zero-initialized. If you comment out the SkSampler::Fill call in 385 // SkWuffsCodec::onStartIncrementalDecode, the test could still pass (in a 386 // standard configuration) but should fail with the MSAN memory sanitizer. DEF_TEST(Codec_GifTruncated3,r)387 DEF_TEST(Codec_GifTruncated3, r) { 388 sk_sp<SkData> data(GetResourceAsData("images/box.gif")); 389 if (!data) { 390 return; 391 } 392 393 data = SkData::MakeSubset(data.get(), 0, 23); 394 sk_sp<SkImage> image(SkImage::MakeFromEncoded(data)); 395 396 if (!image) { 397 ERRORF(r, "Missing image"); 398 return; 399 } 400 401 REPORTER_ASSERT(r, image->width() == 200); 402 REPORTER_ASSERT(r, image->height() == 55); 403 404 SkBitmap bm; 405 if (!bm.tryAllocPixels(SkImageInfo::MakeN32Premul(200, 55))) { 406 ERRORF(r, "Failed to allocate pixels"); 407 return; 408 } 409 410 bm.eraseColor(SK_ColorTRANSPARENT); 411 412 SkCanvas canvas(bm); 413 canvas.drawImage(image, 0, 0, nullptr); 414 415 for (int i = 0; i < image->width(); ++i) 416 for (int j = 0; j < image->height(); ++j) { 417 SkColor actual = SkUnPreMultiply::PMColorToColor(*bm.getAddr32(i, j)); 418 if (actual != SK_ColorTRANSPARENT) { 419 ERRORF(r, "did not initialize pixels! %i, %i is %x", i, j, actual); 420 } 421 } 422 } 423 #endif 424 DEF_TEST(Codec_gif_out_of_palette,r)425 DEF_TEST(Codec_gif_out_of_palette, r) { 426 if (GetResourcePath().isEmpty()) { 427 return; 428 } 429 430 const char* path = "images/out-of-palette.gif"; 431 auto data = GetResourceAsData(path); 432 if (!data) { 433 ERRORF(r, "failed to find %s", path); 434 return; 435 } 436 437 auto codec = SkCodec::MakeFromData(std::move(data)); 438 if (!codec) { 439 ERRORF(r, "Could not create codec from %s", path); 440 return; 441 } 442 443 SkBitmap bm; 444 bm.allocPixels(codec->getInfo()); 445 auto result = codec->getPixels(bm.pixmap()); 446 REPORTER_ASSERT(r, result == SkCodec::kSuccess, "Failed to decode %s with error %s", 447 path, SkCodec::ResultToString(result)); 448 449 struct { 450 int x; 451 int y; 452 SkColor expected; 453 } pixels[] = { 454 { 0, 0, SK_ColorBLACK }, 455 { 1, 0, SK_ColorWHITE }, 456 { 0, 1, SK_ColorTRANSPARENT }, 457 { 1, 1, SK_ColorTRANSPARENT }, 458 }; 459 for (auto& pixel : pixels) { 460 auto actual = bm.getColor(pixel.x, pixel.y); 461 REPORTER_ASSERT(r, actual == pixel.expected, 462 "pixel (%i,%i) mismatch! expected: %x actual: %x", 463 pixel.x, pixel.y, pixel.expected, actual); 464 } 465 } 466