Changeset 202616 in webkit


Ignore:
Timestamp:
Jun 29, 2016 12:23:51 AM (8 years ago)
Author:
Carlos Garcia Campos
Message:

REGRESSION(r198782, r201043): [image-decoders] Flickering with some animated gif
https://bugs.webkit.org/show_bug.cgi?id=159089

Reviewed by Antonio Gomes.

There's some flickering when loading big enough animated gifs running the animation in a loop. The first time it
loads everything is fine, but after the first loop iteration there are several flickering effects, once every
time the animation finishes and some others happening in the middle of the animation loop. The flickering
happens because we fail to render some of the frames, and it has two diferent causes:

  • In r198782, ImageDecoder::createFrameImageAtIndex(), was modified to check first if the image is empty to

return early, and then try to get the frame image from the decoder. This is the aone causing the flickering
always on the first frame after one iteration. It happens because ImageDecoder::size() is always empty at that
point. The first time doesn't happen because the gif is loaded and BitmapImage calls isSizeAvailable() from
BitmapImage::dataChanged(). The isSizeAvailable call makes the gif decoder calculate the size. But for the next
iterations, frames are cached and BitmapImage already has the decoded data so isSizeAvailable is not called
again. When createFrameImageAtIndex() is called again for the first frame, size is empty until the gif decoder
creates the reader again, which happens when frameBufferAtIndex() is called, so after that the size is
available. So, we could call isSizeAvailable before checking the size, or simply do the check after the
frameBufferAtIndex() call as we used to do.

  • In r201043 BitmapImage::destroyDecodedDataIfNecessary() was fixed to use the actual bytes used by the frame

in order to decide whether to destroy decoded data or not. This actually revealed a bug, it didn't happen before
because we were never destroying frames before. The bug is in the gif decoder that doesn't correctly handle the
case of destroying only some of the frames from the buffer cache. The gif decoder is designed to always process the
frames in order, the reader keeps an index of the currently processed frame, so when some frames are read from the
cache, and then we ask the decoder for a not cached frame, the currently processed frame is not in sync with the
actual frame we are asking for, and we end do not processing any frame at all.

  • platform/image-decoders/ImageDecoder.cpp:

(WebCore::ImageDecoder::createFrameImageAtIndex): Check the size after calling frameBufferAtIndex().

  • platform/image-decoders/gif/GIFImageDecoder.cpp:

(WebCore::GIFImageDecoder::clearFrameBufferCache): Delete the reader when clearing the cache since it's out of sync.

Location:
trunk/Source/WebCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r202615 r202616  
     12016-06-29  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        REGRESSION(r198782, r201043): [image-decoders] Flickering with some animated gif
     4        https://bugs.webkit.org/show_bug.cgi?id=159089
     5
     6        Reviewed by Antonio Gomes.
     7
     8        There's some flickering when loading big enough animated gifs running the animation in a loop. The first time it
     9        loads everything is fine, but after the first loop iteration there are several flickering effects, once every
     10        time the animation finishes and some others happening in the middle of the animation loop. The flickering
     11        happens because we fail to render some of the frames, and it has two diferent causes:
     12
     13         - In r198782, ImageDecoder::createFrameImageAtIndex(), was modified to check first if the image is empty to
     14        return early, and then try to get the frame image from the decoder. This is the aone causing the flickering
     15        always on the first frame after one iteration. It happens because ImageDecoder::size() is always empty at that
     16        point. The first time doesn't happen because the gif is loaded and BitmapImage calls isSizeAvailable() from
     17        BitmapImage::dataChanged(). The isSizeAvailable call makes the gif decoder calculate the size. But for the next
     18        iterations, frames are cached and BitmapImage already has the decoded data so isSizeAvailable is not called
     19        again. When createFrameImageAtIndex() is called again for the first frame, size is empty until the gif decoder
     20        creates the reader again, which happens when frameBufferAtIndex() is called, so after that the size is
     21        available. So, we could call isSizeAvailable before checking the size, or simply do the check after the
     22        frameBufferAtIndex() call as we used to do.
     23
     24         - In r201043 BitmapImage::destroyDecodedDataIfNecessary() was fixed to use the actual bytes used by the frame
     25        in order to decide whether to destroy decoded data or not. This actually revealed a bug, it didn't happen before
     26        because we were never destroying frames before. The bug is in the gif decoder that doesn't correctly handle the
     27        case of destroying only some of the frames from the buffer cache. The gif decoder is designed to always process the
     28        frames in order, the reader keeps an index of the currently processed frame, so when some frames are read from the
     29        cache, and then we ask the decoder for a not cached frame, the currently processed frame is not in sync with the
     30        actual frame we are asking for, and we end do not processing any frame at all.
     31
     32        * platform/image-decoders/ImageDecoder.cpp:
     33        (WebCore::ImageDecoder::createFrameImageAtIndex): Check the size after calling frameBufferAtIndex().
     34        * platform/image-decoders/gif/GIFImageDecoder.cpp:
     35        (WebCore::GIFImageDecoder::clearFrameBufferCache): Delete the reader when clearing the cache since it's out of sync.
     36
    1372016-06-28  Carlos Garcia Campos  <cgarcia@igalia.com>
    238
  • trunk/Source/WebCore/platform/image-decoders/ImageDecoder.cpp

    r199312 r202616  
    313313NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel)
    314314{
    315     // Zero-height images can cause problems for some ports. If we have an
    316     // empty image dimension, just bail.
    317     if (size().isEmpty())
    318         return nullptr;
    319 
    320315    ImageFrame* buffer = frameBufferAtIndex(index);
    321     if (!buffer || buffer->status() == ImageFrame::FrameEmpty)
     316    // Zero-height images can cause problems for some ports. If we have an empty image dimension, just bail.
     317    // It's important to check the size after calling frameBufferAtIndex() to ensure the decoder has updated the size.
     318    // See https://bugs.webkit.org/show_bug.cgi?id=159089.
     319    if (!buffer || buffer->status() == ImageFrame::FrameEmpty || size().isEmpty())
    322320        return nullptr;
    323321
  • trunk/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp

    r199312 r202616  
    177177            j->clearPixelData();
    178178    }
     179
     180    // When some frames are cleared, the reader is out of sync, since the caller might ask for any frame not
     181    // necessarily in the order expected by the reader. See https://bugs.webkit.org/show_bug.cgi?id=159089.
     182    m_reader = nullptr;
    179183}
    180184
Note: See TracChangeset for help on using the changeset viewer.