/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bmp.h" #include "ifilesystem.h" typedef unsigned char byte; #include "imagelib.h" #include "bytestreamutils.h" typedef unsigned char PaletteEntry[4]; typedef struct { char id[2]; unsigned long fileSize; unsigned long reserved0; unsigned long bitmapDataOffset; unsigned long bitmapHeaderSize; unsigned long width; unsigned long height; unsigned short planes; unsigned short bitsPerPixel; unsigned long compression; unsigned long bitmapDataSize; unsigned long hRes; unsigned long vRes; unsigned long colors; unsigned long importantColors; PaletteEntry palette[256]; } BMPHeader_t; class ReadPixel8 { PaletteEntry* m_palette; public: ReadPixel8( PaletteEntry* palette ) : m_palette( palette ){ } void operator()( PointerInputStream& inputStream, byte*& pixbuf ) const { byte palIndex; inputStream.read( &palIndex, 1 ); *pixbuf++ = m_palette[palIndex][2]; *pixbuf++ = m_palette[palIndex][1]; *pixbuf++ = m_palette[palIndex][0]; *pixbuf++ = 0xff; } }; class ReadPixel16 { public: void operator()( PointerInputStream& inputStream, byte*& pixbuf ) const { unsigned short shortPixel; inputStream.read( reinterpret_cast( &shortPixel ), sizeof( unsigned short ) ); //!\todo Is this endian safe? *pixbuf++ = static_cast( shortPixel & ( 31 << 10 ) ) >> 7; *pixbuf++ = static_cast( shortPixel & ( 31 << 5 ) ) >> 2; *pixbuf++ = static_cast( shortPixel & ( 31 ) ) << 3; *pixbuf++ = 0xff; } }; class ReadPixel24 { public: void operator()( PointerInputStream& inputStream, byte*& pixbuf ) const { byte bgr[3]; inputStream.read( bgr, 3 ); *pixbuf++ = bgr[2]; *pixbuf++ = bgr[1]; *pixbuf++ = bgr[0]; *pixbuf++ = 255; } }; class ReadPixel32 { public: void operator()( PointerInputStream& inputStream, byte*& pixbuf ) const { byte bgra[4]; inputStream.read( bgra, 4 ); *pixbuf++ = bgra[2]; *pixbuf++ = bgra[1]; *pixbuf++ = bgra[0]; *pixbuf++ = bgra[3]; } }; template void ReadBMP( PointerInputStream& inputStream, byte* bmpRGBA, int rows, int columns, ReadPixel readPixel ){ for ( int row = rows - 1; row >= 0; row-- ) { byte* pixbuf = bmpRGBA + row * columns * 4; for ( int column = 0; column < columns; column++ ) { readPixel( inputStream, pixbuf ); } } } Image* LoadBMPBuff( PointerInputStream& inputStream, std::size_t length ){ BMPHeader_t bmpHeader; inputStream.read( reinterpret_cast( bmpHeader.id ), 2 ); bmpHeader.fileSize = istream_read_uint32_le( inputStream ); bmpHeader.reserved0 = istream_read_uint32_le( inputStream ); bmpHeader.bitmapDataOffset = istream_read_uint32_le( inputStream ); bmpHeader.bitmapHeaderSize = istream_read_uint32_le( inputStream ); bmpHeader.width = istream_read_uint32_le( inputStream ); bmpHeader.height = istream_read_uint32_le( inputStream ); bmpHeader.planes = istream_read_uint16_le( inputStream ); bmpHeader.bitsPerPixel = istream_read_uint16_le( inputStream ); bmpHeader.compression = istream_read_uint32_le( inputStream ); bmpHeader.bitmapDataSize = istream_read_uint32_le( inputStream ); bmpHeader.hRes = istream_read_uint32_le( inputStream ); bmpHeader.vRes = istream_read_uint32_le( inputStream ); bmpHeader.colors = istream_read_uint32_le( inputStream ); bmpHeader.importantColors = istream_read_uint32_le( inputStream ); if ( bmpHeader.bitsPerPixel == 8 ) { int paletteSize = bmpHeader.colors * 4; inputStream.read( reinterpret_cast( bmpHeader.palette ), paletteSize ); } if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' ) { globalErrorStream() << "LoadBMP: only Windows-style BMP files supported\n"; return 0; } if ( bmpHeader.fileSize != length ) { globalErrorStream() << "LoadBMP: header size does not match file size (" << Unsigned( bmpHeader.fileSize ) << " vs. " << Unsigned( length ) << ")\n"; return 0; } if ( bmpHeader.compression != 0 ) { globalErrorStream() << "LoadBMP: only uncompressed BMP files supported\n"; return 0; } if ( bmpHeader.bitsPerPixel < 8 ) { globalErrorStream() << "LoadBMP: monochrome and 4-bit BMP files not supported\n"; return 0; } int columns = bmpHeader.width; int rows = bmpHeader.height; if ( rows < 0 ) { rows = -rows; } RGBAImage* image = new RGBAImage( columns, rows ); switch ( bmpHeader.bitsPerPixel ) { case 8: ReadBMP( inputStream, image->getRGBAPixels(), rows, columns, ReadPixel8( bmpHeader.palette ) ); break; case 16: ReadBMP( inputStream, image->getRGBAPixels(), rows, columns, ReadPixel16() ); break; case 24: ReadBMP( inputStream, image->getRGBAPixels(), rows, columns, ReadPixel24() ); break; case 32: ReadBMP( inputStream, image->getRGBAPixels(), rows, columns, ReadPixel32() ); break; default: globalErrorStream() << "LoadBMP: illegal pixel_size '" << bmpHeader.bitsPerPixel << "'\n"; image->release(); return 0; } return image; } Image* LoadBMP( ArchiveFile& file ){ ScopedArchiveBuffer buffer( file ); PointerInputStream inputStream( buffer.buffer ); return LoadBMPBuff( inputStream, buffer.length ); }