]> git.saurik.com Git - wxWidgets.git/blob - src/common/imagbmp.cpp
Corrected some combobox-related code, changed ordering and added wxSpinCtrl
[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/imagbmp.h"
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
48 IMPLEMENT_DYNAMIC_CLASS(wxBMPHandler,wxImageHandler)
49
50
51 #if wxUSE_STREAMS
52
53
54 bool 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
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
179 bool wxBMPHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) )
180 {
181 int rshift = 0, gshift = 0, bshift = 0;
182 wxUint8 aByte;
183 wxUint16 aWord;
184 wxInt32 dbuf[4], aDword,
185 rmask = 0, gmask = 0, bmask = 0;
186 wxInt8 bbuf[4];
187 struct _cmap {
188 unsigned char r, g, b;
189 } *cmap = NULL;
190
191 off_t start_offset = stream.TellI();
192 if (start_offset == wxInvalidOffset) start_offset = 0;
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
203 #if 0 // unused
204 wxInt32 size = wxINT32_SWAP_ON_BE( dbuf[0] );
205 #endif
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 {
213 if (verbose)
214 wxLogError( _("BMP: Image width > 32767 pixels for file.") );
215 return FALSE;
216 }
217 if (height > 32767)
218 {
219 if (verbose)
220 wxLogError( _("BMP: Image height > 32767 pixels for file.") );
221 return FALSE;
222 }
223
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 {
233 if (verbose)
234 wxLogError( _("BMP: Unknown bitdepth in file.") );
235 return FALSE;
236 }
237
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 {
242 if (verbose)
243 wxLogError( _("BMP: Unknown encoding in file.") );
244 return FALSE;
245 }
246
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 */
252 if (((comp == BI_RLE4) && (bpp != 4)) ||
253 ((comp == BI_RLE8) && (bpp != 8)) ||
254 ((comp == BI_BITFIELDS) && (bpp != 16 && bpp != 32)))
255 {
256 if (verbose)
257 wxLogError( _("BMP: Encoding doesn't match bitdepth.") );
258 return FALSE;
259 }
260 if (bpp < 16)
261 {
262 cmap = (struct _cmap *)malloc(sizeof(struct _cmap) * ncolors);
263 if (!cmap)
264 {
265 if (verbose)
266 wxLogError( _("BMP: Couldn't allocate memory.") );
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 {
277 if (verbose)
278 wxLogError( _("BMP: Couldn't allocate memory.") );
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 {
289 for (int j = 0; j < ncolors; j++)
290 {
291 stream.Read( bbuf, 4 );
292 cmap[j].b = bbuf[0];
293 cmap[j].g = bbuf[1];
294 cmap[j].r = bbuf[2];
295 }
296 }
297 else if (bpp == 16 || bpp == 32)
298 {
299 if (comp == BI_BITFIELDS)
300 {
301 int bit = 0;
302 stream.Read( dbuf, 4 * 3 );
303 bmask = wxINT32_SWAP_ON_BE( dbuf[0] );
304 gmask = wxINT32_SWAP_ON_BE( dbuf[1] );
305 rmask = wxINT32_SWAP_ON_BE( dbuf[2] );
306 /* find shift amount.. ugly, but i can't think of a better way */
307 for (bit = 0; bit < bpp; bit++)
308 {
309 if (bmask & (1 << bit))
310 bshift = bit;
311 if (gmask & (1 << bit))
312 gshift = bit;
313 if (rmask & (1 << bit))
314 rshift = bit;
315 }
316 }
317 else if (bpp == 16)
318 {
319 rmask = 0x7C00;
320 gmask = 0x03E0;
321 bmask = 0x001F;
322 rshift = 10;
323 gshift = 5;
324 bshift = 0;
325 }
326 else if (bpp == 32)
327 {
328 rmask = 0x00FF0000;
329 gmask = 0x0000FF00;
330 bmask = 0x000000FF;
331 rshift = 16;
332 gshift = 8;
333 bshift = 0;
334 }
335 }
336
337 /*
338 * Reading the image data
339 */
340 stream.SeekI( start_offset + offset );
341 unsigned char *data = ptr;
342
343 /* set the whole image to the background color */
344 if (bpp < 16 && (comp == BI_RLE4 || comp == BI_RLE8))
345 {
346 for (int i = 0; i < width * height; i++)
347 {
348 *ptr++ = cmap[0].r;
349 *ptr++ = cmap[0].g;
350 *ptr++ = cmap[0].b;
351 }
352 ptr = data;
353 }
354
355 int line = 0;
356 int column = 0;
357 int linesize = ((width * bpp + 31) / 32) * 4;
358
359 /* BMPs are stored upside down */
360 for (line = (height - 1); line >= 0; line--)
361 {
362 int linepos = 0;
363 for (column = 0; column < width;)
364 {
365 if (bpp < 16)
366 {
367 int index = 0;
368 linepos++;
369 aByte = stream.GetC();
370 if (bpp == 1)
371 {
372 int bit = 0;
373 for (bit = 0; bit < 8; bit++)
374 {
375 index = ((aByte & (0x80 >> bit)) ? 1 : 0);
376 ptr[poffset] = cmap[index].r;
377 ptr[poffset + 1] = cmap[index].g;
378 ptr[poffset + 2] = cmap[index].b;
379 column++;
380 }
381 }
382 else if (bpp == 4)
383 {
384 if (comp == BI_RLE4)
385 {
386 if (verbose)
387 wxLogError( _("BMP: Cannot deal with 4bit encoded yet.") );
388 image->Destroy();
389 free(cmap);
390 return FALSE;
391 }
392 else
393 {
394 int nibble = 0;
395 for (nibble = 0; nibble < 2; nibble++)
396 {
397 index = ((aByte & (0xF0 >> nibble * 4)) >> (!nibble * 4));
398 if (index >= 16)
399 index = 15;
400 ptr[poffset] = cmap[index].r;
401 ptr[poffset + 1] = cmap[index].g;
402 ptr[poffset + 2] = cmap[index].b;
403 column++;
404 }
405 }
406 }
407 else if (bpp == 8)
408 {
409 if (comp == BI_RLE8)
410 {
411 unsigned char first;
412 first = aByte;
413 aByte = stream.GetC();
414 if (first == 0)
415 {
416 if (aByte == 0)
417 {
418 /* column = width; */
419 }
420 else if (aByte == 1)
421 {
422 column = width;
423 line = -1;
424 }
425 else if (aByte == 2)
426 {
427 aByte = stream.GetC();
428 column += aByte;
429 linepos = column * bpp / 8;
430 aByte = stream.GetC();
431 line += aByte;
432 }
433 else
434 {
435 int absolute = aByte;
436 for (int k = 0; k < absolute; k++)
437 {
438 linepos++;
439 aByte = stream.GetC();
440 ptr[poffset ] = cmap[aByte].r;
441 ptr[poffset + 1] = cmap[aByte].g;
442 ptr[poffset + 2] = cmap[aByte].b;
443 column++;
444 }
445 if (absolute & 0x01)
446 aByte = stream.GetC();
447 }
448 }
449 else
450 {
451 for (int l = 0; l < first; l++)
452 {
453 ptr[poffset ] = cmap[aByte].r;
454 ptr[poffset + 1] = cmap[aByte].g;
455 ptr[poffset + 2] = cmap[aByte].b;
456 column++;
457 linepos++;
458 }
459 }
460 }
461 else
462 {
463 ptr[poffset ] = cmap[aByte].r;
464 ptr[poffset + 1] = cmap[aByte].g;
465 ptr[poffset + 2] = cmap[aByte].b;
466 column++;
467 // linepos += size; seems to be wrong, RR
468 }
469 }
470 }
471 else if (bpp == 24)
472 {
473 stream.Read( &bbuf, 3 );
474 linepos += 3;
475 ptr[poffset ] = (unsigned char)bbuf[2];
476 ptr[poffset + 1] = (unsigned char)bbuf[1];
477 ptr[poffset + 2] = (unsigned char)bbuf[0];
478 column++;
479 }
480 else if (bpp == 16)
481 {
482 unsigned char temp;
483 stream.Read( &aWord, 2 );
484 aWord = wxUINT16_SWAP_ON_BE( aWord );
485 linepos += 2;
486 temp = (aWord & rmask) >> rshift;
487 ptr[poffset] = temp;
488 temp = (aWord & gmask) >> gshift;
489 ptr[poffset + 1] = temp;
490 temp = (aWord & bmask) >> bshift;
491 ptr[poffset + 2] = temp;
492 column++;
493 }
494 else
495 {
496 unsigned char temp;
497 stream.Read( &aDword, 4 );
498 aDword = wxINT32_SWAP_ON_BE( aDword );
499 linepos += 4;
500 temp = (aDword & rmask) >> rshift;
501 ptr[poffset] = temp;
502 temp = (aDword & gmask) >> gshift;
503 ptr[poffset + 1] = temp;
504 temp = (aDword & bmask) >> bshift;
505 ptr[poffset + 2] = temp;
506 column++;
507 }
508 }
509 while ((linepos < linesize) && (comp != 1) && (comp != 2))
510 {
511 stream.Read( &aByte, 1 );
512 linepos += 1;
513 if (stream.LastError() != wxStream_NOERROR)
514 break;
515 }
516 }
517 if (cmap)
518 free(cmap);
519
520 image->SetMask( FALSE );
521
522 return TRUE;
523 }
524
525 bool wxBMPHandler::DoCanRead( wxInputStream& stream )
526 {
527 unsigned char hdr[2];
528
529 stream.Read(&hdr, 2);
530 stream.SeekI(-2, wxFromCurrent);
531 return (hdr[0] == 'B' && hdr[1] == 'M');
532 }
533
534 #endif // wxUSE_STREAMS
535
536