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::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.
DEF_TEST(Gif_Sampled,r)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.
DEF_TEST(Codec_GifTruncated,r)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
DEF_TEST(Codec_GifTruncated2,r)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