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