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