1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2016 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/codec/SkAndroidCodec.h"
9cb93a386Sopenharmony_ci#include "include/codec/SkCodec.h"
10cb93a386Sopenharmony_ci#include "include/codec/SkCodecAnimation.h"
11cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h"
12cb93a386Sopenharmony_ci#include "include/core/SkData.h"
13cb93a386Sopenharmony_ci#include "include/core/SkImage.h"
14cb93a386Sopenharmony_ci#include "include/core/SkImageInfo.h"
15cb93a386Sopenharmony_ci#include "include/core/SkRect.h"
16cb93a386Sopenharmony_ci#include "include/core/SkRefCnt.h"
17cb93a386Sopenharmony_ci#include "include/core/SkSize.h"
18cb93a386Sopenharmony_ci#include "include/core/SkString.h"
19cb93a386Sopenharmony_ci#include "include/core/SkTypes.h"
20cb93a386Sopenharmony_ci#include "include/utils/SkAnimCodecPlayer.h"
21cb93a386Sopenharmony_ci#include "tests/CodecPriv.h"
22cb93a386Sopenharmony_ci#include "tests/Test.h"
23cb93a386Sopenharmony_ci#include "tools/Resources.h"
24cb93a386Sopenharmony_ci#include "tools/ToolUtils.h"
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ci#include <stdio.h>
27cb93a386Sopenharmony_ci#include <cstring>
28cb93a386Sopenharmony_ci#include <initializer_list>
29cb93a386Sopenharmony_ci#include <memory>
30cb93a386Sopenharmony_ci#include <utility>
31cb93a386Sopenharmony_ci#include <vector>
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ciDEF_TEST(Codec_trunc, r) {
34cb93a386Sopenharmony_ci    sk_sp<SkData> data(GetResourceAsData("images/box.gif"));
35cb93a386Sopenharmony_ci    if (!data) {
36cb93a386Sopenharmony_ci        return;
37cb93a386Sopenharmony_ci    }
38cb93a386Sopenharmony_ci    // See also Codec_GifTruncated2 in GifTest.cpp for this magic 23.
39cb93a386Sopenharmony_ci    //
40cb93a386Sopenharmony_ci    // TODO: just move this getFrameInfo call to Codec_GifTruncated2?
41cb93a386Sopenharmony_ci    SkCodec::MakeFromData(SkData::MakeSubset(data.get(), 0, 23))->getFrameInfo();
42cb93a386Sopenharmony_ci}
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci// 565 does not support alpha, but there is no reason for it not to support an
45cb93a386Sopenharmony_ci// animated image with a frame that has alpha but then blends onto an opaque
46cb93a386Sopenharmony_ci// frame making the result opaque. Test that we can decode such a frame.
47cb93a386Sopenharmony_ciDEF_TEST(Codec_565, r) {
48cb93a386Sopenharmony_ci    sk_sp<SkData> data(GetResourceAsData("images/blendBG.webp"));
49cb93a386Sopenharmony_ci    if (!data) {
50cb93a386Sopenharmony_ci        return;
51cb93a386Sopenharmony_ci    }
52cb93a386Sopenharmony_ci    std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(std::move(data)));
53cb93a386Sopenharmony_ci    auto info = codec->getInfo().makeColorType(kRGB_565_SkColorType);
54cb93a386Sopenharmony_ci    SkBitmap bm;
55cb93a386Sopenharmony_ci    bm.allocPixels(info);
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_ci    SkCodec::Options options;
58cb93a386Sopenharmony_ci    options.fFrameIndex = 1;
59cb93a386Sopenharmony_ci    options.fPriorFrame = SkCodec::kNoFrame;
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci    const auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes(),
62cb93a386Sopenharmony_ci                                         &options);
63cb93a386Sopenharmony_ci    REPORTER_ASSERT(r, result == SkCodec::kSuccess);
64cb93a386Sopenharmony_ci}
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_cistatic bool restore_previous(const SkCodec::FrameInfo& info) {
67cb93a386Sopenharmony_ci    return info.fDisposalMethod == SkCodecAnimation::DisposalMethod::kRestorePrevious;
68cb93a386Sopenharmony_ci}
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_cinamespace {
71cb93a386Sopenharmony_ciSkString to_string(bool boolean) { return boolean ? SkString("true") : SkString("false"); }
72cb93a386Sopenharmony_ciSkString to_string(SkCodecAnimation::Blend blend) {
73cb93a386Sopenharmony_ci    switch (blend) {
74cb93a386Sopenharmony_ci        case SkCodecAnimation::Blend::kSrcOver:
75cb93a386Sopenharmony_ci            return SkString("kSrcOver");
76cb93a386Sopenharmony_ci        case SkCodecAnimation::Blend::kSrc:
77cb93a386Sopenharmony_ci            return SkString("kSrc");
78cb93a386Sopenharmony_ci        default:
79cb93a386Sopenharmony_ci            return SkString();
80cb93a386Sopenharmony_ci    }
81cb93a386Sopenharmony_ci}
82cb93a386Sopenharmony_ciSkString to_string(SkIRect rect) {
83cb93a386Sopenharmony_ci    return SkStringPrintf("{ %i, %i, %i, %i }", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
84cb93a386Sopenharmony_ci}
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_citemplate <typename T>
87cb93a386Sopenharmony_civoid reporter_assert_equals(skiatest::Reporter* r, const char* name, int i, const char* prop,
88cb93a386Sopenharmony_ci                            T expected, T actual) {
89cb93a386Sopenharmony_ci    REPORTER_ASSERT(r, expected == actual, "%s's frame %i has wrong %s! expected:"
90cb93a386Sopenharmony_ci                    " %s\tactual: %s", name, i, prop, to_string(expected).c_str(),
91cb93a386Sopenharmony_ci                    to_string(actual).c_str());
92cb93a386Sopenharmony_ci}
93cb93a386Sopenharmony_ci} // namespace
94cb93a386Sopenharmony_ci
95cb93a386Sopenharmony_ciDEF_TEST(Codec_frames, r) {
96cb93a386Sopenharmony_ci    constexpr int kNoFrame = SkCodec::kNoFrame;
97cb93a386Sopenharmony_ci    constexpr SkAlphaType kOpaque = kOpaque_SkAlphaType;
98cb93a386Sopenharmony_ci    constexpr SkAlphaType kUnpremul = kUnpremul_SkAlphaType;
99cb93a386Sopenharmony_ci    constexpr SkCodecAnimation::DisposalMethod kKeep =
100cb93a386Sopenharmony_ci            SkCodecAnimation::DisposalMethod::kKeep;
101cb93a386Sopenharmony_ci    constexpr SkCodecAnimation::DisposalMethod kRestoreBG =
102cb93a386Sopenharmony_ci            SkCodecAnimation::DisposalMethod::kRestoreBGColor;
103cb93a386Sopenharmony_ci    constexpr SkCodecAnimation::DisposalMethod kRestorePrev =
104cb93a386Sopenharmony_ci            SkCodecAnimation::DisposalMethod::kRestorePrevious;
105cb93a386Sopenharmony_ci    constexpr auto kSrcOver = SkCodecAnimation::Blend::kSrcOver;
106cb93a386Sopenharmony_ci    constexpr auto kSrc     = SkCodecAnimation::Blend::kSrc;
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ci    static const struct {
109cb93a386Sopenharmony_ci        const char*                                   fName;
110cb93a386Sopenharmony_ci        int                                           fFrameCount;
111cb93a386Sopenharmony_ci        // One less than fFramecount, since the first frame is always
112cb93a386Sopenharmony_ci        // independent.
113cb93a386Sopenharmony_ci        std::vector<int>                              fRequiredFrames;
114cb93a386Sopenharmony_ci        // Same, since the first frame should match getInfo
115cb93a386Sopenharmony_ci        std::vector<SkAlphaType>                      fAlphas;
116cb93a386Sopenharmony_ci        // The size of this one should match fFrameCount for animated, empty
117cb93a386Sopenharmony_ci        // otherwise.
118cb93a386Sopenharmony_ci        std::vector<int>                              fDurations;
119cb93a386Sopenharmony_ci        int                                           fRepetitionCount;
120cb93a386Sopenharmony_ci        std::vector<SkCodecAnimation::DisposalMethod> fDisposalMethods;
121cb93a386Sopenharmony_ci        std::vector<bool>                             fAlphaWithinBounds;
122cb93a386Sopenharmony_ci        std::vector<SkCodecAnimation::Blend>          fBlends;
123cb93a386Sopenharmony_ci        std::vector<SkIRect>                          fFrameRects;
124cb93a386Sopenharmony_ci    } gRecs[] = {
125cb93a386Sopenharmony_ci        { "images/required.gif", 7,
126cb93a386Sopenharmony_ci            { 0, 1, 2, 3, 4, 5 },
127cb93a386Sopenharmony_ci            { kOpaque, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
128cb93a386Sopenharmony_ci            { 100, 100, 100, 100, 100, 100, 100 },
129cb93a386Sopenharmony_ci            0,
130cb93a386Sopenharmony_ci            { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep },
131cb93a386Sopenharmony_ci            { false, true, true, true, true, true, true },
132cb93a386Sopenharmony_ci            { kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
133cb93a386Sopenharmony_ci              kSrcOver },
134cb93a386Sopenharmony_ci            { {0, 0, 100, 100}, {0, 0, 75, 75}, {0, 0, 50, 50}, {0, 0, 60, 60},
135cb93a386Sopenharmony_ci              {0, 0, 100, 100}, {0, 0, 50, 50}, {0, 0, 75, 75}},
136cb93a386Sopenharmony_ci          },
137cb93a386Sopenharmony_ci        { "images/alphabetAnim.gif", 13,
138cb93a386Sopenharmony_ci            { kNoFrame, 0, 0, 0, 0, 5, 6, kNoFrame, kNoFrame, 9, 10, 11 },
139cb93a386Sopenharmony_ci            { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
140cb93a386Sopenharmony_ci              kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
141cb93a386Sopenharmony_ci            { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 },
142cb93a386Sopenharmony_ci            0,
143cb93a386Sopenharmony_ci            { kKeep, kRestorePrev, kRestorePrev, kRestorePrev, kRestorePrev,
144cb93a386Sopenharmony_ci              kRestoreBG, kKeep, kRestoreBG, kRestoreBG, kKeep, kKeep,
145cb93a386Sopenharmony_ci              kRestoreBG, kKeep },
146cb93a386Sopenharmony_ci            { true, false, true, false, true, true, true, true, true, true, true, true, true },
147cb93a386Sopenharmony_ci            { kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
148cb93a386Sopenharmony_ci              kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
149cb93a386Sopenharmony_ci              kSrcOver },
150cb93a386Sopenharmony_ci            { {25, 25, 75, 75}, {25, 25, 75, 75}, {25, 25, 75, 75}, {37, 37, 62, 62},
151cb93a386Sopenharmony_ci              {37, 37, 62, 62}, {25, 25, 75, 75}, {0, 0, 50, 50}, {0, 0, 100, 100},
152cb93a386Sopenharmony_ci              {25, 25, 75, 75}, {25, 25, 75, 75}, {0, 0, 100, 100}, {25, 25, 75, 75},
153cb93a386Sopenharmony_ci              {37, 37, 62, 62}},
154cb93a386Sopenharmony_ci          },
155cb93a386Sopenharmony_ci        { "images/randPixelsAnim2.gif", 4,
156cb93a386Sopenharmony_ci            // required frames
157cb93a386Sopenharmony_ci            { 0, 0, 1 },
158cb93a386Sopenharmony_ci            // alphas
159cb93a386Sopenharmony_ci            { kOpaque, kOpaque, kOpaque },
160cb93a386Sopenharmony_ci            // durations
161cb93a386Sopenharmony_ci            { 0, 1000, 170, 40 },
162cb93a386Sopenharmony_ci            // repetition count
163cb93a386Sopenharmony_ci            0,
164cb93a386Sopenharmony_ci            { kKeep, kKeep, kRestorePrev, kKeep },
165cb93a386Sopenharmony_ci            { false, true, false, false },
166cb93a386Sopenharmony_ci            { kSrcOver, kSrcOver, kSrcOver, kSrcOver },
167cb93a386Sopenharmony_ci            { {0, 0, 8, 8}, {6, 6, 8, 8}, {4, 4, 8, 8}, {7, 0, 8, 8} },
168cb93a386Sopenharmony_ci          },
169cb93a386Sopenharmony_ci        { "images/randPixelsAnim.gif", 13,
170cb93a386Sopenharmony_ci            // required frames
171cb93a386Sopenharmony_ci            { 0, 1, 2, 3, 4, 3, 6, 7, 7, 7, 9, 9 },
172cb93a386Sopenharmony_ci            { kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul,
173cb93a386Sopenharmony_ci              kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul, kUnpremul },
174cb93a386Sopenharmony_ci            // durations
175cb93a386Sopenharmony_ci            { 0, 1000, 170, 40, 220, 7770, 90, 90, 90, 90, 90, 90, 90 },
176cb93a386Sopenharmony_ci            // repetition count
177cb93a386Sopenharmony_ci            0,
178cb93a386Sopenharmony_ci            { kKeep, kKeep, kKeep, kKeep, kRestoreBG, kRestoreBG, kRestoreBG,
179cb93a386Sopenharmony_ci              kRestoreBG, kRestorePrev, kRestoreBG, kRestorePrev, kRestorePrev,
180cb93a386Sopenharmony_ci              kRestorePrev,  },
181cb93a386Sopenharmony_ci            { false, true, true, false, true, true, false, false, true, true, false, false,
182cb93a386Sopenharmony_ci              true },
183cb93a386Sopenharmony_ci            { kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
184cb93a386Sopenharmony_ci              kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver, kSrcOver,
185cb93a386Sopenharmony_ci              kSrcOver },
186cb93a386Sopenharmony_ci            { {4, 4, 12, 12}, {4, 4, 12, 12}, {4, 4, 12, 12}, {0, 0, 8, 8}, {8, 8, 16, 16},
187cb93a386Sopenharmony_ci              {8, 8, 16, 16}, {8, 8, 16, 16}, {2, 2, 10, 10}, {7, 7, 15, 15}, {7, 7, 15, 15},
188cb93a386Sopenharmony_ci              {7, 7, 15, 15}, {0, 0, 8, 8}, {14, 14, 16, 16} },
189cb93a386Sopenharmony_ci        },
190cb93a386Sopenharmony_ci        { "images/box.gif", 1, {}, {}, {}, 0, { kKeep }, {}, {}, {} },
191cb93a386Sopenharmony_ci        { "images/color_wheel.gif", 1, {}, {}, {}, 0, { kKeep }, {}, {}, {} },
192cb93a386Sopenharmony_ci        { "images/test640x479.gif", 4, { 0, 1, 2 },
193cb93a386Sopenharmony_ci                { kOpaque, kOpaque, kOpaque },
194cb93a386Sopenharmony_ci                { 200, 200, 200, 200 },
195cb93a386Sopenharmony_ci                SkCodec::kRepetitionCountInfinite,
196cb93a386Sopenharmony_ci                { kKeep, kKeep, kKeep, kKeep },
197cb93a386Sopenharmony_ci                { false, true, true, true },
198cb93a386Sopenharmony_ci                { kSrcOver, kSrcOver, kSrcOver, kSrcOver },
199cb93a386Sopenharmony_ci                { {0, 0, 640, 479}, {0, 0, 640, 479}, {0, 0, 640, 479}, {0, 0, 640, 479} },
200cb93a386Sopenharmony_ci        },
201cb93a386Sopenharmony_ci        { "images/colorTables.gif", 2, { 0 }, { kOpaque }, { 1000, 1000 }, 5,
202cb93a386Sopenharmony_ci                { kKeep, kKeep }, {false, true}, { kSrcOver, kSrcOver },
203cb93a386Sopenharmony_ci                { {0, 0, 640, 400}, {0, 0, 640, 200}},
204cb93a386Sopenharmony_ci        },
205cb93a386Sopenharmony_ci
206cb93a386Sopenharmony_ci        { "images/arrow.png",  1, {}, {}, {}, 0, {}, {}, {}, {} },
207cb93a386Sopenharmony_ci        { "images/google_chrome.ico", 1, {}, {}, {}, 0, {}, {}, {}, {} },
208cb93a386Sopenharmony_ci        { "images/brickwork-texture.jpg", 1, {}, {}, {}, 0, {}, {}, {}, {} },
209cb93a386Sopenharmony_ci#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
210cb93a386Sopenharmony_ci        { "images/dng_with_preview.dng", 1, {}, {}, {}, 0, {}, {}, {}, {} },
211cb93a386Sopenharmony_ci#endif
212cb93a386Sopenharmony_ci        { "images/mandrill.wbmp", 1, {}, {}, {}, 0, {}, {}, {}, {} },
213cb93a386Sopenharmony_ci        { "images/randPixels.bmp", 1, {}, {}, {}, 0, {}, {}, {}, {} },
214cb93a386Sopenharmony_ci        { "images/yellow_rose.webp", 1, {}, {}, {}, 0, {}, {}, {}, {} },
215cb93a386Sopenharmony_ci        { "images/stoplight.webp", 3, { 0, 1 }, { kOpaque, kOpaque },
216cb93a386Sopenharmony_ci            { 1000, 500, 1000 }, SkCodec::kRepetitionCountInfinite,
217cb93a386Sopenharmony_ci            { kKeep, kKeep, kKeep }, {false, false, false},
218cb93a386Sopenharmony_ci            {kSrcOver, kSrcOver, kSrcOver},
219cb93a386Sopenharmony_ci            { {0, 0, 11, 29}, {2, 10, 9, 27}, {2, 2, 9, 18}},
220cb93a386Sopenharmony_ci        },
221cb93a386Sopenharmony_ci        { "images/blendBG.webp", 7,
222cb93a386Sopenharmony_ci            { 0, kNoFrame, kNoFrame, kNoFrame, 4, 4 },
223cb93a386Sopenharmony_ci            { kOpaque, kOpaque, kUnpremul, kOpaque, kUnpremul, kUnpremul },
224cb93a386Sopenharmony_ci            { 525, 500, 525, 437, 609, 729, 444 },
225cb93a386Sopenharmony_ci            6,
226cb93a386Sopenharmony_ci            { kKeep, kKeep, kKeep, kKeep, kKeep, kKeep, kKeep },
227cb93a386Sopenharmony_ci            { false, true, false, true, false, true, true },
228cb93a386Sopenharmony_ci            { kSrc, kSrcOver, kSrc, kSrc, kSrc, kSrc, kSrc },
229cb93a386Sopenharmony_ci            { {0, 0, 200, 200}, {0, 0, 200, 200}, {0, 0, 200, 200}, {0, 0, 200, 200},
230cb93a386Sopenharmony_ci              {0, 0, 200, 200}, {100, 100, 200, 200}, {100, 100, 200, 200} },
231cb93a386Sopenharmony_ci        },
232cb93a386Sopenharmony_ci        { "images/required.webp", 7,
233cb93a386Sopenharmony_ci            { 0, 1, 1, kNoFrame, 4, 4 },
234cb93a386Sopenharmony_ci            { kOpaque, kUnpremul, kUnpremul, kOpaque, kOpaque, kOpaque },
235cb93a386Sopenharmony_ci            { 100, 100, 100, 100, 100, 100, 100 },
236cb93a386Sopenharmony_ci            0,
237cb93a386Sopenharmony_ci            { kKeep, kRestoreBG, kKeep, kKeep, kKeep, kRestoreBG, kKeep },
238cb93a386Sopenharmony_ci            { false, false, false, false, false, false, false },
239cb93a386Sopenharmony_ci            { kSrc, kSrcOver, kSrcOver, kSrcOver, kSrc, kSrcOver,
240cb93a386Sopenharmony_ci              kSrcOver },
241cb93a386Sopenharmony_ci            { {0, 0, 100, 100}, {0, 0, 75, 75}, {0, 0, 50, 50}, {0, 0, 60, 60},
242cb93a386Sopenharmony_ci              {0, 0, 100, 100}, {0, 0, 50, 50}, {0, 0, 75, 75}},
243cb93a386Sopenharmony_ci          },
244cb93a386Sopenharmony_ci    };
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci    for (const auto& rec : gRecs) {
247cb93a386Sopenharmony_ci        sk_sp<SkData> data(GetResourceAsData(rec.fName));
248cb93a386Sopenharmony_ci        if (!data) {
249cb93a386Sopenharmony_ci            // Useful error statement, but sometimes people run tests without
250cb93a386Sopenharmony_ci            // resources, and they do not want to see these messages.
251cb93a386Sopenharmony_ci            //ERRORF(r, "Missing resources? Could not find '%s'", rec.fName);
252cb93a386Sopenharmony_ci            continue;
253cb93a386Sopenharmony_ci        }
254cb93a386Sopenharmony_ci
255cb93a386Sopenharmony_ci        std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data));
256cb93a386Sopenharmony_ci        if (!codec) {
257cb93a386Sopenharmony_ci            ERRORF(r, "Failed to create an SkCodec from '%s'", rec.fName);
258cb93a386Sopenharmony_ci            continue;
259cb93a386Sopenharmony_ci        }
260cb93a386Sopenharmony_ci
261cb93a386Sopenharmony_ci        {
262cb93a386Sopenharmony_ci            SkCodec::FrameInfo frameInfo;
263cb93a386Sopenharmony_ci            REPORTER_ASSERT(r, !codec->getFrameInfo(0, &frameInfo));
264cb93a386Sopenharmony_ci        }
265cb93a386Sopenharmony_ci
266cb93a386Sopenharmony_ci        const int repetitionCount = codec->getRepetitionCount();
267cb93a386Sopenharmony_ci        if (repetitionCount != rec.fRepetitionCount) {
268cb93a386Sopenharmony_ci            ERRORF(r, "%s repetition count does not match! expected: %i\tactual: %i",
269cb93a386Sopenharmony_ci                      rec.fName, rec.fRepetitionCount, repetitionCount);
270cb93a386Sopenharmony_ci        }
271cb93a386Sopenharmony_ci
272cb93a386Sopenharmony_ci        const int expected = rec.fFrameCount;
273cb93a386Sopenharmony_ci        if (rec.fRequiredFrames.size() + 1 != static_cast<size_t>(expected)) {
274cb93a386Sopenharmony_ci            ERRORF(r, "'%s' has wrong number entries in fRequiredFrames; expected: %i\tactual: %zu",
275cb93a386Sopenharmony_ci                   rec.fName, expected - 1, rec.fRequiredFrames.size());
276cb93a386Sopenharmony_ci            continue;
277cb93a386Sopenharmony_ci        }
278cb93a386Sopenharmony_ci
279cb93a386Sopenharmony_ci        if (expected > 1) {
280cb93a386Sopenharmony_ci            if (rec.fDurations.size() != static_cast<size_t>(expected)) {
281cb93a386Sopenharmony_ci                ERRORF(r, "'%s' has wrong number entries in fDurations; expected: %i\tactual: %zu",
282cb93a386Sopenharmony_ci                       rec.fName, expected, rec.fDurations.size());
283cb93a386Sopenharmony_ci                continue;
284cb93a386Sopenharmony_ci            }
285cb93a386Sopenharmony_ci
286cb93a386Sopenharmony_ci            if (rec.fAlphas.size() + 1 != static_cast<size_t>(expected)) {
287cb93a386Sopenharmony_ci                ERRORF(r, "'%s' has wrong number entries in fAlphas; expected: %i\tactual: %zu",
288cb93a386Sopenharmony_ci                       rec.fName, expected - 1, rec.fAlphas.size());
289cb93a386Sopenharmony_ci                continue;
290cb93a386Sopenharmony_ci            }
291cb93a386Sopenharmony_ci
292cb93a386Sopenharmony_ci            if (rec.fDisposalMethods.size() != static_cast<size_t>(expected)) {
293cb93a386Sopenharmony_ci                ERRORF(r, "'%s' has wrong number entries in fDisposalMethods; "
294cb93a386Sopenharmony_ci                       "expected %i\tactual: %zu",
295cb93a386Sopenharmony_ci                       rec.fName, expected, rec.fDisposalMethods.size());
296cb93a386Sopenharmony_ci                continue;
297cb93a386Sopenharmony_ci            }
298cb93a386Sopenharmony_ci        }
299cb93a386Sopenharmony_ci
300cb93a386Sopenharmony_ci        enum class TestMode {
301cb93a386Sopenharmony_ci            kVector,
302cb93a386Sopenharmony_ci            kIndividual,
303cb93a386Sopenharmony_ci        };
304cb93a386Sopenharmony_ci
305cb93a386Sopenharmony_ci        for (auto mode : { TestMode::kVector, TestMode::kIndividual }) {
306cb93a386Sopenharmony_ci            // Re-create the codec to reset state and test parsing.
307cb93a386Sopenharmony_ci            codec = SkCodec::MakeFromData(data);
308cb93a386Sopenharmony_ci
309cb93a386Sopenharmony_ci            int frameCount;
310cb93a386Sopenharmony_ci            std::vector<SkCodec::FrameInfo> frameInfos;
311cb93a386Sopenharmony_ci            switch (mode) {
312cb93a386Sopenharmony_ci                case TestMode::kVector:
313cb93a386Sopenharmony_ci                    frameInfos = codec->getFrameInfo();
314cb93a386Sopenharmony_ci                    // getFrameInfo returns empty set for non-animated.
315cb93a386Sopenharmony_ci                    frameCount = frameInfos.empty() ? 1 : frameInfos.size();
316cb93a386Sopenharmony_ci                    break;
317cb93a386Sopenharmony_ci                case TestMode::kIndividual:
318cb93a386Sopenharmony_ci                    frameCount = codec->getFrameCount();
319cb93a386Sopenharmony_ci                    break;
320cb93a386Sopenharmony_ci            }
321cb93a386Sopenharmony_ci
322cb93a386Sopenharmony_ci            if (frameCount != expected) {
323cb93a386Sopenharmony_ci                ERRORF(r, "'%s' expected frame count: %i\tactual: %i",
324cb93a386Sopenharmony_ci                       rec.fName, expected, frameCount);
325cb93a386Sopenharmony_ci                continue;
326cb93a386Sopenharmony_ci            }
327cb93a386Sopenharmony_ci
328cb93a386Sopenharmony_ci            // From here on, we are only concerned with animated images.
329cb93a386Sopenharmony_ci            if (1 == frameCount) {
330cb93a386Sopenharmony_ci                continue;
331cb93a386Sopenharmony_ci            }
332cb93a386Sopenharmony_ci
333cb93a386Sopenharmony_ci            for (int i = 0; i < frameCount; i++) {
334cb93a386Sopenharmony_ci                SkCodec::FrameInfo frameInfo;
335cb93a386Sopenharmony_ci                switch (mode) {
336cb93a386Sopenharmony_ci                    case TestMode::kVector:
337cb93a386Sopenharmony_ci                        frameInfo = frameInfos[i];
338cb93a386Sopenharmony_ci                        break;
339cb93a386Sopenharmony_ci                    case TestMode::kIndividual:
340cb93a386Sopenharmony_ci                        REPORTER_ASSERT(r, codec->getFrameInfo(i, nullptr));
341cb93a386Sopenharmony_ci                        REPORTER_ASSERT(r, codec->getFrameInfo(i, &frameInfo));
342cb93a386Sopenharmony_ci                        break;
343cb93a386Sopenharmony_ci                }
344cb93a386Sopenharmony_ci
345cb93a386Sopenharmony_ci                if (rec.fDurations[i] != frameInfo.fDuration) {
346cb93a386Sopenharmony_ci                    ERRORF(r, "%s frame %i's durations do not match! expected: %i\tactual: %i",
347cb93a386Sopenharmony_ci                           rec.fName, i, rec.fDurations[i], frameInfo.fDuration);
348cb93a386Sopenharmony_ci                }
349cb93a386Sopenharmony_ci
350cb93a386Sopenharmony_ci                auto to_string = [](SkAlphaType alpha) {
351cb93a386Sopenharmony_ci                    switch (alpha) {
352cb93a386Sopenharmony_ci                        case kUnpremul_SkAlphaType:
353cb93a386Sopenharmony_ci                            return "unpremul";
354cb93a386Sopenharmony_ci                        case kOpaque_SkAlphaType:
355cb93a386Sopenharmony_ci                            return "opaque";
356cb93a386Sopenharmony_ci                        default:
357cb93a386Sopenharmony_ci                            SkASSERT(false);
358cb93a386Sopenharmony_ci                            return "unknown";
359cb93a386Sopenharmony_ci                    }
360cb93a386Sopenharmony_ci                };
361cb93a386Sopenharmony_ci
362cb93a386Sopenharmony_ci                auto expectedAlpha = 0 == i ? codec->getInfo().alphaType() : rec.fAlphas[i-1];
363cb93a386Sopenharmony_ci                auto alpha = frameInfo.fAlphaType;
364cb93a386Sopenharmony_ci                if (expectedAlpha != alpha) {
365cb93a386Sopenharmony_ci                    ERRORF(r, "%s's frame %i has wrong alpha type! expected: %s\tactual: %s",
366cb93a386Sopenharmony_ci                           rec.fName, i, to_string(expectedAlpha), to_string(alpha));
367cb93a386Sopenharmony_ci                }
368cb93a386Sopenharmony_ci
369cb93a386Sopenharmony_ci                if (0 == i) {
370cb93a386Sopenharmony_ci                    REPORTER_ASSERT(r, frameInfo.fRequiredFrame == SkCodec::kNoFrame);
371cb93a386Sopenharmony_ci                } else if (rec.fRequiredFrames[i-1] != frameInfo.fRequiredFrame) {
372cb93a386Sopenharmony_ci                    ERRORF(r, "%s's frame %i has wrong dependency! expected: %i\tactual: %i",
373cb93a386Sopenharmony_ci                           rec.fName, i, rec.fRequiredFrames[i-1], frameInfo.fRequiredFrame);
374cb93a386Sopenharmony_ci                }
375cb93a386Sopenharmony_ci
376cb93a386Sopenharmony_ci                REPORTER_ASSERT(r, frameInfo.fDisposalMethod == rec.fDisposalMethods[i]);
377cb93a386Sopenharmony_ci
378cb93a386Sopenharmony_ci                reporter_assert_equals<bool>(r, rec.fName, i, "alpha within bounds",
379cb93a386Sopenharmony_ci                                             rec.fAlphaWithinBounds[i],
380cb93a386Sopenharmony_ci                                             frameInfo.fHasAlphaWithinBounds);
381cb93a386Sopenharmony_ci
382cb93a386Sopenharmony_ci                reporter_assert_equals(r, rec.fName, i, "blend mode", rec.fBlends[i],
383cb93a386Sopenharmony_ci                                       frameInfo.fBlend);
384cb93a386Sopenharmony_ci
385cb93a386Sopenharmony_ci                reporter_assert_equals(r, rec.fName, i, "frame rect", rec.fFrameRects[i],
386cb93a386Sopenharmony_ci                                       frameInfo.fFrameRect);
387cb93a386Sopenharmony_ci            }
388cb93a386Sopenharmony_ci
389cb93a386Sopenharmony_ci            if (TestMode::kIndividual == mode) {
390cb93a386Sopenharmony_ci                // No need to test decoding twice.
391cb93a386Sopenharmony_ci                continue;
392cb93a386Sopenharmony_ci            }
393cb93a386Sopenharmony_ci
394cb93a386Sopenharmony_ci            // Compare decoding in multiple ways:
395cb93a386Sopenharmony_ci            // - Start from scratch for each frame. |codec| will have to decode the required frame
396cb93a386Sopenharmony_ci            //   (and any it depends on) to decode. This is stored in |cachedFrames|.
397cb93a386Sopenharmony_ci            // - Provide the frame that a frame depends on, so |codec| just has to blend.
398cb93a386Sopenharmony_ci            // - Provide a frame after the required frame, which will be covered up by the newest
399cb93a386Sopenharmony_ci            //   frame.
400cb93a386Sopenharmony_ci            // All should look the same.
401cb93a386Sopenharmony_ci            std::vector<SkBitmap> cachedFrames(frameCount);
402cb93a386Sopenharmony_ci            const auto info = codec->getInfo().makeColorType(kN32_SkColorType);
403cb93a386Sopenharmony_ci
404cb93a386Sopenharmony_ci            auto decode = [&](SkBitmap* bm, int index, int cachedIndex) {
405cb93a386Sopenharmony_ci                auto decodeInfo = info;
406cb93a386Sopenharmony_ci                if (index > 0) {
407cb93a386Sopenharmony_ci                    decodeInfo = info.makeAlphaType(frameInfos[index].fAlphaType);
408cb93a386Sopenharmony_ci                }
409cb93a386Sopenharmony_ci                bm->allocPixels(decodeInfo);
410cb93a386Sopenharmony_ci                if (cachedIndex != SkCodec::kNoFrame) {
411cb93a386Sopenharmony_ci                    // First copy the pixels from the cached frame
412cb93a386Sopenharmony_ci                    const bool success =
413cb93a386Sopenharmony_ci                            ToolUtils::copy_to(bm, kN32_SkColorType, cachedFrames[cachedIndex]);
414cb93a386Sopenharmony_ci                    REPORTER_ASSERT(r, success);
415cb93a386Sopenharmony_ci                }
416cb93a386Sopenharmony_ci                SkCodec::Options opts;
417cb93a386Sopenharmony_ci                opts.fFrameIndex = index;
418cb93a386Sopenharmony_ci                opts.fPriorFrame = cachedIndex;
419cb93a386Sopenharmony_ci                const auto result = codec->getPixels(decodeInfo, bm->getPixels(), bm->rowBytes(),
420cb93a386Sopenharmony_ci                                                     &opts);
421cb93a386Sopenharmony_ci                if (cachedIndex != SkCodec::kNoFrame &&
422cb93a386Sopenharmony_ci                        restore_previous(frameInfos[cachedIndex])) {
423cb93a386Sopenharmony_ci                    if (result == SkCodec::kInvalidParameters) {
424cb93a386Sopenharmony_ci                        return true;
425cb93a386Sopenharmony_ci                    }
426cb93a386Sopenharmony_ci                    ERRORF(r, "Using a kRestorePrevious frame as fPriorFrame should fail");
427cb93a386Sopenharmony_ci                    return false;
428cb93a386Sopenharmony_ci                }
429cb93a386Sopenharmony_ci                if (result != SkCodec::kSuccess) {
430cb93a386Sopenharmony_ci                    ERRORF(r, "Failed to decode frame %i from %s when providing prior frame %i, "
431cb93a386Sopenharmony_ci                              "error %i", index, rec.fName, cachedIndex, result);
432cb93a386Sopenharmony_ci                }
433cb93a386Sopenharmony_ci                return result == SkCodec::kSuccess;
434cb93a386Sopenharmony_ci            };
435cb93a386Sopenharmony_ci
436cb93a386Sopenharmony_ci            for (int i = 0; i < frameCount; i++) {
437cb93a386Sopenharmony_ci                SkBitmap& cachedFrame = cachedFrames[i];
438cb93a386Sopenharmony_ci                if (!decode(&cachedFrame, i, SkCodec::kNoFrame)) {
439cb93a386Sopenharmony_ci                    continue;
440cb93a386Sopenharmony_ci                }
441cb93a386Sopenharmony_ci                const auto reqFrame = frameInfos[i].fRequiredFrame;
442cb93a386Sopenharmony_ci                if (reqFrame == SkCodec::kNoFrame) {
443cb93a386Sopenharmony_ci                    // Nothing to compare against.
444cb93a386Sopenharmony_ci                    continue;
445cb93a386Sopenharmony_ci                }
446cb93a386Sopenharmony_ci                for (int j = reqFrame; j < i; j++) {
447cb93a386Sopenharmony_ci                    SkBitmap frame;
448cb93a386Sopenharmony_ci                    if (restore_previous(frameInfos[j])) {
449cb93a386Sopenharmony_ci                        (void) decode(&frame, i, j);
450cb93a386Sopenharmony_ci                        continue;
451cb93a386Sopenharmony_ci                    }
452cb93a386Sopenharmony_ci                    if (!decode(&frame, i, j)) {
453cb93a386Sopenharmony_ci                        continue;
454cb93a386Sopenharmony_ci                    }
455cb93a386Sopenharmony_ci
456cb93a386Sopenharmony_ci                    // Now verify they're equal.
457cb93a386Sopenharmony_ci                    const size_t rowLen = info.bytesPerPixel() * info.width();
458cb93a386Sopenharmony_ci                    for (int y = 0; y < info.height(); y++) {
459cb93a386Sopenharmony_ci                        const void* cachedAddr = cachedFrame.getAddr(0, y);
460cb93a386Sopenharmony_ci                        SkASSERT(cachedAddr != nullptr);
461cb93a386Sopenharmony_ci                        const void* addr = frame.getAddr(0, y);
462cb93a386Sopenharmony_ci                        SkASSERT(addr != nullptr);
463cb93a386Sopenharmony_ci                        const bool lineMatches = memcmp(cachedAddr, addr, rowLen) == 0;
464cb93a386Sopenharmony_ci                        if (!lineMatches) {
465cb93a386Sopenharmony_ci                            SkString name = SkStringPrintf("cached_%i", i);
466cb93a386Sopenharmony_ci                            write_bm(name.c_str(), cachedFrame);
467cb93a386Sopenharmony_ci                            name = SkStringPrintf("frame_%i", i);
468cb93a386Sopenharmony_ci                            write_bm(name.c_str(), frame);
469cb93a386Sopenharmony_ci                            ERRORF(r, "%s's frame %i is different (starting from line %i) when "
470cb93a386Sopenharmony_ci                                      "providing prior frame %i!", rec.fName, i, y, j);
471cb93a386Sopenharmony_ci                            break;
472cb93a386Sopenharmony_ci                        }
473cb93a386Sopenharmony_ci                    }
474cb93a386Sopenharmony_ci                }
475cb93a386Sopenharmony_ci            }
476cb93a386Sopenharmony_ci        }
477cb93a386Sopenharmony_ci    }
478cb93a386Sopenharmony_ci}
479cb93a386Sopenharmony_ci
480cb93a386Sopenharmony_ci// Verify that an image can be animated scaled down. These images have a
481cb93a386Sopenharmony_ci// kRestoreBG frame, so they are interesting to test. After decoding that
482cb93a386Sopenharmony_ci// frame, we have to erase its rectangle. The rectangle has to be adjusted
483cb93a386Sopenharmony_ci// based on the scaled size.
484cb93a386Sopenharmony_cistatic void test_animated_AndroidCodec(skiatest::Reporter* r, const char* file) {
485cb93a386Sopenharmony_ci    if (GetResourcePath().isEmpty()) {
486cb93a386Sopenharmony_ci        return;
487cb93a386Sopenharmony_ci    }
488cb93a386Sopenharmony_ci
489cb93a386Sopenharmony_ci    sk_sp<SkData> data(GetResourceAsData(file));
490cb93a386Sopenharmony_ci    if (!data) {
491cb93a386Sopenharmony_ci        ERRORF(r, "Missing %s", file);
492cb93a386Sopenharmony_ci        return;
493cb93a386Sopenharmony_ci    }
494cb93a386Sopenharmony_ci
495cb93a386Sopenharmony_ci    auto codec = SkAndroidCodec::MakeFromCodec(SkCodec::MakeFromData(std::move(data)));
496cb93a386Sopenharmony_ci    if (!codec) {
497cb93a386Sopenharmony_ci        ERRORF(r, "Failed to decode %s", file);
498cb93a386Sopenharmony_ci        return;
499cb93a386Sopenharmony_ci    }
500cb93a386Sopenharmony_ci
501cb93a386Sopenharmony_ci    auto info = codec->getInfo().makeAlphaType(kPremul_SkAlphaType);
502cb93a386Sopenharmony_ci
503cb93a386Sopenharmony_ci    for (int sampleSize : { 8, 32, 100 }) {
504cb93a386Sopenharmony_ci        auto dimensions = codec->codec()->getScaledDimensions(1.0f / sampleSize);
505cb93a386Sopenharmony_ci        info = info.makeDimensions(dimensions);
506cb93a386Sopenharmony_ci        SkBitmap bm;
507cb93a386Sopenharmony_ci        bm.allocPixels(info);
508cb93a386Sopenharmony_ci
509cb93a386Sopenharmony_ci        SkCodec::Options options;
510cb93a386Sopenharmony_ci        for (int i = 0; i < codec->codec()->getFrameCount(); ++i) {
511cb93a386Sopenharmony_ci            SkCodec::FrameInfo frameInfo;
512cb93a386Sopenharmony_ci            REPORTER_ASSERT(r, codec->codec()->getFrameInfo(i, &frameInfo));
513cb93a386Sopenharmony_ci            if (5 == i) {
514cb93a386Sopenharmony_ci                REPORTER_ASSERT(r, frameInfo.fDisposalMethod
515cb93a386Sopenharmony_ci                        == SkCodecAnimation::DisposalMethod::kRestoreBGColor);
516cb93a386Sopenharmony_ci            }
517cb93a386Sopenharmony_ci            options.fFrameIndex = i;
518cb93a386Sopenharmony_ci            options.fPriorFrame = i - 1;
519cb93a386Sopenharmony_ci            info = info.makeAlphaType(frameInfo.fAlphaType);
520cb93a386Sopenharmony_ci
521cb93a386Sopenharmony_ci            auto result = codec->codec()->getPixels(info, bm.getPixels(), bm.rowBytes(),
522cb93a386Sopenharmony_ci                                                    &options);
523cb93a386Sopenharmony_ci            REPORTER_ASSERT(r, result == SkCodec::kSuccess);
524cb93a386Sopenharmony_ci
525cb93a386Sopenharmony_ci            // Now compare to not using prior frame.
526cb93a386Sopenharmony_ci            SkBitmap bm2;
527cb93a386Sopenharmony_ci            bm2.allocPixels(info);
528cb93a386Sopenharmony_ci
529cb93a386Sopenharmony_ci            options.fPriorFrame = SkCodec::kNoFrame;
530cb93a386Sopenharmony_ci            result = codec->codec()->getPixels(info, bm2.getPixels(), bm2.rowBytes(),
531cb93a386Sopenharmony_ci                                               &options);
532cb93a386Sopenharmony_ci            REPORTER_ASSERT(r, result == SkCodec::kSuccess);
533cb93a386Sopenharmony_ci
534cb93a386Sopenharmony_ci            for (int y = 0; y < info.height(); ++y) {
535cb93a386Sopenharmony_ci                if (0 != memcmp(bm.getAddr32(0, y), bm2.getAddr32(0, y), info.minRowBytes())) {
536cb93a386Sopenharmony_ci                    ERRORF(r, "pixel mismatch for sample size %i, frame %i resulting in "
537cb93a386Sopenharmony_ci                              "dimensions %i x %i line %i\n",
538cb93a386Sopenharmony_ci                              sampleSize, i, info.width(), info.height(), y);
539cb93a386Sopenharmony_ci                    break;
540cb93a386Sopenharmony_ci                }
541cb93a386Sopenharmony_ci            }
542cb93a386Sopenharmony_ci        }
543cb93a386Sopenharmony_ci    }
544cb93a386Sopenharmony_ci}
545cb93a386Sopenharmony_ci
546cb93a386Sopenharmony_ciDEF_TEST(AndroidCodec_animated, r) {
547cb93a386Sopenharmony_ci    test_animated_AndroidCodec(r, "images/required.webp");
548cb93a386Sopenharmony_ci}
549cb93a386Sopenharmony_ci
550cb93a386Sopenharmony_ciDEF_TEST(AndroidCodec_animated_gif, r) {
551cb93a386Sopenharmony_ci    test_animated_AndroidCodec(r, "images/required.gif");
552cb93a386Sopenharmony_ci}
553cb93a386Sopenharmony_ci
554cb93a386Sopenharmony_ciDEF_TEST(EncodedOriginToMatrixTest, r) {
555cb93a386Sopenharmony_ci    // SkAnimCodecPlayer relies on the fact that these matrices are invertible.
556cb93a386Sopenharmony_ci    for (auto origin : { kTopLeft_SkEncodedOrigin     ,
557cb93a386Sopenharmony_ci                         kTopRight_SkEncodedOrigin    ,
558cb93a386Sopenharmony_ci                         kBottomRight_SkEncodedOrigin ,
559cb93a386Sopenharmony_ci                         kBottomLeft_SkEncodedOrigin  ,
560cb93a386Sopenharmony_ci                         kLeftTop_SkEncodedOrigin     ,
561cb93a386Sopenharmony_ci                         kRightTop_SkEncodedOrigin    ,
562cb93a386Sopenharmony_ci                         kRightBottom_SkEncodedOrigin ,
563cb93a386Sopenharmony_ci                         kLeftBottom_SkEncodedOrigin  }) {
564cb93a386Sopenharmony_ci        // Arbitrary output dimensions.
565cb93a386Sopenharmony_ci        auto matrix = SkEncodedOriginToMatrix(origin, 100, 80);
566cb93a386Sopenharmony_ci        REPORTER_ASSERT(r, matrix.invert(nullptr));
567cb93a386Sopenharmony_ci    }
568cb93a386Sopenharmony_ci}
569cb93a386Sopenharmony_ci
570cb93a386Sopenharmony_ciDEF_TEST(AnimCodecPlayer, r) {
571cb93a386Sopenharmony_ci    static constexpr struct {
572cb93a386Sopenharmony_ci        const char* fFile;
573cb93a386Sopenharmony_ci        uint32_t    fDuration;
574cb93a386Sopenharmony_ci        SkISize     fSize;
575cb93a386Sopenharmony_ci    } gTests[] = {
576cb93a386Sopenharmony_ci        { "images/alphabetAnim.gif"  , 1300, {100, 100} },
577cb93a386Sopenharmony_ci        { "images/randPixels.gif"    ,    0, {  8,   8} },
578cb93a386Sopenharmony_ci        { "images/randPixels.jpg"    ,    0, {  8,   8} },
579cb93a386Sopenharmony_ci        { "images/randPixels.png"    ,    0, {  8,   8} },
580cb93a386Sopenharmony_ci        { "images/stoplight.webp"    , 2500, { 11,  29} },
581cb93a386Sopenharmony_ci        { "images/stoplight_h.webp"  , 2500, { 29,  11} },
582cb93a386Sopenharmony_ci        { "images/orientation/1.webp",    0, {100,  80} },
583cb93a386Sopenharmony_ci        { "images/orientation/2.webp",    0, {100,  80} },
584cb93a386Sopenharmony_ci        { "images/orientation/3.webp",    0, {100,  80} },
585cb93a386Sopenharmony_ci        { "images/orientation/4.webp",    0, {100,  80} },
586cb93a386Sopenharmony_ci        { "images/orientation/5.webp",    0, {100,  80} },
587cb93a386Sopenharmony_ci        { "images/orientation/6.webp",    0, {100,  80} },
588cb93a386Sopenharmony_ci        { "images/orientation/7.webp",    0, {100,  80} },
589cb93a386Sopenharmony_ci        { "images/orientation/8.webp",    0, {100,  80} },
590cb93a386Sopenharmony_ci    };
591cb93a386Sopenharmony_ci
592cb93a386Sopenharmony_ci    for (const auto& test : gTests) {
593cb93a386Sopenharmony_ci        auto codec = SkCodec::MakeFromData(GetResourceAsData(test.fFile));
594cb93a386Sopenharmony_ci        REPORTER_ASSERT(r, codec);
595cb93a386Sopenharmony_ci
596cb93a386Sopenharmony_ci        auto player = std::make_unique<SkAnimCodecPlayer>(std::move(codec));
597cb93a386Sopenharmony_ci        REPORTER_ASSERT(r, player->duration() == test.fDuration);
598cb93a386Sopenharmony_ci        REPORTER_ASSERT(r, player->dimensions() == test.fSize);
599cb93a386Sopenharmony_ci
600cb93a386Sopenharmony_ci        auto f0 = player->getFrame();
601cb93a386Sopenharmony_ci        REPORTER_ASSERT(r, f0);
602cb93a386Sopenharmony_ci        REPORTER_ASSERT(r, f0->bounds().size() == test.fSize,
603cb93a386Sopenharmony_ci                        "Mismatched size for initial frame of %s", test.fFile);
604cb93a386Sopenharmony_ci
605cb93a386Sopenharmony_ci        player->seek(500);
606cb93a386Sopenharmony_ci        auto f1 = player->getFrame();
607cb93a386Sopenharmony_ci        REPORTER_ASSERT(r, f1);
608cb93a386Sopenharmony_ci        REPORTER_ASSERT(r, f1->bounds().size() == test.fSize,
609cb93a386Sopenharmony_ci                        "Mismatched size for frame at 500 ms of %s", test.fFile);
610cb93a386Sopenharmony_ci    }
611cb93a386Sopenharmony_ci}
612