| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 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 |  | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 8 | #include "CodecPriv.h" | 
 | 9 | #include "Resources.h" | 
| Ben Wagner | 1ebeefe | 2018-03-02 16:59:53 -0500 | [diff] [blame] | 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" | 
| Leon Scroggins III | 2f86220 | 2018-03-05 14:19:40 -0500 | [diff] [blame] | 18 | #include "SkPicture.h" | 
| Ben Wagner | 1ebeefe | 2018-03-02 16:59:53 -0500 | [diff] [blame] | 19 | #include "SkRefCnt.h" | 
 | 20 | #include "SkSize.h" | 
 | 21 | #include "SkString.h" | 
 | 22 | #include "SkTypes.h" | 
 | 23 | #include "SkUnPreMultiply.h" | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 24 | #include "Test.h" | 
 | 25 | #include "sk_tool_utils.h" | 
 | 26 |  | 
| Ben Wagner | 1ebeefe | 2018-03-02 16:59:53 -0500 | [diff] [blame] | 27 | #include <algorithm> | 
 | 28 | #include <memory> | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 29 | #include <vector> | 
 | 30 |  | 
| Leon Scroggins III | 2f86220 | 2018-03-05 14:19:40 -0500 | [diff] [blame] | 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 |  | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 75 | DEF_TEST(AnimatedImage, r) { | 
 | 76 |     if (GetResourcePath().isEmpty()) { | 
 | 77 |         return; | 
 | 78 |     } | 
 | 79 |     for (const char* file : { "images/alphabetAnim.gif", | 
 | 80 |                               "images/colorTables.gif", | 
 | 81 |                               "images/webp-animated.webp", | 
 | 82 |                               "images/required.webp", | 
 | 83 |                               }) { | 
 | 84 |         auto data = GetResourceAsData(file); | 
 | 85 |         if (!data) { | 
 | 86 |             ERRORF(r, "Could not get %s", file); | 
 | 87 |             continue; | 
 | 88 |         } | 
 | 89 |  | 
 | 90 |         auto codec = SkCodec::MakeFromData(data); | 
 | 91 |         if (!codec) { | 
 | 92 |             ERRORF(r, "Could not create codec for %s", file); | 
 | 93 |             continue; | 
 | 94 |         } | 
 | 95 |  | 
 | 96 |         const int defaultRepetitionCount = codec->getRepetitionCount(); | 
 | 97 |         std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo(); | 
 | 98 |         std::vector<SkBitmap> frames(frameInfos.size()); | 
 | 99 |         // Used down below for our test image. | 
 | 100 |         const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType); | 
 | 101 |  | 
 | 102 |         for (size_t i = 0; i < frameInfos.size(); ++i) { | 
 | 103 |             auto info = codec->getInfo().makeAlphaType(frameInfos[i].fAlphaType); | 
 | 104 |             auto& bm = frames[i]; | 
 | 105 |  | 
 | 106 |             SkCodec::Options options; | 
 | 107 |             options.fFrameIndex = (int) i; | 
 | 108 |             options.fPriorFrame = frameInfos[i].fRequiredFrame; | 
 | 109 |             if (options.fPriorFrame == SkCodec::kNone) { | 
 | 110 |                 bm.allocPixels(info); | 
 | 111 |                 bm.eraseColor(0); | 
 | 112 |             } else { | 
 | 113 |                 const SkBitmap& priorFrame = frames[options.fPriorFrame]; | 
 | 114 |                 if (!sk_tool_utils::copy_to(&bm, priorFrame.colorType(), priorFrame)) { | 
 | 115 |                     ERRORF(r, "Failed to copy %s frame %i", file, options.fPriorFrame); | 
 | 116 |                     options.fPriorFrame = SkCodec::kNone; | 
 | 117 |                 } | 
 | 118 |                 REPORTER_ASSERT(r, bm.setAlphaType(frameInfos[i].fAlphaType)); | 
 | 119 |             } | 
 | 120 |  | 
 | 121 |             auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), &options); | 
 | 122 |             if (result != SkCodec::kSuccess) { | 
 | 123 |                 ERRORF(r, "error in %s frame %zu: %s", file, i, SkCodec::ResultToString(result)); | 
 | 124 |             } | 
 | 125 |         } | 
 | 126 |  | 
 | 127 |         auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec)); | 
 | 128 |         if (!androidCodec) { | 
 | 129 |             ERRORF(r, "Could not create androidCodec for %s", file); | 
 | 130 |             continue; | 
 | 131 |         } | 
 | 132 |  | 
 | 133 |         auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec)); | 
 | 134 |         if (!animatedImage) { | 
 | 135 |             ERRORF(r, "Could not create animated image for %s", file); | 
 | 136 |             continue; | 
 | 137 |         } | 
 | 138 |  | 
