OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkBitmap.h" | 8 #include "SkBitmap.h" |
9 #include "SkCodec.h" | 9 #include "SkCodec.h" |
10 #include "SkData.h" | 10 #include "SkData.h" |
(...skipping 20 matching lines...) Expand all Loading... |
31 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(std::move(data))); | 31 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(std::move(data))); |
32 if (!codec) { | 32 if (!codec) { |
33 return false; | 33 return false; |
34 } | 34 } |
35 | 35 |
36 const SkImageInfo info = standardize_info(codec); | 36 const SkImageInfo info = standardize_info(codec); |
37 dst->allocPixels(info); | 37 dst->allocPixels(info); |
38 return SkCodec::kSuccess == codec->getPixels(info, dst->getPixels(), dst->ro
wBytes()); | 38 return SkCodec::kSuccess == codec->getPixels(info, dst->getPixels(), dst->ro
wBytes()); |
39 } | 39 } |
40 | 40 |
| 41 static void compare_bitmaps(skiatest::Reporter* r, const SkBitmap& bm1, const Sk
Bitmap& bm2) { |
| 42 const SkImageInfo& info = bm1.info(); |
| 43 if (info != bm2.info()) { |
| 44 ERRORF(r, "Bitmaps have different image infos!"); |
| 45 return; |
| 46 } |
| 47 const size_t rowBytes = info.minRowBytes(); |
| 48 for (int i = 0; i < info.height(); i++) { |
| 49 REPORTER_ASSERT(r, !memcmp(bm1.getAddr(0, 0), bm2.getAddr(0, 0), rowByte
s)); |
| 50 } |
| 51 } |
| 52 |
41 /* | 53 /* |
42 * Represents a stream without all of its data. | 54 * Represents a stream without all of its data. |
43 */ | 55 */ |
44 class HaltingStream : public SkStream { | 56 class HaltingStream : public SkStream { |
45 public: | 57 public: |
46 HaltingStream(sk_sp<SkData> data) | 58 HaltingStream(sk_sp<SkData> data, size_t initialLimit) |
47 : fTotalSize(data->size()) | 59 : fTotalSize(data->size()) |
48 , fLimit(fTotalSize / 2) | 60 , fLimit(initialLimit) |
49 , fStream(std::move(data)) | 61 , fStream(std::move(data)) |
50 {} | 62 {} |
51 | 63 |
52 void addNewData() { | 64 void addNewData(size_t extra) { |
53 // Arbitrary size, but deliberately different from | 65 fLimit = SkTMin(fTotalSize, fLimit + extra); |
54 // the buffer size used by SkPngCodec. | |
55 fLimit = SkTMin(fTotalSize, fLimit + 1000); | |
56 } | 66 } |
57 | 67 |
58 size_t read(void* buffer, size_t size) override { | 68 size_t read(void* buffer, size_t size) override { |
59 if (fStream.getPosition() + size > fLimit) { | 69 if (fStream.getPosition() + size > fLimit) { |
60 size = fLimit - fStream.getPosition(); | 70 size = fLimit - fStream.getPosition(); |
61 } | 71 } |
62 | 72 |
63 return fStream.read(buffer, size); | 73 return fStream.read(buffer, size); |
64 } | 74 } |
65 | 75 |
66 bool isAtEnd() const override { | 76 bool isAtEnd() const override { |
67 return fStream.isAtEnd(); | 77 return fStream.isAtEnd(); |
68 } | 78 } |
69 | 79 |
70 bool hasPosition() const override { return true; } | 80 bool hasPosition() const override { return true; } |
71 size_t getPosition() const override { return fStream.getPosition(); } | 81 size_t getPosition() const override { return fStream.getPosition(); } |
72 bool rewind() override { return fStream.rewind(); } | 82 bool rewind() override { return fStream.rewind(); } |
73 bool move(long offset) override { return fStream.move(offset); } | 83 bool move(long offset) override { return fStream.move(offset); } |
74 | 84 |
| 85 bool isAllDataReceived() const { return fLimit == fTotalSize; } |
| 86 |
75 private: | 87 private: |
76 const size_t fTotalSize; | 88 const size_t fTotalSize; |
77 size_t fLimit; | 89 size_t fLimit; |
78 SkMemoryStream fStream; | 90 SkMemoryStream fStream; |
79 }; | 91 }; |
80 | 92 |
81 static void test_partial(skiatest::Reporter* r, const char* name) { | 93 static void test_partial(skiatest::Reporter* r, const char* name) { |
82 sk_sp<SkData> file = make_from_resource(name); | 94 sk_sp<SkData> file = make_from_resource(name); |
83 if (!file) { | 95 if (!file) { |
84 SkDebugf("missing resource %s\n", name); | 96 SkDebugf("missing resource %s\n", name); |
85 return; | 97 return; |
86 } | 98 } |
87 | 99 |
88 SkBitmap truth; | 100 SkBitmap truth; |
89 if (!create_truth(file, &truth)) { | 101 if (!create_truth(file, &truth)) { |
90 ERRORF(r, "Failed to decode %s\n", name); | 102 ERRORF(r, "Failed to decode %s\n", name); |
91 return; | 103 return; |
92 } | 104 } |
93 | 105 |
94 const size_t fileSize = file->size(); | |
95 | |
96 // Now decode part of the file | 106 // Now decode part of the file |
97 HaltingStream* stream = new HaltingStream(file); | 107 HaltingStream* stream = new HaltingStream(file, file->size() / 2); |
98 | 108 |
99 // Note that we cheat and hold on to a pointer to stream, though it is owned
by | 109 // Note that we cheat and hold on to a pointer to stream, though it is owned
by |
100 // partialCodec. | 110 // partialCodec. |
101 SkAutoTDelete<SkCodec> partialCodec(SkCodec::NewFromStream(stream)); | 111 SkAutoTDelete<SkCodec> partialCodec(SkCodec::NewFromStream(stream)); |
102 if (!partialCodec) { | 112 if (!partialCodec) { |
103 // Technically, this could be a small file where half the file is not | 113 // Technically, this could be a small file where half the file is not |
104 // enough. | 114 // enough. |
105 ERRORF(r, "Failed to create codec for %s", name); | 115 ERRORF(r, "Failed to create codec for %s", name); |
106 return; | 116 return; |
107 } | 117 } |
108 | 118 |
109 const SkImageInfo info = standardize_info(partialCodec); | 119 const SkImageInfo info = standardize_info(partialCodec); |
110 SkASSERT(info == truth.info()); | 120 SkASSERT(info == truth.info()); |
111 SkBitmap incremental; | 121 SkBitmap incremental; |
112 incremental.allocPixels(info); | 122 incremental.allocPixels(info); |
113 | 123 |
114 const SkCodec::Result startResult = partialCodec->startIncrementalDecode(inf
o, | 124 while (true) { |
115 incremental.getPixels(), incremental.rowBytes()); | 125 const SkCodec::Result startResult = partialCodec->startIncrementalDecode
(info, |
116 if (startResult != SkCodec::kSuccess) { | 126 incremental.getPixels(), incremental.rowBytes()); |
117 ERRORF(r, "Failed to start incremental decode\n"); | 127 if (startResult == SkCodec::kSuccess) { |
118 return; | 128 break; |
| 129 } |
| 130 |
| 131 if (stream->isAllDataReceived()) { |
| 132 ERRORF(r, "Failed to start incremental decode\n"); |
| 133 return; |
| 134 } |
| 135 |
| 136 // Append some data. The size is arbitrary, but deliberately different f
rom |
| 137 // the buffer size used by SkPngCodec. |
| 138 stream->addNewData(1000); |
119 } | 139 } |
120 | 140 |
121 while (true) { | 141 while (true) { |
122 const SkCodec::Result result = partialCodec->incrementalDecode(); | 142 const SkCodec::Result result = partialCodec->incrementalDecode(); |
123 | 143 |
124 if (stream->getPosition() == fileSize) { | 144 if (result == SkCodec::kSuccess) { |
125 REPORTER_ASSERT(r, result == SkCodec::kSuccess); | |
126 break; | 145 break; |
127 } | 146 } |
128 | 147 |
129 SkASSERT(stream->getPosition() < fileSize); | |
130 | |
131 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); | 148 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); |
132 | 149 |
133 // Append an arbitrary amount of data. | 150 if (stream->isAllDataReceived()) { |
134 stream->addNewData(); | 151 ERRORF(r, "Failed to completely decode %s", name); |
| 152 return; |
| 153 } |
| 154 |
| 155 // Append some data. The size is arbitrary, but deliberately different f
rom |
| 156 // the buffer size used by SkPngCodec. |
| 157 stream->addNewData(1000); |
135 } | 158 } |
136 | 159 |
137 // compare to original | 160 // compare to original |
138 for (int i = 0; i < info.height(); i++) { | 161 compare_bitmaps(r, truth, incremental); |
139 REPORTER_ASSERT(r, !memcmp(truth.getAddr(0, 0), incremental.getAddr(0, 0
), | |
140 info.minRowBytes())); | |
141 } | |
142 } | 162 } |
143 | 163 |
144 DEF_TEST(Codec_partial, r) { | 164 DEF_TEST(Codec_partial, r) { |
145 test_partial(r, "plane.png"); | 165 test_partial(r, "plane.png"); |
146 test_partial(r, "plane_interlaced.png"); | 166 test_partial(r, "plane_interlaced.png"); |
147 test_partial(r, "yellow_rose.png"); | 167 test_partial(r, "yellow_rose.png"); |
148 test_partial(r, "index8.png"); | 168 test_partial(r, "index8.png"); |
149 test_partial(r, "color_wheel.png"); | 169 test_partial(r, "color_wheel.png"); |
150 test_partial(r, "mandrill_256.png"); | 170 test_partial(r, "mandrill_256.png"); |
151 test_partial(r, "mandrill_32.png"); | 171 test_partial(r, "mandrill_32.png"); |
152 test_partial(r, "arrow.png"); | 172 test_partial(r, "arrow.png"); |
153 test_partial(r, "randPixels.png"); | 173 test_partial(r, "randPixels.png"); |
154 test_partial(r, "baby_tux.png"); | 174 test_partial(r, "baby_tux.png"); |
| 175 |
| 176 test_partial(r, "box.gif"); |
| 177 test_partial(r, "randPixels.gif"); |
| 178 test_partial(r, "color_wheel.gif"); |
| 179 } |
| 180 |
| 181 DEF_TEST(Codec_partialAnim, r) { |
| 182 auto path = "test640x479.gif"; |
| 183 sk_sp<SkData> file = make_from_resource(path); |
| 184 if (!file) { |
| 185 return; |
| 186 } |
| 187 |
| 188 // This stream will be owned by fullCodec, but we hang on to the pointer |
| 189 // to determine frame offsets. |
| 190 SkStream* stream = new SkMemoryStream(file); |
| 191 std::unique_ptr<SkCodec> fullCodec(SkCodec::NewFromStream(stream)); |
| 192 const auto info = standardize_info(fullCodec.get()); |
| 193 |
| 194 // frameByteCounts stores the number of bytes to decode a particular frame. |
| 195 // - [0] is the number of bytes for the header |
| 196 // - frames[i] requires frameByteCounts[i+1] bytes to decode |
| 197 std::vector<size_t> frameByteCounts; |
| 198 std::vector<SkBitmap> frames; |
| 199 size_t lastOffset = 0; |
| 200 for (size_t i = 0; true; i++) { |
| 201 frameByteCounts.push_back(stream->getPosition() - lastOffset); |
| 202 lastOffset = stream->getPosition(); |
| 203 |
| 204 SkBitmap frame; |
| 205 frame.allocPixels(info); |
| 206 |
| 207 SkCodec::Options opts; |
| 208 opts.fFrameIndex = i; |
| 209 const SkCodec::Result result = fullCodec->getPixels(info, frame.getPixel
s(), |
| 210 frame.rowBytes(), &opts, nullptr, nullptr); |
| 211 |
| 212 if (result == SkCodec::kIncompleteInput) { |
| 213 frameByteCounts.push_back(stream->getPosition() - lastOffset); |
| 214 |
| 215 // We need to distinguish between a partial frame and no more frames
. |
| 216 // getFrameInfo lets us do this, since it tells the number of frames |
| 217 // not considering whether they are complete. |
| 218 // FIXME: Should we use a different Result? |
| 219 if (fullCodec->getFrameInfo().size() > i) { |
| 220 // This is a partial frame. |
| 221 frames.push_back(frame); |
| 222 } |
| 223 break; |
| 224 } |
| 225 |
| 226 if (result != SkCodec::kSuccess) { |
| 227 ERRORF(r, "Failed to decode frame %i from %s", i, path); |
| 228 return; |
| 229 } |
| 230 |
| 231 frames.push_back(frame); |
| 232 } |
| 233 |
| 234 // Now decode frames partially, then completely, and compare to the original
. |
| 235 HaltingStream* haltingStream = new HaltingStream(file, frameByteCounts[0]); |
| 236 std::unique_ptr<SkCodec> partialCodec(SkCodec::NewFromStream(haltingStream))
; |
| 237 if (!partialCodec) { |
| 238 ERRORF(r, "Failed to create a partial codec from %s with %i bytes out of
%i", |
| 239 path, frameByteCounts[0], file->size()); |
| 240 return; |
| 241 } |
| 242 |
| 243 SkASSERT(frameByteCounts.size() > frames.size()); |
| 244 for (size_t i = 0; i < frames.size(); i++) { |
| 245 const size_t fullFrameBytes = frameByteCounts[i + 1]; |
| 246 const size_t firstHalf = fullFrameBytes / 2; |
| 247 const size_t secondHalf = fullFrameBytes - firstHalf; |
| 248 |
| 249 haltingStream->addNewData(firstHalf); |
| 250 |
| 251 SkBitmap frame; |
| 252 frame.allocPixels(info); |
| 253 |
| 254 SkCodec::Options opts; |
| 255 opts.fFrameIndex = i; |
| 256 SkCodec::Result result = partialCodec->startIncrementalDecode(info, |
| 257 frame.getPixels(), frame.rowBytes(), &opts); |
| 258 if (result != SkCodec::kSuccess) { |
| 259 ERRORF(r, "Failed to start incremental decode for %s on frame %i", |
| 260 path, i); |
| 261 return; |
| 262 } |
| 263 |
| 264 result = partialCodec->incrementalDecode(); |
| 265 REPORTER_ASSERT(r, SkCodec::kIncompleteInput == result); |
| 266 |
| 267 haltingStream->addNewData(secondHalf); |
| 268 result = partialCodec->incrementalDecode(); |
| 269 REPORTER_ASSERT(r, SkCodec::kSuccess == result); |
| 270 |
| 271 // allocPixels locked the pixels for frame, but frames[i] was copied |
| 272 // from another bitmap, and did not retain the locked status. |
| 273 SkAutoLockPixels alp(frames[i]); |
| 274 compare_bitmaps(r, frames[i], frame); |
| 275 } |
155 } | 276 } |
156 | 277 |
157 // Test that calling getPixels when an incremental decode has been | 278 // Test that calling getPixels when an incremental decode has been |
158 // started (but not finished) makes the next call to incrementalDecode | 279 // started (but not finished) makes the next call to incrementalDecode |
159 // require a call to startIncrementalDecode. | 280 // require a call to startIncrementalDecode. |
160 static void test_interleaved(skiatest::Reporter* r, const char* name) { | 281 static void test_interleaved(skiatest::Reporter* r, const char* name) { |
161 sk_sp<SkData> file = make_from_resource(name); | 282 sk_sp<SkData> file = make_from_resource(name); |
| 283 if (!file) { |
| 284 return; |
| 285 } |
| 286 const size_t halfSize = file->size() / 2; |
162 SkAutoTDelete<SkCodec> partialCodec(SkCodec::NewFromStream( | 287 SkAutoTDelete<SkCodec> partialCodec(SkCodec::NewFromStream( |
163 new HaltingStream(std::move(file)))); | 288 new HaltingStream(std::move(file), halfSize))); |
164 if (!partialCodec) { | 289 if (!partialCodec) { |
165 ERRORF(r, "Failed to create codec for %s", name); | 290 ERRORF(r, "Failed to create codec for %s", name); |
166 return; | 291 return; |
167 } | 292 } |
168 | 293 |
169 const SkImageInfo info = standardize_info(partialCodec); | 294 const SkImageInfo info = standardize_info(partialCodec); |
170 SkBitmap incremental; | 295 SkBitmap incremental; |
171 incremental.allocPixels(info); | 296 incremental.allocPixels(info); |
172 | 297 |
173 const SkCodec::Result startResult = partialCodec->startIncrementalDecode(inf
o, | 298 const SkCodec::Result startResult = partialCodec->startIncrementalDecode(inf
o, |
(...skipping 12 matching lines...) Expand all Loading... |
186 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); | 311 REPORTER_ASSERT(r, result == SkCodec::kIncompleteInput); |
187 | 312 |
188 // Now incremental decode will fail | 313 // Now incremental decode will fail |
189 result = partialCodec->incrementalDecode(); | 314 result = partialCodec->incrementalDecode(); |
190 REPORTER_ASSERT(r, result == SkCodec::kInvalidParameters); | 315 REPORTER_ASSERT(r, result == SkCodec::kInvalidParameters); |
191 } | 316 } |
192 | 317 |
193 DEF_TEST(Codec_rewind, r) { | 318 DEF_TEST(Codec_rewind, r) { |
194 test_interleaved(r, "plane.png"); | 319 test_interleaved(r, "plane.png"); |
195 test_interleaved(r, "plane_interlaced.png"); | 320 test_interleaved(r, "plane_interlaced.png"); |
| 321 test_interleaved(r, "box.gif"); |
196 } | 322 } |
OLD | NEW |