| Index: Source/platform/graphics/ImageFrameGenerator.cpp
|
| diff --git a/Source/platform/graphics/ImageFrameGenerator.cpp b/Source/platform/graphics/ImageFrameGenerator.cpp
|
| index ad92ffc0331e71bf29081ffd270c1703ebc17421..d7f1332636a0e6afb702e003468bf0a727f9139e 100644
|
| --- a/Source/platform/graphics/ImageFrameGenerator.cpp
|
| +++ b/Source/platform/graphics/ImageFrameGenerator.cpp
|
| @@ -35,15 +35,48 @@
|
| #include "platform/image-decoders/ImageDecoder.h"
|
|
|
| #include "skia/ext/image_operations.h"
|
| +#include "third_party/skia/include/core/SkMallocPixelRef.h"
|
|
|
| namespace WebCore {
|
|
|
| +// Creates a SkPixelRef such that the memory for pixels is given by an external body.
|
| +// This is used to write directly to the memory given by Skia during decoding.
|
| +class ImageFrameGenerator::ExternalMemoryAllocator : public SkBitmap::Allocator {
|
| +public:
|
| + ExternalMemoryAllocator(const SkImageInfo& info, void* pixels, size_t rowBytes)
|
| + : m_info(info)
|
| + , m_pixels(pixels)
|
| + , m_rowBytes(rowBytes)
|
| + {
|
| + }
|
| +
|
| + virtual bool allocPixelRef(SkBitmap* dst, SkColorTable* ctable) OVERRIDE
|
| + {
|
| + SkImageInfo info;
|
| + if (!dst->asImageInfo(&info))
|
| + return false;
|
| +
|
| + if (info != m_info || m_rowBytes != dst->rowBytes())
|
| + return false;
|
| +
|
| + if (!dst->installPixels(m_info, m_pixels, m_rowBytes, 0, 0))
|
| + return false;
|
| + dst->lockPixels();
|
| + return true;
|
| + }
|
| +
|
| +private:
|
| + SkImageInfo m_info;
|
| + void* m_pixels;
|
| + size_t m_rowBytes;
|
| +};
|
| +
|
| ImageFrameGenerator::ImageFrameGenerator(const SkISize& fullSize, PassRefPtr<SharedBuffer> data, bool allDataReceived, bool isMultiFrame)
|
| : m_fullSize(fullSize)
|
| , m_isMultiFrame(isMultiFrame)
|
| , m_decodeFailedAndEmpty(false)
|
| , m_decodeCount(ScaledImageFragment::FirstPartialImage)
|
| - , m_allocator(adoptPtr(new DiscardablePixelRefAllocator()))
|
| + , m_discardableAllocator(adoptPtr(new DiscardablePixelRefAllocator()))
|
| {
|
| setData(data.get(), allDataReceived);
|
| }
|
| @@ -95,33 +128,50 @@ const ScaledImageFragment* ImageFrameGenerator::decodeAndScale(const SkISize& sc
|
| bool ImageFrameGenerator::decodeAndScale(const SkImageInfo& info, size_t index, void* pixels, size_t rowBytes)
|
| {
|
| // This method is called to populate a discardable memory owned by Skia.
|
| - // Ideally we want the decoder to write directly to |pixels| but this
|
| - // simple implementation copies from a decoded bitmap.
|
| +
|
| + // Prevents concurrent decode or scale operations on the same image data.
|
| + MutexLocker lock(m_decodeMutex);
|
|
|
| // This implementation does not support scaling so check the requested size.
|
| - ASSERT(m_fullSize.width() == info.fWidth);
|
| - ASSERT(m_fullSize.height() == info.fHeight);
|
| + SkISize scaledSize = SkISize::Make(info.fWidth, info.fHeight);
|
| + ASSERT(m_fullSize == scaledSize);
|
| +
|
| + if (m_decodeFailedAndEmpty)
|
| + return 0;
|
| +
|
| + TRACE_EVENT2("webkit", "ImageFrameGenerator::decodeAndScale", "generator", this, "decodeCount", static_cast<int>(m_decodeCount));
|
|
|
| // Don't use discardable memory for decoding if Skia is providing output
|
| - // memory. By clearing the memory allocator decoding will use heap memory.
|
| + // memory. Instead use ExternalMemoryAllocator such that we can
|
| + // write directly to the memory given by Skia.
|
| //
|
| // TODO:
|
| // This is not pretty because this class is used in two different code
|
| // paths: discardable memory decoding on Android and discardable memory
|
| // in Skia. Once the transition to caching in Skia is complete we can get
|
| // rid of the logic that handles discardable memory.
|
| - m_allocator.clear();
|
| + m_discardableAllocator.clear();
|
| + m_externalAllocator = adoptPtr(new ExternalMemoryAllocator(info, pixels, rowBytes));
|
|
|
| - const ScaledImageFragment* cachedImage = decodeAndScale(SkISize::Make(info.fWidth, info.fHeight), index);
|
| + const ScaledImageFragment* cachedImage = tryToResumeDecode(scaledSize, index);
|
| if (!cachedImage)
|
| return false;
|
|
|
| - ASSERT(cachedImage->bitmap().width() == info.fWidth);
|
| - ASSERT(cachedImage->bitmap().height() == info.fHeight);
|
| + // Don't keep the allocator because it contains a pointer to memory
|
| + // that we do not own.
|
| + m_externalAllocator.clear();
|
| +
|
| + ASSERT(cachedImage->bitmap().width() == scaledSize.width());
|
| + ASSERT(cachedImage->bitmap().height() == scaledSize.height());
|
|
|
| - bool copied = cachedImage->bitmap().copyPixelsTo(pixels, rowBytes * info.fHeight, rowBytes);
|
| + // If the image is fully decoded and is not multi-frame then we have
|
| + // written directly to the memory given by Skia. There is no need to
|
| + // copy.
|
| + bool result = true;
|
| + if (!cachedImage->isComplete() || m_isMultiFrame)
|
| + result = cachedImage->bitmap().copyPixelsTo(pixels, rowBytes * info.fHeight, rowBytes);
|
| ImageDecodingStore::instance()->unlockCache(this, cachedImage);
|
| - return copied;
|
| + return result;
|
| }
|
|
|
| const ScaledImageFragment* ImageFrameGenerator::tryToLockCompleteCache(const SkISize& scaledSize, size_t index)
|
| @@ -188,10 +238,12 @@ PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(size_t index, ImageD
|
| ASSERT(decoder);
|
| SharedBuffer* data = 0;
|
| bool allDataReceived = false;
|
| + bool newDecoder = false;
|
| m_data.data(&data, &allDataReceived);
|
|
|
| // Try to create an ImageDecoder if we are not given one.
|
| if (!*decoder) {
|
| + newDecoder = true;
|
| if (m_imageDecoderFactory)
|
| *decoder = m_imageDecoderFactory->create().leakPtr();
|
|
|
| @@ -202,10 +254,15 @@ PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(size_t index, ImageD
|
| return nullptr;
|
| }
|
|
|
| - // TODO: this is very ugly. We need to refactor the way how we can pass a
|
| - // memory allocator to image decoders.
|
| - if (!m_isMultiFrame)
|
| - (*decoder)->setMemoryAllocator(m_allocator.get());
|
| + if (!m_isMultiFrame && newDecoder && allDataReceived) {
|
| + // We are supporting two decoding paths in this code. Use the
|
| + // external memory allocator in the Skia discardable path to
|
| + // save one memory copy.
|
| + if (m_externalAllocator)
|
| + (*decoder)->setMemoryAllocator(m_externalAllocator.get());
|
| + else
|
| + (*decoder)->setMemoryAllocator(m_discardableAllocator.get());
|
| + }
|
| (*decoder)->setData(data, allDataReceived);
|
| // If this call returns a newly allocated DiscardablePixelRef, then
|
| // ImageFrame::m_bitmap and the contained DiscardablePixelRef are locked.
|
| @@ -215,11 +272,15 @@ PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(size_t index, ImageD
|
| ImageFrame* frame = (*decoder)->frameBufferAtIndex(index);
|
| (*decoder)->setData(0, false); // Unref SharedBuffer from ImageDecoder.
|
| (*decoder)->clearCacheExceptFrame(index);
|
| + (*decoder)->setMemoryAllocator(0);
|
|
|
| if (!frame || frame->status() == ImageFrame::FrameEmpty)
|
| return nullptr;
|
|
|
| - const bool isComplete = frame->status() == ImageFrame::FrameComplete;
|
| + // A cache object is considered complete if we can decode a complete frame.
|
| + // Or we have received all data. The image might not be fully decoded in
|
| + // the latter case.
|
| + const bool isCacheComplete = frame->status() == ImageFrame::FrameComplete || allDataReceived;
|
| SkBitmap fullSizeBitmap = frame->getSkBitmap();
|
| if (fullSizeBitmap.isNull())
|
| return nullptr;
|
| @@ -236,13 +297,18 @@ PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(size_t index, ImageD
|
| }
|
| ASSERT(fullSizeBitmap.width() == m_fullSize.width() && fullSizeBitmap.height() == m_fullSize.height());
|
|
|
| - if (isComplete)
|
| + if (isCacheComplete)
|
| return ScaledImageFragment::createComplete(m_fullSize, index, fullSizeBitmap);
|
|
|
| // If the image is partial we need to return a copy. This is to avoid future
|
| // decode operations writing to the same bitmap.
|
| + // FIXME: Note that discardable allocator is used. This is because the code
|
| + // is still used in the Android discardable memory path. When this code is
|
| + // used in the Skia discardable memory path |m_discardableAllocator| is empty.
|
| + // This is confusing and should be cleaned up when we can deprecate the use
|
| + // case for Android discardable memory.
|
| SkBitmap copyBitmap;
|
| - return fullSizeBitmap.copyTo(©Bitmap, fullSizeBitmap.config(), m_allocator.get()) ?
|
| + return fullSizeBitmap.copyTo(©Bitmap, fullSizeBitmap.config(), m_discardableAllocator.get()) ?
|
| ScaledImageFragment::createPartial(m_fullSize, index, nextGenerationId(), copyBitmap) : nullptr;
|
| }
|
|
|
|
|