blob: 0ab6bdfe93def106d13377d0c1e754c1a2535742 [file] [log] [blame]
scroggo8e6c7ad2016-09-16 08:20:38 -07001/*
2 * Copyright 2016 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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/codec/SkCodec.h"
9#include "include/core/SkBitmap.h"
10#include "include/core/SkData.h"
11#include "include/core/SkImageInfo.h"
12#include "include/core/SkRefCnt.h"
13#include "include/core/SkStream.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040014#include "include/core/SkString.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050015#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050016#include "tests/CodecPriv.h"
17#include "tests/FakeStreams.h"
18#include "tests/Test.h"
19#include "tools/Resources.h"
Ben Wagner1a462bd2018-03-12 13:46:21 -040020
Ben Wagnerb607a8f2018-03-12 13:46:21 -040021#include <cstring>
Ben Wagner9707a7e2019-05-06 17:17:19 -040022#include <initializer_list>
Ben Wagnerb607a8f2018-03-12 13:46:21 -040023#include <memory>
24#include <utility>
25#include <vector>
26
scroggo8e6c7ad2016-09-16 08:20:38 -070027static SkImageInfo standardize_info(SkCodec* codec) {
28 SkImageInfo defaultInfo = codec->getInfo();
29 // Note: This drops the SkColorSpace, allowing the equality check between two
30 // different codecs created from the same file to have the same SkImageInfo.
31 return SkImageInfo::MakeN32Premul(defaultInfo.width(), defaultInfo.height());
32}
33
34static bool create_truth(sk_sp<SkData> data, SkBitmap* dst) {
Mike Reedede7bac2017-07-23 15:30:02 -040035 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(std::move(data)));
scroggo8e6c7ad2016-09-16 08:20:38 -070036 if (!codec) {
37 return false;
38 }
39
Ben Wagner145dbcd2016-11-03 14:40:50 -040040 const SkImageInfo info = standardize_info(codec.get());
scroggo8e6c7ad2016-09-16 08:20:38 -070041 dst->allocPixels(info);
42 return SkCodec::kSuccess == codec->getPixels(info, dst->getPixels(), dst->rowBytes());
43}
44
Leon Scroggins IIIcb6b8842018-12-04 13:55:13 -050045static bool compare_bitmaps(skiatest::Reporter* r, const SkBitmap& bm1, const SkBitmap& bm2) {
scroggo19b91532016-10-24 09:03:26 -070046 const SkImageInfo& info = bm1.info();
47 if (info != bm2.info()) {
48 ERRORF(r, "Bitmaps have different image infos!");
Leon Scroggins IIIcb6b8842018-12-04 13:55:13 -050049 return false;
scroggo19b91532016-10-24 09:03:26 -070050 }
51 const size_t rowBytes = info.minRowBytes();
52 for (int i = 0; i < info.height(); i++) {
nagarajan.n0ec0bf02017-10-11 10:41:27 +053053 if (memcmp(bm1.getAddr(0, i), bm2.getAddr(0, i), rowBytes)) {
54 ERRORF(r, "Bitmaps have different pixels, starting on line %i!", i);
Leon Scroggins IIIcb6b8842018-12-04 13:55:13 -050055 return false;
nagarajan.n0ec0bf02017-10-11 10:41:27 +053056 }
scroggo19b91532016-10-24 09:03:26 -070057 }
Leon Scroggins IIIcb6b8842018-12-04 13:55:13 -050058
59 return true;
scroggo19b91532016-10-24 09:03:26 -070060}
61
Leon Scroggins III762ff972018-12-13 10:34:38 -050062static void test_partial(skiatest::Reporter* r, const char* name, const sk_sp<SkData>& file,
63 size_t minBytes, size_t increment) {
scroggo8e6c7ad2016-09-16 08:20:38 -070064 SkBitmap truth;
65 if (!create_truth(file, &truth)) {
66 ERRORF(r, "Failed to decode %s\n", name);
67 return;
68 }
69
scroggo8e6c7ad2016-09-16 08:20:38 -070070 // Now decode part of the file
Leon Scroggins III762ff972018-12-13 10:34:38 -050071 HaltingStream* stream = new HaltingStream(file, minBytes);
scroggo8e6c7ad2016-09-16 08:20:38 -070072
73 // Note that we cheat and hold on to a pointer to stream, though it is owned by
74 // partialCodec.
Leon Scroggins III762ff972018-12-13 10:34:38 -050075 auto partialCodec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
scroggo8e6c7ad2016-09-16 08:20:38 -070076 if (!partialCodec) {
Leon Scroggins III762ff972018-12-13 10:34:38 -050077 ERRORF(r, "Failed to create codec for %s with %zu bytes", name, minBytes);
scroggo8e6c7ad2016-09-16 08:20:38 -070078 return;
79 }
80
Ben Wagner145dbcd2016-11-03 14:40:50 -040081 const SkImageInfo info = standardize_info(partialCodec.get());
scroggo8e6c7ad2016-09-16 08:20:38 -070082 SkASSERT(info == truth.info());
83 SkBitmap incremental;
84 incremental.allocPixels(info);
85
scroggo19b91532016-10-24 09:03:26 -070086 while (true) {
87 const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info,
88 incremental.getPixels(), incremental.rowBytes());
89 if (startResult == SkCodec::kSuccess) {
90 break;
91 }
92
93 if (stream->isAllDataReceived()) {
94 ERRORF(r, "Failed to start incremental decode\n");
95 return;
96 }
97
Leon Scroggins III762ff972018-12-13 10:34:38 -050098 stream->addNewData(increment);
scroggo8e6c7ad2016-09-16 08:20:38 -070099 }
100
101 while (true) {
Leon Scroggins III762ff972018-12-13 10:34:38 -0500102 // This imitates how Chromium calls getFrameCount before resuming a decode.
Leon Scroggins III762ff972018-12-13 10:34:38 -0500103 partialCodec->getFrameCount();
Nigel Tao1f1cd1f2019-06-22 17:35:38 +1000104
scroggo8e6c7ad2016-09-16 08:20:38 -0700105 const SkCodec::Result result = partialCodec->incrementalDecode();
106
scroggo19b91532016-10-24 09:03:26 -0700107 if (result == SkCodec::kSuccess) {
scroggo8e6c7ad2016-09-16 08:20:38 -0700108 break;
109 }
110
scroggo8e6c7ad2016-09-16 08:20:38 -0700111 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
112
scroggo19b91532016-10-24 09:03:26 -0700113 if (stream->isAllDataReceived()) {
114 ERRORF(r, "Failed to completely decode %s", name);
115 return;
116 }
117
Leon Scroggins III762ff972018-12-13 10:34:38 -0500118 stream->addNewData(increment);
scroggo8e6c7ad2016-09-16 08:20:38 -0700119 }
120
121 // compare to original
scroggo19b91532016-10-24 09:03:26 -0700122 compare_bitmaps(r, truth, incremental);
scroggo8e6c7ad2016-09-16 08:20:38 -0700123}
124
Leon Scroggins III762ff972018-12-13 10:34:38 -0500125static void test_partial(skiatest::Reporter* r, const char* name, size_t minBytes = 0) {
126 sk_sp<SkData> file = GetResourceAsData(name);
127 if (!file) {
128 SkDebugf("missing resource %s\n", name);
129 return;
130 }
131
132 // This size is arbitrary, but deliberately different from the buffer size used by SkPngCodec.
133 constexpr size_t kIncrement = 1000;
134 test_partial(r, name, file, SkTMax(file->size() / 2, minBytes), kIncrement);
135}
136
scroggo8e6c7ad2016-09-16 08:20:38 -0700137DEF_TEST(Codec_partial, r) {
Leon Scroggins III83239652017-04-21 13:47:12 -0400138#if 0
139 // FIXME (scroggo): SkPngCodec needs to use SkStreamBuffer in order to
140 // support incremental decoding.
Hal Canaryc465d132017-12-08 10:21:31 -0500141 test_partial(r, "images/plane.png");
142 test_partial(r, "images/plane_interlaced.png");
143 test_partial(r, "images/yellow_rose.png");
144 test_partial(r, "images/index8.png");
145 test_partial(r, "images/color_wheel.png");
146 test_partial(r, "images/mandrill_256.png");
147 test_partial(r, "images/mandrill_32.png");
148 test_partial(r, "images/arrow.png");
149 test_partial(r, "images/randPixels.png");
150 test_partial(r, "images/baby_tux.png");
Leon Scroggins III83239652017-04-21 13:47:12 -0400151#endif
Hal Canaryc465d132017-12-08 10:21:31 -0500152 test_partial(r, "images/box.gif");
153 test_partial(r, "images/randPixels.gif", 215);
154 test_partial(r, "images/color_wheel.gif");
scroggo19b91532016-10-24 09:03:26 -0700155}
156
Leon Scroggins III762ff972018-12-13 10:34:38 -0500157DEF_TEST(Codec_partialWuffs, r) {
158 const char* path = "images/alphabetAnim.gif";
159 auto file = GetResourceAsData(path);
160 if (!file) {
161 ERRORF(r, "missing %s", path);
162 } else {
163 // This is the end of the first frame. SkCodec will treat this as a
164 // single frame gif.
165 file = SkData::MakeSubset(file.get(), 0, 153);
166 // Start with 100 to get a partial decode, then add the rest of the
167 // first frame to decode a full image.
168 test_partial(r, path, file, 100, 53);
169 }
170}
171
Nigel Taoa1cc9f62019-10-24 15:02:05 +1100172DEF_TEST(Codec_frameCountUpdatesInIncrementalDecode, r) {
173 sk_sp<SkData> file = GetResourceAsData("images/colorTables.gif");
174 size_t fileSize = file->size();
175 REPORTER_ASSERT(r, fileSize == 2829);
176 std::unique_ptr<SkCodec> fullCodec(SkCodec::MakeFromData(file));
177 REPORTER_ASSERT(r, fullCodec->getFrameCount() == 2);
178 const SkImageInfo info = standardize_info(fullCodec.get());
179
180 static const size_t n = 1000;
181 HaltingStream* stream = new HaltingStream(file, n);
182 // Note that we cheat and hold on to a pointer to stream, though it is owned by
183 // partialCodec.
184 auto partialCodec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
185 REPORTER_ASSERT(r, partialCodec->getFrameCount() == 1);
186
187 SkBitmap bitmap;
188 bitmap.allocPixels(info);
189 REPORTER_ASSERT(r, SkCodec::kSuccess ==
190 partialCodec->startIncrementalDecode(
191 info, bitmap.getPixels(), bitmap.rowBytes()));
192 REPORTER_ASSERT(r, SkCodec::kIncompleteInput ==
193 partialCodec->incrementalDecode());
194
195 REPORTER_ASSERT(r, partialCodec->getFrameCount() == 1);
196 stream->addNewData(fileSize - n);
197 REPORTER_ASSERT(r, partialCodec->getFrameCount() == 2);
198}
Nigel Taoa1cc9f62019-10-24 15:02:05 +1100199
Leon Scroggins IIIe4ba1052017-01-30 13:55:14 -0500200// Verify that when decoding an animated gif byte by byte we report the correct
201// fRequiredFrame as soon as getFrameInfo reports the frame.
202DEF_TEST(Codec_requiredFrame, r) {
Hal Canaryc465d132017-12-08 10:21:31 -0500203 auto path = "images/colorTables.gif";
Leon Scroggins IIIe4ba1052017-01-30 13:55:14 -0500204 sk_sp<SkData> file = GetResourceAsData(path);
205 if (!file) {
206 return;
207 }
208
Mike Reedede7bac2017-07-23 15:30:02 -0400209 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(file));
Leon Scroggins IIIe4ba1052017-01-30 13:55:14 -0500210 if (!codec) {
211 ERRORF(r, "Failed to create codec from %s", path);
212 return;
213 }
214
215 auto frameInfo = codec->getFrameInfo();
216 if (frameInfo.size() <= 1) {
217 ERRORF(r, "Test is uninteresting with 0 or 1 frames");
218 return;
219 }
220
221 HaltingStream* stream(nullptr);
222 std::unique_ptr<SkCodec> partialCodec(nullptr);
223 for (size_t i = 0; !partialCodec; i++) {
224 if (file->size() == i) {
225 ERRORF(r, "Should have created a partial codec for %s", path);
226 return;
227 }
228 stream = new HaltingStream(file, i);
Mike Reedede7bac2017-07-23 15:30:02 -0400229 partialCodec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
Leon Scroggins IIIe4ba1052017-01-30 13:55:14 -0500230 }
231
232 std::vector<SkCodec::FrameInfo> partialInfo;
233 size_t frameToCompare = 0;
Nigel Taoafea9c32018-08-01 15:00:32 +1000234 while (true) {
Leon Scroggins IIIe4ba1052017-01-30 13:55:14 -0500235 partialInfo = partialCodec->getFrameInfo();
236 for (; frameToCompare < partialInfo.size(); frameToCompare++) {
237 REPORTER_ASSERT(r, partialInfo[frameToCompare].fRequiredFrame
238 == frameInfo[frameToCompare].fRequiredFrame);
239 }
240
241 if (frameToCompare == frameInfo.size()) {
242 break;
243 }
Nigel Taoafea9c32018-08-01 15:00:32 +1000244
245 if (stream->getLength() == file->size()) {
246 ERRORF(r, "Should have found all frames for %s", path);
247 return;
248 }
249 stream->addNewData(1);
Leon Scroggins IIIe4ba1052017-01-30 13:55:14 -0500250 }
251}
252
scroggo19b91532016-10-24 09:03:26 -0700253DEF_TEST(Codec_partialAnim, r) {
Hal Canaryc465d132017-12-08 10:21:31 -0500254 auto path = "images/test640x479.gif";
Leon Scroggins IIIe4ba1052017-01-30 13:55:14 -0500255 sk_sp<SkData> file = GetResourceAsData(path);
scroggo19b91532016-10-24 09:03:26 -0700256 if (!file) {
257 return;
258 }
259
260 // This stream will be owned by fullCodec, but we hang on to the pointer
261 // to determine frame offsets.
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500262 std::unique_ptr<SkCodec> fullCodec(SkCodec::MakeFromStream(std::make_unique<SkMemoryStream>(file)));
scroggo19b91532016-10-24 09:03:26 -0700263 const auto info = standardize_info(fullCodec.get());
264
265 // frameByteCounts stores the number of bytes to decode a particular frame.
266 // - [0] is the number of bytes for the header
267 // - frames[i] requires frameByteCounts[i+1] bytes to decode
Leon Scroggins III1f6af6b2017-06-12 16:41:09 -0400268 const std::vector<size_t> frameByteCounts = { 455, 69350, 1344, 1346, 1327 };
scroggo19b91532016-10-24 09:03:26 -0700269 std::vector<SkBitmap> frames;
scroggo19b91532016-10-24 09:03:26 -0700270 for (size_t i = 0; true; i++) {
scroggo19b91532016-10-24 09:03:26 -0700271 SkBitmap frame;
272 frame.allocPixels(info);
273
274 SkCodec::Options opts;
275 opts.fFrameIndex = i;
276 const SkCodec::Result result = fullCodec->getPixels(info, frame.getPixels(),
Leon Scroggins571b30f2017-07-11 17:35:31 +0000277 frame.rowBytes(), &opts);
scroggo19b91532016-10-24 09:03:26 -0700278
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500279 if (result == SkCodec::kIncompleteInput || result == SkCodec::kInvalidInput) {
scroggo19b91532016-10-24 09:03:26 -0700280 // We need to distinguish between a partial frame and no more frames.
281 // getFrameInfo lets us do this, since it tells the number of frames
282 // not considering whether they are complete.
283 // FIXME: Should we use a different Result?
284 if (fullCodec->getFrameInfo().size() > i) {
285 // This is a partial frame.
286 frames.push_back(frame);
287 }
288 break;
289 }
290
291 if (result != SkCodec::kSuccess) {
292 ERRORF(r, "Failed to decode frame %i from %s", i, path);
293 return;
294 }
295
296 frames.push_back(frame);
297 }
298
299 // Now decode frames partially, then completely, and compare to the original.
300 HaltingStream* haltingStream = new HaltingStream(file, frameByteCounts[0]);
Mike Reedede7bac2017-07-23 15:30:02 -0400301 std::unique_ptr<SkCodec> partialCodec(SkCodec::MakeFromStream(
302 std::unique_ptr<SkStream>(haltingStream)));
scroggo19b91532016-10-24 09:03:26 -0700303 if (!partialCodec) {
304 ERRORF(r, "Failed to create a partial codec from %s with %i bytes out of %i",
305 path, frameByteCounts[0], file->size());
306 return;
307 }
308
309 SkASSERT(frameByteCounts.size() > frames.size());
310 for (size_t i = 0; i < frames.size(); i++) {
311 const size_t fullFrameBytes = frameByteCounts[i + 1];
312 const size_t firstHalf = fullFrameBytes / 2;
313 const size_t secondHalf = fullFrameBytes - firstHalf;
314
315 haltingStream->addNewData(firstHalf);
Leon Scroggins III3639faa2016-12-08 11:38:58 -0500316 auto frameInfo = partialCodec->getFrameInfo();
317 REPORTER_ASSERT(r, frameInfo.size() == i + 1);
318 REPORTER_ASSERT(r, !frameInfo[i].fFullyReceived);
scroggo19b91532016-10-24 09:03:26 -0700319
320 SkBitmap frame;
321 frame.allocPixels(info);
322
323 SkCodec::Options opts;
324 opts.fFrameIndex = i;
325 SkCodec::Result result = partialCodec->startIncrementalDecode(info,
326 frame.getPixels(), frame.rowBytes(), &opts);
327 if (result != SkCodec::kSuccess) {
328 ERRORF(r, "Failed to start incremental decode for %s on frame %i",
329 path, i);
330 return;
331 }
332
333 result = partialCodec->incrementalDecode();
334 REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
335
336 haltingStream->addNewData(secondHalf);
337 result = partialCodec->incrementalDecode();
338 REPORTER_ASSERT(r, SkCodec::kSuccess == result);
339
Leon Scroggins III3639faa2016-12-08 11:38:58 -0500340 frameInfo = partialCodec->getFrameInfo();
341 REPORTER_ASSERT(r, frameInfo.size() == i + 1);
342 REPORTER_ASSERT(r, frameInfo[i].fFullyReceived);
Leon Scroggins IIIcb6b8842018-12-04 13:55:13 -0500343 if (!compare_bitmaps(r, frames[i], frame)) {
344 ERRORF(r, "\tfailure was on frame %i", i);
345 SkString name = SkStringPrintf("expected_%i", i);
346 write_bm(name.c_str(), frames[i]);
347
348 name = SkStringPrintf("actual_%i", i);
349 write_bm(name.c_str(), frame);
350 }
scroggo19b91532016-10-24 09:03:26 -0700351 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700352}
353
354// Test that calling getPixels when an incremental decode has been
355// started (but not finished) makes the next call to incrementalDecode
356// require a call to startIncrementalDecode.
357static void test_interleaved(skiatest::Reporter* r, const char* name) {
Leon Scroggins IIIe4ba1052017-01-30 13:55:14 -0500358 sk_sp<SkData> file = GetResourceAsData(name);
scroggo19b91532016-10-24 09:03:26 -0700359 if (!file) {
360 return;
361 }
362 const size_t halfSize = file->size() / 2;
Mike Reedede7bac2017-07-23 15:30:02 -0400363 std::unique_ptr<SkCodec> partialCodec(SkCodec::MakeFromStream(
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500364 std::make_unique<HaltingStream>(std::move(file), halfSize)));
scroggo8e6c7ad2016-09-16 08:20:38 -0700365 if (!partialCodec) {
366 ERRORF(r, "Failed to create codec for %s", name);
367 return;
368 }
369
Ben Wagner145dbcd2016-11-03 14:40:50 -0400370 const SkImageInfo info = standardize_info(partialCodec.get());
scroggo8e6c7ad2016-09-16 08:20:38 -0700371 SkBitmap incremental;
372 incremental.allocPixels(info);
373
374 const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info,
375 incremental.getPixels(), incremental.rowBytes());
376 if (startResult != SkCodec::kSuccess) {
377 ERRORF(r, "Failed to start incremental decode\n");
378 return;
379 }
380
381 SkCodec::Result result = partialCodec->incrementalDecode();
382 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
383
384 SkBitmap full;
385 full.allocPixels(info);
386 result = partialCodec->getPixels(info, full.getPixels(), full.rowBytes());
387 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
388
389 // Now incremental decode will fail
390 result = partialCodec->incrementalDecode();
391 REPORTER_ASSERT(r, result == SkCodec::kInvalidParameters);
392}
393
394DEF_TEST(Codec_rewind, r) {
Hal Canaryc465d132017-12-08 10:21:31 -0500395 test_interleaved(r, "images/plane.png");
396 test_interleaved(r, "images/plane_interlaced.png");
397 test_interleaved(r, "images/box.gif");
scroggo8e6c7ad2016-09-16 08:20:38 -0700398}
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500399
400// Modified version of the giflib logo, from
401// http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html
402// The global color map has been replaced with a local color map.
403static unsigned char gNoGlobalColorMap[] = {
404 // Header
405 0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
406
407 // Logical screen descriptor
408 0x0A, 0x00, 0x0A, 0x00, 0x11, 0x00, 0x00,
409
410 // Image descriptor
411 0x2C, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x81,
412
413 // Local color table
414 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
415
416 // Image data
417 0x02, 0x16, 0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75,
418 0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01, 0x00,
419
420 // Trailer
421 0x3B,
422};
423
424// Test that a gif file truncated before its local color map behaves as expected.
425DEF_TEST(Codec_GifPreMap, r) {
426 sk_sp<SkData> data = SkData::MakeWithoutCopy(gNoGlobalColorMap, sizeof(gNoGlobalColorMap));
Mike Reedede7bac2017-07-23 15:30:02 -0400427 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500428 if (!codec) {
429 ERRORF(r, "failed to create codec");
430 return;
431 }
432
433 SkBitmap truth;
434 auto info = standardize_info(codec.get());
435 truth.allocPixels(info);
436
437 auto result = codec->getPixels(info, truth.getPixels(), truth.rowBytes());
438 REPORTER_ASSERT(r, result == SkCodec::kSuccess);
439
440 // Truncate to 23 bytes, just before the color map. This should fail to decode.
Leon Scroggins IIIe93ec682018-10-26 09:25:51 -0400441 //
442 // See also Codec_GifTruncated2 in GifTest.cpp for this magic 23.
Mike Reedede7bac2017-07-23 15:30:02 -0400443 codec = SkCodec::MakeFromData(SkData::MakeWithoutCopy(gNoGlobalColorMap, 23));
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500444 REPORTER_ASSERT(r, codec);
445 if (codec) {
446 SkBitmap bm;
447 bm.allocPixels(info);
448 result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
Leon Scroggins IIIe93ec682018-10-26 09:25:51 -0400449
450 // See the comments in Codec_GifTruncated2.
451#ifdef SK_HAS_WUFFS_LIBRARY
452 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
453#else
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500454 REPORTER_ASSERT(r, result == SkCodec::kInvalidInput);
Leon Scroggins IIIe93ec682018-10-26 09:25:51 -0400455#endif
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500456 }
457
458 // Again, truncate to 23 bytes, this time for an incremental decode. We
459 // cannot start an incremental decode until we have more data. If we did,
460 // we would be using the wrong color table.
461 HaltingStream* stream = new HaltingStream(data, 23);
Mike Reedede7bac2017-07-23 15:30:02 -0400462 codec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500463 REPORTER_ASSERT(r, codec);
464 if (codec) {
465 SkBitmap bm;
466 bm.allocPixels(info);
467 result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
Leon Scroggins IIIe93ec682018-10-26 09:25:51 -0400468
469 // See the comments in Codec_GifTruncated2.
470#ifdef SK_HAS_WUFFS_LIBRARY
471 REPORTER_ASSERT(r, result == SkCodec::kSuccess);
472
473 // Note that this is incrementalDecode, not startIncrementalDecode.
474 result = codec->incrementalDecode();
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500475 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
476
477 stream->addNewData(data->size());
Leon Scroggins IIIe93ec682018-10-26 09:25:51 -0400478#else
479 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
480
481 // Note that this is startIncrementalDecode, not incrementalDecode.
482 stream->addNewData(data->size());
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500483 result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
484 REPORTER_ASSERT(r, result == SkCodec::kSuccess);
Leon Scroggins IIIe93ec682018-10-26 09:25:51 -0400485#endif
Leon Scroggins III3fc97d72016-12-09 16:39:33 -0500486
487 result = codec->incrementalDecode();
488 REPORTER_ASSERT(r, result == SkCodec::kSuccess);
489 compare_bitmaps(r, truth, bm);
490 }
491}
Leon Scroggins IIIb6446502017-04-24 09:32:50 -0400492
493DEF_TEST(Codec_emptyIDAT, r) {
Hal Canaryc465d132017-12-08 10:21:31 -0500494 const char* name = "images/baby_tux.png";
Leon Scroggins IIIb6446502017-04-24 09:32:50 -0400495 sk_sp<SkData> file = GetResourceAsData(name);
496 if (!file) {
Leon Scroggins IIIb6446502017-04-24 09:32:50 -0400497 return;
498 }
499
500 // Truncate to the beginning of the IDAT, immediately after the IDAT tag.
501 file = SkData::MakeSubset(file.get(), 0, 80);
502
Mike Reedede7bac2017-07-23 15:30:02 -0400503 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(std::move(file)));
Leon Scroggins IIIb6446502017-04-24 09:32:50 -0400504 if (!codec) {
505 ERRORF(r, "Failed to create a codec for %s", name);
506 return;
507 }
508
509 SkBitmap bm;
510 const auto info = standardize_info(codec.get());
511 bm.allocPixels(info);
512
513 const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
514 REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
515}
Leon Scroggins III588fb042017-07-14 16:32:31 -0400516
517DEF_TEST(Codec_incomplete, r) {
Hal Canaryc465d132017-12-08 10:21:31 -0500518 for (const char* name : { "images/baby_tux.png",
519 "images/baby_tux.webp",
520 "images/CMYK.jpg",
521 "images/color_wheel.gif",
522 "images/google_chrome.ico",
523 "images/rle.bmp",
524 "images/mandrill.wbmp",
Leon Scroggins III588fb042017-07-14 16:32:31 -0400525 }) {
526 sk_sp<SkData> file = GetResourceAsData(name);
Bruce Wang61f36d32018-05-29 17:29:24 -0400527 if (!file) {
Leon Scroggins III588fb042017-07-14 16:32:31 -0400528 continue;
529 }
530
531 for (size_t len = 14; len <= file->size(); len += 5) {
532 SkCodec::Result result;
Mike Reedede7bac2017-07-23 15:30:02 -0400533 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
Mike Kleinf46d5ca2019-12-11 10:45:01 -0500534 std::make_unique<SkMemoryStream>(file->data(), len), &result));
Leon Scroggins III588fb042017-07-14 16:32:31 -0400535 if (codec) {
536 if (result != SkCodec::kSuccess) {
537 ERRORF(r, "Created an SkCodec for %s with %lu bytes, but "
538 "reported an error %i", name, len, result);
539 }
540 break;
541 }
542
543 if (SkCodec::kIncompleteInput != result) {
544 ERRORF(r, "Reported error %i for %s with %lu bytes",
545 result, name, len);
546 break;
547 }
548 }
549 }
550}