blob: 762cd477a7ac0b4e6996f4f8aeae757f7e33916f [file] [log] [blame]
scroggo19b91532016-10-24 09:03:26 -07001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
scroggo38a2cf52016-10-24 09:56:40 -07008#include "SkBitmap.h"
scroggo19b91532016-10-24 09:03:26 -07009#include "SkCodec.h"
10#include "SkStream.h"
11
12#include "Resources.h"
13#include "Test.h"
14
15#include <initializer_list>
16#include <vector>
17
18DEF_TEST(Codec_frames, r) {
19 static const struct {
20 const char* fName;
21 size_t fFrameCount;
22 // One less than fFramecount, since the first frame is always
23 // independent.
24 std::vector<size_t> fRequiredFrames;
25 // The size of this one should match fFrameCount for animated, empty
26 // otherwise.
27 std::vector<size_t> fDurations;
scroggoe71b1a12016-11-01 08:28:28 -070028 int fRepetitionCount;
scroggo19b91532016-10-24 09:03:26 -070029 } gRecs[] = {
scroggoe71b1a12016-11-01 08:28:28 -070030 { "box.gif", 1, {}, {}, 0 },
31 { "color_wheel.gif", 1, {}, {}, 0 },
32 { "test640x479.gif", 4, { 0, 1, 2 }, { 200, 200, 200, 200 },
33 SkCodec::kRepetitionCountInfinite },
34 { "colorTables.gif", 2, { 0 }, { 1000, 1000 }, 5 },
scroggo19b91532016-10-24 09:03:26 -070035
scroggoe71b1a12016-11-01 08:28:28 -070036 { "arrow.png", 1, {}, {}, 0 },
37 { "google_chrome.ico", 1, {}, {}, 0 },
38 { "brickwork-texture.jpg", 1, {}, {}, 0 },
scroggo19b91532016-10-24 09:03:26 -070039#if defined(SK_CODEC_DECODES_RAW) && (!defined(_WIN32))
scroggoe71b1a12016-11-01 08:28:28 -070040 { "dng_with_preview.dng", 1, {}, {}, 0 },
scroggo19b91532016-10-24 09:03:26 -070041#endif
scroggoe71b1a12016-11-01 08:28:28 -070042 { "mandrill.wbmp", 1, {}, {}, 0 },
43 { "randPixels.bmp", 1, {}, {}, 0 },
44 { "yellow_rose.webp", 1, {}, {}, 0 },
scroggo19b91532016-10-24 09:03:26 -070045 };
46
47 for (auto rec : gRecs) {
48 std::unique_ptr<SkStream> stream(GetResourceAsStream(rec.fName));
49 if (!stream) {
50 // Useful error statement, but sometimes people run tests without
51 // resources, and they do not want to see these messages.
52 //ERRORF(r, "Missing resources? Could not find '%s'", rec.fName);
53 continue;
54 }
55
56 std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
57 if (!codec) {
58 ERRORF(r, "Failed to create an SkCodec from '%s'", rec.fName);
59 continue;
60 }
61
scroggoe71b1a12016-11-01 08:28:28 -070062 const int repetitionCount = codec->getRepetitionCount();
63 if (repetitionCount != rec.fRepetitionCount) {
64 ERRORF(r, "%s repetition count does not match! expected: %i\tactual: %i",
65 rec.fName, rec.fRepetitionCount, repetitionCount);
66 }
67
scroggo19b91532016-10-24 09:03:26 -070068 const size_t expected = rec.fFrameCount;
69 const auto frameInfos = codec->getFrameInfo();
70 // getFrameInfo returns empty set for non-animated.
71 const size_t frameCount = frameInfos.size() == 0 ? 1 : frameInfos.size();
72 if (frameCount != expected) {
73 ERRORF(r, "'%s' expected frame count: %i\tactual: %i", rec.fName, expected, frameCount);
74 continue;
75 }
76
77 if (rec.fRequiredFrames.size() + 1 != expected) {
78 ERRORF(r, "'%s' has wrong number entries in fRequiredFrames; expected: %i\tactual: %i",
Leon Scroggins IIIfc49b402016-10-31 14:08:56 -040079 rec.fName, expected, rec.fRequiredFrames.size() + 1);
scroggo19b91532016-10-24 09:03:26 -070080 continue;
81 }
82
83 if (1 == frameCount) {
84 continue;
85 }
86
87 // From here on, we are only concerned with animated images.
88 REPORTER_ASSERT(r, frameInfos[0].fRequiredFrame == SkCodec::kNone);
89 for (size_t i = 1; i < frameCount; i++) {
90 REPORTER_ASSERT(r, rec.fRequiredFrames[i-1] == frameInfos[i].fRequiredFrame);
91 }
92
93 // Compare decoding in two ways:
94 // 1. Provide the frame that a frame depends on, so the codec just has to blend.
95 // (in the array cachedFrames)
96 // 2. Do not provide the frame that a frame depends on, so the codec has to decode all the
97 // way back to a key-frame. (in a local variable uncachedFrame)
98 // The two should look the same.
99 std::vector<SkBitmap> cachedFrames(frameCount);
100 const auto& info = codec->getInfo().makeColorType(kN32_SkColorType);
101
102 auto decode = [&](SkBitmap* bm, bool cached, size_t index) {
103 bm->allocPixels(info);
104 if (cached) {
105 // First copy the pixels from the cached frame
106 const size_t requiredFrame = frameInfos[index].fRequiredFrame;
107 if (requiredFrame != SkCodec::kNone) {
108 const bool success = cachedFrames[requiredFrame].copyTo(bm);
109 REPORTER_ASSERT(r, success);
110 }
111 }
112 SkCodec::Options opts;
113 opts.fFrameIndex = index;
114 opts.fHasPriorFrame = cached;
115 const SkCodec::Result result = codec->getPixels(info, bm->getPixels(), bm->rowBytes(),
116 &opts, nullptr, nullptr);
117 REPORTER_ASSERT(r, result == SkCodec::kSuccess);
118 };
119
120 for (size_t i = 0; i < frameCount; i++) {
121 SkBitmap& cachedFrame = cachedFrames[i];
122 decode(&cachedFrame, true, i);
123 SkBitmap uncachedFrame;
124 decode(&uncachedFrame, false, i);
125
126 // Now verify they're equal.
127 const size_t rowLen = info.bytesPerPixel() * info.width();
128 for (int y = 0; y < info.height(); y++) {
129 const void* cachedAddr = cachedFrame.getAddr(0, y);
130 SkASSERT(cachedAddr != nullptr);
131 const void* uncachedAddr = uncachedFrame.getAddr(0, y);
132 SkASSERT(uncachedAddr != nullptr);
133 const bool lineMatches = memcmp(cachedAddr, uncachedAddr, rowLen) == 0;
134 if (!lineMatches) {
135 ERRORF(r, "%s's frame %i is different depending on caching!", rec.fName, i);
136 break;
137 }
138 }
139 }
140
141 if (rec.fDurations.size() != expected) {
142 ERRORF(r, "'%s' has wrong number entries in fDurations; expected: %i\tactual: %i",
143 rec.fName, expected, rec.fDurations.size());
144 continue;
145 }
146
147 for (size_t i = 0; i < frameCount; i++) {
148 REPORTER_ASSERT(r, rec.fDurations[i] == frameInfos[i].fDuration);
149 }
150 }
151}