This source file includes following definitions.
- m_andMaskState
- decodeBMP
- readInfoHeaderSize
- processInfoHeader
- readInfoHeader
- isInfoHeaderValid
- processBitmasks
- processColorTable
- processRLEData
- processNonRLEData
- moveBufferToNextRow
#include "config.h"
#include "platform/image-decoders/bmp/BMPImageReader.h"
namespace WebCore {
BMPImageReader::BMPImageReader(ImageDecoder* parent, size_t decodedAndHeaderOffset, size_t imgDataOffset, bool usesAndMask)
: m_parent(parent)
, m_buffer(0)
, m_decodedOffset(decodedAndHeaderOffset)
, m_headerOffset(decodedAndHeaderOffset)
, m_imgDataOffset(imgDataOffset)
, m_isOS21x(false)
, m_isOS22x(false)
, m_isTopDown(false)
, m_needToProcessBitmasks(false)
, m_needToProcessColorTable(false)
, m_seenNonZeroAlphaPixel(false)
, m_seenZeroAlphaPixel(false)
, m_andMaskState(usesAndMask ? NotYetDecoded : None)
{
memset(&m_infoHeader, 0, sizeof(m_infoHeader));
}
bool BMPImageReader::decodeBMP(bool onlySize)
{
if (!m_infoHeader.biSize && !readInfoHeaderSize())
return false;
if ((m_decodedOffset < (m_headerOffset + m_infoHeader.biSize)) && !processInfoHeader())
return false;
if (onlySize)
return true;
if (m_needToProcessBitmasks && !processBitmasks())
return false;
if (m_needToProcessColorTable && !processColorTable())
return false;
ASSERT(m_buffer);
if (m_buffer->status() == ImageFrame::FrameEmpty) {
if (!m_buffer->setSize(m_parent->size().width(), m_parent->size().height()))
return m_parent->setFailed();
m_buffer->setStatus(ImageFrame::FramePartial);
m_buffer->setHasAlpha(false);
m_buffer->setOriginalFrameRect(IntRect(IntPoint(), m_parent->size()));
if (!m_isTopDown)
m_coord.setY(m_parent->size().height() - 1);
}
if ((m_andMaskState != Decoding) && !pastEndOfImage(0)) {
if ((m_infoHeader.biCompression != RLE4) && (m_infoHeader.biCompression != RLE8) && (m_infoHeader.biCompression != RLE24)) {
const ProcessingResult result = processNonRLEData(false, 0);
if (result != Success)
return (result == Failure) ? m_parent->setFailed() : false;
} else if (!processRLEData())
return false;
}
if ((m_andMaskState == NotYetDecoded) && !m_buffer->hasAlpha()) {
m_coord.setX(0);
m_coord.setY(m_isTopDown ? 0 : (m_parent->size().height() - 1));
m_infoHeader.biBitCount = 1;
m_andMaskState = Decoding;
}
if (m_andMaskState == Decoding) {
const ProcessingResult result = processNonRLEData(false, 0);
if (result != Success)
return (result == Failure) ? m_parent->setFailed() : false;
}
m_buffer->setStatus(ImageFrame::FrameComplete);
return true;
}
bool BMPImageReader::readInfoHeaderSize()
{
ASSERT(m_decodedOffset == m_headerOffset);
if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < 4))
return false;
m_infoHeader.biSize = readUint32(0);
if (((m_headerOffset + m_infoHeader.biSize) < m_headerOffset) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize))))
return m_parent->setFailed();
if (m_infoHeader.biSize == 12)
m_isOS21x = true;
else if ((m_infoHeader.biSize == 40) || isWindowsV4Plus())
;
else if ((m_infoHeader.biSize >= 16) && (m_infoHeader.biSize <= 64) && (!(m_infoHeader.biSize & 3) || (m_infoHeader.biSize == 42) || (m_infoHeader.biSize == 46)))
m_isOS22x = true;
else
return m_parent->setFailed();
return true;
}
bool BMPImageReader::processInfoHeader()
{
ASSERT(m_decodedOffset == m_headerOffset);
if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < m_infoHeader.biSize) || !readInfoHeader())
return false;
m_decodedOffset += m_infoHeader.biSize;
if (!isInfoHeaderValid())
return m_parent->setFailed();
if (!m_parent->setSize(m_infoHeader.biWidth, m_infoHeader.biHeight))
return false;
if (m_infoHeader.biBitCount < 16) {
const uint32_t maxColors = static_cast<uint32_t>(1) << m_infoHeader.biBitCount;
if (!m_infoHeader.biClrUsed || (m_infoHeader.biClrUsed > maxColors))
m_infoHeader.biClrUsed = maxColors;
}
if (m_infoHeader.biCompression == RLE8)
m_infoHeader.biBitCount = 8;
else if (m_infoHeader.biCompression == RLE4)
m_infoHeader.biBitCount = 4;
if (m_infoHeader.biBitCount >= 16)
m_needToProcessBitmasks = true;
else if (m_infoHeader.biBitCount)
m_needToProcessColorTable = true;
return true;
}
bool BMPImageReader::readInfoHeader()
{
m_infoHeader.biCompression = RGB;
m_infoHeader.biClrUsed = 0;
if (m_isOS21x) {
m_infoHeader.biWidth = readUint16(4);
m_infoHeader.biHeight = readUint16(6);
ASSERT(m_andMaskState == None);
m_infoHeader.biBitCount = readUint16(10);
return true;
}
m_infoHeader.biWidth = readUint32(4);
m_infoHeader.biHeight = readUint32(8);
if (m_andMaskState != None)
m_infoHeader.biHeight /= 2;
m_infoHeader.biBitCount = readUint16(14);
if (m_infoHeader.biSize >= 20) {
uint32_t biCompression = readUint32(16);
if ((biCompression == 3) && (m_infoHeader.biBitCount == 1)) {
m_infoHeader.biCompression = HUFFMAN1D;
m_isOS22x = true;
} else if ((biCompression == 4) && (m_infoHeader.biBitCount == 24)) {
m_infoHeader.biCompression = RLE24;
m_isOS22x = true;
} else if (biCompression > 5)
return m_parent->setFailed();
else
m_infoHeader.biCompression = static_cast<CompressionType>(biCompression);
}
if (m_infoHeader.biSize >= 36)
m_infoHeader.biClrUsed = readUint32(32);
if (isWindowsV4Plus()) {
m_bitMasks[0] = readUint32(40);
m_bitMasks[1] = readUint32(44);
m_bitMasks[2] = readUint32(48);
m_bitMasks[3] = readUint32(52);
}
if (m_infoHeader.biHeight < 0) {
m_isTopDown = true;
m_infoHeader.biHeight = -m_infoHeader.biHeight;
}
return true;
}
bool BMPImageReader::isInfoHeaderValid() const
{
if ((m_infoHeader.biWidth <= 0) || !m_infoHeader.biHeight)
return false;
if (m_isTopDown && (m_isOS21x || m_isOS22x))
return false;
if ((m_infoHeader.biBitCount != 1) && (m_infoHeader.biBitCount != 4) && (m_infoHeader.biBitCount != 8) && (m_infoHeader.biBitCount != 24)) {
if (m_isOS21x || m_isOS22x || (m_infoHeader.biBitCount && (m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32)))
return false;
}
switch (m_infoHeader.biCompression) {
case RGB:
if (!m_infoHeader.biBitCount)
return false;
break;
case RLE8:
if (!m_infoHeader.biBitCount || (m_infoHeader.biBitCount > 8))
return false;
break;
case RLE4:
if (!m_infoHeader.biBitCount || (m_infoHeader.biBitCount > 4))
return false;
break;
case BITFIELDS:
if (m_isOS21x || m_isOS22x || ((m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32)))
return false;
break;
case JPEG:
case PNG:
if (m_isOS21x || m_isOS22x || m_infoHeader.biBitCount)
return false;
break;
case HUFFMAN1D:
if (!m_isOS22x || (m_infoHeader.biBitCount != 1))
return false;
break;
case RLE24:
if (!m_isOS22x || (m_infoHeader.biBitCount != 24))
return false;
break;
default:
ASSERT_NOT_REACHED();
return false;
}
if (m_isTopDown && (m_infoHeader.biCompression != RGB) && (m_infoHeader.biCompression != BITFIELDS))
return false;
if ((m_infoHeader.biWidth >= (1 << 16)) || (m_infoHeader.biHeight >= (1 << 16)))
return false;
if ((m_infoHeader.biCompression == JPEG) || (m_infoHeader.biCompression == PNG))
return false;
if (m_infoHeader.biCompression == HUFFMAN1D)
return false;
return true;
}
bool BMPImageReader::processBitmasks()
{
if (m_infoHeader.biCompression != BITFIELDS) {
const int numBits = (m_infoHeader.biBitCount == 16) ? 5 : 8;
for (int i = 0; i <= 2; ++i)
m_bitMasks[i] = ((static_cast<uint32_t>(1) << (numBits * (3 - i))) - 1) ^ ((static_cast<uint32_t>(1) << (numBits * (2 - i))) - 1);
if (m_infoHeader.biBitCount < 32)
m_bitMasks[3] = 0;
else if (!isWindowsV4Plus())
m_bitMasks[3] = static_cast<uint32_t>(0xff000000);
} else if (!isWindowsV4Plus()) {
static const size_t SIZEOF_BITMASKS = 12;
if (((m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS) < (m_headerOffset + m_infoHeader.biSize)) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS))))
return m_parent->setFailed();
if ((m_data->size() - m_decodedOffset) < SIZEOF_BITMASKS)
return false;
m_bitMasks[0] = readUint32(0);
m_bitMasks[1] = readUint32(4);
m_bitMasks[2] = readUint32(8);
m_bitMasks[3] = 0;
m_decodedOffset += SIZEOF_BITMASKS;
}
if (m_imgDataOffset)
m_decodedOffset = m_imgDataOffset;
m_needToProcessBitmasks = false;
for (int i = 0; i < 4; ++i) {
if (m_infoHeader.biBitCount < 32)
m_bitMasks[i] &= ((static_cast<uint32_t>(1) << m_infoHeader.biBitCount) - 1);
uint32_t tempMask = m_bitMasks[i];
if (!tempMask) {
m_bitShiftsRight[i] = m_bitShiftsLeft[i] = 0;
continue;
}
for (int j = 0; j < i; ++j) {
if (tempMask & m_bitMasks[j])
return m_parent->setFailed();
}
for (m_bitShiftsRight[i] = 0; !(tempMask & 1); tempMask >>= 1)
++m_bitShiftsRight[i];
for (m_bitShiftsLeft[i] = 8; tempMask & 1; tempMask >>= 1)
--m_bitShiftsLeft[i];
if (tempMask)
return m_parent->setFailed();
if (m_bitShiftsLeft[i] < 0) {
m_bitShiftsRight[i] -= m_bitShiftsLeft[i];
m_bitShiftsLeft[i] = 0;
}
}
return true;
}
bool BMPImageReader::processColorTable()
{
size_t tableSizeInBytes = m_infoHeader.biClrUsed * (m_isOS21x ? 3 : 4);
if (((m_headerOffset + m_infoHeader.biSize + tableSizeInBytes) < (m_headerOffset + m_infoHeader.biSize)) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize + tableSizeInBytes))))
return m_parent->setFailed();
if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < tableSizeInBytes))
return false;
m_colorTable.resize(m_infoHeader.biClrUsed);
for (size_t i = 0; i < m_infoHeader.biClrUsed; ++i) {
m_colorTable[i].rgbBlue = m_data->data()[m_decodedOffset++];
m_colorTable[i].rgbGreen = m_data->data()[m_decodedOffset++];
m_colorTable[i].rgbRed = m_data->data()[m_decodedOffset++];
if (!m_isOS21x)
++m_decodedOffset;
}
if (m_imgDataOffset)
m_decodedOffset = m_imgDataOffset;
m_needToProcessColorTable = false;
return true;
}
bool BMPImageReader::processRLEData()
{
if (m_decodedOffset > m_data->size())
return false;
while (true) {
if ((m_data->size() - m_decodedOffset) < 2)
return false;
const uint8_t count = m_data->data()[m_decodedOffset];
const uint8_t code = m_data->data()[m_decodedOffset + 1];
if ((count || (code != 1)) && pastEndOfImage(0))
return m_parent->setFailed();
if (!count) {
switch (code) {
case 0:
if (m_coord.x() < m_parent->size().width())
m_buffer->setHasAlpha(true);
moveBufferToNextRow();
m_decodedOffset += 2;
break;
case 1:
if ((m_coord.x() < m_parent->size().width()) || (m_isTopDown ? (m_coord.y() < (m_parent->size().height() - 1)) : (m_coord.y() > 0)))
m_buffer->setHasAlpha(true);
return true;
case 2: {
if ((m_data->size() - m_decodedOffset) < 4)
return false;
const uint8_t dx = m_data->data()[m_decodedOffset + 2];
const uint8_t dy = m_data->data()[m_decodedOffset + 3];
if (dx || dy)
m_buffer->setHasAlpha(true);
if (((m_coord.x() + dx) > m_parent->size().width()) || pastEndOfImage(dy))
return m_parent->setFailed();
m_coord.move(dx, m_isTopDown ? dy : -dy);
m_decodedOffset += 4;
break;
}
default: {
m_decodedOffset += 2;
const ProcessingResult result = processNonRLEData(true, code);
if (result == Failure)
return m_parent->setFailed();
if (result == InsufficientData) {
m_decodedOffset -= 2;
return false;
}
break;
}
}
} else {
const int endX = std::min(m_coord.x() + count, m_parent->size().width());
if (m_infoHeader.biCompression == RLE24) {
if ((m_data->size() - m_decodedOffset) < 4)
return false;
fillRGBA(endX, m_data->data()[m_decodedOffset + 3], m_data->data()[m_decodedOffset + 2], code, 0xff);
m_decodedOffset += 4;
} else {
size_t colorIndexes[2] = {code, code};
if (m_infoHeader.biCompression == RLE4) {
colorIndexes[0] = (colorIndexes[0] >> 4) & 0xf;
colorIndexes[1] &= 0xf;
}
for (int which = 0; m_coord.x() < endX; ) {
if (colorIndexes[which] < m_infoHeader.biClrUsed)
setI(colorIndexes[which]);
else
setRGBA(0, 0, 0, 255);
which = !which;
}
m_decodedOffset += 2;
}
}
}
}
BMPImageReader::ProcessingResult BMPImageReader::processNonRLEData(bool inRLE, int numPixels)
{
if (m_decodedOffset > m_data->size())
return InsufficientData;
if (!inRLE)
numPixels = m_parent->size().width();
const int endX = m_coord.x() + numPixels;
if (endX > m_parent->size().width())
return Failure;
const size_t pixelsPerByte = 8 / m_infoHeader.biBitCount;
const size_t bytesPerPixel = m_infoHeader.biBitCount / 8;
const size_t unpaddedNumBytes = (m_infoHeader.biBitCount < 16) ? ((numPixels + pixelsPerByte - 1) / pixelsPerByte) : (numPixels * bytesPerPixel);
const size_t alignBits = inRLE ? 1 : 3;
const size_t paddedNumBytes = (unpaddedNumBytes + alignBits) & ~alignBits;
while (!pastEndOfImage(0)) {
if ((m_data->size() - m_decodedOffset) < paddedNumBytes)
return InsufficientData;
if (m_infoHeader.biBitCount < 16) {
const uint8_t mask = (1 << m_infoHeader.biBitCount) - 1;
for (size_t byte = 0; byte < unpaddedNumBytes; ++byte) {
uint8_t pixelData = m_data->data()[m_decodedOffset + byte];
for (size_t pixel = 0; (pixel < pixelsPerByte) && (m_coord.x() < endX); ++pixel) {
const size_t colorIndex = (pixelData >> (8 - m_infoHeader.biBitCount)) & mask;
if (m_andMaskState == Decoding) {
if (colorIndex) {
setRGBA(0, 0, 0, 0);
m_buffer->setHasAlpha(true);
} else
m_coord.move(1, 0);
} else {
if (colorIndex < m_infoHeader.biClrUsed)
setI(colorIndex);
else
setRGBA(0, 0, 0, 255);
}
pixelData <<= m_infoHeader.biBitCount;
}
}
} else {
while (m_coord.x() < endX) {
const uint32_t pixel = readCurrentPixel(bytesPerPixel);
int alpha = getAlpha(pixel);
if (!m_seenNonZeroAlphaPixel && !alpha) {
m_seenZeroAlphaPixel = true;
alpha = 255;
} else {
m_seenNonZeroAlphaPixel = true;
if (m_seenZeroAlphaPixel) {
m_buffer->zeroFillPixelData();
m_seenZeroAlphaPixel = false;
} else if (alpha != 255)
m_buffer->setHasAlpha(true);
}
setRGBA(getComponent(pixel, 0), getComponent(pixel, 1),
getComponent(pixel, 2), alpha);
}
}
m_decodedOffset += paddedNumBytes;
if (inRLE)
return Success;
moveBufferToNextRow();
}
return Success;
}
void BMPImageReader::moveBufferToNextRow()
{
m_coord.move(-m_coord.x(), m_isTopDown ? 1 : -1);
}
}