• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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 "SkAnimatedImage.h"
12 #include "SkBitmap.h"
13 #include "SkCanvas.h"
14 #include "SkCodec.h"
15 #include "SkColor.h"
16 #include "SkData.h"
17 #include "SkImageInfo.h"
18 #include "SkPicture.h"
19 #include "SkRefCnt.h"
20 #include "SkSize.h"
21 #include "SkString.h"
22 #include "SkTypes.h"
23 #include "SkUnPreMultiply.h"
24 #include "Test.h"
25 #include "sk_tool_utils.h"
26 
27 #include <algorithm>
28 #include <memory>
29 #include <vector>
30 
DEF_TEST(AnimatedImage_scaled,r)31 DEF_TEST(AnimatedImage_scaled, r) {
32     if (GetResourcePath().isEmpty()) {
33         return;
34     }
35 
36     const char* file = "images/alphabetAnim.gif";
37     auto data = GetResourceAsData(file);
38     if (!data) {
39         ERRORF(r, "Could not get %s", file);
40         return;
41     }
42 
43     auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data));
44     if (!codec) {
45         ERRORF(r, "Could not create codec for %s", file);
46         return;
47     }
48 
49     // Force the drawable follow its special case that requires scaling.
50     auto info = codec->getInfo();
51     info = info.makeWH(info.width() - 5, info.height() - 5);
52     auto rect = info.bounds();
53     auto image = SkAnimatedImage::Make(std::move(codec), info, rect, nullptr);
54     if (!image) {
55         ERRORF(r, "Failed to create animated image for %s", file);
56         return;
57     }
58 
59     // Clear a bitmap to non-transparent and draw to it. pixels that are transparent
60     // in the image should not replace the original non-transparent color.
61     SkBitmap bm;
62     bm.allocPixels(SkImageInfo::MakeN32Premul(info.width(), info.height()));
63     bm.eraseColor(SK_ColorBLUE);
64     SkCanvas canvas(bm);
65     image->draw(&canvas);
66     for (int i = 0; i < info.width();  ++i)
67     for (int j = 0; j < info.height(); ++j) {
68         if (*bm.getAddr32(i, j) == SK_ColorTRANSPARENT) {
69             ERRORF(r, "Erased color underneath!");
70             return;
71         }
72     }
73 }
74 
compare_bitmaps(skiatest::Reporter * r,const char * file,int expectedFrame,const SkBitmap & expectedBm,const SkBitmap & actualBm)75 static bool compare_bitmaps(skiatest::Reporter* r,
76                             const char* file,
77                             int expectedFrame,
78                             const SkBitmap& expectedBm,
79                             const SkBitmap& actualBm) {
80     REPORTER_ASSERT(r, expectedBm.colorType() == actualBm.colorType());
81     REPORTER_ASSERT(r, expectedBm.dimensions() == actualBm.dimensions());
82     for (int i = 0; i < actualBm.width();  ++i)
83     for (int j = 0; j < actualBm.height(); ++j) {
84         SkColor expected = SkUnPreMultiply::PMColorToColor(*expectedBm.getAddr32(i, j));
85         SkColor actual   = SkUnPreMultiply::PMColorToColor(*actualBm  .getAddr32(i, j));
86         if (expected != actual) {
87             ERRORF(r, "frame %i of %s does not match at pixel %i, %i!"
88                             " expected %x\tactual: %x",
89                             expectedFrame, file, i, j, expected, actual);
90             SkString expected_name = SkStringPrintf("expected_%c", '0' + expectedFrame);
91             SkString actual_name   = SkStringPrintf("actual_%c",   '0' + expectedFrame);
92             write_bm(expected_name.c_str(), expectedBm);
93             write_bm(actual_name.c_str(),   actualBm);
94             return false;
95         }
96     }
97     return true;
98 }
99 
DEF_TEST(AnimatedImage_copyOnWrite,r)100 DEF_TEST(AnimatedImage_copyOnWrite, r) {
101     if (GetResourcePath().isEmpty()) {
102         return;
103     }
104     for (const char* file : { "images/alphabetAnim.gif",
105                               "images/colorTables.gif",
106                               "images/webp-animated.webp",
107                               "images/required.webp",
108                               }) {
109         auto data = GetResourceAsData(file);
110         if (!data) {
111             ERRORF(r, "Could not get %s", file);
112             continue;
113         }
114 
115         auto codec = SkCodec::MakeFromData(data);
116         if (!codec) {
117             ERRORF(r, "Could not create codec for %s", file);
118             continue;
119         }
120 
121         const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
122         const int frameCount = codec->getFrameCount();
123         auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
124         if (!androidCodec) {
125             ERRORF(r, "Could not create androidCodec for %s", file);
126             continue;
127         }
128 
129         auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec));
130         if (!animatedImage) {
131             ERRORF(r, "Could not create animated image for %s", file);
132             continue;
133         }
134         animatedImage->setRepetitionCount(0);
135 
136         std::vector<SkBitmap> expected(frameCount);
137         std::vector<sk_sp<SkPicture>> pictures(frameCount);
138         for (int i = 0; i < frameCount; i++) {
139             SkBitmap& bm = expected[i];
140             bm.allocPixels(imageInfo);
141             bm.eraseColor(SK_ColorTRANSPARENT);
142             SkCanvas canvas(bm);
143 
144             pictures[i].reset(animatedImage->newPictureSnapshot());
145             canvas.drawPicture(pictures[i]);
146 
147             const auto duration = animatedImage->decodeNextFrame();
148             // We're attempting to decode i + 1, so decodeNextFrame will return
149             // kFinished if that is the last frame (or we attempt to decode one
150             // more).
151             if (i >= frameCount - 2) {
152                 REPORTER_ASSERT(r, duration == SkAnimatedImage::kFinished);
153             } else {
154                 REPORTER_ASSERT(r, duration != SkAnimatedImage::kFinished);
155             }
156         }
157 
158         for (int i = 0; i < frameCount; i++) {
159             SkBitmap test;
160             test.allocPixels(imageInfo);
161             test.eraseColor(SK_ColorTRANSPARENT);
162             SkCanvas canvas(test);
163 
164             canvas.drawPicture(pictures[i]);
165 
166             compare_bitmaps(r, file, i, expected[i], test);
167         }
168     }
169 }
170 
DEF_TEST(AnimatedImage,r)171 DEF_TEST(AnimatedImage, r) {
172     if (GetResourcePath().isEmpty()) {
173         return;
174     }
175     for (const char* file : { "images/alphabetAnim.gif",
176                               "images/colorTables.gif",
177                               "images/webp-animated.webp",
178                               "images/required.webp",
179                               }) {
180         auto data = GetResourceAsData(file);
181         if (!data) {
182             ERRORF(r, "Could not get %s", file);
183             continue;
184         }
185 
186         auto codec = SkCodec::MakeFromData(data);
187         if (!codec) {
188             ERRORF(r, "Could not create codec for %s", file);
189             continue;
190         }
191 
192         const int defaultRepetitionCount = codec->getRepetitionCount();
193         std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
194         std::vector<SkBitmap> frames(frameInfos.size());
195         // Used down below for our test image.
196         const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
197 
198         for (size_t i = 0; i < frameInfos.size(); ++i) {
199             auto info = codec->getInfo().makeAlphaType(frameInfos[i].fAlphaType);
200             auto& bm = frames[i];
201 
202             SkCodec::Options options;
203             options.fFrameIndex = (int) i;
204             options.fPriorFrame = frameInfos[i].fRequiredFrame;
205             if (options.fPriorFrame == SkCodec::kNoFrame) {
206                 bm.allocPixels(info);
207                 bm.eraseColor(0);
208             } else {
209                 const SkBitmap& priorFrame = frames[options.fPriorFrame];
210                 if (!sk_tool_utils::copy_to(&bm, priorFrame.colorType(), priorFrame)) {
211                     ERRORF(r, "Failed to copy %s frame %i", file, options.fPriorFrame);
212                     options.fPriorFrame = SkCodec::kNoFrame;
213                 }
214                 REPORTER_ASSERT(r, bm.setAlphaType(frameInfos[i].fAlphaType));
215             }
216 
217             auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), &options);
218             if (result != SkCodec::kSuccess) {
219                 ERRORF(r, "error in %s frame %zu: %s", file, i, SkCodec::ResultToString(result));
220             }
221         }
222 
223         auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
224         if (!androidCodec) {
225             ERRORF(r, "Could not create androidCodec for %s", file);
226             continue;
227         }
228 
229         auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec));
230         if (!animatedImage) {
231             ERRORF(r, "Could not create animated image for %s", file);
232             continue;
233         }
234 
235         REPORTER_ASSERT(r, defaultRepetitionCount == animatedImage->getRepetitionCount());
236 
237         auto testDraw = [r, &frames, &imageInfo, file](const sk_sp<SkAnimatedImage>& animatedImage,
238                                                        int expectedFrame) {
239             SkBitmap test;
240             test.allocPixels(imageInfo);
241             test.eraseColor(0);
242             SkCanvas c(test);
243             animatedImage->draw(&c);
244 
245             const SkBitmap& frame = frames[expectedFrame];
246             return compare_bitmaps(r, file, expectedFrame, frame, test);
247         };
248 
249         REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration);
250 
251         if (!testDraw(animatedImage, 0)) {
252             ERRORF(r, "Did not start with frame 0");
253             continue;
254         }
255 
256         // Start at an arbitrary time.
257         bool failed = false;
258         for (size_t i = 1; i < frameInfos.size(); ++i) {
259             const int frameTime = animatedImage->decodeNextFrame();
260             REPORTER_ASSERT(r, frameTime == animatedImage->currentFrameDuration());
261 
262             if (i == frameInfos.size() - 1 && defaultRepetitionCount == 0) {
263                 REPORTER_ASSERT(r, frameTime == SkAnimatedImage::kFinished);
264                 REPORTER_ASSERT(r, animatedImage->isFinished());
265             } else {
266                 REPORTER_ASSERT(r, frameTime == frameInfos[i].fDuration);
267                 REPORTER_ASSERT(r, !animatedImage->isFinished());
268             }
269 
270             if (!testDraw(animatedImage, i)) {
271                 ERRORF(r, "Did not update to %i properly", i);
272                 failed = true;
273                 break;
274             }
275         }
276 
277         if (failed) {
278             continue;
279         }
280 
281         animatedImage->reset();
282         REPORTER_ASSERT(r, !animatedImage->isFinished());
283         if (!testDraw(animatedImage, 0)) {
284             ERRORF(r, "reset failed");
285             continue;
286         }
287 
288         // Test reset from all the frames.
289         // j is the frame to call reset on.
290         for (int j = 0; j < (int) frameInfos.size(); ++j) {
291             if (failed) {
292                 break;
293             }
294 
295             // i is the frame to decode.
296             for (int i = 0; i <= j; ++i) {
297                 if (i == j) {
298                     animatedImage->reset();
299                     if (!testDraw(animatedImage, 0)) {
300                         ERRORF(r, "reset failed for image %s from frame %i",
301                                 file, i);
302                         failed = true;
303                         break;
304                     }
305                 } else if (i != 0) {
306                     animatedImage->decodeNextFrame();
307                     if (!testDraw(animatedImage, i)) {
308                         ERRORF(r, "failed to match frame %i in %s on iteration %i",
309                                 i, file, j);
310                         failed = true;
311                         break;
312                     }
313                 }
314             }
315         }
316 
317         if (failed) {
318             continue;
319         }
320 
321         for (int loopCount : { 0, 1, 2, 5 }) {
322             animatedImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec(
323                         SkCodec::MakeFromData(data)));
324             animatedImage->setRepetitionCount(loopCount);
325             REPORTER_ASSERT(r, animatedImage->getRepetitionCount() == loopCount);
326 
327             for (int loops = 0; loops <= loopCount; loops++) {
328                 if (failed) {
329                     break;
330                 }
331                 REPORTER_ASSERT(r, !animatedImage->isFinished());
332                 for (size_t i = 1; i <= frameInfos.size(); ++i) {
333                     const int frameTime = animatedImage->decodeNextFrame();
334                     if (frameTime == SkAnimatedImage::kFinished) {
335                         if (loops != loopCount) {
336                             ERRORF(r, "%s animation stopped early: loops: %i\tloopCount: %i",
337                                     file, loops, loopCount);
338                             failed = true;
339                         }
340                         if (i != frameInfos.size() - 1) {
341                             ERRORF(r, "%s animation stopped early: i: %i\tsize: %i",
342                                     file, i, frameInfos.size());
343                             failed = true;
344                         }
345                         break;
346                     }
347                 }
348             }
349 
350             if (!animatedImage->isFinished()) {
351                 ERRORF(r, "%s animation should have finished with specified loop count (%i)",
352                           file, loopCount);
353             }
354         }
355     }
356 }
357