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

Side by Side Diff: Source/platform/image-decoders/png/PNGImageDecoder.cpp

Issue 1312843006: Animated PNG implementation (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 5 years, 3 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 21 matching lines...) Expand all
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 "config.h" 39 #include "config.h"
40 #include "platform/image-decoders/png/PNGImageDecoder.h" 40 #include "platform/image-decoders/png/PNGImageDecoder.h"
41 41
42 #include "platform/RuntimeEnabledFeatures.h"
42 #include "wtf/PassOwnPtr.h" 43 #include "wtf/PassOwnPtr.h"
43 44
44 #include "png.h" 45 #include "png.h"
45 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR) 46 #if !defined(PNG_LIBPNG_VER_MAJOR) || !defined(PNG_LIBPNG_VER_MINOR)
46 #error version error: compile against a versioned libpng. 47 #error version error: compile against a versioned libpng.
47 #endif 48 #endif
48 #if USE(QCMSLIB) 49 #if USE(QCMSLIB)
49 #include "qcms.h" 50 #include "qcms.h"
50 #endif 51 #endif
51 52
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 } // anonymous 86 } // anonymous
86 87
87 namespace blink { 88 namespace blink {
88 89
89 class PNGImageReader { 90 class PNGImageReader {
90 WTF_MAKE_FAST_ALLOCATED(PNGImageReader); 91 WTF_MAKE_FAST_ALLOCATED(PNGImageReader);
91 public: 92 public:
92 PNGImageReader(PNGImageDecoder* decoder) 93 PNGImageReader(PNGImageDecoder* decoder)
93 : m_decoder(decoder) 94 : m_decoder(decoder)
94 , m_readOffset(0) 95 , m_readOffset(0)
95 , m_currentBufferSize(0) 96 , m_parseOffset(0)
96 , m_decodingSizeOnly(false) 97 , m_infoSize(0)
98 , m_isAnimated(false)
99 , m_isParsed(false)
100 , m_width(0)
101 , m_height(0)
102 , m_repetitionCount(cAnimationNone)
103 , m_posterFrame(0)
104 , m_visibleFrames(1)
97 , m_hasAlpha(false) 105 , m_hasAlpha(false)
98 #if USE(QCMSLIB) 106 #if USE(QCMSLIB)
99 , m_transform(0) 107 , m_transform(0)
100 , m_rowBuffer() 108 , m_rowBuffer()
101 #endif 109 #endif
102 { 110 {
103 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0); 111 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
104 m_info = png_create_info_struct(m_png); 112 m_info = png_create_info_struct(m_png);
105 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, pngRow Available, pngComplete); 113 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, pngRow Available, pngComplete);
114 memset(&m_currentFrame, 0, sizeof(m_currentFrame));
106 } 115 }
107 116
108 ~PNGImageReader() 117 ~PNGImageReader()
109 { 118 {
110 close(); 119 close();
111 } 120 }
112 121
122 struct FrameInfo {
123 unsigned start;
124 unsigned finish;
125 unsigned width;
126 unsigned height;
127 unsigned xOffset;
128 unsigned yOffset;
129 unsigned duration;
130 unsigned dispose;
131 unsigned blend;
132 };
133
134 static const unsigned headerSize = 33;
135
113 void close() 136 void close()
114 { 137 {
115 if (m_png && m_info) 138 if (m_png && m_info)
116 // This will zero the pointers. 139 // This will zero the pointers.
117 png_destroy_read_struct(&m_png, &m_info, 0); 140 png_destroy_read_struct(&m_png, &m_info, 0);
118 #if USE(QCMSLIB) 141 #if USE(QCMSLIB)
119 clearColorTransform(); 142 clearColorTransform();
120 #endif 143 #endif
121 m_readOffset = 0; 144 m_readOffset = 0;
145 m_parseOffset = 0;
122 } 146 }
123 147
124 bool decode(const SharedBuffer& data, bool sizeOnly) 148 bool parse(const SharedBuffer& data)
125 { 149 {
126 m_decodingSizeOnly = sizeOnly; 150 if (data.size() < headerSize)
151 return false;
127 152
128 // We need to do the setjmp here. Otherwise bad things will happen. 153 const unsigned long maxPNGSize = 1000000UL;
129 if (setjmp(JMPBUF(m_png))) 154 png_bytep inputData = reinterpret_cast<png_bytep>(const_cast<char*>(data .data()));
130 return m_decoder->setFailed(); 155 unsigned inputSize = data.size();
131 156
132 const char* segment; 157 if (!m_parseOffset) {
133 while (unsigned segmentLength = data.getSomeData(segment, m_readOffset)) { 158 if (memcmp(data.data() + 12, "IHDR", 4))
134 m_readOffset += segmentLength; 159 return false;
135 m_currentBufferSize = m_readOffset; 160 m_currentFrame.width = m_width = png_get_uint_32(inputData + 16);
136 png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_ca st<char*>(segment)), segmentLength); 161 m_currentFrame.height = m_height = png_get_uint_32(inputData + 20);
137 if (sizeOnly ? m_decoder->isDecodedSizeAvailable() : m_decoder->fram eIsCompleteAtIndex(0)) 162 if (m_width > maxPNGSize || m_height > maxPNGSize)
138 return true; 163 return false;
164 png_byte bitDepth = inputData[24];
165 if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && bitDepth != 16)
166 return false;
167 png_byte colorType = inputData[25];
168 if (colorType == 1 || colorType == 5 || colorType > 6)
169 return false;
170 if (crc32(crc32(0, Z_NULL, 0), inputData + 12, 17) != png_get_uint_3 2(inputData + 29))
171 return false;
172
173 m_infoSize = m_parseOffset = headerSize;
139 } 174 }
140 175
141 return false; 176 while (!m_isParsed) {
177 if (inputSize < m_parseOffset + 12)
178 return false;
179
180 png_bytep chunk = inputData + m_parseOffset;
181 png_bytep chunkId = chunk + 4;
182 unsigned chunkSize = png_get_uint_32(chunk);
183 unsigned chunkEnd = m_parseOffset + chunkSize + 12;
184
185 if (!memcmp(chunkId, "IEND", 4) && !chunkSize) {
186 if (!m_currentFrame.start)
187 return false;
188 m_isParsed = true;
189 m_currentFrame.finish = chunkEnd;
190 m_frames.append(m_currentFrame);
191 return true;
192 }
193 if (!memcmp(chunkId, "IDAT", 4) && !m_currentFrame.start) {
194 m_currentFrame.start = m_parseOffset;
195 if (!m_decoder->setSize(m_width, m_height))
196 return false;
197 }
198
199 if (inputSize < chunkEnd)
200 return false;
201
202 if (RuntimeEnabledFeatures::animatedPNGEnabled()) {
203 if (!memcmp(chunkId, "fdAT", 4) && m_isAnimated && !m_currentFra me.start) {
204 m_currentFrame.start = m_parseOffset;
205 } else if (!memcmp(chunkId, "acTL", 4) && chunkSize == 8 && !m_i sAnimated && !m_currentFrame.start) {
206 m_isAnimated = true;
207 m_posterFrame = 1;
208 m_visibleFrames = 0;
209 m_repetitionCount = static_cast<int>(png_get_uint_32(chunk + 12)) - 1;
210 } else if (!memcmp(chunkId, "fcTL", 4) && chunkSize == 26 && (m_ isAnimated || !m_currentFrame.start)) {
211 if (m_currentFrame.start) {
212 m_currentFrame.finish = chunkEnd;
213 m_frames.append(m_currentFrame);
214 m_currentFrame.start = 0;
215 } else if (m_frames.isEmpty()) {
216 m_posterFrame = 0;
217 }
218
219 m_currentFrame.width = png_get_uint_32(chunk + 12);
220 m_currentFrame.height = png_get_uint_32(chunk + 16);
221 m_currentFrame.xOffset = png_get_uint_32(chunk + 20);
222 m_currentFrame.yOffset = png_get_uint_32(chunk + 24);
223 unsigned numerator = png_get_uint_16(chunk + 28);
224 unsigned denominator = png_get_uint_16(chunk + 30);
225 m_currentFrame.duration = (!denominator) ? numerator * 10 : (int)(numerator * 1000.0 / denominator);
226 m_currentFrame.dispose = chunk[32];
227 m_currentFrame.blend = chunk[33];
228
229 if (m_currentFrame.width > maxPNGSize || m_currentFrame.heig ht > maxPNGSize
230 || m_currentFrame.xOffset > maxPNGSize || m_currentFrame .yOffset > maxPNGSize
231 || m_currentFrame.xOffset + m_currentFrame.width > m_wid th
232 || m_currentFrame.yOffset + m_currentFrame.height > m_he ight
233 || m_currentFrame.dispose > 2 || m_currentFrame.blend > 1)
234 return false;
235
236 if (!m_visibleFrames) {
237 m_currentFrame.blend = 0;
238 if (m_currentFrame.dispose == 2)
239 m_currentFrame.dispose = 1;
240 }
241 m_visibleFrames++;
242 } else if (!m_currentFrame.start && m_frames.isEmpty()) {
243 m_infoSize = chunkEnd;
244 }
245 }
246
247 m_parseOffset = chunkEnd;
248 }
249 return true;
250 }
251
252 bool decode(const SharedBuffer& data, size_t index)
253 {
254 static png_byte dataIEND[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130};
255
256 index += m_posterFrame;
257 if (index && index >= m_frames.size())
258 return true;
259
260 if (index || !m_readOffset) {
261 png_destroy_read_struct(&m_png, &m_info, 0);
262 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
263 m_info = png_create_info_struct(m_png);
264 png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable, pn gRowAvailable, pngComplete);
265 }
266
267 if (setjmp(JMPBUF(m_png)))
268 return false;
269
270 if (!index) {
271 const char* segment;
272 unsigned endOffset = (!m_frames.isEmpty()) ? m_frames[0].finish : 0;
273 while (unsigned segmentLength = data.getSomeData(segment, m_readOffs et)) {
274 if (endOffset && m_readOffset + segmentLength > endOffset)
275 segmentLength = endOffset - m_readOffset;
276
277 m_readOffset += segmentLength;
278 png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(cons t_cast<char*>(segment)), segmentLength);
279
280 if (m_readOffset == endOffset) {
281 png_process_data(m_png, m_info, dataIEND, 12);
282 break;
283 }
284 }
285 } else {
286 m_readOffset = 0;
287 png_bytep inputData = reinterpret_cast<png_bytep>(const_cast<char*>( data.data()));
288 png_bytep chunk = inputData + m_frames[index].start;
289 png_bytep finish = inputData + m_frames[index].finish;
290 if (inputData + data.size() < finish)
291 return true;
292
293 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
294 if (m_frames[index].width != m_width || m_frames[index].height != m_ height) {
295 unsigned char header[headerSize];
296 memcpy(&header[0], inputData, headerSize);
297 png_save_uint_32(&header[16], m_frames[index].width);
298 png_save_uint_32(&header[20], m_frames[index].height);
299 png_process_data(m_png, m_info, header, headerSize);
300 png_process_data(m_png, m_info, inputData + headerSize, m_infoSi ze - headerSize);
301 } else {
302 png_process_data(m_png, m_info, inputData, m_infoSize);
303 }
304
305 png_byte dataIDAT[8] = {0, 0, 0, 0, 73, 68, 65, 84};
306 while (chunk < finish) {
307 unsigned size = png_get_uint_32(chunk);
308 if (!memcmp(chunk + 4, "fdAT", 4)) {
309 png_save_uint_32(&dataIDAT[0], size - 4);
310 png_process_data(m_png, m_info, dataIDAT, 8);
311 png_process_data(m_png, m_info, chunk + 12, size);
312 }
313 chunk += size + 12;
314 }
315 png_process_data(m_png, m_info, dataIEND, 12);
316 }
317
318 return true;
142 } 319 }
143 320
144 png_structp pngPtr() const { return m_png; } 321 png_structp pngPtr() const { return m_png; }
145 png_infop infoPtr() const { return m_info; } 322 png_infop infoPtr() const { return m_info; }
146 323
147 void setReadOffset(unsigned offset) { m_readOffset = offset; } 324 int repetitionCount() const { return m_repetitionCount; }
148 unsigned currentBufferSize() const { return m_currentBufferSize; } 325 size_t imagesCount() const { return m_frames.isEmpty() ? 1 : m_frames.size() - m_posterFrame; }
149 bool decodingSizeOnly() const { return m_decodingSizeOnly; } 326 const FrameInfo * frameAtIndex(size_t index) const { return m_frames.isEmpty () ? &m_currentFrame : &m_frames[index + m_posterFrame]; }
150 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; } 327 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; }
151 bool hasAlpha() const { return m_hasAlpha; } 328 bool hasAlpha() const { return m_hasAlpha; }
152 329
153 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); } 330 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); }
154 void createInterlaceBuffer(int size) { m_interlaceBuffer = adoptArrayPtr(new png_byte[size]); } 331 void createInterlaceBuffer(int size) { m_interlaceBuffer = adoptArrayPtr(new png_byte[size]); }
155 #if USE(QCMSLIB) 332 #if USE(QCMSLIB)
156 png_bytep rowBuffer() const { return m_rowBuffer.get(); } 333 png_bytep rowBuffer() const { return m_rowBuffer.get(); }
157 void createRowBuffer(int size) { m_rowBuffer = adoptArrayPtr(new png_byte[si ze]); } 334 void createRowBuffer(int size) { m_rowBuffer = adoptArrayPtr(new png_byte[si ze]); }
158 qcms_transform* colorTransform() const { return m_transform; } 335 qcms_transform* colorTransform() const { return m_transform; }
159 336
(...skipping 27 matching lines...) Expand all
187 m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProf ile, dataFormat, QCMS_INTENT_PERCEPTUAL); 364 m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProf ile, dataFormat, QCMS_INTENT_PERCEPTUAL);
188 qcms_profile_release(inputProfile); 365 qcms_profile_release(inputProfile);
189 } 366 }
190 #endif 367 #endif
191 368
192 private: 369 private:
193 png_structp m_png; 370 png_structp m_png;
194 png_infop m_info; 371 png_infop m_info;
195 PNGImageDecoder* m_decoder; 372 PNGImageDecoder* m_decoder;
196 unsigned m_readOffset; 373 unsigned m_readOffset;
197 unsigned m_currentBufferSize; 374 unsigned m_parseOffset;
198 bool m_decodingSizeOnly; 375 unsigned m_infoSize;
376 bool m_isAnimated;
377 bool m_isParsed;
378 unsigned m_width;
379 unsigned m_height;
380 int m_repetitionCount;
381 size_t m_posterFrame;
382 size_t m_visibleFrames;
383 FrameInfo m_currentFrame;
384 Vector<FrameInfo> m_frames;
199 bool m_hasAlpha; 385 bool m_hasAlpha;
200 OwnPtr<png_byte[]> m_interlaceBuffer; 386 OwnPtr<png_byte[]> m_interlaceBuffer;
201 #if USE(QCMSLIB) 387 #if USE(QCMSLIB)
202 qcms_transform* m_transform; 388 qcms_transform* m_transform;
203 OwnPtr<png_byte[]> m_rowBuffer; 389 OwnPtr<png_byte[]> m_rowBuffer;
204 #endif 390 #endif
205 }; 391 };
206 392
207 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp tion colorOptions, size_t maxDecodedBytes) 393 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOp tion colorOptions, size_t maxDecodedBytes)
208 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes) 394 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes)
209 , m_hasColorProfile(false) 395 , m_hasColorProfile(false)
396 , m_currentFrame(0)
210 { 397 {
211 } 398 }
212 399
213 PNGImageDecoder::~PNGImageDecoder() 400 PNGImageDecoder::~PNGImageDecoder()
214 { 401 {
215 } 402 }
216 403
404 bool PNGImageDecoder::frameIsCompleteAtIndex(size_t index) const
405 {
406 if (!m_reader || failed())
407 return false;
408 if (m_frameBufferCache.size() <= 1)
409 return ImageDecoder::frameIsCompleteAtIndex(index);
410 bool frameIsLoadedAtIndex = index < m_frameBufferCache.size();
411 return frameIsLoadedAtIndex;
412 }
413
414 float PNGImageDecoder::frameDurationAtIndex(size_t index) const
415 {
416 return (index < m_frameBufferCache.size()) ? m_frameBufferCache[index].durat ion() : 0;
417 }
418
419 int PNGImageDecoder::repetitionCount() const
420 {
421 return (!m_reader || failed()) ? cAnimationNone : m_reader->repetitionCount( );
422 }
423
424 size_t PNGImageDecoder::clearCacheExceptFrame(size_t clearExceptFrame)
425 {
426 if (clearExceptFrame >= m_frameBufferCache.size() || m_frameBufferCache.size () <= 1)
427 return 0;
428
429 size_t prevFrame = clearExceptFrame;
430 if (m_frameBufferCache[clearExceptFrame].disposalMethod() == ImageFrame::Dis poseOverwritePrevious)
431 prevFrame = m_frameBufferCache[clearExceptFrame].requiredPreviousFrameIn dex();
432
433 while (clearExceptFrame != kNotFound && m_frameBufferCache[clearExceptFrame] .status() != ImageFrame::FrameComplete)
434 clearExceptFrame = m_frameBufferCache[clearExceptFrame].requiredPrevious FrameIndex();
435
436 size_t frameBytesCleared = 0;
437 for (size_t i = 0; i < m_frameBufferCache.size(); ++i) {
438 if (i != prevFrame && i != clearExceptFrame) {
439 frameBytesCleared += frameBytesAtIndex(i);
440 clearFrameBuffer(i);
441 }
442 }
443 return frameBytesCleared;
444 }
445
217 #if USE(QCMSLIB) 446 #if USE(QCMSLIB)
218 static void getColorProfile(png_structp png, png_infop info, ColorProfile& color Profile, bool& sRGB) 447 static void getColorProfile(png_structp png, png_infop info, ColorProfile& color Profile, bool& sRGB)
219 { 448 {
220 #ifdef PNG_iCCP_SUPPORTED 449 #ifdef PNG_iCCP_SUPPORTED
221 ASSERT(colorProfile.isEmpty()); 450 ASSERT(colorProfile.isEmpty());
222 if (png_get_valid(png, info, PNG_INFO_sRGB)) { 451 if (png_get_valid(png, info, PNG_INFO_sRGB)) {
223 sRGB = true; 452 sRGB = true;
224 return; 453 return;
225 } 454 }
226 455
(...skipping 21 matching lines...) Expand all
248 if (!ignoreProfile) 477 if (!ignoreProfile)
249 colorProfile.append(profileData, profileLength); 478 colorProfile.append(profileData, profileLength);
250 #endif 479 #endif
251 } 480 }
252 #endif 481 #endif
253 482
254 void PNGImageDecoder::headerAvailable() 483 void PNGImageDecoder::headerAvailable()
255 { 484 {
256 png_structp png = m_reader->pngPtr(); 485 png_structp png = m_reader->pngPtr();
257 png_infop info = m_reader->infoPtr(); 486 png_infop info = m_reader->infoPtr();
258 png_uint_32 width = png_get_image_width(png, info); 487 png_uint_32 width, height;
259 png_uint_32 height = png_get_image_height(png, info);
260
261 // Protect against large PNGs. See http://bugzil.la/251381 for more details.
262 const unsigned long maxPNGSize = 1000000UL;
263 if (width > maxPNGSize || height > maxPNGSize) {
264 longjmp(JMPBUF(png), 1);
265 return;
266 }
267
268 // Set the image size now that the image header is available.
269 if (!setSize(width, height)) {
270 longjmp(JMPBUF(png), 1);
271 return;
272 }
273
274 int bitDepth, colorType, interlaceType, compressionType, filterType, channel s; 488 int bitDepth, colorType, interlaceType, compressionType, filterType, channel s;
275 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceTy pe, &compressionType, &filterType); 489 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceTy pe, &compressionType, &filterType);
276 490
277 // The options we set here match what Mozilla does. 491 // The options we set here match what Mozilla does.
278 492
279 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 493 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
280 if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) 494 if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
281 png_set_expand(png); 495 png_set_expand(png);
282 496
283 png_bytep trns = 0; 497 png_bytep trns = 0;
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
330 // Tell libpng to send us rows for interlaced pngs. 544 // Tell libpng to send us rows for interlaced pngs.
331 if (interlaceType == PNG_INTERLACE_ADAM7) 545 if (interlaceType == PNG_INTERLACE_ADAM7)
332 png_set_interlace_handling(png); 546 png_set_interlace_handling(png);
333 547
334 // Update our info now. 548 // Update our info now.
335 png_read_update_info(png, info); 549 png_read_update_info(png, info);
336 channels = png_get_channels(png, info); 550 channels = png_get_channels(png, info);
337 ASSERT(channels == 3 || channels == 4); 551 ASSERT(channels == 3 || channels == 4);
338 552
339 m_reader->setHasAlpha(channels == 4); 553 m_reader->setHasAlpha(channels == 4);
340
341 if (m_reader->decodingSizeOnly()) {
342 // If we only needed the size, halt the reader.
343 #if PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MIN OR >= 5)
344 // '0' argument to png_process_data_pause means: Do not cache unprocesse d data.
345 m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data _pause(png, 0));
346 #else
347 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size );
348 png->buffer_size = 0;
349 #endif
350 }
351 } 554 }
352 555
353 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int) 556 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int)
354 { 557 {
355 if (m_frameBufferCache.isEmpty()) 558 if (m_frameBufferCache.isEmpty())
356 return; 559 return;
357 560
358 // Initialize the framebuffer if needed. 561 // Initialize the framebuffer if needed.
359 ImageFrame& buffer = m_frameBufferCache[0]; 562 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
360 if (buffer.status() == ImageFrame::FrameEmpty) { 563 if (buffer.status() == ImageFrame::FrameEmpty) {
361 png_structp png = m_reader->pngPtr(); 564 png_structp png = m_reader->pngPtr();
362 if (!buffer.setSize(size().width(), size().height())) { 565 if (!buffer.setSize(size().width(), size().height())) {
363 longjmp(JMPBUF(png), 1); 566 longjmp(JMPBUF(png), 1);
364 return; 567 return;
365 } 568 }
366 569
367 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; 570 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3;
368 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr ())) { 571 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr ())
369 m_reader->createInterlaceBuffer(colorChannels * size().width() * siz e().height()); 572 || m_currentFrame) {
573 if (!m_reader->interlaceBuffer())
574 m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height());
370 if (!m_reader->interlaceBuffer()) { 575 if (!m_reader->interlaceBuffer()) {
371 longjmp(JMPBUF(png), 1); 576 longjmp(JMPBUF(png), 1);
372 return; 577 return;
373 } 578 }
374 } 579 }
375 580
376 #if USE(QCMSLIB) 581 #if USE(QCMSLIB)
377 if (m_reader->colorTransform()) { 582 if (m_reader->colorTransform()) {
378 m_reader->createRowBuffer(colorChannels * size().width()); 583 if (!m_reader->rowBuffer())
584 m_reader->createRowBuffer(colorChannels * size().width());
379 if (!m_reader->rowBuffer()) { 585 if (!m_reader->rowBuffer()) {
380 longjmp(JMPBUF(png), 1); 586 longjmp(JMPBUF(png), 1);
381 return; 587 return;
382 } 588 }
383 } 589 }
384 #endif 590 #endif
385 buffer.setStatus(ImageFrame::FramePartial); 591 buffer.setStatus(ImageFrame::FramePartial);
386 buffer.setHasAlpha(false); 592 buffer.setHasAlpha(false);
387 593
388 // For PNGs, the frame always fills the entire image. 594 if (!initFrameBuffer()) {
389 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); 595 longjmp(JMPBUF(png), 1);
596 return;
597 }
390 } 598 }
391 599
392 /* libpng comments (here to explain what follows). 600 /* libpng comments (here to explain what follows).
393 * 601 *
394 * this function is called for every row in the image. If the 602 * this function is called for every row in the image. If the
395 * image is interlacing, and you turned on the interlace handler, 603 * image is interlacing, and you turned on the interlace handler,
396 * this function will be called for every row in every pass. 604 * this function will be called for every row in every pass.
397 * Some of these rows will not be changed from the previous pass. 605 * Some of these rows will not be changed from the previous pass.
398 * When the row is not changed, the new_row variable will be NULL. 606 * When the row is not changed, the new_row variable will be NULL.
399 * The rows and passes are called in order, so you don't really 607 * The rows and passes are called in order, so you don't really
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
431 639
432 bool hasAlpha = m_reader->hasAlpha(); 640 bool hasAlpha = m_reader->hasAlpha();
433 png_bytep row = rowBuffer; 641 png_bytep row = rowBuffer;
434 642
435 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { 643 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) {
436 unsigned colorChannels = hasAlpha ? 4 : 3; 644 unsigned colorChannels = hasAlpha ? 4 : 3;
437 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); 645 row = interlaceBuffer + (rowIndex * colorChannels * size().width());
438 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); 646 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer);
439 } 647 }
440 648
649 // Only do incremental image display for the first frame.
650 if (m_currentFrame)
651 return;
652
441 #if USE(QCMSLIB) 653 #if USE(QCMSLIB)
442 if (qcms_transform* transform = m_reader->colorTransform()) { 654 if (qcms_transform* transform = m_reader->colorTransform()) {
443 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width( )); 655 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width( ));
444 row = m_reader->rowBuffer(); 656 row = m_reader->rowBuffer();
445 } 657 }
446 #endif 658 #endif
447 659
448 // Write the decoded row pixels to the frame buffer. The repetitive 660 // Write the decoded row pixels to the frame buffer. The repetitive
449 // form of the row write loops is for speed. 661 // form of the row write loops is for speed.
450 ImageFrame::PixelData* address = buffer.getAddr(0, y); 662 ImageFrame::PixelData* address = buffer.getAddr(0, y);
(...skipping 23 matching lines...) Expand all
474 buffer.setHasAlpha(true); 686 buffer.setHasAlpha(true);
475 687
476 buffer.setPixelsChanged(true); 688 buffer.setPixelsChanged(true);
477 } 689 }
478 690
479 void PNGImageDecoder::complete() 691 void PNGImageDecoder::complete()
480 { 692 {
481 if (m_frameBufferCache.isEmpty()) 693 if (m_frameBufferCache.isEmpty())
482 return; 694 return;
483 695
484 m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete); 696 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
697 buffer.setStatus(ImageFrame::FrameComplete);
698
699 if (!m_currentFrame)
700 return;
701
702 const IntRect& rect = buffer.originalFrameRect();
703 bool hasAlpha = m_reader->hasAlpha();
704 unsigned colorChannels = hasAlpha ? 4 : 3;
705 unsigned alphaMask = 255;
706 ImageFrame::AlphaBlendSource blendMethod = buffer.alphaBlendSource();
707
708 if (blendMethod == ImageFrame::BlendAtopPreviousFrame && !hasAlpha)
709 blendMethod = ImageFrame::BlendAtopBgcolor;
710
711 png_bytep row = m_reader->interlaceBuffer();
712 for (int y = rect.y(); y < rect.maxY(); ++y, row += colorChannels * size().w idth()) {
713 png_bytep pixel = row;
714 #if USE(QCMSLIB)
715 if (qcms_transform* transform = m_reader->colorTransform()) {
716 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().wi dth());
717 pixel = m_reader->rowBuffer();
718 }
719 #endif
720 ImageFrame::PixelData* address = buffer.getAddr(rect.x(), y);
721 if (hasAlpha) {
722 if (blendMethod == ImageFrame::BlendAtopBgcolor) {
723 if (buffer.premultiplyAlpha()) {
724 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) {
725 buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], pixel[3]);
726 alphaMask &= pixel[3];
727 }
728 } else {
729 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) {
730 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2 ], pixel[3]);
731 alphaMask &= pixel[3];
732 }
733 }
734 } else {
735 if (buffer.premultiplyAlpha()) {
736 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) {
737 buffer.overRGBAPremultiply(address++, pixel[0], pixel[1] , pixel[2], pixel[3]);
738 alphaMask &= pixel[3];
739 }
740 } else {
741 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 4) {
742 buffer.overRGBARaw(address++, pixel[0], pixel[1], pixel[ 2], pixel[3]);
743 alphaMask &= pixel[3];
744 }
745 }
746 }
747 } else {
748 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += 3) {
749 buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255);
750 }
751 }
752 }
753
754 if (alphaMask == 255) {
755 if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) {
756 buffer.setHasAlpha(false);
757 } else {
758 size_t frameIndex = m_currentFrame;
759 const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex];
760 while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::Di sposeOverwritePrevious))
761 prevBuffer = &m_frameBufferCache[--frameIndex];
762 if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgc olor)
763 && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contain s(prevBuffer->originalFrameRect()))
764 buffer.setHasAlpha(false);
765 }
766 } else if (blendMethod == ImageFrame::BlendAtopBgcolor && !buffer.hasAlpha() ) {
767 buffer.setHasAlpha(true);
768 }
485 } 769 }
486 770
487 inline bool isComplete(const PNGImageDecoder* decoder) 771 size_t PNGImageDecoder::decodeFrameCount()
488 { 772 {
489 return decoder->frameIsCompleteAtIndex(0); 773 return parse() ? m_reader->imagesCount() : m_frameBufferCache.size();
490 } 774 }
491 775
492 void PNGImageDecoder::decode(bool onlySize) 776 void PNGImageDecoder::decode(size_t index)
777 {
778 if (!m_reader || failed())
779 return;
780
781 Vector<size_t> framesToDecode;
782 size_t frameToDecode = index;
783 do {
784 framesToDecode.append(frameToDecode);
785 frameToDecode = m_frameBufferCache[frameToDecode].requiredPreviousFrameI ndex();
786 } while (frameToDecode != kNotFound && m_frameBufferCache[frameToDecode].sta tus() != ImageFrame::FrameComplete);
787
788 for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); ++i) {
789 m_currentFrame = *i;
790 if (!m_reader->decode(*m_data, m_currentFrame)) {
791 setFailed();
792 break;
793 }
794
795 // We need more data to continue decoding.
796 if (m_frameBufferCache[m_currentFrame].status() != ImageFrame::FrameComp lete)
797 break;
798 }
799 }
800
801 void PNGImageDecoder::initializeNewFrame(size_t index)
802 {
803 ImageFrame* buffer = &m_frameBufferCache[index];
804 bool frameRectIsOpaque = (buffer->status() == ImageFrame::FrameComplete) ? ! m_reader->hasAlpha() : false;
805 buffer->setRequiredPreviousFrameIndex(findRequiredPreviousFrame(index, frame RectIsOpaque));
806 const PNGImageReader::FrameInfo* frame = m_reader->frameAtIndex(index);
807 IntRect frameRect(frame->xOffset, frame->yOffset, frame->width, frame->heigh t);
808 buffer->setOriginalFrameRect(intersection(frameRect, IntRect(IntPoint(), siz e())));
809 buffer->setDuration(frame->duration);
810 buffer->setDisposalMethod((frame->dispose == 2) ? ImageFrame::DisposeOverwri tePrevious : (frame->dispose == 1) ? ImageFrame::DisposeOverwriteBgcolor : Image Frame::DisposeKeep);
811 buffer->setAlphaBlendSource((frame->blend == 1) ? ImageFrame::BlendAtopPrevi ousFrame : ImageFrame::BlendAtopBgcolor);
812 }
813
814 bool PNGImageDecoder::initFrameBuffer()
815 {
816 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
817
818 const size_t requiredPreviousFrameIndex = buffer.requiredPreviousFrameIndex( );
819 if (requiredPreviousFrameIndex == kNotFound) {
820 // This frame doesn't rely on any previous data.
821 buffer.zeroFillPixelData();
822 if (!m_currentFrame)
823 buffer.setHasAlpha(false);
824 } else {
825 const ImageFrame& prevBuffer = m_frameBufferCache[requiredPreviousFrameI ndex];
826 ASSERT(prevBuffer.status() == ImageFrame::FrameComplete);
827
828 // Preserve the last frame as the starting state for this frame.
829 if (!buffer.copyBitmapData(prevBuffer))
830 return setFailed();
831
832 if (prevBuffer.disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) {
833 // We want to clear the previous frame to transparent, without
834 // affecting pixels in the image outside of the frame.
835 const IntRect& prevRect = prevBuffer.originalFrameRect();
836 ASSERT(!prevRect.contains(IntRect(IntPoint(), size())));
837 buffer.zeroFillFrameRect(prevRect);
838 }
839 }
840 return true;
841 }
842
843 bool PNGImageDecoder::parse()
493 { 844 {
494 if (failed()) 845 if (failed())
495 return; 846 return false;
496 847
497 if (!m_reader) 848 if (!m_reader)
498 m_reader = adoptPtr(new PNGImageReader(this)); 849 m_reader = adoptPtr(new PNGImageReader(this));
499 850
500 // If we couldn't decode the image but have received all the data, decoding 851 // If we couldn't decode the image but have received all the data, decoding
501 // has failed. 852 // has failed.
502 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) 853 if (!m_reader->parse(*m_data) && isAllDataReceived())
503 setFailed(); 854 setFailed();
504 855
505 // If decoding is done or failed, we don't need the PNGImageReader anymore. 856 if (failed()) {
506 if (isComplete(this) || failed())
507 m_reader.clear(); 857 m_reader.clear();
858 return false;
859 }
860 return true;
508 } 861 }
509 862
510 } // namespace blink 863 } // namespace blink
OLDNEW
« no previous file with comments | « Source/platform/image-decoders/png/PNGImageDecoder.h ('k') | Source/platform/image-decoders/png/PNGImageDecoderTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698