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