blob: 3382495c8b154fa6bb7f41e60ffff0e5f5873d1e [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 Wagner1ebeefe2018-03-02 16:59:53 -050027#include <algorithm>
28#include <memory>
Leon Scroggins III4c119452018-01-20 10:33:24 -050029#include <vector>
30
Leon Scroggins III2f862202018-03-05 14:19:40 -050031DEF_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.
Leon Scroggins III85463352019-02-04 15:22:10 -050050 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);
Leon Scroggins III2f862202018-03-05 14:19:40 -050054 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;
Leon Scroggins III85463352019-02-04 15:22:10 -050062 bm.allocPixels(SkImageInfo::MakeN32Premul(info.width(), info.height()));
Leon Scroggins III2f862202018-03-05 14:19:40 -050063 bm.eraseColor(SK_ColorBLUE);
64 SkCanvas canvas(bm);
65 image->draw(&canvas);
Leon Scroggins III85463352019-02-04 15:22:10 -050066 for (int i = 0; i < info.width(); ++i)
67 for (int j = 0; j < info.height(); ++j) {
Leon Scroggins III2f862202018-03-05 14:19:40 -050068 if (*bm.getAddr32(i, j) == SK_ColorTRANSPARENT) {
69 ERRORF(r, "Erased color underneath!");
70 return;
71 }
72 }
73}
74
Leon Scroggins III4aafb3a2018-05-23 16:15:09 -040075static 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
100DEF_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];
Leon Scroggins III698219e2019-02-04 10:12:33 -0500140 bm.allocPixels(imageInfo);
Leon Scroggins III4aafb3a2018-05-23 16:15:09 -0400141 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;
Leon Scroggins III698219e2019-02-04 10:12:33 -0500160 test.allocPixels(imageInfo);
Leon Scroggins III4aafb3a2018-05-23 16:15:09 -0400161 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
Leon Scroggins III4c119452018-01-20 10:33:24 -0500171DEF_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;
Nigel Tao66bc5242018-08-22 10:56:03 +1000205 if (options.fPriorFrame == SkCodec::kNoFrame) {
Leon Scroggins III4c119452018-01-20 10:33:24 -0500206 bm.allocPixels(info);
207 bm.eraseColor(0);
208 } else {
209 const SkBitmap& priorFrame = frames[options.fPriorFrame];
Mike Kleinea3f0142019-03-20 11:12:10 -0500210 if (!ToolUtils::copy_to(&bm, priorFrame.colorType(), priorFrame)) {
Leon Scroggins III4c119452018-01-20 10:33:24 -0500211 ERRORF(r, "Failed to copy %s frame %i", file, options.fPriorFrame);
Nigel Tao66bc5242018-08-22 10:56:03 +1000212 options.fPriorFrame = SkCodec::kNoFrame;
Leon Scroggins III4c119452018-01-20 10:33:24 -0500213 }
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
Leon Scroggins IIIabe639c2018-01-26 11:06:12 -0500235 REPORTER_ASSERT(r, defaultRepetitionCount == animatedImage->getRepetitionCount());
236
Leon Scroggins III4c119452018-01-20 10:33:24 -0500237 auto testDraw = [r, &frames, &imageInfo, file](const sk_sp<SkAnimatedImage>& animatedImage,
238 int expectedFrame) {
239 SkBitmap test;
Leon Scroggins III698219e2019-02-04 10:12:33 -0500240 test.allocPixels(imageInfo);
Leon Scroggins III4c119452018-01-20 10:33:24 -0500241 test.eraseColor(0);
242 SkCanvas c(test);
243 animatedImage->draw(&c);
244
245 const SkBitmap& frame = frames[expectedFrame];
Leon Scroggins III4aafb3a2018-05-23 16:15:09 -0400246 return compare_bitmaps(r, file, expectedFrame, frame, test);
Leon Scroggins III4c119452018-01-20 10:33:24 -0500247 };
248
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500249 REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration);
250
Leon Scroggins III4c119452018-01-20 10:33:24 -0500251 if (!testDraw(animatedImage, 0)) {
252 ERRORF(r, "Did not start with frame 0");
253 continue;
254 }
255
Leon Scroggins III4c119452018-01-20 10:33:24 -0500256 // Start at an arbitrary time.
Leon Scroggins III4c119452018-01-20 10:33:24 -0500257 bool failed = false;
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500258 for (size_t i = 1; i < frameInfos.size(); ++i) {
259 const int frameTime = animatedImage->decodeNextFrame();
260 REPORTER_ASSERT(r, frameTime == animatedImage->currentFrameDuration());
261
Leon Scroggins III4c119452018-01-20 10:33:24 -0500262 if (i == frameInfos.size() - 1 && defaultRepetitionCount == 0) {
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500263 REPORTER_ASSERT(r, frameTime == SkAnimatedImage::kFinished);
Leon Scroggins III2cb6cb12018-01-21 15:50:12 -0500264 REPORTER_ASSERT(r, animatedImage->isFinished());
Leon Scroggins III4c119452018-01-20 10:33:24 -0500265 } else {
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500266 REPORTER_ASSERT(r, frameTime == frameInfos[i].fDuration);
Leon Scroggins III2cb6cb12018-01-21 15:50:12 -0500267 REPORTER_ASSERT(r, !animatedImage->isFinished());
Leon Scroggins III4c119452018-01-20 10:33:24 -0500268 }
269
270 if (!testDraw(animatedImage, i)) {
271 ERRORF(r, "Did not update to %i properly", i);
272 failed = true;
273 break;
274 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500275 }
276
277 if (failed) {
278 continue;
279 }
280
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500281 animatedImage->reset();
282 REPORTER_ASSERT(r, !animatedImage->isFinished());
283 if (!testDraw(animatedImage, 0)) {
284 ERRORF(r, "reset failed");
285 continue;
286 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500287
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500288 // 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) {
Leon Scroggins III4c119452018-01-20 10:33:24 -0500292 break;
293 }
294
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500295 // 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 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500314 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500315 }
316
317 if (failed) {
Leon Scroggins III4c119452018-01-20 10:33:24 -0500318 continue;
319 }
320
321 for (int loopCount : { 0, 1, 2, 5 }) {
322 animatedImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec(
323 SkCodec::MakeFromData(data)));
Leon Scroggins III4c119452018-01-20 10:33:24 -0500324 animatedImage->setRepetitionCount(loopCount);
Leon Scroggins IIIabe639c2018-01-26 11:06:12 -0500325 REPORTER_ASSERT(r, animatedImage->getRepetitionCount() == loopCount);
326
Leon Scroggins III4c119452018-01-20 10:33:24 -0500327 for (int loops = 0; loops <= loopCount; loops++) {
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500328 if (failed) {
329 break;
330 }
Leon Scroggins III2cb6cb12018-01-21 15:50:12 -0500331 REPORTER_ASSERT(r, !animatedImage->isFinished());
Leon Scroggins III495e0f02018-01-29 19:35:55 -0500332 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;
Leon Scroggins III4c119452018-01-20 10:33:24 -0500346 }
347 }
348 }
Leon Scroggins III8524c302018-01-22 12:31:21 -0500349
Leon Scroggins III2cb6cb12018-01-21 15:50:12 -0500350 if (!animatedImage->isFinished()) {
351 ERRORF(r, "%s animation should have finished with specified loop count (%i)",
352 file, loopCount);
353 }
Leon Scroggins III4c119452018-01-20 10:33:24 -0500354 }
355 }
356}