blob: 0de5fc586d8be1ad5857991a6a0cb0e372c6eec5 [file] [log] [blame]
halcanary@google.com29d4e632013-10-11 18:21:56 +00001/*
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
msarett7f7ec202016-03-01 12:12:27 -08008#include "CodecPriv.h"
scroggo9d214292015-05-14 14:44:13 -07009#include "Resources.h"
msarett7f7ec202016-03-01 12:12:27 -080010#include "SkAndroidCodec.h"
halcanary@google.com29d4e632013-10-11 18:21:56 +000011#include "SkBitmap.h"
12#include "SkData.h"
halcanary@google.com29d4e632013-10-11 18:21:56 +000013#include "SkImage.h"
14#include "SkStream.h"
msarett7f7ec202016-03-01 12:12:27 -080015#include "SkTypes.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000016#include "Test.h"
halcanary@google.com29d4e632013-10-11 18:21:56 +000017
tfarina@chromium.org58674812014-01-21 23:39:22 +000018static 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[] = {
Leon Scroggins III3fc97d72016-12-09 16:39:33 -050029 // 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
tfarina@chromium.org58674812014-01-21 23:39:22 +000041};
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};
halcanary@google.com29d4e632013-10-11 18:21:56 +000053
54static void test_gif_data_no_colormap(skiatest::Reporter* r,
tfarina@chromium.org58674812014-01-21 23:39:22 +000055 void* data,
56 size_t size) {
halcanary@google.com29d4e632013-10-11 18:21:56 +000057 SkBitmap bm;
msarett7f7ec202016-03-01 12:12:27 -080058 bool imageDecodeSuccess = decode_memory(data, size, &bm);
halcanary@google.com29d4e632013-10-11 18:21:56 +000059 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())) {
scroggo19b91532016-10-24 09:03:26 -070064 REPORTER_ASSERT(r, bm.getColor(0, 0) == 0x00000000);
halcanary@google.com29d4e632013-10-11 18:21:56 +000065 }
66}
67static void test_gif_data(skiatest::Reporter* r, void* data, size_t size) {
68 SkBitmap bm;
msarett7f7ec202016-03-01 12:12:27 -080069 bool imageDecodeSuccess = decode_memory(data, size, &bm);
halcanary@google.com29d4e632013-10-11 18:21:56 +000070 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}
msarett7f7ec202016-03-01 12:12:27 -080086static 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}
halcanary@google.com29d4e632013-10-11 18:21:56 +000095static void test_interlaced_gif_data(skiatest::Reporter* r,
96 void* data,
97 size_t size) {
98 SkBitmap bm;
msarett7f7ec202016-03-01 12:12:27 -080099 bool imageDecodeSuccess = decode_memory(data, size, &bm);
halcanary@google.com29d4e632013-10-11 18:21:56 +0000100 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;
msarett7f7ec202016-03-01 12:12:27 -0800131 bool imageDecodeSuccess = decode_memory(data, size, &bm);
halcanary@google.com29d4e632013-10-11 18:21:56 +0000132 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/**
msarett7f7ec202016-03-01 12:12:27 -0800147 This test will test the ability of the SkCodec to deal with
halcanary@google.com29d4e632013-10-11 18:21:56 +0000148 GIF files which have been mangled somehow. We want to display as
149 much of the GIF as possible.
150*/
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000151DEF_TEST(Gif, reporter) {
halcanary@google.com29d4e632013-10-11 18:21:56 +0000152 // test perfectly good images.
tfarina@chromium.org58674812014-01-21 23:39:22 +0000153 test_gif_data(reporter, static_cast<void *>(gGIFData), sizeof(gGIFData));
154 test_interlaced_gif_data(reporter, static_cast<void *>(gInterlacedGIF),
155 sizeof(gInterlacedGIF));
halcanary@google.com29d4e632013-10-11 18:21:56 +0000156
tfarina@chromium.org58674812014-01-21 23:39:22 +0000157 unsigned char badData[sizeof(gGIFData)];
halcanary@google.com29d4e632013-10-11 18:21:56 +0000158
tfarina@chromium.org58674812014-01-21 23:39:22 +0000159 memcpy(badData, gGIFData, sizeof(gGIFData));
halcanary@google.com29d4e632013-10-11 18:21:56 +0000160 badData[6] = 0x01; // image too wide
tfarina@chromium.org58674812014-01-21 23:39:22 +0000161 test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData));
halcanary@google.com29d4e632013-10-11 18:21:56 +0000162 // "libgif warning [image too wide, expanding output to size]"
163
tfarina@chromium.org58674812014-01-21 23:39:22 +0000164 memcpy(badData, gGIFData, sizeof(gGIFData));
halcanary@google.com29d4e632013-10-11 18:21:56 +0000165 badData[8] = 0x01; // image too tall
tfarina@chromium.org58674812014-01-21 23:39:22 +0000166 test_gif_data(reporter, static_cast<void *>(badData), sizeof(gGIFData));
halcanary@google.com29d4e632013-10-11 18:21:56 +0000167 // "libgif warning [image too tall, expanding output to size]"
168
tfarina@chromium.org58674812014-01-21 23:39:22 +0000169 memcpy(badData, gGIFData, sizeof(gGIFData));
halcanary@google.com29d4e632013-10-11 18:21:56 +0000170 badData[62] = 0x01; // image shifted right
msarett7f7ec202016-03-01 12:12:27 -0800171 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 4, 3);
halcanary@google.com29d4e632013-10-11 18:21:56 +0000172
tfarina@chromium.org58674812014-01-21 23:39:22 +0000173 memcpy(badData, gGIFData, sizeof(gGIFData));
halcanary@google.com29d4e632013-10-11 18:21:56 +0000174 badData[64] = 0x01; // image shifted down
msarett7f7ec202016-03-01 12:12:27 -0800175 test_gif_data_dims(reporter, static_cast<void *>(badData), sizeof(gGIFData), 3, 4);
halcanary@google.com29d4e632013-10-11 18:21:56 +0000176
tfarina@chromium.org58674812014-01-21 23:39:22 +0000177 memcpy(badData, gGIFData, sizeof(gGIFData));
msarett7f7ec202016-03-01 12:12:27 -0800178 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);
halcanary@google.com29d4e632013-10-11 18:21:56 +0000181
tfarina@chromium.org58674812014-01-21 23:39:22 +0000182 memcpy(badData, gGIFData, sizeof(gGIFData));
msarett7f7ec202016-03-01 12:12:27 -0800183 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);
halcanary@google.com29d4e632013-10-11 18:21:56 +0000186
tfarina@chromium.org58674812014-01-21 23:39:22 +0000187 test_gif_data_no_colormap(reporter, static_cast<void *>(gGIFDataNoColormap),
188 sizeof(gGIFDataNoColormap));
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500189
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);
Mike Reedede7bac2017-07-23 15:30:02 -0400198 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500199 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 }
halcanary@google.com29d4e632013-10-11 18:21:56 +0000215
216 // test short Gif. 80 is missing a few bytes.
tfarina@chromium.org58674812014-01-21 23:39:22 +0000217 test_gif_data_short(reporter, static_cast<void *>(gGIFData), 80);
halcanary@google.com29d4e632013-10-11 18:21:56 +0000218 // "libgif warning [DGifGetLine]"
219
tfarina@chromium.org58674812014-01-21 23:39:22 +0000220 test_interlaced_gif_data(reporter, static_cast<void *>(gInterlacedGIF),
halcanary@google.com29d4e632013-10-11 18:21:56 +0000221 100); // 100 is missing a few bytes
222 // "libgif warning [interlace DGifGetLine]"
223}
224
scroggo9d214292015-05-14 14:44:13 -0700225// Regression test for decoding a gif image with sampleSize of 4, which was
226// previously crashing.
227DEF_TEST(Gif_Sampled, r) {
Mike Reed0933bc92017-12-09 01:27:41 +0000228 auto data = GetResourceAsData("images/test640x479.gif");
229 REPORTER_ASSERT(r, data);
230 if (!data) {
scroggo9d214292015-05-14 14:44:13 -0700231 return;
232 }
Mike Reed0933bc92017-12-09 01:27:41 +0000233 std::unique_ptr<SkStreamAsset> stream(new SkMemoryStream(std::move(data)));
Mike Reedede7bac2017-07-23 15:30:02 -0400234 std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromStream(std::move(stream)));
msarett7f7ec202016-03-01 12:12:27 -0800235 REPORTER_ASSERT(r, codec);
236 if (!codec) {
scroggo9d214292015-05-14 14:44:13 -0700237 return;
238 }
msarett7f7ec202016-03-01 12:12:27 -0800239
msarett7f7ec202016-03-01 12:12:27 -0800240 SkAndroidCodec::AndroidOptions options;
241 options.fSampleSize = 4;
msarett7f7ec202016-03-01 12:12:27 -0800242
scroggo9d214292015-05-14 14:44:13 -0700243 SkBitmap bm;
Leon Scroggins571b30f2017-07-11 17:35:31 +0000244 bm.allocPixels(codec->getInfo());
msarett7f7ec202016-03-01 12:12:27 -0800245 const SkCodec::Result result = codec->getAndroidPixels(codec->getInfo(), bm.getPixels(),
246 bm.rowBytes(), &options);
247 REPORTER_ASSERT(r, result == SkCodec::kSuccess);
scroggo9d214292015-05-14 14:44:13 -0700248}
Leon Scroggins III4993b952016-12-08 11:54:04 -0500249
250// If a GIF file is truncated before the header for the first image is defined,
251// we should not create an SkCodec.
252DEF_TEST(Codec_GifTruncated, r) {
Hal Canaryc465d132017-12-08 10:21:31 -0500253 sk_sp<SkData> data(GetResourceAsData("images/test640x479.gif"));
Leon Scroggins IIIe726e7c2017-07-18 16:22:52 -0400254 if (!data) {
255 return;
256 }
Leon Scroggins III4993b952016-12-08 11:54:04 -0500257
258 // This is right before the header for the first image.
259 data = SkData::MakeSubset(data.get(), 0, 446);
Mike Reedede7bac2017-07-23 15:30:02 -0400260 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
Leon Scroggins III4993b952016-12-08 11:54:04 -0500261 REPORTER_ASSERT(r, !codec);
262}
Leon Scroggins IIIe726e7c2017-07-18 16:22:52 -0400263
264DEF_TEST(Codec_GifTruncated2, r) {
Hal Canaryc465d132017-12-08 10:21:31 -0500265 sk_sp<SkData> data(GetResourceAsData("images/box.gif"));
Leon Scroggins IIIe726e7c2017-07-18 16:22:52 -0400266 if (!data) {
267 return;
268 }
269
270 // This is after the header, but before the color table.
271 data = SkData::MakeSubset(data.get(), 0, 23);
Mike Reedede7bac2017-07-23 15:30:02 -0400272 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
Leon Scroggins IIIe726e7c2017-07-18 16:22:52 -0400273 if (!codec) {
274 ERRORF(r, "Failed to create codec with partial data");
275 return;
276 }
277
278 // Although we correctly created a codec, no frame is
279 // complete enough that it has its metadata. Returning 0
280 // ensures that Chromium will not try to create a frame
281 // too early.
282 REPORTER_ASSERT(r, codec->getFrameCount() == 0);
283}