blob: 98f486068e0d1c6bcac7ac72db1409f620d47f58 [file] [log] [blame]
Leon Scroggins III4c119452018-01-20 10:33:24 -05001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#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"
Leon Scroggins III4c119452018-01-20 10:33:24 -050026
Ben Wagner9707a7e2019-05-06 17:17:19 -040027#include <initializer_list>
Ben Wagner1ebeefe2018-03-02 16:59:53 -050028#include <memory>
Ben Wagner9707a7e2019-05-06 17:17:19 -040029#include <utility>
Leon Scroggins III4c119452018-01-20 10:33:24 -050030#include <vector>
31
Leon Scroggins III2f862202018-03-05 14:19:40 -050032DEF_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.
Leon Scroggins III85463352019-02-04 15:22:10 -050051 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);
Leon Scroggins III2f862202018-03-05 14:19:40 -050055 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;
Leon Scroggins III85463352019-02-04 15:22:10 -050063 bm.allocPixels(SkImageInfo::MakeN32Premul(info.width(), info.height()));
Leon Scroggins III2f862202018-03-05 14:19:40 -050064 bm.eraseColor(SK_ColorBLUE);
65 SkCanvas canvas(bm);
66 image->draw(&canvas);
Leon Scroggins III85463352019-02-04 15:22:10 -050067 for (int i = 0; i < info.width(); ++i)
68 for (int j = 0; j < info.height(); ++j) {
Leon Scroggins III2f862202018-03-05 14:19:40 -050069 if (*bm.getAddr32(i, j) == SK_ColorTRANSPARENT) {
70 ERRORF(r, "Erased color underneath!");
71 return;
72 }
73 }
74}
75
Leon Scroggins III4aafb3a2018-05-23 16:15:09 -040076static 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
101DEF_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];
Leon Scroggins III698219e2019-02-04 10:12:33 -0500141 bm.allocPixels(imageInfo);
Leon Scroggins III4aafb3a2018-05-23 16:15:09 -0400142 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;
Leon Scroggins III698219e2019-02-04 10:12:33 -0500161 test.allocPixels(imageInfo);
Leon Scroggins III4aafb3a2018-05-23 16:15:09 -0400162 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
Leon Scroggins III4c119452018-01-20 10:33:24 -0500172DEF_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;
Nigel Tao66bc5242018-08-22 10:56:03 +1000206 if (options.fPriorFrame == SkCodec::kNoFrame) {
Leon Scroggins III4c119452018-01-20 10:33:24 -0500207 bm.allocPixels(info);
208 bm.eraseColor(0);
209 } else {
210 const SkBitmap& priorFrame = frames[options.fPriorFrame];
Mike Kleinea3f0142019-03-20 11:12:10 -0500211 if (!ToolUtils::copy_to(&bm, priorFrame.colorType(), priorFrame)) {
Leon Scroggins III4c119452018-01-20 10:33:24 -0500212 ERRORF(r, "Failed to copy %s frame %i", file, options.fPriorFrame);
Nigel Tao66bc5242018-08-22 10:56:03 +1000213 options.fPriorFrame = SkCodec::kNoFrame;
Leon Scroggins III4c119452018-01-20 10:33:24 -0500214 }
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
Leon Scroggins IIIabe639c2018-01-26 11:06:12 -0500236 REPORTER_ASSERT(r, defaultRepetitionCount == animatedImage->getRepetitionCount());
237
Leon Scroggins III4c119452018-01-20 10:33:24 -0500238 auto testDraw = [r, &frames, &imageInfo, file](const sk_sp<SkAnimatedImage>& animatedImage,
239 int expectedFrame) {
240 SkBitmap test;
Leon Scroggins III698219e2019-02-04 10:12:33 -0500241 test.allocPixels(imageInfo);
Leon Scroggins III4c119452018-01-20 10:33:24 -0500242 test.eraseColor(0);
243 SkCanvas c(test);
244 animatedImage->draw(&c);
245
246 const SkBitmap& frame = frames[expectedFrame];
Leon Scroggins III4aafb3a2018-05-23 16:15:09 -0400247 return compare_bitmaps(r, file, expectedFrame, frame, test);
Leon Scroggins III4c119452018-01-20 10:33:24 -0500248 };
249
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500250 REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration);
251
Leon Scroggins III4c119452018-01-20 10:33:24 -0500252 if (!testDraw(animatedImage, 0)) {
253 ERRORF(r, "Did not start with frame 0");
254 continue;
255 }
256
Leon Scroggins III4c119452018-01-20 10:33:24 -0500257 // Start at an arbitrary time.
Leon Scroggins III4c119452018-01-20 10:33:24 -0500258 bool failed = false;
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500259 for (size_t i = 1; i < frameInfos.size(); ++i) {
260 const int frameTime = animatedImage->decodeNextFrame();
261 REPORTER_ASSERT(r, frameTime == animatedImage->currentFrameDuration());
262
Leon Scroggins III4c119452018-01-20 10:33:24 -0500263 if (i == frameInfos.size() - 1 && defaultRepetitionCount == 0) {
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500264 REPORTER_ASSERT(r, frameTime == SkAnimatedImage::kFinished);
Leon Scroggins III2cb6cb12018-01-21 15:50:12 -0500265 REPORTER_ASSERT(r, animatedImage->isFinished());
Leon Scroggins III4c119452018-01-20 10:33:24 -0500266 } else {
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500267 REPORTER_ASSERT(r, frameTime == frameInfos[i].fDuration);
Leon Scroggins III2cb6cb12018-01-21 15:50:12 -0500268 REPORTER_ASSERT(r, !animatedImage->isFinished());
Leon Scroggins III4c119452018-01-20 10:33:24 -0500269 }
270
271 if (!testDraw(animatedImage, i)) {
272 ERRORF(r, "Did not update to %i properly", i);
273 failed = true;
274 break;
275 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500276 }
277
278 if (failed) {
279 continue;
280 }
281
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500282 animatedImage->reset();
283 REPORTER_ASSERT(r, !animatedImage->isFinished());
284 if (!testDraw(animatedImage, 0)) {
285 ERRORF(r, "reset failed");
286 continue;
287 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500288
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500289 // 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) {
Leon Scroggins III4c119452018-01-20 10:33:24 -0500293 break;
294 }
295
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500296 // 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 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500315 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500316 }
317
318 if (failed) {
Leon Scroggins III4c119452018-01-20 10:33:24 -0500319 continue;
320 }
321
322 for (int loopCount : { 0, 1, 2, 5 }) {
323 animatedImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec(
324 SkCodec::MakeFromData(data)));
Leon Scroggins III4c119452018-01-20 10:33:24 -0500325 animatedImage->setRepetitionCount(loopCount);
Leon Scroggins IIIabe639c2018-01-26 11:06:12 -0500326 REPORTER_ASSERT(r, animatedImage->getRepetitionCount() == loopCount);
327
Leon Scroggins III4c119452018-01-20 10:33:24 -0500328 for (int loops = 0; loops <= loopCount; loops++) {
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500329 if (failed) {
330 break;
331 }
Leon Scroggins III2cb6cb12018-01-21 15:50:12 -0500332 REPORTER_ASSERT(r, !animatedImage->isFinished());
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500333 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: %i\tsize: %i",
343 file, i, frameInfos.size());
344 failed = true;
345 }
346 break;
Leon Scroggins III4c119452018-01-20 10:33:24 -0500347 }
348 }
349 }
Leon Scroggins III8524c302018-01-22 12:31:21 -0500350
Leon Scroggins III2cb6cb12018-01-21 15:50:12 -0500351 if (!animatedImage->isFinished()) {
352 ERRORF(r, "%s animation should have finished with specified loop count (%i)",
353 file, loopCount);
354 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500355 }
356 }
357}