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

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

Issue 1567053002: Animated PNG implementation Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Moved some code to PNGImageReader.cpp and PNGImageReader.h Created 4 years, 1 month 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
« no previous file with comments | « third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/RuntimeEnabledFeatures.h"
42 #include "platform/image-decoders/FastSharedBufferReader.h"
43
41 #include "platform/image-decoders/SegmentReader.h" 44 #include "platform/image-decoders/SegmentReader.h"
42 #include "png.h" 45 #include "png.h"
43 #include "wtf/PtrUtil.h" 46 #include "wtf/PtrUtil.h"
47 #include "zlib.h"
44 #include <memory> 48 #include <memory>
45 49
50 #define get16(p) ((p)[0] << 8 | (p)[1])
51 #define get32(p) ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
52
46 namespace { 53 namespace {
47 54
48 inline blink::PNGImageDecoder* imageDecoder(png_structp png) { 55 inline blink::PNGImageDecoder* imageDecoder(png_structp png) {
49 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png)); 56 return static_cast<blink::PNGImageDecoder*>(png_get_progressive_ptr(png));
50 } 57 }
51 58
52 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) { 59 void PNGAPI pngHeaderAvailable(png_structp png, png_infop) {
53 imageDecoder(png)->headerAvailable(); 60 imageDecoder(png)->headerAvailable();
54 } 61 }
55 62
(...skipping 12 matching lines...) Expand all
68 longjmp(JMPBUF(png), 1); 75 longjmp(JMPBUF(png), 1);
69 } 76 }
70 77
71 } // namespace 78 } // namespace
72 79
73 namespace blink { 80 namespace blink {
74 81
75 PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t readOffset) 82 PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t readOffset)
76 : m_decoder(decoder), 83 : m_decoder(decoder),
77 m_readOffset(readOffset), 84 m_readOffset(readOffset),
78 m_currentBufferSize(0), 85 m_parseOffset(readOffset),
79 m_decodingSizeOnly(false), 86 m_decodeOffset(readOffset),
87 m_infoSize(0),
88 m_isAnimated(false),
89 m_isParsed(false),
90 m_width(0),
91 m_height(0),
92 m_repetitionCount(cAnimationNone),
93 m_posterFrame(0),
94 m_visibleFrames(1),
80 m_hasAlpha(false) { 95 m_hasAlpha(false) {
81 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); 96 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
82 m_info = png_create_info_struct(m_png); 97 m_info = png_create_info_struct(m_png);
83 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, 98 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
84 pngRowAvailable, pngComplete); 99 pngRowAvailable, pngComplete);
100 memset(&m_currentFrame, 0, sizeof(m_currentFrame));
85 } 101 }
86 102
87 PNGImageReader::~PNGImageReader() { 103 PNGImageReader::~PNGImageReader() {
88 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0); 104 png_destroy_read_struct(m_png ? &m_png : 0, m_info ? &m_info : 0, 0);
89 DCHECK(!m_png && !m_info); 105 DCHECK(!m_png && !m_info);
90 106
91 m_readOffset = 0; 107 m_readOffset = 0;
92 } 108 }
93 109
94 bool PNGImageReader::decode(const SegmentReader& data, bool sizeOnly) { 110 // PNG signature and IHDR size
95 m_decodingSizeOnly = sizeOnly; 111 static const size_t headerSize = 33;
96 112 // fcTL size
97 // We need to do the setjmp here. Otherwise bad things will happen. 113 static const size_t frameControlSize = 34;
114
115 bool PNGImageReader::parse(SegmentReader& data) {
116 const unsigned long maxPNGSize = 1000000UL;
117 size_t inputSize = data.size();
118 const png_byte* chunk;
119 FastSharedBufferReader reader(&data);
120 char readBuffer[frameControlSize];
121
122 if (m_parseOffset == m_readOffset) {
123 if (m_readOffset + headerSize > inputSize)
124 return false;
125
126 chunk = reinterpret_cast<const png_byte*>(reader.getConsecutiveData(
127 m_readOffset + 8, headerSize - 8, readBuffer));
128 const png_byte* chunkId = chunk + 4;
129 png_uint_32 chunkSize = get32(chunk);
130 if (memcmp(chunkId, "IHDR", 4) || chunkSize != 13)
131 return false;
132 m_currentFrame.width = m_width = get32(chunk + 8);
133 m_currentFrame.height = m_height = get32(chunk + 12);
134 if (m_width > maxPNGSize || m_height > maxPNGSize)
135 return false;
136 png_byte bitDepth = chunk[16];
137 if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 &&
138 bitDepth != 16)
139 return false;
140 png_byte colorType = chunk[17];
141 if (colorType == 1 || colorType == 5 || colorType > 6)
142 return false;
143 png_uint_32 chunkCrc = get32(chunk + 21);
144 if (crc32(crc32(0, Z_NULL, 0), chunkId, 17) != chunkCrc)
145 return false;
146 m_infoSize = m_parseOffset = m_readOffset + headerSize;
147 }
148
149 while (!m_isParsed) {
150 if (m_parseOffset + 8 > inputSize)
151 return false;
152
153 chunk = reinterpret_cast<const png_byte*>(
154 reader.getConsecutiveData(m_parseOffset, 8, readBuffer));
155 const png_byte* chunkId = chunk + 4;
156 png_uint_32 chunkSize = get32(chunk);
157
158 if (!memcmp(chunkId, "IDAT", 4) && !m_currentFrame.start) {
159 m_currentFrame.start = m_parseOffset;
160 if (!m_decoder->setSize(m_width, m_height))
161 return false;
162 png_destroy_read_struct(&m_png, &m_info, 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, pngComplete);
167
168 if (setjmp(JMPBUF(m_png)))
169 return false;
170
171 processData(data, 0, m_readOffset, m_parseOffset + 8);
172 }
173
174 size_t chunkEnd = m_parseOffset + chunkSize + 12;
175 if (chunkEnd > inputSize)
176 return false;
177
178 if (!memcmp(chunkId, "IEND", 4) && !chunkSize) {
179 if (!m_currentFrame.start)
180 return false;
181 m_isParsed = true;
182 m_currentFrame.finish = chunkEnd;
183 m_frames.append(m_currentFrame);
184 return true;
185 }
186
187 if (RuntimeEnabledFeatures::animatedPNGEnabled()) {
188 if (!memcmp(chunkId, "fdAT", 4) && m_isAnimated &&
189 !m_currentFrame.start) {
190 m_currentFrame.start = m_parseOffset;
191 } else if (!memcmp(chunkId, "acTL", 4) && chunkSize == 8 &&
192 !m_isAnimated && !m_currentFrame.start) {
193 chunk = reinterpret_cast<const png_byte*>(
194 reader.getConsecutiveData(m_parseOffset, 16, readBuffer));
195 m_isAnimated = true;
196 m_posterFrame = 1;
197 m_visibleFrames = 0;
198 m_repetitionCount = static_cast<int>(get32(chunk + 12)) - 1;
199 } else if (!memcmp(chunkId, "fcTL", 4) && chunkSize == 26 &&
200 (m_isAnimated || !m_currentFrame.start)) {
201 if (m_currentFrame.start) {
202 m_currentFrame.finish = chunkEnd;
203 m_frames.append(m_currentFrame);
204 m_currentFrame.start = 0;
205 } else if (m_frames.isEmpty()) {
206 m_posterFrame = 0;
207 }
208
209 chunk = reinterpret_cast<const png_byte*>(reader.getConsecutiveData(
210 m_parseOffset, frameControlSize, readBuffer));
211 m_currentFrame.width = get32(chunk + 12);
212 m_currentFrame.height = get32(chunk + 16);
213 m_currentFrame.xOffset = get32(chunk + 20);
214 m_currentFrame.yOffset = get32(chunk + 24);
215 unsigned numerator = get16(chunk + 28);
216 unsigned denominator = get16(chunk + 30);
217 m_currentFrame.duration = !denominator
218 ? numerator * 10
219 : (int)(numerator * 1000.0 / denominator);
220 m_currentFrame.dispose = chunk[32];
221 m_currentFrame.blend = chunk[33];
222
223 if (m_currentFrame.width > maxPNGSize ||
224 m_currentFrame.height > maxPNGSize ||
225 m_currentFrame.xOffset > maxPNGSize ||
226 m_currentFrame.yOffset > maxPNGSize ||
227 m_currentFrame.xOffset + m_currentFrame.width > m_width ||
228 m_currentFrame.yOffset + m_currentFrame.height > m_height ||
229 m_currentFrame.dispose > 2 || m_currentFrame.blend > 1)
230 return false;
231
232 if (!m_visibleFrames) {
233 m_currentFrame.blend = 0;
234 if (m_currentFrame.dispose == 2)
235 m_currentFrame.dispose = 1;
236 }
237 m_visibleFrames++;
238 } else if (!m_currentFrame.start && m_frames.isEmpty()) {
239 m_infoSize = chunkEnd;
240 }
241 }
242
243 m_parseOffset = chunkEnd;
244 }
245 return true;
246 }
247
248 bool PNGImageReader::decode(SegmentReader& data, size_t index) {
249 static png_byte dataIEND[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130};
250
251 index += m_posterFrame;
252 if (index && index >= m_frames.size())
253 return true;
254
255 if (index || m_decodeOffset == m_readOffset) {
256 png_destroy_read_struct(&m_png, &m_info, 0);
257 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
258 m_info = png_create_info_struct(m_png);
259 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
260 pngRowAvailable, pngComplete);
261 }
262
98 if (setjmp(JMPBUF(m_png))) 263 if (setjmp(JMPBUF(m_png)))
99 return m_decoder->setFailed(); 264 return false;
100 265
101 const char* segment; 266 if (!index) {
102 while (size_t segmentLength = data.getSomeData(segment, m_readOffset)) { 267 size_t endOffset = !m_frames.isEmpty() ? m_frames[0].finish : 0;
103 m_readOffset += segmentLength; 268 processData(data, index, m_decodeOffset, endOffset);
104 m_currentBufferSize = m_readOffset; 269 if (m_decodeOffset == endOffset)
105 png_process_data(m_png, m_info, 270 png_process_data(m_png, m_info, dataIEND, 12);
106 reinterpret_cast<png_bytep>(const_cast<char*>(segment)), 271 } else {
107 segmentLength); 272 const png_byte* chunk;
108 if (sizeOnly ? m_decoder->isDecodedSizeAvailable() 273 FastSharedBufferReader reader(&data);
109 : m_decoder->frameIsCompleteAtIndex(0)) 274 char readBuffer[headerSize];
275
276 m_decodeOffset = m_readOffset;
277 size_t offset = m_frames[index].start;
278 size_t endOffset = m_frames[index].finish;
279 if (data.size() < endOffset)
110 return true; 280 return true;
111 } 281
112 282 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
113 return false; 283 if (m_frames[index].width != m_width ||
284 m_frames[index].height != m_height) {
285 unsigned char header[headerSize];
286 chunk = reinterpret_cast<const png_byte*>(
287 reader.getConsecutiveData(m_readOffset, headerSize, readBuffer));
288 memcpy(&header[0], chunk, headerSize);
289 png_save_uint_32(&header[16], m_frames[index].width);
290 png_save_uint_32(&header[20], m_frames[index].height);
291 png_process_data(m_png, m_info, header, headerSize);
292 processData(data, index, m_readOffset + headerSize, m_infoSize);
293 } else {
294 processData(data, index, m_readOffset, m_infoSize);
295 }
296
297 png_byte dataIDAT[8] = {0, 0, 0, 0, 73, 68, 65, 84};
298 while (offset < endOffset) {
299 chunk = reinterpret_cast<const png_byte*>(
300 reader.getConsecutiveData(offset, 8, readBuffer));
301 png_uint_32 chunkSize = get32(chunk);
302 if (!memcmp(chunk + 4, "fdAT", 4)) {
303 png_save_uint_32(&dataIDAT[0], chunkSize - 4);
304 png_process_data(m_png, m_info, dataIDAT, 8);
305 processData(data, index, offset + 12, offset + 12 + chunkSize);
306 }
307 offset += chunkSize + 12;
308 }
309 png_process_data(m_png, m_info, dataIEND, 12);
310 }
311 return true;
114 } 312 }
115 313
116 } // namespace blink 314 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698