1 /*
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 
8 #include "SkBitmap.h"
9 #include "SkCodec.h"
10 #include "SkData.h"
11 #include "SkImageInfo.h"
12 #include "SkMakeUnique.h"
13 #include "SkRWBuffer.h"
14 #include "SkString.h"
15 
16 #include "FakeStreams.h"
17 #include "Resources.h"
18 #include "Test.h"
19 
standardize_info(SkCodec * codec)20 static SkImageInfo standardize_info(SkCodec* codec) {
21     SkImageInfo defaultInfo = codec->getInfo();
22     // Note: This drops the SkColorSpace, allowing the equality check between two
23     // different codecs created from the same file to have the same SkImageInfo.
24     return SkImageInfo::MakeN32Premul(defaultInfo.width(), defaultInfo.height());
25 }
26 
create_truth(sk_sp<SkData> data,SkBitmap * dst)27 static bool create_truth(sk_sp<SkData> data, SkBitmap* dst) {
28     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(std::move(data)));
29     if (!codec) {
30         return false;
31     }
32 
33     const SkImageInfo info = standardize_info(codec.get());
34     dst->allocPixels(info);
35     return SkCodec::kSuccess == codec->getPixels(info, dst->getPixels(), dst->rowBytes());
36 }
37 
compare_bitmaps(skiatest::Reporter * r,const SkBitmap & bm1,const SkBitmap & bm2)38 static void compare_bitmaps(skiatest::Reporter* r, const SkBitmap& bm1, const SkBitmap& bm2) {
39     const SkImageInfo& info = bm1.info();
40     if (info != bm2.info()) {
41         ERRORF(r, "Bitmaps have different image infos!");
42         return;
43     }
44     const size_t rowBytes = info.minRowBytes();
45     for (int i = 0; i < info.height(); i++) {
46         if (memcmp(bm1.getAddr(0, i), bm2.getAddr(0, i), rowBytes)) {
47             ERRORF(r, "Bitmaps have different pixels, starting on line %i!", i);
48             return;
49         }
50     }
51 }
52 
test_partial(skiatest::Reporter * r,const char * name,size_t minBytes=0)53 static void test_partial(skiatest::Reporter* r, const char* name, size_t minBytes = 0) {
54     sk_sp<SkData> file = GetResourceAsData(name);
55     if (!file) {
56         SkDebugf("missing resource %s\n", name);
57         return;
58     }
59 
60     SkBitmap truth;
61     if (!create_truth(file, &truth)) {
62         ERRORF(r, "Failed to decode %s\n", name);
63         return;
64     }
65 
66     // Now decode part of the file
67     HaltingStream* stream = new HaltingStream(file, SkTMax(file->size() / 2, minBytes));
68 
69     // Note that we cheat and hold on to a pointer to stream, though it is owned by
70     // partialCodec.
71     std::unique_ptr<SkCodec> partialCodec(SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream)));
72     if (!partialCodec) {
73         // Technically, this could be a small file where half the file is not
74         // enough.
75         ERRORF(r, "Failed to create codec for %s", name);
76         return;
77     }
78 
79     const SkImageInfo info = standardize_info(partialCodec.get());
80     SkASSERT(info == truth.info());
81     SkBitmap incremental;
82     incremental.allocPixels(info);
83 
84     while (true) {
85         const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info,
86                 incremental.getPixels(), incremental.rowBytes());
87         if (startResult == SkCodec::kSuccess) {
88             break;
89         }
90 
91         if (stream->isAllDataReceived()) {
92             ERRORF(r, "Failed to start incremental decode\n");
93             return;
94         }
95 
96         // Append some data. The size is arbitrary, but deliberately different from
97         // the buffer size used by SkPngCodec.
98         stream->addNewData(1000);
99     }
100 
101     while (true) {
102         const SkCodec::Result result = partialCodec->incrementalDecode();
103 
104         if (result == SkCodec::kSuccess) {
105             break;
106         }
107 
108         REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
109 
110         if (stream->isAllDataReceived()) {
111             ERRORF(r, "Failed to completely decode %s", name);
112             return;
113         }
114 
115         // Append some data. The size is arbitrary, but deliberately different from
116         // the buffer size used by SkPngCodec.
117         stream->addNewData(1000);
118     }
119 
120     // compare to original
121     compare_bitmaps(r, truth, incremental);
122 }
123 
DEF_TEST(Codec_partial,r)124 DEF_TEST(Codec_partial, r) {
125 #if 0
126     // FIXME (scroggo): SkPngCodec needs to use SkStreamBuffer in order to
127     // support incremental decoding.
128     test_partial(r, "images/plane.png");
129     test_partial(r, "images/plane_interlaced.png");
130     test_partial(r, "images/yellow_rose.png");
131     test_partial(r, "images/index8.png");
132     test_partial(r, "images/color_wheel.png");
133     test_partial(r, "images/mandrill_256.png");
134     test_partial(r, "images/mandrill_32.png");
135     test_partial(r, "images/arrow.png");
136     test_partial(r, "images/randPixels.png");
137     test_partial(r, "images/baby_tux.png");
138 #endif
139     test_partial(r, "images/box.gif");
140     test_partial(r, "images/randPixels.gif", 215);
141     test_partial(r, "images/color_wheel.gif");
142 }
143 
144 // Verify that when decoding an animated gif byte by byte we report the correct
145 // fRequiredFrame as soon as getFrameInfo reports the frame.
DEF_TEST(Codec_requiredFrame,r)146 DEF_TEST(Codec_requiredFrame, r) {
147     auto path = "images/colorTables.gif";
148     sk_sp<SkData> file = GetResourceAsData(path);
149     if (!file) {
150         return;
151     }
152 
153     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(file));
154     if (!codec) {
155         ERRORF(r, "Failed to create codec from %s", path);
156         return;
157     }
158 
159     auto frameInfo = codec->getFrameInfo();
160     if (frameInfo.size() <= 1) {
161         ERRORF(r, "Test is uninteresting with 0 or 1 frames");
162         return;
163     }
164 
165     HaltingStream* stream(nullptr);
166     std::unique_ptr<SkCodec> partialCodec(nullptr);
167     for (size_t i = 0; !partialCodec; i++) {
168         if (file->size() == i) {
169             ERRORF(r, "Should have created a partial codec for %s", path);
170             return;
171         }
172         stream = new HaltingStream(file, i);
173         partialCodec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
174     }
175 
176     std::vector<SkCodec::FrameInfo> partialInfo;
177     size_t frameToCompare = 0;
178     for (; stream->getLength() <= file->size(); stream->addNewData(1)) {
179         partialInfo = partialCodec->getFrameInfo();
180         for (; frameToCompare < partialInfo.size(); frameToCompare++) {
181             REPORTER_ASSERT(r, partialInfo[frameToCompare].fRequiredFrame
182                                 == frameInfo[frameToCompare].fRequiredFrame);
183         }
184 
185         if (frameToCompare == frameInfo.size()) {
186             break;
187         }
188     }
189 }
190 
DEF_TEST(Codec_partialAnim,r)191 DEF_TEST(Codec_partialAnim, r) {
192     auto path = "images/test640x479.gif";
193     sk_sp<SkData> file = GetResourceAsData(path);
194     if (!file) {
195         return;
196     }
197 
198     // This stream will be owned by fullCodec, but we hang on to the pointer
199     // to determine frame offsets.
200     std::unique_ptr<SkCodec> fullCodec(SkCodec::MakeFromStream(skstd::make_unique<SkMemoryStream>(file)));
201     const auto info = standardize_info(fullCodec.get());
202 
203     // frameByteCounts stores the number of bytes to decode a particular frame.
204     // - [0] is the number of bytes for the header
205     // - frames[i] requires frameByteCounts[i+1] bytes to decode
206     const std::vector<size_t> frameByteCounts = { 455, 69350, 1344, 1346, 1327 };
207     std::vector<SkBitmap> frames;
208     for (size_t i = 0; true; i++) {
209         SkBitmap frame;
210         frame.allocPixels(info);
211 
212         SkCodec::Options opts;
213         opts.fFrameIndex = i;
214         const SkCodec::Result result = fullCodec->getPixels(info, frame.getPixels(),
215                 frame.rowBytes(), &opts);
216 
217         if (result == SkCodec::kIncompleteInput || result == SkCodec::kInvalidInput) {
218             // We need to distinguish between a partial frame and no more frames.
219             // getFrameInfo lets us do this, since it tells the number of frames
220             // not considering whether they are complete.
221             // FIXME: Should we use a different Result?
222             if (fullCodec->getFrameInfo().size() > i) {
223                 // This is a partial frame.
224                 frames.push_back(frame);
225             }
226             break;
227         }
228 
229         if (result != SkCodec::kSuccess) {
230             ERRORF(r, "Failed to decode frame %i from %s", i, path);
231             return;
232         }
233 
234         frames.push_back(frame);
235     }
236 
237     // Now decode frames partially, then completely, and compare to the original.
238     HaltingStream* haltingStream = new HaltingStream(file, frameByteCounts[0]);
239     std::unique_ptr<SkCodec> partialCodec(SkCodec::MakeFromStream(
240                                                       std::unique_ptr<SkStream>(haltingStream)));
241     if (!partialCodec) {
242         ERRORF(r, "Failed to create a partial codec from %s with %i bytes out of %i",
243                path, frameByteCounts[0], file->size());
244         return;
245     }
246 
247     SkASSERT(frameByteCounts.size() > frames.size());
248     for (size_t i = 0; i < frames.size(); i++) {
249         const size_t fullFrameBytes = frameByteCounts[i + 1];
250         const size_t firstHalf = fullFrameBytes / 2;
251         const size_t secondHalf = fullFrameBytes - firstHalf;
252 
253         haltingStream->addNewData(firstHalf);
254         auto frameInfo = partialCodec->getFrameInfo();
255         REPORTER_ASSERT(r, frameInfo.size() == i + 1);
256         REPORTER_ASSERT(r, !frameInfo[i].fFullyReceived);
257 
258         SkBitmap frame;
259         frame.allocPixels(info);
260 
261         SkCodec::Options opts;
262         opts.fFrameIndex = i;
263         SkCodec::Result result = partialCodec->startIncrementalDecode(info,
264                 frame.getPixels(), frame.rowBytes(), &opts);
265         if (result != SkCodec::kSuccess) {
266             ERRORF(r, "Failed to start incremental decode for %s on frame %i",
267                    path, i);
268             return;
269         }
270 
271         result = partialCodec->incrementalDecode();
272         REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
273 
274         haltingStream->addNewData(secondHalf);
275         result = partialCodec->incrementalDecode();
276         REPORTER_ASSERT(r, SkCodec::kSuccess == result);
277 
278         frameInfo = partialCodec->getFrameInfo();
279         REPORTER_ASSERT(r, frameInfo.size() == i + 1);
280         REPORTER_ASSERT(r, frameInfo[i].fFullyReceived);
281         compare_bitmaps(r, frames[i], frame);
282     }
283 }
284 
285 // Test that calling getPixels when an incremental decode has been
286 // started (but not finished) makes the next call to incrementalDecode
287 // require a call to startIncrementalDecode.
test_interleaved(skiatest::Reporter * r,const char * name)288 static void test_interleaved(skiatest::Reporter* r, const char* name) {
289     sk_sp<SkData> file = GetResourceAsData(name);
290     if (!file) {
291         return;
292     }
293     const size_t halfSize = file->size() / 2;
294     std::unique_ptr<SkCodec> partialCodec(SkCodec::MakeFromStream(
295                                   skstd::make_unique<HaltingStream>(std::move(file), halfSize)));
296     if (!partialCodec) {
297         ERRORF(r, "Failed to create codec for %s", name);
298         return;
299     }
300 
301     const SkImageInfo info = standardize_info(partialCodec.get());
302     SkBitmap incremental;
303     incremental.allocPixels(info);
304 
305     const SkCodec::Result startResult = partialCodec->startIncrementalDecode(info,
306             incremental.getPixels(), incremental.rowBytes());
307     if (startResult != SkCodec::kSuccess) {
308         ERRORF(r, "Failed to start incremental decode\n");
309         return;
310     }
311 
312     SkCodec::Result result = partialCodec->incrementalDecode();
313     REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
314 
315     SkBitmap full;
316     full.allocPixels(info);
317     result = partialCodec->getPixels(info, full.getPixels(), full.rowBytes());
318     REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
319 
320     // Now incremental decode will fail
321     result = partialCodec->incrementalDecode();
322     REPORTER_ASSERT(r, result == SkCodec::kInvalidParameters);
323 }
324 
DEF_TEST(Codec_rewind,r)325 DEF_TEST(Codec_rewind, r) {
326     test_interleaved(r, "images/plane.png");
327     test_interleaved(r, "images/plane_interlaced.png");
328     test_interleaved(r, "images/box.gif");
329 }
330 
331 // Modified version of the giflib logo, from
332 // http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html
333 // The global color map has been replaced with a local color map.
334 static unsigned char gNoGlobalColorMap[] = {
335   // Header
336   0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
337 
338   // Logical screen descriptor
339   0x0A, 0x00, 0x0A, 0x00, 0x11, 0x00, 0x00,
340 
341   // Image descriptor
342   0x2C, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x81,
343 
344   // Local color table
345   0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
346 
347   // Image data
348   0x02, 0x16, 0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33, 0xA0, 0x02, 0x75,
349   0xEC, 0x95, 0xFA, 0xA8, 0xDE, 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01, 0x00,
350 
351   // Trailer
352   0x3B,
353 };
354 
355 // Test that a gif file truncated before its local color map behaves as expected.
DEF_TEST(Codec_GifPreMap,r)356 DEF_TEST(Codec_GifPreMap, r) {
357     sk_sp<SkData> data = SkData::MakeWithoutCopy(gNoGlobalColorMap, sizeof(gNoGlobalColorMap));
358     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
359     if (!codec) {
360         ERRORF(r, "failed to create codec");
361         return;
362     }
363 
364     SkBitmap truth;
365     auto info = standardize_info(codec.get());
366     truth.allocPixels(info);
367 
368     auto result = codec->getPixels(info, truth.getPixels(), truth.rowBytes());
369     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
370 
371     // Truncate to 23 bytes, just before the color map. This should fail to decode.
372     codec = SkCodec::MakeFromData(SkData::MakeWithoutCopy(gNoGlobalColorMap, 23));
373     REPORTER_ASSERT(r, codec);
374     if (codec) {
375         SkBitmap bm;
376         bm.allocPixels(info);
377         result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
378         REPORTER_ASSERT(r, result == SkCodec::kInvalidInput);
379     }
380 
381     // Again, truncate to 23 bytes, this time for an incremental decode. We
382     // cannot start an incremental decode until we have more data. If we did,
383     // we would be using the wrong color table.
384     HaltingStream* stream = new HaltingStream(data, 23);
385     codec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
386     REPORTER_ASSERT(r, codec);
387     if (codec) {
388         SkBitmap bm;
389         bm.allocPixels(info);
390         result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
391         REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput);
392 
393         stream->addNewData(data->size());
394         result = codec->startIncrementalDecode(info, bm.getPixels(), bm.rowBytes());
395         REPORTER_ASSERT(r, result == SkCodec::kSuccess);
396 
397         result = codec->incrementalDecode();
398         REPORTER_ASSERT(r, result == SkCodec::kSuccess);
399         compare_bitmaps(r, truth, bm);
400     }
401 }
402 
DEF_TEST(Codec_emptyIDAT,r)403 DEF_TEST(Codec_emptyIDAT, r) {
404     const char* name = "images/baby_tux.png";
405     sk_sp<SkData> file = GetResourceAsData(name);
406     if (!file) {
407         return;
408     }
409 
410     // Truncate to the beginning of the IDAT, immediately after the IDAT tag.
411     file = SkData::MakeSubset(file.get(), 0, 80);
412 
413     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(std::move(file)));
414     if (!codec) {
415         ERRORF(r, "Failed to create a codec for %s", name);
416         return;
417     }
418 
419     SkBitmap bm;
420     const auto info = standardize_info(codec.get());
421     bm.allocPixels(info);
422 
423     const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes());
424     REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result);
425 }
426 
DEF_TEST(Codec_incomplete,r)427 DEF_TEST(Codec_incomplete, r) {
428     for (const char* name : { "images/baby_tux.png",
429                               "images/baby_tux.webp",
430                               "images/CMYK.jpg",
431                               "images/color_wheel.gif",
432                               "images/google_chrome.ico",
433                               "images/rle.bmp",
434                               "images/mandrill.wbmp",
435                               }) {
436         sk_sp<SkData> file = GetResourceAsData(name);
437         if (!name) {
438             continue;
439         }
440 
441         for (size_t len = 14; len <= file->size(); len += 5) {
442             SkCodec::Result result;
443             std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(
444                                    skstd::make_unique<SkMemoryStream>(file->data(), len), &result));
445             if (codec) {
446                 if (result != SkCodec::kSuccess) {
447                     ERRORF(r, "Created an SkCodec for %s with %lu bytes, but "
448                               "reported an error %i", name, len, result);
449                 }
450                 break;
451             }
452 
453             if (SkCodec::kIncompleteInput != result) {
454                 ERRORF(r, "Reported error %i for %s with %lu bytes",
455                        result, name, len);
456                 break;
457             }
458         }
459     }
460 }
461