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