bool ImageReaderBMP::readImage()

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;
}