]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dib.cpp
a3e9b49a45406a70110b3a2b5179d63b7b8d74e9
[wxWidgets.git] / src / msw / dib.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dib.cpp
3 // Purpose: implements wxDIB class
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 03.03.03 (replaces the old file with the same name)
7 // RCS-ID: $Id$
8 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwindows.org>
9 // License: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 /*
13 TODO: support for palettes is very incomplete, several functions simply
14 ignore them (we should select and realize the palette, if any, before
15 caling GetDIBits() in the DC we use with it and we shouldn't use
16 GetBitmapBits() at all because we can't do it with it)
17 */
18
19 // ============================================================================
20 // declarations
21 // ============================================================================
22
23 // ----------------------------------------------------------------------------
24 // headers
25 // ----------------------------------------------------------------------------
26
27 // For compilers that support precompilation, includes "wx.h".
28 #include "wx/wxprec.h"
29
30 #ifdef __BORLANDC__
31 #pragma hdrstop
32 #endif
33
34 #ifndef WX_PRECOMP
35 #include "wx/string.h"
36 #include "wx/log.h"
37 #endif //WX_PRECOMP
38
39 #include "wx/bitmap.h"
40 #include "wx/intl.h"
41 #include "wx/file.h"
42
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 #if !defined(__MWERKS__) && !defined(__SALFORDC__)
47 #include <memory.h>
48 #endif
49
50 #ifdef __GNUWIN32_OLD__
51 #include "wx/msw/gnuwin32/extra.h"
52 #endif
53
54 #include "wx/image.h"
55 #include "wx/msw/dib.h"
56
57 // ----------------------------------------------------------------------------
58 // private functions
59 // ----------------------------------------------------------------------------
60
61 // calculate the number of palette entries needed for the bitmap with this
62 // number of bits per pixel
63 static WORD wxGetNumOfBitmapColors(WORD bitsPerPixel)
64 {
65 // only 1, 4 and 8bpp bitmaps use palettes (well, they could be used with
66 // 24bpp ones too but we don't support this as I think it's quite uncommon)
67 return bitsPerPixel <= 8 ? 1 << bitsPerPixel : 0;
68 }
69
70 // ============================================================================
71 // implementation
72 // ============================================================================
73
74 // ----------------------------------------------------------------------------
75 // wxDIB creation
76 // ----------------------------------------------------------------------------
77
78 bool wxDIB::Create(int width, int height, int depth)
79 {
80 // we don't handle the palette yet
81 wxASSERT_MSG( depth == 24 || depth == 32,
82 _T("unsupported image depth in wxDIB::Create()") );
83
84 static const int infosize = sizeof(BITMAPINFOHEADER);
85
86 BITMAPINFO *info = (BITMAPINFO *)malloc(infosize);
87 wxCHECK_MSG( info, NULL, _T("malloc(BITMAPINFO) failed") );
88
89 memset(info, 0, infosize);
90
91 info->bmiHeader.biSize = infosize;
92 info->bmiHeader.biWidth = width;
93
94 // we use positive height here which corresponds to a DIB with normal, i.e.
95 // bottom to top, order -- normally using negative height (which means
96 // reversed for MS and hence natural for all the normal people top to
97 // bottom line scan order) could be used to avoid the need for the image
98 // reversal in Create(image) but this doesn't work under NT, only Win9x!
99 info->bmiHeader.biHeight = height;
100
101 info->bmiHeader.biPlanes = 1;
102 info->bmiHeader.biBitCount = depth;
103 info->bmiHeader.biSizeImage = GetLineSize(width, depth)*height;
104
105 m_handle = ::CreateDIBSection
106 (
107 0, // hdc (unused with DIB_RGB_COLORS)
108 info, // bitmap description
109 DIB_RGB_COLORS, // use RGB, not palette
110 &m_data, // [out] DIB bits
111 NULL, // don't use file mapping
112 0 // file mapping offset (not used here)
113 );
114
115 free(info);
116
117 if ( !m_handle )
118 {
119 wxLogLastError(wxT("CreateDIBSection"));
120
121 return false;
122 }
123
124 m_width = width;
125 m_height = height;
126 m_depth = depth;
127
128 return true;
129 }
130
131 bool wxDIB::Create(const wxBitmap& bmp)
132 {
133 wxCHECK_MSG( bmp.Ok(), false, _T("wxDIB::Create(): invalid bitmap") );
134
135 const int w = bmp.GetWidth();
136 const int h = bmp.GetHeight();
137 int d = bmp.GetDepth();
138 if ( d == -1 )
139 d = wxDisplayDepth();
140
141 if ( !Create(w, h, d) )
142 return false;
143
144 // we could have used GetDIBits() too but GetBitmapBits() is simpler
145 if ( !::GetBitmapBits
146 (
147 GetHbitmapOf(bmp), // the source DDB
148 GetLineSize(w, d)*h, // the number of bytes to copy
149 m_data // the pixels will be copied here
150 ) )
151 {
152 wxLogLastError(wxT("GetDIBits()"));
153
154 return 0;
155 }
156
157 return true;
158 }
159
160 // ----------------------------------------------------------------------------
161 // Loading/saving the DIBs
162 // ----------------------------------------------------------------------------
163
164 bool wxDIB::Load(const wxString& filename)
165 {
166 m_handle = (HBITMAP)::LoadImage
167 (
168 wxGetInstance(),
169 filename,
170 IMAGE_BITMAP,
171 0, 0, // don't specify the size
172 LR_CREATEDIBSECTION | LR_LOADFROMFILE
173 );
174 if ( !m_handle )
175 {
176 wxLogLastError(_T("LoadImage(LR_CREATEDIBSECTION | LR_LOADFROMFILE)"));
177
178 return false;
179 }
180
181 return true;
182 }
183
184 bool wxDIB::Save(const wxString& filename)
185 {
186 wxCHECK_MSG( m_handle, false, _T("wxDIB::Save(): invalid object") );
187
188 wxFile file(filename, wxFile::write);
189 bool ok = file.IsOpened();
190 if ( ok )
191 {
192 DIBSECTION ds;
193 if ( !::GetObject(m_handle, sizeof(ds), &ds) )
194 {
195 wxLogLastError(_T("GetObject(hDIB)"));
196 }
197 else
198 {
199 BITMAPFILEHEADER bmpHdr;
200 wxZeroMemory(bmpHdr);
201
202 const size_t sizeHdr = ds.dsBmih.biSize;
203 const size_t sizeImage = ds.dsBmih.biSizeImage;
204
205 bmpHdr.bfType = 0x4d42; // 'BM' in little endian
206 bmpHdr.bfOffBits = sizeof(BITMAPFILEHEADER) + ds.dsBmih.biSize;
207 bmpHdr.bfSize = bmpHdr.bfOffBits + sizeImage;
208
209 // first write the file header, then the bitmap header and finally the
210 // bitmap data itself
211 ok = file.Write(&bmpHdr, sizeof(bmpHdr)) == sizeof(bmpHdr) &&
212 file.Write(&ds.dsBmih, sizeHdr) == sizeHdr &&
213 file.Write(ds.dsBm.bmBits, sizeImage) == sizeImage;
214 }
215 }
216
217 if ( !ok )
218 {
219 wxLogError(_("Failed to save the bitmap image to file \"%s\"."),
220 filename.c_str());
221 }
222
223 return ok;
224 }
225
226 // ----------------------------------------------------------------------------
227 // wxDIB accessors
228 // ----------------------------------------------------------------------------
229
230 void wxDIB::DoGetObject() const
231 {
232 // only do something if we have a valid DIB but we don't [yet] have valid
233 // data
234 if ( m_handle && !m_data )
235 {
236 // although all the info we need is in BITMAP and so we don't really
237 // need DIBSECTION we still ask for it as modifying the bit values only
238 // works for the real DIBs and not for the bitmaps and it's better to
239 // check for this now rather than trying to find out why it doesn't
240 // work later
241 DIBSECTION ds;
242 if ( !::GetObject(m_handle, sizeof(ds), &ds) )
243 {
244 wxLogLastError(_T("GetObject(hDIB)"));
245 return;
246 }
247
248 wxDIB *self = wxConstCast(this, wxDIB);
249
250 self->m_width = ds.dsBm.bmWidth;
251 self->m_height = ds.dsBm.bmHeight;
252 self->m_depth = ds.dsBm.bmBitsPixel;
253 self->m_data = ds.dsBm.bmBits;
254 }
255 }
256
257 // ----------------------------------------------------------------------------
258 // DDB <-> DIB conversions
259 // ----------------------------------------------------------------------------
260
261 HBITMAP wxDIB::CreateDDB(HDC hdc) const
262 {
263 wxCHECK_MSG( m_handle, 0, _T("wxDIB::CreateDDB(): invalid object") );
264
265 DIBSECTION ds;
266 if ( !::GetObject(m_handle, sizeof(ds), &ds) )
267 {
268 wxLogLastError(_T("GetObject(hDIB)"));
269
270 return 0;
271 }
272
273 return ConvertToBitmap((BITMAPINFO *)&ds.dsBmih, hdc, ds.dsBm.bmBits);
274 }
275
276 /* static */
277 HBITMAP wxDIB::ConvertToBitmap(const BITMAPINFO *pbmi, HDC hdc, void *bits)
278 {
279 wxCHECK_MSG( pbmi, 0, _T("invalid DIB in ConvertToBitmap") );
280
281 // here we get BITMAPINFO struct followed by the actual bitmap bits and
282 // BITMAPINFO starts with BITMAPINFOHEADER followed by colour info
283 const BITMAPINFOHEADER *pbmih = &pbmi->bmiHeader;
284
285 // get the pointer to the start of the real image data if we have a plain
286 // DIB and not a DIB section (in the latter case the pointer must be passed
287 // to us by the caller)
288 if ( !bits )
289 {
290 // we must skip over the colour table to get to the image data
291 //
292 // colour table either has the real colour data in which case its
293 // number of entries is given by biClrUsed or is used for masks to be
294 // used for extracting colour information from true colour bitmaps in
295 // which case it always have exactly 3 DWORDs
296 int numColors;
297 switch ( pbmih->biCompression )
298 {
299 case BI_BITFIELDS:
300 numColors = 3;
301 break;
302
303 case BI_RGB:
304 // biClrUsed has the number of colors but it may be not initialized at
305 // all
306 numColors = pbmih->biClrUsed;
307 if ( !numColors )
308 {
309 numColors = wxGetNumOfBitmapColors(pbmih->biBitCount);
310 }
311 break;
312
313 default:
314 // no idea how it should be calculated for the other cases
315 numColors = 0;
316 }
317
318 bits = (char *)pbmih + sizeof(*pbmih) + numColors*sizeof(RGBQUAD);
319 }
320
321 HBITMAP hbmp = ::CreateDIBitmap
322 (
323 hdc ? hdc // create bitmap compatible
324 : (HDC) ScreenHDC(), // with this DC
325 pbmih, // used to get size &c
326 CBM_INIT, // initialize bitmap bits too
327 bits, // ... using this data
328 pbmi, // this is used for palette only
329 DIB_RGB_COLORS // direct or indexed palette?
330 );
331
332 if ( !hbmp )
333 {
334 wxLogLastError(wxT("CreateDIBitmap"));
335 }
336
337 return hbmp;
338 }
339
340 /* static */
341 size_t wxDIB::ConvertFromBitmap(BITMAPINFO *pbi, HBITMAP hbmp)
342 {
343 wxASSERT_MSG( hbmp, wxT("invalid bmp can't be converted to DIB") );
344
345 // prepare all the info we need
346 BITMAP bm;
347 if ( !::GetObject(hbmp, sizeof(bm), &bm) )
348 {
349 wxLogLastError(wxT("GetObject(bitmap)"));
350
351 return 0;
352 }
353
354 // we need a BITMAPINFO anyhow and if we're not given a pointer to it we
355 // use this one
356 BITMAPINFO bi2;
357
358 const bool wantSizeOnly = pbi == NULL;
359 if ( wantSizeOnly )
360 pbi = &bi2;
361
362 // just for convenience
363 const int h = bm.bmHeight;
364
365 // init the header
366 BITMAPINFOHEADER& bi = pbi->bmiHeader;
367 wxZeroMemory(bi);
368 bi.biSize = sizeof(BITMAPINFOHEADER);
369 bi.biWidth = bm.bmWidth;
370 bi.biHeight = h;
371 bi.biPlanes = 1;
372 bi.biBitCount = bm.bmBitsPixel;
373
374 // memory we need for BITMAPINFO only
375 DWORD dwLen = bi.biSize + wxGetNumOfBitmapColors(bm.bmBitsPixel) * sizeof(RGBQUAD);
376
377 // get either just the image size or the image bits
378 if ( !::GetDIBits
379 (
380 ScreenHDC(), // the DC to use
381 hbmp, // the source DDB
382 0, // first scan line
383 h, // number of lines to copy
384 wantSizeOnly ? NULL // pointer to the buffer or
385 : (char *)pbi + dwLen, // NULL if we don't have it
386 pbi, // bitmap header
387 DIB_RGB_COLORS // or DIB_PAL_COLORS
388 ) )
389 {
390 wxLogLastError(wxT("GetDIBits()"));
391
392 return 0;
393 }
394
395 // return the total size
396 return dwLen + bi.biSizeImage;
397 }
398
399 /* static */
400 HGLOBAL wxDIB::ConvertFromBitmap(HBITMAP hbmp)
401 {
402 // first calculate the size needed
403 const size_t size = ConvertFromBitmap(NULL, hbmp);
404 if ( !size )
405 {
406 // conversion to DDB failed?
407 return NULL;
408 }
409
410 HGLOBAL hDIB = ::GlobalAlloc(GMEM_MOVEABLE, size);
411 if ( !hDIB )
412 {
413 // this is an error which does risk to happen especially under Win9x
414 // and which the user may understand so let him know about it
415 wxLogError(_("Failed to allocated %luKb of memory for bitmap data."),
416 (unsigned long)(size / 1024));
417
418 return NULL;
419 }
420
421 if ( !ConvertFromBitmap((BITMAPINFO *)GlobalHandle(hDIB), hbmp) )
422 {
423 // this really shouldn't happen... it worked the first time, why not
424 // now?
425 wxFAIL_MSG( _T("wxDIB::ConvertFromBitmap() unexpectedly failed") );
426
427 return NULL;
428 }
429
430 return hDIB;
431 }
432
433 // ----------------------------------------------------------------------------
434 // palette support
435 // ----------------------------------------------------------------------------
436
437 #if wxUSE_PALETTE
438
439 wxPalette *wxDIB::CreatePalette() const
440 {
441 wxCHECK_MSG( m_handle, NULL, _T("wxDIB::CreatePalette(): invalid object") );
442
443 DIBSECTION ds;
444 if ( !::GetObject(m_handle, sizeof(ds), &ds) )
445 {
446 wxLogLastError(_T("GetObject(hDIB)"));
447
448 return 0;
449 }
450
451 // how many colours are we going to have in the palette?
452 DWORD biClrUsed = ds.dsBmih.biClrUsed;
453 if ( !biClrUsed )
454 {
455 // biClrUsed field might not be set
456 biClrUsed = wxGetNumOfBitmapColors(ds.dsBmih.biBitCount);
457 }
458
459 if ( !biClrUsed )
460 {
461 // bitmaps of this depth don't have palettes at all
462 //
463 // NB: another possibility would be to return
464 // GetStockObject(DEFAULT_PALETTE) or even CreateHalftonePalette()?
465 return NULL;
466 }
467
468 // LOGPALETTE struct has only 1 element in palPalEntry array, we're
469 // going to have biClrUsed of them so add necessary space
470 LOGPALETTE *pPalette = (LOGPALETTE *)
471 malloc(sizeof(LOGPALETTE) + (biClrUsed - 1)*sizeof(PALETTEENTRY));
472 wxCHECK_MSG( pPalette, NULL, _T("out of memory") );
473
474 // initialize the palette header
475 pPalette->palVersion = 0x300; // magic number, not in docs but works
476 pPalette->palNumEntries = biClrUsed;
477
478 // and the colour table (it starts right after the end of the header)
479 const RGBQUAD *pRGB = (RGBQUAD *)((char *)&ds.dsBmih + ds.dsBmih.biSize);
480 for ( DWORD i = 0; i < biClrUsed; i++, pRGB++ )
481 {
482 pPalette->palPalEntry[i].peRed = pRGB->rgbRed;
483 pPalette->palPalEntry[i].peGreen = pRGB->rgbGreen;
484 pPalette->palPalEntry[i].peBlue = pRGB->rgbBlue;
485 pPalette->palPalEntry[i].peFlags = 0;
486 }
487
488 HPALETTE hPalette = ::CreatePalette(pPalette);
489
490 free(pPalette);
491
492 if ( !hPalette )
493 {
494 wxLogLastError(_T("CreatePalette"));
495
496 return NULL;
497 }
498
499 wxPalette *palette = new wxPalette;
500 palette->SetHPALETTE((WXHPALETTE)hPalette);
501
502 return palette;
503 }
504
505 #endif // wxUSE_PALETTE
506
507 // ----------------------------------------------------------------------------
508 // wxImage support
509 // ----------------------------------------------------------------------------
510
511 #if wxUSE_IMAGE
512
513 bool wxDIB::Create(const wxImage& image)
514 {
515 wxCHECK_MSG( image.Ok(), false, _T("invalid wxImage in wxDIB ctor") );
516
517 const int h = image.GetHeight();
518 const int w = image.GetWidth();
519
520 // if we have alpha channel, we need to create a 32bpp RGBA DIB, otherwise
521 // a 24bpp RGB is sufficient
522 const bool hasAlpha = image.HasAlpha();
523 const int bpp = hasAlpha ? 32 : 24;
524
525 if ( !Create(w, h, bpp) )
526 return false;
527
528 // DIBs are stored in bottom to top order (see also the comment above in
529 // Create()) so we need to copy bits line by line and starting from the end
530 const int srcBytesPerLine = w * 3;
531 const int dstBytesPerLine = GetLineSize(w, bpp);
532 const unsigned char *src = image.GetData() + ((h - 1) * srcBytesPerLine);
533 const unsigned char *alpha = hasAlpha ? image.GetAlpha() + (h - 1)*w : NULL;
534 unsigned char *dstLineStart = (unsigned char *)m_data;
535 for ( int y = 0; y < h; y++ )
536 {
537 // copy one DIB line
538 unsigned char *dst = dstLineStart;
539 for ( int x = 0; x < w; x++ )
540 {
541 // also, the order of RGB is inversed for DIBs
542 *dst++ = src[2];
543 *dst++ = src[1];
544 *dst++ = src[0];
545
546 src += 3;
547
548 if ( alpha )
549 *dst++ = *alpha++;
550 }
551
552 // pass to the previous line in the image
553 src -= 2*srcBytesPerLine;
554 if ( alpha )
555 alpha -= 2*w;
556
557 // and to the next one in the DIB
558 dstLineStart += dstBytesPerLine;
559 }
560
561 return true;
562 }
563
564 #endif // wxUSE_IMAGE
565