• 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 size = codec->getInfo().dimensions();
51     size.set(size.width() - 5, size.height() - 5);
52     auto rect = SkIRect::MakeSize(size);
53     auto image = SkAnimatedImage::Make(std::move(codec), size, 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(size.width(), size.height()));
63     bm.eraseColor(SK_ColorBLUE);
64     SkCanvas canvas(bm);
65     image->draw(&canvas);
66     for (int i = 0; i < size.width();  ++i)
67     for (int j = 0; j < size.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 
100 // Temporary hack to avoid linear sRGB 8888 surfaces.
temporarily_sanitize(SkImageInfo info)101 static SkImageInfo temporarily_sanitize(SkImageInfo info) {
102     if (info.colorType() == kRGBA_8888_SkColorType ||
103         info.colorType() == kBGRA_8888_SkColorType) {
104         if (info.colorSpace() && info.colorSpace()->isSRGB()) {
105             info = info.makeColorSpace(nullptr);
106         }
107     }
108     return info;
109 }
110 
DEF_TEST(AnimatedImage_copyOnWrite,r)111 DEF_TEST(AnimatedImage_copyOnWrite, r) {
112     if (GetResourcePath().isEmpty()) {
113         return;
114     }
115     for (const char* file : { "images/alphabetAnim.gif",
116                               "images/colorTables.gif",
117                               "images/webp-animated.webp",
118                               "images/required.webp",
119                               }) {
120         auto data = GetResourceAsData(file);
121         if (!data) {
122             ERRORF(r, "Could not get %s", file);
123             continue;
124         }
125 
126         auto codec = SkCodec::MakeFromData(data);
127         if (!codec) {
128             ERRORF(r, "Could not create codec for %s", file);
129             continue;
130         }
131 
132         const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
133         const int frameCount = codec->getFrameCount();
134         auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
135         if (!androidCodec) {
136             ERRORF(r, "Could not create androidCodec for %s", file);
137             continue;
138         }
139 
140         auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec));
141         if (!animatedImage) {
142             ERRORF(r, "Could not create animated image for %s", file);
143             continue;
144         }
145         animatedImage->setRepetitionCount(0);
146 
147         std::vector<SkBitmap> expected(frameCount);
148         std::vector<sk_sp<SkPicture>> pictures(frameCount);
149         for (int i = 0; i < frameCount; i++) {
150             SkBitmap& bm = expected[i];
151             bm.allocPixels(temporarily_sanitize(imageInfo));
152             bm.eraseColor(SK_ColorTRANSPARENT);
153             SkCanvas canvas(bm);
154 
155             pictures[i].reset(animatedImage->newPictureSnapshot());
156             canvas.drawPicture(pictures[i]);
157 
158             const auto duration = animatedImage->decodeNextFrame();
159             // We're attempting to decode i + 1, so decodeNextFrame will return
160             // kFinished if that is the last frame (or we attempt to decode one
161             // more).
162             if (i >= frameCount - 2) {
163                 REPORTER_ASSERT(r, duration == SkAnimatedImage::kFinished);
164             } else {
165                 REPORTER_ASSERT(r, duration != SkAnimatedImage::kFinished);
166             }
167         }
168 
169         for (int i = 0; i < frameCount; i++) {
170             SkBitmap test;
171             test.allocPixels(temporarily_sanitize(imageInfo));
172             test.eraseColor(SK_ColorTRANSPARENT);
173             SkCanvas canvas(test);
174 
175             canvas.drawPicture(pictures[i]);
176 
177             compare_bitmaps(r, file, i, expected[i], test);
178         }
179     }
180 }
181 
DEF_TEST(AnimatedImage,r)182 DEF_TEST(AnimatedImage, r) {
183     if (GetResourcePath().isEmpty()) {
184         return;
185     }
186     for (const char* file : { "images/alphabetAnim.gif",
187                               "images/colorTables.gif",
188                               "images/webp-animated.webp",
189                               "images/required.webp",
190                               }) {
191         auto data = GetResourceAsData(file);
192         if (!data) {
193             ERRORF(r, "Could not get %s", file);
194             continue;
195         }
196 
197         auto codec = SkCodec::MakeFromData(data);
198         if (!codec) {
199             ERRORF(r, "Could not create codec for %s", file);
200             continue;
201         }
202 
203         const int defaultRepetitionCount = codec->getRepetitionCount();
204         std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo();
205         std::vector<SkBitmap> frames(frameInfos.size());
206         // Used down below for our test image.
207         const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
208 
209         for (size_t i = 0; i < frameInfos.size(); ++i) {
210             auto info = codec->getInfo().makeAlphaType(frameInfos[i].fAlphaType);
211             auto& bm = frames[i];
212 
213             SkCodec::Options options;
214             options.fFrameIndex = (int) i;
215             options.fPriorFrame = frameInfos[i].fRequiredFrame;
216             if (options.fPriorFrame == SkCodec::kNoFrame) {
217                 bm.allocPixels(info);
218                 bm.eraseColor(0);
219             } else {
220                 const SkBitmap& priorFrame = frames[options.fPriorFrame];
221                 if (!sk_tool_utils::copy_to(&bm, priorFrame.colorType(), priorFrame)) {
222                     ERRORF(r, "Failed to copy %s frame %i", file, options.fPriorFrame);
223                     options.fPriorFrame = SkCodec::kNoFrame;
224                 }
225                 REPORTER_ASSERT(r, bm.setAlphaType(frameInfos[i].fAlphaType));
226             }
227 
228             auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), &options);
229             if (result != SkCodec::kSuccess) {
230                 ERRORF(r, "error in %s frame %zu: %s", file, i, SkCodec::ResultToString(result));
231             }
232         }
233 
234         auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
235         if (!androidCodec) {
236             ERRORF(r, "Could not create androidCodec for %s", file);
237             continue;
238         }
239 
240         auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec));
241         if (!animatedImage) {
242             ERRORF(r, "Could not create animated image for %s", file);
243             continue;
244         }
245 
246         REPORTER_ASSERT(r, defaultRepetitionCount == animatedImage->getRepetitionCount());
247 
248         auto testDraw = [r, &frames, &imageInfo, file](const sk_sp<SkAnimatedImage>& animatedImage,
249                                                        int expectedFrame) {
250             SkBitmap test;
251             test.allocPixels(temporarily_sanitize(imageInfo));
252             test.eraseColor(0);
253             SkCanvas c(test);
254             animatedImage->draw(&c);
255 
256             const SkBitmap& frame = frames[expectedFrame];
257             return compare_bitmaps(r, file, expectedFrame, frame, test);
258         };
259 
260         REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration);
261 
262         if (!testDraw(animatedImage, 0)) {
263             ERRORF(r, "Did not start with frame 0");
264             continue;
265         }
266 
267         // Start at an arbitrary time.
268         bool failed = false;
269         for (size_t i = 1; i < frameInfos.size(); ++i) {
270             const int frameTime = animatedImage->decodeNextFrame();
271             REPORTER_ASSERT(r, frameTime == animatedImage->currentFrameDuration());
272 
273             if (i == frameInfos.size() - 1 && defaultRepetitionCount == 0) {
274                 REPORTER_ASSERT(r, frameTime == SkAnimatedImage::kFinished);
275                 REPORTER_ASSERT(r, animatedImage->isFinished());
276             } else {
277                 REPORTER_ASSERT(r, frameTime == frameInfos[i].fDuration);
278                 REPORTER_ASSERT(r, !animatedImage->isFinished());
279             }
280 
281             if (!testDraw(animatedImage, i)) {
282                 ERRORF(r, "Did not update to %i properly", i);
283                 failed = true;
284                 break;
285             }
286         }
287 
288         if (failed) {
289             continue;
290         }
291 
292         animatedImage->reset();
293         REPORTER_ASSERT(r, !animatedImage->isFinished());
294         if (!testDraw(animatedImage, 0)) {
295             ERRORF(r, "reset failed");
296             continue;
297         }
298 
299         // Test reset from all the frames.
300         // j is the frame to call reset on.
301         for (int j = 0; j < (int) frameInfos.size(); ++j) {
302             if (failed) {
303                 break;
304             }
305 
306             // i is the frame to decode.
307             for (int i = 0; i <= j; ++i) {
308                 if (i == j) {
309                     animatedImage->reset();
310                     if (!testDraw(animatedImage, 0)) {
311                         ERRORF(r, "reset failed for image %s from frame %i",
312                                 file, i);
313                         failed = true;
314                         break;
315                     }
316                 } else if (i != 0) {
317                     animatedImage->decodeNextFrame();
318                     if (!testDraw(animatedImage, i)) {
319                         ERRORF(r, "failed to match frame %i in %s on iteration %i",
320                                 i, file, j);
321                         failed = true;
322                         break;
323                     }
324                 }
325             }
326         }
327 
328         if (failed) {
329             continue;
330         }
331 
332         for (int loopCount : { 0, 1, 2, 5 }) {
333             animatedImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec(
334                         SkCodec::MakeFromData(data)));
335             animatedImage->setRepetitionCount(loopCount);
336             REPORTER_ASSERT(r, animatedImage->getRepetitionCount() == loopCount);
337 
338             for (int loops = 0; loops <= loopCount; loops++) {
339                 if (failed) {
340                     break;
341                 }
342                 REPORTER_ASSERT(r, !animatedImage->isFinished());
343                 for (size_t i = 1; i <= frameInfos.size(); ++i) {
344                     const int frameTime = animatedImage->decodeNextFrame();
345                     if (frameTime == SkAnimatedImage::kFinished) {
346                         if (loops != loopCount) {
347                             ERRORF(r, "%s animation stopped early: loops: %i\tloopCount: %i",
348                                     file, loops, loopCount);
349                             failed = true;
350                         }
351                         if (i != frameInfos.size() - 1) {
352                             ERRORF(r, "%s animation stopped early: i: %i\tsize: %i",
353                                     file, i, frameInfos.size());
354                             failed = true;
355                         }
356                         break;
357                     }
358                 }
359             }
360 
361             if (!animatedImage->isFinished()) {
362                 ERRORF(r, "%s animation should have finished with specified loop count (%i)",
363                           file, loopCount);
364             }
365         }
366     }
367 }
368