• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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