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