1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2003 The GemRB Project
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  */
20 
21 #include "BMPImporter.h"
22 
23 #include "RGBAColor.h"
24 
25 #include "Interface.h"
26 #include "Video.h"
27 
28 using namespace GemRB;
29 
30 #define BMP_HEADER_SIZE  54
31 
BMPImporter(void)32 BMPImporter::BMPImporter(void)
33 {
34 	Palette = NULL;
35 	pixels = NULL;
36 	Size = Width = Height = Compression = ImageSize = Planes = 0;
37 	BitCount = PaddedRowLength = NumColors = 0;
38 }
39 
~BMPImporter(void)40 BMPImporter::~BMPImporter(void)
41 {
42 	free( Palette );
43 	free( pixels );
44 }
45 
Open(DataStream * stream)46 bool BMPImporter::Open(DataStream* stream)
47 {
48 	str = stream;
49 	//we release the previous pixel data
50 	free( pixels );
51 	pixels = NULL;
52 	free( Palette );
53 	Palette = NULL;
54 
55 	//BITMAPFILEHEADER
56 	char Signature[2];
57 	ieDword FileSize, DataOffset;
58 
59 	str->Read( Signature, 2 );
60 	if (strncmp( Signature, "BM", 2 ) != 0) {
61 		Log(ERROR, "BMPImporter", "Not a valid BMP File.");
62 		return false;
63 	}
64 	str->ReadDword( &FileSize );
65 	str->Seek( 4, GEM_CURRENT_POS );
66 	str->ReadDword( &DataOffset );
67 
68 	//BITMAPINFOHEADER
69 
70 	str->ReadDword( &Size );
71 	//some IE palettes are of a different format (OS/2 BMP)!
72 	if (Size < 24) {
73 		Log(ERROR, "BMPImporter", "OS/2 Bitmap, not supported.");
74 		return false;
75 	}
76 	str->ReadDword( &Width );
77 	str->ReadDword( &Height );
78 	str->ReadWord( &Planes );
79 	str->ReadWord( &BitCount );
80 	str->ReadDword( &Compression );
81 	str->ReadDword( &ImageSize );
82 	//24 = the already read bytes 3x4+2x2+2x4
83 	//this is normally 16
84 	str->Seek( Size-24, GEM_CURRENT_POS );
85 	//str->ReadDword(&Hres );
86 	//str->ReadDword(&Vres );
87 	//str->ReadDword(&ColorsUsed );
88 	//str->ReadDword(&ColorsImportant );
89 	if (Compression != 0) {
90 		Log(ERROR, "BMPImporter", "Compressed %d-bits Image, not supported.", BitCount);
91 		return false;
92 	}
93 	//COLORTABLE
94 	Palette = NULL;
95 	if (BitCount <= 8) {
96 		if (BitCount == 8)
97 			NumColors = 256;
98 		else
99 			NumColors = 16;
100 		Palette = ( Color * ) malloc( 4 * NumColors );
101 //		memset(Palette, 0, 4 * NumColors);
102 		for (unsigned int i = 0; i < NumColors; i++) {
103 			str->Read( &Palette[i].b, 1 );
104 			str->Read( &Palette[i].g, 1 );
105 			str->Read( &Palette[i].r, 1 );
106 			str->Read( &Palette[i].a, 1 );
107 			Palette[i].a = (Palette[i].a == 0) ? 0xff : Palette[i].a;
108 		}
109 	}
110 	str->Seek( DataOffset, GEM_STREAM_START );
111 	//no idea if we have to swap this or not
112 	//RASTERDATA
113 	switch (BitCount) {
114 		case 32:
115 			PaddedRowLength = Width * 4;
116 			break;
117 
118 		case 24:
119 			PaddedRowLength = Width * 3;
120 			break;
121 
122 		case 16:
123 			PaddedRowLength = Width * 2;
124 			break;
125 
126 		case 8:
127 			PaddedRowLength = Width;
128 			break;
129 
130 		case 4:
131 			PaddedRowLength = ( Width >> 1 );
132 			break;
133 		default:
134 			Log(ERROR, "BMPImporter", "BitCount %d is not supported.", BitCount);
135 			return false;
136 	}
137 	//if(BitCount!=4)
138 	//{
139 	if (PaddedRowLength & 3) {
140 		PaddedRowLength += 4 - ( PaddedRowLength & 3 );
141 	}
142 	//}
143 	void* rpixels = malloc( PaddedRowLength* Height );
144 	str->Read( rpixels, PaddedRowLength * Height );
145 	if (BitCount == 32) {
146 		int size = Width * Height * 4;
147 		pixels = malloc( size );
148 		unsigned int * dest = ( unsigned int * ) pixels;
149 		dest += Width * Height;
150 		unsigned char * src = ( unsigned char * ) rpixels;
151 		for (int i = Height; i; i--) {
152 			dest -= Width;
153 			// BGRX
154 			for (unsigned int j=0;j<Width;j++)
155 				dest[j] = (0xFF << 24) | (src[j*4+0] << 16) |
156 				          (src[j*4+1] << 8) | (src[j*4+2]);
157 			src += PaddedRowLength;
158 		}
159 	} else if (BitCount == 24) {
160 		//convert to 32 bits on the fly
161 		int size = Width * Height * 4;
162 		pixels = malloc( size );
163 		unsigned int * dest = ( unsigned int * ) pixels;
164 		dest += Width * Height;
165 		unsigned char * src = ( unsigned char * ) rpixels;
166 		for (int i = Height; i; i--) {
167 			dest -= Width;
168 			// BGR
169 			for (unsigned int j=0;j<Width;j++)
170 				dest[j] = (0xFF << 24) | (src[j*3+0] << 16) |
171 				          (src[j*3+1] << 8) | (src[j*3+2]);
172 			src += PaddedRowLength;
173 		}
174 		BitCount = 32;
175 	} else if (BitCount == 8) {
176 		pixels = malloc( Width * Height );
177 		unsigned char * dest = ( unsigned char * ) pixels;
178 		dest += Height * Width;
179 		unsigned char * src = ( unsigned char * ) rpixels;
180 		for (int i = Height; i; i--) {
181 			dest -= Width;
182 			memcpy( dest, src, Width );
183 			src += PaddedRowLength;
184 		}
185 	} else if (BitCount == 4) {
186 		Read4To8(rpixels);
187 	}
188 	free( rpixels );
189 	return true;
190 }
191 
Read8To8(void * rpixels)192 void BMPImporter::Read8To8(void *rpixels)
193 {
194 	pixels = malloc( Width * Height );
195 	unsigned char * dest = ( unsigned char * ) pixels;
196 	dest += Height * Width;
197 	unsigned char * src = ( unsigned char * ) rpixels;
198 	for (int i = Height; i; i--) {
199 		dest -= Width;
200 		memcpy( dest, src, Width );
201 		src += PaddedRowLength;
202 	}
203 }
204 
Read4To8(void * rpixels)205 void BMPImporter::Read4To8(void *rpixels)
206 {
207 	BitCount = 8;
208 	pixels = malloc( Width * Height );
209 	unsigned char * dest = ( unsigned char * ) pixels;
210 	dest += Height * Width;
211 	unsigned char * src = ( unsigned char * ) rpixels;
212 	for (int i = Height; i; i--) {
213 		dest -= Width;
214 		for (unsigned int j=0;j<Width;j++) {
215 			if (!(j&1)) {
216 				dest[j] = ((unsigned) src[j/2])>>4;
217 			} else {
218 				dest[j] = src[j/2]&15;
219 			}
220 		}
221 		src += PaddedRowLength;
222 	}
223 }
224 
GetSprite2D()225 Holder<Sprite2D> BMPImporter::GetSprite2D()
226 {
227 	Holder<Sprite2D> spr;
228 	if (BitCount == 32) {
229 		const ieDword red_mask = 0x000000ff;
230 		const ieDword green_mask = 0x0000ff00;
231 		const ieDword blue_mask = 0x00ff0000;
232 		void* p = malloc( Width * Height * 4 );
233 		memcpy( p, pixels, Width * Height * 4 );
234 		spr = core->GetVideoDriver()->CreateSprite(Region(0,0, Width, Height), 32,
235 			red_mask, green_mask, blue_mask, 0x00000000, p,
236 			true, green_mask|(0xff<<24) );
237 	} else if (BitCount == 8) {
238 		void* p = malloc( Width* Height );
239 		memcpy( p, pixels, Width * Height );
240 		spr = core->GetVideoDriver()->CreatePalettedSprite(Region(0,0, Width, Height), NumColors == 16 ? 4 : 8,
241 														   p, Palette, true, 0);
242 	}
243 	return spr;
244 }
245 
GetPalette(int colors,Color * pal)246 int BMPImporter::GetPalette(int colors, Color* pal)
247 {
248 	if (BitCount > 8) {
249 		return ImageMgr::GetPalette(colors, pal);
250 	}
251 
252 	for (int i = 0; i < colors; i++) {
253 		pal[i].r = Palette[i%NumColors].r;
254 		pal[i].g = Palette[i%NumColors].g;
255 		pal[i].b = Palette[i%NumColors].b;
256 		pal[i].a = 0xff;
257 	}
258 	return -1;
259 }
260 
GetBitmap()261 Bitmap* BMPImporter::GetBitmap()
262 {
263 	Bitmap *data = new Bitmap(Width,Height);
264 
265 	unsigned char *p = ( unsigned char * ) pixels;
266 	switch (BitCount) {
267 	case 8:
268 		unsigned int y;
269 		for (y = 0; y < Height; y++) {
270 			for (unsigned int x = 0; x < Width; x++) {
271 				data->SetAt(x,y,p[y*Width + x]);
272 			}
273 		}
274 		break;
275 	case 32:
276 		Log(ERROR, "BMPImporter", "Don't know how to handle 32bpp bitmap from %s...", str->filename);
277 		for (y = 0; y < Height; y++) {
278 			for (unsigned int x = 0; x < Width; x++) {
279 				data->SetAt(x,y,p[4*(y*Width + x)]);
280 			}
281 		}
282 		break;
283 	}
284 
285 	return data;
286 }
287 
GetImage()288 Image* BMPImporter::GetImage()
289 {
290 	Image *data = new Image(Width,Height);
291 
292 	switch (BitCount) {
293 	case 8: {
294 		unsigned char *p = ( unsigned char * ) pixels;
295 		unsigned int y;
296 		for (y = 0; y < Height; y++) {
297 			for (unsigned int x = 0; x < Width; x++) {
298 				data->SetPixel(x,y,Palette[p[y*Width + x]%NumColors]);
299 			}
300 		}
301 		break;
302 	}
303 	case 32: {
304 		unsigned int *p = ( unsigned int * ) pixels;
305 		unsigned int y;
306 		for (y = 0; y < Height; y++) {
307 			for (unsigned int x = 0; x < Width; x++) {
308 				unsigned int col = *p++;
309 				Color c(col, (col >> 8), (col >> 16), 0xFF);
310 				data->SetPixel(x,y,c);
311 			}
312 		}
313 		break;
314 	}
315 	}
316 
317 	return data;
318 }
319 
320 #include "plugindef.h"
321 
322 GEMRB_PLUGIN(0xD768B1, "BMP File Reader")
323 PLUGIN_IE_RESOURCE(BMPImporter, "bmp", (ieWord)IE_BMP_CLASS_ID)
324 END_PLUGIN()
325