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