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 "SkAndroidCodec.h" 9 #include "SkAnimatedImage.h" 10 #include "SkCanvas.h" 11 #include "SkCodec.h" 12 #include "SkUnPreMultiply.h" 13 14 #include "CodecPriv.h" 15 #include "Resources.h" 16 #include "Test.h" 17 #include "sk_tool_utils.h" 18 19 #include <vector> 20 21 DEF_TEST(AnimatedImage, r) { 22 if (GetResourcePath().isEmpty()) { 23 return; 24 } 25 for (const char* file : { "images/alphabetAnim.gif", 26 "images/colorTables.gif", 27 "images/webp-animated.webp", 28 "images/required.webp", 29 }) { 30 auto data = GetResourceAsData(file); 31 if (!data) { 32 ERRORF(r, "Could not get %s", file); 33 continue; 34 } 35 36 auto codec = SkCodec::MakeFromData(data); 37 if (!codec) { 38 ERRORF(r, "Could not create codec for %s", file); 39 continue; 40 } 41 42 const int defaultRepetitionCount = codec->getRepetitionCount(); 43 std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo(); 44 std::vector<SkBitmap> frames(frameInfos.size()); 45 // Used down below for our test image. 46 const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType); 47 48 for (size_t i = 0; i < frameInfos.size(); ++i) { 49 auto info = codec->getInfo().makeAlphaType(frameInfos[i].fAlphaType); 50 auto& bm = frames[i]; 51 52 SkCodec::Options options; 53 options.fFrameIndex = (int) i; 54 options.fPriorFrame = frameInfos[i].fRequiredFrame; 55 if (options.fPriorFrame == SkCodec::kNone) { 56 bm.allocPixels(info); 57 bm.eraseColor(0); 58 } else { 59 const SkBitmap& priorFrame = frames[options.fPriorFrame]; 60 if (!sk_tool_utils::copy_to(&bm, priorFrame.colorType(), priorFrame)) { 61 ERRORF(r, "Failed to copy %s frame %i", file, options.fPriorFrame); 62 options.fPriorFrame = SkCodec::kNone; 63 } 64 REPORTER_ASSERT(r, bm.setAlphaType(frameInfos[i].fAlphaType)); 65 } 66 67 auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), &options); 68 if (result != SkCodec::kSuccess) { 69 ERRORF(r, "error in %s frame %zu: %s", file, i, SkCodec::ResultToString(result)); 70 } 71 } 72 73 auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec)); 74 if (!androidCodec) { 75 ERRORF(r, "Could not create androidCodec for %s", file); 76 continue; 77 } 78 79 auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec)); 80 if (!animatedImage) { 81 ERRORF(r, "Could not create animated image for %s", file); 82 continue; 83 } 84 85 REPORTER_ASSERT(r, defaultRepetitionCount == animatedImage->getRepetitionCount()); 86 87 auto testDraw = [r, &frames, &imageInfo, file](const sk_sp<SkAnimatedImage>& animatedImage, 88 int expectedFrame) { 89 SkBitmap test; 90 test.allocPixels(imageInfo); 91 test.eraseColor(0); 92 SkCanvas c(test); 93 animatedImage->draw(&c); 94 95 const SkBitmap& frame = frames[expectedFrame]; 96 REPORTER_ASSERT(r, frame.colorType() == test.colorType()); 97 REPORTER_ASSERT(r, frame.dimensions() == test.dimensions()); 98 for (int i = 0; i < test.width(); ++i) 99 for (int j = 0; j < test.height(); ++j) { 100 SkColor expected = SkUnPreMultiply::PMColorToColor(*frame.getAddr32(i, j)); 101 SkColor actual = SkUnPreMultiply::PMColorToColor(*test .getAddr32(i, j)); 102 if (expected != actual) { 103 ERRORF(r, "frame %i of %s does not match at pixel %i, %i!" 104 " expected %x\tactual: %x", 105 expectedFrame, file, i, j, expected, actual); 106 SkString expected_name = SkStringPrintf("expected_%c", '0' + expectedFrame); 107 SkString actual_name = SkStringPrintf("actual_%c", '0' + expectedFrame); 108 write_bm(expected_name.c_str(), frame); 109 write_bm(actual_name.c_str(), test); 110 return false; 111 } 112 } 113 return true; 114 }; 115 116 REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration); 117 118 if (!testDraw(animatedImage, 0)) { 119 ERRORF(r, "Did not start with frame 0"); 120 continue; 121 } 122 123 // Start at an arbitrary time. 124 bool failed = false; 125 for (size_t i = 1; i < frameInfos.size(); ++i) { 126 const int frameTime = animatedImage->decodeNextFrame(); 127 REPORTER_ASSERT(r, frameTime == animatedImage->currentFrameDuration()); 128 129 if (i == frameInfos.size() - 1 && defaultRepetitionCount == 0) { 130 REPORTER_ASSERT(r, frameTime == SkAnimatedImage::kFinished); 131 REPORTER_ASSERT(r, animatedImage->isFinished()); 132 } else { 133 REPORTER_ASSERT(r, frameTime == frameInfos[i].fDuration); 134 REPORTER_ASSERT(r, !animatedImage->isFinished()); 135 } 136 137 if (!testDraw(animatedImage, i)) { 138 ERRORF(r, "Did not update to %i properly", i); 139 failed = true; 140 break; 141 } 142 } 143 144 if (failed) { 145 continue; 146 } 147 148 animatedImage->reset(); 149 REPORTER_ASSERT(r, !animatedImage->isFinished()); 150 if (!testDraw(animatedImage, 0)) { 151 ERRORF(r, "reset failed"); 152 continue; 153 } 154 155 // Test reset from all the frames. 156 // j is the frame to call reset on. 157 for (int j = 0; j < (int) frameInfos.size(); ++j) { 158 if (failed) { 159 break; 160 } 161 162 // i is the frame to decode. 163 for (int i = 0; i <= j; ++i) { 164 if (i == j) { 165 animatedImage->reset(); 166 if (!testDraw(animatedImage, 0)) { 167 ERRORF(r, "reset failed for image %s from frame %i", 168 file, i); 169 failed = true; 170 break; 171 } 172 } else if (i != 0) { 173 animatedImage->decodeNextFrame(); 174 if (!testDraw(animatedImage, i)) { 175 ERRORF(r, "failed to match frame %i in %s on iteration %i", 176 i, file, j); 177 failed = true; 178 break; 179 } 180 } 181 } 182 } 183 184 if (failed) { 185 continue; 186 } 187 188 for (int loopCount : { 0, 1, 2, 5 }) { 189 animatedImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec( 190 SkCodec::MakeFromData(data))); 191 animatedImage->setRepetitionCount(loopCount); 192 REPORTER_ASSERT(r, animatedImage->getRepetitionCount() == loopCount); 193 194 for (int loops = 0; loops <= loopCount; loops++) { 195 if (failed) { 196 break; 197 } 198 REPORTER_ASSERT(r, !animatedImage->isFinished()); 199 for (size_t i = 1; i <= frameInfos.size(); ++i) { 200 const int frameTime = animatedImage->decodeNextFrame(); 201 if (frameTime == SkAnimatedImage::kFinished) { 202 if (loops != loopCount) { 203 ERRORF(r, "%s animation stopped early: loops: %i\tloopCount: %i", 204 file, loops, loopCount); 205 failed = true; 206 } 207 if (i != frameInfos.size() - 1) { 208 ERRORF(r, "%s animation stopped early: i: %i\tsize: %i", 209 file, i, frameInfos.size()); 210 failed = true; 211 } 212 break; 213 } 214 } 215 } 216 217 if (!animatedImage->isFinished()) { 218 ERRORF(r, "%s animation should have finished with specified loop count (%i)", 219 file, loopCount); 220 } 221 } 222 } 223 } 224