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

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/png/PNGImageDecoder.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
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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
43 #include "wtf/PtrUtil.h" 43 #include "wtf/PtrUtil.h"
44 #include <memory> 44 #include <memory>
45 45
46 namespace blink { 46 namespace blink {
47 47
48 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, 48 PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption,
49 ColorSpaceOption colorOptions, 49 ColorSpaceOption colorOptions,
50 size_t maxDecodedBytes, 50 size_t maxDecodedBytes,
51 size_t offset) 51 size_t offset)
52 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes), 52 : ImageDecoder(alphaOption, colorOptions, maxDecodedBytes),
53 m_offset(offset) {} 53 m_offset(offset),
54 m_currentFrame(0) {}
54 55
55 PNGImageDecoder::~PNGImageDecoder() {} 56 PNGImageDecoder::~PNGImageDecoder() {}
56 57
58 bool PNGImageDecoder::frameIsCompleteAtIndex(size_t index) const {
59 if (!m_reader || failed())
60 return false;
61 if (m_frameBufferCache.size() <= 1)
62 return ImageDecoder::frameIsCompleteAtIndex(index);
63 bool frameIsLoadedAtIndex = index < m_frameBufferCache.size();
64 return frameIsLoadedAtIndex;
65 }
66
67 float PNGImageDecoder::frameDurationAtIndex(size_t index) const {
68 return (index < m_frameBufferCache.size())
69 ? m_frameBufferCache[index].duration()
70 : 0;
71 }
72
73 int PNGImageDecoder::repetitionCount() const {
74 return (!m_reader || failed()) ? cAnimationNone : m_reader->repetitionCount();
75 }
76
77 size_t PNGImageDecoder::clearCacheExceptFrame(size_t clearExceptFrame) {
78 if (clearExceptFrame >= m_frameBufferCache.size() ||
79 m_frameBufferCache.size() <= 1)
80 return 0;
81
82 size_t prevFrame = clearExceptFrame;
83 if (m_frameBufferCache[prevFrame].getDisposalMethod() ==
84 ImageFrame::DisposeOverwritePrevious)
85 prevFrame = m_frameBufferCache[prevFrame].requiredPreviousFrameIndex();
86
87 while (clearExceptFrame != kNotFound &&
88 m_frameBufferCache[clearExceptFrame].getStatus() !=
89 ImageFrame::FrameComplete) {
90 clearExceptFrame =
91 m_frameBufferCache[clearExceptFrame].requiredPreviousFrameIndex();
92 }
93
94 size_t frameBytesCleared = 0;
95 for (size_t i = 0; i < m_frameBufferCache.size(); ++i) {
96 if (i != prevFrame && i != clearExceptFrame) {
97 frameBytesCleared += frameBytesAtIndex(i);
98 clearFrameBuffer(i);
99 }
100 }
101 return frameBytesCleared;
102 }
103
57 inline float pngFixedToFloat(png_fixed_point x) { 104 inline float pngFixedToFloat(png_fixed_point x) {
58 return ((float)x) * 0.00001f; 105 return ((float)x) * 0.00001f;
59 } 106 }
60 107
61 inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) { 108 inline sk_sp<SkColorSpace> readColorSpace(png_structp png, png_infop info) {
62 if (png_get_valid(png, info, PNG_INFO_sRGB)) { 109 if (png_get_valid(png, info, PNG_INFO_sRGB)) {
63 return SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); 110 return SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
64 } 111 }
65 112
66 png_charp name = nullptr; 113 png_charp name = nullptr;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 // However, the current behavior matches Safari and Firefox. 151 // However, the current behavior matches Safari and Firefox.
105 } 152 }
106 } 153 }
107 154
108 return nullptr; 155 return nullptr;
109 } 156 }
110 157
111 void PNGImageDecoder::headerAvailable() { 158 void PNGImageDecoder::headerAvailable() {
112 png_structp png = m_reader->pngPtr(); 159 png_structp png = m_reader->pngPtr();
113 png_infop info = m_reader->infoPtr(); 160 png_infop info = m_reader->infoPtr();
114 png_uint_32 width = png_get_image_width(png, info); 161 png_uint_32 width, height;
115 png_uint_32 height = png_get_image_height(png, info);
116
117 // Protect against large PNGs. See http://bugzil.la/251381 for more details.
118 const unsigned long maxPNGSize = 1000000UL;
119 if (width > maxPNGSize || height > maxPNGSize) {
120 longjmp(JMPBUF(png), 1);
121 return;
122 }
123
124 // Set the image size now that the image header is available.
125 if (!setSize(width, height)) {
126 longjmp(JMPBUF(png), 1);
127 return;
128 }
129
130 int bitDepth, colorType, interlaceType, compressionType, filterType, channels; 162 int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
131 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, 163 png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType,
132 &interlaceType, &compressionType, &filterType); 164 &interlaceType, &compressionType, &filterType);
133 165
134 // The options we set here match what Mozilla does. 166 // The options we set here match what Mozilla does.
135 167
136 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 168 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
137 if (colorType == PNG_COLOR_TYPE_PALETTE || 169 if (colorType == PNG_COLOR_TYPE_PALETTE ||
138 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) 170 (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
139 png_set_expand(png); 171 png_set_expand(png);
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 // Tell libpng to send us rows for interlaced pngs. 225 // Tell libpng to send us rows for interlaced pngs.
194 if (interlaceType == PNG_INTERLACE_ADAM7) 226 if (interlaceType == PNG_INTERLACE_ADAM7)
195 png_set_interlace_handling(png); 227 png_set_interlace_handling(png);
196 228
197 // Update our info now. 229 // Update our info now.
198 png_read_update_info(png, info); 230 png_read_update_info(png, info);
199 channels = png_get_channels(png, info); 231 channels = png_get_channels(png, info);
200 ASSERT(channels == 3 || channels == 4); 232 ASSERT(channels == 3 || channels == 4);
201 233
202 m_reader->setHasAlpha(channels == 4); 234 m_reader->setHasAlpha(channels == 4);
203
204 if (m_reader->decodingSizeOnly()) {
205 // If we only needed the size, halt the reader.
206 #if PNG_LIBPNG_VER_MAJOR > 1 || \
207 (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5)
208 // Passing '0' tells png_process_data_pause() not to cache unprocessed data.
209 m_reader->setReadOffset(m_reader->currentBufferSize() -
210 png_process_data_pause(png, 0));
211 #else
212 m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
213 png->buffer_size = 0;
214 #endif
215 }
216 } 235 }
217 236
218 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, 237 void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer,
219 unsigned rowIndex, 238 unsigned rowIndex,
220 int) { 239 int) {
221 if (m_frameBufferCache.isEmpty()) 240 if (m_frameBufferCache.isEmpty())
222 return; 241 return;
223 242
224 // Initialize the framebuffer if needed. 243 // Initialize the framebuffer if needed.
225 ImageFrame& buffer = m_frameBufferCache[0]; 244 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
226 if (buffer.getStatus() == ImageFrame::FrameEmpty) { 245 if (buffer.getStatus() == ImageFrame::FrameEmpty) {
227 png_structp png = m_reader->pngPtr(); 246 png_structp png = m_reader->pngPtr();
228 if (!buffer.setSizeAndColorSpace(size().width(), size().height(), 247 if (!buffer.setSizeAndColorSpace(size().width(), size().height(),
229 colorSpace())) { 248 colorSpace())) {
230 longjmp(JMPBUF(png), 1); 249 longjmp(JMPBUF(png), 1);
231 return; 250 return;
232 } 251 }
233 252
234 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; 253 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3;
235 if (PNG_INTERLACE_ADAM7 == 254 if (PNG_INTERLACE_ADAM7 ==
236 png_get_interlace_type(png, m_reader->infoPtr())) { 255 png_get_interlace_type(png, m_reader->infoPtr()) ||
237 m_reader->createInterlaceBuffer(colorChannels * size().width() * 256 m_currentFrame) {
238 size().height()); 257 if (!m_reader->interlaceBuffer()) {
258 m_reader->createInterlaceBuffer(colorChannels * size().width() *
259 size().height());
260 }
239 if (!m_reader->interlaceBuffer()) { 261 if (!m_reader->interlaceBuffer()) {
240 longjmp(JMPBUF(png), 1); 262 longjmp(JMPBUF(png), 1);
241 return; 263 return;
242 } 264 }
243 } 265 }
244 266
245 buffer.setStatus(ImageFrame::FramePartial); 267 buffer.setStatus(ImageFrame::FramePartial);
246 buffer.setHasAlpha(false); 268 buffer.setHasAlpha(false);
247 269
248 // For PNGs, the frame always fills the entire image. 270 if (!initFrameBuffer()) {
249 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); 271 longjmp(JMPBUF(png), 1);
272 return;
273 }
250 } 274 }
251 275
252 /* libpng comments (here to explain what follows). 276 /* libpng comments (here to explain what follows).
253 * 277 *
254 * this function is called for every row in the image. If the 278 * this function is called for every row in the image. If the
255 * image is interlacing, and you turned on the interlace handler, 279 * image is interlacing, and you turned on the interlace handler,
256 * this function will be called for every row in every pass. 280 * this function will be called for every row in every pass.
257 * Some of these rows will not be changed from the previous pass. 281 * Some of these rows will not be changed from the previous pass.
258 * When the row is not changed, the new_row variable will be NULL. 282 * When the row is not changed, the new_row variable will be NULL.
259 * The rows and passes are called in order, so you don't really 283 * 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
291 315
292 bool hasAlpha = m_reader->hasAlpha(); 316 bool hasAlpha = m_reader->hasAlpha();
293 png_bytep row = rowBuffer; 317 png_bytep row = rowBuffer;
294 318
295 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { 319 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) {
296 unsigned colorChannels = hasAlpha ? 4 : 3; 320 unsigned colorChannels = hasAlpha ? 4 : 3;
297 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); 321 row = interlaceBuffer + (rowIndex * colorChannels * size().width());
298 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); 322 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer);
299 } 323 }
300 324
325 // Only do incremental image display for the first frame.
326 if (m_currentFrame)
327 return;
328
301 // Write the decoded row pixels to the frame buffer. The repetitive 329 // Write the decoded row pixels to the frame buffer. The repetitive
302 // form of the row write loops is for speed. 330 // form of the row write loops is for speed.
303 ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y); 331 ImageFrame::PixelData* const dstRow = buffer.getAddr(0, y);
304 unsigned alphaMask = 255; 332 unsigned alphaMask = 255;
305 int width = size().width(); 333 int width = size().width();
306 334
307 png_bytep srcPtr = row; 335 png_bytep srcPtr = row;
308 if (hasAlpha) { 336 if (hasAlpha) {
309 // Here we apply the color space transformation to the dst space. 337 // Here we apply the color space transformation to the dst space.
310 // It does not really make sense to transform to a gamma-encoded 338 // It does not really make sense to transform to a gamma-encoded
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 if (alphaMask != 255 && !buffer.hasAlpha()) 384 if (alphaMask != 255 && !buffer.hasAlpha())
357 buffer.setHasAlpha(true); 385 buffer.setHasAlpha(true);
358 386
359 buffer.setPixelsChanged(true); 387 buffer.setPixelsChanged(true);
360 } 388 }
361 389
362 void PNGImageDecoder::complete() { 390 void PNGImageDecoder::complete() {
363 if (m_frameBufferCache.isEmpty()) 391 if (m_frameBufferCache.isEmpty())
364 return; 392 return;
365 393
366 m_frameBufferCache[0].setStatus(ImageFrame::FrameComplete); 394 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
367 } 395 buffer.setStatus(ImageFrame::FrameComplete);
368 396
369 inline bool isComplete(const PNGImageDecoder* decoder) { 397 if (!m_currentFrame)
370 return decoder->frameIsCompleteAtIndex(0); 398 return;
371 } 399
372 400 const IntRect& rect = buffer.originalFrameRect();
373 void PNGImageDecoder::decode(bool onlySize) { 401 bool hasAlpha = m_reader->hasAlpha();
402 unsigned colorChannels = hasAlpha ? 4 : 3;
403 unsigned alphaMask = 255;
404 ImageFrame::AlphaBlendSource blendMethod = buffer.getAlphaBlendSource();
405
406 if (blendMethod == ImageFrame::BlendAtopPreviousFrame && !hasAlpha)
407 blendMethod = ImageFrame::BlendAtopBgcolor;
408
409 png_bytep row = m_reader->interlaceBuffer();
410 for (int y = rect.y(); y < rect.maxY();
411 ++y, row += colorChannels * size().width()) {
412 png_bytep srcPtr = row;
413 ImageFrame::PixelData* dstRow = buffer.getAddr(rect.x(), y);
414 if (hasAlpha) {
415 if (SkColorSpaceXform* xform = colorTransform()) {
416 SkColorSpaceXform::ColorFormat colorFormat =
417 SkColorSpaceXform::kRGBA_8888_ColorFormat;
418 xform->apply(colorFormat, srcPtr, colorFormat, srcPtr, rect.width(),
419 kUnpremul_SkAlphaType);
420 }
421
422 if (blendMethod == ImageFrame::BlendAtopBgcolor) {
423 if (buffer.premultiplyAlpha()) {
424 for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width();
425 dstPixel++, srcPtr += 4) {
426 buffer.setRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
427 srcPtr[3]);
428 alphaMask &= srcPtr[3];
429 }
430 } else {
431 for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width();
432 dstPixel++, srcPtr += 4) {
433 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
434 srcPtr[3]);
435 alphaMask &= srcPtr[3];
436 }
437 }
438 } else {
439 if (buffer.premultiplyAlpha()) {
440 for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width();
441 dstPixel++, srcPtr += 4) {
442 buffer.overRGBAPremultiply(dstPixel, srcPtr[0], srcPtr[1],
443 srcPtr[2], srcPtr[3]);
444 alphaMask &= srcPtr[3];
445 }
446 } else {
447 for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width();
448 dstPixel++, srcPtr += 4) {
449 buffer.overRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2],
450 srcPtr[3]);
451 alphaMask &= srcPtr[3];
452 }
453 }
454 }
455 } else {
456 for (auto *dstPixel = dstRow; dstPixel < dstRow + rect.width();
457 dstPixel++, srcPtr += 3) {
458 buffer.setRGBARaw(dstPixel, srcPtr[0], srcPtr[1], srcPtr[2], 255);
459 }
460
461 if (SkColorSpaceXform* xform = colorTransform()) {
462 xform->apply(xformColorFormat(), dstRow, xformColorFormat(), dstRow,
463 rect.width(), kOpaque_SkAlphaType);
464 }
465 }
466 }
467
468 if (alphaMask == 255) {
469 if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) {
470 buffer.setHasAlpha(false);
471 } else {
472 size_t frameIndex = m_currentFrame;
473 const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex];
474 while (frameIndex && (prevBuffer->getDisposalMethod() ==
475 ImageFrame::DisposeOverwritePrevious))
476 prevBuffer = &m_frameBufferCache[--frameIndex];
477 if ((prevBuffer->getDisposalMethod() ==
478 ImageFrame::DisposeOverwriteBgcolor) &&
479 !prevBuffer->hasAlpha() &&
480 buffer.originalFrameRect().contains(prevBuffer->originalFrameRect()))
481 buffer.setHasAlpha(false);
482 }
483 } else if (blendMethod == ImageFrame::BlendAtopBgcolor &&
484 !buffer.hasAlpha()) {
485 buffer.setHasAlpha(true);
486 }
487 }
488
489 size_t PNGImageDecoder::decodeFrameCount() {
490 return parse() ? m_reader->imagesCount() : m_frameBufferCache.size();
491 }
492
493 void PNGImageDecoder::decode(size_t index) {
494 if (!m_reader || failed())
495 return;
496
497 Vector<size_t> framesToDecode;
498 size_t frameToDecode = index;
499 do {
500 framesToDecode.append(frameToDecode);
501 frameToDecode =
502 m_frameBufferCache[frameToDecode].requiredPreviousFrameIndex();
503 } while (frameToDecode != kNotFound &&
504 m_frameBufferCache[frameToDecode].getStatus() !=
505 ImageFrame::FrameComplete);
506
507 for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); ++i) {
508 m_currentFrame = *i;
509 if (!m_reader->decode(*m_data, m_currentFrame)) {
510 setFailed();
511 break;
512 }
513
514 // We need more data to continue decoding.
515 if (m_frameBufferCache[m_currentFrame].getStatus() !=
516 ImageFrame::FrameComplete)
517 break;
518 }
519 }
520
521 void PNGImageDecoder::initializeNewFrame(size_t index) {
522 ImageFrame* buffer = &m_frameBufferCache[index];
523 bool frameRectIsOpaque = buffer->getStatus() == ImageFrame::FrameComplete
524 ? !m_reader->hasAlpha()
525 : false;
526 buffer->setRequiredPreviousFrameIndex(
527 findRequiredPreviousFrame(index, frameRectIsOpaque));
528 const PNGImageReader::FrameInfo* frame = m_reader->frameInfo(index);
529 IntRect frameRect(frame->xOffset, frame->yOffset, frame->width,
530 frame->height);
531 buffer->setOriginalFrameRect(
532 intersection(frameRect, IntRect(IntPoint(), size())));
533 buffer->setDuration(frame->duration);
534 buffer->setDisposalMethod(frame->dispose == 2
535 ? ImageFrame::DisposeOverwritePrevious
536 : frame->dispose == 1
537 ? ImageFrame::DisposeOverwriteBgcolor
538 : ImageFrame::DisposeKeep);
539 buffer->setAlphaBlendSource(frame->blend == 1
540 ? ImageFrame::BlendAtopPreviousFrame
541 : ImageFrame::BlendAtopBgcolor);
542 }
543
544 bool PNGImageDecoder::initFrameBuffer() {
545 ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
546
547 const size_t requiredPreviousFrameIndex = buffer.requiredPreviousFrameIndex();
548 if (requiredPreviousFrameIndex == kNotFound) {
549 // This frame doesn't rely on any previous data.
550 buffer.zeroFillPixelData();
551 if (!m_currentFrame)
552 buffer.setHasAlpha(false);
553 } else {
554 ImageFrame& prevBuffer = m_frameBufferCache[requiredPreviousFrameIndex];
555 DCHECK(prevBuffer.getStatus() == ImageFrame::FrameComplete);
556
557 // Preserve the last frame as the starting state for this frame.
558 if (!buffer.takeBitmapDataIfWritable(&prevBuffer)) {
559 if (!buffer.copyBitmapData(prevBuffer))
560 return setFailed();
561 }
562
563 if (prevBuffer.getDisposalMethod() == ImageFrame::DisposeOverwriteBgcolor) {
564 // We want to clear the previous frame to transparent, without
565 // affecting pixels in the image outside of the frame.
566 const IntRect& prevRect = prevBuffer.originalFrameRect();
567 DCHECK(!prevRect.contains(IntRect(IntPoint(), size())));
568 buffer.zeroFillFrameRect(prevRect);
569 }
570 }
571 return true;
572 }
573
574 bool PNGImageDecoder::parse() {
374 if (failed()) 575 if (failed())
375 return; 576 return false;
376 577
377 if (!m_reader) 578 if (!m_reader)
378 m_reader = wrapUnique(new PNGImageReader(this, m_offset)); 579 m_reader = wrapUnique(new PNGImageReader(this, m_offset));
379 580
380 // If we couldn't decode the image but have received all the data, decoding 581 // If we couldn't decode the image but have received all the data, decoding
381 // has failed. 582 // has failed.
382 if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) 583 if (!m_reader->parse(*m_data) && isAllDataReceived())
383 setFailed(); 584 setFailed();
384 585
385 // If decoding is done or failed, we don't need the PNGImageReader anymore. 586 if (failed()) {
386 if (isComplete(this) || failed())
387 m_reader.reset(); 587 m_reader.reset();
588 return false;
589 }
590 return true;
388 } 591 }
389 592
390 } // namespace blink 593 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698