Changeset 181553 in webkit
- Timestamp:
- Mar 16, 2015 7:24:46 AM (9 years ago)
- Location:
- trunk
- Files:
-
- 26 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r181526 r181553 1 2015-03-16 Max Stepin <maxstepin@gmail.com> 2 3 Add APNG support 4 https://bugs.webkit.org/show_bug.cgi?id=17022 5 6 Reviewed by Carlos Garcia Campos. 7 8 * fast/images/animated-png-expected.html: Added. 9 * fast/images/animated-png.html: Added. 10 * fast/images/resources/apng00-ref.png: Added. 11 * fast/images/resources/apng00.png: Added. 12 * fast/images/resources/apng01-ref.png: Added. 13 * fast/images/resources/apng01.png: Added. 14 * fast/images/resources/apng02-ref.png: Added. 15 * fast/images/resources/apng02.png: Added. 16 * fast/images/resources/apng04-ref.png: Added. 17 * fast/images/resources/apng04.png: Added. 18 * fast/images/resources/apng08-ref.png: Added. 19 * fast/images/resources/apng08.png: Added. 20 * fast/images/resources/apng10-ref.png: Added. 21 * fast/images/resources/apng10.png: Added. 22 * fast/images/resources/apng11-ref.png: Added. 23 * fast/images/resources/apng11.png: Added. 24 * fast/images/resources/apng12-ref.png: Added. 25 * fast/images/resources/apng12.png: Added. 26 * fast/images/resources/apng14-ref.png: Added. 27 * fast/images/resources/apng14.png: Added. 28 * fast/images/resources/apng18-ref.png: Added. 29 * fast/images/resources/apng18.png: Added. 30 * fast/images/resources/apng24-ref.png: Added. 31 * fast/images/resources/apng24.png: Added. 32 * fast/images/resources/apng26-ref.png: Added. 33 * fast/images/resources/apng26.png: Added. 34 * platform/mac/TestExpectations: 35 1 36 2015-03-16 Gyuyoung Kim <gyuyoung.kim@samsung.com> 2 37 -
trunk/LayoutTests/platform/mac/TestExpectations
r181450 r181553 1274 1274 # Test uses Yosemite blurs 1275 1275 [ Mavericks ] compositing/media-controls-bar-appearance.html [ Skip ] 1276 1277 # Mavericks and prior do not support APNG. 1278 webkit.org/b/17022 [ MountainLion Mavericks ] fast/images/animated-png.html [ Skip ] -
trunk/Source/WTF/ChangeLog
r181525 r181553 1 2015-03-16 Max Stepin <maxstepin@gmail.com> 2 3 Add APNG support 4 https://bugs.webkit.org/show_bug.cgi?id=17022 5 6 Reviewed by Carlos Garcia Campos. 7 8 * wtf/FeatureDefines.h: 9 1 10 2015-03-15 Benjamin Poulain <benjamin@webkit.org> 2 11 -
trunk/Source/WTF/wtf/FeatureDefines.h
r179687 r181553 308 308 #endif 309 309 310 #if !defined(ENABLE_APNG) 311 #define ENABLE_APNG 1 312 #endif 313 310 314 #if !defined(ENABLE_BATTERY_STATUS) 311 315 #define ENABLE_BATTERY_STATUS 0 -
trunk/Source/WebCore/ChangeLog
r181525 r181553 1 2015-03-16 Max Stepin <maxstepin@gmail.com> 2 3 Add APNG support 4 https://bugs.webkit.org/show_bug.cgi?id=17022 5 6 Reviewed by Carlos Garcia Campos. 7 8 Test: fast/images/animated-png.html 9 10 * platform/image-decoders/ImageDecoder.h: 11 (WebCore::ImageFrame::divide255): 12 (WebCore::ImageFrame::overRGBA): 13 * platform/image-decoders/png/PNGImageDecoder.cpp: 14 (WebCore::frameHeader): 15 (WebCore::readChunks): 16 (WebCore::PNGImageReader::PNGImageReader): 17 (WebCore::PNGImageDecoder::PNGImageDecoder): 18 (WebCore::PNGImageDecoder::frameBufferAtIndex): 19 (WebCore::PNGImageDecoder::headerAvailable): 20 (WebCore::PNGImageDecoder::rowAvailable): 21 (WebCore::PNGImageDecoder::pngComplete): 22 (WebCore::PNGImageDecoder::readChunks): 23 (WebCore::PNGImageDecoder::frameHeader): 24 (WebCore::PNGImageDecoder::init): 25 (WebCore::PNGImageDecoder::clearFrameBufferCache): 26 (WebCore::PNGImageDecoder::initFrameBuffer): 27 (WebCore::PNGImageDecoder::frameComplete): 28 (WebCore::PNGImageDecoder::processingStart): 29 (WebCore::PNGImageDecoder::processingFinish): 30 (WebCore::PNGImageDecoder::fallbackNotAnimated): 31 * platform/image-decoders/png/PNGImageDecoder.h: 32 (WebCore::PNGImageDecoder::frameCount): 33 (WebCore::PNGImageDecoder::repetitionCount): 34 (WebCore::PNGImageDecoder::isComplete): 35 1 36 2015-03-15 Benjamin Poulain <benjamin@webkit.org> 2 37 -
trunk/Source/WebCore/platform/image-decoders/ImageDecoder.h
r167760 r181553 166 166 } 167 167 168 #if ENABLE(APNG) 169 static inline unsigned divide255(unsigned a) 170 { 171 return (a + (a >> 8) + 1) >> 8; 172 } 173 174 inline void overRGBA(PixelData* dest, unsigned r, unsigned g, unsigned b, unsigned a) 175 { 176 if (!a) 177 return; 178 179 if (a < 255) { 180 unsigned aDest = ((*dest) >> 24) & 255; 181 if (aDest) { 182 unsigned rDest = ((*dest) >> 16) & 255; 183 unsigned gDest = ((*dest) >> 8) & 255; 184 unsigned bDest = (*dest) & 255; 185 unsigned aAux = 255 - a; 186 if (!m_premultiplyAlpha) { 187 rDest = divide255(rDest * aDest); 188 gDest = divide255(gDest * aDest); 189 bDest = divide255(bDest * aDest); 190 } 191 r = divide255(r * a + rDest * aAux); 192 g = divide255(g * a + gDest * aAux); 193 b = divide255(b * a + bDest * aAux); 194 a += divide255(aDest * aAux); 195 if (!m_premultiplyAlpha) { 196 r = (r * 255 + a - 1) / a; 197 g = (g * 255 + a - 1) / a; 198 b = (b * 255 + a - 1) / a; 199 } 200 } else if (m_premultiplyAlpha) { 201 r = divide255(r * a); 202 g = divide255(g * a); 203 b = divide255(b * a); 204 } 205 } 206 *dest = (a << 24 | r << 16 | g << 8 | b); 207 } 208 #endif 209 168 210 private: 169 211 int width() const -
trunk/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp
r178166 r181553 8 8 * Other contributors: 9 9 * Stuart Parmenter <stuart@mozilla.com> 10 * Max Stepin <maxstepin@gmail.com> 10 11 * 11 12 * This library is free software; you can redistribute it and/or … … 101 102 } 102 103 104 #if ENABLE(APNG) 105 // Called when we have the frame header. 106 static void PNGAPI frameHeader(png_structp png, png_infop) 107 { 108 static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->frameHeader(); 109 } 110 111 // Called when we found user chunks. 112 static int PNGAPI readChunks(png_structp png, png_unknown_chunkp chunk) 113 { 114 static_cast<PNGImageDecoder*>(png_get_user_chunk_ptr(png))->readChunks(chunk); 115 return 1; 116 } 117 #endif 118 103 119 class PNGImageReader { 104 120 WTF_MAKE_FAST_ALLOCATED; … … 118 134 m_info = png_create_info_struct(m_png); 119 135 png_set_progressive_read_fn(m_png, decoder, headerAvailable, rowAvailable, pngComplete); 136 #if ENABLE(APNG) 137 png_byte apngChunks[]= {"acTL\0fcTL\0fdAT\0"}; 138 png_set_keep_unknown_chunks(m_png, 1, apngChunks, 3); 139 png_set_read_user_chunk_fn(m_png, static_cast<png_voidp>(decoder), readChunks); 140 decoder->init(); 141 #endif 120 142 } 121 143 … … 216 238 }; 217 239 218 PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption, 219 ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) 240 PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) 220 241 : ImageDecoder(alphaOption, gammaAndColorProfileOption) 221 242 , m_doNothingOnFailure(false) 243 , m_currentFrame(0) 244 #if ENABLE(APNG) 245 , m_png(nullptr) 246 , m_info(nullptr) 247 , m_isAnimated(false) 248 , m_frameInfo(false) 249 , m_frameIsHidden(false) 250 , m_hasInfo(false) 251 , m_gamma(45455) 252 , m_frameCount(1) 253 , m_playCount(0) 254 , m_totalFrames(0) 255 , m_sizePLTE(0) 256 , m_sizetRNS(0) 257 , m_sequenceNumber(0) 258 , m_width(0) 259 , m_height(0) 260 , m_xOffset(0) 261 , m_yOffset(0) 262 , m_delayNumerator(1) 263 , m_delayDenominator(1) 264 , m_dispose(0) 265 , m_blend(0) 266 #endif 222 267 { 223 268 } … … 246 291 ImageFrame* PNGImageDecoder::frameBufferAtIndex(size_t index) 247 292 { 293 #if ENABLE(APNG) 294 if (!isSizeAvailable()) 295 return nullptr; 296 297 if (index >= frameCount()) 298 index = frameCount() - 1; 299 #else 248 300 if (index) 249 return 0; 301 return nullptr; 302 #endif 250 303 251 304 if (m_frameBufferCache.isEmpty()) { … … 254 307 } 255 308 256 ImageFrame& frame = m_frameBufferCache[ 0];309 ImageFrame& frame = m_frameBufferCache[index]; 257 310 if (frame.status() != ImageFrame::FrameComplete) 258 311 decode(false); … … 330 383 // The options we set here match what Mozilla does. 331 384 385 #if ENABLE(APNG) 386 m_hasInfo = true; 387 if (m_isAnimated) { 388 png_save_uint_32(m_dataIHDR, 13); 389 memcpy(m_dataIHDR + 4, "IHDR", 4); 390 png_save_uint_32(m_dataIHDR + 8, width); 391 png_save_uint_32(m_dataIHDR + 12, height); 392 m_dataIHDR[16] = bitDepth; 393 m_dataIHDR[17] = colorType; 394 m_dataIHDR[18] = compressionType; 395 m_dataIHDR[19] = filterType; 396 m_dataIHDR[20] = interlaceType; 397 } 398 #endif 399 332 400 // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. 333 if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) 401 if (colorType == PNG_COLOR_TYPE_PALETTE) { 402 #if ENABLE(APNG) 403 if (m_isAnimated) { 404 png_colorp palette; 405 int paletteSize = 0; 406 png_get_PLTE(png, info, &palette, &paletteSize); 407 paletteSize *= 3; 408 png_save_uint_32(m_dataPLTE, paletteSize); 409 memcpy(m_dataPLTE + 4, "PLTE", 4); 410 memcpy(m_dataPLTE + 8, palette, paletteSize); 411 m_sizePLTE = paletteSize + 12; 412 } 413 #endif 414 png_set_expand(png); 415 } 416 417 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) 334 418 png_set_expand(png); 335 419 336 420 png_bytep trns = 0; 337 421 int trnsCount = 0; 422 png_color_16p transValues; 338 423 if (png_get_valid(png, info, PNG_INFO_tRNS)) { 339 png_get_tRNS(png, info, &trns, &trnsCount, 0); 424 png_get_tRNS(png, info, &trns, &trnsCount, &transValues); 425 #if ENABLE(APNG) 426 if (m_isAnimated) { 427 if (colorType == PNG_COLOR_TYPE_RGB) { 428 png_save_uint_16(m_datatRNS + 8, transValues->red); 429 png_save_uint_16(m_datatRNS + 10, transValues->green); 430 png_save_uint_16(m_datatRNS + 12, transValues->blue); 431 trnsCount = 6; 432 } else if (colorType == PNG_COLOR_TYPE_GRAY) { 433 png_save_uint_16(m_datatRNS + 8, transValues->gray); 434 trnsCount = 2; 435 } else if (colorType == PNG_COLOR_TYPE_PALETTE) 436 memcpy(m_datatRNS + 8, trns, trnsCount); 437 438 png_save_uint_32(m_datatRNS, trnsCount); 439 memcpy(m_datatRNS + 4, "tRNS", 4); 440 m_sizetRNS = trnsCount + 12; 441 } 442 #endif 340 443 png_set_expand(png); 341 444 } … … 370 473 } 371 474 png_set_gamma(png, cDefaultGamma, gamma); 475 #if ENABLE(APNG) 476 m_gamma = static_cast<int>(gamma * 100000); 477 #endif 372 478 } else 373 479 png_set_gamma(png, cDefaultGamma, cInverseGamma); … … 425 531 426 532 // Initialize the framebuffer if needed. 427 ImageFrame& buffer = m_frameBufferCache[0]; 533 #if ENABLE(APNG) 534 if (m_currentFrame >= frameCount()) 535 return; 536 #endif 537 ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; 428 538 if (buffer.status() == ImageFrame::FrameEmpty) { 429 539 png_structp png = m_reader->pngPtr(); … … 434 544 435 545 unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; 436 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) { 437 m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); 546 if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr()) 547 || m_currentFrame) { 548 if (!m_reader->interlaceBuffer()) 549 m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); 438 550 if (!m_reader->interlaceBuffer()) { 439 551 longjmp(JMPBUF(png), 1); … … 443 555 444 556 #if USE(QCMSLIB) 445 if (m_reader->colorTransform() ) {557 if (m_reader->colorTransform() && !m_currentFrame) { 446 558 m_reader->createRowBuffer(colorChannels * size().width()); 447 559 if (!m_reader->rowBuffer()) { … … 455 567 buffer.setColorProfile(m_colorProfile); 456 568 569 #if ENABLE(APNG) 570 if (m_currentFrame) 571 initFrameBuffer(m_currentFrame); 572 else 573 #endif 457 574 // For PNGs, the frame always fills the entire image. 458 575 buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); … … 505 622 if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { 506 623 row = interlaceBuffer + (rowIndex * colorChannels * size().width()); 624 #if ENABLE(APNG) 625 if (m_currentFrame) { 626 png_progressive_combine_row(m_png, row, rowBuffer); 627 return; // Only do incremental image display for the first frame. 628 } 629 #endif 507 630 png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); 508 631 } … … 553 676 void PNGImageDecoder::pngComplete() 554 677 { 678 #if ENABLE(APNG) 679 if (m_isAnimated) { 680 if (!processingFinish() && m_frameCount == m_currentFrame) 681 return; 682 683 fallbackNotAnimated(); 684 } 685 #endif 555 686 if (!m_frameBufferCache.isEmpty()) 556 687 m_frameBufferCache.first().setStatus(ImageFrame::FrameComplete); … … 575 706 } 576 707 708 #if ENABLE(APNG) 709 void PNGImageDecoder::readChunks(png_unknown_chunkp chunk) 710 { 711 if (!memcmp(chunk->name, "acTL", 4) && chunk->size == 8) { 712 if (m_hasInfo || m_isAnimated) 713 return; 714 715 m_frameCount = png_get_uint_32(chunk->data); 716 m_playCount = png_get_uint_32(chunk->data + 4); 717 718 if (!m_frameCount || m_frameCount > PNG_UINT_31_MAX || m_playCount > PNG_UINT_31_MAX) { 719 fallbackNotAnimated(); 720 return; 721 } 722 723 m_isAnimated = true; 724 if (!m_frameInfo) 725 m_frameIsHidden = true; 726 727 if (m_frameBufferCache.size() == m_frameCount) 728 return; 729 730 m_frameBufferCache.resize(m_frameCount); 731 for (auto& imageFrame : m_frameBufferCache) 732 imageFrame.setPremultiplyAlpha(m_premultiplyAlpha); 733 } else if (!memcmp(chunk->name, "fcTL", 4) && chunk->size == 26) { 734 if (m_hasInfo && !m_isAnimated) 735 return; 736 737 m_frameInfo = false; 738 739 if (processingFinish()) { 740 fallbackNotAnimated(); 741 return; 742 } 743 744 // At this point the old frame is done. Let's start a new one. 745 unsigned sequenceNumber = png_get_uint_32(chunk->data); 746 if (sequenceNumber != m_sequenceNumber++) { 747 fallbackNotAnimated(); 748 return; 749 } 750 751 m_width = png_get_uint_32(chunk->data + 4); 752 m_height = png_get_uint_32(chunk->data + 8); 753 m_xOffset = png_get_uint_32(chunk->data + 12); 754 m_yOffset = png_get_uint_32(chunk->data + 16); 755 m_delayNumerator = png_get_uint_16(chunk->data + 20); 756 m_delayDenominator = png_get_uint_16(chunk->data + 22); 757 m_dispose = chunk->data[24]; 758 m_blend = chunk->data[25]; 759 760 png_structp png = m_reader->pngPtr(); 761 png_infop info = m_reader->infoPtr(); 762 png_uint_32 width = png_get_image_width(png, info); 763 png_uint_32 height = png_get_image_height(png, info); 764 765 if (m_width > cMaxPNGSize || m_height > cMaxPNGSize 766 || m_xOffset > cMaxPNGSize || m_yOffset > cMaxPNGSize 767 || m_xOffset + m_width > width 768 || m_yOffset + m_height > height 769 || m_dispose > 2 || m_blend > 1) { 770 fallbackNotAnimated(); 771 return; 772 } 773 774 m_frameInfo = true; 775 m_frameIsHidden = false; 776 777 if (processingStart(chunk)) { 778 fallbackNotAnimated(); 779 return; 780 } 781 } else if (!memcmp(chunk->name, "fdAT", 4) && chunk->size >= 4) { 782 if (!m_frameInfo || !m_isAnimated) 783 return; 784 785 unsigned sequenceNumber = png_get_uint_32(chunk->data); 786 if (sequenceNumber != m_sequenceNumber++) { 787 fallbackNotAnimated(); 788 return; 789 } 790 791 if (setjmp(JMPBUF(m_png))) { 792 fallbackNotAnimated(); 793 return; 794 } 795 796 png_save_uint_32(chunk->data, chunk->size - 4); 797 png_process_data(m_png, m_info, chunk->data, 4); 798 memcpy(chunk->data, "IDAT", 4); 799 png_process_data(m_png, m_info, chunk->data, chunk->size); 800 png_process_data(m_png, m_info, chunk->data, 4); 801 } 802 } 803 804 void PNGImageDecoder::frameHeader() 805 { 806 int colorType = png_get_color_type(m_png, m_info); 807 808 if (colorType == PNG_COLOR_TYPE_PALETTE) 809 png_set_expand(m_png); 810 811 int bitDepth = png_get_bit_depth(m_png, m_info); 812 if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) 813 png_set_expand(m_png); 814 815 if (png_get_valid(m_png, m_info, PNG_INFO_tRNS)) 816 png_set_expand(m_png); 817 818 if (bitDepth == 16) 819 png_set_strip_16(m_png); 820 821 if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) 822 png_set_gray_to_rgb(m_png); 823 824 double gamma; 825 if (png_get_gAMA(m_png, m_info, &gamma)) 826 png_set_gamma(m_png, cDefaultGamma, gamma); 827 828 png_set_interlace_handling(m_png); 829 830 png_read_update_info(m_png, m_info); 831 } 832 833 void PNGImageDecoder::init() 834 { 835 m_isAnimated = false; 836 m_frameInfo = false; 837 m_frameIsHidden = false; 838 m_hasInfo = false; 839 m_currentFrame = 0; 840 m_totalFrames = 0; 841 m_sequenceNumber = 0; 842 } 843 844 void PNGImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) 845 { 846 if (m_frameBufferCache.isEmpty()) 847 return; 848 849 // See GIFImageDecoder for full explanation. 850 clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1); 851 const Vector<ImageFrame>::iterator end(m_frameBufferCache.begin() + clearBeforeFrame); 852 853 Vector<ImageFrame>::iterator i(end); 854 for (; (i != m_frameBufferCache.begin()) && ((i->status() == ImageFrame::FrameEmpty) || (i->disposalMethod() == ImageFrame::DisposeOverwritePrevious)); --i) { 855 if ((i->status() == ImageFrame::FrameComplete) && (i != end)) 856 i->clearPixelData(); 857 } 858 859 // Now |i| holds the last frame we need to preserve; clear prior frames. 860 for (Vector<ImageFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j) { 861 ASSERT(j->status() != ImageFrame::FramePartial); 862 if (j->status() != ImageFrame::FrameEmpty) 863 j->clearPixelData(); 864 } 865 } 866 867 void PNGImageDecoder::initFrameBuffer(size_t frameIndex) 868 { 869 if (frameIndex >= frameCount()) 870 return; 871 872 IntRect frameRect(m_xOffset, m_yOffset, m_width, m_height); 873 874 // Make sure the frameRect doesn't extend outside the buffer. 875 if (frameRect.maxX() > size().width()) 876 frameRect.setWidth(size().width() - m_xOffset); 877 if (frameRect.maxY() > size().height()) 878 frameRect.setHeight(size().height() - m_yOffset); 879 880 ImageFrame& buffer = m_frameBufferCache[frameIndex]; 881 int left = upperBoundScaledX(frameRect.x()); 882 int right = lowerBoundScaledX(frameRect.maxX(), left); 883 int top = upperBoundScaledY(frameRect.y()); 884 int bottom = lowerBoundScaledY(frameRect.maxY(), top); 885 buffer.setOriginalFrameRect(IntRect(left, top, right - left, bottom - top)); 886 887 // The starting state for this frame depends on the previous frame's 888 // disposal method. 889 // 890 // Frames that use the DisposeOverwritePrevious method are effectively 891 // no-ops in terms of changing the starting state of a frame compared to 892 // the starting state of the previous frame, so skip over them. (If the 893 // first frame specifies this method, it will get treated like 894 // DisposeOverwriteBgcolor below and reset to a completely empty image.) 895 const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex]; 896 ImageFrame::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod(); 897 while (frameIndex && (prevMethod == ImageFrame::DisposeOverwritePrevious)) { 898 prevBuffer = &m_frameBufferCache[--frameIndex]; 899 prevMethod = prevBuffer->disposalMethod(); 900 } 901 ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); 902 903 if (prevMethod == ImageFrame::DisposeKeep) { 904 // Preserve the last frame as the starting state for this frame. 905 buffer.copyBitmapData(*prevBuffer); 906 } else { 907 // We want to clear the previous frame to transparent, without 908 // affecting pixels in the image outside of the frame. 909 const IntRect& prevRect = prevBuffer->originalFrameRect(); 910 if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) { 911 // Clearing the first frame, or a frame the size of the whole 912 // image, results in a completely empty image. 913 buffer.zeroFillPixelData(); 914 } else { 915 // Copy the whole previous buffer, then clear just its frame. 916 buffer.copyBitmapData(*prevBuffer); 917 buffer.zeroFillFrameRect(prevRect); 918 } 919 } 920 } 921 922 void PNGImageDecoder::frameComplete() 923 { 924 if (m_frameIsHidden || m_currentFrame >= frameCount()) 925 return; 926 927 ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; 928 buffer.setStatus(ImageFrame::FrameComplete); 929 930 if (!m_delayDenominator) 931 buffer.setDuration(m_delayNumerator * 10); 932 else 933 buffer.setDuration(m_delayNumerator * 1000 / m_delayDenominator); 934 935 if (m_dispose == 2) 936 buffer.setDisposalMethod(ImageFrame::DisposeOverwritePrevious); 937 else if (m_dispose == 1) 938 buffer.setDisposalMethod(ImageFrame::DisposeOverwriteBgcolor); 939 else 940 buffer.setDisposalMethod(ImageFrame::DisposeKeep); 941 942 png_bytep interlaceBuffer = m_reader->interlaceBuffer(); 943 944 if (m_currentFrame && interlaceBuffer) { 945 const IntRect& rect = buffer.originalFrameRect(); 946 bool hasAlpha = m_reader->hasAlpha(); 947 unsigned colorChannels = hasAlpha ? 4 : 3; 948 bool nonTrivialAlpha = false; 949 if (m_blend && !hasAlpha) 950 m_blend = 0; 951 952 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) 953 for (int y = 0; y < rect.maxY() - rect.y(); ++y) { 954 png_bytep row = interlaceBuffer + (m_scaled ? m_scaledRows[y] : y) * colorChannels * size().width(); 955 #if USE(QCMSLIB) 956 if (qcms_transform* transform = m_reader->colorTransform()) { 957 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); 958 row = m_reader->rowBuffer(); 959 } 960 #endif 961 ImageFrame::PixelData* address = buffer.getAddr(rect.x(), y + rect.y()); 962 for (int x = 0; x < rect.maxX() - rect.x(); ++x) { 963 png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels; 964 unsigned alpha = hasAlpha ? pixel[3] : 255; 965 nonTrivialAlpha |= alpha < 255; 966 if (!m_blend) 967 buffer.setRGBA(address++, pixel[0], pixel[1], pixel[2], alpha); 968 else 969 buffer.overRGBA(address++, pixel[0], pixel[1], pixel[2], alpha); 970 } 971 } 972 #else 973 ASSERT(!m_scaled); 974 png_bytep row = interlaceBuffer; 975 for (int y = rect.y(); y < rect.maxY(); ++y, row += colorChannels * size().width()) { 976 png_bytep pixel = row; 977 #if USE(QCMSLIB) 978 if (qcms_transform* transform = m_reader->colorTransform()) { 979 qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); 980 pixel = m_reader->rowBuffer(); 981 } 982 #endif 983 ImageFrame::PixelData* address = buffer.getAddr(rect.x(), y); 984 for (int x = rect.x(); x < rect.maxX(); ++x, pixel += colorChannels) { 985 unsigned alpha = hasAlpha ? pixel[3] : 255; 986 nonTrivialAlpha |= alpha < 255; 987 if (!m_blend) 988 buffer.setRGBA(address++, pixel[0], pixel[1], pixel[2], alpha); 989 else 990 buffer.overRGBA(address++, pixel[0], pixel[1], pixel[2], alpha); 991 } 992 } 993 #endif 994 995 if (!nonTrivialAlpha) { 996 if (buffer.originalFrameRect().contains(IntRect(IntPoint(), scaledSize()))) 997 buffer.setHasAlpha(false); 998 else { 999 size_t frameIndex = m_currentFrame; 1000 const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex]; 1001 while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwritePrevious)) 1002 prevBuffer = &m_frameBufferCache[--frameIndex]; 1003 if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) 1004 && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuffer->originalFrameRect())) 1005 buffer.setHasAlpha(false); 1006 } 1007 } else if (!m_blend && !buffer.hasAlpha()) 1008 buffer.setHasAlpha(nonTrivialAlpha); 1009 } 1010 m_currentFrame++; 1011 } 1012 1013 int PNGImageDecoder::processingStart(png_unknown_chunkp chunk) 1014 { 1015 static png_byte dataPNG[8] = {137, 80, 78, 71, 13, 10, 26, 10}; 1016 static png_byte datagAMA[16] = {0, 0, 0, 4, 103, 65, 77, 65}; 1017 1018 if (!m_hasInfo) 1019 return 0; 1020 1021 m_totalFrames++; 1022 1023 m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, 0); 1024 m_info = png_create_info_struct(m_png); 1025 if (setjmp(JMPBUF(m_png))) 1026 return 1; 1027 1028 png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); 1029 png_set_progressive_read_fn(m_png, static_cast<png_voidp>(this), 1030 WebCore::frameHeader, WebCore::rowAvailable, 0); 1031 1032 memcpy(m_dataIHDR + 8, chunk->data + 4, 8); 1033 png_save_uint_32(datagAMA + 8, m_gamma); 1034 1035 png_process_data(m_png, m_info, dataPNG, 8); 1036 png_process_data(m_png, m_info, m_dataIHDR, 25); 1037 png_process_data(m_png, m_info, datagAMA, 16); 1038 if (m_sizePLTE > 0) 1039 png_process_data(m_png, m_info, m_dataPLTE, m_sizePLTE); 1040 if (m_sizetRNS > 0) 1041 png_process_data(m_png, m_info, m_datatRNS, m_sizetRNS); 1042 1043 return 0; 1044 } 1045 1046 int PNGImageDecoder::processingFinish() 1047 { 1048 static png_byte dataIEND[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130}; 1049 1050 if (!m_hasInfo) 1051 return 0; 1052 1053 if (m_totalFrames) { 1054 if (setjmp(JMPBUF(m_png))) 1055 return 1; 1056 1057 png_process_data(m_png, m_info, dataIEND, 12); 1058 png_destroy_read_struct(&m_png, &m_info, 0); 1059 } 1060 1061 frameComplete(); 1062 return 0; 1063 } 1064 1065 void PNGImageDecoder::fallbackNotAnimated() 1066 { 1067 m_isAnimated = false; 1068 m_frameCount = 1; 1069 m_playCount = 0; 1070 m_currentFrame = 0; 1071 m_frameBufferCache.resize(1); 1072 } 1073 #endif 1074 577 1075 } // namespace WebCore -
trunk/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h
r177423 r181553 28 28 29 29 #include "ImageDecoder.h" 30 #if ENABLE(APNG) 31 #include <png.h> 32 #endif 30 33 31 34 namespace WebCore { … … 41 44 // ImageDecoder 42 45 virtual String filenameExtension() const { return "png"; } 46 #if ENABLE(APNG) 47 virtual size_t frameCount() override { return m_frameCount; } 48 virtual int repetitionCount() const override { return m_playCount-1; } 49 #endif 43 50 virtual bool isSizeAvailable(); 44 51 virtual bool setSize(unsigned width, unsigned height); … … 53 60 void rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass); 54 61 void pngComplete(); 62 #if ENABLE(APNG) 63 void readChunks(png_unknown_chunkp); 64 void frameHeader(); 65 66 void init(); 67 virtual void clearFrameBufferCache(size_t clearBeforeFrame); 68 #endif 55 69 56 70 bool isComplete() const 57 71 { 58 return !m_frameBufferCache.isEmpty() && (m_frameBufferCache.first().status() == ImageFrame::FrameComplete); 72 if (m_frameBufferCache.isEmpty()) 73 return false; 74 75 for (auto& imageFrame : m_frameBufferCache) { 76 if (imageFrame.status() != ImageFrame::FrameComplete) 77 return false; 78 } 79 80 return true; 59 81 } 60 82 … … 64 86 // data coming, sets the "decode failure" flag. 65 87 void decode(bool onlySize); 88 #if ENABLE(APNG) 89 void initFrameBuffer(size_t frameIndex); 90 void frameComplete(); 91 int processingStart(png_unknown_chunkp); 92 int processingFinish(); 93 void fallbackNotAnimated(); 94 #endif 66 95 67 96 std::unique_ptr<PNGImageReader> m_reader; 68 97 bool m_doNothingOnFailure; 98 unsigned m_currentFrame; 99 #if ENABLE(APNG) 100 png_structp m_png; 101 png_infop m_info; 102 bool m_isAnimated; 103 bool m_frameInfo; 104 bool m_frameIsHidden; 105 bool m_hasInfo; 106 int m_gamma; 107 size_t m_frameCount; 108 unsigned m_playCount; 109 unsigned m_totalFrames; 110 unsigned m_sizePLTE; 111 unsigned m_sizetRNS; 112 unsigned m_sequenceNumber; 113 unsigned m_width; 114 unsigned m_height; 115 unsigned m_xOffset; 116 unsigned m_yOffset; 117 unsigned m_delayNumerator; 118 unsigned m_delayDenominator; 119 unsigned m_dispose; 120 unsigned m_blend; 121 png_byte m_dataIHDR[12 + 13]; 122 png_byte m_dataPLTE[12 + 256 * 3]; 123 png_byte m_datatRNS[12 + 256]; 124 #endif 69 125 }; 70 126
Note: See TracChangeset
for help on using the changeset viewer.