• 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 "SkBitmap.h"
9 #include "SkCodec.h"
10 #include "SkCommonFlags.h"
11 #include "SkImageEncoder.h"
12 #include "SkOSPath.h"
13 #include "SkStream.h"
14 
15 #include "Resources.h"
16 #include "Test.h"
17 #include "sk_tool_utils.h"
18 
19 #include <initializer_list>
20 #include <vector>
21 
write_bm(const char * name,const SkBitmap & bm)22 static void write_bm(const char* name, const SkBitmap& bm) {
23     if (FLAGS_writePath.isEmpty()) {
24         return;
25     }
26 
27     SkString filename = SkOSPath::Join(FLAGS_writePath[0], name);
28     filename.appendf(".png");
29     SkFILEWStream file(filename.c_str());
30     if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
31         SkDebugf("failed to write '%s'\n", filename.c_str());
32     }
33 }
34 
DEF_TEST(Codec_trunc,r)35 DEF_TEST(Codec_trunc, r) {
36     sk_sp<SkData> data(GetResourceAsData("box.gif"));
37     if (!data) {
38         return;
39     }
40     data = SkData::MakeSubset(data.get(), 0, 23);
41     std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
42     codec->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("blendBG.webp"));
50     if (!data) {
51         return;
52     }
53     std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(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::kNone;
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     #define kOpaque         kOpaque_SkAlphaType
73     #define kUnpremul       kUnpremul_SkAlphaType
74     #define kKeep           SkCodecAnimation::DisposalMethod::kKeep
75     #define kRestoreBG      SkCodecAnimation::DisposalMethod::kRestoreBGColor
76     #define kRestorePrev    SkCodecAnimation::DisposalMethod::kRestorePrevious
77     static const struct {
78         const char*                                   fName;
79         int                                           fFrameCount;
80         // One less than fFramecount, since the first frame is always
81         // independent.
82         std::vector<int>                              fRequiredFrames;
83         // Same, since the first frame should match getInfo.
84         std::vector<SkAlphaType>                      fAlphaTypes;
85         // The size of this one should match fFrameCount for animated, empty
86         // otherwise.
87         std::vector<int>                              fDurations;
88         int                                           fRepetitionCount;
89         std::vector<SkCodecAnimation::DisposalMethod> fDisposalMethods;
90     } gRecs[] = {
91         { "required.gif", 7,
92             { 0, 1, 1, SkCodec::kNone, 4, 4 },
93             { kOpaque, kUnpremul, kUnpremul, kOpaque, kOpaque, kOpaque },
94             { 100, 100, 100, 100, 100, 100, 100 },
95             0,
96             { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep } },
97         { "alphabetAnim.gif", 13,
98             { SkCodec::kNone, 0, 0, 0, 0, 5, 6, SkCodec::kNone,
99               SkCodec::kNone, SkCodec::kNone, 10, 11 },
100             { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
101               kUnpremul, kUnpremul, kUnpremul, kOpaque, kOpaque, 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         { "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         { "randPixelsAnim.gif", 13,
118             // required frames
119             { SkCodec::kNone, 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         { "box.gif", 1, {}, {}, {}, 0, { kKeep } },
130         { "color_wheel.gif", 1, {}, {}, {}, 0, { kKeep } },
131         { "test640x479.gif", 4, { 0, 1, 2 },
132                 { kOpaque, kOpaque, kOpaque },
133                 { 200, 200, 200, 200 },
134                 SkCodec::kRepetitionCountInfinite,
135                 { kKeep, kKeep, kKeep, kKeep } },
136         { "colorTables.gif", 2, { 0 }, { kOpaque }, { 1000, 1000 }, 5,
137                 { kKeep, kKeep } },
138 
139         { "arrow.png",  1, {}, {}, {}, 0, {} },
140         { "google_chrome.ico", 1, {}, {}, {}, 0, {} },
141         { "brickwork-texture.jpg", 1, {}, {}, {}, 0, {} },
142 #if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
143         { "dng_with_preview.dng", 1, {}, {}, {}, 0, {} },
144 #endif
145         { "mandrill.wbmp", 1, {}, {}, {}, 0, {} },
146         { "randPixels.bmp", 1, {}, {}, {}, 0, {} },
147         { "yellow_rose.webp", 1, {}, {}, {}, 0, {} },
148         { "webp-animated.webp", 3, { 0, 1 }, { kOpaque, kOpaque },
149             { 1000, 500, 1000 }, SkCodec::kRepetitionCountInfinite,
150             { kKeep, kKeep, kKeep } },
151         { "blendBG.webp", 7, { 0, SkCodec::kNone, SkCodec::kNone, SkCodec::kNone,
152                                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         { "required.webp", 7,
157             { 0, 1, 1, SkCodec::kNone, 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     #undef kOpaque
164     #undef kUnpremul
165     #undef kKeep
166     #undef kRestorePrev
167     #undef kRestoreBG
168 
169     for (const auto& rec : gRecs) {
170         sk_sp<SkData> data(GetResourceAsData(rec.fName));
171         if (!data) {
172             // Useful error statement, but sometimes people run tests without
173             // resources, and they do not want to see these messages.
174             //ERRORF(r, "Missing resources? Could not find '%s'", rec.fName);
175             continue;
176         }
177 
178         std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
179         if (!codec) {
180             ERRORF(r, "Failed to create an SkCodec from '%s'", rec.fName);
181             continue;
182         }
183 
184         {
185             SkCodec::FrameInfo frameInfo;
186             REPORTER_ASSERT(r, !codec->getFrameInfo(0, &frameInfo));
187         }
188 
189         const int repetitionCount = codec->getRepetitionCount();
190         if (repetitionCount != rec.fRepetitionCount) {
191             ERRORF(r, "%s repetition count does not match! expected: %i\tactual: %i",
192                       rec.fName, rec.fRepetitionCount, repetitionCount);
193         }
194 
195         const int expected = rec.fFrameCount;
196         if (rec.fRequiredFrames.size() + 1 != static_cast<size_t>(expected)) {
197             ERRORF(r, "'%s' has wrong number entries in fRequiredFrames; expected: %i\tactual: %i",
198                    rec.fName, expected - 1, rec.fRequiredFrames.size());
199             continue;
200         }
201 
202         if (expected > 1) {
203             if (rec.fDurations.size() != static_cast<size_t>(expected)) {
204                 ERRORF(r, "'%s' has wrong number entries in fDurations; expected: %i\tactual: %i",
205                        rec.fName, expected, rec.fDurations.size());
206                 continue;
207             }
208 
209             if (rec.fAlphaTypes.size() + 1 != static_cast<size_t>(expected)) {
210                 ERRORF(r, "'%s' has wrong number entries in fAlphaTypes; expected: %i\tactual: %i",
211                        rec.fName, expected - 1, rec.fAlphaTypes.size());
212                 continue;
213             }
214 
215             if (rec.fDisposalMethods.size() != static_cast<size_t>(expected)) {
216                 ERRORF(r, "'%s' has wrong number entries in fDisposalMethods; "
217                        "expected %i\tactual: %i",
218                        rec.fName, expected, rec.fDisposalMethods.size());
219                 continue;
220             }
221         }
222 
223         enum class TestMode {
224             kVector,
225             kIndividual,
226         };
227 
228         for (auto mode : { TestMode::kVector, TestMode::kIndividual }) {
229             // Re-create the codec to reset state and test parsing.
230             codec.reset(SkCodec::NewFromData(data));
231 
232             int frameCount;
233             std::vector<SkCodec::FrameInfo> frameInfos;
234             switch (mode) {
235                 case TestMode::kVector:
236                     frameInfos = codec->getFrameInfo();
237                     // getFrameInfo returns empty set for non-animated.
238                     frameCount = frameInfos.empty() ? 1 : frameInfos.size();
239                     break;
240                 case TestMode::kIndividual:
241                     frameCount = codec->getFrameCount();
242                     break;
243             }
244 
245             if (frameCount != expected) {
246                 ERRORF(r, "'%s' expected frame count: %i\tactual: %i",
247                        rec.fName, expected, frameCount);
248                 continue;
249             }
250 
251             // From here on, we are only concerned with animated images.
252             if (1 == frameCount) {
253                 continue;
254             }
255 
256             for (int i = 0; i < frameCount; i++) {
257                 SkCodec::FrameInfo frameInfo;
258                 switch (mode) {
259                     case TestMode::kVector:
260                         frameInfo = frameInfos[i];
261                         break;
262                     case TestMode::kIndividual:
263                         REPORTER_ASSERT(r, codec->getFrameInfo(i, nullptr));
264                         REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo));
265                         break;
266                 }
267 
268                 if (rec.fDurations[i] != frameInfo.fDuration) {
269                     ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i",
270                            rec.fName, i, rec.fDurations[i], frameInfo.fDuration);
271                 }
272 
273                 auto to_string = [](SkAlphaType type) {
274                     switch (type) {
275                         case kUnpremul_SkAlphaType:
276                             return "unpremul";
277                         case kOpaque_SkAlphaType:
278                             return "opaque";
279                         default:
280                             return "other";
281                     }
282                 };
283 
284                 auto expectedAlpha = 0 == i ? codec->getInfo().alphaType() : rec.fAlphaTypes[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::kNone);
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::kNone) {
323                     // First copy the pixels from the cached frame
324                     const bool success = sk_tool_utils::copy_to(bm, kN32_SkColorType,
325                             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::kNone && restore_previous(frameInfos[cachedIndex])) {
334                     if (result == SkCodec::kInvalidParameters) {
335                         return true;
336                     }
337                     ERRORF(r, "Using a kRestorePrevious frame as fPriorFrame should fail");
338                     return false;
339                 }
340                 if (result != SkCodec::kSuccess) {
341                     ERRORF(r, "Failed to decode frame %i from %s when providing prior frame %i, "
342                               "error %i", index, rec.fName, cachedIndex, result);
343                 }
344                 return result == SkCodec::kSuccess;
345             };
346 
347             for (int i = 0; i < frameCount; i++) {
348                 SkBitmap& cachedFrame = cachedFrames[i];
349                 if (!decode(&cachedFrame, i, SkCodec::kNone)) {
350                     continue;
351                 }
352                 const auto reqFrame = frameInfos[i].fRequiredFrame;
353                 if (reqFrame == SkCodec::kNone) {
354                     // Nothing to compare against.
355                     continue;
356                 }
357                 for (int j = reqFrame; j < i; j++) {
358                     SkBitmap frame;
359                     if (restore_previous(frameInfos[j])) {
360                         (void) decode(&frame, i, j);
361                         continue;
362                     }
363                     if (!decode(&frame, i, j)) {
364                         continue;
365                     }
366 
367                     // Now verify they're equal.
368                     const size_t rowLen = info.bytesPerPixel() * info.width();
369                     for (int y = 0; y < info.height(); y++) {
370                         const void* cachedAddr = cachedFrame.getAddr(0, y);
371                         SkASSERT(cachedAddr != nullptr);
372                         const void* addr = frame.getAddr(0, y);
373                         SkASSERT(addr != nullptr);
374                         const bool lineMatches = memcmp(cachedAddr, addr, rowLen) == 0;
375                         if (!lineMatches) {
376                             SkString name = SkStringPrintf("cached_%i", i);
377                             write_bm(name.c_str(), cachedFrame);
378                             name = SkStringPrintf("frame_%i", i);
379                             write_bm(name.c_str(), frame);
380                             ERRORF(r, "%s's frame %i is different (starting from line %i) when "
381                                       "providing prior frame %i!", rec.fName, i, y, j);
382                             break;
383                         }
384                     }
385                 }
386             }
387         }
388     }
389 }
390