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