Changeset 181553 in webkit


Ignore:
Timestamp:
Mar 16, 2015 7:24:46 AM (9 years ago)
Author:
commit-queue@webkit.org
Message:

Add APNG support
https://bugs.webkit.org/show_bug.cgi?id=17022

Patch by Max Stepin <maxstepin@gmail.com> on 2015-03-16
Reviewed by Carlos Garcia Campos.

Source/WebCore:

Test: fast/images/animated-png.html

  • platform/image-decoders/ImageDecoder.h:

(WebCore::ImageFrame::divide255):
(WebCore::ImageFrame::overRGBA):

  • platform/image-decoders/png/PNGImageDecoder.cpp:

(WebCore::frameHeader):
(WebCore::readChunks):
(WebCore::PNGImageReader::PNGImageReader):
(WebCore::PNGImageDecoder::PNGImageDecoder):
(WebCore::PNGImageDecoder::frameBufferAtIndex):
(WebCore::PNGImageDecoder::headerAvailable):
(WebCore::PNGImageDecoder::rowAvailable):
(WebCore::PNGImageDecoder::pngComplete):
(WebCore::PNGImageDecoder::readChunks):
(WebCore::PNGImageDecoder::frameHeader):
(WebCore::PNGImageDecoder::init):
(WebCore::PNGImageDecoder::clearFrameBufferCache):
(WebCore::PNGImageDecoder::initFrameBuffer):
(WebCore::PNGImageDecoder::frameComplete):
(WebCore::PNGImageDecoder::processingStart):
(WebCore::PNGImageDecoder::processingFinish):
(WebCore::PNGImageDecoder::fallbackNotAnimated):

  • platform/image-decoders/png/PNGImageDecoder.h:

(WebCore::PNGImageDecoder::frameCount):
(WebCore::PNGImageDecoder::repetitionCount):
(WebCore::PNGImageDecoder::isComplete):

Source/WTF:

  • wtf/FeatureDefines.h:

LayoutTests:

  • fast/images/animated-png-expected.html: Added.
  • fast/images/animated-png.html: Added.
  • fast/images/resources/apng00-ref.png: Added.
  • fast/images/resources/apng00.png: Added.
  • fast/images/resources/apng01-ref.png: Added.
  • fast/images/resources/apng01.png: Added.
  • fast/images/resources/apng02-ref.png: Added.
  • fast/images/resources/apng02.png: Added.
  • fast/images/resources/apng04-ref.png: Added.
  • fast/images/resources/apng04.png: Added.
  • fast/images/resources/apng08-ref.png: Added.
  • fast/images/resources/apng08.png: Added.
  • fast/images/resources/apng10-ref.png: Added.
  • fast/images/resources/apng10.png: Added.
  • fast/images/resources/apng11-ref.png: Added.
  • fast/images/resources/apng11.png: Added.
  • fast/images/resources/apng12-ref.png: Added.
  • fast/images/resources/apng12.png: Added.
  • fast/images/resources/apng14-ref.png: Added.
  • fast/images/resources/apng14.png: Added.
  • fast/images/resources/apng18-ref.png: Added.
  • fast/images/resources/apng18.png: Added.
  • fast/images/resources/apng24-ref.png: Added.
  • fast/images/resources/apng24.png: Added.
  • fast/images/resources/apng26-ref.png: Added.
  • fast/images/resources/apng26.png: Added.
  • platform/mac/TestExpectations:
