#include "config.h"
#include "ImageDecoder.h"
#include "BMPImageDecoder.h"
#include "GIFImageDecoder.h"
#include "ICOImageDecoder.h"
#include "JPEGImageDecoder.h"
#include "PNGImageDecoder.h"
#include "SharedBuffer.h"
#if USE(WEBP)
#include "WEBPImageDecoder.h"
#endif
#include <algorithm>
#include <cmath>
using namespace std;
namespace WebCore {
namespace {
unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const SharedBuffer& sharedBuffer, unsigned offset)
{
unsigned bytesExtracted = 0;
const char* moreData;
while (unsigned moreDataLength = sharedBuffer.getSomeData(moreData, offset)) {
unsigned bytesToCopy = min(bufferLength - bytesExtracted, moreDataLength);
memcpy(buffer + bytesExtracted, moreData, bytesToCopy);
bytesExtracted += bytesToCopy;
if (bytesExtracted == bufferLength)
break;
offset += bytesToCopy;
}
return bytesExtracted;
}
bool matchesGIFSignature(char* contents)
{
return !memcmp(contents, "GIF87a", 6) || !memcmp(contents, "GIF89a", 6);
}
bool matchesPNGSignature(char* contents)
{
return !memcmp(contents, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8);
}
bool matchesJPEGSignature(char* contents)
{
return !memcmp(contents, "\xFF\xD8\xFF", 3);
}
#if USE(WEBP)
bool matchesWebPSignature(char* contents)
{
return !memcmp(contents, "RIFF", 4) && !memcmp(contents + 8, "WEBPVP", 6);
}
#endif
bool matchesBMPSignature(char* contents)
{
return !memcmp(contents, "BM", 2);
}
bool matchesICOSignature(char* contents)
{
return !memcmp(contents, "\x00\x00\x01\x00", 4);
}
bool matchesCURSignature(char* contents)
{
return !memcmp(contents, "\x00\x00\x02\x00", 4);
}
}
std::unique_ptr<ImageDecoder> ImageDecoder::create(const SharedBuffer& data, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
{
static const unsigned lengthOfLongestSignature = 14; char contents[lengthOfLongestSignature];
unsigned length = copyFromSharedBuffer(contents, lengthOfLongestSignature, data, 0);
if (length < lengthOfLongestSignature)
return nullptr;
if (matchesGIFSignature(contents))
return std::unique_ptr<ImageDecoder> { std::make_unique<GIFImageDecoder>(alphaOption, gammaAndColorProfileOption) };
if (matchesPNGSignature(contents))
return std::unique_ptr<ImageDecoder> { std::make_unique<PNGImageDecoder>(alphaOption, gammaAndColorProfileOption) };
if (matchesICOSignature(contents) || matchesCURSignature(contents))
return std::unique_ptr<ImageDecoder> { std::make_unique<ICOImageDecoder>(alphaOption, gammaAndColorProfileOption) };
if (matchesJPEGSignature(contents))
return std::unique_ptr<ImageDecoder> { std::make_unique<JPEGImageDecoder>(alphaOption, gammaAndColorProfileOption) };
#if USE(WEBP)
if (matchesWebPSignature(contents))
return std::unique_ptr<ImageDecoder> { std::make_unique<WEBPImageDecoder>(alphaOption, gammaAndColorProfileOption) };
#endif
if (matchesBMPSignature(contents))
return std::unique_ptr<ImageDecoder> { std::make_unique<BMPImageDecoder>(alphaOption, gammaAndColorProfileOption) };
return nullptr;
}
namespace {
enum MatchType {
Exact,
UpperBound,
LowerBound
};
inline void fillScaledValues(Vector<int>& scaledValues, double scaleRate, int length)
{
double inflateRate = 1. / scaleRate;
scaledValues.reserveCapacity(static_cast<int>(length * scaleRate + 0.5));
for (int scaledIndex = 0; ; ++scaledIndex) {
int index = static_cast<int>(scaledIndex * inflateRate + 0.5);
if (index >= length)
break;
scaledValues.append(index);
}
}
template <MatchType type> int getScaledValue(const Vector<int>& scaledValues, int valueToMatch, int searchStart)
{
if (scaledValues.isEmpty())
return valueToMatch;
const int* dataStart = scaledValues.data();
const int* dataEnd = dataStart + scaledValues.size();
const int* matched = std::lower_bound(dataStart + searchStart, dataEnd, valueToMatch);
switch (type) {
case Exact:
return matched != dataEnd && *matched == valueToMatch ? matched - dataStart : -1;
case LowerBound:
return matched != dataEnd && *matched == valueToMatch ? matched - dataStart : matched - dataStart - 1;
case UpperBound:
default:
return matched != dataEnd ? matched - dataStart : -1;
}
}
}
bool ImageDecoder::frameIsCompleteAtIndex(size_t index)
{
LockHolder locker(m_lock);
ImageFrame* buffer = frameBufferAtIndex(index);
return buffer && buffer->isComplete();
}
bool ImageDecoder::frameHasAlphaAtIndex(size_t index) const
{
if (m_frameBufferCache.size() <= index)
return true;
if (m_frameBufferCache[index].isComplete())
return m_frameBufferCache[index].hasAlpha();
return true;
}
unsigned ImageDecoder::frameBytesAtIndex(size_t index) const
{
if (m_frameBufferCache.size() <= index)
return 0;
return (m_size.area() * sizeof(RGBA32)).unsafeGet();
}
float ImageDecoder::frameDurationAtIndex(size_t index)
{
LockHolder locker(m_lock);
ImageFrame* buffer = frameBufferAtIndex(index);
if (!buffer || buffer->isEmpty())
return 0;
const float duration = buffer->duration() / 1000.0f;
if (duration < 0.011f)
return 0.100f;
return duration;
}
NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel, DecodingMode)
{
if (size().isEmpty())
return nullptr;
LockHolder locker(m_lock);
ImageFrame* buffer = frameBufferAtIndex(index);
if (!buffer || buffer->isEmpty() || !buffer->hasBackingStore())
return nullptr;
return buffer->backingStore()->image();
}
void ImageDecoder::prepareScaleDataIfNecessary()
{
m_scaled = false;
m_scaledColumns.clear();
m_scaledRows.clear();
int width = size().width();
int height = size().height();
int numPixels = height * width;
if (m_maxNumPixels <= 0 || numPixels <= m_maxNumPixels)
return;
m_scaled = true;
double scale = sqrt(m_maxNumPixels / (double)numPixels);
fillScaledValues(m_scaledColumns, scale, width);
fillScaledValues(m_scaledRows, scale, height);
}
int ImageDecoder::upperBoundScaledX(int origX, int searchStart)
{
return getScaledValue<UpperBound>(m_scaledColumns, origX, searchStart);
}
int ImageDecoder::lowerBoundScaledX(int origX, int searchStart)
{
return getScaledValue<LowerBound>(m_scaledColumns, origX, searchStart);
}
int ImageDecoder::upperBoundScaledY(int origY, int searchStart)
{
return getScaledValue<UpperBound>(m_scaledRows, origY, searchStart);
}
int ImageDecoder::lowerBoundScaledY(int origY, int searchStart)
{
return getScaledValue<LowerBound>(m_scaledRows, origY, searchStart);
}
int ImageDecoder::scaledY(int origY, int searchStart)
{
return getScaledValue<Exact>(m_scaledRows, origY, searchStart);
}
}