| Leon Scroggins III | abe639c | 2018-01-26 11:06:12 -0500 | [diff] [blame] | 139 |         REPORTER_ASSERT(r, defaultRepetitionCount == animatedImage->getRepetitionCount()); | 
 | 140 |  | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 141 |         auto testDraw = [r, &frames, &imageInfo, file](const sk_sp<SkAnimatedImage>& animatedImage, | 
 | 142 |                                                        int expectedFrame) { | 
 | 143 |             SkBitmap test; | 
 | 144 |             test.allocPixels(imageInfo); | 
 | 145 |             test.eraseColor(0); | 
 | 146 |             SkCanvas c(test); | 
 | 147 |             animatedImage->draw(&c); | 
 | 148 |  | 
 | 149 |             const SkBitmap& frame = frames[expectedFrame]; | 
 | 150 |             REPORTER_ASSERT(r, frame.colorType() == test.colorType()); | 
 | 151 |             REPORTER_ASSERT(r, frame.dimensions() == test.dimensions()); | 
 | 152 |             for (int i = 0; i < test.width();  ++i) | 
 | 153 |             for (int j = 0; j < test.height(); ++j) { | 
 | 154 |                 SkColor expected = SkUnPreMultiply::PMColorToColor(*frame.getAddr32(i, j)); | 
 | 155 |                 SkColor actual   = SkUnPreMultiply::PMColorToColor(*test .getAddr32(i, j)); | 
 | 156 |                 if (expected != actual) { | 
 | 157 |                     ERRORF(r, "frame %i of %s does not match at pixel %i, %i!" | 
 | 158 |                             " expected %x\tactual: %x", | 
 | 159 |                             expectedFrame, file, i, j, expected, actual); | 
 | 160 |                     SkString expected_name = SkStringPrintf("expected_%c", '0' + expectedFrame); | 
 | 161 |                     SkString actual_name   = SkStringPrintf("actual_%c",   '0' + expectedFrame); | 
 | 162 |                     write_bm(expected_name.c_str(), frame); | 
 | 163 |                     write_bm(actual_name.c_str(),   test); | 
 | 164 |                     return false; | 
 | 165 |                 } | 
 | 166 |             } | 
 | 167 |             return true; | 
 | 168 |         }; | 
 | 169 |  | 
| Leon Scroggins III | 495e0f0 | 2018-01-29 19:35:55 -0500 | [diff] [blame] | 170 |         REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration); | 
 | 171 |  | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 172 |         if (!testDraw(animatedImage, 0)) { | 
 | 173 |             ERRORF(r, "Did not start with frame 0"); | 
 | 174 |             continue; | 
 | 175 |         } | 
 | 176 |  | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 177 |         // Start at an arbitrary time. | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 178 |         bool failed = false; | 
| Leon Scroggins III | 495e0f0 | 2018-01-29 19:35:55 -0500 | [diff] [blame] | 179 |         for (size_t i = 1; i < frameInfos.size(); ++i) { | 
 | 180 |             const int frameTime = animatedImage->decodeNextFrame(); | 
 | 181 |             REPORTER_ASSERT(r, frameTime == animatedImage->currentFrameDuration()); | 
 | 182 |  | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 183 |             if (i == frameInfos.size() - 1 && defaultRepetitionCount == 0) { | 
| Leon Scroggins III | 495e0f0 | 2018-01-29 19:35:55 -0500 | [diff] [blame] | 184 |                 REPORTER_ASSERT(r, frameTime == SkAnimatedImage::kFinished); | 
| Leon Scroggins III | 2cb6cb1 | 2018-01-21 15:50:12 -0500 | [diff] [blame] | 185 |                 REPORTER_ASSERT(r, animatedImage->isFinished()); | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 186 |             } else { | 
| Leon Scroggins III | 495e0f0 | 2018-01-29 19:35:55 -0500 | [diff] [blame] | 187 |                 REPORTER_ASSERT(r, frameTime == frameInfos[i].fDuration); | 
| Leon Scroggins III | 2cb6cb1 | 2018-01-21 15:50:12 -0500 | [diff] [blame] | 188 |                 REPORTER_ASSERT(r, !animatedImage->isFinished()); | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 189 |             } | 
 | 190 |  | 
 | 191 |             if (!testDraw(animatedImage, i)) { | 
 | 192 |                 ERRORF(r, "Did not update to %i properly", i); | 
 | 193 |                 failed = true; | 
 | 194 |                 break; | 
 | 195 |             } | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 196 |         } | 
 | 197 |  | 
 | 198 |         if (failed) { | 
 | 199 |             continue; | 
 | 200 |         } | 
 | 201 |  | 
