| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include "core/platform/graphics/ImageFrameGenerator.h" |
| |
| #include "core/platform/graphics/DiscardablePixelRef.h" |
| #include "core/platform/graphics/ImageDecodingStore.h" |
| #include "core/platform/image-decoders/ImageDecoder.h" |
| #include "platform/SharedBuffer.h" |
| #include "platform/TraceEvent.h" |
| #include "platform/graphics/ScaledImageFragment.h" |
| |
| #include "skia/ext/image_operations.h" |
| |
| namespace WebCore { |
| |
| namespace { |
| |
| skia::ImageOperations::ResizeMethod resizeMethod() |
| { |
| return skia::ImageOperations::RESIZE_LANCZOS3; |
| } |
| |
| } // namespace |
| |
| 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())) |
| { |
| setData(data.get(), allDataReceived); |
| } |
| |
| ImageFrameGenerator::~ImageFrameGenerator() |
| { |
| // FIXME: This check is not really thread-safe. This should be changed to: |
| // ImageDecodingStore::removeCacheFromInstance(this); |
| // Which uses a lock internally. |
| if (ImageDecodingStore::instance()) |
| ImageDecodingStore::instance()->removeCacheIndexedByGenerator(this); |
| } |
| |
| void ImageFrameGenerator::setData(PassRefPtr<SharedBuffer> data, bool allDataReceived) |
| { |
| m_data.setData(data.get(), allDataReceived); |
| } |
| |
| void ImageFrameGenerator::copyData(RefPtr<SharedBuffer>* data, bool* allDataReceived) |
| { |
| SharedBuffer* buffer = 0; |
| m_data.data(&buffer, allDataReceived); |
| if (buffer) |
| *data = buffer->copy(); |
| } |
| |
| const ScaledImageFragment* ImageFrameGenerator::decodeAndScale(const SkISize& scaledSize, size_t index) |
| { |
| // Prevents concurrent decode or scale operations on the same image data. |
| // Multiple LazyDecodingPixelRefs can call this method at the same time. |
| MutexLocker lock(m_decodeMutex); |
| if (m_decodeFailedAndEmpty) |
| return 0; |
| |
| const ScaledImageFragment* cachedImage = 0; |
| |
| cachedImage = tryToLockCompleteCache(scaledSize, index); |
| if (cachedImage) |
| return cachedImage; |
| |
| TRACE_EVENT2("webkit", "ImageFrameGenerator::decodeAndScale", "generator", this, "decodeCount", static_cast<int>(m_decodeCount)); |
| |
| cachedImage = tryToScale(0, scaledSize, index); |
| if (cachedImage) |
| return cachedImage; |
| |
| cachedImage = tryToResumeDecodeAndScale(scaledSize, index); |
| if (cachedImage) |
| return cachedImage; |
| return 0; |
| } |
| |
| const ScaledImageFragment* ImageFrameGenerator::tryToLockCompleteCache(const SkISize& scaledSize, size_t index) |
| { |
| const ScaledImageFragment* cachedImage = 0; |
| if (ImageDecodingStore::instance()->lockCache(this, scaledSize, index, &cachedImage)) |
| return cachedImage; |
| return 0; |
| } |
| |
| const ScaledImageFragment* ImageFrameGenerator::tryToScale(const ScaledImageFragment* fullSizeImage, const SkISize& scaledSize, size_t index) |
| { |
| TRACE_EVENT0("webkit", "ImageFrameGenerator::tryToScale"); |
| |
| // If the requested scaled size is the same as the full size then exit |
| // early. This saves a cache lookup. |
| if (scaledSize == m_fullSize) |
| return 0; |
| |
| if (!fullSizeImage && !ImageDecodingStore::instance()->lockCache(this, m_fullSize, index, &fullSizeImage)) |
| return 0; |
| |
| // This call allocates the DiscardablePixelRef and lock/unlocks it |
| // afterwards. So the memory allocated to the scaledBitmap can be |
| // discarded after this call. Need to lock the scaledBitmap and |
| // check the pixels before using it next time. |
| SkBitmap scaledBitmap = skia::ImageOperations::Resize(fullSizeImage->bitmap(), resizeMethod(), scaledSize.width(), scaledSize.height(), m_allocator.get()); |
| |
| OwnPtr<ScaledImageFragment> scaledImage; |
| if (fullSizeImage->isComplete()) |
| scaledImage = ScaledImageFragment::createComplete(scaledSize, fullSizeImage->index(), scaledBitmap); |
| else |
| scaledImage = ScaledImageFragment::createPartial(scaledSize, fullSizeImage->index(), nextGenerationId(), scaledBitmap); |
| ImageDecodingStore::instance()->unlockCache(this, fullSizeImage); |
| return ImageDecodingStore::instance()->insertAndLockCache(this, scaledImage.release()); |
| } |
| |
| const ScaledImageFragment* ImageFrameGenerator::tryToResumeDecodeAndScale(const SkISize& scaledSize, size_t index) |
| { |
| TRACE_EVENT1("webkit", "ImageFrameGenerator::tryToResumeDecodeAndScale", "index", static_cast<int>(index)); |
| |
| ImageDecoder* decoder = 0; |
| const bool resumeDecoding = ImageDecodingStore::instance()->lockDecoder(this, m_fullSize, &decoder); |
| ASSERT(!resumeDecoding || decoder); |
| |
| OwnPtr<ScaledImageFragment> fullSizeImage = decode(index, &decoder); |
| |
| if (!decoder) |
| return 0; |
| |
| // If we are not resuming decoding that means the decoder is freshly |
| // created and we have ownership. If we are resuming decoding then |
| // the decoder is owned by ImageDecodingStore. |
| OwnPtr<ImageDecoder> decoderContainer; |
| if (!resumeDecoding) |
| decoderContainer = adoptPtr(decoder); |
| |
| if (!fullSizeImage) { |
| // If decode has failed and resulted an empty image we can save work |
| // in the future by returning early. |
| m_decodeFailedAndEmpty = !m_isMultiFrame && decoder->failed(); |
| |
| if (resumeDecoding) |
| ImageDecodingStore::instance()->unlockDecoder(this, decoder); |
| return 0; |
| } |
| |
| const ScaledImageFragment* cachedImage = ImageDecodingStore::instance()->insertAndLockCache(this, fullSizeImage.release()); |
| |
| // If the image generated is complete then there is no need to keep |
| // the decoder. The exception is multi-frame decoder which can generate |
| // multiple complete frames. |
| const bool removeDecoder = cachedImage->isComplete() && !m_isMultiFrame; |
| |
| if (resumeDecoding) { |
| if (removeDecoder) |
| ImageDecodingStore::instance()->removeDecoder(this, decoder); |
| else |
| ImageDecodingStore::instance()->unlockDecoder(this, decoder); |
| } else if (!removeDecoder) { |
| ImageDecodingStore::instance()->insertDecoder(this, decoderContainer.release(), DiscardablePixelRef::isDiscardable(cachedImage->bitmap().pixelRef())); |
| } |
| |
| if (m_fullSize == scaledSize) |
| return cachedImage; |
| return tryToScale(cachedImage, scaledSize, index); |
| } |
| |
| PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(size_t index, ImageDecoder** decoder) |
| { |
| TRACE_EVENT2("webkit", "ImageFrameGenerator::decode", "width", m_fullSize.width(), "height", m_fullSize.height()); |
| |
| ASSERT(decoder); |
| SharedBuffer* data = 0; |
| bool allDataReceived = false; |
| m_data.data(&data, &allDataReceived); |
| |
| // Try to create an ImageDecoder if we are not given one. |
| if (!*decoder) { |
| if (m_imageDecoderFactory) |
| *decoder = m_imageDecoderFactory->create().leakPtr(); |
| |
| if (!*decoder) |
| *decoder = ImageDecoder::create(*data, ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileApplied).leakPtr(); |
| |
| if (!*decoder) |
| 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()); |
| (*decoder)->setData(data, allDataReceived); |
| // If this call returns a newly allocated DiscardablePixelRef, then |
| // ImageFrame::m_bitmap and the contained DiscardablePixelRef are locked. |
| // They will be unlocked when ImageDecoder is destroyed since ImageDecoder |
| // owns the ImageFrame. Partially decoded SkBitmap is thus inserted into the |
| // ImageDecodingStore while locked. |
| ImageFrame* frame = (*decoder)->frameBufferAtIndex(index); |
| (*decoder)->setData(0, false); // Unref SharedBuffer from ImageDecoder. |
| (*decoder)->clearCacheExceptFrame(index); |
| |
| if (!frame || frame->status() == ImageFrame::FrameEmpty) |
| return nullptr; |
| |
| const bool isComplete = frame->status() == ImageFrame::FrameComplete; |
| SkBitmap fullSizeBitmap = frame->getSkBitmap(); |
| if (fullSizeBitmap.isNull()) |
| return nullptr; |
| |
| { |
| MutexLocker lock(m_alphaMutex); |
| if (index >= m_hasAlpha.size()) { |
| const size_t oldSize = m_hasAlpha.size(); |
| m_hasAlpha.resize(index + 1); |
| for (size_t i = oldSize; i < m_hasAlpha.size(); ++i) |
| m_hasAlpha[i] = true; |
| } |
| m_hasAlpha[index] = !fullSizeBitmap.isOpaque(); |
| } |
| ASSERT(fullSizeBitmap.width() == m_fullSize.width() && fullSizeBitmap.height() == m_fullSize.height()); |
| |
| if (isComplete) |
| 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. |
| SkBitmap copyBitmap; |
| return fullSizeBitmap.copyTo(©Bitmap, fullSizeBitmap.config(), m_allocator.get()) ? |
| ScaledImageFragment::createPartial(m_fullSize, index, nextGenerationId(), copyBitmap) : nullptr; |
| } |
| |
| bool ImageFrameGenerator::hasAlpha(size_t index) |
| { |
| MutexLocker lock(m_alphaMutex); |
| if (index < m_hasAlpha.size()) |
| return m_hasAlpha[index]; |
| return true; |
| } |
| |
| } // namespace |