Location:
trunk
Files:
26 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r181526 r181553  
     12015-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
    1362015-03-16  Gyuyoung Kim  <gyuyoung.kim@samsung.com>
    237
  • trunk/LayoutTests/platform/mac/TestExpectations

    r181450 r181553  
    12741274# Test uses Yosemite blurs
    12751275[ Mavericks ] compositing/media-controls-bar-appearance.html [ Skip ]
     1276
     1277# Mavericks and prior do not support APNG.
     1278webkit.org/b/17022 [ MountainLion Mavericks ] fast/images/animated-png.html [ Skip ]
  • trunk/Source/WTF/ChangeLog

    r181525 r181553  
     12015-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
    1102015-03-15  Benjamin Poulain  <benjamin@webkit.org>
    211
  • trunk/Source/WTF/wtf/FeatureDefines.h

    r179687 r181553  
    308308#endif
    309309
     310#if !defined(ENABLE_APNG)
     311#define ENABLE_APNG 1
     312#endif
     313
    310314#if !defined(ENABLE_BATTERY_STATUS)
    311315#define ENABLE_BATTERY_STATUS 0
  • trunk/Source/WebCore/ChangeLog

    r181525 r181553  
     12015-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
    1362015-03-15  Benjamin Poulain  <benjamin@webkit.org>
    237
  • trunk/Source/WebCore/platform/image-decoders/ImageDecoder.h

    r167760 r181553  
    166166        }
    167167
     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
    168210    private:
    169211        int width() const
  • trunk/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp

    r178166 r181553  
    88 * Other contributors:
    99 *   Stuart Parmenter <stuart@mozilla.com>
     10 *   Max Stepin <maxstepin@gmail.com>
    1011 *
    1112 * This library is free software; you can redistribute it and/or
     
    101102}
    102103
     104#if ENABLE(APNG)
     105// Called when we have the frame header.
     106static 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.
     112static 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
    103119class PNGImageReader {
    104120    WTF_MAKE_FAST_ALLOCATED;
     
    118134        m_info = png_create_info_struct(m_png);
    119135        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
    120142    }
    121143
     
    216238};
    217239
    218 PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption,
    219                                  ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
     240PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
    220241    : ImageDecoder(alphaOption, gammaAndColorProfileOption)
    221242    , 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
    222267{
    223268}
     
    246291ImageFrame* PNGImageDecoder::frameBufferAtIndex(size_t index)
    247292{
     293#if ENABLE(APNG)
     294    if (!isSizeAvailable())
     295        return nullptr;
     296
     297    if (index >= frameCount())
     298        index = frameCount() - 1;
     299#else
    248300    if (index)
    249         return 0;
     301        return nullptr;
     302#endif
    250303
    251304    if (m_frameBufferCache.isEmpty()) {
     
    254307    }
    255308
    256     ImageFrame& frame = m_frameBufferCache[0];
     309    ImageFrame& frame = m_frameBufferCache[index];
    257310    if (frame.status() != ImageFrame::FrameComplete)
    258311        decode(false);
     
    330383    // The options we set here match what Mozilla does.
    331384
     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
    332400    // 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)
    334418        png_set_expand(png);
    335419
    336420    png_bytep trns = 0;
    337421    int trnsCount = 0;
     422    png_color_16p transValues;
    338423    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
    340443        png_set_expand(png);
    341444    }
     
    370473        }
    371474        png_set_gamma(png, cDefaultGamma, gamma);
     475#if ENABLE(APNG)
     476        m_gamma = static_cast<int>(gamma * 100000);
     477#endif
    372478    } else
    373479        png_set_gamma(png, cDefaultGamma, cInverseGamma);
     
    425531
    426532    // 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];
    428538    if (buffer.status() == ImageFrame::FrameEmpty) {
    429539        png_structp png = m_reader->pngPtr();
     
    434544
    435545        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());
    438550            if (!m_reader->interlaceBuffer()) {
    439551                longjmp(JMPBUF(png), 1);
     
    443555
    444556#if USE(QCMSLIB)
    445         if (m_reader->colorTransform()) {
     557        if (m_reader->colorTransform() && !m_currentFrame) {
    446558            m_reader->createRowBuffer(colorChannels * size().width());
    447559            if (!m_reader->rowBuffer()) {
     
    455567        buffer.setColorProfile(m_colorProfile);
    456568
     569#if ENABLE(APNG)
     570        if (m_currentFrame)
     571            initFrameBuffer(m_currentFrame);
     572        else
     573#endif
    457574        // For PNGs, the frame always fills the entire image.
    458575        buffer.setOriginalFrameRect(IntRect(IntPoint(), size()));
     
    505622    if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) {
    506623        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
    507630        png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer);
    508631    }
     
    553676void PNGImageDecoder::pngComplete()
    554677{
     678#if ENABLE(APNG)
     679    if (m_isAnimated) {
     680        if (!processingFinish() && m_frameCount == m_currentFrame)
     681            return;
     682
     683        fallbackNotAnimated();
     684    }
     685#endif
    555686    if (!m_frameBufferCache.isEmpty())
    556687        m_frameBufferCache.first().setStatus(ImageFrame::FrameComplete);
     
    575706}
    576707
     708#if ENABLE(APNG)
     709void 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
     804void 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
     833void 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
     844void 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
     867void 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
     922void 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
     1013int 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
     1046int 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
     1065void 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
    5771075} // namespace WebCore
  • trunk/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h

    r177423 r181553  
    2828
    2929#include "ImageDecoder.h"
     30#if ENABLE(APNG)
     31#include <png.h>
     32#endif
    3033
    3134namespace WebCore {
     
    4144        // ImageDecoder
    4245        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
    4350        virtual bool isSizeAvailable();
    4451        virtual bool setSize(unsigned width, unsigned height);
     
    5360        void rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass);
    5461        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
    5569
    5670        bool isComplete() const
    5771        {
    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;
    5981        }
    6082
     
    6486        // data coming, sets the "decode failure" flag.
    6587        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
    6695
    6796        std::unique_ptr<PNGImageReader> m_reader;
    6897        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
    69125    };
    70126
Note: See TracChangeset for help on using the changeset viewer.