#include "platform/SharedBuffer.h"
#include "platform/TraceEvent.h"
-#include "platform/graphics/DiscardablePixelRef.h"
#include "platform/graphics/ImageDecodingStore.h"
-#include "platform/graphics/ScaledImageFragment.h"
#include "platform/image-decoders/ImageDecoder.h"
#include "skia/ext/image_operations.h"
size_t m_rowBytes;
};
+static bool updateYUVComponentSizes(ImageDecoder* decoder, SkISize componentSizes[3], ImageDecoder::SizeType sizeType)
+{
+ // canDecodeToYUV() has to be called AFTER isSizeAvailable(),
+ // otherwise the output color space may not be set in the decoder.
+ if (!decoder->isSizeAvailable() || !decoder->canDecodeToYUV())
+ return false;
+
+ IntSize size = decoder->decodedYUVSize(0, sizeType);
+ componentSizes[0].set(size.width(), size.height());
+ size = decoder->decodedYUVSize(1, sizeType);
+ componentSizes[1].set(size.width(), size.height());
+ size = decoder->decodedYUVSize(2, sizeType);
+ componentSizes[2].set(size.width(), size.height());
+ return true;
+}
+
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_discardableAllocator(adoptPtr(new DiscardablePixelRefAllocator()))
+ , m_decodeCount(0)
{
setData(data.get(), allDataReceived);
}
*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("blink", "ImageFrameGenerator::decodeAndScale", "generator", this, "decodeCount", static_cast<int>(m_decodeCount));
-
- cachedImage = tryToResumeDecode(scaledSize, index);
- if (cachedImage)
- return cachedImage;
- return 0;
-}
-
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.
ASSERT(m_fullSize == scaledSize);
if (m_decodeFailedAndEmpty)
- return 0;
-
- TRACE_EVENT2("blink", "ImageFrameGenerator::decodeAndScale", "generator", this, "decodeCount", static_cast<int>(m_decodeCount));
-
- // Don't use discardable memory for decoding if Skia is providing output
- // 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_discardableAllocator.clear();
+ return false;
+
+ TRACE_EVENT2("blink", "ImageFrameGenerator::decodeAndScale", "generator", this, "decodeCount", m_decodeCount);
+
m_externalAllocator = adoptPtr(new ExternalMemoryAllocator(info, pixels, rowBytes));
- const ScaledImageFragment* cachedImage = tryToResumeDecode(scaledSize, index);
- if (!cachedImage)
+ SkBitmap bitmap = tryToResumeDecode(scaledSize, index);
+ if (bitmap.isNull())
return false;
// 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());
+ ASSERT(bitmap.width() == scaledSize.width());
+ ASSERT(bitmap.height() == scaledSize.height());
bool result = true;
// Check to see if decoder has written directly to the memory provided
// by Skia. If not make a copy.
- if (cachedImage->bitmap().getPixels() != pixels)
- result = cachedImage->bitmap().copyPixelsTo(pixels, rowBytes * info.fHeight, rowBytes);
- ImageDecodingStore::instance()->unlockCache(this, cachedImage);
+ if (bitmap.getPixels() != pixels)
+ result = bitmap.copyPixelsTo(pixels, rowBytes * info.fHeight, rowBytes);
return result;
}
-const ScaledImageFragment* ImageFrameGenerator::tryToLockCompleteCache(const SkISize& scaledSize, size_t index)
+bool ImageFrameGenerator::decodeToYUV(SkISize componentSizes[3], void* planes[3], size_t rowBytes[3])
{
- const ScaledImageFragment* cachedImage = 0;
- if (ImageDecodingStore::instance()->lockCache(this, scaledSize, index, &cachedImage))
- return cachedImage;
- return 0;
+ // This method is called to populate a discardable memory owned by Skia.
+
+ // Prevents concurrent decode or scale operations on the same image data.
+ MutexLocker lock(m_decodeMutex);
+
+ if (m_decodeFailedAndEmpty)
+ return false;
+
+ TRACE_EVENT2("blink", "ImageFrameGenerator::decodeToYUV", "generator", this, "decodeCount", static_cast<int>(m_decodeCount));
+
+ if (!planes || !planes[0] || !planes[1] || !planes[2]
+ || !rowBytes || !rowBytes[0] || !rowBytes[1] || !rowBytes[2]) {
+ return false;
+ }
+
+ SharedBuffer* data = 0;
+ bool allDataReceived = false;
+ m_data.data(&data, &allDataReceived);
+
+ // FIXME: YUV decoding does not currently support progressive decoding.
+ ASSERT(allDataReceived);
+
+ OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data, ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileApplied);
+ if (!decoder)
+ return false;
+
+ decoder->setData(data, allDataReceived);
+
+ OwnPtr<ImagePlanes> imagePlanes = adoptPtr(new ImagePlanes(planes, rowBytes));
+ decoder->setImagePlanes(imagePlanes.release());
+
+ bool sizeUpdated = updateYUVComponentSizes(decoder.get(), componentSizes, ImageDecoder::ActualSize);
+ RELEASE_ASSERT(sizeUpdated);
+
+ bool yuvDecoded = decoder->decodeToYUV();
+ if (yuvDecoded)
+ setHasAlpha(0, false); // YUV is always opaque
+ return yuvDecoded;
}
-const ScaledImageFragment* ImageFrameGenerator::tryToResumeDecode(const SkISize& scaledSize, size_t index)
+SkBitmap ImageFrameGenerator::tryToResumeDecode(const SkISize& scaledSize, size_t index)
{
TRACE_EVENT1("blink", "ImageFrameGenerator::tryToResumeDecodeAndScale", "index", static_cast<int>(index));
const bool resumeDecoding = ImageDecodingStore::instance()->lockDecoder(this, m_fullSize, &decoder);
ASSERT(!resumeDecoding || decoder);
- OwnPtr<ScaledImageFragment> fullSizeImage = decode(index, &decoder);
+ SkBitmap fullSizeImage;
+ bool complete = decode(index, &decoder, &fullSizeImage);
if (!decoder)
- return 0;
+ return SkBitmap();
// If we are not resuming decoding that means the decoder is freshly
// created and we have ownership. If we are resuming decoding then
if (!resumeDecoding)
decoderContainer = adoptPtr(decoder);
- if (!fullSizeImage) {
+ if (fullSizeImage.isNull()) {
// 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;
+ return SkBitmap();
}
- 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;
+ const bool removeDecoder = complete && !m_isMultiFrame;
if (resumeDecoding) {
if (removeDecoder)
else
ImageDecodingStore::instance()->unlockDecoder(this, decoder);
} else if (!removeDecoder) {
- ImageDecodingStore::instance()->insertDecoder(this, decoderContainer.release(), DiscardablePixelRef::isDiscardable(cachedImage->bitmap().pixelRef()));
+ ImageDecodingStore::instance()->insertDecoder(this, decoderContainer.release());
}
+ return fullSizeImage;
+}
- return cachedImage;
+void ImageFrameGenerator::setHasAlpha(size_t index, bool hasAlpha)
+{
+ 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] = hasAlpha;
}
-PassOwnPtr<ScaledImageFragment> ImageFrameGenerator::decode(size_t index, ImageDecoder** decoder)
+bool ImageFrameGenerator::decode(size_t index, ImageDecoder** decoder, SkBitmap* bitmap)
{
TRACE_EVENT2("blink", "ImageFrameGenerator::decode", "width", m_fullSize.width(), "height", m_fullSize.height());
*decoder = ImageDecoder::create(*data, ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileApplied).leakPtr();
if (!*decoder)
- return nullptr;
+ return false;
}
- // This variable is set to true if we can skip a memcpy of the decoded bitmap.
- bool canSkipBitmapCopy = false;
-
if (!m_isMultiFrame && newDecoder && allDataReceived) {
// If we're using an external memory allocator that means we're decoding
// directly into the output memory and we can save one memcpy.
- canSkipBitmapCopy = true;
- if (m_externalAllocator)
- (*decoder)->setMemoryAllocator(m_externalAllocator.get());
- else
- (*decoder)->setMemoryAllocator(m_discardableAllocator.get());
+ ASSERT(m_externalAllocator.get());
+ (*decoder)->setMemoryAllocator(m_externalAllocator.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);
(*decoder)->setMemoryAllocator(0);
if (!frame || frame->status() == ImageFrame::FrameEmpty)
- return nullptr;
+ return false;
// 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;
+ const bool isDecodeComplete = frame->status() == ImageFrame::FrameComplete || allDataReceived;
SkBitmap fullSizeBitmap = frame->getSkBitmap();
- if (fullSizeBitmap.isNull())
- return nullptr;
-
+ if (!fullSizeBitmap.isNull())
{
- 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());
+ setHasAlpha(index, !fullSizeBitmap.isOpaque());
}
- ASSERT(fullSizeBitmap.width() == m_fullSize.width() && fullSizeBitmap.height() == m_fullSize.height());
-
- // We early out and do not copy the memory if decoder writes directly to
- // the memory provided by Skia and the decode was complete.
- if (canSkipBitmapCopy && isCacheComplete)
- return ScaledImageFragment::createComplete(m_fullSize, index, fullSizeBitmap);
-
- // If the image is progressively decoded 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;
- if (!fullSizeBitmap.copyTo(©Bitmap, fullSizeBitmap.colorType(), m_discardableAllocator.get()))
- return nullptr;
-
- if (isCacheComplete)
- return ScaledImageFragment::createComplete(m_fullSize, index, copyBitmap);
- return ScaledImageFragment::createPartial(m_fullSize, index, nextGenerationId(), copyBitmap);
+ *bitmap = fullSizeBitmap;
+ return isDecodeComplete;
}
bool ImageFrameGenerator::hasAlpha(size_t index)
return true;
}
+bool ImageFrameGenerator::getYUVComponentSizes(SkISize componentSizes[3])
+{
+ ASSERT(componentSizes);
+
+ TRACE_EVENT2("webkit", "ImageFrameGenerator::getYUVComponentSizes", "width", m_fullSize.width(), "height", m_fullSize.height());
+
+ SharedBuffer* data = 0;
+ bool allDataReceived = false;
+ m_data.data(&data, &allDataReceived);
+
+ // FIXME: YUV decoding does not currently support progressive decoding.
+ if (!allDataReceived)
+ return false;
+
+ OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data, ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileApplied);
+ if (!decoder)
+ return false;
+
+ // Setting a dummy ImagePlanes object signals to the decoder that we want to do YUV decoding.
+ decoder->setData(data, allDataReceived);
+ OwnPtr<ImagePlanes> dummyImagePlanes = adoptPtr(new ImagePlanes);
+ decoder->setImagePlanes(dummyImagePlanes.release());
+
+ return updateYUVComponentSizes(decoder.get(), componentSizes, ImageDecoder::SizeForMemoryAllocation);
+}
+
} // namespace blink