]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dib.cpp
don't unselect all radio buttons if there are 2 consecutive radio groups, just those...
[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 // biClrUsed has the number of colors but it may be not initialized at
293 // all
294 int numColors = pbmih->biClrUsed;
295 if ( !numColors )
296 {
297 numColors = wxGetNumOfBitmapColors(pbmih->biBitCount);
298 }
299
300 bits = (char *)pbmih + sizeof(*pbmih) + numColors*sizeof(RGBQUAD);
301 }
302
303 HBITMAP hbmp = ::CreateDIBitmap
304 (
305 hdc ? hdc // create bitmap compatible
306 : (HDC) ScreenHDC(), // with this DC
307 pbmih, // used to get size &c
308 CBM_INIT, // initialize bitmap bits too
309 bits, // ... using this data
310 pbmi, // this is used for palette only
311 DIB_RGB_COLORS // direct or indexed palette?
312 );
313
314 if ( !hbmp )
315 {
316 wxLogLastError(wxT("CreateDIBitmap"));
317 }
318
319 return hbmp;
320 }
321
322 /* static */
323 size_t wxDIB::ConvertFromBitmap(BITMAPINFO *pbi, HBITMAP hbmp)
324 {
325 wxASSERT_MSG( hbmp, wxT("invalid bmp can't be converted to DIB") );
326
327 // prepare all the info we need
328 BITMAP bm;
329 if ( !::GetObject(hbmp, sizeof(bm), &bm) )
330 {
331 wxLogLastError(wxT("GetObject(bitmap)"));
332
333 return 0;
334 }
335
336 // we need a BITMAPINFO anyhow and if we're not given a pointer to it we
337 // use this one
338 BITMAPINFO bi2;
339
340 const bool wantSizeOnly = pbi == NULL;
341 if ( wantSizeOnly )
342 pbi = &bi2;
343
344 // just for convenience
345 const int h = bm.bmHeight;
346
347 // init the header
348 BITMAPINFOHEADER& bi = pbi->bmiHeader;
349 wxZeroMemory(bi);
350 bi.biSize = sizeof(BITMAPINFOHEADER);
351 bi.biWidth = bm.bmWidth;
352 bi.biHeight = h;
353 bi.biPlanes = 1;
354 bi.biBitCount = bm.bmBitsPixel;
355
356 // memory we need for BITMAPINFO only
357 DWORD dwLen = bi.biSize + wxGetNumOfBitmapColors(bm.bmBitsPixel) * sizeof(RGBQUAD);
358
359 // get either just the image size or the image bits
360 if ( !::GetDIBits
361 (
362 ScreenHDC(), // the DC to use
363 hbmp, // the source DDB
364 0, // first scan line
365 h, // number of lines to copy
366 wantSizeOnly ? NULL // pointer to the buffer or
367 : (char *)pbi + dwLen, // NULL if we don't have it
368 pbi, // bitmap header
369 DIB_RGB_COLORS // or DIB_PAL_COLORS
370 ) )
371 {
372 wxLogLastError(wxT("GetDIBits()"));
373
374 return 0;
375 }
376
377 // return the total size
378 return dwLen + bi.biSizeImage;
379 }
380
381 /* static */
382 HGLOBAL wxDIB::ConvertFromBitmap(HBITMAP hbmp)
383 {
384 // first calculate the size needed
385 const size_t size = ConvertFromBitmap(NULL, hbmp);
386 if ( !size )
387 {
388 // conversion to DDB failed?
389 return NULL;
390 }
391
392 HGLOBAL hDIB = ::GlobalAlloc(GMEM_MOVEABLE, size);
393 if ( !hDIB )
394 {
395 // this is an error which does risk to happen especially under Win9x
396 // and which the user may understand so let him know about it
397 wxLogError(_("Failed to allocated %luKb of memory for bitmap data."),
398 (unsigned long)(size / 1024));
399
400 return NULL;
401 }
402
403 if ( !ConvertFromBitmap((BITMAPINFO *)GlobalHandle(hDIB), hbmp) )
404 {
405 // this really shouldn't happen... it worked the first time, why not
406 // now?
407 wxFAIL_MSG( _T("wxDIB::ConvertFromBitmap() unexpectedly failed") );
408
409 return NULL;
410 }
411
412 return hDIB;
413 }
414
415 // ----------------------------------------------------------------------------
416 // palette support
417 // ----------------------------------------------------------------------------
418
419 #if wxUSE_PALETTE
420
421 wxPalette *wxDIB::CreatePalette() const
422 {
423 wxCHECK_MSG( m_handle, NULL, _T("wxDIB::CreatePalette(): invalid object") );
424
425 DIBSECTION ds;
426 if ( !::GetObject(m_handle, sizeof(ds), &ds) )
427 {
428 wxLogLastError(_T("GetObject(hDIB)"));
429
430 return 0;
431 }
432
433 // how many colours are we going to have in the palette?
434 DWORD biClrUsed = ds.dsBmih.biClrUsed;
435 if ( !biClrUsed )
436 {
437 // biClrUsed field might not be set
438 biClrUsed = wxGetNumOfBitmapColors(ds.dsBmih.biBitCount);
439 }
440
441 if ( !biClrUsed )
442 {
443 // bitmaps of this depth don't have palettes at all
444 //
445 // NB: another possibility would be to return
446 // GetStockObject(DEFAULT_PALETTE) or even CreateHalftonePalette()?
447 return NULL;
448 }
449
450 // LOGPALETTE struct has only 1 element in palPalEntry array, we're
451 // going to have biClrUsed of them so add necessary space
452 LOGPALETTE *pPalette = (LOGPALETTE *)
453 malloc(sizeof(LOGPALETTE) + (biClrUsed - 1)*sizeof(PALETTEENTRY));
454 wxCHECK_MSG( pPalette, NULL, _T("out of memory") );
455
456 // initialize the palette header
457 pPalette->palVersion = 0x300; // magic number, not in docs but works
458 pPalette->palNumEntries = biClrUsed;
459
460 // and the colour table (it starts right after the end of the header)
461 const RGBQUAD *pRGB = (RGBQUAD *)((char *)&ds.dsBmih + ds.dsBmih.biSize);
462 for ( DWORD i = 0; i < biClrUsed; i++, pRGB++ )
463 {
464 pPalette->palPalEntry[i].peRed = pRGB->rgbRed;
465 pPalette->palPalEntry[i].peGreen = pRGB->rgbGreen;
466 pPalette->palPalEntry[i].peBlue = pRGB->rgbBlue;
467 pPalette->palPalEntry[i].peFlags = 0;
468 }
469
470 HPALETTE hPalette = ::CreatePalette(pPalette);
471
472 free(pPalette);
473
474 if ( !hPalette )
475 {
476 wxLogLastError(_T("CreatePalette"));
477
478 return NULL;
479 }
480
481 wxPalette *palette = new wxPalette;
482 palette->SetHPALETTE((WXHPALETTE)hPalette);
483
484 return palette;
485 }
486
487 #endif // wxUSE_PALETTE
488
489 // ----------------------------------------------------------------------------
490 // wxImage support
491 // ----------------------------------------------------------------------------
492
493 #if wxUSE_IMAGE
494
495 bool wxDIB::Create(const wxImage& image)
496 {
497 wxCHECK_MSG( image.Ok(), false, _T("invalid wxImage in wxDIB ctor") );
498
499 const int h = image.GetHeight();
500 const int w = image.GetWidth();
501
502 // if we have alpha channel, we need to create a 32bpp RGBA DIB, otherwise
503 // a 24bpp RGB is sufficient
504 const bool hasAlpha = image.HasAlpha();
505 const int bpp = hasAlpha ? 32 : 24;
506
507 if ( !Create(w, h, bpp) )
508 return false;
509
510 // DIBs are stored in bottom to top order (see also the comment above in
511 // Create()) so we need to copy bits line by line and starting from the end
512 const int srcBytesPerLine = w * 3;
513 const int dstBytesPerLine = GetLineSize(w, bpp);
514 const unsigned char *src = image.GetData() + ((h - 1) * srcBytesPerLine);
515 const unsigned char *alpha = hasAlpha ? image.GetAlpha() + (h - 1)*w : NULL;
516 unsigned char *dstLineStart = (unsigned char *)m_data;
517 for ( int y = 0; y < h; y++ )
518 {
519 // copy one DIB line
520 unsigned char *dst = dstLineStart;
521 for ( int x = 0; x < w; x++ )
522 {
523 // also, the order of RGB is inversed for DIBs
524 *dst++ = src[2];
525 *dst++ = src[1];
526 *dst++ = src[0];
527
528 src += 3;
529
530 if ( alpha )
531 *dst++ = *alpha++;
532 }
533
534 // pass to the previous line in the image
535 src -= 2*srcBytesPerLine;
536 if ( alpha )
537 alpha -= 2*w;
538
539 // and to the next one in the DIB
540 dstLineStart += dstBytesPerLine;
541 }
542
543 return true;
544 }
545
546 #endif // wxUSE_IMAGE
547