blob: 6696c960a5875f0431729b119ab6b24127d7a5ca [file] [log] [blame]
/*
* Copyright (C) 2006, 2010, 2011, 2012, 2014, 2016 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk>
* Copyright (C) 2008, Google Inc. All rights reserved.
* Copyright (C) 2007-2009 Torch Mobile, Inc
*
* 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 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 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 "ImageSource.h"
#include "GraphicsContext.h"
#if USE(CG)
#include "ImageDecoderCG.h"
#if PLATFORM(WIN)
#include <WebKitSystemInterface/WebKitSystemInterface.h>
#endif
#elif USE(DIRECT2D)
#include "ImageDecoderDirect2D.h"
#else
#include "ImageDecoder.h"
#endif
#include "ImageOrientation.h"
#include <wtf/CurrentTime.h>
namespace WebCore {
ImageSource::ImageSource(NativeImagePtr&& nativeImage)
: m_frameCache(ImageFrameCache::create(WTFMove(nativeImage)))
{
}
ImageSource::ImageSource(Image* image, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
: m_frameCache(ImageFrameCache::create(image))
, m_alphaOption(alphaOption)
, m_gammaAndColorProfileOption(gammaAndColorProfileOption)
{
}
ImageSource::~ImageSource()
{
}
void ImageSource::clearFrameBufferCache(size_t clearBeforeFrame)
{
if (!isDecoderAvailable())
return;
m_decoder->clearFrameBufferCache(clearBeforeFrame);
}
bool ImageSource::ensureDecoderAvailable(SharedBuffer* data)
{
if (!data || isDecoderAvailable())
return true;
m_decoder = ImageDecoder::create(*data, m_alphaOption, m_gammaAndColorProfileOption);
if (!isDecoderAvailable())
return false;
m_frameCache->setDecoder(m_decoder.get());
return true;
}
void ImageSource::setDecoderTargetContext(const GraphicsContext* targetContext)
{
#if USE(DIRECT2D)
if (!isDecoderAvailable())
return;
if (targetContext)
m_decoder->setTargetContext(targetContext->platformContext());
#else
UNUSED_PARAM(targetContext);
#endif
}
void ImageSource::setData(SharedBuffer* data, bool allDataReceived)
{
if (!data || !ensureDecoderAvailable(data))
return;
m_decoder->setData(*data, allDataReceived);
}
void ImageSource::resetData(SharedBuffer* data)
{
m_decoder = nullptr;
m_frameCache->setDecoder(nullptr);
setData(data, isAllDataReceived());
}
EncodedDataStatus ImageSource::dataChanged(SharedBuffer* data, bool allDataReceived)
{
#if PLATFORM(IOS)
// FIXME: We should expose a setting to enable/disable progressive loading and make this
// code conditional on it. Then we can remove the PLATFORM(IOS)-guard.
static const double chunkLoadIntervals[] = {0, 1, 3, 6, 15};
double interval = chunkLoadIntervals[std::min(m_progressiveLoadChunkCount, static_cast<uint16_t>(4))];
bool needsUpdate = false;
// The first time through, the chunk time will be 0 and the image will get an update.
if (currentTime() - m_progressiveLoadChunkTime > interval) {
needsUpdate = true;
m_progressiveLoadChunkTime = currentTime();
ASSERT(m_progressiveLoadChunkCount <= std::numeric_limits<uint16_t>::max());
++m_progressiveLoadChunkCount;
}
if (needsUpdate || allDataReceived)
setData(data, allDataReceived);
#else
setData(data, allDataReceived);
#endif
m_frameCache->clearMetadata();
EncodedDataStatus status = encodedDataStatus();
if (status < EncodedDataStatus::SizeAvailable)
return status;
m_frameCache->growFrames();
return status;
}
bool ImageSource::isAllDataReceived()
{
return isDecoderAvailable() ? m_decoder->isAllDataReceived() : m_frameCache->frameCount();
}
bool ImageSource::canUseAsyncDecoding()
{
if (!isDecoderAvailable())
return false;
// FIXME: figure out the best heuristic for enabling async image decoding.
return size().area() * sizeof(RGBA32) >= (frameCount() > 1 ? 100 * KB : 500 * KB);
}
SubsamplingLevel ImageSource::maximumSubsamplingLevel()
{
if (m_maximumSubsamplingLevel)
return m_maximumSubsamplingLevel.value();
if (!isDecoderAvailable() || !m_decoder->frameAllowSubsamplingAtIndex(0))
return SubsamplingLevel::Default;
// FIXME: this value was chosen to be appropriate for iOS since the image
// subsampling is only enabled by default on iOS. Choose a different value
// if image subsampling is enabled on other platform.
const int maximumImageAreaBeforeSubsampling = 5 * 1024 * 1024;
SubsamplingLevel level = SubsamplingLevel::First;
for (; level < SubsamplingLevel::Last; ++level) {
if (frameSizeAtIndex(0, level).area().unsafeGet() < maximumImageAreaBeforeSubsampling)
break;
}
m_maximumSubsamplingLevel = level;
return m_maximumSubsamplingLevel.value();
}
SubsamplingLevel ImageSource::subsamplingLevelForScaleFactor(GraphicsContext& context, const FloatSize& scaleFactor)
{
#if USE(CG)
// Never use subsampled images for drawing into PDF contexts.
if (wkCGContextIsPDFContext(context.platformContext()))
return SubsamplingLevel::Default;
float scale = std::min(float(1), std::max(scaleFactor.width(), scaleFactor.height()));
if (!(scale > 0 && scale <= 1))
return SubsamplingLevel::Default;
int result = std::ceil(std::log2(1 / scale));
return static_cast<SubsamplingLevel>(std::min(result, static_cast<int>(maximumSubsamplingLevel())));
#else
UNUSED_PARAM(context);
UNUSED_PARAM(scaleFactor);
return SubsamplingLevel::Default;
#endif
}
NativeImagePtr ImageSource::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
{
return isDecoderAvailable() ? m_decoder->createFrameImageAtIndex(index, subsamplingLevel) : nullptr;
}
NativeImagePtr ImageSource::frameImageAtIndexCacheIfNeeded(size_t index, SubsamplingLevel subsamplingLevel, const GraphicsContext* targetContext)
{
setDecoderTargetContext(targetContext);
return m_frameCache->frameImageAtIndexCacheIfNeeded(index, subsamplingLevel);
}
void ImageSource::dump(TextStream& ts)
{
ts.dumpProperty("type", filenameExtension());
ts.dumpProperty("frame-count", frameCount());
ts.dumpProperty("repetitions", repetitionCount());
ts.dumpProperty("solid-color", singlePixelSolidColor());
ImageOrientation orientation = frameOrientationAtIndex(0);
if (orientation != OriginTopLeft)
ts.dumpProperty("orientation", orientation);
}
}