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