• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/codec/SkAndroidCodec.h"
9 #include "include/codec/SkCodec.h"
10 #include "include/codec/SkCodecAnimation.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkRefCnt.h"
17 #include "include/core/SkSize.h"
18 #include "include/core/SkString.h"
19 #include "include/core/SkTypes.h"
20 #include "include/utils/SkAnimCodecPlayer.h"
21 #include "src/core/SkMakeUnique.h"
22 #include "tests/CodecPriv.h"
23 #include "tests/Test.h"
24 #include "tools/Resources.h"
25 #include "tools/ToolUtils.h"
26 
27 #include <stdio.h>
28 #include <cstring>
29 #include <initializer_list>
30 #include <memory>
31 #include <utility>
32 #include <vector>
33 
DEF_TEST(Codec_trunc,r)34 DEF_TEST(Codec_trunc, r) {
35     sk_sp<SkData> data(GetResourceAsData("images/box.gif"));
36     if (!data) {
37         return;
38     }
39     // See also Codec_GifTruncated2 in GifTest.cpp for this magic 23.
40     //
41     // TODO: just move this getFrameInfo call to Codec_GifTruncated2?
42     SkCodec::MakeFromData(SkData::MakeSubset(data.get(), 0, 23))->getFrameInfo();
43 }
44 
45 // 565 does not support alpha, but there is no reason for it not to support an
46 // animated image with a frame that has alpha but then blends onto an opaque
47 // frame making the result opaque. Test that we can decode such a frame.
DEF_TEST(Codec_565,r)48 DEF_TEST(Codec_565, r) {
49     sk_sp<SkData> data(GetResourceAsData("images/blendBG.webp"));
50     if (!data) {
51         return;
52     }
53     std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(std::move(data)));
54     auto info = codec->getInfo().makeColorType(kRGB_565_SkColorType);
55     SkBitmap bm;
56     bm.allocPixels(info);
57 
58     SkCodec::Options options;
59     options.fFrameIndex = 1;
60     options.fPriorFrame = SkCodec::kNoFrame;
61 
62     const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(),
63                                          &options);
64     REPORTER_ASSERT(r, result == SkCodec::kSuccess);
65 }
66 
restore_previous(const SkCodec::FrameInfo & info)67 static bool restore_previous(const SkCodec::FrameInfo& info) {
68     return info.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious;
69 }
70 
DEF_TEST(Codec_frames,r)71 DEF_TEST(Codec_frames, r) {
72     constexpr int kNoFrame = SkCodec::kNoFrame;
73     constexpr SkAlphaType kOpaque = kOpaque_SkAlphaType;
74     constexpr SkAlphaType kUnpremul = kUnpremul_SkAlphaType;
75     constexpr SkCodecAnimation::DisposalMethod kKeep =
76             SkCodecAnimation::DisposalMethod::kKeep;
77     constexpr SkCodecAnimation::DisposalMethod kRestoreBG =
78             SkCodecAnimation::DisposalMethod::kRestoreBGColor;
79     constexpr SkCodecAnimation::DisposalMethod kRestorePrev =
80             SkCodecAnimation::DisposalMethod::kRestorePrevious;
81 
82     static const struct {
83         const char*                                   fName;
84         int                                           fFrameCount;
85         // One less than fFramecount, since the first frame is always
86         // independent.
87         std::vector<int>                              fRequiredFrames;
88         // Same, since the first frame should match getInfo
89         std::vector<SkAlphaType>                      fAlphas;
90         // The size of this one should match fFrameCount for animated, empty
91         // otherwise.
92         std::vector<int>                              fDurations;
93         int                                           fRepetitionCount;
94         std::vector<SkCodecAnimation::DisposalMethod> fDisposalMethods;
95     } gRecs[] = {
96         { "images/required.gif", 7,
97             { 0, 1, 2, 3, 4, 5 },
98             { kOpaque, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
99             { 100, 100, 100, 100, 100, 100, 100 },
100             0,
101             { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep } },
102         { "images/alphabetAnim.gif", 13,
103             { kNoFrame, 0, 0, 0, 0, 5, 6, kNoFrame, kNoFrame, 9, 10, 11 },
104             { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
105               kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
106             { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 },
107             0,
108             { kKeep, kRestorePrev, kRestorePrev, kRestorePrev, kRestorePrev,
109               kRestoreBG, kKeep, kRestoreBG, kRestoreBG, kKeep, kKeep,
110               kRestoreBG, kKeep } },
111         { "images/randPixelsAnim2.gif", 4,
112             // required frames
113             { 0, 0, 1 },
114             // alphas
115             { kOpaque, kOpaque, kOpaque },
116             // durations
117             { 0, 1000, 170, 40 },
118             // repetition count
119             0,
120             { kKeep, kKeep, kRestorePrev, kKeep } },
121         { "images/randPixelsAnim.gif", 13,
122             // required frames
123             { 0, 1, 2, 3, 4, 3, 6, 7, 7, 7, 9, 9 },
124             { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
125               kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
126             // durations
127             { 0, 1000, 170, 40, 220, 7770, 90, 90, 90, 90, 90, 90, 90 },
128             // repetition count
129             0,
130             { kKeep, kKeep, kKeep, kKeep, kRestoreBG, kRestoreBG, kRestoreBG,
131               kRestoreBG, kRestorePrev, kRestoreBG, kRestorePrev, kRestorePrev,
132               kRestorePrev,  } },
133         { "images/box.gif", 1, {}, {}, {}, 0, { kKeep } },
134         { "images/color_wheel.gif", 1, {}, {}, {}, 0, { kKeep } },
135         { "images/test640x479.gif", 4, { 0, 1, 2 },
136                 { kOpaque, kOpaque, kOpaque },
137                 { 200, 200, 200, 200 },
138                 SkCodec::kRepetitionCountInfinite,
139                 { kKeep, kKeep, kKeep, kKeep } },
140         { "images/colorTables.gif", 2, { 0 }, { kOpaque }, { 1000, 1000 }, 5,
141                 { kKeep, kKeep } },
142 
143         { "images/arrow.png",  1, {}, {}, {}, 0, {} },
144         { "images/google_chrome.ico", 1, {}, {}, {}, 0, {} },
145         { "images/brickwork-texture.jpg", 1, {}, {}, {}, 0, {} },
146 #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
147         { "images/dng_with_preview.dng", 1, {}, {}, {}, 0, {} },
148 #endif
149         { "images/mandrill.wbmp", 1, {}, {}, {}, 0, {} },
150         { "images/randPixels.bmp", 1, {}, {}, {}, 0, {} },
151         { "images/yellow_rose.webp", 1, {}, {}, {}, 0, {} },
152         { "images/webp-animated.webp", 3, { 0, 1 }, { kOpaque, kOpaque },
153             { 1000, 500, 1000 }, SkCodec::kRepetitionCountInfinite,
154             { kKeep, kKeep, kKeep } },
155         { "images/blendBG.webp", 7,
156             { 0, kNoFrame, kNoFrame, kNoFrame, 4, 4 },
157             { kOpaque, kOpaque, kUnpremul, kOpaque, kUnpremul, kUnpremul },
158             { 525, 500, 525, 437, 609, 729, 444 }, 7,
159             { kKeep, kKeep, kKeep, kKeep, kKeep, kKeep, kKeep } },
160         { "images/required.webp", 7,
161             { 0, 1, 1, kNoFrame, 4, 4 },
162             { kOpaque, kUnpremul, kUnpremul, kOpaque, kOpaque, kOpaque },
163             { 100, 100, 100, 100, 100, 100, 100 },
164             1,
165             { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep } },
166     };
167 
168     for (const auto& rec : gRecs) {
169         sk_sp<SkData> data(GetResourceAsData(rec.fName));
170         if (!data) {
171             // Useful error statement, but sometimes people run tests without
172             // resources, and they do not want to see these messages.
173             //ERRORF(r, "Missing resources? Could not find '%s'", rec.fName);
174             continue;
175         }
176 
177         std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
178         if (!codec) {
179             ERRORF(r, "Failed to create an SkCodec from '%s'", rec.fName);
180             continue;
181         }
182 
183         {
184             SkCodec::FrameInfo frameInfo;
185             REPORTER_ASSERT(r, !codec->getFrameInfo(0, &frameInfo));
186         }
187 
188         const int repetitionCount = codec->getRepetitionCount();
189         if (repetitionCount != rec.fRepetitionCount) {
190             ERRORF(r, "%s repetition count does not match! expected: %i\tactual: %i",
191                       rec.fName, rec.fRepetitionCount, repetitionCount);
192         }
193 
194         const int expected = rec.fFrameCount;
195         if (rec.fRequiredFrames.size() + 1 != static_cast<size_t>(expected)) {
196             ERRORF(r, "'%s' has wrong number entries in fRequiredFrames; expected: %i\tactual: %i",
197                    rec.fName, expected - 1, rec.fRequiredFrames.size());
198             continue;
199         }
200 
201         if (expected > 1) {
202             if (rec.fDurations.size() != static_cast<size_t>(expected)) {
203                 ERRORF(r, "'%s' has wrong number entries in fDurations; expected: %i\tactual: %i",
204                        rec.fName, expected, rec.fDurations.size());
205                 continue;
206             }
207 
208             if (rec.fAlphas.size() + 1 != static_cast<size_t>(expected)) {
209                 ERRORF(r, "'%s' has wrong number entries in fAlphas; expected: %i\tactual: %i",
210                        rec.fName, expected - 1, rec.fAlphas.size());
211                 continue;
212             }
213 
214             if (rec.fDisposalMethods.size() != static_cast<size_t>(expected)) {
215                 ERRORF(r, "'%s' has wrong number entries in fDisposalMethods; "
216                        "expected %i\tactual: %i",
217                        rec.fName, expected, rec.fDisposalMethods.size());
218                 continue;
219             }
220         }
221 
222         enum class TestMode {
223             kVector,
224             kIndividual,
225         };
226 
227         for (auto mode : { TestMode::kVector, TestMode::kIndividual }) {
228             // Re-create the codec to reset state and test parsing.
229             codec = SkCodec::MakeFromData(data);
230 
231             int frameCount;
232             std::vector<SkCodec::FrameInfo> frameInfos;
233             switch (mode) {
234                 case TestMode::kVector:
235                     frameInfos = codec->getFrameInfo();
236                     // getFrameInfo returns empty set for non-animated.
237                     frameCount = frameInfos.empty() ? 1 : frameInfos.size();
238                     break;
239                 case TestMode::kIndividual:
240                     frameCount = codec->getFrameCount();
241                     break;
242             }
243 
244             if (frameCount != expected) {
245                 ERRORF(r, "'%s' expected frame count: %i\tactual: %i",
246                        rec.fName, expected, frameCount);
247                 continue;
248             }
249 
250             // From here on, we are only concerned with animated images.
251             if (1 == frameCount) {
252                 continue;
253             }
254 
255             for (int i = 0; i < frameCount; i++) {
256                 SkCodec::FrameInfo frameInfo;
257                 switch (mode) {
258                     case TestMode::kVector:
259                         frameInfo = frameInfos[i];
260                         break;
261                     case TestMode::kIndividual:
262                         REPORTER_ASSERT(r, codec->getFrameInfo(i, nullptr));
263                         REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo));
264                         break;
265                 }
266 
267                 if (rec.fDurations[i] != frameInfo.fDuration) {
268                     ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i",
269                            rec.fName, i, rec.fDurations[i], frameInfo.fDuration);
270                 }
271 
272                 auto to_string = [](SkAlphaType alpha) {
273                     switch (alpha) {
274                         case kUnpremul_SkAlphaType:
275                             return "unpremul";
276                         case kOpaque_SkAlphaType:
277                             return "opaque";
278                         default:
279                             SkASSERT(false);
280                             return "unknown";
281                     }
282                 };
283 
284                 auto expectedAlpha = 0 == i ? codec->getInfo().alphaType() : rec.fAlphas[i-1];
285                 auto alpha = frameInfo.fAlphaType;
286                 if (expectedAlpha != alpha) {
287                     ERRORF(r, "%s's frame %i has wrong alpha type! expected: %s\tactual: %s",
288                            rec.fName, i, to_string(expectedAlpha), to_string(alpha));
289                 }
290 
291                 if (0 == i) {
292                     REPORTER_ASSERT(r, frameInfo.fRequiredFrame == SkCodec::kNoFrame);
293                 } else if (rec.fRequiredFrames[i-1] != frameInfo.fRequiredFrame) {
294                     ERRORF(r, "%s's frame %i has wrong dependency! expected: %i\tactual: %i",
295                            rec.fName, i, rec.fRequiredFrames[i-1], frameInfo.fRequiredFrame);
296                 }
297 
298                 REPORTER_ASSERT(r, frameInfo.fDisposalMethod == rec.fDisposalMethods[i]);
299             }
300 
301             if (TestMode::kIndividual == mode) {
302                 // No need to test decoding twice.
303                 continue;
304             }
305 
306             // Compare decoding in multiple ways:
307             // - Start from scratch for each frame. |codec| will have to decode the required frame
308             //   (and any it depends on) to decode. This is stored in |cachedFrames|.
309             // - Provide the frame that a frame depends on, so |codec| just has to blend.
310             // - Provide a frame after the required frame, which will be covered up by the newest
311             //   frame.
312             // All should look the same.
313             std::vector<SkBitmap> cachedFrames(frameCount);
314             const auto info = codec->getInfo().makeColorType(kN32_SkColorType);
315 
316             auto decode = [&](SkBitmap* bm, int index, int cachedIndex) {
317                 auto decodeInfo = info;
318                 if (index > 0) {
319                     decodeInfo = info.makeAlphaType(frameInfos[index].fAlphaType);
320                 }
321                 bm->allocPixels(decodeInfo);
322                 if (cachedIndex != SkCodec::kNoFrame) {
323                     // First copy the pixels from the cached frame
324                     const bool success =
325                             ToolUtils::copy_to(bm, kN32_SkColorType, cachedFrames[cachedIndex]);
326                     REPORTER_ASSERT(r, success);
327                 }
328                 SkCodec::Options opts;
329                 opts.fFrameIndex = index;
330                 opts.fPriorFrame = cachedIndex;
331                 const auto result = codec->getPixels(decodeInfo, bm->getPixels(), bm->rowBytes(),
332                                                      &opts);
333                 if (cachedIndex != SkCodec::kNoFrame &&
334                         restore_previous(frameInfos[cachedIndex])) {
335                     if (result == SkCodec::kInvalidParameters) {
336                         return true;
337                     }
338                     ERRORF(r, "Using a kRestorePrevious frame as fPriorFrame should fail");
339                     return false;
340                 }
341                 if (result != SkCodec::kSuccess) {
342                     ERRORF(r, "Failed to decode frame %i from %s when providing prior frame %i, "
343                               "error %i", index, rec.fName, cachedIndex, result);
344                 }
345                 return result == SkCodec::kSuccess;
346             };
347 
348             for (int i = 0; i < frameCount; i++) {
349                 SkBitmap& cachedFrame = cachedFrames[i];
350                 if (!decode(&cachedFrame, i, SkCodec::kNoFrame)) {
351                     continue;
352                 }
353                 const auto reqFrame = frameInfos[i].fRequiredFrame;
354                 if (reqFrame == SkCodec::kNoFrame) {
355                     // Nothing to compare against.
356                     continue;
357                 }
358                 for (int j = reqFrame; j < i; j++) {
359                     SkBitmap frame;
360                     if (restore_previous(frameInfos[j])) {
361                         (void) decode(&frame, i, j);
362                         continue;
363                     }
364                     if (!decode(&frame, i, j)) {
365                         continue;
366                     }
367 
368                     // Now verify they're equal.
369                     const size_t rowLen = info.bytesPerPixel() * info.width();
370                     for (int y = 0; y < info.height(); y++) {
371                         const void* cachedAddr = cachedFrame.getAddr(0, y);
372                         SkASSERT(cachedAddr != nullptr);
373                         const void* addr = frame.getAddr(0, y);
374                         SkASSERT(addr != nullptr);
375                         const bool lineMatches = memcmp(cachedAddr, addr, rowLen) == 0;
376                         if (!lineMatches) {
377                             SkString name = SkStringPrintf("cached_%i", i);
378                             write_bm(name.c_str(), cachedFrame);
379                             name = SkStringPrintf("frame_%i", i);
380                             write_bm(name.c_str(), frame);
381                             ERRORF(r, "%s's frame %i is different (starting from line %i) when "
382                                       "providing prior frame %i!", rec.fName, i, y, j);
383                             break;
384                         }
385                     }
386                 }
387             }
388         }
389     }
390 }
391 
392 // Verify that a webp image can be animated scaled down. This image has a
393 // kRestoreBG frame, so it is an interesting image to test. After decoding that
394 // frame, we have to erase its rectangle. The rectangle has to be adjusted
395 // based on the scaled size.
DEF_TEST(AndroidCodec_animated,r)396 DEF_TEST(AndroidCodec_animated, r) {
397     if (GetResourcePath().isEmpty()) {
398         return;
399     }
400 
401     const char* file = "images/required.webp";
402     sk_sp<SkData> data(GetResourceAsData(file));
403     if (!data) {
404         ERRORF(r, "Missing %s", file);
405         return;
406     }
407 
408     auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
409     if (!codec) {
410         ERRORF(r, "Failed to decode %s", file);
411         return;
412     }
413 
414     auto info = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
415 
416     for (int sampleSize : { 8, 32, 100 }) {
417         auto dimensions = codec->codec()->getScaledDimensions(1.0f / sampleSize);
418         info = info.makeWH(dimensions.width(), dimensions.height());
419         SkBitmap bm;
420         bm.allocPixels(info);
421 
422         SkCodec::Options options;
423         for (int i = 0; i < codec->codec()->getFrameCount(); ++i) {
424             SkCodec::FrameInfo frameInfo;
425             REPORTER_ASSERT(r, codec->codec()->getFrameInfo(i, &frameInfo));
426             if (5 == i) {
427                 REPORTER_ASSERT(r, frameInfo.fDisposalMethod
428                         == SkCodecAnimation::DisposalMethod::kRestoreBGColor);
429             }
430             options.fFrameIndex = i;
431             options.fPriorFrame = i - 1;
432             info = info.makeAlphaType(frameInfo.fAlphaType);
433 
434             auto result = codec->codec()->getPixels(info, bm.getPixels(), bm.rowBytes(),
435                                                     &options);
436             REPORTER_ASSERT(r, result == SkCodec::kSuccess);
437 
438             // Now compare to not using prior frame.
439             SkBitmap bm2;
440             bm2.allocPixels(info);
441 
442             options.fPriorFrame = SkCodec::kNoFrame;
443             result = codec->codec()->getPixels(info, bm2.getPixels(), bm2.rowBytes(),
444                                                &options);
445             REPORTER_ASSERT(r, result == SkCodec::kSuccess);
446 
447             for (int y = 0; y < info.height(); ++y) {
448                 if (memcmp(bm.getAddr32(0, y), bm2.getAddr32(0, y), info.minRowBytes())) {
449                     ERRORF(r, "pixel mismatch for sample size %i, frame %i resulting in "
450                               "dimensions %i x %i line %i\n",
451                               sampleSize, i, info.width(), info.height(), y);
452                     break;
453                 }
454             }
455         }
456     }
457 }
458 
DEF_TEST(AnimCodecPlayer,r)459 DEF_TEST(AnimCodecPlayer, r) {
460     static constexpr struct {
461         const char* fFile;
462         uint32_t    fDuration;
463         SkISize     fSize;
464     } gTests[] = {
465         { "images/alphabetAnim.gif", 1300, {100, 100} },
466         { "images/randPixels.gif"  ,    0, {  8,   8} },
467         { "images/randPixels.jpg"  ,    0, {  8,   8} },
468         { "images/randPixels.png"  ,    0, {  8,   8} },
469     };
470 
471     for (const auto& test : gTests) {
472         auto codec = SkCodec::MakeFromData(GetResourceAsData(test.fFile));
473         REPORTER_ASSERT(r, codec);
474 
475         auto player = skstd::make_unique<SkAnimCodecPlayer>(std::move(codec));
476         if (player->duration() != test.fDuration) {
477             printf("*** %d vs %d\n", player->duration(), test.fDuration);
478         }
479         REPORTER_ASSERT(r, player->duration() == test.fDuration);
480 
481         auto f0 = player->getFrame();
482         REPORTER_ASSERT(r, f0);
483         REPORTER_ASSERT(r, f0->bounds().size() == test.fSize);
484 
485         player->seek(500);
486         auto f1 = player->getFrame();
487         REPORTER_ASSERT(r, f1);
488         REPORTER_ASSERT(r, f1->bounds().size() == test.fSize);
489     }
490 }
491