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
test_gif_data_no_colormap(skiatest::Reporter * r,void * data,size_t size)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 }
test_gif_data(skiatest::Reporter * r,void * data,size_t size)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 }
test_gif_data_dims(skiatest::Reporter * r,void * data,size_t size,int width,int height)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 }
test_interlaced_gif_data(skiatest::Reporter * r,void * data,size_t size)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
test_gif_data_short(skiatest::Reporter * r,void * data,size_t size)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 */
DEF_TEST(Gif,reporter)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::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.
DEF_TEST(Gif_Sampled,r)227 DEF_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.
DEF_TEST(Codec_GifTruncated,r)253 DEF_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
DEF_TEST(Codec_GifTruncated2,r)265 DEF_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