1/*
2 * Copyright (C) 2006 Apple Inc.
3 * Copyright (C) 2007-2009 Torch Mobile, Inc.
4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
5 *
6 * Portions are Copyright (C) 2001 mozilla.org
7 *
8 * Other contributors:
9 * Stuart Parmenter <stuart@mozilla.com>
10 * Max Stepin <maxstepin@gmail.com>
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 *
26 * Alternatively, the contents of this file may be used under the terms
27 * of either the Mozilla Public License Version 1.1, found at
28 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
29 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
30 * (the "GPL"), in which case the provisions of the MPL or the GPL are
31 * applicable instead of those above. If you wish to allow use of your
32 * version of this file only under the terms of one of those two
33 * licenses (the MPL or the GPL) and not to allow others to use your
34 * version of this file under the LGPL, indicate your decision by
35 * deletingthe provisions above and replace them with the notice and
36 * other provisions required by the MPL or the GPL, as the case may be.
37 * If you do not delete the provisions above, a recipient may use your
38 * version of this file under any of the LGPL, the MPL or the GPL.
39 */
40
41#include "config.h"
42#include "PNGImageDecoder.h"
43
44#include "Color.h"
45#include <png.h>
46#include <wtf/StdLibExtras.h>
47#include <wtf/UniqueArray.h>
48
49#if defined(PNG_LIBPNG_VER_MAJOR) && defined(PNG_LIBPNG_VER_MINOR) && (PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4))
50#define JMPBUF(png_ptr) png_jmpbuf(png_ptr)
51#else
52#define JMPBUF(png_ptr) png_ptr->jmpbuf
53#endif
54
55namespace WebCore {
56
57// Gamma constants.
58const double cMaxGamma = 21474.83;
59const double cDefaultGamma = 2.2;
60const double cInverseGamma = 0.45455;
61
62// Protect against large PNGs. See Mozilla's bug #251381 for more info.
63const unsigned long cMaxPNGSize = 1000000UL;
64
65// Called if the decoding of the image fails.
66static void PNGAPI decodingFailed(png_structp png, png_const_charp)
67{
68 longjmp(JMPBUF(png), 1);
69}
70
71// Callbacks given to the read struct. The first is for warnings (we want to
72// treat a particular warning as an error, which is why we have to register this
73// callback).
74static void PNGAPI decodingWarning(png_structp png, png_const_charp warningMsg)
75{
76 // Mozilla did this, so we will too.
77 // Convert a tRNS warning to be an error (see
78 // http://bugzilla.mozilla.org/show_bug.cgi?id=251381 )
79 if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24))
80 png_error(png, warningMsg);
81}
82
83// Called when we have obtained the header information (including the size).
84static void PNGAPI headerAvailable(png_structp png, png_infop)
85{
86 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable();
87}
88
89// Called when a row is ready.
90static void PNGAPI rowAvailable(png_structp png, png_bytep rowBuffer, png_uint_32 rowIndex, int interlacePass)
91{
92 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass);
93}
94
95// Called when we have completely finished decoding the image.
96static void PNGAPI pngComplete(png_structp png, png_infop)
97{
98 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete();
99}
100
101#if ENABLE(APNG)
102// Called when we have the frame header.
103static void PNGAPI frameHeader(png_structp png, png_infop)
104{
105 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->frameHeader();
106}
107
108// Called when we found user chunks.
109static int PNGAPI readChunks(png_structp png, png_unknown_chunkp chunk)
110{
111 static_cast<PNGImageDecoder*>(png_get_user_chunk_ptr(png))->readChunks(chunk);
112 return 1;
113}
114#endif
115
116class PNGImageReader {
117 WTF_MAKE_FAST_ALLOCATED;
118public:
119 PNGImageReader(PNGImageDecoder* decoder)
120 : m_readOffset(0)
121 , m_currentBufferSize(0)
122 , m_decodingSizeOnly(false)
123 , m_hasAlpha(false)
124 {
125 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, decodingWarning);
126 m_info = png_create_info_struct(m_png);
127 png_set_progressive_read_fn(m_png, decoder, headerAvailable, rowAvailable, pngComplete);
128#if ENABLE(APNG)
129 png_byte apngChunks[]= {"acTL\0fcTL\0fdAT\0"};
130 png_set_keep_unknown_chunks(m_png, 1, apngChunks, 3);
131 png_set_read_user_chunk_fn(m_png, static_cast<png_voidp>(decoder), readChunks);
132 decoder->init();
133#endif
134 }
135
136 ~PNGImageReader()
137 {
138 close();
139 }
140
141 void close()
142 {
143 if (m_png && m_info)
144 // This will zero the pointers.
145 png_destroy_read_struct(&m_png, &m_info, 0);
146 m_interlaceBuffer.reset();
147 m_readOffset = 0;
148 }
149
150 bool decode(const SharedBuffer& data, bool sizeOnly, unsigned haltAtFrame)
151 {
152 m_decodingSizeOnly = sizeOnly;
153 PNGImageDecoder* decoder = static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_png));
154
155 // We need to do the setjmp here. Otherwise bad things will happen.
156 if (setjmp(JMPBUF(m_png)))
157 return decoder->setFailed();
158
159 auto bytesToSkip = m_readOffset;
160
161 // FIXME: Use getSomeData which is O(log(n)) instead of skipping bytes which is O(n).
162 for (const auto& element : data) {
163 if (bytesToSkip > element.segment->size()) {
164 bytesToSkip -= element.segment->size();
165 continue;
166 }
167 auto bytesToUse = element.segment->size() - bytesToSkip;
168 m_readOffset += bytesToUse;
169 m_currentBufferSize = m_readOffset;
170 png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_cast<char*>(element.segment->data() + bytesToSkip)), bytesToUse);
171 bytesToSkip = 0;
172 // We explicitly specify the superclass encodedDataStatus() because we
173 // merely want to check if we've managed to set the size, not
174 // (recursively) trigger additional decoding if we haven't.
175 if (sizeOnly ? decoder->ScalableImageDecoder::encodedDataStatus() >= EncodedDataStatus::SizeAvailable : decoder->isCompleteAtIndex(haltAtFrame))
176 return true;
177 }
178 return false;
179 }
180
181 png_structp pngPtr() const { return m_png; }
182 png_infop infoPtr() const { return m_info; }
183
184 void setReadOffset(unsigned offset) { m_readOffset = offset; }
185 unsigned currentBufferSize() const { return m_currentBufferSize; }
186 bool decodingSizeOnly() const { return m_decodingSizeOnly; }
187 void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; }
188 bool hasAlpha() const { return m_hasAlpha; }
189
190 png_bytep interlaceBuffer() const { return m_interlaceBuffer.get(); }
191 void createInterlaceBuffer(int size) { m_interlaceBuffer = makeUniqueArray<png_byte>(size); }
192
193private:
194 png_structp m_png;
195 png_infop m_info;
196 unsigned m_readOffset;
197 unsigned m_currentBufferSize;
198 bool m_decodingSizeOnly;
199 bool m_hasAlpha;
200 UniqueArray<png_byte> m_interlaceBuffer;
201};
202
203PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
204 : ScalableImageDecoder(alphaOption, gammaAndColorProfileOption)
205 , m_doNothingOnFailure(false)
206 , m_currentFrame(0)
207#if ENABLE(APNG)
208 , m_png(nullptr)
209 , m_info(nullptr)
210 , m_isAnimated(false)
211 , m_frameInfo(false)
212 , m_frameIsHidden(false)
213 , m_hasInfo(false)
214 , m_gamma(45455)
215 , m_frameCount(1)
216 , m_playCount(0)
217 , m_totalFrames(0)
218 , m_sizePLTE(0)
219 , m_sizetRNS(0)
220 , m_sequenceNumber(0)
221 , m_width(0)
222 , m_height(0)
223 , m_xOffset(0)
224 , m_yOffset(0)
225 , m_delayNumerator(1)
226 , m_delayDenominator(1)
227 , m_dispose(0)
228 , m_blend(0)
229#endif
230{
231}
232
233PNGImageDecoder::~PNGImageDecoder() = default;
234
235#if ENABLE(APNG)
236RepetitionCount PNGImageDecoder::repetitionCount() const
237{
238 // Signal no repetition if the PNG image is not animated.
239 if (!m_isAnimated)
240 return RepetitionCountNone;
241
242 // APNG format uses 0 to indicate that an animation must play indefinitely. But
243 // the RepetitionCount enumeration uses RepetitionCountInfinite, so we need to adapt this.
244 if (!m_playCount)
245 return RepetitionCountInfinite;
246
247 return m_playCount;
248}
249#endif
250
251bool PNGImageDecoder::setSize(const IntSize& size)
252{
253 if (!ScalableImageDecoder::setSize(size))
254 return false;
255
256 prepareScaleDataIfNecessary();
257 return true;
258}
259
260ScalableImageDecoderFrame* PNGImageDecoder::frameBufferAtIndex(size_t index)
261{
262#if ENABLE(APNG)
263 if (ScalableImageDecoder::encodedDataStatus() < EncodedDataStatus::SizeAvailable)
264 return nullptr;
265
266 if (index >= frameCount())
267 index = frameCount() - 1;
268#else
269 if (index)
270 return nullptr;
271#endif
272
273 if (m_frameBufferCache.isEmpty())
274 m_frameBufferCache.grow(1);
275
276 auto& frame = m_frameBufferCache[index];
277 if (!frame.isComplete())
278 decode(false, index, isAllDataReceived());
279 return &frame;
280}
281
282bool PNGImageDecoder::setFailed()
283{
284 if (m_doNothingOnFailure)
285 return false;
286 m_reader = nullptr;
287 return ScalableImageDecoder::setFailed();
288}
289
290void PNGImageDecoder::headerAvailable()
291{
292 png_structp png = m_reader->pngPtr();
293 png_infop info = m_reader->infoPtr();
294 png_uint_32 width = png_get_image_width(png, info);
295 png_uint_32 height = png_get_image_height(png, info);
296
297 // Protect against large images.
298 if (width > cMaxPNGSize || height > cMaxPNGSize) {
299 longjmp(JMPBUF(png), 1);
300 return;
301 }
302
303 // We can fill in the size now that the header is available. Avoid memory
304 // corruption issues by neutering setFailed() during this call; if we don't
305 // do this, failures will cause |m_reader| to be deleted, and our jmpbuf
306 // will cease to exist. Note that we'll still properly set the failure flag
307 // in this case as soon as we longjmp().
308 m_doNothingOnFailure = true;
309 bool result = setSize(IntSize(width, height));
310 m_doNothingOnFailure = false;
311 if (!result) {
312 longjmp(JMPBUF(png), 1);
313 return;
314 }
315
316 int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
317 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType);
318
319 // The options we set here match what Mozilla does.
320
321#if ENABLE(APNG)
322 m_hasInfo = true;
323 if (m_isAnimated) {
324 png_save_uint_32(m_dataIHDR, 13);
325 memcpy(m_dataIHDR + 4, "IHDR", 4);
326 png_save_uint_32(m_dataIHDR + 8, width);
327 png_save_uint_32(m_dataIHDR + 12, height);
328 m_dataIHDR[16] = bitDepth;
329 m_dataIHDR[17] = colorType;
330 m_dataIHDR[18] = compressionType;
331 m_dataIHDR[19] = filterType;
332 m_dataIHDR[20] = interlaceType;
333 }
334#endif
335
336 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
337 if (colorType == PNG_COLOR_TYPE_PALETTE) {
338#if ENABLE(APNG)
339 if (m_isAnimated) {
340 png_colorp palette;
341 int paletteSize = 0;
342 png_get_PLTE(png, info, &palette, &paletteSize);
343 paletteSize *= 3;
344 png_save_uint_32(m_dataPLTE, paletteSize);
345 memcpy(m_dataPLTE + 4, "PLTE", 4);
346 memcpy(m_dataPLTE + 8, palette, paletteSize);
347 m_sizePLTE = paletteSize + 12;
348 }
349#endif
350 png_set_expand(png);
351 }
352
353 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
354 png_set_expand(png);
355
356 png_bytep trns = 0;
357 int trnsCount = 0;
358 png_color_16p transValues;
359 if (png_get_valid(png, info, PNG_INFO_tRNS)) {
360 png_get_tRNS(png, info, &trns, &trnsCount, &transValues);
361#if ENABLE(APNG)
362 if (m_isAnimated) {
363 if (colorType == PNG_COLOR_TYPE_RGB) {
364 png_save_uint_16(m_datatRNS + 8, transValues->red);
365 png_save_uint_16(m_datatRNS + 10, transValues->green);
366 png_save_uint_16(m_datatRNS + 12, transValues->blue);
367 trnsCount = 6;
368 } else if (colorType == PNG_COLOR_TYPE_GRAY) {
369 png_save_uint_16(m_datatRNS + 8, transValues->gray);
370 trnsCount = 2;
371 } else if (colorType == PNG_COLOR_TYPE_PALETTE)
372 memcpy(m_datatRNS + 8, trns, trnsCount);
373
374 png_save_uint_32(m_datatRNS, trnsCount);
375 memcpy(m_datatRNS + 4, "tRNS", 4);
376 m_sizetRNS = trnsCount + 12;
377 }
378#endif
379 png_set_expand(png);
380 }
381
382 if (bitDepth == 16)
383 png_set_strip_16(png);
384
385 if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
386 png_set_gray_to_rgb(png);
387
388 // Deal with gamma and keep it under our control.
389 double gamma;
390 if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) {
391 if ((gamma <= 0.0) || (gamma > cMaxGamma)) {
392 gamma = cInverseGamma;
393 png_set_gAMA(png, info, gamma);
394 }
395 png_set_gamma(png, cDefaultGamma, gamma);
396#if ENABLE(APNG)
397 m_gamma = static_cast<int>(gamma * 100000);
398#endif
399 } else
400 png_set_gamma(png, cDefaultGamma, cInverseGamma);
401
402 // Tell libpng to send us rows for interlaced pngs.
403 if (interlaceType == PNG_INTERLACE_ADAM7)
404 png_set_interlace_handling(png);
405
406 // Update our info now.
407 png_read_update_info(png, info);
408 channels = png_get_channels(png, info);
409 ASSERT(channels == 3 || channels == 4);
410
411 m_reader->setHasAlpha(channels == 4);
412
413 if (m_reader->decodingSizeOnly()) {
414 // If we only needed the size, halt the reader.
415#if defined(PNG_LIBPNG_VER_MAJOR) && defined(PNG_LIBPNG_VER_MINOR) && (PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5))
416 // '0' argument to png_process_data_pause means: Do not cache unprocessed data.
417 m_reader->setReadOffset(m_reader->currentBufferSize() - png_process_data_pause(png, 0));
418#else
419 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
420 png->buffer_size = 0;
421#endif
422 }
423}
424
425void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int)
426{
427 if (m_frameBufferCache.isEmpty())
428 return;
429
430 // Initialize the framebuffer if needed.
431#if ENABLE(APNG)
432 if (m_currentFrame >= frameCount())
433 return;
434#endif
435 auto& buffer = m_frameBufferCache[m_currentFrame];
436 if (buffer.isInvalid()) {
437 png_structp png = m_reader->pngPtr();
438 if (!buffer.initialize(scaledSize(), m_premultiplyAlpha)) {
439 longjmp(JMPBUF(png), 1);
440 return;
441 }
442
443 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3;
444 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())
445 || m_currentFrame) {
446 if (!m_reader->interlaceBuffer())
447 m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height());
448 if (!m_reader->interlaceBuffer()) {
449 longjmp(JMPBUF(png), 1);
450 return;
451 }
452 }
453
454 buffer.setDecodingStatus(DecodingStatus::Partial);
455 buffer.setHasAlpha(false);
456
457#if ENABLE(APNG)
458 if (m_currentFrame)
459 initFrameBuffer(m_currentFrame);
460#endif
461 }
462
463 /* libpng comments (here to explain what follows).
464 *
465 * this function is called for every row in the image. If the
466 * image is interlacing, and you turned on the interlace handler,
467 * this function will be called for every row in every pass.
468 * Some of these rows will not be changed from the previous pass.
469 * When the row is not changed, the new_row variable will be NULL.
470 * The rows and passes are called in order, so you don't really
471 * need the row_num and pass, but I'm supplying them because it
472 * may make your life easier.
473 */
474
475 // Nothing to do if the row is unchanged, or the row is outside
476 // the image bounds: libpng may send extra rows, ignore them to
477 // make our lives easier.
478 if (!rowBuffer)
479 return;
480 int y = !m_scaled ? rowIndex : scaledY(rowIndex);
481 if (y < 0 || y >= scaledSize().height())
482 return;
483
484 /* libpng comments (continued).
485 *
486 * For the non-NULL rows of interlaced images, you must call
487 * png_progressive_combine_row() passing in the row and the
488 * old row. You can call this function for NULL rows (it will
489 * just return) and for non-interlaced images (it just does the
490 * memcpy for you) if it will make the code easier. Thus, you
491 * can just do this for all cases:
492 *
493 * png_progressive_combine_row(png_ptr, old_row, new_row);
494 *
495 * where old_row is what was displayed for previous rows. Note
496 * that the first pass (pass == 0 really) will completely cover
497 * the old row, so the rows do not have to be initialized. After
498 * the first pass (and only for interlaced images), you will have
499 * to pass the current row, and the function will combine the
500 * old row and the new row.
501 */
502
503 bool hasAlpha = m_reader->hasAlpha();
504 unsigned colorChannels = hasAlpha ? 4 : 3;
505 png_bytep row = rowBuffer;
506
507 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) {
508 row = interlaceBuffer + (rowIndex * colorChannels * size().width());
509#if ENABLE(APNG)
510 if (m_currentFrame) {
511 png_progressive_combine_row(m_png, row, rowBuffer);
512 return; // Only do incremental image display for the first frame.
513 }
514#endif
515 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer);
516 }
517
518 // Write the decoded row pixels to the frame buffer.
519 auto* address = buffer.backingStore()->pixelAt(0, y);
520 int width = scaledSize().width();
521 unsigned char nonTrivialAlphaMask = 0;
522
523 png_bytep pixel = row;
524 if (hasAlpha) {
525 for (int x = 0; x < width; ++x, pixel += 4, ++address) {
526 unsigned alpha = pixel[3];
527 buffer.backingStore()->setPixel(address, pixel[0], pixel[1], pixel[2], alpha);
528 nonTrivialAlphaMask |= (255 - alpha);
529 }
530 } else {
531 for (int x = 0; x < width; ++x, pixel += 3, ++address)
532 *address = makeRGB(pixel[0], pixel[1], pixel[2]);
533 }
534
535 if (nonTrivialAlphaMask && !buffer.hasAlpha())
536 buffer.setHasAlpha(true);
537}
538
539void PNGImageDecoder::pngComplete()
540{
541#if ENABLE(APNG)
542 if (m_isAnimated) {
543 if (!processingFinish() && m_frameCount == m_currentFrame)
544 return;
545
546 fallbackNotAnimated();
547 }
548#endif
549 if (!m_frameBufferCache.isEmpty())
550 m_frameBufferCache.first().setDecodingStatus(DecodingStatus::Complete);
551}
552
553void PNGImageDecoder::decode(bool onlySize, unsigned haltAtFrame, bool allDataReceived)
554{
555 if (failed())
556 return;
557
558 if (!m_reader)
559 m_reader = std::make_unique<PNGImageReader>(this);
560
561 // If we couldn't decode the image but we've received all the data, decoding
562 // has failed.
563 if (!m_reader->decode(*m_data, onlySize, haltAtFrame) && allDataReceived)
564 setFailed();
565 // If we're done decoding the image, we don't need the PNGImageReader
566 // anymore. (If we failed, |m_reader| has already been cleared.)
567 else if (isComplete())
568 m_reader = nullptr;
569}
570
571#if ENABLE(APNG)
572void PNGImageDecoder::readChunks(png_unknown_chunkp chunk)
573{
574 if (!memcmp(chunk->name, "acTL", 4) && chunk->size == 8) {
575 if (m_hasInfo || m_isAnimated)
576 return;
577
578 m_frameCount = png_get_uint_32(chunk->data);
579 m_playCount = png_get_uint_32(chunk->data + 4);
580
581 if (!m_frameCount || m_frameCount > PNG_UINT_31_MAX || m_playCount > PNG_UINT_31_MAX) {
582 fallbackNotAnimated();
583 return;
584 }
585
586 m_isAnimated = true;
587 if (!m_frameInfo)
588 m_frameIsHidden = true;
589
590 if (m_frameBufferCache.size() == m_frameCount)
591 return;
592
593 m_frameBufferCache.resize(m_frameCount);
594 } else if (!memcmp(chunk->name, "fcTL", 4) && chunk->size == 26) {
595 if (m_hasInfo && !m_isAnimated)
596 return;
597
598 m_frameInfo = false;
599
600 if (processingFinish()) {
601 fallbackNotAnimated();
602 return;
603 }
604
605 // At this point the old frame is done. Let's start a new one.
606 unsigned sequenceNumber = png_get_uint_32(chunk->data);
607 if (sequenceNumber != m_sequenceNumber++) {
608 fallbackNotAnimated();
609 return;
610 }
611
612 m_width = png_get_uint_32(chunk->data + 4);
613 m_height = png_get_uint_32(chunk->data + 8);
614 m_xOffset = png_get_uint_32(chunk->data + 12);
615 m_yOffset = png_get_uint_32(chunk->data + 16);
616 m_delayNumerator = png_get_uint_16(chunk->data + 20);
617 m_delayDenominator = png_get_uint_16(chunk->data + 22);
618 m_dispose = chunk->data[24];
619 m_blend = chunk->data[25];
620
621 png_structp png = m_reader->pngPtr();
622 png_infop info = m_reader->infoPtr();
623 png_uint_32 width = png_get_image_width(png, info);
624 png_uint_32 height = png_get_image_height(png, info);
625
626 if (m_width > cMaxPNGSize || m_height > cMaxPNGSize
627 || m_xOffset > cMaxPNGSize || m_yOffset > cMaxPNGSize
628 || m_xOffset + m_width > width
629 || m_yOffset + m_height > height
630 || m_dispose > 2 || m_blend > 1) {
631 fallbackNotAnimated();
632 return;
633 }
634
635 if (m_frameBufferCache.isEmpty())
636 m_frameBufferCache.grow(1);
637
638 if (m_currentFrame < m_frameBufferCache.size()) {
639 auto& buffer = m_frameBufferCache[m_currentFrame];
640
641 if (!m_delayDenominator)
642 buffer.setDuration(Seconds::fromMilliseconds(m_delayNumerator * 10));
643 else
644 buffer.setDuration(Seconds::fromMilliseconds(m_delayNumerator * 1000 / m_delayDenominator));
645
646 if (m_dispose == 2)
647 buffer.setDisposalMethod(ScalableImageDecoderFrame::DisposalMethod::RestoreToPrevious);
648 else if (m_dispose == 1)
649 buffer.setDisposalMethod(ScalableImageDecoderFrame::DisposalMethod::RestoreToBackground);
650 else
651 buffer.setDisposalMethod(ScalableImageDecoderFrame::DisposalMethod::DoNotDispose);
652 }
653
654 m_frameInfo = true;
655 m_frameIsHidden = false;
656
657 if (processingStart(chunk)) {
658 fallbackNotAnimated();
659 return;
660 }
661 } else if (!memcmp(chunk->name, "fdAT", 4) && chunk->size >= 4) {
662 if (!m_frameInfo || !m_isAnimated)
663 return;
664
665 unsigned sequenceNumber = png_get_uint_32(chunk->data);
666 if (sequenceNumber != m_sequenceNumber++) {
667 fallbackNotAnimated();
668 return;
669 }
670
671 if (setjmp(JMPBUF(m_png))) {
672 fallbackNotAnimated();
673 return;
674 }
675
676 png_save_uint_32(chunk->data, chunk->size - 4);
677 png_process_data(m_png, m_info, chunk->data, 4);
678 memcpy(chunk->data, "IDAT", 4);
679 png_process_data(m_png, m_info, chunk->data, chunk->size);
680 png_process_data(m_png, m_info, chunk->data, 4);
681 }
682}
683
684void PNGImageDecoder::frameHeader()
685{
686 int colorType = png_get_color_type(m_png, m_info);
687
688 if (colorType == PNG_COLOR_TYPE_PALETTE)
689 png_set_expand(m_png);
690
691 int bitDepth = png_get_bit_depth(m_png, m_info);
692 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
693 png_set_expand(m_png);
694
695 if (png_get_valid(m_png, m_info, PNG_INFO_tRNS))
696 png_set_expand(m_png);
697
698 if (bitDepth == 16)
699 png_set_strip_16(m_png);
700
701 if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
702 png_set_gray_to_rgb(m_png);
703
704 double gamma;
705 if (png_get_gAMA(m_png, m_info, &gamma))
706 png_set_gamma(m_png, cDefaultGamma, gamma);
707
708 png_set_interlace_handling(m_png);
709
710 png_read_update_info(m_png, m_info);
711}
712
713void PNGImageDecoder::init()
714{
715 m_isAnimated = false;
716 m_frameInfo = false;
717 m_frameIsHidden = false;
718 m_hasInfo = false;
719 m_currentFrame = 0;
720 m_totalFrames = 0;
721 m_sequenceNumber = 0;
722}
723
724void PNGImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame)
725{
726 if (m_frameBufferCache.isEmpty())
727 return;
728
729 // See GIFImageDecoder for full explanation.
730 clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1);
731 const Vector<ScalableImageDecoderFrame>::iterator end(m_frameBufferCache.begin() + clearBeforeFrame);
732
733 Vector<ScalableImageDecoderFrame>::iterator i(end);
734 for (; (i != m_frameBufferCache.begin()) && (i->isInvalid() || (i->disposalMethod() == ScalableImageDecoderFrame::DisposalMethod::RestoreToPrevious)); --i) {
735 if (i->isComplete() && (i != end))
736 i->clear();
737 }
738
739 // Now |i| holds the last frame we need to preserve; clear prior frames.
740 for (Vector<ScalableImageDecoderFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j) {
741 ASSERT(!j->isPartial());
742 if (j->isInvalid())
743 j->clear();
744 }
745}
746
747void PNGImageDecoder::initFrameBuffer(size_t frameIndex)
748{
749 if (frameIndex >= frameCount())
750 return;
751
752 auto& buffer = m_frameBufferCache[frameIndex];
753
754 // The starting state for this frame depends on the previous frame's
755 // disposal method.
756 //
757 // Frames that use the DisposalMethod::RestoreToPrevious method are effectively
758 // no-ops in terms of changing the starting state of a frame compared to
759 // the starting state of the previous frame, so skip over them. (If the
760 // first frame specifies this method, it will get treated like
761 // DisposeOverwriteBgcolor below and reset to a completely empty image.)
762 const auto* prevBuffer = &m_frameBufferCache[--frameIndex];
763 auto prevMethod = prevBuffer->disposalMethod();
764 while (frameIndex && (prevMethod == ScalableImageDecoderFrame::DisposalMethod::RestoreToPrevious)) {
765 prevBuffer = &m_frameBufferCache[--frameIndex];
766 prevMethod = prevBuffer->disposalMethod();
767 }
768
769 png_structp png = m_reader->pngPtr();
770 ASSERT(prevBuffer->isComplete());
771
772 if (prevMethod == ScalableImageDecoderFrame::DisposalMethod::DoNotDispose) {
773 // Preserve the last frame as the starting state for this frame.
774 if (!prevBuffer->backingStore() || !buffer.initialize(*prevBuffer->backingStore()))
775 longjmp(JMPBUF(png), 1);
776 } else {
777 // We want to clear the previous frame to transparent, without
778 // affecting pixels in the image outside of the frame.
779 IntRect prevRect = prevBuffer->backingStore()->frameRect();
780 if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) {
781 // Clearing the first frame, or a frame the size of the whole
782 // image, results in a completely empty image.
783 buffer.backingStore()->clear();
784 buffer.setHasAlpha(true);
785 } else {
786 // Copy the whole previous buffer, then clear just its frame.
787 if (!prevBuffer->backingStore() || !buffer.initialize(*prevBuffer->backingStore())) {
788 longjmp(JMPBUF(png), 1);
789 return;
790 }
791 buffer.backingStore()->clearRect(prevRect);
792 buffer.setHasAlpha(true);
793 }
794 }
795
796 IntRect frameRect(m_xOffset, m_yOffset, m_width, m_height);
797
798 // Make sure the frameRect doesn't extend outside the buffer.
799 if (frameRect.maxX() > size().width())
800 frameRect.setWidth(size().width() - m_xOffset);
801 if (frameRect.maxY() > size().height())
802 frameRect.setHeight(size().height() - m_yOffset);
803
804 int left = upperBoundScaledX(frameRect.x());
805 int right = lowerBoundScaledX(frameRect.maxX(), left);
806 int top = upperBoundScaledY(frameRect.y());
807 int bottom = lowerBoundScaledY(frameRect.maxY(), top);
808 buffer.backingStore()->setFrameRect(IntRect(left, top, right - left, bottom - top));
809}
810
811void PNGImageDecoder::frameComplete()
812{
813 if (m_frameIsHidden || m_currentFrame >= frameCount())
814 return;
815
816 auto& buffer = m_frameBufferCache[m_currentFrame];
817 buffer.setDecodingStatus(DecodingStatus::Complete);
818
819 png_bytep interlaceBuffer = m_reader->interlaceBuffer();
820
821 if (m_currentFrame && interlaceBuffer) {
822 IntRect rect = buffer.backingStore()->frameRect();
823 bool hasAlpha = m_reader->hasAlpha();
824 unsigned colorChannels = hasAlpha ? 4 : 3;
825 bool nonTrivialAlpha = false;
826 if (m_blend && !hasAlpha)
827 m_blend = 0;
828
829 ASSERT(!m_scaled);
830 png_bytep row = interlaceBuffer;
831 for (int y = rect.y(); y < rect.maxY(); ++y, row += colorChannels * size().width()) {
832 png_bytep pixel = row;
833 auto* address = buffer.backingStore()->pixelAt(rect.x(), y);
834 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += colorChannels) {
835 unsigned alpha = hasAlpha ? pixel[3] : 255;
836 nonTrivialAlpha |= alpha < 255;
837 if (!m_blend)
838 buffer.backingStore()->setPixel(address++, pixel[0], pixel[1], pixel[2], alpha);
839 else
840 buffer.backingStore()->blendPixel(address++, pixel[0], pixel[1], pixel[2], alpha);
841 }
842 }
843
844 if (!nonTrivialAlpha) {
845 IntRect rect = buffer.backingStore()->frameRect();
846 if (rect.contains(IntRect(IntPoint(), scaledSize())))
847 buffer.setHasAlpha(false);
848 else {
849 size_t frameIndex = m_currentFrame;
850 const auto* prevBuffer = &m_frameBufferCache[--frameIndex];
851 while (frameIndex && (prevBuffer->disposalMethod() == ScalableImageDecoderFrame::DisposalMethod::RestoreToPrevious))
852 prevBuffer = &m_frameBufferCache[--frameIndex];
853
854 IntRect prevRect = prevBuffer->backingStore()->frameRect();
855 if ((prevBuffer->disposalMethod() == ScalableImageDecoderFrame::DisposalMethod::RestoreToBackground) && !prevBuffer->hasAlpha() && rect.contains(prevRect))
856 buffer.setHasAlpha(false);
857 }
858 } else if (!m_blend && !buffer.hasAlpha())
859 buffer.setHasAlpha(nonTrivialAlpha);
860 }
861 m_currentFrame++;
862}
863
864int PNGImageDecoder::processingStart(png_unknown_chunkp chunk)
865{
866 static png_byte dataPNG[8] = {137, 80, 78, 71, 13, 10, 26, 10};
867 static png_byte datagAMA[16] = {0, 0, 0, 4, 103, 65, 77, 65};
868
869 if (!m_hasInfo)
870 return 0;
871
872 m_totalFrames++;
873
874 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, 0);
875 m_info = png_create_info_struct(m_png);
876 if (setjmp(JMPBUF(m_png)))
877 return 1;
878
879 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
880 png_set_progressive_read_fn(m_png, static_cast<png_voidp>(this),
881 WebCore::frameHeader, WebCore::rowAvailable, 0);
882
883 memcpy(m_dataIHDR + 8, chunk->data + 4, 8);
884 png_save_uint_32(datagAMA + 8, m_gamma);
885
886 png_process_data(m_png, m_info, dataPNG, 8);
887 png_process_data(m_png, m_info, m_dataIHDR, 25);
888 png_process_data(m_png, m_info, datagAMA, 16);
889 if (m_sizePLTE > 0)
890 png_process_data(m_png, m_info, m_dataPLTE, m_sizePLTE);
891 if (m_sizetRNS > 0)
892 png_process_data(m_png, m_info, m_datatRNS, m_sizetRNS);
893
894 return 0;
895}
896
897int PNGImageDecoder::processingFinish()
898{
899 static png_byte dataIEND[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130};
900
901 if (!m_hasInfo)
902 return 0;
903
904 if (m_totalFrames) {
905 if (setjmp(JMPBUF(m_png)))
906 return 1;
907
908 png_process_data(m_png, m_info, dataIEND, 12);
909 png_destroy_read_struct(&m_png, &m_info, 0);
910 }
911
912 frameComplete();
913 return 0;
914}
915
916void PNGImageDecoder::fallbackNotAnimated()
917{
918 m_isAnimated = false;
919 m_playCount = 0;
920 m_currentFrame = 0;
921}
922#endif
923
924} // namespace WebCore
925