| Leon Scroggins III | 495e0f0 | 2018-01-29 19:35:55 -0500 | [diff] [blame] | 202 |         animatedImage->reset(); | 
 | 203 |         REPORTER_ASSERT(r, !animatedImage->isFinished()); | 
 | 204 |         if (!testDraw(animatedImage, 0)) { | 
 | 205 |             ERRORF(r, "reset failed"); | 
 | 206 |             continue; | 
 | 207 |         } | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 208 |  | 
| Leon Scroggins III | 495e0f0 | 2018-01-29 19:35:55 -0500 | [diff] [blame] | 209 |         // Test reset from all the frames. | 
 | 210 |         // j is the frame to call reset on. | 
 | 211 |         for (int j = 0; j < (int) frameInfos.size(); ++j) { | 
 | 212 |             if (failed) { | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 213 |                 break; | 
 | 214 |             } | 
 | 215 |  | 
| Leon Scroggins III | 495e0f0 | 2018-01-29 19:35:55 -0500 | [diff] [blame] | 216 |             // i is the frame to decode. | 
 | 217 |             for (int i = 0; i <= j; ++i) { | 
 | 218 |                 if (i == j) { | 
 | 219 |                     animatedImage->reset(); | 
 | 220 |                     if (!testDraw(animatedImage, 0)) { | 
 | 221 |                         ERRORF(r, "reset failed for image %s from frame %i", | 
 | 222 |                                 file, i); | 
 | 223 |                         failed = true; | 
 | 224 |                         break; | 
 | 225 |                     } | 
 | 226 |                 } else if (i != 0) { | 
 | 227 |                     animatedImage->decodeNextFrame(); | 
 | 228 |                     if (!testDraw(animatedImage, i)) { | 
 | 229 |                         ERRORF(r, "failed to match frame %i in %s on iteration %i", | 
 | 230 |                                 i, file, j); | 
 | 231 |                         failed = true; | 
 | 232 |                         break; | 
 | 233 |                     } | 
 | 234 |                 } | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 235 |             } | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 236 |         } | 
 | 237 |  | 
 | 238 |         if (failed) { | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 239 |             continue; | 
 | 240 |         } | 
 | 241 |  | 
 | 242 |         for (int loopCount : { 0, 1, 2, 5 }) { | 
 | 243 |             animatedImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec( | 
 | 244 |                         SkCodec::MakeFromData(data))); | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 245 |             animatedImage->setRepetitionCount(loopCount); | 
| Leon Scroggins III | abe639c | 2018-01-26 11:06:12 -0500 | [diff] [blame] | 246 |             REPORTER_ASSERT(r, animatedImage->getRepetitionCount() == loopCount); | 
 | 247 |  | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 248 |             for (int loops = 0; loops <= loopCount; loops++) { | 
| Leon Scroggins III | 495e0f0 | 2018-01-29 19:35:55 -0500 | [diff] [blame] | 249 |                 if (failed) { | 
 | 250 |                     break; | 
 | 251 |                 } | 
| Leon Scroggins III | 2cb6cb1 | 2018-01-21 15:50:12 -0500 | [diff] [blame] | 252 |                 REPORTER_ASSERT(r, !animatedImage->isFinished()); | 
| Leon Scroggins III | 495e0f0 | 2018-01-29 19:35:55 -0500 | [diff] [blame] | 253 |                 for (size_t i = 1; i <= frameInfos.size(); ++i) { | 
 | 254 |                     const int frameTime = animatedImage->decodeNextFrame(); | 
 | 255 |                     if (frameTime == SkAnimatedImage::kFinished) { | 
 | 256 |                         if (loops != loopCount) { | 
 | 257 |                             ERRORF(r, "%s animation stopped early: loops: %i\tloopCount: %i", | 
 | 258 |                                     file, loops, loopCount); | 
 | 259 |                             failed = true; | 
 | 260 |                         } | 
 | 261 |                         if (i != frameInfos.size() - 1) { | 
 | 262 |                             ERRORF(r, "%s animation stopped early: i: %i\tsize: %i", | 
 | 263 |                                     file, i, frameInfos.size()); | 
 | 264 |                             failed = true; | 
 | 265 |                         } | 
 | 266 |                         break; | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 267 |                     } | 
 | 268 |                 } | 
 | 269 |             } | 
| Leon Scroggins III | 8524c30 | 2018-01-22 12:31:21 -0500 | [diff] [blame] | 270 |  | 
| Leon Scroggins III | 2cb6cb1 | 2018-01-21 15:50:12 -0500 | [diff] [blame] | 271 |             if (!animatedImage->isFinished()) { | 
 | 272 |                 ERRORF(r, "%s animation should have finished with specified loop count (%i)", | 
 | 273 |                           file, loopCount); | 
 | 274 |             } | 
| Leon Scroggins III | 4c11945 | 2018-01-20 10:33:24 -0500 | [diff] [blame] | 275 |         } | 
 | 276 |     } | 
 | 277 | } |