in imagecore/formats/internal/bmp.cpp [158:441]
bool ImageReaderBMP::readImage(Image* dest)
{
if( !supportsOutputColorModel(dest->getColorModel()) ) {
return false;
}
ImageRGBA* destImage = dest->asRGBA();
// Security checks - make sure we don't exceed the dest capacity, and that nothing has somehow changed since we read the header.
SECURE_ASSERT(destImage->getWidth() == m_Width && destImage->getHeight() == m_Height);
unsigned int destPitch = 0;
uint8_t* destBuffer = destImage->lockRect(m_Width, m_Height, destPitch);
unsigned int destCapacity = destImage->getImageSize();
SECURE_ASSERT(SafeUMul(m_Width, 4U) <= destPitch);
SECURE_ASSERT(destBuffer && destPitch);
/* Load Color Table */
unsigned int ColorTableSize = 0;
if (m_BitmapHeader->BitCount == 1) {
ColorTableSize = 2;
} else if (m_BitmapHeader->BitCount == 4) {
ColorTableSize = 16;
} else if (m_BitmapHeader->BitCount == 8) {
ColorTableSize = 256;
}
BGRA* ColorTable = NULL;
if( ColorTableSize > 0 ) {
ColorTable = new BGRA[ColorTableSize];
if( ColorTable == NULL ) {
return false;
}
if( m_BitmapHeader->ClrUsed == 0 ) {
m_BitmapHeader->ClrUsed = ColorTableSize;
}
if (m_BitmapHeader->ClrUsed < ColorTableSize) {
memset(ColorTable + m_BitmapHeader->ClrUsed, 0, sizeof(BGRA) * (ColorTableSize - m_BitmapHeader->ClrUsed));
}
if( m_BitmapHeader->ClrUsed > ColorTableSize || m_Source->read((char*)ColorTable, SafeUMul((unsigned int)sizeof(BGRA), m_BitmapHeader->ClrUsed)) != SafeUMul((unsigned int)sizeof(BGRA), m_BitmapHeader->ClrUsed) ) {
delete[] ColorTable;
return false;
}
}
unsigned int LineWidth = align(m_Width * m_BitmapHeader->BitCount, 32) / 8;
uint8_t* Line = new uint8_t[LineWidth];
if( Line == NULL ) {
delete [] ColorTable;
return false;
}
// Read and discard everything up to the image data, can't seek.
if( m_BitmapFileHeader.BitsOffset > m_Source->tell() ) {
uint64_t remainingBytes = SafeUSub((uint64_t)m_BitmapFileHeader.BitsOffset, m_Source->tell());
uint8_t discardBuffer[1024];
while( remainingBytes > 0 ) {
uint64_t bytesToRead = remainingBytes < 1024 ? remainingBytes : 1024;
uint64_t bytesRead = m_Source->read(discardBuffer, bytesToRead);
if( bytesRead == 0 ) {
return false;
}
remainingBytes = SafeUSub(remainingBytes, bytesRead);
}
} else if( m_BitmapFileHeader.BitsOffset < m_Source->tell() ) {
// Malformed BMP.
return false;
}
bool result = true;
if (m_BitmapHeader->Compression == 0) {
for (unsigned int i = 0; i < m_Height; i++) {
if( m_Source->read((char*) Line, LineWidth) != LineWidth ) {
result = false;
goto failedRead;
}
uint8_t* LinePtr = Line;
SECURE_ASSERT(LinePtr < Line + LineWidth);
uint8_t* DestPtr = destBuffer + destPitch * (m_Height - i - 1);
SECURE_ASSERT(DestPtr >= destBuffer);
for (unsigned int j = 0; j < m_Width; j++) {
int OutIndex = j * 4;
if (m_BitmapHeader->BitCount == 1) {
unsigned int Color = *((uint8_t*) LinePtr);
for (int k = 0; k < 8; k++) {
DestPtr[OutIndex + 0] = ColorTable[Color & 0x80 ? 1 : 0].Red;
DestPtr[OutIndex + 1] = ColorTable[Color & 0x80 ? 1 : 0].Green;
DestPtr[OutIndex + 2] = ColorTable[Color & 0x80 ? 1 : 0].Blue;
DestPtr[OutIndex + 3] = ColorTable[Color & 0x80 ? 1 : 0].Alpha;
OutIndex += 4;
if(OutIndex > destPitch - 4) { // handle widths that are not multiples of 8
break;
}
Color <<= 1;
}
LinePtr++;
j += 7;
} else if (m_BitmapHeader->BitCount == 4) {
unsigned int Color = *((uint8_t*) LinePtr);
DestPtr[OutIndex + 0] = ColorTable[(Color >> 4) & 0x0f].Red;
DestPtr[OutIndex + 1] = ColorTable[(Color >> 4) & 0x0f].Green;
DestPtr[OutIndex + 2] = ColorTable[(Color >> 4) & 0x0f].Blue;
DestPtr[OutIndex + 3] = ColorTable[(Color >> 4) & 0x0f].Alpha;
j++;
LinePtr++;
if (j < m_Width) {
OutIndex = j * 4;
DestPtr[OutIndex + 0] = ColorTable[Color & 0x0f].Red;
DestPtr[OutIndex + 1] = ColorTable[Color & 0x0f].Green;
DestPtr[OutIndex + 2] = ColorTable[Color & 0x0f].Blue;
DestPtr[OutIndex + 3] = ColorTable[Color & 0x0f].Alpha;
}
} else if (m_BitmapHeader->BitCount == 8) {
unsigned int Color = *((uint8_t*) LinePtr);
DestPtr[OutIndex + 0] = ColorTable[Color].Red;
DestPtr[OutIndex + 1] = ColorTable[Color].Green;
DestPtr[OutIndex + 2] = ColorTable[Color].Blue;
DestPtr[OutIndex + 3] = ColorTable[Color].Alpha;
LinePtr++;
} else if (m_BitmapHeader->BitCount == 16) {
uint16_t Color = letoh16(*((uint16_t*) LinePtr));
DestPtr[OutIndex + 0] = ((Color >> 10) & 0x1f) << 3;
DestPtr[OutIndex + 1] = ((Color >> 5) & 0x1f) << 3;
DestPtr[OutIndex + 2] = (Color & 0x1f) << 3;
DestPtr[OutIndex + 3] = 255;
LinePtr += 2;
} else if (m_BitmapHeader->BitCount == 24) {
DestPtr[OutIndex + 2] = LinePtr[0];
DestPtr[OutIndex + 1] = LinePtr[1];
DestPtr[OutIndex + 0] = LinePtr[2];
DestPtr[OutIndex + 3] = 255;
LinePtr += 3;
} else if (m_BitmapHeader->BitCount == 32) {
DestPtr[OutIndex + 2] = LinePtr[0];
DestPtr[OutIndex + 1] = LinePtr[1];
DestPtr[OutIndex + 0] = LinePtr[2];
DestPtr[OutIndex + 3] = 255;
LinePtr += 4;
} else {
result = false;
goto failedRead;
}
}
}
} else if (m_BitmapHeader->Compression == 1) { // RLE 8
// Just memset the whole buffer, don't want to keep track of which regions actually get written to by the RLE decoder.
memset(destBuffer, 0, destCapacity);
uint8_t Count = 0;
uint8_t ColorIndex = 0;
int x = 0, y = 0;
if( ColorTable == NULL || ColorTableSize == 0 ) {
result = false;
goto failedRead;
}
while( true ) {
if( m_Source->read((char*) &Count, sizeof(uint8_t)) != sizeof(uint8_t) ) {
result = false;
goto failedRead;
}
if( m_Source->read((char*) &ColorIndex, sizeof(uint8_t)) != sizeof(uint8_t) ) {
result = false;
goto failedRead;
}
if( ColorIndex >= ColorTableSize ) {
result = false;
goto failedRead;
}
if (Count > 0) {
int OutIndex = x * 4 + (m_Height - y - 1) * destPitch;
if( OutIndex < 0 || OutIndex + Count * 4 > (int)destCapacity ) {
result = false;
goto failedRead;
}
for (int k = 0; k < Count; k++) {
destBuffer[OutIndex + k * 4 + 0] = ColorTable[ColorIndex].Red;
destBuffer[OutIndex + k * 4 + 1] = ColorTable[ColorIndex].Green;
destBuffer[OutIndex + k * 4 + 2] = ColorTable[ColorIndex].Blue;
destBuffer[OutIndex + k * 4 + 3] = ColorTable[ColorIndex].Alpha;
}
x += Count;
} else if (Count == 0) {
int Flag = ColorIndex;
if (Flag == 0) {
x = 0;
y++;
} else if (Flag == 1) {
break;
} else if (Flag == 2) {
char rx = 0;
char ry = 0;
if( m_Source->read((char*) &rx, sizeof(char)) != (int)sizeof(char) ) {
result = false;
goto failedRead;
}
if( m_Source->read((char*) &ry, sizeof(char)) != (int)sizeof(char) ) {
result = false;
goto failedRead;
}
x += rx;
y += ry;
} else {
Count = Flag;
int OutIndex = x * 4 + (m_Height - y - 1) * destPitch;
if( OutIndex < 0 || OutIndex + Count * 4 > (int)destCapacity ) {
result = false;
goto failedRead;
}
for (int k = 0; k < Count; k++) {
if( m_Source->read((char*) &ColorIndex, sizeof(uint8_t)) != (int)sizeof(uint8_t) ) {
result = false;
goto failedRead;
}
if( ColorIndex >= ColorTableSize ) {
result = false;
goto failedRead;
}
destBuffer[OutIndex + k * 4 + 0] = ColorTable[ColorIndex].Red;
destBuffer[OutIndex + k * 4 + 1] = ColorTable[ColorIndex].Green;
destBuffer[OutIndex + k * 4 + 2] = ColorTable[ColorIndex].Blue;
destBuffer[OutIndex + k * 4 + 3] = ColorTable[ColorIndex].Alpha;
}
x += Count;
uint64_t filePos = m_Source->tell();
if (filePos & 1) {
uint8_t skip;
m_Source->read(&skip, 1);
}
}
}
}
} else if (m_BitmapHeader->Compression == 2) { // RLE 4
result = false;
goto failedRead;
} else if (m_BitmapHeader->Compression == 3) { // BITFIELDS
if( m_BitmapHeader->HeaderSize < sizeof(BITMAP_HEADER_EXTENDED) ||( m_BitmapHeader->BitCount != 16 && m_BitmapHeader->BitCount != 32) ) {
result = false;
goto failedRead;
}
BITMAP_HEADER_EXTENDED* extendedHeader = (BITMAP_HEADER_EXTENDED*)m_BitmapHeader;
unsigned int BitCountRed = BitCountByMask(extendedHeader->RedMask);
unsigned int BitCountGreen = BitCountByMask(extendedHeader->GreenMask);
unsigned int BitCountBlue = BitCountByMask(extendedHeader->BlueMask);
unsigned int BitCountAlpha = BitCountByMask(extendedHeader->AlphaMask);
for (unsigned int i = 0; i < m_Height; i++) {
if( m_Source->read((char*) Line, LineWidth) != LineWidth ) {
result = false;
goto failedRead;
}
uint8_t* LinePtr = Line;
uint8_t* DestPtr = destBuffer + destPitch * (m_Height - i - 1);
for (unsigned int j = 0; j < m_Width; j++) {
unsigned int Color = 0;
int OutIndex = j * 4;
if (m_BitmapHeader->BitCount == 16) {
Color = *((uint16_t*) LinePtr);
LinePtr += 2;
} else if (m_BitmapHeader->BitCount == 32) {
Color = *((uint32_t*) LinePtr);
LinePtr += 4;
} else {
// Other formats are not valid
}
DestPtr[OutIndex + 0] = Convert(ComponentByMask(Color, extendedHeader->RedMask), BitCountRed, 8);
DestPtr[OutIndex + 1] = Convert(ComponentByMask(Color, extendedHeader->GreenMask), BitCountGreen, 8);
DestPtr[OutIndex + 2] = Convert(ComponentByMask(Color, extendedHeader->BlueMask), BitCountBlue, 8);
DestPtr[OutIndex + 3] = Convert(ComponentByMask(Color, extendedHeader->AlphaMask), BitCountAlpha, 8);
}
}
} else {
result = false;
goto failedRead;
}
failedRead:
delete [] ColorTable;
delete [] Line;
destImage->unlockRect();
return result;
}