]> git.saurik.com Git - wxWidgets.git/blob - src/common/imagbmp.cpp
We use native paths now in wxWindows. On the Mac, too.
[wxWidgets.git] / src / common / imagbmp.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: imagbmp.cpp
3 // Purpose: wxImage BMP handler
4 // Author: Robert Roebling
5 // RCS-ID: $Id$
6 // Copyright: (c) Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "imagbmp.h"
12 #endif
13
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
16
17 #ifdef __BORLANDC__
18 #pragma hdrstop
19 #endif
20
21 #include "wx/defs.h"
22
23 #if wxUSE_IMAGE
24
25 #include "wx/imagbmp.h"
26 #include "wx/bitmap.h"
27 #include "wx/debug.h"
28 #include "wx/log.h"
29 #include "wx/app.h"
30 #include "wx/filefn.h"
31 #include "wx/wfstream.h"
32 #include "wx/intl.h"
33 #include "wx/module.h"
34
35 // For memcpy
36 #include <string.h>
37
38 #ifdef __SALFORDC__
39 #ifdef FAR
40 #undef FAR
41 #endif
42 #endif
43
44 #ifdef __WXMSW__
45 #include <windows.h>
46 #endif
47
48 //-----------------------------------------------------------------------------
49 // wxBMPHandler
50 //-----------------------------------------------------------------------------
51
52 IMPLEMENT_DYNAMIC_CLASS(wxBMPHandler,wxImageHandler)
53
54
55 #if wxUSE_STREAMS
56
57
58 bool wxBMPHandler::SaveFile(wxImage *image,
59 wxOutputStream& stream,
60 bool verbose)
61 {
62 wxCHECK_MSG( image, FALSE, _T("invalid pointer in wxBMPHandler::SaveFile") );
63
64 if (!image->Ok())
65 {
66 if (verbose) wxLogError(_("BMP: Couldn't save invalid image."));
67 return FALSE;
68 }
69
70 unsigned width = image->GetWidth();
71 unsigned row_width = width * 3 +
72 (((width % 4) == 0) ? 0 : (4 - (width * 3) % 4));
73 // each row must be aligned to dwords
74 struct
75 {
76 // BitmapHeader:
77 wxUint16 magic; // format magic, always 'BM'
78 wxUint32 filesize; // total file size, inc. headers
79 wxUint32 reserved; // for future use
80 wxUint32 data_offset; // image data offset in the file
81
82 // BitmapInfoHeader:
83 wxUint32 bih_size; // 2nd part's size
84 wxUint32 width, height; // bitmap's dimensions
85 wxUint16 planes; // num of planes
86 wxUint16 bpp; // bits per pixel
87 wxUint32 compression; // compression method
88 wxUint32 size_of_bmp; // size of the bitmap
89 wxUint32 h_res, v_res; // image resolution in dpi
90 wxUint32 num_clrs; // number of colors used
91 wxUint32 num_signif_clrs;// number of significant colors
92 } hdr;
93 wxUint32 hdr_size = 14/*BitmapHeader*/ + 40/*BitmapInfoHeader*/;
94
95 hdr.magic = wxUINT16_SWAP_ON_BE(0x4D42/*'BM'*/);
96 hdr.filesize = wxUINT32_SWAP_ON_BE(
97 hdr_size +
98 row_width * image->GetHeight()
99 );
100 hdr.reserved = 0;
101 hdr.data_offset = wxUINT32_SWAP_ON_BE(hdr_size);
102
103 hdr.bih_size = wxUINT32_SWAP_ON_BE(hdr_size - 14);
104 hdr.width = wxUINT32_SWAP_ON_BE(image->GetWidth());
105 hdr.height = wxUINT32_SWAP_ON_BE(image->GetHeight());
106 hdr.planes = wxUINT16_SWAP_ON_BE(1); // always 1 plane
107 hdr.bpp = wxUINT16_SWAP_ON_BE(24); // always TrueColor
108 hdr.compression = 0; // RGB uncompressed
109 hdr.size_of_bmp = wxUINT32_SWAP_ON_BE(row_width * image->GetHeight());
110 hdr.h_res = hdr.v_res = wxUINT32_SWAP_ON_BE(72); // 72dpi is standard
111 hdr.num_clrs = 0; // maximal possible = 2^24
112 hdr.num_signif_clrs = 0; // all colors are significant
113
114 if (// VS: looks ugly but compilers tend to do ugly things with structs,
115 // like aligning hdr.filesize's ofset to dword :(
116 // VZ: we should add padding then...
117 !stream.Write(&hdr.magic, 2) ||
118 !stream.Write(&hdr.filesize, 4) ||
119 !stream.Write(&hdr.reserved, 4) ||
120 !stream.Write(&hdr.data_offset, 4) ||
121 !stream.Write(&hdr.bih_size, 4) ||
122 !stream.Write(&hdr.width, 4) ||
123 !stream.Write(&hdr.height, 4) ||
124 !stream.Write(&hdr.planes, 2) ||
125 !stream.Write(&hdr.bpp, 2) ||
126 !stream.Write(&hdr.compression, 4) ||
127 !stream.Write(&hdr.size_of_bmp, 4) ||
128 !stream.Write(&hdr.h_res, 4) ||
129 !stream.Write(&hdr.v_res, 4) ||
130 !stream.Write(&hdr.num_clrs, 4) ||
131 !stream.Write(&hdr.num_signif_clrs, 4)
132 )
133 {
134 if (verbose)
135 wxLogError(_("BMP: Couldn't write the file header."));
136 return FALSE;
137 }
138
139 wxUint8 *data = (wxUint8*) image->GetData();
140 wxUint8 *buffer = new wxUint8[row_width];
141 wxUint8 tmpvar;
142 memset(buffer, 0, row_width);
143 int y; unsigned x;
144
145 for (y = image->GetHeight() -1 ; y >= 0; y--)
146 {
147 memcpy(buffer, data + y * 3 * width, 3 * width);
148 for (x = 0; x < width; x++)
149 {
150 tmpvar = buffer[3 * x + 0];
151 buffer[3 * x + 0] = buffer[3 * x + 2];
152 buffer[3 * x + 2] = tmpvar;
153 }
154
155 if (!stream.Write(buffer, row_width))
156 {
157 if (verbose)
158 wxLogError(_("BMP: Couldn't write data."));
159 delete[] buffer;
160 return FALSE;
161 }
162 }
163 delete[] buffer;
164
165 return TRUE;
166 }
167
168
169
170
171 #ifndef BI_RGB
172 #define BI_RGB 0
173 #define BI_RLE8 1
174 #define BI_RLE4 2
175 #endif
176
177 #ifndef BI_BITFIELDS
178 #define BI_BITFIELDS 3
179 #endif
180
181 #define poffset (line * width * 3 + column * 3)
182
183 bool wxBMPHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) )
184 {
185 int rshift = 0, gshift = 0, bshift = 0;
186 wxUint8 aByte;
187 wxUint16 aWord;
188 wxInt32 dbuf[4];
189 wxInt32 aDword, rmask = 0, gmask = 0, bmask = 0;
190 wxInt8 bbuf[4];
191 struct _cmap {
192 unsigned char r, g, b;
193 } *cmap = NULL;
194
195 off_t start_offset = stream.TellI();
196 if (start_offset == wxInvalidOffset) start_offset = 0;
197
198 image->Destroy();
199
200 /*
201 * Read the BMP header
202 */
203
204 stream.Read( bbuf, 2 );
205 stream.Read( dbuf, 4 * 4 );
206
207 #if 0 // unused
208 wxInt32 size = wxINT32_SWAP_ON_BE( dbuf[0] );
209 #endif
210 wxInt32 offset = wxINT32_SWAP_ON_BE( dbuf[2] );
211
212 stream.Read(dbuf, 4 * 2);
213 int width = (int)wxINT32_SWAP_ON_BE( dbuf[0] );
214 int height = (int)wxINT32_SWAP_ON_BE( dbuf[1] );
215 if (width > 32767)
216 {
217 if (verbose)
218 wxLogError( _("BMP: Image width > 32767 pixels for file.") );
219 return FALSE;
220 }
221 if (height > 32767)
222 {
223 if (verbose)
224 wxLogError( _("BMP: Image height > 32767 pixels for file.") );
225 return FALSE;
226 }
227
228 stream.Read( &aWord, 2 );
229 /*
230 TODO
231 int planes = (int)wxUINT16_SWAP_ON_BE( aWord );
232 */
233 stream.Read( &aWord, 2 );
234 int bpp = (int)wxUINT16_SWAP_ON_BE( aWord );
235 if (bpp != 1 && bpp != 4 && bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32)
236 {
237 if (verbose)
238 wxLogError( _("BMP: Unknown bitdepth in file.") );
239 return FALSE;
240 }
241
242 stream.Read( dbuf, 4 * 4 );
243 int comp = (int)wxINT32_SWAP_ON_BE( dbuf[0] );
244 if (comp != BI_RGB && comp != BI_RLE4 && comp != BI_RLE8 && comp != BI_BITFIELDS)
245 {
246 if (verbose)
247 wxLogError( _("BMP: Unknown encoding in file.") );
248 return FALSE;
249 }
250
251 stream.Read( dbuf, 4 * 2 );
252 int ncolors = (int)wxINT32_SWAP_ON_BE( dbuf[0] );
253 if (ncolors == 0)
254 ncolors = 1 << bpp;
255 /* some more sanity checks */
256 if (((comp == BI_RLE4) && (bpp != 4)) ||
257 ((comp == BI_RLE8) && (bpp != 8)) ||
258 ((comp == BI_BITFIELDS) && (bpp != 16 && bpp != 32)))
259 {
260 if (verbose)
261 wxLogError( _("BMP: Encoding doesn't match bitdepth.") );
262 return FALSE;
263 }
264 if (bpp < 16)
265 {
266 cmap = (struct _cmap *)malloc(sizeof(struct _cmap) * ncolors);
267 if (!cmap)
268 {
269 if (verbose)
270 wxLogError( _("BMP: Couldn't allocate memory.") );
271 return FALSE;
272 }
273 }
274 else
275 cmap = NULL;
276
277 image->Create( width, height );
278 unsigned char *ptr = image->GetData();
279 if (!ptr)
280 {
281 if (verbose)
282 wxLogError( _("BMP: Couldn't allocate memory.") );
283 if (cmap)
284 free(cmap);
285 return FALSE;
286 }
287
288 /*
289 * Reading the palette, if it exists.
290 */
291 if (bpp < 16 && ncolors != 0)
292 {
293 unsigned char* r = new unsigned char[ncolors];
294 unsigned char* g = new unsigned char[ncolors];
295 unsigned char* b = new unsigned char[ncolors];
296 for (int j = 0; j < ncolors; j++)
297 {
298 stream.Read( bbuf, 4 );
299 cmap[j].b = bbuf[0];
300 cmap[j].g = bbuf[1];
301 cmap[j].r = bbuf[2];
302
303 r[j] = cmap[j].r;
304 g[j] = cmap[j].g;
305 b[j] = cmap[j].b;
306 }
307 // Set the palette for the wxImage
308 image->SetPalette(wxPalette(ncolors, r, g, b));
309
310 delete[] r;
311 delete[] g;
312 delete[] b;
313 }
314 else if (bpp == 16 || bpp == 32)
315 {
316 if (comp == BI_BITFIELDS)
317 {
318 int bit = 0;
319 stream.Read( dbuf, 4 * 3 );
320 bmask = wxINT32_SWAP_ON_BE( dbuf[0] );
321 gmask = wxINT32_SWAP_ON_BE( dbuf[1] );
322 rmask = wxINT32_SWAP_ON_BE( dbuf[2] );
323 /* find shift amount.. ugly, but i can't think of a better way */
324 for (bit = 0; bit < bpp; bit++)
325 {
326 if (bmask & (1 << bit))
327 bshift = bit;
328 if (gmask & (1 << bit))
329 gshift = bit;
330 if (rmask & (1 << bit))
331 rshift = bit;
332 }
333 }
334 else if (bpp == 16)
335 {
336 rmask = 0x7C00;
337 gmask = 0x03E0;
338 bmask = 0x001F;
339 rshift = 10;
340 gshift = 5;
341 bshift = 0;
342 }
343 else if (bpp == 32)
344 {
345 rmask = 0x00FF0000;
346 gmask = 0x0000FF00;
347 bmask = 0x000000FF;
348 rshift = 16;
349 gshift = 8;
350 bshift = 0;
351 }
352 }
353
354 /*
355 * Reading the image data
356 */
357 stream.SeekI( start_offset + offset );
358 unsigned char *data = ptr;
359
360 /* set the whole image to the background color */
361 if (bpp < 16 && (comp == BI_RLE4 || comp == BI_RLE8))
362 {
363 for (int i = 0; i < width * height; i++)
364 {
365 *ptr++ = cmap[0].r;
366 *ptr++ = cmap[0].g;
367 *ptr++ = cmap[0].b;
368 }
369 ptr = data;
370 }
371
372 int line = 0;
373 int column = 0;
374 int linesize = ((width * bpp + 31) / 32) * 4;
375
376 /* BMPs are stored upside down */
377 for (line = (height - 1); line >= 0; line--)
378 {
379 int linepos = 0;
380 for (column = 0; column < width;)
381 {
382 if (bpp < 16)
383 {
384 int index = 0;
385 linepos++;
386 aByte = stream.GetC();
387 if (bpp == 1)
388 {
389 int bit = 0;
390 for (bit = 0; bit < 8 && column < width; bit++)
391 {
392 index = ((aByte & (0x80 >> bit)) ? 1 : 0);
393 ptr[poffset] = cmap[index].r;
394 ptr[poffset + 1] = cmap[index].g;
395 ptr[poffset + 2] = cmap[index].b;
396 column++;
397 }
398 }
399 else if (bpp == 4)
400 {
401 if (comp == BI_RLE4)
402 {
403 if (verbose)
404 wxLogError( _("BMP: Cannot deal with 4bit encoded yet.") );
405 image->Destroy();
406 free(cmap);
407 return FALSE;
408 }
409 else
410 {
411 int nibble = 0;
412 for (nibble = 0; nibble < 2 && column < width; nibble++)
413 {
414 index = ((aByte & (0xF0 >> nibble * 4)) >> (!nibble * 4));
415 if (index >= 16)
416 index = 15;
417 ptr[poffset] = cmap[index].r;
418 ptr[poffset + 1] = cmap[index].g;
419 ptr[poffset + 2] = cmap[index].b;
420 column++;
421 }
422 }
423 }
424 else if (bpp == 8)
425 {
426 if (comp == BI_RLE8)
427 {
428 unsigned char first;
429 first = aByte;
430 aByte = stream.GetC();
431 if (first == 0)
432 {
433 if (aByte == 0)
434 {
435 /* column = width; */
436 }
437 else if (aByte == 1)
438 {
439 column = width;
440 line = -1;
441 }
442 else if (aByte == 2)
443 {
444 aByte = stream.GetC();
445 column += aByte;
446 linepos = column * bpp / 8;
447 aByte = stream.GetC();
448 line += aByte;
449 }
450 else
451 {
452 int absolute = aByte;
453 for (int k = 0; k < absolute; k++)
454 {
455 linepos++;
456 aByte = stream.GetC();
457 ptr[poffset ] = cmap[aByte].r;
458 ptr[poffset + 1] = cmap[aByte].g;
459 ptr[poffset + 2] = cmap[aByte].b;
460 column++;
461 }
462 if (absolute & 0x01)
463 aByte = stream.GetC();
464 }
465 }
466 else
467 {
468 for (int l = 0; l < first && column < width; l++)
469 {
470 ptr[poffset ] = cmap[aByte].r;
471 ptr[poffset + 1] = cmap[aByte].g;
472 ptr[poffset + 2] = cmap[aByte].b;
473 column++;
474 linepos++;
475 }
476 }
477 }
478 else
479 {
480 ptr[poffset ] = cmap[aByte].r;
481 ptr[poffset + 1] = cmap[aByte].g;
482 ptr[poffset + 2] = cmap[aByte].b;
483 column++;
484 // linepos += size; seems to be wrong, RR
485 }
486 }
487 }
488 else if (bpp == 24)
489 {
490 stream.Read( bbuf, 3 );
491 linepos += 3;
492 ptr[poffset ] = (unsigned char)bbuf[2];
493 ptr[poffset + 1] = (unsigned char)bbuf[1];
494 ptr[poffset + 2] = (unsigned char)bbuf[0];
495 column++;
496 }
497 else if (bpp == 16)
498 {
499 unsigned char temp;
500 stream.Read( &aWord, 2 );
501 aWord = wxUINT16_SWAP_ON_BE( aWord );
502 linepos += 2;
503 temp = (aWord & rmask) >> rshift;
504 ptr[poffset] = temp;
505 temp = (aWord & gmask) >> gshift;
506 ptr[poffset + 1] = temp;
507 temp = (aWord & bmask) >> bshift;
508 ptr[poffset + 2] = temp;
509 column++;
510 }
511 else
512 {
513 unsigned char temp;
514 stream.Read( &aDword, 4 );
515 aDword = wxINT32_SWAP_ON_BE( aDword );
516 linepos += 4;
517 temp = (aDword & rmask) >> rshift;
518 ptr[poffset] = temp;
519 temp = (aDword & gmask) >> gshift;
520 ptr[poffset + 1] = temp;
521 temp = (aDword & bmask) >> bshift;
522 ptr[poffset + 2] = temp;
523 column++;
524 }
525 }
526 while ((linepos < linesize) && (comp != 1) && (comp != 2))
527 {
528 stream.Read( &aByte, 1 );
529 linepos += 1;
530 if (stream.LastError() != wxStream_NOERROR)
531 break;
532 }
533 }
534 if (cmap)
535 free(cmap);
536
537 image->SetMask( FALSE );
538
539 return TRUE;
540 }
541
542 bool wxBMPHandler::DoCanRead( wxInputStream& stream )
543 {
544 unsigned char hdr[2];
545
546 stream.Read(hdr, 2);
547 stream.SeekI(-2, wxFromCurrent);
548 return (hdr[0] == 'B' && hdr[1] == 'M');
549 }
550
551 #endif // wxUSE_STREAMS
552
553 #endif // wxUSE_IMAGE