ICOImageDecoder.cpp [plain text]
#include "config.h"
#include "ICOImageDecoder.h"
namespace WebCore {
static const size_t sizeOfDirectory = 6;
static const size_t sizeOfDirEntry = 16;
void ICOImageDecoder::decodeImage(SharedBuffer* data)
{
if ((m_decodedOffset < sizeOfDirectory) && !processDirectory(data))
return;
if ((m_decodedOffset < (sizeOfDirectory + (m_directory.idCount * sizeOfDirEntry)))
&& !processDirectoryEntries(data))
return;
if (m_imageType == Unknown) {
if (data->size() < (m_dirEntry.dwImageOffset + 4))
return;
m_imageType =
strncmp(&data->data()[m_dirEntry.dwImageOffset], "\x89PNG", 4) ?
BMP : PNG;
}
if (m_imageType == PNG)
decodePNG(data);
else {
decodeBMP(data);
}
}
RGBA32Buffer* ICOImageDecoder::frameBufferAtIndex(size_t index)
{
return (m_imageType == PNG) ? m_pngDecoder.frameBufferAtIndex(0) :
BMPImageReader::frameBufferAtIndex(0);
}
bool ICOImageDecoder::isSizeAvailable() const
{
return (m_imageType == PNG) ? m_pngDecoder.isSizeAvailable() :
BMPImageReader::isSizeAvailable();
}
IntSize ICOImageDecoder::size() const
{
return (m_imageType == PNG) ? m_pngDecoder.size() : BMPImageReader::size();
}
bool ICOImageDecoder::processDirectory(SharedBuffer* data)
{
ASSERT(!m_decodedOffset);
if (data->size() < sizeOfDirectory)
return false;
const uint16_t fileType = readUint16(data, 2);
m_directory.idCount = readUint16(data, 4);
m_decodedOffset = sizeOfDirectory;
enum {
ICON = 1,
CURSOR = 2,
};
if (((fileType != ICON) && (fileType != CURSOR)) ||
(m_directory.idCount == 0))
m_failed = true;
return !m_failed;
}
bool ICOImageDecoder::processDirectoryEntries(SharedBuffer* data)
{
ASSERT(m_decodedOffset == sizeOfDirectory);
if ((m_decodedOffset > data->size()) || (data->size() - m_decodedOffset) <
(m_directory.idCount * sizeOfDirEntry))
return false;
for (int i = 0; i < m_directory.idCount; ++i) {
const IconDirectoryEntry dirEntry = readDirectoryEntry(data);
if ((i == 0) || isBetterEntry(dirEntry))
m_dirEntry = dirEntry;
}
if ((m_dirEntry.dwImageOffset < m_decodedOffset) ||
((m_dirEntry.dwImageOffset + 4) < m_dirEntry.dwImageOffset)) {
m_failed = true;
return false;
}
m_decodedOffset = m_headerOffset = m_dirEntry.dwImageOffset;
return true;
}
ICOImageDecoder::IconDirectoryEntry ICOImageDecoder::readDirectoryEntry(
SharedBuffer* data)
{
IconDirectoryEntry entry;
entry.bWidth = static_cast<uint8_t>(data->data()[m_decodedOffset]);
if (entry.bWidth == 0)
entry.bWidth = 256;
entry.bHeight = static_cast<uint8_t>(data->data()[m_decodedOffset + 1]);
if (entry.bHeight == 0)
entry.bHeight = 256;
entry.wBitCount = readUint16(data, 6);
entry.dwImageOffset = readUint32(data, 12);
if (!entry.wBitCount) {
uint8_t colorCount = data->data()[m_decodedOffset + 2];
if (colorCount) {
for (--colorCount; colorCount; colorCount >>= 1)
++entry.wBitCount;
}
}
m_decodedOffset += sizeOfDirEntry;
return entry;
}
bool ICOImageDecoder::isBetterEntry(const IconDirectoryEntry& entry) const
{
const IntSize entrySize(entry.bWidth, entry.bHeight);
const IntSize dirEntrySize(m_dirEntry.bWidth, m_dirEntry.bHeight);
const int entryArea = entry.bWidth * entry.bHeight;
const int dirEntryArea = m_dirEntry.bWidth * m_dirEntry.bHeight;
if ((entrySize != dirEntrySize) && !m_preferredIconSize.isEmpty()) {
if (entrySize == m_preferredIconSize)
return true;
if (dirEntrySize == m_preferredIconSize)
return false;
if (entryArea != dirEntryArea) {
return (entryArea < dirEntryArea)
&& (entryArea >= (m_preferredIconSize.width() * m_preferredIconSize.height()));
}
}
if (entryArea != dirEntryArea)
return (entryArea > dirEntryArea);
return (entry.wBitCount > m_dirEntry.wBitCount);
}
void ICOImageDecoder::decodePNG(SharedBuffer* data)
{
RefPtr<SharedBuffer> pngData(
SharedBuffer::create(&data->data()[m_dirEntry.dwImageOffset],
data->size() - m_dirEntry.dwImageOffset));
m_pngDecoder.setData(pngData.get(), true);
m_pngDecoder.frameBufferAtIndex(0);
m_failed = m_pngDecoder.failed();
if (isSizeAvailable() && ((size().width() != m_dirEntry.bWidth) ||
(size().height() != m_dirEntry.bHeight)))
m_failed = true;
}
}