]> git.saurik.com Git - wxWidgets.git/blame - src/common/imagbmp.cpp
compilation fix for Windows
[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
c96ea657
VS
21#include "wx/defs.h"
22
23#if wxUSE_IMAGE
24
8f493002 25#include "wx/imagbmp.h"
e65ed37a
RR
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"
1971d23c 34#include "wx/quantize.h"
e65ed37a
RR
35
36// For memcpy
37#include <string.h>
38
39#ifdef __SALFORDC__
40#ifdef FAR
41#undef FAR
42#endif
43#endif
44
45#ifdef __WXMSW__
46#include <windows.h>
47#endif
48
49//-----------------------------------------------------------------------------
37b83ca6 50// wxBMPHandler & wxICOHandler
e65ed37a
RR
51//-----------------------------------------------------------------------------
52
e65ed37a 53IMPLEMENT_DYNAMIC_CLASS(wxBMPHandler,wxImageHandler)
e65ed37a 54
f6bcfd97 55
e65ed37a
RR
56#if wxUSE_STREAMS
57
f6bcfd97 58
37b83ca6
VS
59#ifndef BI_RGB
60#define BI_RGB 0
61#define BI_RLE8 1
62#define BI_RLE4 2
63#endif
64
65#ifndef BI_BITFIELDS
66#define BI_BITFIELDS 3
67#endif
68
69#define poffset (line * width * 3 + column * 3)
70
71
72
73struct ICONDIRENTRY
74 {
75 wxUint8 bWidth; // Width of the image
76 wxUint8 bHeight; // Height of the image (times 2)
77 wxUint8 bColorCount; // Number of colors in image (0 if >=8bpp)
78 wxUint8 bReserved; // Reserved
79 wxUint16 wPlanes; // Color Planes
80 wxUint16 wBitCount; // Bits per pixel
81 wxUint32 dwBytesInRes; // how many bytes in this resource?
82 wxUint32 dwImageOffset; // where in the file is this image
83} ;
84
85
86struct ICONDIR
87{
88 wxUint16 idReserved; // Reserved
89 wxUint16 idType; // resource type (1 for icons)
90 wxUint16 idCount; // how many images?
91} ;
92
93bool wxICOHandler::SaveFile(wxImage *image,
94 wxOutputStream& stream,
95 bool verbose)
96
97{
98 bool bResult = FALSE ;
99 //sanity check; icon must be less than 127 pixels high and 255 wide
100 if (image -> GetHeight () > 127 )
101 {
102 if (verbose)
103 wxLogError( _("ICO: Error Image too tall for an icon.") );
104 return FALSE;
105 }
106 if (image -> GetWidth () > 255 )
107 {
108 if (verbose)
109 wxLogError( _("ICO: Error Image too wide for an icon.") );
110 return FALSE;
111 }
112
113 // only generate one image
114 int m_images = 1 ;
115
116 // write a header, (ICONDIR)
117 // Calculate the header size
118 wxUint32 m_offset = 3 * sizeof(wxUint16);
119
120 ICONDIR m_IconDir ;
121 m_IconDir.idReserved = 0 ;
122 m_IconDir.idType = wxUINT16_SWAP_ON_BE (1);
123 m_IconDir.idCount = wxUINT16_SWAP_ON_BE (m_images);
124 stream.Write(&m_IconDir.idReserved, sizeof(m_IconDir.idReserved));
125 stream.Write(&m_IconDir.idType, sizeof(m_IconDir.idType));
126 stream.Write(&m_IconDir.idCount, sizeof(m_IconDir.idCount));
127 if ( !stream.IsOk () )
128 {
129 if (verbose)
130 wxLogError( _("ICO: Error writing ICONDIR header.") );
131 return FALSE;
132 }
133
134 // for each iamage write a description ICONDIRENTRY
135 ICONDIRENTRY m_icondirentry ;
136 int i ;
137 for ( i = 0; i < m_images; i++ )
138 {
139 wxImage mask ;
140 if (image->HasMask())
141 {
142 //make another image with black/white
143 mask = image -> ConvertToMono (image->GetMaskRed(), image->GetMaskGreen(), image->GetMaskBlue() );
144 //now we need to change the masked regions to black
145 unsigned char r = image -> GetMaskRed() ;
146 unsigned char g = image -> GetMaskGreen() ;
147 unsigned char b = image -> GetMaskBlue() ;
148 if ((r != 0) || (g != 0) || (b != 0) )
149 {
150 //Go round and apply black to the masked bits
151 int i,j;
152 for (i=0; i < mask.GetWidth(); i++)
153 for (j=0; j < mask.GetHeight(); j++)
154 {
155 if ((r == mask.GetRed(i, j)) &&
156 (g == mask.GetGreen(i, j) ) &&
157 (b == mask.GetBlue(i, j)) )
158 image -> SetRGB ( i, j, 0, 0, 0 );
159 }
160
161 }
162 }
163 else
164 {
165 // just make a black mask all over
166 mask = image -> Copy ();
167 int i,j;
168 for (i=0; i < mask.GetWidth(); i++)
169 for (j=0; j < mask.GetHeight(); j++)
170 mask.SetRGB ( i, j, 0, 0, 0 );
171 }
172 //Set the formats for image and mask
173 // windows never saves with more than 8 colors
174 image -> SetOption (wxBMP_FORMAT, wxBMP_8BPP);
175 // monochome bitmap
176 mask . SetOption (wxBMP_FORMAT, wxBMP_1BPP_BW);
177 bool IsBmp = FALSE ;
178 bool IsMask = FALSE ;
179
180 //calculate size and offset of image and mask
181 wxCountingOutputStream cStream ;
182 bResult = SaveDib ( image, cStream, verbose, IsBmp, IsMask ) ;
183 if (!bResult)
184 {
185 if (verbose)
186 wxLogError( _("ICO: Error calculating size of XOR DIB .") );
187 return FALSE;
188 }
189 IsMask = TRUE ;
190 bResult = SaveDib ( &mask, cStream, verbose, IsBmp, IsMask ) ;
191 if (!bResult)
192 {
193 if (verbose)
194 wxLogError( _("ICO: Error calculating size of Mask DIB .") );
195 return FALSE;
196 }
197 wxUint32 m_Size = cStream.GetSize();
198 if (!cStream.Ok())
199 {
200 if (verbose)
201 wxLogError( _("ICO: Error calculating size of DIB .") );
202 return FALSE;
203 }
204
205 m_offset = m_offset + sizeof(ICONDIRENTRY) ;
206
207 m_icondirentry. bWidth = image -> GetWidth () ;
208 m_icondirentry. bHeight = 2 * image -> GetHeight () ;
209 m_icondirentry. bColorCount = 0 ;
210 m_icondirentry. bReserved = 0 ;
211 m_icondirentry. wPlanes = wxUINT16_SWAP_ON_BE(1);
212 m_icondirentry. wBitCount = wxUINT16_SWAP_ON_BE(wxBMP_8BPP) ;
213 m_icondirentry. dwBytesInRes = wxUINT32_SWAP_ON_BE(m_Size);
214 m_icondirentry. dwImageOffset = wxUINT32_SWAP_ON_BE(m_offset);
215
216 //increase size to allow for the data wriitten
217 m_offset = m_offset + m_Size ;
218
219 //write to stream
220 stream.Write(&m_icondirentry. bWidth, sizeof(m_icondirentry. bWidth) );
221 stream.Write(&m_icondirentry. bHeight, sizeof(m_icondirentry. bHeight) );
222 stream.Write(&m_icondirentry. bColorCount, sizeof(m_icondirentry. bColorCount) );
223 stream.Write(&m_icondirentry. bReserved, sizeof(m_icondirentry. bReserved) );
224 stream.Write(&m_icondirentry. wPlanes, sizeof(m_icondirentry. wPlanes) );
225 stream.Write(&m_icondirentry. wBitCount, sizeof(m_icondirentry. wBitCount) );
226 stream.Write(&m_icondirentry. dwBytesInRes, sizeof(m_icondirentry. dwBytesInRes) );
227 stream.Write(&m_icondirentry. dwImageOffset, sizeof(m_icondirentry. dwImageOffset) );
228 if ( !stream.IsOk () )
229 {
230 if (verbose)
231 wxLogError( _("ICO: Error writing ICONDIRENTRY header.") );
232 return FALSE;
233 }
234 //actually save it
235 IsMask = FALSE ;
236 bResult = SaveDib ( image, stream, verbose, IsBmp, IsMask ) ;
237 if (!bResult)
238 {
239 if (verbose)
240 wxLogError( _("ICO: Error writing XOR DIB .") );
241 return FALSE;
242 }
243 IsMask = TRUE ;
244 bResult = SaveDib ( &mask, stream, verbose, IsBmp, IsMask ) ;
245 if (!bResult)
246 {
247 if (verbose)
248 wxLogError( _("ICO: Error writing Mask DIB .") );
249 return FALSE;
250 }
251
252 } // end of for loop
253 return TRUE ;
254}
255
256
f6bcfd97
BP
257bool wxBMPHandler::SaveFile(wxImage *image,
258 wxOutputStream& stream,
259 bool verbose)
37b83ca6
VS
260{
261 bool IsBmp = TRUE;
262 bool IsMask = FALSE ;
263 return SaveDib( image, stream, verbose, IsBmp, IsMask ) ;
264}
265
266bool wxBMPHandler::SaveDib(wxImage *image,
267 wxOutputStream& stream,
268 bool verbose,
269 bool IsBmp,
270 bool IsMask)
271
f6bcfd97
BP
272{
273 wxCHECK_MSG( image, FALSE, _T("invalid pointer in wxBMPHandler::SaveFile") );
274
275 if (!image->Ok())
276 {
277 if (verbose) wxLogError(_("BMP: Couldn't save invalid image."));
278 return FALSE;
279 }
280
1971d23c
VZ
281 // get the format of the BMP file to save, else use 24bpp
282 unsigned format = wxBMP_24BPP;
283 if (image->HasOption(wxBMP_FORMAT))
284 format = image->GetOptionInt(wxBMP_FORMAT);
285
286 unsigned bpp; // # of bits per pixel
287 int palette_size; // # of color map entries, ie. 2^bpp colors
288
289 // set the bpp and appropriate palette_size, and do additional checks
290 if ((format == wxBMP_1BPP) || (format == wxBMP_1BPP_BW))
291 {
292 bpp = 1;
293 palette_size = 2;
294 }
295 else if (format == wxBMP_4BPP)
296 {
297 bpp = 4;
298 palette_size = 16;
299 }
300 else if ((format == wxBMP_8BPP) || (format == wxBMP_8BPP_GREY) ||
301 (format == wxBMP_8BPP_RED) || (format == wxBMP_8BPP_PALETTE))
302 {
303 // need to set a wxPalette to use this, HOW TO CHECK IF VALID, SIZE?
b11e8fb6
VZ
304 if ((format == wxBMP_8BPP_PALETTE)
305#if wxUSE_PALETTE
306 && !image->HasPalette()
307#endif // wxUSE_PALETTE
308 )
1971d23c
VZ
309 {
310 if (verbose)
311 wxLogError(_("BMP: wImage doesn't have own wxPalette."));
312 return FALSE;
313 }
314 bpp = 8;
315 palette_size = 256;
316 }
317 else // you get 24bpp
318 {
319 format = wxBMP_24BPP;
320 bpp = 24;
321 palette_size = 0;
322 }
323
f6bcfd97 324 unsigned width = image->GetWidth();
1971d23c
VZ
325 unsigned row_padding = (4 - int(width*bpp/8.0) % 4) % 4; // # bytes to pad to dword
326 unsigned row_width = int(width * bpp/8.0) + row_padding; // # of bytes per row
327
f6bcfd97
BP
328 struct
329 {
330 // BitmapHeader:
331 wxUint16 magic; // format magic, always 'BM'
332 wxUint32 filesize; // total file size, inc. headers
333 wxUint32 reserved; // for future use
334 wxUint32 data_offset; // image data offset in the file
33ac7e6f 335
f6bcfd97
BP
336 // BitmapInfoHeader:
337 wxUint32 bih_size; // 2nd part's size
338 wxUint32 width, height; // bitmap's dimensions
339 wxUint16 planes; // num of planes
340 wxUint16 bpp; // bits per pixel
341 wxUint32 compression; // compression method
342 wxUint32 size_of_bmp; // size of the bitmap
343 wxUint32 h_res, v_res; // image resolution in dpi
344 wxUint32 num_clrs; // number of colors used
345 wxUint32 num_signif_clrs;// number of significant colors
346 } hdr;
1971d23c 347
f6bcfd97
BP
348 wxUint32 hdr_size = 14/*BitmapHeader*/ + 40/*BitmapInfoHeader*/;
349
350 hdr.magic = wxUINT16_SWAP_ON_BE(0x4D42/*'BM'*/);
1971d23c
VZ
351 hdr.filesize = wxUINT32_SWAP_ON_BE( hdr_size + palette_size*4 +
352 row_width * image->GetHeight() );
f6bcfd97 353 hdr.reserved = 0;
1971d23c 354 hdr.data_offset = wxUINT32_SWAP_ON_BE(hdr_size + palette_size*4);
33ac7e6f 355
f6bcfd97
BP
356 hdr.bih_size = wxUINT32_SWAP_ON_BE(hdr_size - 14);
357 hdr.width = wxUINT32_SWAP_ON_BE(image->GetWidth());
37b83ca6
VS
358 if (IsBmp)
359 {
f6bcfd97 360 hdr.height = wxUINT32_SWAP_ON_BE(image->GetHeight());
37b83ca6
VS
361 }
362 else
363 {
364 hdr.height = wxUINT32_SWAP_ON_BE(2 * image->GetHeight());
365 }
f6bcfd97 366 hdr.planes = wxUINT16_SWAP_ON_BE(1); // always 1 plane
1971d23c 367 hdr.bpp = wxUINT16_SWAP_ON_BE(bpp);
f6bcfd97 368 hdr.compression = 0; // RGB uncompressed
33ac7e6f 369 hdr.size_of_bmp = wxUINT32_SWAP_ON_BE(row_width * image->GetHeight());
1971d23c
VZ
370 hdr.h_res = hdr.v_res = wxUINT32_SWAP_ON_BE(72); // 72dpi is standard
371 hdr.num_clrs = wxUINT32_SWAP_ON_BE(palette_size); // # colors in colormap
372 hdr.num_signif_clrs = 0; // all colors are significant
f6bcfd97 373
37b83ca6
VS
374 if (IsBmp)
375 {
f6bcfd97
BP
376 if (// VS: looks ugly but compilers tend to do ugly things with structs,
377 // like aligning hdr.filesize's ofset to dword :(
378 // VZ: we should add padding then...
379 !stream.Write(&hdr.magic, 2) ||
380 !stream.Write(&hdr.filesize, 4) ||
381 !stream.Write(&hdr.reserved, 4) ||
37b83ca6
VS
382 !stream.Write(&hdr.data_offset, 4)
383 )
384 {
385 if (verbose)
386 wxLogError(_("BMP: Couldn't write the file (Bitmap) header."));
387 return FALSE;
388 }
389 }
390 if (!IsMask)
391 {
392 if (
f6bcfd97
BP
393 !stream.Write(&hdr.bih_size, 4) ||
394 !stream.Write(&hdr.width, 4) ||
395 !stream.Write(&hdr.height, 4) ||
396 !stream.Write(&hdr.planes, 2) ||
397 !stream.Write(&hdr.bpp, 2) ||
398 !stream.Write(&hdr.compression, 4) ||
399 !stream.Write(&hdr.size_of_bmp, 4) ||
400 !stream.Write(&hdr.h_res, 4) ||
401 !stream.Write(&hdr.v_res, 4) ||
402 !stream.Write(&hdr.num_clrs, 4) ||
403 !stream.Write(&hdr.num_signif_clrs, 4)
33ac7e6f 404 )
f6bcfd97
BP
405 {
406 if (verbose)
37b83ca6 407 wxLogError(_("BMP: Couldn't write the file (BitmapInfo) header."));
f6bcfd97
BP
408 return FALSE;
409 }
37b83ca6 410 }
f6bcfd97 411
1971d23c
VZ
412 wxPalette *palette = NULL; // entries for quantized images
413 wxUint8 *rgbquad = NULL; // for the RGBQUAD bytes for the colormap
414 wxImage *q_image = NULL; // destination for quantized image
415
416 // if <24bpp use quantization to reduce colors for *some* of the formats
417 if ( (format == wxBMP_1BPP) || (format == wxBMP_4BPP) ||
418 (format == wxBMP_8BPP) || (format == wxBMP_8BPP_PALETTE))
419 {
420 // make a new palette and quantize the image
421 if (format != wxBMP_8BPP_PALETTE)
422 {
423 q_image = new wxImage();
424
425 // I get a delete error using Quantize when desired colors > 236
426 int quantize = ((palette_size > 236) ? 236 : palette_size);
427 // fill the destination too, it gives much nicer 4bpp images
428 wxQuantize::Quantize( *image, *q_image, &palette, quantize, 0,
429 wxQUANTIZE_FILL_DESTINATION_IMAGE );
430 }
431 else
432 {
b11e8fb6 433#if wxUSE_PALETTE
1971d23c 434 palette = new wxPalette(image->GetPalette());
b11e8fb6 435#endif // wxUSE_PALETTE
1971d23c
VZ
436 }
437
438 int i;
439 unsigned char r, g, b;
440 rgbquad = new wxUint8 [palette_size*4];
441
442 for (i=0; i<palette_size; i++)
443 {
b11e8fb6
VZ
444#if wxUSE_PALETTE
445 if (!palette->GetRGB( i, &r, &g, &b ))
446#endif // wxUSE_PALETTE
447 r = g = b = 0;
1971d23c
VZ
448
449 rgbquad[i*4] = b;
450 rgbquad[i*4+1] = g;
451 rgbquad[i*4+2] = r;
452 rgbquad[i*4+3] = 0;
453 }
454 }
455 // make a 256 entry greyscale colormap or 2 entry black & white
456 else if ((format == wxBMP_8BPP_GREY) || (format == wxBMP_8BPP_RED) ||
457 (format == wxBMP_1BPP_BW))
458 {
459 int i;
460 rgbquad = new wxUint8 [palette_size*4];
461
462 for (i=0; i<palette_size; i++)
463 {
464 // if 1BPP_BW then just 0 and 255 then exit
465 if (( i > 0) && (format == wxBMP_1BPP_BW)) i = 255;
466 rgbquad[i*4] = i;
467 rgbquad[i*4+1] = i;
468 rgbquad[i*4+2] = i;
469 rgbquad[i*4+3] = 0;
470 }
471 }
472
473 // if the colormap was made, then it needs to be written
474 if (rgbquad)
475 {
37b83ca6
VS
476 if (!IsMask)
477 {
1971d23c
VZ
478 if (!stream.Write(rgbquad, palette_size*4))
479 {
b11e8fb6
VZ
480 if (verbose)
481 wxLogError(_("BMP: Couldn't write RGB color map."));
482 delete [] rgbquad;
483#if wxUSE_PALETTE
484 delete palette;
485#endif // wxUSE_PALETTE
486 delete q_image;
1971d23c
VZ
487 return FALSE;
488 }
37b83ca6 489 }
1971d23c
VZ
490 delete []rgbquad;
491 }
492
493 // pointer to the image data, use quantized if available
f6bcfd97 494 wxUint8 *data = (wxUint8*) image->GetData();
1971d23c
VZ
495 if (q_image) if (q_image->Ok()) data = (wxUint8*) q_image->GetData();
496
f6bcfd97 497 wxUint8 *buffer = new wxUint8[row_width];
f6bcfd97
BP
498 memset(buffer, 0, row_width);
499 int y; unsigned x;
1971d23c 500 long int pixel;
f6bcfd97
BP
501
502 for (y = image->GetHeight() -1 ; y >= 0; y--)
503 {
1971d23c
VZ
504 if (format == wxBMP_24BPP) // 3 bytes per pixel red,green,blue
505 {
506 for (x = 0; x < width; x++)
507 {
508 pixel = 3*(y*width + x);
509
510 buffer[3*x ] = data[pixel+2];
511 buffer[3*x + 1] = data[pixel+1];
512 buffer[3*x + 2] = data[pixel];
513 }
514 }
515 else if ((format == wxBMP_8BPP) || // 1 byte per pixel in color
516 (format == wxBMP_8BPP_PALETTE))
517 {
518 for (x = 0; x < width; x++)
519 {
520 pixel = 3*(y*width + x);
b11e8fb6 521#if wxUSE_PALETTE
1971d23c
VZ
522 buffer[x] = palette->GetPixel( data[pixel],
523 data[pixel+1],
524 data[pixel+2] );
b11e8fb6
VZ
525#else
526 // FIXME: what should this be? use some std palette maybe?
527 buffer[x] = 0;
528#endif // wxUSE_PALETTE
1971d23c
VZ
529 }
530 }
531 else if (format == wxBMP_8BPP_GREY) // 1 byte per pix, rgb ave to grey
532 {
533 for (x = 0; x < width; x++)
534 {
535 pixel = 3*(y*width + x);
536 buffer[x] = (wxUint8)(.299*data[pixel] +
537 .587*data[pixel+1] +
538 .114*data[pixel+2]);
539 }
540 }
541 else if (format == wxBMP_8BPP_RED) // 1 byte per pixel, red as greys
f6bcfd97 542 {
1971d23c
VZ
543 for (x = 0; x < width; x++)
544 {
545 buffer[x] = (wxUint8)data[3*(y*width + x)];
546 }
547 }
548 else if (format == wxBMP_4BPP) // 4 bpp in color
549 {
550 for (x = 0; x < width; x+=2)
551 {
552 pixel = 3*(y*width + x);
553
554 // fill buffer, ignore if > width
b11e8fb6 555#if wxUSE_PALETTE
1971d23c 556 buffer[x/2] =
b11e8fb6
VZ
557 ((wxUint8)palette->GetPixel(data[pixel],
558 data[pixel+1],
559 data[pixel+2]) << 4) |
560 (((x+1) > width)
561 ? 0
562 : ((wxUint8)palette->GetPixel(data[pixel+3],
563 data[pixel+4],
564 data[pixel+5]) ));
565#else
566 // FIXME: what should this be? use some std palette maybe?
567 buffer[x/2] = 0;
568#endif // wxUSE_PALETTE
1971d23c
VZ
569 }
570 }
571 else if (format == wxBMP_1BPP) // 1 bpp in "color"
572 {
573 for (x = 0; x < width; x+=8)
574 {
575 pixel = 3*(y*width + x);
576
b11e8fb6
VZ
577#if wxUSE_PALETTE
578 buffer[x/8] = ((wxUint8)palette->GetPixel(data[pixel], data[pixel+1], data[pixel+2]) << 7) |
1971d23c
VZ
579 (((x+1) > width) ? 0 : ((wxUint8)palette->GetPixel(data[pixel+3], data[pixel+4], data[pixel+5]) << 6)) |
580 (((x+2) > width) ? 0 : ((wxUint8)palette->GetPixel(data[pixel+6], data[pixel+7], data[pixel+8]) << 5)) |
581 (((x+3) > width) ? 0 : ((wxUint8)palette->GetPixel(data[pixel+9], data[pixel+10], data[pixel+11]) << 4)) |
582 (((x+4) > width) ? 0 : ((wxUint8)palette->GetPixel(data[pixel+12], data[pixel+13], data[pixel+14]) << 3)) |
583 (((x+5) > width) ? 0 : ((wxUint8)palette->GetPixel(data[pixel+15], data[pixel+16], data[pixel+17]) << 2)) |
584 (((x+6) > width) ? 0 : ((wxUint8)palette->GetPixel(data[pixel+18], data[pixel+19], data[pixel+20]) << 1)) |
585 (((x+7) > width) ? 0 : ((wxUint8)palette->GetPixel(data[pixel+21], data[pixel+22], data[pixel+23]) ));
b11e8fb6
VZ
586#else
587 // FIXME: what should this be? use some std palette maybe?
588 buffer[x/8] = 0;
589#endif // wxUSE_PALETTE
1971d23c
VZ
590 }
591 }
592 else if (format == wxBMP_1BPP_BW) // 1 bpp B&W colormap from red color ONLY
593 {
594 for (x = 0; x < width; x+=8)
595 {
596 pixel = 3*(y*width + x);
597
598 buffer[x/8] =
599 (((wxUint8)(data[pixel] /128.)) << 7) |
600 ( ((x+1) > width) ? 0 : (((wxUint8)(data[pixel+3] /128.)) << 6)) |
601 ( ((x+2) > width) ? 0 : (((wxUint8)(data[pixel+6] /128.)) << 5)) |
602 ( ((x+3) > width) ? 0 : (((wxUint8)(data[pixel+9] /128.)) << 4)) |
603 ( ((x+4) > width) ? 0 : (((wxUint8)(data[pixel+12]/128.)) << 3)) |
604 ( ((x+5) > width) ? 0 : (((wxUint8)(data[pixel+15]/128.)) << 2)) |
605 ( ((x+6) > width) ? 0 : (((wxUint8)(data[pixel+18]/128.)) << 1)) |
606 ( ((x+7) > width) ? 0 : (((wxUint8)(data[pixel+21]/128.)) ));
607 }
f6bcfd97 608 }
33ac7e6f 609
f6bcfd97
BP
610 if (!stream.Write(buffer, row_width))
611 {
612 if (verbose)
613 wxLogError(_("BMP: Couldn't write data."));
614 delete[] buffer;
b11e8fb6
VZ
615#if wxUSE_PALETTE
616 delete palette;
617#endif // wxUSE_PALETTE
618 delete q_image;
f6bcfd97
BP
619 return FALSE;
620 }
621 }
622 delete[] buffer;
b11e8fb6
VZ
623#if wxUSE_PALETTE
624 delete palette;
625#endif // wxUSE_PALETTE
626 delete q_image;
f6bcfd97
BP
627
628 return TRUE;
629}
630
631
632
e65ed37a 633
52b64b0a
RR
634bool wxBMPHandler::DoLoadDib (wxImage * image, int width, int height, int bpp, int ncolors, int comp,
635 off_t bmpOffset, wxInputStream& stream,
636 bool verbose, bool IsBmp, bool hasPalette )
e65ed37a 637 {
31528cd3 638
52b64b0a
RR
639 wxInt32 aDword, rmask = 0, gmask = 0, bmask = 0;
640 int rshift = 0, gshift = 0, bshift = 0;
641 wxInt32 dbuf[4];
642 wxInt8 bbuf[4];
643 wxUint8 aByte;
644 wxUint16 aWord;
31528cd3 645
31528cd3 646
52b64b0a
RR
647 // allocate space for palette if needed
648 struct _cmap
e65ed37a 649 {
52b64b0a 650 unsigned char r, g, b;
e65ed37a 651 }
52b64b0a
RR
652 *cmap = NULL;
653
e65ed37a
RR
654 if (bpp < 16)
655 {
656 cmap = (struct _cmap *)malloc(sizeof(struct _cmap) * ncolors);
657 if (!cmap)
658 {
58c837a4 659 if (verbose)
52b64b0a 660 wxLogError( _("Loading DIB : Couldn't allocate memory.") );
e65ed37a
RR
661 return FALSE;
662 }
663 }
664 else
665 cmap = NULL;
666
52b64b0a
RR
667 // destroy existing here instead of
668 image->Destroy();
e65ed37a
RR
669 image->Create( width, height );
670 unsigned char *ptr = image->GetData();
671 if (!ptr)
672 {
58c837a4 673 if (verbose)
52b64b0a 674 wxLogError( _("Loading DIB : Couldn't allocate memory.") );
e65ed37a
RR
675 if (cmap)
676 free(cmap);
677 return FALSE;
678 }
e65ed37a
RR
679 /*
680 * Reading the palette, if it exists.
681 */
682 if (bpp < 16 && ncolors != 0)
683 {
3f4fc796
JS
684 unsigned char* r = new unsigned char[ncolors];
685 unsigned char* g = new unsigned char[ncolors];
686 unsigned char* b = new unsigned char[ncolors];
e65ed37a
RR
687 for (int j = 0; j < ncolors; j++)
688 {
52b64b0a
RR
689 if (hasPalette)
690 {
e65ed37a
RR
691 stream.Read( bbuf, 4 );
692 cmap[j].b = bbuf[0];
693 cmap[j].g = bbuf[1];
694 cmap[j].r = bbuf[2];
3f4fc796
JS
695
696 r[j] = cmap[j].r;
697 g[j] = cmap[j].g;
698 b[j] = cmap[j].b;
e65ed37a 699 }
52b64b0a
RR
700 else
701 {
702 //used in reading .ico file mask
703 r[j] = cmap[j].r = j * 255;
704 g[j] = cmap[j].g = j * 255;
705 b[j] = cmap[j].b = j * 255;
706 }
707 }
b11e8fb6
VZ
708
709#if wxUSE_PALETTE
3f4fc796
JS
710 // Set the palette for the wxImage
711 image->SetPalette(wxPalette(ncolors, r, g, b));
b11e8fb6 712#endif // wxUSE_PALETTE
3f4fc796
JS
713
714 delete[] r;
715 delete[] g;
716 delete[] b;
e65ed37a
RR
717 }
718 else if (bpp == 16 || bpp == 32)
719 {
720 if (comp == BI_BITFIELDS)
721 {
722 int bit = 0;
723 stream.Read( dbuf, 4 * 3 );
724 bmask = wxINT32_SWAP_ON_BE( dbuf[0] );
725 gmask = wxINT32_SWAP_ON_BE( dbuf[1] );
726 rmask = wxINT32_SWAP_ON_BE( dbuf[2] );
727 /* find shift amount.. ugly, but i can't think of a better way */
728 for (bit = 0; bit < bpp; bit++)
729 {
730 if (bmask & (1 << bit))
731 bshift = bit;
732 if (gmask & (1 << bit))
733 gshift = bit;
734 if (rmask & (1 << bit))
735 rshift = bit;
736 }
737 }
738 else if (bpp == 16)
739 {
740 rmask = 0x7C00;
741 gmask = 0x03E0;
742 bmask = 0x001F;
743 rshift = 10;
744 gshift = 5;
745 bshift = 0;
746 }
747 else if (bpp == 32)
748 {
749 rmask = 0x00FF0000;
750 gmask = 0x0000FF00;
751 bmask = 0x000000FF;
752 rshift = 16;
753 gshift = 8;
754 bshift = 0;
755 }
756 }
757
758 /*
759 * Reading the image data
760 */
52b64b0a
RR
761 if ( IsBmp ) stream.SeekI( bmpOffset ); // else icon, just carry on
762
e65ed37a
RR
763 unsigned char *data = ptr;
764
765 /* set the whole image to the background color */
766 if (bpp < 16 && (comp == BI_RLE4 || comp == BI_RLE8))
767 {
768 for (int i = 0; i < width * height; i++)
769 {
770 *ptr++ = cmap[0].r;
771 *ptr++ = cmap[0].g;
772 *ptr++ = cmap[0].b;
773 }
774 ptr = data;
775 }
31528cd3 776
e65ed37a
RR
777 int line = 0;
778 int column = 0;
779 int linesize = ((width * bpp + 31) / 32) * 4;
780
781 /* BMPs are stored upside down */
31528cd3 782 for (line = (height - 1); line >= 0; line--)
e65ed37a
RR
783 {
784 int linepos = 0;
785 for (column = 0; column < width;)
786 {
787 if (bpp < 16)
788 {
789 int index = 0;
790 linepos++;
791 aByte = stream.GetC();
792 if (bpp == 1)
793 {
794 int bit = 0;
942bef71 795 for (bit = 0; bit < 8 && column < width; bit++)
e65ed37a
RR
796 {
797 index = ((aByte & (0x80 >> bit)) ? 1 : 0);
798 ptr[poffset] = cmap[index].r;
799 ptr[poffset + 1] = cmap[index].g;
800 ptr[poffset + 2] = cmap[index].b;
801 column++;
802 }
803 }
804 else if (bpp == 4)
805 {
806 if (comp == BI_RLE4)
807 {
58c837a4 808 if (verbose)
52b64b0a 809 wxLogError( _("DIB Header: Cannot deal with 4bit encoded yet.") );
e65ed37a
RR
810 image->Destroy();
811 free(cmap);
812 return FALSE;
813 }
814 else
815 {
816 int nibble = 0;
942bef71 817 for (nibble = 0; nibble < 2 && column < width; nibble++)
e65ed37a
RR
818 {
819 index = ((aByte & (0xF0 >> nibble * 4)) >> (!nibble * 4));
820 if (index >= 16)
821 index = 15;
822 ptr[poffset] = cmap[index].r;
823 ptr[poffset + 1] = cmap[index].g;
824 ptr[poffset + 2] = cmap[index].b;
825 column++;
826 }
827 }
828 }
829 else if (bpp == 8)
830 {
831 if (comp == BI_RLE8)
832 {
833 unsigned char first;
834 first = aByte;
835 aByte = stream.GetC();
836 if (first == 0)
837 {
838 if (aByte == 0)
839 {
840 /* column = width; */
841 }
842 else if (aByte == 1)
843 {
844 column = width;
845 line = -1;
846 }
847 else if (aByte == 2)
848 {
849 aByte = stream.GetC();
850 column += aByte;
851 linepos = column * bpp / 8;
852 aByte = stream.GetC();
853 line += aByte;
854 }
855 else
856 {
857 int absolute = aByte;
858 for (int k = 0; k < absolute; k++)
859 {
860 linepos++;
861 aByte = stream.GetC();
862 ptr[poffset ] = cmap[aByte].r;
863 ptr[poffset + 1] = cmap[aByte].g;
864 ptr[poffset + 2] = cmap[aByte].b;
865 column++;
866 }
867 if (absolute & 0x01)
868 aByte = stream.GetC();
869 }
870 }
871 else
872 {
942bef71 873 for (int l = 0; l < first && column < width; l++)
e65ed37a
RR
874 {
875 ptr[poffset ] = cmap[aByte].r;
876 ptr[poffset + 1] = cmap[aByte].g;
877 ptr[poffset + 2] = cmap[aByte].b;
878 column++;
879 linepos++;
880 }
881 }
882 }
883 else
884 {
885 ptr[poffset ] = cmap[aByte].r;
886 ptr[poffset + 1] = cmap[aByte].g;
887 ptr[poffset + 2] = cmap[aByte].b;
888 column++;
953704c1 889 // linepos += size; seems to be wrong, RR
e65ed37a
RR
890 }
891 }
892 }
893 else if (bpp == 24)
894 {
33ac7e6f 895 stream.Read( bbuf, 3 );
e65ed37a
RR
896 linepos += 3;
897 ptr[poffset ] = (unsigned char)bbuf[2];
898 ptr[poffset + 1] = (unsigned char)bbuf[1];
899 ptr[poffset + 2] = (unsigned char)bbuf[0];
900 column++;
901 }
902 else if (bpp == 16)
903 {
904 unsigned char temp;
905 stream.Read( &aWord, 2 );
31528cd3 906 aWord = wxUINT16_SWAP_ON_BE( aWord );
e65ed37a
RR
907 linepos += 2;
908 temp = (aWord & rmask) >> rshift;
909 ptr[poffset] = temp;
910 temp = (aWord & gmask) >> gshift;
911 ptr[poffset + 1] = temp;
e115e771 912 temp = (aWord & bmask) >> bshift;
e65ed37a
RR
913 ptr[poffset + 2] = temp;
914 column++;
915 }
916 else
917 {
918 unsigned char temp;
919 stream.Read( &aDword, 4 );
31528cd3 920 aDword = wxINT32_SWAP_ON_BE( aDword );
e65ed37a
RR
921 linepos += 4;
922 temp = (aDword & rmask) >> rshift;
923 ptr[poffset] = temp;
924 temp = (aDword & gmask) >> gshift;
925 ptr[poffset + 1] = temp;
926 temp = (aDword & bmask) >> bshift;
927 ptr[poffset + 2] = temp;
928 column++;
929 }
930 }
931 while ((linepos < linesize) && (comp != 1) && (comp != 2))
932 {
933 stream.Read( &aByte, 1 );
934 linepos += 1;
935 if (stream.LastError() != wxStream_NOERROR)
936 break;
937 }
938 }
31528cd3 939 if (cmap)
e65ed37a
RR
940 free(cmap);
941
942 image->SetMask( FALSE );
943
52b64b0a
RR
944 return stream.IsOk();
945}
946
947
948bool wxBMPHandler::LoadDib( wxImage *image, wxInputStream& stream, bool verbose, bool IsBmp )
949{
52b64b0a
RR
950 wxUint16 aWord;
951 wxInt32 dbuf[4];
952 wxInt8 bbuf[4];
953 off_t offset;
954
955 offset = 0; // keep gcc quiet
956 if ( IsBmp )
957 {
958 // read the header off the .BMP format file
959
960 offset = stream.TellI();
961 if (offset == wxInvalidOffset) offset = 0;
962
963 stream.Read( bbuf, 2 );
964
965 stream.Read( dbuf, 16 );
966 }
967 else
968 {
969 stream.Read( dbuf, 4 );
970 }
971 #if 0 // unused
972 wxInt32 size = wxINT32_SWAP_ON_BE( dbuf[0] );
973 #endif
974 offset = offset + wxINT32_SWAP_ON_BE( dbuf[2] );
975
976 stream.Read(dbuf, 4 * 2);
977 int width = (int)wxINT32_SWAP_ON_BE( dbuf[0] );
978 int height = (int)wxINT32_SWAP_ON_BE( dbuf[1] );
979 if ( !IsBmp ) height = height / 2; // for icons divide by 2
980
981 if (width > 32767)
982 {
983 if (verbose)
984 wxLogError( _("DIB Header: Image width > 32767 pixels for file.") );
985 return FALSE;
986 }
987 if (height > 32767)
988 {
989 if (verbose)
990 wxLogError( _("DIB Header: Image height > 32767 pixels for file.") );
991 return FALSE;
992 }
993
994 stream.Read( &aWord, 2 );
995 /*
996 TODO
997 int planes = (int)wxUINT16_SWAP_ON_BE( aWord );
998 */
999 stream.Read( &aWord, 2 );
1000 int bpp = (int)wxUINT16_SWAP_ON_BE( aWord );
1001 if (bpp != 1 && bpp != 4 && bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32)
1002 {
1003 if (verbose)
1004 wxLogError( _("DIB Header: Unknown bitdepth in file.") );
1005 return FALSE;
1006 }
1007
1008 stream.Read( dbuf, 4 * 4 );
1009 int comp = (int)wxINT32_SWAP_ON_BE( dbuf[0] );
1010 if (comp != BI_RGB && comp != BI_RLE4 && comp != BI_RLE8 && comp != BI_BITFIELDS)
1011 {
1012 if (verbose)
1013 wxLogError( _("DIB Header: Unknown encoding in file.") );
1014 return FALSE;
1015 }
1016
1017 stream.Read( dbuf, 4 * 2 );
1018 int ncolors = (int)wxINT32_SWAP_ON_BE( dbuf[0] );
1019 if (ncolors == 0)
1020 ncolors = 1 << bpp;
1021 /* some more sanity checks */
1022 if (((comp == BI_RLE4) && (bpp != 4)) ||
1023 ((comp == BI_RLE8) && (bpp != 8)) ||
1024 ((comp == BI_BITFIELDS) && (bpp != 16 && bpp != 32)))
1025 {
1026 if (verbose)
1027 wxLogError( _("DIB Header: Encoding doesn't match bitdepth.") );
1028 return FALSE;
1029 }
1030
1031 //read DIB; this is the BMP image or the XOR part of an icon image
1032 if (!DoLoadDib (image, width, height, bpp, ncolors, comp, offset, stream,
1033 verbose, IsBmp, TRUE ) )
1034 {
1035 if (verbose)
1036 wxLogError( _("Error in reading image DIB .") );
1037 return FALSE;
1038 }
1039
1040 if ( !IsBmp )
1041 {
1042 //read Icon mask which is monochrome
1043 //there is no palette, so we will create one
1f5b2017 1044 wxImage mask;
52b64b0a
RR
1045 if (!DoLoadDib (&mask, width, height, 1, 2, BI_RGB, offset, stream,
1046 verbose, IsBmp, FALSE ) )
1047 {
1048 if (verbose)
1049 wxLogError( _("ICO: Error in reading mask DIB.") );
1050 return FALSE;
1051 }
1f5b2017 1052 image->SetMaskFromImage(mask, 255, 255, 255);
52b64b0a
RR
1053
1054 }
e65ed37a
RR
1055 return TRUE;
1056}
1057
52b64b0a
RR
1058
1059bool wxBMPHandler::LoadFile ( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) )
1060{
1061 bool IsBmp = TRUE;
1062 //Read a single DIB fom the file
1063 return LoadDib ( image, stream, verbose, IsBmp ) ;
1064}
1065
1066
1067
1068bool wxICOHandler::LoadFile ( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) )
1069{
1070 bool bResult = FALSE ;
1071 bool IsBmp = FALSE;
1072
1073 ICONDIR m_IconDir ;
1074 stream.Read (&m_IconDir, sizeof(m_IconDir));
1075 wxUint16 nIcons = wxUINT16_SWAP_ON_BE ( m_IconDir.idCount ) ;
1076
1077 //loop round the icons and choose the best one
1078 ICONDIRENTRY * pIconDirEntry = new ICONDIRENTRY [nIcons];
1079 ICONDIRENTRY * pCurrentEntry = pIconDirEntry ;
1080 int i ;
1081 int wMax = 0 ;
1082 int colmax = 0 ;
1083 int iSel = wxNOT_FOUND ;
1084 for (i=0; i < nIcons ; i++ )
1085 {
1086 stream.Read(pCurrentEntry, sizeof(ICONDIRENTRY));
1087 //bHeight and bColorCount are wxUint8
1088 if (pCurrentEntry->bWidth >= wMax )
1089 {
1090 // see if we have more colors, ==0 indicates > 8bpp
1091 if (pCurrentEntry->bColorCount == 0 ) pCurrentEntry->bColorCount = 255 ;
1092 if (pCurrentEntry->bColorCount >= colmax)
1093 {
1094 iSel = i ;
1095 wMax = pCurrentEntry->bWidth ;
1096 colmax = pCurrentEntry->bColorCount ;
1097 }
1098 }
1099 pCurrentEntry ++ ;
1100 }
1101 if (iSel == wxNOT_FOUND)
1102 {
1103 bResult = FALSE;
1104 }
1105 else
1106 {
1107 //seek to selected icon
1108 pCurrentEntry = pIconDirEntry + iSel ;
1109 stream.SeekI (wxUINT32_SWAP_ON_BE ( pCurrentEntry -> dwImageOffset ), wxFromStart ) ;
1110 bResult = LoadDib ( image, stream, TRUE, IsBmp );
1111 }
1112 delete [] pIconDirEntry ;
37b83ca6 1113 return bResult ;
52b64b0a
RR
1114}
1115
52b64b0a 1116
995612e2 1117bool wxBMPHandler::DoCanRead( wxInputStream& stream )
0828c087
VS
1118{
1119 unsigned char hdr[2];
995612e2 1120
33ac7e6f 1121 stream.Read(hdr, 2);
0828c087
VS
1122 stream.SeekI(-2, wxFromCurrent);
1123 return (hdr[0] == 'B' && hdr[1] == 'M');
1124}
1125
52b64b0a
RR
1126bool wxICOHandler::DoCanRead( wxInputStream& stream )
1127{
1128 unsigned char hdr[4];
1129
1130 stream.Read(hdr, 4);
1131 stream.SeekI(-4, wxFromCurrent);
1132 return (hdr[0] == '\0' && hdr[1] == '\0' && hdr[2] == '\1' && hdr[3] == '\0');
1133}
1134
e65ed37a
RR
1135#endif // wxUSE_STREAMS
1136
c96ea657 1137#endif // wxUSE_IMAGE