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 "CodecPriv.h"
      9 #include "Resources.h"
     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"
     18 #include "SkPicture.h"
     19 #include "SkRefCnt.h"
     20 #include "SkSize.h"
     21 #include "SkString.h"
     22 #include "SkTypes.h"
     23 #include "SkUnPreMultiply.h"
     24 #include "Test.h"
     25 #include "sk_tool_utils.h"
     26 
     27 #include <algorithm>
     28 #include <memory>
     29 #include <vector>
     30 
     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 
     75 static 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 
    100 DEF_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];
    140             bm.allocPixels(imageInfo);
    141             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;
    160             test.allocPixels(imageInfo);
    161             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 
    171 DEF_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;
    205             if (options.fPriorFrame == SkCodec::kNone) {
    206                 bm.allocPixels(info);
    207                 bm.eraseColor(0);
    208             } else {
    209                 const SkBitmap& priorFrame = frames[options.fPriorFrame];
    210                 if (!sk_tool_utils::copy_to(&bm, priorFrame.colorType(), priorFrame)) {
    211                     ERRORF(r, "Failed to copy %s frame %i", file, options.fPriorFrame);
    212                     options.fPriorFrame = SkCodec::kNone;
    213                 }
    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 
    235         REPORTER_ASSERT(r, defaultRepetitionCount == animatedImage->getRepetitionCount());
    236 
    237         auto testDraw = [r, &frames, &imageInfo, file](const sk_sp<SkAnimatedImage>& animatedImage,
    238                                                        int expectedFrame) {
    239             SkBitmap test;
    240             test.allocPixels(imageInfo);
    241             test.eraseColor(0);
    242             SkCanvas c(test);
    243             animatedImage->draw(&c);
    244 
    245             const SkBitmap& frame = frames[expectedFrame];
    246             return compare_bitmaps(r, file, expectedFrame, frame, test);
    247         };
    248 
    249         REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration);
    250 
    251         if (!testDraw(animatedImage, 0)) {
    252             ERRORF(r, "Did not start with frame 0");
    253             continue;
    254         }
    255 
    256         // Start at an arbitrary time.
    257         bool failed = false;
    258         for (size_t i = 1; i < frameInfos.size(); ++i) {
    259             const int frameTime = animatedImage->decodeNextFrame();
    260             REPORTER_ASSERT(r, frameTime == animatedImage->currentFrameDuration());
    261 
    262             if (i == frameInfos.size() - 1 && defaultRepetitionCount == 0) {
    263                 REPORTER_ASSERT(r, frameTime == SkAnimatedImage::kFinished);
    264                 REPORTER_ASSERT(r, animatedImage->isFinished());
    265             } else {
    266                 REPORTER_ASSERT(r, frameTime == frameInfos[i].fDuration);
    267                 REPORTER_ASSERT(r, !animatedImage->isFinished());
    268             }
    269 
    270             if (!testDraw(animatedImage, i)) {
    271                 ERRORF(r, "Did not update to %i properly", i);
    272                 failed = true;
    273                 break;
    274             }
    275         }
    276 
    277         if (failed) {
    278             continue;
    279         }
    280 
    281         animatedImage->reset();
    282         REPORTER_ASSERT(r, !animatedImage->isFinished());
    283         if (!testDraw(animatedImage, 0)) {
    284             ERRORF(r, "reset failed");
    285             continue;
    286         }
    287 
    288         // 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) {
    292                 break;
    293             }
    294 
    295             // 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                 }
    314             }
    315         }
    316 
    317         if (failed) {
    318             continue;
    319         }
    320 
    321         for (int loopCount : { 0, 1, 2, 5 }) {
    322             animatedImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec(
    323                         SkCodec::MakeFromData(data)));
    324             animatedImage->setRepetitionCount(loopCount);
    325             REPORTER_ASSERT(r, animatedImage->getRepetitionCount() == loopCount);
    326 
    327             for (int loops = 0; loops <= loopCount; loops++) {
    328                 if (failed) {
    329                     break;
    330                 }
    331                 REPORTER_ASSERT(r, !animatedImage->isFinished());
    332                 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;
    346                     }
    347                 }
    348             }
    349 
    350             if (!animatedImage->isFinished()) {
    351                 ERRORF(r, "%s animation should have finished with specified loop count (%i)",
    352                           file, loopCount);
    353             }
    354         }
    355     }
    356 }
    357