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