Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp

Issue 2618633004: Add support for Animated PNG (Closed)
Patch Set: Reject bad data. Cleanups Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006 Apple Computer, Inc. 2 * Copyright (C) 2006 Apple Computer, Inc.
3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. 3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
4 * 4 *
5 * Portions are Copyright (C) 2001 mozilla.org 5 * Portions are Copyright (C) 2001 mozilla.org
6 * 6 *
7 * Other contributors: 7 * Other contributors:
8 * Stuart Parmenter <stuart@mozilla.com> 8 * Stuart Parmenter <stuart@mozilla.com>
9 * 9 *
10 * This library is free software; you can redistribute it and/or 10 * This library is free software; you can redistribute it and/or
(...skipping 20 matching lines...) Expand all
31 * licenses (the MPL or the GPL) and not to allow others to use your 31 * licenses (the MPL or the GPL) and not to allow others to use your
32 * version of this file under the LGPL, indicate your decision by 32 * version of this file under the LGPL, indicate your decision by
33 * deletingthe provisions above and replace them with the notice and 33 * deletingthe provisions above and replace them with the notice and
34 * other provisions required by the MPL or the GPL, as the case may be. 34 * other provisions required by the MPL or the GPL, as the case may be.
35 * If you do not delete the provisions above, a recipient may use your 35 * If you do not delete the provisions above, a recipient may use your
36 * version of this file under any of the LGPL, the MPL or the GPL. 36 * version of this file under any of the LGPL, the MPL or the GPL.
37 */ 37 */
38 38
39 #include "platform/image-decoders/png/PNGImageReader.h" 39 #include "platform/image-decoders/png/PNGImageReader.h"
40 40
41 #include "platform/image-decoders/FastSharedBufferReader.h"
41 #include "platform/image-decoders/SegmentReader.h" 42 #include "platform/image-decoders/SegmentReader.h"
42 #include "png.h" 43 #include "platform/image-decoders/png/PNGImageDecoder.h"
43 #include "wtf/PtrUtil.h" 44 #include "wtf/PtrUtil.h"
45 #include "zlib.h"
44 #include <memory> 46 #include <memory>
45 47
46 namespace { 48 namespace {
Noel Gordon 2017/02/21 13:53:56 I did a quick pass over this file only for now.
scroggo_chromium 2017/02/23 22:09:54 Acknowledged.
47 49
48 inline blink::PNGImageDecoder* imageDecoder(png_structp png) { 50 inline blink::PNGImageDecoder* imageDecoder(png_structp png) {
49 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png)); 51 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png));
50 } 52 }
51 53
52 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) { 54 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) {
53 imageDecoder(png)->headerAvailable(); 55 imageDecoder(png)->headerAvailable();
54 } 56 }
55 57
56 void PNGAPI pngRowAvailable(png_structp png, 58 void PNGAPI pngRowAvailable(png_structp png,
57 png_bytep row, 59 png_bytep row,
58 png_uint_32 rowIndex, 60 png_uint_32 rowIndex,
59 int state) { 61 int state) {
60 imageDecoder(png)->rowAvailable(row, rowIndex, state); 62 imageDecoder(png)->rowAvailable(row, rowIndex, state);
61 } 63 }
62 64
63 void PNGAPI pngComplete(png_structp png, png_infop) { 65 void PNGAPI pngComplete(png_structp png, png_infop) {
Noel Gordon 2017/02/21 13:53:56 Now that the PNG decoder is multi-frame, lets call
scroggo_chromium 2017/02/23 22:09:55 Done.
64 imageDecoder(png)->complete(); 66 imageDecoder(png)->complete();
Noel Gordon 2017/02/21 13:53:56 imageDecoder(png)->frameComplete(); and adjust th
scroggo_chromium 2017/02/23 22:09:55 Done.
65 } 67 }
66 68
67 void PNGAPI pngFailed(png_structp png, png_const_charp) { 69 void PNGAPI pngFailed(png_structp png, png_const_charp) {
68 longjmp(JMPBUF(png), 1); 70 longjmp(JMPBUF(png), 1);
69 } 71 }
70 72
71 } // namespace 73 } // namespace
72 74
73 namespace blink { 75 namespace blink {
74 76
75 PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) 77 PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t initialOffset)
76 : m_decoder(decoder), 78 : m_width(0),
77 m_readOffset(readOffset), 79 m_height(0),
78 m_currentBufferSize(0), 80 m_decoder(decoder),
79 m_decodingSizeOnly(false), 81 m_initialOffset(initialOffset),
80 m_hasAlpha(false) { 82 m_readOffset(initialOffset),
83 m_progressiveDecodeOffset(0),
84 m_idatOffset(0),
85 m_idatIsPartOfAnimation(false),
86 m_expectIdats(true),
87 m_isAnimated(false),
88 m_parsedSignature(false),
89 m_parsedIHDR(false),
90 m_parseCompleted(false),
91 m_reportedFrameCount(0),
92 m_nextSequenceNumber(0),
93 m_fctlNeedsDatChunk(false),
94 m_ignoreAnimation(false) {
81 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); 95 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
82 m_info = png_create_info_struct(m_png); 96 m_info = png_create_info_struct(m_png);
83 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, 97 png_set_progressive_read_fn(m_png, m_decoder, nullptr, pngRowAvailable,
84 pngRowAvailable, pngComplete); 98 pngComplete);
Noel Gordon 2017/02/21 13:53:57 pngFrameComplete, here and anywhere else it's used
scroggo_chromium 2017/02/23 22:09:54 Done.
85 } 99 }
86 100
87 PNGImageReader::~PNGImageReader() { 101 PNGImageReader::~PNGImageReader() {
88 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); 102 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0);
89 DCHECK(!m_png && !m_info); 103 DCHECK(!m_png && !m_info);
90 104 }
91 m_readOffset = 0; 105
92 } 106 // This method reads from the FastSharedBufferReader, starting at offset,
93 107 // and returns |length| bytes in the form of a pointer to a const png_byte*.
94 bool PNGImageReader::decode(const SegmentReader& data, bool sizeOnly) { 108 // This function is used to make it easy to access data from the reader in a
95 m_decodingSizeOnly = sizeOnly; 109 // png friendly way, and pass it to libpng for decoding.
96 110 //
97 // We need to do the setjmp here. Otherwise bad things will happen. 111 // Pre-conditions before using this:
112 // - |reader|.size() >= |readOffset| + |length|
113 // - |buffer|.size() >= |length|
114 // - |length| <= |kBufferSize|
115 //
116 // The reason for the last two precondition is that currently the png signature
117 // plus IHDR chunk (8B + 25B = 33B) is the largest chunk that is read using this
118 // method. If the data is not consecutive, it is stored in |buffer|, which must
119 // have the size of (at least) |length|, but there's no need for it to be larger
120 // than |kBufferSize|.
121 static constexpr size_t kBufferSize = 33;
122 const png_byte* readAsConstPngBytep(const FastSharedBufferReader& reader,
123 size_t readOffset,
124 size_t length,
125 char* buffer) {
126 DCHECK(length <= kBufferSize);
127 return reinterpret_cast<const png_byte*>(
128 reader.getConsecutiveData(readOffset, length, buffer));
129 }
130
131 // This is used as a value for the byteLength of a frameInfo struct to
132 // indicate that it is the first frame and its byteLength is not yet known. 1
133 // is a safe value since the byteLength field of a frame is at least 12, in
134 // the case of an empty fdAT or IDAT chunk.
135 static constexpr size_t kFirstFrameIndicator = 1;
136
137 // Return false on a fatal error
138 bool PNGImageReader::decode(SegmentReader& data, size_t index) {
139 if (index >= m_frameInfo.size())
140 return true;
141
142 const FastSharedBufferReader reader(&data);
143
144 if (!m_isAnimated) {
145 if (setjmp(JMPBUF(m_png))) {
Noel Gordon 2017/02/21 13:53:56 if (setjmp(JMPBUF(m_png))) return false;
scroggo_chromium 2017/02/23 22:09:55 Done.
146 return false;
147 }
148 DCHECK_EQ(0u, index);
149 m_progressiveDecodeOffset += processData(
150 reader, m_frameInfo[0].startOffset + m_progressiveDecodeOffset, 0);
151 return true;
152 }
153
154 if (index > 0 && m_progressiveDecodeOffset > 0) {
Noel Gordon 2017/02/21 13:53:56 DCHECK(m_isAnimated); if (index > 0 && m_progress
scroggo_chromium 2017/02/23 22:09:54 Done.
155 clearDecodeState(0);
156 }
157
158 const bool decodeAsNewPNG = !m_png;
159 if (decodeAsNewPNG) {
160 DCHECK(!m_info);
161 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
162 m_info = png_create_info_struct(m_png);
163 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
164 pngRowAvailable, pngComplete);
165 }
166
98 if (setjmp(JMPBUF(m_png))) 167 if (setjmp(JMPBUF(m_png)))
99 return m_decoder->setFailed(); 168 return false;
100 169
170 if (decodeAsNewPNG)
171 startFrameDecoding(reader, index);
172
173 if (index == 0 &&
Noel Gordon 2017/02/21 13:53:56 comparisons to 0; use !index && ...
scroggo_chromium 2017/02/23 22:09:54 Done.
174 (!firstFrameFullyReceived() || m_progressiveDecodeOffset > 0)) {
175 const bool decodedFrameCompletely = progressivelyDecodeFirstFrame(reader);
176 if (!decodedFrameCompletely)
177 return true;
178
179 m_progressiveDecodeOffset = 0;
180 } else {
181 decodeFrame(reader, index);
182 }
183
184 png_byte IEND[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 174, 66, 96, 130};
Noel Gordon 2017/02/21 13:53:56 Could this be static png_byte IEND[12] ?
scroggo_chromium 2017/02/23 22:09:54 Done.
185 png_process_data(m_png, m_info, IEND, 12);
186 png_destroy_read_struct(&m_png, &m_info, 0);
187 DCHECK(!m_png && !m_info);
188
189 return true;
190 }
191
192 void PNGImageReader::startFrameDecoding(const FastSharedBufferReader& reader,
193 size_t index) {
194 // If the frame is the size of the whole image, just re-process all header
195 // data up to the first frame.
196 const IntRect& frameRect = m_frameInfo[index].frameRect;
197 if (frameRect.location() == IntPoint() &&
198 frameRect.size() == m_decoder->size()) {
199 processData(reader, m_initialOffset, m_idatOffset);
200 return;
201 }
202
203 // Process the IHDR chunk, but change the width and height so it reflects
204 // the frame's width and height. ImageDecoder will apply the x,y offset.
205 char readBuffer[kBufferSize];
206
207 // |headerSize| is equal to |kBufferSize|, but adds more semantic insight.
208 constexpr size_t headerSize = 33;
209 png_byte header[headerSize];
210 const png_byte* chunk =
211 readAsConstPngBytep(reader, m_initialOffset, headerSize, readBuffer);
212 memcpy(header, chunk, headerSize);
213
214 png_save_uint_32(header + 16, frameRect.width());
215 png_save_uint_32(header + 20, frameRect.height());
216 // IHDR has been modified, so tell libpng to ignore CRC errors.
217 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
218 png_process_data(m_png, m_info, header, headerSize);
219
220 // Process the rest of the header chunks.
221 processData(reader, m_initialOffset + headerSize, m_idatOffset - headerSize);
222 }
223
224 // Determine if the bytes 4 to 7 of |chunk| indicate that it is a |tag| chunk.
225 // - The length of |chunk| must be >= 8
226 // - The length of |tag| must be = 4
227 static inline bool isChunk(const png_byte* chunk, const char* tag) {
228 return memcmp(chunk + 4, tag, 4) == 0;
229 }
230
231 bool PNGImageReader::progressivelyDecodeFirstFrame(
232 const FastSharedBufferReader& reader) {
233 char readBuffer[8]; // large enough to identify a chunk.
234 size_t offset = m_frameInfo[0].startOffset;
235
236 // Loop while there is enough data to do progressive decoding.
237 while (reader.size() >= offset + 8) {
238 // At the beginning of each loop, the offset is at the start of a chunk.
239 const png_byte* chunk = readAsConstPngBytep(reader, offset, 8, readBuffer);
240 const png_uint_32 length = png_get_uint_32(chunk);
241 DCHECK(length <= PNG_UINT_31_MAX);
242
243 // When an fcTL or IEND chunk is encountered, the frame data has ended.
244 // Return true, since all frame data is decoded.
245 if (isChunk(chunk, "fcTL") || isChunk(chunk, "IEND"))
246 return true;
247
248 // If this chunk was already decoded, move on to the next.
249 if (m_progressiveDecodeOffset >= offset + length + 12) {
250 offset += length + 12;
251 continue;
252 }
253
254 // At this point, three scenarios are possible:
255 // 1) Some bytes of this chunk were already decoded in a previous call.
256 // Continue from there.
257 // 2) This is an fdAT chunk. Convert it to an IDAT chunk to decode.
258 // 3) This is any other chunk, most likely an IDAT chunk.
259 size_t endOffsetChunk = offset + length + 12;
260
261 if (m_progressiveDecodeOffset >= offset + 8) {
262 offset = m_progressiveDecodeOffset;
263 } else if (isChunk(chunk, "fdAT")) {
264 processFdatChunkAsIdat(length);
265 // Skip the sequence number
Noel Gordon 2017/02/21 13:53:57 Sentences end with a period.
scroggo_chromium 2017/02/23 22:09:54 Done.
266 offset += 12;
267 } else {
268 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
269 offset += 8;
270 }
271
272 size_t bytesLeftInChunk = endOffsetChunk - offset;
273 size_t bytesDecoded = processData(reader, offset, bytesLeftInChunk);
274 m_progressiveDecodeOffset = offset + bytesDecoded;
275 if (bytesDecoded < bytesLeftInChunk)
276 return false;
277 offset += bytesDecoded;
278 }
279
280 return false;
281 }
282
283 void PNGImageReader::processFdatChunkAsIdat(png_uint_32 fdatLength) {
284 // An fdAT chunk is build up as follows:
285 // - |length| (4B)
286 // - fdAT tag (4B)
287 // - sequence number (4B)
288 // - frame data (|length| - 4B)
289 // - CRC (4B)
290 // Thus, to reformat this into an IDAT chunk, do the following:
291 // - write |length| - 4 as the new length, since the sequence number
292 // must be removed.
293 // - change the tag to IDAT.
294 // - omit the sequence number from the data part of the chunk.
295 png_byte chunkIDAT[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'};
Noel Gordon 2017/02/21 13:53:57 static png_byte chunkIDAT[] ?
scroggo_chromium 2017/02/23 22:09:54 We could, but then we'd need to copy it, since the
296 png_save_uint_32(chunkIDAT, fdatLength - 4);
297 // The CRC is incorrect when applied to the modified fdAT.
298 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
299 png_process_data(m_png, m_info, chunkIDAT, 8);
300 }
301
302 void PNGImageReader::decodeFrame(const FastSharedBufferReader& reader,
303 size_t index) {
304 size_t offset = m_frameInfo[index].startOffset;
305 size_t endOffset = offset + m_frameInfo[index].byteLength;
306 char readBuffer[8];
307
308 while (offset < endOffset) {
309 const png_byte* chunk = readAsConstPngBytep(reader, offset, 8, readBuffer);
310 const png_uint_32 length = png_get_uint_32(chunk);
311 DCHECK(length <= PNG_UINT_31_MAX);
312
313 if (isChunk(chunk, "fdAT")) {
314 processFdatChunkAsIdat(length);
315 // The frame data and the CRC span |length| bytes, so skip the
316 // sequence number and process |length| bytes to decode the frame.
317 processData(reader, offset + 12, length);
318 } else {
319 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
320 processData(reader, offset + 8, length + 4);
321 }
322 offset += 12 + length;
323 }
324 }
325
326 // Compute the CRC and compare to the stored value
Noel Gordon 2017/02/21 13:53:56 Sentences.
scroggo_chromium 2017/02/23 22:09:54 Done.
327 static bool checkCrc(const FastSharedBufferReader& reader,
328 size_t chunkStart,
329 size_t chunkLength) {
330 const size_t kSizeNeededForfcTL = 26 + 4;
331 char readBuffer[kSizeNeededForfcTL];
332 DCHECK(chunkLength + 4 <= kSizeNeededForfcTL);
333 const png_byte* chunk =
334 readAsConstPngBytep(reader, chunkStart + 4, chunkLength + 4, readBuffer);
335
336 char crcBuffer[4];
337 const png_byte* crcLoc =
338 readAsConstPngBytep(reader, chunkStart + 8 + chunkLength, 4, crcBuffer);
339 const png_uint_32 crc = png_get_uint_32(crcLoc);
340 const png_uint_32 actual = crc32(crc32(0, Z_NULL, 0), chunk, chunkLength + 4);
341 return actual == crc;
342 }
343
344 bool PNGImageReader::checkSequenceNumber(const png_byte* position) {
345 png_uint_32 sequenceNumber = png_get_uint_32(position);
346 if (sequenceNumber != m_nextSequenceNumber ||
347 sequenceNumber > PNG_UINT_31_MAX)
348 return false;
349
350 m_nextSequenceNumber++;
Noel Gordon 2017/02/21 13:53:56 ++m_nextSequenceNumber;
scroggo_chromium 2017/02/23 22:09:54 Done.
351 return true;
352 }
353
354 // Return false if there was a fatal error; true otherwise.
355 bool PNGImageReader::parse(SegmentReader& data, PNGParseQuery query) {
Noel Gordon 2017/02/21 13:53:57 bool PNGImageReader::parse(SegmentReader& data, Pa
Noel Gordon 2017/02/21 13:53:57 bool PNGImageReader::parse(SegmentReader& data, Pa
scroggo_chromium 2017/02/23 22:09:55 Done.
356 if (m_parseCompleted)
357 return true;
358
359 const FastSharedBufferReader reader(&data);
360
361 if (!parseSize(reader))
362 return false;
363
364 if (query == PNGParseQuery::PNGSizeQuery ||
Noel Gordon 2017/02/21 13:53:57 if (query == ParseQuery::Size ...
scroggo_chromium 2017/02/23 22:09:55 Done.
365 !m_decoder->isDecodedSizeAvailable())
366 return true;
367
368 // For non animated images (identified by no acTL chunk before the IDAT),
369 // there is no need to continue parsing.
370 if (!m_isAnimated) {
371 DCHECK(m_frameInfo.isEmpty());
372 FrameInfo frame;
373 frame.startOffset = m_readOffset;
374 frame.duration = 0;
375 frame.frameRect = IntRect(IntPoint(), {m_width, m_height});
376 frame.disposalMethod = ImageFrame::DisposalMethod::DisposeKeep;
377 frame.alphaBlend = ImageFrame::AlphaBlendSource::BlendAtopBgcolor;
378 m_frameInfo.append(frame);
Noel Gordon 2017/02/21 13:53:56 append -> push_back
scroggo_chromium 2017/02/23 22:09:55 Done.
379 m_parseCompleted = true;
380 return true;
381 }
382
383 char readBuffer[kBufferSize];
Noel Gordon 2017/02/21 13:53:56 DCHECK_EQ(query, ParseQuery::MetaData); DCHECK(m_i
scroggo_chromium 2017/02/23 22:09:54 Done.
384
385 // At this point, the query is FrameMetaDataQuery and the image is animated.
Noel Gordon 2017/02/21 13:53:56 // Loop over the data and manually register all fr
scroggo_chromium 2017/02/23 22:09:54 Done.
386 // Loop over the data and manually register all frames. Nothing is passed to
387 // libpng for processing. A frame is registered on the next fcTL chunk or
388 // when the IEND chunk is found. This ensures that only complete frames are
389 // reported, unless there is an error in the stream.
390 while (reader.size() >= m_readOffset + 8) {
391 const png_byte* chunk =
392 readAsConstPngBytep(reader, m_readOffset, 8, readBuffer);
393 const size_t length = png_get_uint_32(chunk);
394 if (length > PNG_UINT_31_MAX)
395 return false;
396
397 const bool IDAT = isChunk(chunk, "IDAT");
398 if (IDAT && !m_expectIdats) {
Noel Gordon 2017/02/21 13:53:56 { } not needed.
scroggo_chromium 2017/02/23 22:09:55 Done.
399 return false;
400 }
401
402 const bool fdAT = isChunk(chunk, "fdAT");
403 if (fdAT && m_expectIdats) {
Noel Gordon 2017/02/21 13:53:56 { } not needed.
scroggo_chromium 2017/02/23 22:09:55 Done.
404 return false;
405 }
406
407 if (fdAT || (IDAT && m_idatIsPartOfAnimation)) {
408 m_fctlNeedsDatChunk = false;
409 if (m_newFrame.startOffset == 0) {
410 // Beginning of a new frame's data.
411 m_newFrame.startOffset = m_readOffset;
412
413 if (m_frameInfo.isEmpty()) {
414 // This is the first frame. Report it immediately so it can be
415 // decoded progressively.
416 m_newFrame.byteLength = kFirstFrameIndicator;
417 m_frameInfo.append(m_newFrame);
Noel Gordon 2017/02/21 13:53:56 append -> push_back.
scroggo_chromium 2017/02/23 22:09:55 Done.
418 }
419 }
420
421 if (fdAT) {
422 if (reader.size() < m_readOffset + 8 + 4) {
Noel Gordon 2017/02/21 13:53:56 { } not needed. if (reader.size() < m_readOffset
scroggo_chromium 2017/02/23 22:09:55 Done.
423 return true;
424 }
425 const png_byte* sequenceNumPosition =
426 readAsConstPngBytep(reader, m_readOffset + 8, 4, readBuffer);
427 if (!checkSequenceNumber(sequenceNumPosition))
428 return false;
429 }
430
431 } else if (isChunk(chunk, "fcTL") || isChunk(chunk, "IEND")) {
432 // This marks the end of the previous frame.
433 if (m_newFrame.startOffset != 0) {
434 m_newFrame.byteLength = m_readOffset - m_newFrame.startOffset;
435 if (m_frameInfo[0].byteLength == kFirstFrameIndicator) {
436 m_frameInfo[0].byteLength = m_newFrame.byteLength;
437 } else {
438 m_frameInfo.append(m_newFrame);
Noel Gordon 2017/02/21 13:53:56 append -> push_back
scroggo_chromium 2017/02/23 22:09:54 Done.
439 if (isChunk(chunk, "fcTL")) {
440 if (m_frameInfo.size() >= m_reportedFrameCount)
441 return false;
442 } else {
443 // IEND
444 if (m_frameInfo.size() != m_reportedFrameCount)
445 return false;
446 }
447 }
448
449 m_newFrame.startOffset = 0;
450 }
451
452 if (reader.size() < m_readOffset + 12 + length)
453 return true;
454
455 if (isChunk(chunk, "IEND")) {
456 m_parseCompleted = true;
457 return true;
458 }
459
460 if (length != 26 || !checkCrc(reader, m_readOffset, length))
461 return false;
462
463 chunk = readAsConstPngBytep(reader, m_readOffset + 8, length, readBuffer);
464 if (!parseFrameInfo(chunk))
465 return false;
466
467 m_expectIdats = false;
468 } else if (isChunk(chunk, "acTL")) {
469 // There should only be one acTL chunk, and it should be before the
470 // IDAT chunk.
471 return false;
472 }
473 m_readOffset += 12 + length;
474 }
475 return true;
476 }
477
478 // If |length| == 0, read until the stream ends.
479 // @return: number of bytes processed.
Noel Gordon 2017/02/21 13:53:57 @?
scroggo_chromium 2017/02/23 22:09:55 Done.
480 size_t PNGImageReader::processData(const FastSharedBufferReader& reader,
481 size_t offset,
482 size_t length) {
101 const char* segment; 483 const char* segment;
102 while (size_t segmentLength = data.getSomeData(segment, m_readOffset)) { 484 size_t totalProcessedBytes = 0;
103 m_readOffset += segmentLength; 485 while (reader.size() > offset) {
104 m_currentBufferSize = m_readOffset; 486 size_t segmentLength = reader.getSomeData(segment, offset);
487 if (length > 0 && segmentLength + totalProcessedBytes > length)
488 segmentLength = length - totalProcessedBytes;
489
105 png_process_data(m_png, m_info, 490 png_process_data(m_png, m_info,
106 reinterpret_cast<png_bytep>(const_cast<char*>(segment)), 491 reinterpret_cast<png_byte*>(const_cast<char*>(segment)),
107 segmentLength); 492 segmentLength);
108 if (sizeOnly ? m_decoder->isDecodedSizeAvailable() 493 offset += segmentLength;
109 : m_decoder->frameIsCompleteAtIndex(0)) 494 totalProcessedBytes += segmentLength;
495 if (totalProcessedBytes == length)
496 return length;
497 }
498 return totalProcessedBytes;
499 }
500
501 // Process up to the start of the IDAT with libpng.
502 // Return false for a fatal error. True otherwise.
503 bool PNGImageReader::parseSize(const FastSharedBufferReader& reader) {
504 if (m_decoder->isDecodedSizeAvailable())
505 return true;
506
507 char readBuffer[kBufferSize];
508
509 if (setjmp(JMPBUF(m_png))) {
Noel Gordon 2017/02/21 13:53:56 if (setjmp(JMPBUF(m_png))) return false;
scroggo_chromium 2017/02/23 22:09:55 Done.
510 return false;
511 }
512
513 if (!m_parsedSignature) {
514 if (reader.size() < m_readOffset + 8)
110 return true; 515 return true;
111 } 516
112 517 const png_byte* chunk =
113 return false; 518 readAsConstPngBytep(reader, m_readOffset, 8, readBuffer);
114 } 519 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
115 520 m_readOffset += 8;
116 } // namespace blink 521 m_parsedSignature = true;
522 // Initialize the newFrame by setting the readOffset to 0.
523 m_newFrame.startOffset = 0;
524 }
525
526 // This loop peeks at the chunk tag until the IDAT chunk is found. APNG
527 // chunks are handled manually. Other tags are passed on to libpng for general
528 // parsing. Peek at chunks by looking at the first 8 bytes, which contain the
529 // length and the chunk tag.
530 for (png_uint_32 length = 0; reader.size() >= m_readOffset + 8;
531 m_readOffset += length + 12) {
532 const png_byte* chunk =
533 readAsConstPngBytep(reader, m_readOffset, 8, readBuffer);
534 length = png_get_uint_32(chunk);
535
536 // If we encounter the IDAT chunk, we're done with the png header
537 if (isChunk(chunk, "IDAT")) {
538 // Done with header chunks.
539 m_idatOffset = m_readOffset;
540 m_fctlNeedsDatChunk = false;
541 if (m_ignoreAnimation)
542 m_isAnimated = false;
543 if (!m_isAnimated || 1 == m_reportedFrameCount)
544 m_decoder->setRepetitionCount(cAnimationNone);
545 // Call headerAvailable manually. IDAT will be supplied (possibly
546 // modified) when decoding the frame.
547 m_decoder->headerAvailable();
548 return true;
549 }
550
551 // 12 is the length, tag and crc part of the chunk, which are all 4B.
552 if (reader.size() < m_readOffset + length + 12)
553 break;
554
555 if (isChunk(chunk, "acTL")) {
556 if (m_ignoreAnimation)
557 continue;
558 if (m_isAnimated || length != 8 || !m_parsedIHDR ||
559 !checkCrc(reader, m_readOffset, 8)) {
560 m_ignoreAnimation = true;
561 continue;
562 }
563 chunk = readAsConstPngBytep(reader, m_readOffset + 8, length, readBuffer);
564 m_reportedFrameCount = png_get_uint_32(chunk);
565 if (!m_reportedFrameCount || m_reportedFrameCount > PNG_UINT_31_MAX) {
566 m_ignoreAnimation = true;
567 continue;
568 }
569 png_uint_32 repetitionCount = png_get_uint_32(chunk + 4);
570 if (repetitionCount > PNG_UINT_31_MAX) {
571 m_ignoreAnimation = true;
572 continue;
573 }
574 m_isAnimated = true;
575 m_decoder->setRepetitionCount((int)repetitionCount - 1);
576 } else if (isChunk(chunk, "fcTL")) {
577 if (m_ignoreAnimation)
578 continue;
579 if (length != 26 || !m_parsedIHDR ||
580 !checkCrc(reader, m_readOffset, 26)) {
581 m_ignoreAnimation = true;
582 continue;
583 }
584 chunk = readAsConstPngBytep(reader, m_readOffset + 8, length, readBuffer);
585 if (!parseFrameInfo(chunk) ||
586 m_newFrame.frameRect != IntRect(0, 0, m_width, m_height)) {
587 m_ignoreAnimation = true;
588 continue;
589 }
590 m_idatIsPartOfAnimation = true;
591 } else if (isChunk(chunk, "fdAT")) {
592 m_ignoreAnimation = true;
593 } else {
594 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
595 processData(reader, m_readOffset + 8, length + 4);
596 if (isChunk(chunk, "IHDR")) {
597 m_parsedIHDR = true;
598 m_width = png_get_image_width(m_png, m_info);
599 m_height = png_get_image_height(m_png, m_info);
600 }
601 }
602 }
603
604 // Not enough data to call headerAvailable.
605 return true;
606 }
607
608 bool PNGImageReader::firstFrameFullyReceived() const {
609 DCHECK_GT(m_frameInfo.size(), 0u);
610 return m_frameInfo[0].byteLength != kFirstFrameIndicator;
611 }
612
613 void PNGImageReader::clearDecodeState(size_t frameIndex) {
614 if (frameIndex == 0) {
615 png_destroy_read_struct(m_png ? &m_png : nullptr,
616 m_info ? &m_info : nullptr, 0);
617 DCHECK(!m_png && !m_info);
618 m_progressiveDecodeOffset = 0;
619 }
620 }
621
622 size_t PNGImageReader::frameCount() const {
623 return m_frameInfo.size();
624 }
625
626 const PNGImageReader::FrameInfo& PNGImageReader::frameInfo(size_t index) const {
627 DCHECK(index < m_frameInfo.size());
628 return m_frameInfo[index];
629 }
630
631 // These constants map to the frame control parameters defined in fcTL chunks,
632 // as specified at https://wiki.mozilla.org/APNG_Specification.
633 #define kAPNG_DISPOSE_OP_NONE 0
634 #define kAPNG_DISPOSE_OP_BACKGROUND 1
635 #define kAPNG_DISPOSE_OP_PREVIOUS 2
636
637 #define kAPNG_BLEND_OP_SOURCE 0
638 #define kAPNG_BLEND_OP_OVER 1
639
640 // Extract the frame control info and store it in m_newFrame. The length check
641 // on the data chunk has been done by the calling code.
642 bool PNGImageReader::parseFrameInfo(const png_byte* data) {
643 if (m_fctlNeedsDatChunk)
644 return false;
645
646 if (!checkSequenceNumber(data))
647 return false;
648
649 png_uint_32 frameWidth = png_get_uint_32(data + 4);
650 png_uint_32 frameHeight = png_get_uint_32(data + 8);
651 png_uint_32 xOffset = png_get_uint_32(data + 12);
652 png_uint_32 yOffset = png_get_uint_32(data + 16);
653
654 if (!frameWidth || !frameHeight || xOffset + frameWidth > m_width ||
655 yOffset + frameHeight > m_height)
656 return false;
657
658 png_uint_16 delayNumerator = png_get_uint_16(data + 20);
659 png_uint_16 delayDenominator = png_get_uint_16(data + 22);
660
661 m_newFrame.duration = (delayDenominator == 0)
662 ? delayNumerator * 10
663 : delayNumerator * 1000 / delayDenominator;
664 m_newFrame.frameRect = IntRect(xOffset, yOffset, frameWidth, frameHeight);
665 const uint8_t disposalMethod = data[24];
666 switch (disposalMethod) {
667 case kAPNG_DISPOSE_OP_NONE:
668 m_newFrame.disposalMethod = ImageFrame::DisposalMethod::DisposeKeep;
669 break;
670 case kAPNG_DISPOSE_OP_BACKGROUND:
671 m_newFrame.disposalMethod =
672 ImageFrame::DisposalMethod::DisposeOverwriteBgcolor;
673 break;
674 case kAPNG_DISPOSE_OP_PREVIOUS:
675 m_newFrame.disposalMethod =
676 ImageFrame::DisposalMethod::DisposeOverwritePrevious;
677 break;
678 default:
679 return false;
680 }
681
682 const uint8_t alphaBlend = data[25];
683 switch (alphaBlend) {
684 case kAPNG_BLEND_OP_SOURCE:
685 m_newFrame.alphaBlend = ImageFrame::AlphaBlendSource::BlendAtopBgcolor;
686 break;
687 case kAPNG_BLEND_OP_OVER:
688 m_newFrame.alphaBlend =
689 ImageFrame::AlphaBlendSource::BlendAtopPreviousFrame;
690 break;
691 default:
692 return false;
693 }
694 m_fctlNeedsDatChunk = true;
695 return true;
696 }
697
698 }; // namespace blink
Noel Gordon 2017/02/21 13:53:56 ; not needed.
scroggo_chromium 2017/02/23 22:09:55 Done.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698