1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/android/SkAnimatedImage.h" 9cb93a386Sopenharmony_ci#include "include/codec/SkAndroidCodec.h" 10cb93a386Sopenharmony_ci#include "include/codec/SkCodec.h" 11cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h" 12cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h" 13cb93a386Sopenharmony_ci#include "include/core/SkColor.h" 14cb93a386Sopenharmony_ci#include "include/core/SkData.h" 15cb93a386Sopenharmony_ci#include "include/core/SkImageInfo.h" 16cb93a386Sopenharmony_ci#include "include/core/SkPicture.h" 17cb93a386Sopenharmony_ci#include "include/core/SkRefCnt.h" 18cb93a386Sopenharmony_ci#include "include/core/SkSize.h" 19cb93a386Sopenharmony_ci#include "include/core/SkString.h" 20cb93a386Sopenharmony_ci#include "include/core/SkTypes.h" 21cb93a386Sopenharmony_ci#include "include/core/SkUnPreMultiply.h" 22cb93a386Sopenharmony_ci#include "tests/CodecPriv.h" 23cb93a386Sopenharmony_ci#include "tests/Test.h" 24cb93a386Sopenharmony_ci#include "tools/Resources.h" 25cb93a386Sopenharmony_ci#include "tools/ToolUtils.h" 26cb93a386Sopenharmony_ci 27cb93a386Sopenharmony_ci#include <initializer_list> 28cb93a386Sopenharmony_ci#include <memory> 29cb93a386Sopenharmony_ci#include <utility> 30cb93a386Sopenharmony_ci#include <vector> 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ciDEF_TEST(AnimatedImage_simple, r) { 33cb93a386Sopenharmony_ci if (GetResourcePath().isEmpty()) { 34cb93a386Sopenharmony_ci return; 35cb93a386Sopenharmony_ci } 36cb93a386Sopenharmony_ci 37cb93a386Sopenharmony_ci const char* file = "images/stoplight_h.webp"; 38cb93a386Sopenharmony_ci auto data = GetResourceAsData(file); 39cb93a386Sopenharmony_ci if (!data) { 40cb93a386Sopenharmony_ci ERRORF(r, "Could not get %s", file); 41cb93a386Sopenharmony_ci return; 42cb93a386Sopenharmony_ci } 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_ci // An animated image with a non-default exif orientation is no longer 45cb93a386Sopenharmony_ci // "simple"; verify that the assert has been removed. 46cb93a386Sopenharmony_ci auto androidCodec = SkAndroidCodec::MakeFromData(std::move(data)); 47cb93a386Sopenharmony_ci auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec)); 48cb93a386Sopenharmony_ci REPORTER_ASSERT(r, animatedImage); 49cb93a386Sopenharmony_ci} 50cb93a386Sopenharmony_ci 51cb93a386Sopenharmony_ciDEF_TEST(AnimatedImage_rotation, r) { 52cb93a386Sopenharmony_ci if (GetResourcePath().isEmpty()) { 53cb93a386Sopenharmony_ci return; 54cb93a386Sopenharmony_ci } 55cb93a386Sopenharmony_ci 56cb93a386Sopenharmony_ci // These images use different exif orientations to achieve the same final 57cb93a386Sopenharmony_ci // dimensions 58cb93a386Sopenharmony_ci const auto expectedBounds = SkRect::MakeIWH(100, 80); 59cb93a386Sopenharmony_ci for (int i = 1; i <=8; i++) { 60cb93a386Sopenharmony_ci for (const SkString& name : { SkStringPrintf("images/orientation/%d.webp", i), 61cb93a386Sopenharmony_ci SkStringPrintf("images/orientation/%d_444.jpg", i) }) { 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_ci const char* file = name.c_str(); 64cb93a386Sopenharmony_ci auto data = GetResourceAsData(file); 65cb93a386Sopenharmony_ci if (!data) { 66cb93a386Sopenharmony_ci ERRORF(r, "Could not get %s", file); 67cb93a386Sopenharmony_ci return; 68cb93a386Sopenharmony_ci } 69cb93a386Sopenharmony_ci 70cb93a386Sopenharmony_ci auto androidCodec = SkAndroidCodec::MakeFromData(std::move(data)); 71cb93a386Sopenharmony_ci auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec)); 72cb93a386Sopenharmony_ci if (!animatedImage) { 73cb93a386Sopenharmony_ci ERRORF(r, "Failed to create animated image from %s", file); 74cb93a386Sopenharmony_ci return; 75cb93a386Sopenharmony_ci } 76cb93a386Sopenharmony_ci 77cb93a386Sopenharmony_ci auto bounds = animatedImage->getBounds(); 78cb93a386Sopenharmony_ci if (bounds != expectedBounds) { 79cb93a386Sopenharmony_ci ERRORF(r, "Mismatched bounds for %", file); 80cb93a386Sopenharmony_ci bounds.dump(); 81cb93a386Sopenharmony_ci } 82cb93a386Sopenharmony_ci } 83cb93a386Sopenharmony_ci } 84cb93a386Sopenharmony_ci} 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_ciDEF_TEST(AnimatedImage_invalidCrop, r) { 87cb93a386Sopenharmony_ci if (GetResourcePath().isEmpty()) { 88cb93a386Sopenharmony_ci return; 89cb93a386Sopenharmony_ci } 90cb93a386Sopenharmony_ci 91cb93a386Sopenharmony_ci const char* file = "images/alphabetAnim.gif"; 92cb93a386Sopenharmony_ci auto data = GetResourceAsData(file); 93cb93a386Sopenharmony_ci if (!data) { 94cb93a386Sopenharmony_ci ERRORF(r, "Could not get %s", file); 95cb93a386Sopenharmony_ci return; 96cb93a386Sopenharmony_ci } 97cb93a386Sopenharmony_ci 98cb93a386Sopenharmony_ci const struct Rec { 99cb93a386Sopenharmony_ci bool valid; 100cb93a386Sopenharmony_ci SkISize scaledSize; 101cb93a386Sopenharmony_ci SkIRect cropRect; 102cb93a386Sopenharmony_ci } gRecs[] = { 103cb93a386Sopenharmony_ci // cropRect contained by original dimensions 104cb93a386Sopenharmony_ci { true, {100, 100}, { 0, 0, 100, 100} }, 105cb93a386Sopenharmony_ci { true, {100, 100}, { 0, 0, 50, 50} }, 106cb93a386Sopenharmony_ci { true, {100, 100}, { 10, 10, 100, 100} }, 107cb93a386Sopenharmony_ci { true, {100, 100}, { 0, 0, 100, 100} }, 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_ci // unsorted cropRect 110cb93a386Sopenharmony_ci { false, {100, 100}, { 0, 100, 100, 0} }, 111cb93a386Sopenharmony_ci { false, {100, 100}, { 100, 0, 0, 100} }, 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_ci // cropRect not contained by original dimensions 114cb93a386Sopenharmony_ci { false, {100, 100}, { 0, 1, 100, 101} }, 115cb93a386Sopenharmony_ci { false, {100, 100}, { 0, -1, 100, 99} }, 116cb93a386Sopenharmony_ci { false, {100, 100}, { -1, 0, 99, 100} }, 117cb93a386Sopenharmony_ci { false, {100, 100}, { 100, 100, 200, 200} }, 118cb93a386Sopenharmony_ci 119cb93a386Sopenharmony_ci // cropRect contained by scaled dimensions 120cb93a386Sopenharmony_ci { true, { 50, 50}, { 0, 0, 50, 50} }, 121cb93a386Sopenharmony_ci { true, { 50, 50}, { 0, 0, 25, 25} }, 122cb93a386Sopenharmony_ci { true, {200, 200}, { 0, 1, 100, 101} }, 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci // cropRect not contained by scaled dimensions 125cb93a386Sopenharmony_ci { false, { 50, 50}, { 0, 0, 75, 25} }, 126cb93a386Sopenharmony_ci { false, { 50, 50}, { 0, 0, 25, 75} }, 127cb93a386Sopenharmony_ci 128cb93a386Sopenharmony_ci }; 129cb93a386Sopenharmony_ci for (const auto& rec : gRecs) { 130cb93a386Sopenharmony_ci auto codec = SkAndroidCodec::MakeFromData(data); 131cb93a386Sopenharmony_ci if (!codec) { 132cb93a386Sopenharmony_ci ERRORF(r, "Could not create codec for %s", file); 133cb93a386Sopenharmony_ci return; 134cb93a386Sopenharmony_ci } 135cb93a386Sopenharmony_ci 136cb93a386Sopenharmony_ci auto info = codec->getInfo(); 137cb93a386Sopenharmony_ci REPORTER_ASSERT(r, info.dimensions() == SkISize::Make(100, 100)); 138cb93a386Sopenharmony_ci 139cb93a386Sopenharmony_ci auto image = SkAnimatedImage::Make(std::move(codec), info.makeDimensions(rec.scaledSize), 140cb93a386Sopenharmony_ci rec.cropRect, nullptr); 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_ci REPORTER_ASSERT(r, rec.valid == !!image.get()); 143cb93a386Sopenharmony_ci } 144cb93a386Sopenharmony_ci} 145cb93a386Sopenharmony_ci 146cb93a386Sopenharmony_ciDEF_TEST(AnimatedImage_scaled, r) { 147cb93a386Sopenharmony_ci if (GetResourcePath().isEmpty()) { 148cb93a386Sopenharmony_ci return; 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci const char* file = "images/alphabetAnim.gif"; 152cb93a386Sopenharmony_ci auto data = GetResourceAsData(file); 153cb93a386Sopenharmony_ci if (!data) { 154cb93a386Sopenharmony_ci ERRORF(r, "Could not get %s", file); 155cb93a386Sopenharmony_ci return; 156cb93a386Sopenharmony_ci } 157cb93a386Sopenharmony_ci 158cb93a386Sopenharmony_ci auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(data)); 159cb93a386Sopenharmony_ci if (!codec) { 160cb93a386Sopenharmony_ci ERRORF(r, "Could not create codec for %s", file); 161cb93a386Sopenharmony_ci return; 162cb93a386Sopenharmony_ci } 163cb93a386Sopenharmony_ci 164cb93a386Sopenharmony_ci // Force the drawable follow its special case that requires scaling. 165cb93a386Sopenharmony_ci auto info = codec->getInfo(); 166cb93a386Sopenharmony_ci info = info.makeWH(info.width() - 5, info.height() - 5); 167cb93a386Sopenharmony_ci auto rect = info.bounds(); 168cb93a386Sopenharmony_ci auto image = SkAnimatedImage::Make(std::move(codec), info, rect, nullptr); 169cb93a386Sopenharmony_ci if (!image) { 170cb93a386Sopenharmony_ci ERRORF(r, "Failed to create animated image for %s", file); 171cb93a386Sopenharmony_ci return; 172cb93a386Sopenharmony_ci } 173cb93a386Sopenharmony_ci 174cb93a386Sopenharmony_ci // Clear a bitmap to non-transparent and draw to it. pixels that are transparent 175cb93a386Sopenharmony_ci // in the image should not replace the original non-transparent color. 176cb93a386Sopenharmony_ci SkBitmap bm; 177cb93a386Sopenharmony_ci bm.allocPixels(SkImageInfo::MakeN32Premul(info.width(), info.height())); 178cb93a386Sopenharmony_ci bm.eraseColor(SK_ColorBLUE); 179cb93a386Sopenharmony_ci SkCanvas canvas(bm); 180cb93a386Sopenharmony_ci image->draw(&canvas); 181cb93a386Sopenharmony_ci for (int i = 0; i < info.width(); ++i) 182cb93a386Sopenharmony_ci for (int j = 0; j < info.height(); ++j) { 183cb93a386Sopenharmony_ci if (*bm.getAddr32(i, j) == SK_ColorTRANSPARENT) { 184cb93a386Sopenharmony_ci ERRORF(r, "Erased color underneath!"); 185cb93a386Sopenharmony_ci return; 186cb93a386Sopenharmony_ci } 187cb93a386Sopenharmony_ci } 188cb93a386Sopenharmony_ci} 189cb93a386Sopenharmony_ci 190cb93a386Sopenharmony_cistatic bool compare_bitmaps(skiatest::Reporter* r, 191cb93a386Sopenharmony_ci const char* file, 192cb93a386Sopenharmony_ci int expectedFrame, 193cb93a386Sopenharmony_ci const SkBitmap& expectedBm, 194cb93a386Sopenharmony_ci const SkBitmap& actualBm) { 195cb93a386Sopenharmony_ci REPORTER_ASSERT(r, expectedBm.colorType() == actualBm.colorType()); 196cb93a386Sopenharmony_ci REPORTER_ASSERT(r, expectedBm.dimensions() == actualBm.dimensions()); 197cb93a386Sopenharmony_ci for (int i = 0; i < actualBm.width(); ++i) 198cb93a386Sopenharmony_ci for (int j = 0; j < actualBm.height(); ++j) { 199cb93a386Sopenharmony_ci SkColor expected = SkUnPreMultiply::PMColorToColor(*expectedBm.getAddr32(i, j)); 200cb93a386Sopenharmony_ci SkColor actual = SkUnPreMultiply::PMColorToColor(*actualBm .getAddr32(i, j)); 201cb93a386Sopenharmony_ci if (expected != actual) { 202cb93a386Sopenharmony_ci ERRORF(r, "frame %i of %s does not match at pixel %i, %i!" 203cb93a386Sopenharmony_ci " expected %x\tactual: %x", 204cb93a386Sopenharmony_ci expectedFrame, file, i, j, expected, actual); 205cb93a386Sopenharmony_ci SkString expected_name = SkStringPrintf("expected_%c", '0' + expectedFrame); 206cb93a386Sopenharmony_ci SkString actual_name = SkStringPrintf("actual_%c", '0' + expectedFrame); 207cb93a386Sopenharmony_ci write_bm(expected_name.c_str(), expectedBm); 208cb93a386Sopenharmony_ci write_bm(actual_name.c_str(), actualBm); 209cb93a386Sopenharmony_ci return false; 210cb93a386Sopenharmony_ci } 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci return true; 213cb93a386Sopenharmony_ci} 214cb93a386Sopenharmony_ci 215cb93a386Sopenharmony_ciDEF_TEST(AnimatedImage_copyOnWrite, r) { 216cb93a386Sopenharmony_ci if (GetResourcePath().isEmpty()) { 217cb93a386Sopenharmony_ci return; 218cb93a386Sopenharmony_ci } 219cb93a386Sopenharmony_ci for (const char* file : { "images/alphabetAnim.gif", 220cb93a386Sopenharmony_ci "images/colorTables.gif", 221cb93a386Sopenharmony_ci "images/stoplight.webp", 222cb93a386Sopenharmony_ci "images/required.webp", 223cb93a386Sopenharmony_ci }) { 224cb93a386Sopenharmony_ci auto data = GetResourceAsData(file); 225cb93a386Sopenharmony_ci if (!data) { 226cb93a386Sopenharmony_ci ERRORF(r, "Could not get %s", file); 227cb93a386Sopenharmony_ci continue; 228cb93a386Sopenharmony_ci } 229cb93a386Sopenharmony_ci 230cb93a386Sopenharmony_ci auto codec = SkCodec::MakeFromData(data); 231cb93a386Sopenharmony_ci if (!codec) { 232cb93a386Sopenharmony_ci ERRORF(r, "Could not create codec for %s", file); 233cb93a386Sopenharmony_ci continue; 234cb93a386Sopenharmony_ci } 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_ci const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType); 237cb93a386Sopenharmony_ci const int frameCount = codec->getFrameCount(); 238cb93a386Sopenharmony_ci auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec)); 239cb93a386Sopenharmony_ci if (!androidCodec) { 240cb93a386Sopenharmony_ci ERRORF(r, "Could not create androidCodec for %s", file); 241cb93a386Sopenharmony_ci continue; 242cb93a386Sopenharmony_ci } 243cb93a386Sopenharmony_ci 244cb93a386Sopenharmony_ci auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec)); 245cb93a386Sopenharmony_ci if (!animatedImage) { 246cb93a386Sopenharmony_ci ERRORF(r, "Could not create animated image for %s", file); 247cb93a386Sopenharmony_ci continue; 248cb93a386Sopenharmony_ci } 249cb93a386Sopenharmony_ci animatedImage->setRepetitionCount(0); 250cb93a386Sopenharmony_ci 251cb93a386Sopenharmony_ci std::vector<SkBitmap> expected(frameCount); 252cb93a386Sopenharmony_ci std::vector<sk_sp<SkPicture>> pictures(frameCount); 253cb93a386Sopenharmony_ci for (int i = 0; i < frameCount; i++) { 254cb93a386Sopenharmony_ci SkBitmap& bm = expected[i]; 255cb93a386Sopenharmony_ci bm.allocPixels(imageInfo); 256cb93a386Sopenharmony_ci bm.eraseColor(SK_ColorTRANSPARENT); 257cb93a386Sopenharmony_ci SkCanvas canvas(bm); 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ci pictures[i].reset(animatedImage->newPictureSnapshot()); 260cb93a386Sopenharmony_ci canvas.drawPicture(pictures[i]); 261cb93a386Sopenharmony_ci 262cb93a386Sopenharmony_ci const auto duration = animatedImage->decodeNextFrame(); 263cb93a386Sopenharmony_ci // We're attempting to decode i + 1, so decodeNextFrame will return 264cb93a386Sopenharmony_ci // kFinished if that is the last frame (or we attempt to decode one 265cb93a386Sopenharmony_ci // more). 266cb93a386Sopenharmony_ci if (i >= frameCount - 2) { 267cb93a386Sopenharmony_ci REPORTER_ASSERT(r, duration == SkAnimatedImage::kFinished); 268cb93a386Sopenharmony_ci } else { 269cb93a386Sopenharmony_ci REPORTER_ASSERT(r, duration != SkAnimatedImage::kFinished); 270cb93a386Sopenharmony_ci } 271cb93a386Sopenharmony_ci } 272cb93a386Sopenharmony_ci 273cb93a386Sopenharmony_ci for (int i = 0; i < frameCount; i++) { 274cb93a386Sopenharmony_ci SkBitmap test; 275cb93a386Sopenharmony_ci test.allocPixels(imageInfo); 276cb93a386Sopenharmony_ci test.eraseColor(SK_ColorTRANSPARENT); 277cb93a386Sopenharmony_ci SkCanvas canvas(test); 278cb93a386Sopenharmony_ci 279cb93a386Sopenharmony_ci canvas.drawPicture(pictures[i]); 280cb93a386Sopenharmony_ci 281cb93a386Sopenharmony_ci compare_bitmaps(r, file, i, expected[i], test); 282cb93a386Sopenharmony_ci } 283cb93a386Sopenharmony_ci } 284cb93a386Sopenharmony_ci} 285cb93a386Sopenharmony_ci 286cb93a386Sopenharmony_ciDEF_TEST(AnimatedImage, r) { 287cb93a386Sopenharmony_ci if (GetResourcePath().isEmpty()) { 288cb93a386Sopenharmony_ci return; 289cb93a386Sopenharmony_ci } 290cb93a386Sopenharmony_ci for (const char* file : { "images/alphabetAnim.gif", 291cb93a386Sopenharmony_ci "images/colorTables.gif", 292cb93a386Sopenharmony_ci "images/stoplight.webp", 293cb93a386Sopenharmony_ci "images/required.webp", 294cb93a386Sopenharmony_ci }) { 295cb93a386Sopenharmony_ci auto data = GetResourceAsData(file); 296cb93a386Sopenharmony_ci if (!data) { 297cb93a386Sopenharmony_ci ERRORF(r, "Could not get %s", file); 298cb93a386Sopenharmony_ci continue; 299cb93a386Sopenharmony_ci } 300cb93a386Sopenharmony_ci 301cb93a386Sopenharmony_ci auto codec = SkCodec::MakeFromData(data); 302cb93a386Sopenharmony_ci if (!codec) { 303cb93a386Sopenharmony_ci ERRORF(r, "Could not create codec for %s", file); 304cb93a386Sopenharmony_ci continue; 305cb93a386Sopenharmony_ci } 306cb93a386Sopenharmony_ci 307cb93a386Sopenharmony_ci const int defaultRepetitionCount = codec->getRepetitionCount(); 308cb93a386Sopenharmony_ci std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo(); 309cb93a386Sopenharmony_ci std::vector<SkBitmap> frames(frameInfos.size()); 310cb93a386Sopenharmony_ci // Used down below for our test image. 311cb93a386Sopenharmony_ci const auto imageInfo = codec->getInfo().makeAlphaType(kPremul_SkAlphaType); 312cb93a386Sopenharmony_ci 313cb93a386Sopenharmony_ci for (size_t i = 0; i < frameInfos.size(); ++i) { 314cb93a386Sopenharmony_ci auto info = codec->getInfo().makeAlphaType(frameInfos[i].fAlphaType); 315cb93a386Sopenharmony_ci auto& bm = frames[i]; 316cb93a386Sopenharmony_ci 317cb93a386Sopenharmony_ci SkCodec::Options options; 318cb93a386Sopenharmony_ci options.fFrameIndex = (int) i; 319cb93a386Sopenharmony_ci options.fPriorFrame = frameInfos[i].fRequiredFrame; 320cb93a386Sopenharmony_ci if (options.fPriorFrame == SkCodec::kNoFrame) { 321cb93a386Sopenharmony_ci bm.allocPixels(info); 322cb93a386Sopenharmony_ci bm.eraseColor(0); 323cb93a386Sopenharmony_ci } else { 324cb93a386Sopenharmony_ci const SkBitmap& priorFrame = frames[options.fPriorFrame]; 325cb93a386Sopenharmony_ci if (!ToolUtils::copy_to(&bm, priorFrame.colorType(), priorFrame)) { 326cb93a386Sopenharmony_ci ERRORF(r, "Failed to copy %s frame %i", file, options.fPriorFrame); 327cb93a386Sopenharmony_ci options.fPriorFrame = SkCodec::kNoFrame; 328cb93a386Sopenharmony_ci } 329cb93a386Sopenharmony_ci REPORTER_ASSERT(r, bm.setAlphaType(frameInfos[i].fAlphaType)); 330cb93a386Sopenharmony_ci } 331cb93a386Sopenharmony_ci 332cb93a386Sopenharmony_ci auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(), &options); 333cb93a386Sopenharmony_ci if (result != SkCodec::kSuccess) { 334cb93a386Sopenharmony_ci ERRORF(r, "error in %s frame %zu: %s", file, i, SkCodec::ResultToString(result)); 335cb93a386Sopenharmony_ci } 336cb93a386Sopenharmony_ci } 337cb93a386Sopenharmony_ci 338cb93a386Sopenharmony_ci auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec)); 339cb93a386Sopenharmony_ci if (!androidCodec) { 340cb93a386Sopenharmony_ci ERRORF(r, "Could not create androidCodec for %s", file); 341cb93a386Sopenharmony_ci continue; 342cb93a386Sopenharmony_ci } 343cb93a386Sopenharmony_ci 344cb93a386Sopenharmony_ci auto animatedImage = SkAnimatedImage::Make(std::move(androidCodec)); 345cb93a386Sopenharmony_ci if (!animatedImage) { 346cb93a386Sopenharmony_ci ERRORF(r, "Could not create animated image for %s", file); 347cb93a386Sopenharmony_ci continue; 348cb93a386Sopenharmony_ci } 349cb93a386Sopenharmony_ci 350cb93a386Sopenharmony_ci REPORTER_ASSERT(r, defaultRepetitionCount == animatedImage->getRepetitionCount()); 351cb93a386Sopenharmony_ci 352cb93a386Sopenharmony_ci auto testDraw = [r, &frames, &imageInfo, file](const sk_sp<SkAnimatedImage>& animatedImage, 353cb93a386Sopenharmony_ci int expectedFrame) { 354cb93a386Sopenharmony_ci SkBitmap test; 355cb93a386Sopenharmony_ci test.allocPixels(imageInfo); 356cb93a386Sopenharmony_ci test.eraseColor(0); 357cb93a386Sopenharmony_ci SkCanvas c(test); 358cb93a386Sopenharmony_ci animatedImage->draw(&c); 359cb93a386Sopenharmony_ci 360cb93a386Sopenharmony_ci const SkBitmap& frame = frames[expectedFrame]; 361cb93a386Sopenharmony_ci return compare_bitmaps(r, file, expectedFrame, frame, test); 362cb93a386Sopenharmony_ci }; 363cb93a386Sopenharmony_ci 364cb93a386Sopenharmony_ci REPORTER_ASSERT(r, animatedImage->currentFrameDuration() == frameInfos[0].fDuration); 365cb93a386Sopenharmony_ci 366cb93a386Sopenharmony_ci if (!testDraw(animatedImage, 0)) { 367cb93a386Sopenharmony_ci ERRORF(r, "Did not start with frame 0"); 368cb93a386Sopenharmony_ci continue; 369cb93a386Sopenharmony_ci } 370cb93a386Sopenharmony_ci 371cb93a386Sopenharmony_ci // Start at an arbitrary time. 372cb93a386Sopenharmony_ci bool failed = false; 373cb93a386Sopenharmony_ci for (size_t i = 1; i < frameInfos.size(); ++i) { 374cb93a386Sopenharmony_ci const int frameTime = animatedImage->decodeNextFrame(); 375cb93a386Sopenharmony_ci REPORTER_ASSERT(r, frameTime == animatedImage->currentFrameDuration()); 376cb93a386Sopenharmony_ci 377cb93a386Sopenharmony_ci if (i == frameInfos.size() - 1 && defaultRepetitionCount == 0) { 378cb93a386Sopenharmony_ci REPORTER_ASSERT(r, frameTime == SkAnimatedImage::kFinished); 379cb93a386Sopenharmony_ci REPORTER_ASSERT(r, animatedImage->isFinished()); 380cb93a386Sopenharmony_ci } else { 381cb93a386Sopenharmony_ci REPORTER_ASSERT(r, frameTime == frameInfos[i].fDuration); 382cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !animatedImage->isFinished()); 383cb93a386Sopenharmony_ci } 384cb93a386Sopenharmony_ci 385cb93a386Sopenharmony_ci if (!testDraw(animatedImage, i)) { 386cb93a386Sopenharmony_ci ERRORF(r, "Did not update to %zu properly", i); 387cb93a386Sopenharmony_ci failed = true; 388cb93a386Sopenharmony_ci break; 389cb93a386Sopenharmony_ci } 390cb93a386Sopenharmony_ci } 391cb93a386Sopenharmony_ci 392cb93a386Sopenharmony_ci if (failed) { 393cb93a386Sopenharmony_ci continue; 394cb93a386Sopenharmony_ci } 395cb93a386Sopenharmony_ci 396cb93a386Sopenharmony_ci animatedImage->reset(); 397cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !animatedImage->isFinished()); 398cb93a386Sopenharmony_ci if (!testDraw(animatedImage, 0)) { 399cb93a386Sopenharmony_ci ERRORF(r, "reset failed"); 400cb93a386Sopenharmony_ci continue; 401cb93a386Sopenharmony_ci } 402cb93a386Sopenharmony_ci 403cb93a386Sopenharmony_ci // Test reset from all the frames. 404cb93a386Sopenharmony_ci // j is the frame to call reset on. 405cb93a386Sopenharmony_ci for (int j = 0; j < (int) frameInfos.size(); ++j) { 406cb93a386Sopenharmony_ci if (failed) { 407cb93a386Sopenharmony_ci break; 408cb93a386Sopenharmony_ci } 409cb93a386Sopenharmony_ci 410cb93a386Sopenharmony_ci // i is the frame to decode. 411cb93a386Sopenharmony_ci for (int i = 0; i <= j; ++i) { 412cb93a386Sopenharmony_ci if (i == j) { 413cb93a386Sopenharmony_ci animatedImage->reset(); 414cb93a386Sopenharmony_ci if (!testDraw(animatedImage, 0)) { 415cb93a386Sopenharmony_ci ERRORF(r, "reset failed for image %s from frame %i", 416cb93a386Sopenharmony_ci file, i); 417cb93a386Sopenharmony_ci failed = true; 418cb93a386Sopenharmony_ci break; 419cb93a386Sopenharmony_ci } 420cb93a386Sopenharmony_ci } else if (i != 0) { 421cb93a386Sopenharmony_ci animatedImage->decodeNextFrame(); 422cb93a386Sopenharmony_ci if (!testDraw(animatedImage, i)) { 423cb93a386Sopenharmony_ci ERRORF(r, "failed to match frame %i in %s on iteration %i", 424cb93a386Sopenharmony_ci i, file, j); 425cb93a386Sopenharmony_ci failed = true; 426cb93a386Sopenharmony_ci break; 427cb93a386Sopenharmony_ci } 428cb93a386Sopenharmony_ci } 429cb93a386Sopenharmony_ci } 430cb93a386Sopenharmony_ci } 431cb93a386Sopenharmony_ci 432cb93a386Sopenharmony_ci if (failed) { 433cb93a386Sopenharmony_ci continue; 434cb93a386Sopenharmony_ci } 435cb93a386Sopenharmony_ci 436cb93a386Sopenharmony_ci for (int loopCount : { 0, 1, 2, 5 }) { 437cb93a386Sopenharmony_ci animatedImage = SkAnimatedImage::Make(SkAndroidCodec::MakeFromCodec( 438cb93a386Sopenharmony_ci SkCodec::MakeFromData(data))); 439cb93a386Sopenharmony_ci animatedImage->setRepetitionCount(loopCount); 440cb93a386Sopenharmony_ci REPORTER_ASSERT(r, animatedImage->getRepetitionCount() == loopCount); 441cb93a386Sopenharmony_ci 442cb93a386Sopenharmony_ci for (int loops = 0; loops <= loopCount; loops++) { 443cb93a386Sopenharmony_ci if (failed) { 444cb93a386Sopenharmony_ci break; 445cb93a386Sopenharmony_ci } 446cb93a386Sopenharmony_ci REPORTER_ASSERT(r, !animatedImage->isFinished()); 447cb93a386Sopenharmony_ci for (size_t i = 1; i <= frameInfos.size(); ++i) { 448cb93a386Sopenharmony_ci const int frameTime = animatedImage->decodeNextFrame(); 449cb93a386Sopenharmony_ci if (frameTime == SkAnimatedImage::kFinished) { 450cb93a386Sopenharmony_ci if (loops != loopCount) { 451cb93a386Sopenharmony_ci ERRORF(r, "%s animation stopped early: loops: %i\tloopCount: %i", 452cb93a386Sopenharmony_ci file, loops, loopCount); 453cb93a386Sopenharmony_ci failed = true; 454cb93a386Sopenharmony_ci } 455cb93a386Sopenharmony_ci if (i != frameInfos.size() - 1) { 456cb93a386Sopenharmony_ci ERRORF(r, "%s animation stopped early: i: %zu\tsize: %zu", 457cb93a386Sopenharmony_ci file, i, frameInfos.size()); 458cb93a386Sopenharmony_ci failed = true; 459cb93a386Sopenharmony_ci } 460cb93a386Sopenharmony_ci break; 461cb93a386Sopenharmony_ci } 462cb93a386Sopenharmony_ci } 463cb93a386Sopenharmony_ci } 464cb93a386Sopenharmony_ci 465cb93a386Sopenharmony_ci if (!animatedImage->isFinished()) { 466cb93a386Sopenharmony_ci ERRORF(r, "%s animation should have finished with specified loop count (%i)", 467cb93a386Sopenharmony_ci file, loopCount); 468cb93a386Sopenharmony_ci } 469cb93a386Sopenharmony_ci } 470cb93a386Sopenharmony_ci } 471cb93a386Sopenharmony_ci} 472