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