• 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 "include/codec/SkAndroidCodec.h"
9 #include "include/codec/SkCodec.h"
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorType.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkStream.h"
19 #include "include/core/SkString.h"
20 #include "include/core/SkTypes.h"
21 #include "include/core/SkUnPreMultiply.h"
22 #include "tests/CodecPriv.h"
23 #include "tests/Test.h"
24 #include "tools/Resources.h"
25 
26 #include <cstring>
27 #include <initializer_list>
28 #include <memory>
29 #include <utility>
30 #include <vector>
31 
32 static unsigned char gGIFData[] = {
33   0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x03, 0x00, 0x03, 0x00, 0xe3, 0x08,
34   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00,
35   0xff, 0x80, 0x80, 0x80, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
36   0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
37   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
38   0xff, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x04,
39   0x07, 0x50, 0x1c, 0x43, 0x40, 0x41, 0x23, 0x44, 0x00, 0x3b
40 };
41 
42 static unsigned char gGIFDataNoColormap[] = {
43   // Header
44   0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
45   // Screen descriptor
46   0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
47   // Graphics control extension
48   0x21, 0xf9, 0x04, 0x01, 0x0a, 0x00, 0x01, 0x00,
49   // Image descriptor
50   0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
51   // Image data
52   0x02, 0x02, 0x4c, 0x01, 0x00,
53   // Trailer
54   0x3b
55 };
56 
57 static unsigned char gInterlacedGIF[] = {
58   0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x09, 0x00, 0x09, 0x00, 0xe3, 0x08, 0x00,
59   0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x80,
60   0x80, 0x80, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
61   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
62   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00,
63   0x00, 0x09, 0x00, 0x09, 0x00, 0x40, 0x04, 0x1b, 0x50, 0x1c, 0x23, 0xe9, 0x44,
64   0x23, 0x60, 0x9d, 0x09, 0x28, 0x1e, 0xf8, 0x6d, 0x64, 0x56, 0x9d, 0x53, 0xa8,
65   0x7e, 0xa8, 0x65, 0x94, 0x5c, 0xb0, 0x8a, 0x45, 0x04, 0x00, 0x3b
66 };
67 
test_gif_data_no_colormap(skiatest::Reporter * r,void * data,size_t size)68 static void test_gif_data_no_colormap(skiatest::Reporter* r,
69                                       void* data,
70                                       size_t size) {
71     SkBitmap bm;
72     bool imageDecodeSuccess = decode_memory(data, size, &bm);
73     REPORTER_ASSERT(r, imageDecodeSuccess);
74     REPORTER_ASSERT(r, bm.width() == 1);
75     REPORTER_ASSERT(r, bm.height() == 1);
76     REPORTER_ASSERT(r, !(bm.empty()));
77     if (!(bm.empty())) {
78         REPORTER_ASSERT(r, bm.getColor(0, 0) == 0x00000000);
79     }
80 }
test_gif_data(skiatest::Reporter * r,void * data,size_t size)81 static void test_gif_data(skiatest::Reporter* r, void* data, size_t size) {
82     SkBitmap bm;
83     bool imageDecodeSuccess = decode_memory(data, size, &bm);
84     REPORTER_ASSERT(r, imageDecodeSuccess);
85     REPORTER_ASSERT(r, bm.width() == 3);
86     REPORTER_ASSERT(r, bm.height() == 3);
87     REPORTER_ASSERT(r, !(bm.empty()));
88     if (!(bm.empty())) {
89         REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000);
90         REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00);
91         REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff);
92         REPORTER_ASSERT(r, bm.getColor(0, 1) == 0xff808080);
93         REPORTER_ASSERT(r, bm.getColor(1, 1) == 0xff000000);
94         REPORTER_ASSERT(r, bm.getColor(2, 1) == 0xff00ff00);
95         REPORTER_ASSERT(r, bm.getColor(0, 2) == 0xffffffff);
96         REPORTER_ASSERT(r, bm.getColor(1, 2) == 0xffff00ff);
97         REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff);
98     }
99 }
test_gif_data_dims(skiatest::Reporter * r,void * data,size_t size,int width,int height)100 static void test_gif_data_dims(skiatest::Reporter* r, void* data, size_t size, int width,
101         int height) {
102     SkBitmap bm;
103     bool imageDecodeSuccess = decode_memory(data, size, &bm);
104     REPORTER_ASSERT(r, imageDecodeSuccess);
105     REPORTER_ASSERT(r, bm.width() == width);
106     REPORTER_ASSERT(r, bm.height() == height);
107     REPORTER_ASSERT(r, !(bm.empty()));
108 }
test_interlaced_gif_data(skiatest::Reporter * r,void * data,size_t size)109 static void test_interlaced_gif_data(skiatest::Reporter* r,
110                                      void* data,
111                                      size_t size) {
112     SkBitmap bm;
113     bool imageDecodeSuccess = decode_memory(data, size, &bm);
114     REPORTER_ASSERT(r, imageDecodeSuccess);
115     REPORTER_ASSERT(r, bm.width() == 9);
116     REPORTER_ASSERT(r, bm.height() == 9);
117     REPORTER_ASSERT(r, !(bm.empty()));
118     if (!(bm.empty())) {
119         REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000);
120         REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00);
121         REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff);
122 
123         REPORTER_ASSERT(r, bm.getColor(0, 2) == 0xffffffff);
124         REPORTER_ASSERT(r, bm.getColor(1, 2) == 0xffff00ff);
125         REPORTER_ASSERT(r, bm.getColor(2, 2) == 0xff0000ff);
126 
127         REPORTER_ASSERT(r, bm.getColor(0, 4) == 0xff808080);
128         REPORTER_ASSERT(r, bm.getColor(1, 4) == 0xff000000);
129         REPORTER_ASSERT(r, bm.getColor(2, 4) == 0xff00ff00);
130 
131         REPORTER_ASSERT(r, bm.getColor(0, 6) == 0xffff0000);
132         REPORTER_ASSERT(r, bm.getColor(1, 6) == 0xffffff00);
133         REPORTER_ASSERT(r, bm.getColor(2, 6) == 0xff00ffff);
134 
135         REPORTER_ASSERT(r, bm.getColor(0, 8) == 0xffffffff);
136         REPORTER_ASSERT(r, bm.getColor(1, 8) == 0xffff00ff);
137         REPORTER_ASSERT(r, bm.getColor(2, 8) == 0xff0000ff);
138     }
139 }
140 
test_gif_data_short(skiatest::Reporter * r,void * data,size_t size)141 static void test_gif_data_short(skiatest::Reporter* r,
142                                 void* data,
143                                 size_t size) {
144     SkBitmap bm;
145     bool imageDecodeSuccess = decode_memory(data, size, &bm);
146     REPORTER_ASSERT(r, imageDecodeSuccess);
147     REPORTER_ASSERT(r, bm.width() == 3);
148     REPORTER_ASSERT(r, bm.height() == 3);
149     REPORTER_ASSERT(r, !(bm.empty()));
150     if (!(bm.empty())) {
151         REPORTER_ASSERT(r, bm.getColor(0, 0) == 0xffff0000);
152         REPORTER_ASSERT(r, bm.getColor(1, 0) == 0xffffff00);
153         REPORTER_ASSERT(r, bm.getColor(2, 0) == 0xff00ffff);
154         REPORTER_ASSERT(r, bm.getColor(0, 1) == 0xff808080);
155         REPORTER_ASSERT(r, bm.getColor(1, 1) == 0xff000000);
156         REPORTER_ASSERT(r, bm.getColor(2, 1) == 0xff00ff00);
157     }
158 }
159 
160 /**
161   This test will test the ability of the SkCodec to deal with
162   GIF files which have been mangled somehow.  We want to display as
163   much of the GIF as possible.
164 */
DEF_TEST(Gif,reporter)165 DEF_TEST(Gif, reporter) {
166     // test perfectly good images.
167     test_gif_data(reporter, static_cast<void *>(gGIFData), sizeof(gGIFData));
168     test_interlaced_gif_data(reporter, static_cast<void *>(gInterlacedGIF),
169                           sizeof(gInterlacedGIF));
170 
171     unsigned char badData[sizeof(gGIFData)];
172 
173     memcpy(badData, gGIFData, sizeof(gGIFData));
174     badData[6] = 0x01;  // image too wide
175     test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData));
176     // "libgif warning [image too wide, expanding output to size]"
177 
178     memcpy(badData, gGIFData, sizeof(gGIFData));
179     badData[8] = 0x01;  // image too tall
180     test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData));
181     // "libgif warning [image too tall,  expanding output to size]"
182 
183     memcpy(badData, gGIFData, sizeof(gGIFData));
184     badData[62] = 0x01;  // image shifted right
185     test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 4, 3);
186 
187     memcpy(badData, gGIFData, sizeof(gGIFData));
188     badData[64] = 0x01;  // image shifted down
189     test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 4);
190 
191     memcpy(badData, gGIFData, sizeof(gGIFData));
192     badData[62] = 0xff;  // image shifted right
193     badData[63] = 0xff;
194     test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3 + 0xFFFF, 3);
195 
196     memcpy(badData, gGIFData, sizeof(gGIFData));
197     badData[64] = 0xff;  // image shifted down
198     badData[65] = 0xff;
199     test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 3 + 0xFFFF);
200 
201     test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap),
202                               sizeof(gGIFDataNoColormap));
203 
204     // test short Gif.  80 is missing a few bytes.
205     test_gif_data_short(reporter, static_cast<void *>(gGIFData), 80);
206     // "libgif warning [DGifGetLine]"
207 
208     test_interlaced_gif_data(reporter, static_cast<void *>(gInterlacedGIF),
209                              100);  // 100 is missing a few bytes
210     // "libgif warning [interlace DGifGetLine]"
211 }
212 
DEF_TEST(Codec_GifInterlacedTruncated,r)213 DEF_TEST(Codec_GifInterlacedTruncated, r) {
214     // Check that gInterlacedGIF is exactly 102 bytes long, and that the final
215     // 30 bytes, in the half-open range [72, 102), consists of 0x1b (indicating
216     // a block of 27 bytes), then those 27 bytes, then 0x00 (end of the blocks)
217     // then 0x3b (end of the GIF).
218     if ((sizeof(gInterlacedGIF) != 102) ||
219         (gInterlacedGIF[72] != 0x1b) ||
220         (gInterlacedGIF[100] != 0x00) ||
221         (gInterlacedGIF[101] != 0x3b)) {
222         ERRORF(r, "Invalid gInterlacedGIF data");
223         return;
224     }
225 
226     // We want to test the GIF codec's output on some (but not all) of the
227     // LZW-compressed data. As is, there is only one block of LZW-compressed
228     // data, 27 bytes long. Wuffs can output partial results from a partial
229     // block, but some other GIF implementations output intermediate rows only
230     // on block boundaries, so truncating to a prefix of gInterlacedGIF isn't
231     // enough. We also have to modify the block size down from 0x1b so that the
232     // edited version still contains a complete block. In this case, it's a
233     // block of 10 bytes.
234     unsigned char data[83];
235     memcpy(data, gInterlacedGIF, sizeof(data));
236     data[72] = sizeof(data) - 73;
237 
238     // Just like test_interlaced_gif_data, check that we get a 9x9 image.
239     SkBitmap bm;
240     bool imageDecodeSuccess = decode_memory(data, sizeof(data), &bm);
241     REPORTER_ASSERT(r, imageDecodeSuccess);
242     REPORTER_ASSERT(r, bm.width() == 9);
243     REPORTER_ASSERT(r, bm.height() == 9);
244 
245     // For an interlaced, non-transparent image, we thicken or replicate the
246     // rows of earlier interlace passes so that, when e.g. decoding a GIF
247     // sourced from a slow network connection, we show a richer intermediate
248     // image while waiting for the complete image. This replication is
249     // sometimes described as a "Haeberli inspired technique".
250     //
251     // For a 9 pixel high image, interlacing shuffles the row order to be: 0,
252     // 8, 4, 2, 6, 1, 3, 5, 7. Even though truncating to 10 bytes of
253     // LZW-compressed data only explicitly contains completed rows 0 and 8, we
254     // still expect row 7 to be set, due to replication, and therefore not
255     // transparent black (zero).
256     REPORTER_ASSERT(r, bm.getColor(0, 7) != 0);
257 }
258 
259 // Regression test for decoding a gif image with sampleSize of 4, which was
260 // previously crashing.
DEF_TEST(Gif_Sampled,r)261 DEF_TEST(Gif_Sampled, r) {
262     auto data = GetResourceAsData("images/test640x479.gif");
263     REPORTER_ASSERT(r, data);
264     if (!data) {
265         return;
266     }
267     std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(std::move(data)));
268     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream)));
269     REPORTER_ASSERT(r, codec);
270     if (!codec) {
271         return;
272     }
273 
274     SkAndroidCodec::AndroidOptions options;
275     options.fSampleSize = 4;
276 
277     SkBitmap bm;
278     bm.allocPixels(codec->getInfo());
279     const SkCodec::Result result = codec->getAndroidPixels(codec->getInfo(), bm.getPixels(),
280             bm.rowBytes(), &options);
281     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
282 }
283 
284 // If a GIF file is truncated before the header for the first image is defined,
285 // we should not create an SkCodec.
DEF_TEST(Codec_GifTruncated,r)286 DEF_TEST(Codec_GifTruncated, r) {
287     sk_sp<SkData> data(GetResourceAsData("images/test640x479.gif"));
288     if (!data) {
289         return;
290     }
291 
292     // This is right before the header for the first image.
293     data = SkData::MakeSubset(data.get(), 0, 446);
294     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
295     REPORTER_ASSERT(r, !codec);
296 }
297 
298 /*
299 For the Codec_GifTruncated2 test, immediately below,
300 resources/images/box.gif's first 23 bytes are:
301 
302 00000000: 4749 4638 3961 c800 3700 203f 002c 0000  GIF89a..7. ?.,..
303 00000010: 0000 c800 3700 85                        ....7..
304 
305 The breakdown:
306 
307 @000  6 bytes magic "GIF89a"
308 @006  7 bytes Logical Screen Descriptor: 0xC8 0x00 ... 0x00
309    - width     =   200
310    - height    =    55
311    - flags     =  0x20
312    - background color index, pixel aspect ratio bytes ignored
313 @00D 10 bytes Image Descriptor header: 0x2C 0x00 ... 0x85
314    - origin_x  =     0
315    - origin_y  =     0
316    - width     =   200
317    - height    =    55
318    - flags     =  0x85, local color table, 64 RGB entries
319 
320 In particular, 23 bytes is after the header, but before the color table.
321 */
322 
DEF_TEST(Codec_GifTruncated2,r)323 DEF_TEST(Codec_GifTruncated2, r) {
324     // Truncate box.gif at 21, 22 and 23 bytes.
325     //
326     // See also Codec_GifTruncated3 in this file, below.
327     //
328     // See also Codec_trunc in CodecAnimTest.cpp for this magic 23.
329     //
330     // See also Codec_GifPreMap in CodecPartialTest.cpp for this magic 23.
331     for (int i = 21; i < 24; i++) {
332         sk_sp<SkData> data(GetResourceAsData("images/box.gif"));
333         if (!data) {
334             return;
335         }
336 
337         data = SkData::MakeSubset(data.get(), 0, i);
338         std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
339 
340         if (i <= 21) {
341             if (codec) {
342                 ERRORF(r, "Invalid data gave non-nullptr codec");
343             }
344             return;
345         }
346 
347         if (!codec) {
348             ERRORF(r, "Failed to create codec with partial data (truncated at %d)", i);
349             return;
350         }
351 
352         // The input is truncated in the Image Descriptor, before the local
353         // color table, and before (21) or after (22, 23) the first frame's
354         // XYWH (left / top / width / height) can be decoded. A detailed
355         // breakdown of those 23 bytes is in a comment above this function.
356         //
357         // At 21 bytes, the MakeFromStream factory method returns a nullptr
358         // SkCodec*, because creating a SkCodec requires knowing the image width
359         // and height (as its constructor takes an SkEncodedInfo argument), and
360         // specifically for GIF, decoding the image width and height requires
361         // decoding the first frame's XYWH, as per
362         // https://raw.githubusercontent.com/google/wuffs/master/test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt
363         //
364         // At 22 or 23 bytes, the first frame is complete enough that we can
365         // fill in all of a SkCodec::FrameInfo's fields (other than
366         // fFullyReceived). Specifically, we can fill in fRequiredFrame and
367         // fAlphaType, even though we haven't yet decoded the frame's RGB
368         // palette entries, as we do know the frame rectangle and that every
369         // palette entry is fully opaque, due to the lack of a Graphic Control
370         // Extension before the Image Descriptor.
371         REPORTER_ASSERT(r, codec->getFrameCount() == 1);
372     }
373 }
374 
375 // This tests that, after truncating the input, the pixels are still
376 // zero-initialized. If you comment out the SkSampler::Fill call in
377 // SkWuffsCodec::onStartIncrementalDecode, the test could still pass (in a
378 // standard configuration) but should fail with the MSAN memory sanitizer.
DEF_TEST(Codec_GifTruncated3,r)379 DEF_TEST(Codec_GifTruncated3, r) {
380     sk_sp<SkData> data(GetResourceAsData("images/box.gif"));
381     if (!data) {
382         return;
383     }
384 
385     data = SkData::MakeSubset(data.get(), 0, 23);
386     sk_sp<SkImage> image(SkImage::MakeFromEncoded(data));
387 
388     if (!image) {
389         ERRORF(r, "Missing image");
390         return;
391     }
392 
393     REPORTER_ASSERT(r, image->width() == 200);
394     REPORTER_ASSERT(r, image->height() == 55);
395 
396     SkBitmap bm;
397     if (!bm.tryAllocPixels(SkImageInfo::MakeN32Premul(200, 55))) {
398         ERRORF(r, "Failed to allocate pixels");
399         return;
400     }
401 
402     bm.eraseColor(SK_ColorTRANSPARENT);
403     REPORTER_ASSERT(r, image->readPixels(nullptr,
404                                          bm.info(),
405                                          bm.getPixels(),
406                                          200 * 4,
407                                          0, 0));
408 
409     for (int i = 0; i < image->width();  ++i)
410     for (int j = 0; j < image->height(); ++j) {
411         SkColor actual = SkUnPreMultiply::PMColorToColor(*bm.getAddr32(i, j));
412         if (actual != SK_ColorTRANSPARENT) {
413             ERRORF(r, "did not initialize pixels! %i, %i is %x", i, j, actual);
414         }
415     }
416 }
417 
DEF_TEST(Codec_gif_out_of_palette,r)418 DEF_TEST(Codec_gif_out_of_palette, r) {
419     if (GetResourcePath().isEmpty()) {
420         return;
421     }
422 
423     const char* path = "images/out-of-palette.gif";
424     auto data = GetResourceAsData(path);
425     if (!data) {
426         ERRORF(r, "failed to find %s", path);
427         return;
428     }
429 
430     auto codec = SkCodec::MakeFromData(std::move(data));
431     if (!codec) {
432         ERRORF(r, "Could not create codec from %s", path);
433         return;
434     }
435 
436     SkBitmap bm;
437     bm.allocPixels(codec->getInfo());
438     auto result = codec->getPixels(bm.pixmap());
439     REPORTER_ASSERT(r, result == SkCodec::kSuccess, "Failed to decode %s with error %s",
440                     path, SkCodec::ResultToString(result));
441 
442     struct {
443         int     x;
444         int     y;
445         SkColor expected;
446     } pixels[] = {
447         { 0, 0, SK_ColorBLACK },
448         { 1, 0, SK_ColorWHITE },
449         { 0, 1, SK_ColorTRANSPARENT },
450         { 1, 1, SK_ColorTRANSPARENT },
451     };
452     for (auto& pixel : pixels) {
453         auto actual = bm.getColor(pixel.x, pixel.y);
454         REPORTER_ASSERT(r, actual == pixel.expected,
455                         "pixel (%i,%i) mismatch! expected: %x actual: %x",
456                         pixel.x, pixel.y, pixel.expected, actual);
457     }
458 }
459 
460 // This tests decoding the GIF image created by this script:
461 // https://raw.githubusercontent.com/google/wuffs/6c2fb9a2fd9e3334ee7dabc1ad60bfc89158084f/test/data/artificial/gif-transparent-index.gif.make-artificial.txt
462 //
463 // It is a 4x2 animated image with 2 frames. The first frame is full of various
464 // red pixels. The second frame overlays a 3x1 rectangle at (1, 1): light blue,
465 // transparent, dark blue.
DEF_TEST(Codec_AnimatedTransparentGif,r)466 DEF_TEST(Codec_AnimatedTransparentGif, r) {
467     const char* path = "images/gif-transparent-index.gif";
468     auto data = GetResourceAsData(path);
469     if (!data) {
470         ERRORF(r, "failed to find %s", path);
471         return;
472     }
473 
474     auto codec = SkCodec::MakeFromData(std::move(data));
475     if (!codec) {
476         ERRORF(r, "Could not create codec from %s", path);
477         return;
478     }
479 
480     SkImageInfo info = codec->getInfo();
481     if ((info.width() != 4) || (info.height() != 2) || (codec->getFrameInfo().size() != 2)) {
482         ERRORF(r, "Unexpected image info");
483         return;
484     }
485 
486     for (bool use565 : { false, true }) {
487         SkBitmap bm;
488         bm.allocPixels(use565 ? info.makeColorType(kRGB_565_SkColorType) : info);
489 
490         for (int i = 0; i < 2; i++) {
491             SkCodec::Options options;
492             options.fFrameIndex = i;
493             options.fPriorFrame = (i > 0) ? (i - 1) : SkCodec::kNoFrame;
494             auto result = codec->getPixels(bm.pixmap(), &options);
495             REPORTER_ASSERT(r, result == SkCodec::kSuccess, "Failed to decode frame %i", i);
496 
497             // Per above: the first frame is full of various red pixels.
498             SkColor expectedPixels[2][4] = {
499                 { 0xFF800000, 0xFF900000, 0xFFA00000, 0xFFB00000 },
500                 { 0xFFC00000, 0xFFD00000, 0xFFE00000, 0xFFF00000 },
501             };
502             if (use565) {
503                 // For kRGB_565_SkColorType, copy the red channel's high 3 bits
504                 // to its low 3 bits.
505                 expectedPixels[0][0] = 0xFF840000;
506                 expectedPixels[0][1] = 0xFF940000;
507                 expectedPixels[0][2] = 0xFFA50000;
508                 expectedPixels[0][3] = 0xFFB50000;
509                 expectedPixels[1][0] = 0xFFC60000;
510                 expectedPixels[1][1] = 0xFFD60000;
511                 expectedPixels[1][2] = 0xFFE70000;
512                 expectedPixels[1][3] = 0xFFF70000;
513             }
514             if (i > 0) {
515                 // Per above: the second frame overlays a 3x1 rectangle at (1,
516                 // 1): light blue, transparent, dark blue.
517                 //
518                 // Again, for kRGB_565_SkColorType, copy the blue channel's
519                 // high 3 bits to its low 3 bits.
520                 expectedPixels[1][1] = use565 ? 0xFF0000FF : 0xFF0000FF;
521                 expectedPixels[1][3] = use565 ? 0xFF000052 : 0xFF000055;
522             }
523 
524             for (int y = 0; y < 2; y++) {
525                 for (int x = 0; x < 4; x++) {
526                     auto expected = expectedPixels[y][x];
527                     auto actual = bm.getColor(x, y);
528                     REPORTER_ASSERT(r, actual == expected,
529                                     "use565 %i, frame %i, pixel (%i,%i) "
530                                     "mismatch! expected: %x actual: %x",
531                                     (int)use565, i, x, y, expected, actual);
532                 }
533             }
534         }
535     }
536 }
537 
538 // This test verifies that a GIF frame outside the image dimensions is handled
539 // as desired:
540 // - The image reports a size of 0 x 0, but the first frame is 100 x 90. The
541 // image (or "canvas") is expanded to fit the first frame. The first frame is red.
542 // - The second frame is a green 75 x 75 rectangle, reporting its x-offset and
543 // y-offset to be 105, placing it off screen. The decoder interprets this as no
544 // change from the first frame.
DEF_TEST(Codec_xOffsetTooBig,r)545 DEF_TEST(Codec_xOffsetTooBig, r) {
546     const char* path = "images/xOffsetTooBig.gif";
547     auto data = GetResourceAsData(path);
548     if (!data) {
549         ERRORF(r, "failed to find %s", path);
550         return;
551     }
552 
553     auto codec = SkCodec::MakeFromData(std::move(data));
554     if (!codec) {
555         ERRORF(r, "Could not create codec from %s", path);
556         return;
557     }
558 
559     REPORTER_ASSERT(r, codec->getFrameCount() == 2);
560 
561     auto info = codec->getInfo();
562     REPORTER_ASSERT(r, info.width() == 100 && info.height() == 90);
563 
564     SkBitmap bm;
565     bm.allocPixels(info);
566     for (int i = 0; i < 2; i++) {
567         SkCodec::FrameInfo frameInfo;
568         REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo));
569 
570         SkIRect expectedRect = i == 0 ? SkIRect{0, 0, 100, 90} : SkIRect{100, 90, 100, 90};
571         REPORTER_ASSERT(r, expectedRect == frameInfo.fFrameRect);
572 
573         SkCodec::Options options;
574         options.fFrameIndex = i;
575         REPORTER_ASSERT(r, SkCodec::kSuccess == codec->getPixels(bm.pixmap(), &options));
576 
577         REPORTER_ASSERT(r, bm.getColor(0, 0) == SK_ColorRED);
578     }
579 }
580