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 18static 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 28static 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 43static 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 54static 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} 67static 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} 86static 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} 95static 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 127static 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*/ 151DEF_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::NewFromData(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. 227DEF_TEST(Gif_Sampled, r) { 228 std::unique_ptr<SkFILEStream> stream( 229 new SkFILEStream(GetResourcePath("test640x479.gif").c_str())); 230 REPORTER_ASSERT(r, stream->isValid()); 231 if (!stream->isValid()) { 232 return; 233 } 234 235 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(stream.release())); 236 REPORTER_ASSERT(r, codec); 237 if (!codec) { 238 return; 239 } 240 241 SkAndroidCodec::AndroidOptions options; 242 options.fSampleSize = 4; 243 244 SkBitmap bm; 245 bm.allocPixels(codec->getInfo()); 246 const SkCodec::Result result = codec->getAndroidPixels(codec->getInfo(), bm.getPixels(), 247 bm.rowBytes(), &options); 248 REPORTER_ASSERT(r, result == SkCodec::kSuccess); 249} 250 251// If a GIF file is truncated before the header for the first image is defined, 252// we should not create an SkCodec. 253DEF_TEST(Codec_GifTruncated, r) { 254 sk_sp<SkData> data(GetResourceAsData("test640x479.gif")); 255 if (!data) { 256 return; 257 } 258 259 // This is right before the header for the first image. 260 data = SkData::MakeSubset(data.get(), 0, 446); 261 std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); 262 REPORTER_ASSERT(r, !codec); 263} 264 265DEF_TEST(Codec_GifTruncated2, r) { 266 sk_sp<SkData> data(GetResourceAsData("box.gif")); 267 if (!data) { 268 return; 269 } 270 271 // This is after the header, but before the color table. 272 data = SkData::MakeSubset(data.get(), 0, 23); 273 std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data)); 274 if (!codec) { 275 ERRORF(r, "Failed to create codec with partial data"); 276 return; 277 } 278 279 // Although we correctly created a codec, no frame is 280 // complete enough that it has its metadata. Returning 0 281 // ensures that Chromium will not try to create a frame 282 // too early. 283 REPORTER_ASSERT(r, codec->getFrameCount() == 0); 284} 285