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

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

Issue 2618633004: Add support for Animated PNG (Closed)
Patch Set: Respond to comments Created 3 years, 9 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 <memory>
42 #include "platform/image-decoders/FastSharedBufferReader.h"
41 #include "platform/image-decoders/SegmentReader.h" 43 #include "platform/image-decoders/SegmentReader.h"
42 #include "platform/image-decoders/png/PNGImageDecoder.h" 44 #include "platform/image-decoders/png/PNGImageDecoder.h"
43 #include "png.h"
44 #include "wtf/PtrUtil.h" 45 #include "wtf/PtrUtil.h"
45 #include <memory> 46 #include "zlib.h"
46 47
47 namespace { 48 namespace {
48 49
49 inline blink::PNGImageDecoder* imageDecoder(png_structp png) { 50 inline blink::PNGImageDecoder* imageDecoder(png_structp png) {
50 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png)); 51 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png));
51 } 52 }
52 53
53 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) { 54 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) {
54 imageDecoder(png)->headerAvailable(); 55 imageDecoder(png)->headerAvailable();
55 } 56 }
56 57
57 void PNGAPI pngRowAvailable(png_structp png, 58 void PNGAPI pngRowAvailable(png_structp png,
58 png_bytep row, 59 png_bytep row,
59 png_uint_32 rowIndex, 60 png_uint_32 rowIndex,
60 int state) { 61 int state) {
61 imageDecoder(png)->rowAvailable(row, rowIndex, state); 62 imageDecoder(png)->rowAvailable(row, rowIndex, state);
62 } 63 }
63 64
64 void PNGAPI pngComplete(png_structp png, png_infop) { 65 void PNGAPI pngFrameComplete(png_structp png, png_infop) {
65 imageDecoder(png)->complete(); 66 imageDecoder(png)->frameComplete();
66 } 67 }
67 68
68 void PNGAPI pngFailed(png_structp png, png_const_charp) { 69 void PNGAPI pngFailed(png_structp png, png_const_charp) {
69 longjmp(JMPBUF(png), 1); 70 longjmp(JMPBUF(png), 1);
70 } 71 }
71 72
72 } // namespace 73 } // namespace
73 74
74 namespace blink { 75 namespace blink {
75 76
76 PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) 77 PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t initialOffset)
77 : m_decoder(decoder), 78 : m_width(0),
78 m_readOffset(readOffset), 79 m_height(0),
79 m_currentBufferSize(0), 80 m_decoder(decoder),
80 m_decodingSizeOnly(false), 81 m_initialOffset(initialOffset),
81 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) {
82 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);
83 m_info = png_create_info_struct(m_png); 96 m_info = png_create_info_struct(m_png);
84 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, 97 png_set_progressive_read_fn(m_png, m_decoder, nullptr, pngRowAvailable,
85 pngRowAvailable, pngComplete); 98 pngFrameComplete);
86 } 99 }
87 100
88 PNGImageReader::~PNGImageReader() { 101 PNGImageReader::~PNGImageReader() {
89 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);
90 DCHECK(!m_png && !m_info); 103 DCHECK(!m_png && !m_info);
91 104 }
92 m_readOffset = 0; 105
93 } 106 // This method reads from the FastSharedBufferReader, starting at offset,
94 107 // and returns |length| bytes in the form of a pointer to a const png_byte*.
95 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
96 m_decodingSizeOnly = sizeOnly; 109 // png friendly way, and pass it to libpng for decoding.
97 110 //
98 // 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 bool PNGImageReader::shouldDecodeWithNewPNG(size_t index) const {
132 if (!m_png)
133 return true;
134 const bool firstFrameDecodeInProgress = m_progressiveDecodeOffset;
135 const bool matchesIHDR =
Noel Gordon 2017/03/13 16:00:45 nit: frameSizeMatchesIHDR ?
scroggo_chromium 2017/03/13 16:37:09 Done.
136 m_frameInfo[index].frameRect == IntRect(0, 0, m_width, m_height);
137 if (index)
138 return firstFrameDecodeInProgress || !matchesIHDR;
139 return !firstFrameDecodeInProgress && !matchesIHDR;
140 }
141
142 // Return false on a fatal error
Noel Gordon 2017/03/13 16:00:44 nit: sentences end with a "."
scroggo_chromium 2017/03/13 16:37:09 Done.
143 bool PNGImageReader::decode(SegmentReader& data, size_t index) {
144 if (index >= m_frameInfo.size())
145 return true;
146
147 const FastSharedBufferReader reader(&data);
148
149 if (!m_isAnimated) {
150 if (setjmp(JMPBUF(m_png)))
151 return false;
152 DCHECK_EQ(0u, index);
153 m_progressiveDecodeOffset += processData(
154 reader, m_frameInfo[0].startOffset + m_progressiveDecodeOffset, 0);
155 return true;
156 }
157
158 DCHECK(m_isAnimated);
159
160 const bool decodeAsNewPNG = shouldDecodeWithNewPNG(index);
Noel Gordon 2017/03/13 16:00:45 nit: const bool decodeWithNewPNG = ?
scroggo_chromium 2017/03/13 16:37:10 Done.
161 if (decodeAsNewPNG) {
162 clearDecodeState(0);
163 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
164 m_info = png_create_info_struct(m_png);
165 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
166 pngRowAvailable, pngFrameComplete);
167 }
168
99 if (setjmp(JMPBUF(m_png))) 169 if (setjmp(JMPBUF(m_png)))
100 return m_decoder->setFailed(); 170 return false;
101 171
172 if (decodeAsNewPNG)
173 startFrameDecoding(reader, index);
174
175 if (!index && (!firstFrameFullyReceived() || m_progressiveDecodeOffset)) {
176 const bool decodedFrameCompletely = progressivelyDecodeFirstFrame(reader);
Noel Gordon 2017/03/13 16:00:45 nit: maybe bool decodedEntireFrame = ?
scroggo_chromium 2017/03/13 16:37:09 Done.
177 if (!decodedFrameCompletely)
178 return true;
179
Noel Gordon 2017/03/13 16:00:45 This empty line could be removed ?
scroggo_chromium 2017/03/13 16:37:09 Done.
180 m_progressiveDecodeOffset = 0;
181 } else {
182 decodeFrame(reader, index);
183 }
184
185 static png_byte IEND[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 174, 66, 96, 130};
186 png_process_data(m_png, m_info, IEND, 12);
187 png_destroy_read_struct(&m_png, &m_info, 0);
188 DCHECK(!m_png && !m_info);
189
190 return true;
191 }
192
193 void PNGImageReader::startFrameDecoding(const FastSharedBufferReader& reader,
194 size_t index) {
195 // If the frame is the size of the whole image, just re-process all header
196 // data up to the first frame.
197 const IntRect& frameRect = m_frameInfo[index].frameRect;
198 if (frameRect == IntRect(0, 0, m_width, m_height)) {
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 constexpr size_t headerSize = kBufferSize;
206 char readBuffer[kBufferSize];
Noel Gordon 2017/03/13 16:00:45 nit: kBufferSize -> headerSize.
scroggo_chromium 2017/03/13 16:37:09 Done.
207 const png_byte* chunk =
208 readAsConstPngBytep(reader, m_initialOffset, headerSize, readBuffer);
209 png_byte* header = reinterpret_cast<png_byte*>(readBuffer);
210 if (chunk != header)
211 memcpy(header, chunk, headerSize);
212 png_save_uint_32(header + 16, frameRect.width());
213 png_save_uint_32(header + 20, frameRect.height());
214 // IHDR has been modified, so tell libpng to ignore CRC errors.
215 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
216 png_process_data(m_png, m_info, header, headerSize);
217
218 // Process the rest of the header chunks.
219 processData(reader, m_initialOffset + headerSize, m_idatOffset - headerSize);
220 }
221
222 // Determine if the bytes 4 to 7 of |chunk| indicate that it is a |tag| chunk.
223 // - The length of |chunk| must be >= 8
224 // - The length of |tag| must be = 4
225 static inline bool isChunk(const png_byte* chunk, const char* tag) {
226 return memcmp(chunk + 4, tag, 4) == 0;
227 }
228
229 bool PNGImageReader::progressivelyDecodeFirstFrame(
230 const FastSharedBufferReader& reader) {
231 size_t offset = m_frameInfo[0].startOffset;
232
233 // Loop while there is enough data to do progressive decoding.
234 while (reader.size() >= offset + 8) {
235 char readBuffer[8];
236 // At the beginning of each loop, the offset is at the start of a chunk.
237 const png_byte* chunk = readAsConstPngBytep(reader, offset, 8, readBuffer);
238 const png_uint_32 length = png_get_uint_32(chunk);
239 DCHECK(length <= PNG_UINT_31_MAX);
240
241 // When an fcTL or IEND chunk is encountered, the frame data has ended.
242 // Return true, since all frame data is decoded.
243 if (isChunk(chunk, "fcTL") || isChunk(chunk, "IEND"))
244 return true;
245
246 // If this chunk was already decoded, move on to the next.
247 if (m_progressiveDecodeOffset >= offset + length + 12) {
248 offset += length + 12;
249 continue;
250 }
251
252 // At this point, three scenarios are possible:
Noel Gordon 2017/03/13 16:00:45 // Three scenarios are possible here:
scroggo_chromium 2017/03/13 16:37:09 Done.
253 // 1) Some bytes of this chunk were already decoded in a previous call.
254 // Continue from there.
255 // 2) This is an fdAT chunk. Convert it to an IDAT chunk to decode.
256 // 3) This is any other chunk, most likely an IDAT chunk.
Noel Gordon 2017/03/13 16:00:44 How about // 3) This is any other chunk. Send it
scroggo_chromium 2017/03/13 16:37:10 Done.
257 size_t endOffsetChunk = offset + length + 12;
258
259 if (m_progressiveDecodeOffset >= offset + 8) {
260 offset = m_progressiveDecodeOffset;
261 } else if (isChunk(chunk, "fdAT")) {
262 processFdatChunkAsIdat(length);
263 // Skip the sequence number.
264 offset += 12;
265 } else {
266 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
267 offset += 8;
268 }
269
270 size_t bytesLeftInChunk = endOffsetChunk - offset;
271 size_t bytesDecoded = processData(reader, offset, bytesLeftInChunk);
272 m_progressiveDecodeOffset = offset + bytesDecoded;
273 if (bytesDecoded < bytesLeftInChunk)
274 return false;
275 offset += bytesDecoded;
276 }
277
278 return false;
279 }
280
281 void PNGImageReader::processFdatChunkAsIdat(png_uint_32 fdatLength) {
282 // An fdAT chunk is build up as follows:
283 // - |length| (4B)
284 // - fdAT tag (4B)
285 // - sequence number (4B)
286 // - frame data (|length| - 4B)
287 // - CRC (4B)
288 // Thus, to reformat this into an IDAT chunk, do the following:
289 // - write |length| - 4 as the new length, since the sequence number
290 // must be removed.
291 // - change the tag to IDAT.
292 // - omit the sequence number from the data part of the chunk.
293 png_byte chunkIDAT[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'};
294 png_save_uint_32(chunkIDAT, fdatLength - 4);
295 // The CRC is incorrect when applied to the modified fdAT.
296 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
297 png_process_data(m_png, m_info, chunkIDAT, 8);
298 }
299
300 void PNGImageReader::decodeFrame(const FastSharedBufferReader& reader,
301 size_t index) {
302 size_t offset = m_frameInfo[index].startOffset;
303 size_t endOffset = offset + m_frameInfo[index].byteLength;
304 char readBuffer[8];
305
306 while (offset < endOffset) {
307 const png_byte* chunk = readAsConstPngBytep(reader, offset, 8, readBuffer);
308 const png_uint_32 length = png_get_uint_32(chunk);
309 DCHECK(length <= PNG_UINT_31_MAX);
310
311 if (isChunk(chunk, "fdAT")) {
312 processFdatChunkAsIdat(length);
313 // The frame data and the CRC span |length| bytes, so skip the
314 // sequence number and process |length| bytes to decode the frame.
315 processData(reader, offset + 12, length);
316 } else {
317 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
318 processData(reader, offset + 8, length + 4);
319 }
320
321 offset += 12 + length;
322 }
323 }
324
325 // Compute the CRC and compare to the stored value.
326 static bool checkCrc(const FastSharedBufferReader& reader,
327 size_t chunkStart,
328 size_t chunkLength) {
329 const size_t kSizeNeededForfcTL = 26 + 4;
Noel Gordon 2017/03/13 16:00:44 nit: would "constexpr size_t ..." work here ?
scroggo_chromium 2017/03/13 16:37:09 Done.
330 char readBuffer[kSizeNeededForfcTL];
331 DCHECK(chunkLength + 4 <= kSizeNeededForfcTL);
332 const png_byte* chunk =
333 readAsConstPngBytep(reader, chunkStart + 4, chunkLength + 4, readBuffer);
334
335 char crcBuffer[4];
336 const png_byte* crcPosition =
337 readAsConstPngBytep(reader, chunkStart + 8 + chunkLength, 4, crcBuffer);
338 png_uint_32 crc = png_get_uint_32(crcPosition);
339 return crc == crc32(crc32(0, Z_NULL, 0), chunk, chunkLength + 4);
340 }
341
342 bool PNGImageReader::checkSequenceNumber(const png_byte* position) {
343 png_uint_32 sequence = png_get_uint_32(position);
344 if (sequence != m_nextSequenceNumber || sequence > PNG_UINT_31_MAX)
345 return false;
346
347 ++m_nextSequenceNumber;
348 return true;
349 }
350
351 // Return false if there was a fatal error; true otherwise.
352 bool PNGImageReader::parse(SegmentReader& data, ParseQuery query) {
353 if (m_parseCompleted)
354 return true;
355
356 const FastSharedBufferReader reader(&data);
357
358 if (!parseSize(reader))
359 return false;
360
361 if (!m_decoder->isDecodedSizeAvailable())
362 return true;
363
364 // For non animated images (identified by no acTL chunk before the IDAT),
365 // there is no need to continue parsing.
366 if (!m_isAnimated) {
367 FrameInfo frame;
368 frame.startOffset = m_readOffset;
369 // This should never be read in this case, but initialize just in case.
370 frame.byteLength = kFirstFrameIndicator;
371 frame.duration = 0;
372 frame.frameRect = IntRect(0, 0, m_width, m_height);
373 frame.disposalMethod = ImageFrame::DisposalMethod::DisposeKeep;
374 frame.alphaBlend = ImageFrame::AlphaBlendSource::BlendAtopBgcolor;
375 DCHECK(m_frameInfo.isEmpty());
376 m_frameInfo.push_back(frame);
377 m_parseCompleted = true;
378 return true;
379 }
380
381 if (query == ParseQuery::Size)
382 return true;
383
384 DCHECK_EQ(ParseQuery::MetaData, query);
385 DCHECK(m_isAnimated);
386
387 // Loop over the data and manually register all frames. Nothing is passed to
388 // libpng for processing. A frame is registered on the next fcTL chunk or
389 // when the IEND chunk is found. This ensures that only complete frames are
390 // reported, unless there is an error in the stream.
391 char readBuffer[kBufferSize];
392 while (reader.size() >= m_readOffset + 8) {
393 const png_byte* chunk =
394 readAsConstPngBytep(reader, m_readOffset, 8, readBuffer);
395 const size_t length = png_get_uint_32(chunk);
396 if (length > PNG_UINT_31_MAX)
397 return false;
398
399 const bool IDAT = isChunk(chunk, "IDAT");
400 if (IDAT && !m_expectIdats)
401 return false;
402
403 const bool fdAT = isChunk(chunk, "fdAT");
404 if (fdAT && m_expectIdats)
405 return false;
406
407 if (fdAT || (IDAT && m_idatIsPartOfAnimation)) {
408 m_fctlNeedsDatChunk = false;
409 if (!m_newFrame.startOffset) {
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.push_back(m_newFrame);
418 }
419 }
420
421 if (fdAT) {
422 if (reader.size() < m_readOffset + 8 + 4)
423 return true;
424 const png_byte* sequencePosition =
425 readAsConstPngBytep(reader, m_readOffset + 8, 4, readBuffer);
426 if (!checkSequenceNumber(sequencePosition))
427 return false;
428 }
429
430 } else if (isChunk(chunk, "fcTL") || isChunk(chunk, "IEND")) {
431 // This marks the end of the previous frame.
432 if (m_newFrame.startOffset) {
433 m_newFrame.byteLength = m_readOffset - m_newFrame.startOffset;
434 if (m_frameInfo[0].byteLength == kFirstFrameIndicator) {
435 m_frameInfo[0].byteLength = m_newFrame.byteLength;
436 } else {
437 m_frameInfo.push_back(m_newFrame);
438 if (isChunk(chunk, "fcTL")) {
439 if (m_frameInfo.size() >= m_reportedFrameCount)
440 return false;
441 } else {
442 // IEND
Noel Gordon 2017/03/13 16:00:45 Perhaps } else { // IEND ... here.
scroggo_chromium 2017/03/13 16:37:09 Done.
443 if (m_frameInfo.size() != m_reportedFrameCount)
444 return false;
445 }
446 }
447
448 m_newFrame.startOffset = 0;
449 }
450
451 if (reader.size() < m_readOffset + 12 + length)
452 return true;
453
454 if (isChunk(chunk, "IEND")) {
455 m_parseCompleted = true;
456 return true;
457 }
458
459 if (length != 26 || !checkCrc(reader, m_readOffset, length))
460 return false;
461
462 chunk = readAsConstPngBytep(reader, m_readOffset + 8, length, readBuffer);
463 if (!parseFrameInfo(chunk))
464 return false;
465
466 m_expectIdats = false;
467 } else if (isChunk(chunk, "acTL")) {
468 // There should only be one acTL chunk, and it should be before the
469 // IDAT chunk.
470 return false;
471 }
472 m_readOffset += 12 + length;
Noel Gordon 2017/03/13 16:00:45 loop condition: space before this line.
scroggo_chromium 2017/03/13 16:37:09 Done.
473 }
474 return true;
475 }
476
477 // If |length| == 0, read until the stream ends. Return number of bytes
478 // processed.
479 size_t PNGImageReader::processData(const FastSharedBufferReader& reader,
480 size_t offset,
481 size_t length) {
102 const char* segment; 482 const char* segment;
103 while (size_t segmentLength = data.getSomeData(segment, m_readOffset)) { 483 size_t totalProcessedBytes = 0;
104 m_readOffset += segmentLength; 484 while (reader.size() > offset) {
105 m_currentBufferSize = m_readOffset; 485 size_t segmentLength = reader.getSomeData(segment, offset);
486 if (length > 0 && segmentLength + totalProcessedBytes > length)
487 segmentLength = length - totalProcessedBytes;
488
106 png_process_data(m_png, m_info, 489 png_process_data(m_png, m_info,
107 reinterpret_cast<png_bytep>(const_cast<char*>(segment)), 490 reinterpret_cast<png_byte*>(const_cast<char*>(segment)),
108 segmentLength); 491 segmentLength);
109 if (sizeOnly ? m_decoder->isDecodedSizeAvailable() 492 offset += segmentLength;
110 : m_decoder->frameIsCompleteAtIndex(0)) 493 totalProcessedBytes += segmentLength;
494 if (totalProcessedBytes == length)
495 return length;
496 }
497 return totalProcessedBytes;
498 }
499
500 // Process up to the start of the IDAT with libpng.
501 // Return false for a fatal error. True otherwise.
502 bool PNGImageReader::parseSize(const FastSharedBufferReader& reader) {
503 if (m_decoder->isDecodedSizeAvailable())
504 return true;
505
506 char readBuffer[kBufferSize];
507
508 if (setjmp(JMPBUF(m_png)))
509 return false;
510
511 if (!m_parsedSignature) {
512 if (reader.size() < m_readOffset + 8)
111 return true; 513 return true;
112 } 514
113 515 const png_byte* chunk =
114 return false; 516 readAsConstPngBytep(reader, m_readOffset, 8, readBuffer);
517 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
518 m_readOffset += 8;
519 m_parsedSignature = true;
520 m_newFrame.startOffset = 0;
521 }
522
523 // Process APNG chunks manually, and pass other chunks to libpng.
Noel Gordon 2017/03/13 16:00:45 nit ", and pass" -> ", pass"
scroggo_chromium 2017/03/13 16:37:09 Done.
524 for (png_uint_32 length = 0; reader.size() >= m_readOffset + 8;
525 m_readOffset += length + 12) {
526 const png_byte* chunk =
527 readAsConstPngBytep(reader, m_readOffset, 8, readBuffer);
528 length = png_get_uint_32(chunk);
529
530 if (isChunk(chunk, "IDAT")) {
531 // Done with header chunks.
532 m_idatOffset = m_readOffset;
533 m_fctlNeedsDatChunk = false;
534 if (m_ignoreAnimation)
535 m_isAnimated = false;
536 if (!m_isAnimated || 1 == m_reportedFrameCount)
537 m_decoder->setRepetitionCount(cAnimationNone);
538 m_decoder->headerAvailable();
539 return true;
540 }
541
542 // Wait until the entire chunk is available for parsing simplicity.
543 if (reader.size() < m_readOffset + length + 12)
544 break;
545
546 if (isChunk(chunk, "acTL")) {
547 if (m_ignoreAnimation)
548 continue;
549 if (m_isAnimated || length != 8 || !m_parsedIHDR ||
550 !checkCrc(reader, m_readOffset, 8)) {
551 m_ignoreAnimation = true;
552 continue;
553 }
554 chunk = readAsConstPngBytep(reader, m_readOffset + 8, length, readBuffer);
555 m_reportedFrameCount = png_get_uint_32(chunk);
556 if (!m_reportedFrameCount || m_reportedFrameCount > PNG_UINT_31_MAX) {
557 m_ignoreAnimation = true;
558 continue;
559 }
560 png_uint_32 repetitionCount = png_get_uint_32(chunk + 4);
561 if (repetitionCount > PNG_UINT_31_MAX) {
562 m_ignoreAnimation = true;
563 continue;
564 }
565 m_isAnimated = true;
566 m_decoder->setRepetitionCount(static_cast<int>(repetitionCount) - 1);
567 } else if (isChunk(chunk, "fcTL")) {
568 if (m_ignoreAnimation)
569 continue;
570 if (length != 26 || !m_parsedIHDR ||
571 !checkCrc(reader, m_readOffset, 26)) {
572 m_ignoreAnimation = true;
573 continue;
574 }
575 chunk = readAsConstPngBytep(reader, m_readOffset + 8, length, readBuffer);
576 if (!parseFrameInfo(chunk) ||
577 m_newFrame.frameRect != IntRect(0, 0, m_width, m_height)) {
578 m_ignoreAnimation = true;
579 continue;
580 }
581 m_idatIsPartOfAnimation = true;
582 } else if (isChunk(chunk, "fdAT")) {
583 m_ignoreAnimation = true;
584 } else {
585 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
586 processData(reader, m_readOffset + 8, length + 4);
587 if (isChunk(chunk, "IHDR")) {
588 m_parsedIHDR = true;
589 m_width = png_get_image_width(m_png, m_info);
590 m_height = png_get_image_height(m_png, m_info);
591 }
592 }
593 }
594
595 // Not enough data to call headerAvailable.
596 return true;
597 }
598
599 void PNGImageReader::clearDecodeState(size_t index) {
600 if (index)
601 return;
602 png_destroy_read_struct(m_png ? &m_png : nullptr,
603 m_info ? &m_info : nullptr, 0);
604 DCHECK(!m_png && !m_info);
605 m_progressiveDecodeOffset = 0;
606 }
607
608 const PNGImageReader::FrameInfo& PNGImageReader::frameInfo(size_t index) const {
609 DCHECK(index < m_frameInfo.size());
610 return m_frameInfo[index];
611 }
612
613 // These constants map to the frame control parameters defined in fcTL chunks,
614 // as specified at https://wiki.mozilla.org/APNG_Specification.
615 #define kAPNG_DISPOSE_OP_NONE 0
616 #define kAPNG_DISPOSE_OP_BACKGROUND 1
617 #define kAPNG_DISPOSE_OP_PREVIOUS 2
618
619 #define kAPNG_BLEND_OP_SOURCE 0
Noel Gordon 2017/03/13 16:00:45 We could drop the comment re: the spec here and ma
scroggo_chromium 2017/03/13 16:37:09 Done.
620 #define kAPNG_BLEND_OP_OVER 1
621
622 // Extract the frame control info and store it in m_newFrame. The length check
Noel Gordon 2017/03/13 16:00:45 "the frame control ..." -> "the fcTL frame control
scroggo_chromium 2017/03/13 16:37:09 Done.
623 // on the data chunk has been done by the calling code.
Noel Gordon 2017/03/13 16:00:45 "data chunk" -> "fcTL data"
scroggo_chromium 2017/03/13 16:37:09 Done.
624 bool PNGImageReader::parseFrameInfo(const png_byte* data) {
625 if (m_fctlNeedsDatChunk)
626 return false;
627
628 png_uint_32 frameWidth = png_get_uint_32(data + 4);
629 png_uint_32 frameHeight = png_get_uint_32(data + 8);
630 png_uint_32 xOffset = png_get_uint_32(data + 12);
631 png_uint_32 yOffset = png_get_uint_32(data + 16);
632 png_uint_16 delayNumerator = png_get_uint_16(data + 20);
633 png_uint_16 delayDenominator = png_get_uint_16(data + 22);
634
635 if (!checkSequenceNumber(data))
636 return false;
637 if (!frameWidth || !frameHeight)
638 return false;
639 if (xOffset + frameWidth > m_width || yOffset + frameHeight > m_height)
640 return false;
641
642 m_newFrame.frameRect = IntRect(xOffset, yOffset, frameWidth, frameHeight);
643
644 if (delayDenominator)
645 m_newFrame.duration = delayNumerator * 1000 / delayDenominator;
646 else
647 m_newFrame.duration = delayNumerator * 10;
648
649 const png_byte& disposalMethod = data[24];
650 switch (disposalMethod) {
651 case kAPNG_DISPOSE_OP_NONE:
652 m_newFrame.disposalMethod = ImageFrame::DisposalMethod::DisposeKeep;
653 break;
654 case kAPNG_DISPOSE_OP_BACKGROUND:
655 m_newFrame.disposalMethod =
656 ImageFrame::DisposalMethod::DisposeOverwriteBgcolor;
657 break;
658 case kAPNG_DISPOSE_OP_PREVIOUS:
659 m_newFrame.disposalMethod =
660 ImageFrame::DisposalMethod::DisposeOverwritePrevious;
661 break;
662 default:
663 return false;
664 }
665
666 const png_byte& alphaBlend = data[25];
667 switch (alphaBlend) {
668 case kAPNG_BLEND_OP_SOURCE:
669 m_newFrame.alphaBlend = ImageFrame::AlphaBlendSource::BlendAtopBgcolor;
670 break;
671 case kAPNG_BLEND_OP_OVER:
672 m_newFrame.alphaBlend =
673 ImageFrame::AlphaBlendSource::BlendAtopPreviousFrame;
674 break;
675 default:
676 return false;
677 }
678
679 m_fctlNeedsDatChunk = true;
680 return true;
115 } 681 }
116 682
117 } // namespace blink 683 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698