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