OLD | NEW |
---|---|
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 Loading... | |
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.
| |
OLD | NEW |