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

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

Issue 2618633004: Add support for Animated PNG (Closed)
Patch Set: Use a custom allocator in GIFImageDecoderTest 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 frameSizeMatchesIHDR =
136 m_frameInfo[index].frameRect == IntRect(0, 0, m_width, m_height);
137 if (index)
138 return firstFrameDecodeInProgress || !frameSizeMatchesIHDR;
139 return !firstFrameDecodeInProgress && !frameSizeMatchesIHDR;
140 }
141
142 // Return false on a fatal error.
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 decodeWithNewPNG = shouldDecodeWithNewPNG(index);
161 if (decodeWithNewPNG) {
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 (decodeWithNewPNG)
173 startFrameDecoding(reader, index);
174
175 if (!index && (!firstFrameFullyReceived() || m_progressiveDecodeOffset)) {
176 const bool decodedEntireFrame = progressivelyDecodeFirstFrame(reader);
177 if (!decodedEntireFrame)
178 return true;
179 m_progressiveDecodeOffset = 0;
180 } else {
181 decodeFrame(reader, index);
182 }
183
184 static png_byte IEND[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 174, 66, 96, 130};
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 == IntRect(0, 0, m_width, m_height)) {
198 processData(reader, m_initialOffset, m_idatOffset);
199 return;
200 }
201
202 // Process the IHDR chunk, but change the width and height so it reflects
203 // the frame's width and height. ImageDecoder will apply the x,y offset.
204 constexpr size_t headerSize = kBufferSize;
205 char readBuffer[headerSize];
206 const png_byte* chunk =
207 readAsConstPngBytep(reader, m_initialOffset, headerSize, readBuffer);
208 png_byte* header = reinterpret_cast<png_byte*>(readBuffer);
209 if (chunk != header)
210 memcpy(header, chunk, headerSize);
211 png_save_uint_32(header + 16, frameRect.width());
212 png_save_uint_32(header + 20, frameRect.height());
213 // IHDR has been modified, so tell libpng to ignore CRC errors.
214 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
215 png_process_data(m_png, m_info, header, headerSize);
216
217 // Process the rest of the header chunks.
218 processData(reader, m_initialOffset + headerSize, m_idatOffset - headerSize);
219 }
220
221 // Determine if the bytes 4 to 7 of |chunk| indicate that it is a |tag| chunk.
222 // - The length of |chunk| must be >= 8
223 // - The length of |tag| must be = 4
224 static inline bool isChunk(const png_byte* chunk, const char* tag) {
225 return memcmp(chunk + 4, tag, 4) == 0;
226 }
227
228 bool PNGImageReader::progressivelyDecodeFirstFrame(
229 const FastSharedBufferReader& reader) {
230 size_t offset = m_frameInfo[0].startOffset;
231
232 // Loop while there is enough data to do progressive decoding.
233 while (reader.size() >= offset + 8) {
234 char readBuffer[8];
235 // At the beginning of each loop, the offset is at the start of a chunk.
236 const png_byte* chunk = readAsConstPngBytep(reader, offset, 8, readBuffer);
237 const png_uint_32 length = png_get_uint_32(chunk);
238 DCHECK(length <= PNG_UINT_31_MAX);
239
240 // When an fcTL or IEND chunk is encountered, the frame data has ended.
241 // Return true, since all frame data is decoded.
242 if (isChunk(chunk, "fcTL") || isChunk(chunk, "IEND"))
243 return true;
244
245 // If this chunk was already decoded, move on to the next.
246 if (m_progressiveDecodeOffset >= offset + length + 12) {
247 offset += length + 12;
248 continue;
249 }
250
251 // Three scenarios are possible here:
252 // 1) Some bytes of this chunk were already decoded in a previous call.
253 // Continue from there.
254 // 2) This is an fdAT chunk. Convert it to an IDAT chunk to decode.
255 // 3) This is any other chunk. Pass it to libpng for processing.
256 size_t endOffsetChunk = offset + length + 12;
257
258 if (m_progressiveDecodeOffset >= offset + 8) {
259 offset = m_progressiveDecodeOffset;
260 } else if (isChunk(chunk, "fdAT")) {
261 processFdatChunkAsIdat(length);
262 // Skip the sequence number.
263 offset += 12;
264 } else {
265 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
266 offset += 8;
267 }
268
269 size_t bytesLeftInChunk = endOffsetChunk - offset;
270 size_t bytesDecoded = processData(reader, offset, bytesLeftInChunk);
271 m_progressiveDecodeOffset = offset + bytesDecoded;
272 if (bytesDecoded < bytesLeftInChunk)
273 return false;
274 offset += bytesDecoded;
275 }
276
277 return false;
278 }
279
280 void PNGImageReader::processFdatChunkAsIdat(png_uint_32 fdatLength) {
281 // An fdAT chunk is build up as follows:
282 // - |length| (4B)
283 // - fdAT tag (4B)
284 // - sequence number (4B)
285 // - frame data (|length| - 4B)
286 // - CRC (4B)
287 // Thus, to reformat this into an IDAT chunk, do the following:
288 // - write |length| - 4 as the new length, since the sequence number
289 // must be removed.
290 // - change the tag to IDAT.
291 // - omit the sequence number from the data part of the chunk.
292 png_byte chunkIDAT[] = {0, 0, 0, 0, 'I', 'D', 'A', 'T'};
293 png_save_uint_32(chunkIDAT, fdatLength - 4);
294 // The CRC is incorrect when applied to the modified fdAT.
295 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
296 png_process_data(m_png, m_info, chunkIDAT, 8);
297 }
298
299 void PNGImageReader::decodeFrame(const FastSharedBufferReader& reader,
300 size_t index) {
301 size_t offset = m_frameInfo[index].startOffset;
302 size_t endOffset = offset + m_frameInfo[index].byteLength;
303 char readBuffer[8];
304
305 while (offset < endOffset) {
306 const png_byte* chunk = readAsConstPngBytep(reader, offset, 8, readBuffer);
307 const png_uint_32 length = png_get_uint_32(chunk);
308 DCHECK(length <= PNG_UINT_31_MAX);
309
310 if (isChunk(chunk, "fdAT")) {
311 processFdatChunkAsIdat(length);
312 // The frame data and the CRC span |length| bytes, so skip the
313 // sequence number and process |length| bytes to decode the frame.
314 processData(reader, offset + 12, length);
315 } else {
316 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
317 processData(reader, offset + 8, length + 4);
318 }
319
320 offset += 12 + length;
321 }
322 }
323
324 // Compute the CRC and compare to the stored value.
325 static bool checkCrc(const FastSharedBufferReader& reader,
326 size_t chunkStart,
327 size_t chunkLength) {
328 constexpr size_t kSizeNeededForfcTL = 26 + 4;
329 char readBuffer[kSizeNeededForfcTL];
330 DCHECK(chunkLength + 4 <= kSizeNeededForfcTL);
331 const png_byte* chunk =
332 readAsConstPngBytep(reader, chunkStart + 4, chunkLength + 4, readBuffer);
333
334 char crcBuffer[4];
335 const png_byte* crcPosition =
336 readAsConstPngBytep(reader, chunkStart + 8 + chunkLength, 4, crcBuffer);
337 png_uint_32 crc = png_get_uint_32(crcPosition);
338 return crc == crc32(crc32(0, Z_NULL, 0), chunk, chunkLength + 4);
339 }
340
341 bool PNGImageReader::checkSequenceNumber(const png_byte* position) {
342 png_uint_32 sequence = png_get_uint_32(position);
343 if (sequence != m_nextSequenceNumber || sequence > PNG_UINT_31_MAX)
344 return false;
345
346 ++m_nextSequenceNumber;
347 return true;
348 }
349
350 // Return false if there was a fatal error; true otherwise.
351 bool PNGImageReader::parse(SegmentReader& data, ParseQuery query) {
352 if (m_parseCompleted)
353 return true;
354
355 const FastSharedBufferReader reader(&data);
356
357 if (!parseSize(reader))
358 return false;
359
360 if (!m_decoder->isDecodedSizeAvailable())
361 return true;
362
363 // For non animated images (identified by no acTL chunk before the IDAT),
364 // there is no need to continue parsing.
365 if (!m_isAnimated) {
366 FrameInfo frame;
367 frame.startOffset = m_readOffset;
368 // This should never be read in this case, but initialize just in case.
369 frame.byteLength = kFirstFrameIndicator;
370 frame.duration = 0;
371 frame.frameRect = IntRect(0, 0, m_width, m_height);
372 frame.disposalMethod = ImageFrame::DisposalMethod::DisposeKeep;
373 frame.alphaBlend = ImageFrame::AlphaBlendSource::BlendAtopBgcolor;
374 DCHECK(m_frameInfo.isEmpty());
375 m_frameInfo.push_back(frame);
376 m_parseCompleted = true;
377 return true;
378 }
379
380 if (query == ParseQuery::Size)
381 return true;
382
383 DCHECK_EQ(ParseQuery::MetaData, query);
384 DCHECK(m_isAnimated);
385
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 char readBuffer[kBufferSize];
391 while (reader.size() >= m_readOffset + 8) {
392 const png_byte* chunk =
393 readAsConstPngBytep(reader, m_readOffset, 8, readBuffer);
394 const size_t length = png_get_uint_32(chunk);
395 if (length > PNG_UINT_31_MAX)
396 return false;
397
398 const bool IDAT = isChunk(chunk, "IDAT");
399 if (IDAT && !m_expectIdats)
400 return false;
401
402 const bool fdAT = isChunk(chunk, "fdAT");
403 if (fdAT && m_expectIdats)
404 return false;
405
406 if (fdAT || (IDAT && m_idatIsPartOfAnimation)) {
407 m_fctlNeedsDatChunk = false;
408 if (!m_newFrame.startOffset) {
409 // Beginning of a new frame's data.
410 m_newFrame.startOffset = m_readOffset;
411
412 if (m_frameInfo.isEmpty()) {
413 // This is the first frame. Report it immediately so it can be
414 // decoded progressively.
415 m_newFrame.byteLength = kFirstFrameIndicator;
416 m_frameInfo.push_back(m_newFrame);
417 }
418 }
419
420 if (fdAT) {
421 if (reader.size() < m_readOffset + 8 + 4)
422 return true;
423 const png_byte* sequencePosition =
424 readAsConstPngBytep(reader, m_readOffset + 8, 4, readBuffer);
425 if (!checkSequenceNumber(sequencePosition))
426 return false;
427 }
428
429 } else if (isChunk(chunk, "fcTL") || isChunk(chunk, "IEND")) {
430 // This marks the end of the previous frame.
431 if (m_newFrame.startOffset) {
432 m_newFrame.byteLength = m_readOffset - m_newFrame.startOffset;
433 if (m_frameInfo[0].byteLength == kFirstFrameIndicator) {
434 m_frameInfo[0].byteLength = m_newFrame.byteLength;
435 } else {
436 m_frameInfo.push_back(m_newFrame);
437 if (isChunk(chunk, "fcTL")) {
438 if (m_frameInfo.size() >= m_reportedFrameCount)
439 return false;
440 } else { // IEND
441 if (m_frameInfo.size() != m_reportedFrameCount)
442 return false;
443 }
444 }
445
446 m_newFrame.startOffset = 0;
447 }
448
449 if (reader.size() < m_readOffset + 12 + length)
450 return true;
451
452 if (isChunk(chunk, "IEND")) {
453 m_parseCompleted = true;
454 return true;
455 }
456
457 if (length != 26 || !checkCrc(reader, m_readOffset, length))
458 return false;
459
460 chunk = readAsConstPngBytep(reader, m_readOffset + 8, length, readBuffer);
461 if (!parseFrameInfo(chunk))
462 return false;
463
464 m_expectIdats = false;
465 } else if (isChunk(chunk, "acTL")) {
466 // There should only be one acTL chunk, and it should be before the
467 // IDAT chunk.
468 return false;
469 }
470
471 m_readOffset += 12 + length;
472 }
473 return true;
474 }
475
476 // If |length| == 0, read until the stream ends. Return number of bytes
477 // processed.
478 size_t PNGImageReader::processData(const FastSharedBufferReader& reader,
479 size_t offset,
480 size_t length) {
102 const char* segment; 481 const char* segment;
103 while (size_t segmentLength = data.getSomeData(segment, m_readOffset)) { 482 size_t totalProcessedBytes = 0;
104 m_readOffset += segmentLength; 483 while (reader.size() > offset) {
105 m_currentBufferSize = m_readOffset; 484 size_t segmentLength = reader.getSomeData(segment, offset);
485 if (length > 0 && segmentLength + totalProcessedBytes > length)
486 segmentLength = length - totalProcessedBytes;
487
106 png_process_data(m_png, m_info, 488 png_process_data(m_png, m_info,
107 reinterpret_cast<png_bytep>(const_cast<char*>(segment)), 489 reinterpret_cast<png_byte*>(const_cast<char*>(segment)),
108 segmentLength); 490 segmentLength);
109 if (sizeOnly ? m_decoder->isDecodedSizeAvailable() 491 offset += segmentLength;
110 : m_decoder->frameIsCompleteAtIndex(0)) 492 totalProcessedBytes += segmentLength;
493 if (totalProcessedBytes == length)
494 return length;
495 }
496 return totalProcessedBytes;
497 }
498
499 // Process up to the start of the IDAT with libpng.
500 // Return false for a fatal error. True otherwise.
501 bool PNGImageReader::parseSize(const FastSharedBufferReader& reader) {
502 if (m_decoder->isDecodedSizeAvailable())
503 return true;
504
505 char readBuffer[kBufferSize];
506
507 if (setjmp(JMPBUF(m_png)))
508 return false;
509
510 if (!m_parsedSignature) {
511 if (reader.size() < m_readOffset + 8)
111 return true; 512 return true;
112 } 513
113 514 const png_byte* chunk =
114 return false; 515 readAsConstPngBytep(reader, m_readOffset, 8, readBuffer);
516 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
517 m_readOffset += 8;
518 m_parsedSignature = true;
519 m_newFrame.startOffset = 0;
520 }
521
522 // Process APNG chunks manually, pass other chunks to libpng.
523 for (png_uint_32 length = 0; reader.size() >= m_readOffset + 8;
524 m_readOffset += length + 12) {
525 const png_byte* chunk =
526 readAsConstPngBytep(reader, m_readOffset, 8, readBuffer);
527 length = png_get_uint_32(chunk);
528
529 if (isChunk(chunk, "IDAT")) {
530 // Done with header chunks.
531 m_idatOffset = m_readOffset;
532 m_fctlNeedsDatChunk = false;
533 if (m_ignoreAnimation)
534 m_isAnimated = false;
535 if (!m_isAnimated || 1 == m_reportedFrameCount)
536 m_decoder->setRepetitionCount(cAnimationNone);
537 m_decoder->headerAvailable();
538 return true;
539 }
540
541 // Wait until the entire chunk is available for parsing simplicity.
542 if (reader.size() < m_readOffset + length + 12)
543 break;
544
545 if (isChunk(chunk, "acTL")) {
546 if (m_ignoreAnimation)
547 continue;
548 if (m_isAnimated || length != 8 || !m_parsedIHDR ||
549 !checkCrc(reader, m_readOffset, 8)) {
550 m_ignoreAnimation = true;
551 continue;
552 }
553 chunk = readAsConstPngBytep(reader, m_readOffset + 8, length, readBuffer);
554 m_reportedFrameCount = png_get_uint_32(chunk);
555 if (!m_reportedFrameCount || m_reportedFrameCount > PNG_UINT_31_MAX) {
556 m_ignoreAnimation = true;
557 continue;
558 }
559 png_uint_32 repetitionCount = png_get_uint_32(chunk + 4);
560 if (repetitionCount > PNG_UINT_31_MAX) {
561 m_ignoreAnimation = true;
562 continue;
563 }
564 m_isAnimated = true;
565 m_decoder->setRepetitionCount(static_cast<int>(repetitionCount) - 1);
566 } else if (isChunk(chunk, "fcTL")) {
567 if (m_ignoreAnimation)
568 continue;
569 if (length != 26 || !m_parsedIHDR ||
570 !checkCrc(reader, m_readOffset, 26)) {
571 m_ignoreAnimation = true;
572 continue;
573 }
574 chunk = readAsConstPngBytep(reader, m_readOffset + 8, length, readBuffer);
575 if (!parseFrameInfo(chunk) ||
576 m_newFrame.frameRect != IntRect(0, 0, m_width, m_height)) {
577 m_ignoreAnimation = true;
578 continue;
579 }
580 m_idatIsPartOfAnimation = true;
581 } else if (isChunk(chunk, "fdAT")) {
582 m_ignoreAnimation = true;
583 } else {
584 png_process_data(m_png, m_info, const_cast<png_byte*>(chunk), 8);
585 processData(reader, m_readOffset + 8, length + 4);
586 if (isChunk(chunk, "IHDR")) {
587 m_parsedIHDR = true;
588 m_width = png_get_image_width(m_png, m_info);
589 m_height = png_get_image_height(m_png, m_info);
590 }
591 }
592 }
593
594 // Not enough data to call headerAvailable.
595 return true;
596 }
597
598 void PNGImageReader::clearDecodeState(size_t index) {
599 if (index)
600 return;
601 png_destroy_read_struct(m_png ? &m_png : nullptr,
602 m_info ? &m_info : nullptr, 0);
603 DCHECK(!m_png && !m_info);
604 m_progressiveDecodeOffset = 0;
605 }
606
607 const PNGImageReader::FrameInfo& PNGImageReader::frameInfo(size_t index) const {
608 DCHECK(index < m_frameInfo.size());
609 return m_frameInfo[index];
610 }
611
612 // Extract the fcTL frame control info and store it in m_newFrame. The length
613 // check on the fcTL data has been done by the calling code.
614 bool PNGImageReader::parseFrameInfo(const png_byte* data) {
615 if (m_fctlNeedsDatChunk)
616 return false;
617
618 png_uint_32 frameWidth = png_get_uint_32(data + 4);
619 png_uint_32 frameHeight = png_get_uint_32(data + 8);
620 png_uint_32 xOffset = png_get_uint_32(data + 12);
621 png_uint_32 yOffset = png_get_uint_32(data + 16);
622 png_uint_16 delayNumerator = png_get_uint_16(data + 20);
623 png_uint_16 delayDenominator = png_get_uint_16(data + 22);
624
625 if (!checkSequenceNumber(data))
626 return false;
627 if (!frameWidth || !frameHeight)
628 return false;
629 if (xOffset + frameWidth > m_width || yOffset + frameHeight > m_height)
630 return false;
631
632 m_newFrame.frameRect = IntRect(xOffset, yOffset, frameWidth, frameHeight);
633
634 if (delayDenominator)
635 m_newFrame.duration = delayNumerator * 1000 / delayDenominator;
636 else
637 m_newFrame.duration = delayNumerator * 10;
638
639 enum DisposeOperations : png_byte {
640 kAPNG_DISPOSE_OP_NONE = 0,
641 kAPNG_DISPOSE_OP_BACKGROUND = 1,
642 kAPNG_DISPOSE_OP_PREVIOUS = 2,
643 };
644 const png_byte& disposeOp = data[24];
645 switch (disposeOp) {
646 case kAPNG_DISPOSE_OP_NONE:
647 m_newFrame.disposalMethod = ImageFrame::DisposalMethod::DisposeKeep;
648 break;
649 case kAPNG_DISPOSE_OP_BACKGROUND:
650 m_newFrame.disposalMethod =
651 ImageFrame::DisposalMethod::DisposeOverwriteBgcolor;
652 break;
653 case kAPNG_DISPOSE_OP_PREVIOUS:
654 m_newFrame.disposalMethod =
655 ImageFrame::DisposalMethod::DisposeOverwritePrevious;
656 break;
657 default:
658 return false;
659 }
660
661 enum BlendOperations : png_byte {
662 kAPNG_BLEND_OP_SOURCE = 0,
663 kAPNG_BLEND_OP_OVER = 1,
664 };
665 const png_byte& blendOp = data[25];
666 switch (blendOp) {
667 case kAPNG_BLEND_OP_SOURCE:
668 m_newFrame.alphaBlend = ImageFrame::AlphaBlendSource::BlendAtopBgcolor;
669 break;
670 case kAPNG_BLEND_OP_OVER:
671 m_newFrame.alphaBlend =
672 ImageFrame::AlphaBlendSource::BlendAtopPreviousFrame;
673 break;
674 default:
675 return false;
676 }
677
678 m_fctlNeedsDatChunk = true;
679 return true;
115 } 680 }
116 681
117 } // namespace blink 682 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698