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 "CodecPriv.h" 9 #include "Resources.h" 10 #include "SkAndroidCodec.h" 11 #include "SkBitmap.h" 12 #include "SkData.h" 13 #include "SkImage.h" 14 #include "SkStream.h" 15 #include "SkTypes.h" 16 #include "Test.h" 17 18 static unsigned char gGIFData[] = { 19 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x03, 0x00, 0x03, 0x00, 0xe3, 0x08, 20 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 21 0xff, 0x80, 0x80, 0x80, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 22 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 23 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 24 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x04, 25 0x07, 0x50, 0x1c, 0x43, 0x40, 0x41, 0x23, 0x44, 0x00, 0x3b 26 }; 27 28 static unsigned char gGIFDataNoColormap[] = { 29 // Header 30 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 31 // Screen descriptor 32 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 33 // Graphics control extension 34 0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00, 0x01, 0x00, 35 // Image descriptor 36 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 37 // Image data 38 0x02, 0x02, 0x4c, 0x01, 0x00, 39 // Trailer 40 0x3b 41 }; 42 43 static unsigned char gInterlacedGIF[] = { 44 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x09, 0x00, 0x09, 0x00, 0xe3, 0x08, 0x00, 45 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x80, 46 0x80, 0x80, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 47 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 48 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 49 0x00, 0x09, 0x00, 0x09, 0x00, 0x40, 0x04, 0x1b, 0x50, 0x1c, 0x23, 0xe9, 0x44, 50 0x23, 0x60, 0x9d, 0x09, 0x28, 0x1e, 0xf8, 0x6d, 0x64, 0x56, 0x9d, 0x53, 0xa8, 51 0x7e, 0xa8, 0x65, 0x94, 0x5c, 0xb0, 0x8a, 0x45, 0x04, 0x00, 0x3b 52 }; 53 54 static void test_gif_data_no_colormap(skiatest::Reporter* r, 55 void* data, 56 size_t size) { 57 SkBitmap bm; 58 bool imageDecodeSuccess = decode_memory(data, size, &bm); 59 REPORTER_ASSERT(r, imageDecodeSuccess); 60 REPORTER_ASSERT(r, bm.width() == 1); 61 REPORTER_ASSERT(r, bm.height() == 1); 62 REPORTER_ASSERT(r, !(bm.empty())); 63 if (!(bm.empty())) { 64 REPORTER_ASSERT(r, bm.getColor(0, 0) == 0x00000000); 65 } 66 } 67 static void test_gif_data(skiatest::Reporter* r, void* data, size_t size) { 68 SkBitmap bm; 69 bool imageDecodeSuccess = decode_memory(data, size, &bm); 70 REPORTER_ASSERT(r, imageDecodeSuccess); 71 REPORTER_ASSERT(r, bm.width() == 3); 72 REPORTER_ASSERT(r, bm.height() == 3); 73 REPORTER_ASSERT(r, !(bm.empty())); 74 if (!(bm.empty())) { 75 REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000); 76 REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00); 77 REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff); 78 REPORTER_ASSERT(r, bm.getColor(0, 1) == 0xff808080); 79 REPORTER_ASSERT(r, bm.getColor(1, 1) == 0xff000000); 80 REPORTER_ASSERT(r, bm.getColor(2, 1) == 0xff00ff00); 81 REPORTER_ASSERT(r, bm.getColor(0, 2) == 0xffffffff); 82 REPORTER_ASSERT(r, bm.getColor(1, 2) == 0xffff00ff); 83 REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff); 84 } 85 } 86 static void test_gif_data_dims(skiatest::Reporter* r, void* data, size_t size, int width, 87 int height) { 88 SkBitmap bm; 89 bool imageDecodeSuccess = decode_memory(data, size, &bm); 90 REPORTER_ASSERT(r, imageDecodeSuccess); 91 REPORTER_ASSERT(r, bm.width() == width); 92 REPORTER_ASSERT(r, bm.height() == height); 93 REPORTER_ASSERT(r, !(bm.empty())); 94 } 95 static void test_interlaced_gif_data(skiatest::Reporter* r, 96 void* data, 97 size_t size) { 98 SkBitmap bm; 99 bool imageDecodeSuccess = decode_memory(data, size, &bm); 100 REPORTER_ASSERT(r, imageDecodeSuccess); 101 REPORTER_ASSERT(r, bm.width() == 9); 102 REPORTER_ASSERT(r, bm.height() == 9); 103 REPORTER_ASSERT(r, !(bm.empty())); 104 if (!(bm.empty())) { 105 REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000); 106 REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00); 107 REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff); 108 109 REPORTER_ASSERT(r, bm.getColor(0, 2) == 0xffffffff); 110 REPORTER_ASSERT(r, bm.getColor(1, 2) == 0xffff00ff); 111 REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff); 112 113 REPORTER_ASSERT(r, bm.getColor(0, 4) == 0xff808080); 114 REPORTER_ASSERT(r, bm.getColor(1, 4) == 0xff000000); 115 REPORTER_ASSERT(r, bm.getColor(2, 4) == 0xff00ff00); 116 117 REPORTER_ASSERT(r, bm.getColor(0, 6) == 0xffff0000); 118 REPORTER_ASSERT(r, bm.getColor(1, 6) == 0xffffff00); 119 REPORTER_ASSERT(r, bm.getColor(2, 6) == 0xff00ffff); 120 121 REPORTER_ASSERT(r, bm.getColor(0, 8) == 0xffffffff); 122 REPORTER_ASSERT(r, bm.getColor(1, 8) == 0xffff00ff); 123 REPORTER_ASSERT(r, bm.getColor(2, 8) == 0xff0000ff); 124 } 125 } 126 127 static void test_gif_data_short(skiatest::Reporter* r, 128 void* data, 129 size_t size) { 130 SkBitmap bm; 131 bool imageDecodeSuccess = decode_memory(data, size, &bm); 132 REPORTER_ASSERT(r, imageDecodeSuccess); 133 REPORTER_ASSERT(r, bm.width() == 3); 134 REPORTER_ASSERT(r, bm.height() == 3); 135 REPORTER_ASSERT(r, !(bm.empty())); 136 if (!(bm.empty())) { 137 REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000); 138 REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00); 139 REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff); 140 REPORTER_ASSERT(r, bm.getColor(0, 1) == 0xff808080); 141 REPORTER_ASSERT(r, bm.getColor(1, 1) == 0xff000000); 142 REPORTER_ASSERT(r, bm.getColor(2, 1) == 0xff00ff00); 143 } 144 } 145 146 /** 147 This test will test the ability of the SkCodec to deal with 148 GIF files which have been mangled somehow. We want to display as 149 much of the GIF as possible. 150 */ 151 DEF_TEST(Gif, reporter) { 152 // test perfectly good images. 153 test_gif_data(reporter, static_cast<void *>(gGIFData), sizeof(gGIFData)); 154 test_interlaced_gif_data(reporter, static_cast<void *>(gInterlacedGIF), 155 sizeof(gInterlacedGIF)); 156 157 unsigned char badData[sizeof(gGIFData)]; 158 159 memcpy(badData, gGIFData, sizeof(gGIFData)); 160 badData[6] = 0x01; // image too wide 161 test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData)); 162 // "libgif warning [image too wide, expanding output to size]" 163 164 memcpy(badData, gGIFData, sizeof(gGIFData)); 165 badData[8] = 0x01; // image too tall 166 test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData)); 167 // "libgif warning [image too tall, expanding output to size]" 168 169 memcpy(badData, gGIFData, sizeof(gGIFData)); 170 badData[62] = 0x01; // image shifted right 171 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 4, 3); 172 173 memcpy(badData, gGIFData, sizeof(gGIFData)); 174 badData[64] = 0x01; // image shifted down 175 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 4); 176 177 memcpy(badData, gGIFData, sizeof(gGIFData)); 178 badData[62] = 0xff; // image shifted right 179 badData[63] = 0xff; 180 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3 + 0xFFFF, 3); 181 182 memcpy(badData, gGIFData, sizeof(gGIFData)); 183 badData[64] = 0xff; // image shifted down 184 badData[65] = 0xff; 185 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 3 + 0xFFFF); 186 187 test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap), 188 sizeof(gGIFDataNoColormap)); 189 190 // Since there is no color map, we do not even need to parse the image data 191 // to know that we should draw transparent. Truncate the file before the 192 // data. This should still succeed. 193 test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap), 31); 194 195 // Likewise, incremental decoding should succeed here. 196 { 197 sk_sp<SkData> data = SkData::MakeWithoutCopy(gGIFDataNoColormap, 31); 198 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); 199 REPORTER_ASSERT(reporter, codec); 200 if (codec) { 201 auto info = codec->getInfo().makeColorType(kN32_SkColorType); 202 SkBitmap bm; 203 bm.allocPixels(info); 204 REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->startIncrementalDecode( 205 info, bm.getPixels(), bm.rowBytes())); 206 REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->incrementalDecode()); 207 REPORTER_ASSERT(reporter, bm.width() == 1); 208 REPORTER_ASSERT(reporter, bm.height() == 1); 209 REPORTER_ASSERT(reporter, !(bm.empty())); 210 if (!(bm.empty())) { 211 REPORTER_ASSERT(reporter, bm.getColor(0, 0) == 0x00000000); 212 } 213 } 214 } 215 216 // test short Gif. 80 is missing a few bytes. 217 test_gif_data_short(reporter, static_cast<void *>(gGIFData), 80); 218 // "libgif warning [DGifGetLine]" 219 220 test_interlaced_gif_data(reporter, static_cast<void *>(gInterlacedGIF), 221 100); // 100 is missing a few bytes 222 // "libgif warning [interlace DGifGetLine]" 223 } 224 225 // Regression test for decoding a gif image with sampleSize of 4, which was 226 // previously crashing. 227 DEF_TEST(Gif_Sampled, r) { 228 auto data = GetResourceAsData("images/test640x479.gif"); 229 REPORTER_ASSERT(r, data); 230 if (!data) { 231 return; 232 } 233 std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(std::move(data))); 234 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream))); 235 REPORTER_ASSERT(r, codec); 236 if (!codec) { 237 return; 238 } 239 240 SkAndroidCodec::AndroidOptions options; 241 options.fSampleSize = 4; 242 243 SkBitmap bm; 244 bm.allocPixels(codec->getInfo()); 245 const SkCodec::Result result = codec->getAndroidPixels(codec->getInfo(), bm.getPixels(), 246 bm.rowBytes(), &options); 247 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 248 } 249 250 // If a GIF file is truncated before the header for the first image is defined, 251 // we should not create an SkCodec. 252 DEF_TEST(Codec_GifTruncated, r) { 253 sk_sp<SkData> data(GetResourceAsData("images/test640x479.gif")); 254 if (!data) { 255 return; 256 } 257 258 // This is right before the header for the first image. 259 data = SkData::MakeSubset(data.get(), 0, 446); 260 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); 261 REPORTER_ASSERT(r, !codec); 262 } 263 264 DEF_TEST(Codec_GifTruncated2, r) { 265 sk_sp<SkData> data(GetResourceAsData("images/box.gif")); 266 if (!data) { 267 return; 268 } 269 270 // This is after the header, but before the color table. 271 data = SkData::MakeSubset(data.get(), 0, 23); 272 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); 273 if (!codec) { 274 ERRORF(r, "Failed to create codec with partial data"); 275 return; 276 } 277 278 // Although we correctly created a codec, no frame is 279 // complete enough that it has its metadata. Returning 0 280 // ensures that Chromium will not try to create a frame 281 // too early. 282 REPORTER_ASSERT(r, codec->getFrameCount() == 0); 283 } 284