#include "config.h"
#include "ImageSource.h"
#if USE(CG)
#include "ImageDecoderCG.h"
#elif USE(DIRECT2D)
#include "GraphicsContext.h"
#include "ImageDecoderDirect2D.h"
#include <WinCodec.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);
}
void ImageSource::clear(SharedBuffer* data)
{
m_decoder = nullptr;
m_frameCache->setDecoder(nullptr);
setData(data, isAllDataReceived());
}
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);
}
bool ImageSource::dataChanged(SharedBuffer* data, bool allDataReceived)
{
m_frameCache->destroyIncompleteDecodedData();
#if PLATFORM(IOS)
static const double chunkLoadIntervals[] = {0, 1, 3, 6, 15};
double interval = chunkLoadIntervals[std::min(m_progressiveLoadChunkCount, static_cast<uint16_t>(4))];
bool needsUpdate = false;
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();
if (!isSizeAvailable())
return false;
m_frameCache->growFrames();
return true;
}
bool ImageSource::isAllDataReceived()
{
return isDecoderAvailable() ? m_decoder->isAllDataReceived() : m_frameCache->frameCount();
}
bool ImageSource::isAsyncDecodingRequired()
{
return size().area() * sizeof(RGBA32) >= 100 * KB;
}
SubsamplingLevel ImageSource::maximumSubsamplingLevel()
{
if (m_maximumSubsamplingLevel)
return m_maximumSubsamplingLevel.value();
if (!isDecoderAvailable() || !m_decoder->frameAllowSubsamplingAtIndex(0))
return SubsamplingLevel::Default;
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::subsamplingLevelForScale(float scale)
{
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())));
}
NativeImagePtr ImageSource::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
{
return isDecoderAvailable() ? m_decoder->createFrameImageAtIndex(index, subsamplingLevel) : nullptr;
}
NativeImagePtr ImageSource::frameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const GraphicsContext* targetContext)
{
setDecoderTargetContext(targetContext);
return m_frameCache->frameImageAtIndex(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);
}
}