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