Home | History | Annotate | Download | only in tests
      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