]> git.saurik.com Git - wxWidgets.git/blame - src/msw/bitmap.cpp
fixes bug 414369
[wxWidgets.git] / src / msw / bitmap.cpp
CommitLineData
2bda0e17
KB
1/////////////////////////////////////////////////////////////////////////////
2// Name: bitmap.cpp
3// Purpose: wxBitmap
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart and Markus Holzem
1d792928 9// Licence: wxWindows license
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
10fcf31a
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17 20#ifdef __GNUG__
10fcf31a 21 #pragma implementation "bitmap.h"
2bda0e17
KB
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
10fcf31a 28 #pragma hdrstop
2bda0e17
KB
29#endif
30
31#ifndef WX_PRECOMP
10fcf31a
VZ
32 #include <stdio.h>
33
34 #include "wx/list.h"
35 #include "wx/utils.h"
36 #include "wx/app.h"
37 #include "wx/palette.h"
38 #include "wx/dcmemory.h"
39 #include "wx/bitmap.h"
40 #include "wx/icon.h"
2bda0e17
KB
41#endif
42
43#include "wx/msw/private.h"
1d792928
VZ
44#include "wx/log.h"
45
2bda0e17 46#include "wx/msw/dib.h"
b75dd496 47#include "wx/image.h"
2bda0e17 48
3c1a88d8
VZ
49// missing from mingw32 header
50#ifndef CLR_INVALID
51 #define CLR_INVALID ((COLORREF)-1)
52#endif // no CLR_INVALID
53
10fcf31a
VZ
54// ----------------------------------------------------------------------------
55// macros
56// ----------------------------------------------------------------------------
57
4b7f2165
VZ
58IMPLEMENT_DYNAMIC_CLASS(wxBitmap, wxGDIObject)
59IMPLEMENT_DYNAMIC_CLASS(wxMask, wxObject)
6d167489 60
4b7f2165 61IMPLEMENT_DYNAMIC_CLASS(wxBitmapHandler, wxObject)
2bda0e17 62
10fcf31a
VZ
63// ============================================================================
64// implementation
65// ============================================================================
66
67// ----------------------------------------------------------------------------
68// wxBitmapRefData
69// ----------------------------------------------------------------------------
70
71wxBitmapRefData::wxBitmapRefData()
2bda0e17 72{
6d167489
VZ
73 m_quality = 0;
74 m_selectedInto = NULL;
75 m_numColors = 0;
76 m_bitmapMask = NULL;
340196c0 77 m_hBitmap = (WXHBITMAP) NULL;
2bda0e17
KB
78}
79
6d167489 80void wxBitmapRefData::Free()
2bda0e17 81{
d59ceba5
VZ
82 wxASSERT_MSG( !m_selectedInto,
83 wxT("deleting bitmap still selected into wxMemoryDC") );
2bda0e17 84
d59ceba5 85 if ( m_hBitmap)
6d167489
VZ
86 {
87 if ( !::DeleteObject((HBITMAP)m_hBitmap) )
88 {
f6bcfd97 89 wxLogLastError(wxT("DeleteObject(hbitmap)"));
6d167489
VZ
90 }
91 }
e7003166 92
6d167489
VZ
93 delete m_bitmapMask;
94 m_bitmapMask = NULL;
2bda0e17
KB
95}
96
10fcf31a 97// ----------------------------------------------------------------------------
6d167489 98// wxBitmap creation
10fcf31a
VZ
99// ----------------------------------------------------------------------------
100
4fe5383d
VZ
101// this function should be called from all wxBitmap ctors
102void wxBitmap::Init()
2bda0e17 103{
4fe5383d 104 // m_refData = NULL; done in the base class ctor
2bda0e17 105
07cf98cb
VZ
106 if ( wxTheBitmapList )
107 wxTheBitmapList->AddBitmap(this);
4fe5383d
VZ
108}
109
6d167489
VZ
110#ifdef __WIN32__
111
112bool wxBitmap::CopyFromIconOrCursor(const wxGDIImage& icon)
113{
114 // it may be either HICON or HCURSOR
115 HICON hicon = (HICON)icon.GetHandle();
116
117 ICONINFO iconInfo;
118 if ( !::GetIconInfo(hicon, &iconInfo) )
119 {
f6bcfd97 120 wxLogLastError(wxT("GetIconInfo"));
6d167489
VZ
121
122 return FALSE;
123 }
124
125 wxBitmapRefData *refData = new wxBitmapRefData;
126 m_refData = refData;
127
d9c8e68e
VZ
128 int w = icon.GetWidth(),
129 h = icon.GetHeight();
130
131 refData->m_width = w;
132 refData->m_height = h;
6d167489
VZ
133 refData->m_depth = wxDisplayDepth();
134
135 refData->m_hBitmap = (WXHBITMAP)iconInfo.hbmColor;
d9c8e68e
VZ
136
137 // the mask returned by GetIconInfo() is inversed compared to the usual
138 // wxWin convention
4b7f2165
VZ
139 refData->m_bitmapMask = new wxMask((WXHBITMAP)
140 wxInvertMask(iconInfo.hbmMask, w, h));
6d167489
VZ
141
142#if WXWIN_COMPATIBILITY_2
143 refData->m_ok = TRUE;
144#endif // WXWIN_COMPATIBILITY_2
145
146 return TRUE;
147}
148
149#endif // Win32
150
151bool wxBitmap::CopyFromCursor(const wxCursor& cursor)
4fe5383d
VZ
152{
153 UnRef();
07cf98cb 154
6d167489 155 if ( !cursor.Ok() )
4fe5383d 156 return FALSE;
07cf98cb 157
6d167489
VZ
158#ifdef __WIN16__
159 wxFAIL_MSG( _T("don't know how to convert cursor to bitmap") );
160
161 return FALSE;
8f177c8e 162#else
6d167489 163 return CopyFromIconOrCursor(cursor);
8f177c8e 164#endif // Win16
6d167489
VZ
165}
166
167bool wxBitmap::CopyFromIcon(const wxIcon& icon)
168{
169 UnRef();
07cf98cb 170
6d167489
VZ
171 if ( !icon.Ok() )
172 return FALSE;
4fe5383d
VZ
173
174 // GetIconInfo() doesn't exist under Win16 and I don't know any other way
175 // to create a bitmap from icon there - but using this way we won't have
176 // the mask (FIXME)
177#ifdef __WIN16__
6d167489
VZ
178 int width = icon.GetWidth(),
179 height = icon.GetHeight();
180
4fe5383d 181 // copy the icon to the bitmap
6d167489 182 ScreenHDC hdcScreen;
4fe5383d
VZ
183 HDC hdc = ::CreateCompatibleDC(hdcScreen);
184 HBITMAP hbitmap = ::CreateCompatibleBitmap(hdcScreen, width, height);
07cf98cb
VZ
185 HBITMAP hbmpOld = (HBITMAP)::SelectObject(hdc, hbitmap);
186
6d167489 187 ::DrawIcon(hdc, 0, 0, GetHiconOf(icon));
07cf98cb
VZ
188
189 ::SelectObject(hdc, hbmpOld);
190 ::DeleteDC(hdc);
4fe5383d 191
6d167489
VZ
192 wxBitmapRefData *refData = new wxBitmapRefData;
193 m_refData = refData;
4fe5383d 194
6d167489
VZ
195 refData->m_width = width;
196 refData->m_height = height;
197 refData->m_depth = wxDisplayDepth();
07cf98cb 198
6d167489 199 refData->m_hBitmap = (WXHBITMAP)hbitmap;
07cf98cb 200
6d167489
VZ
201#if WXWIN_COMPATIBILITY_2
202 refData->m_ok = TRUE;
203#endif // WXWIN_COMPATIBILITY_2
222594ea 204
4fe5383d 205 return TRUE;
6d167489
VZ
206#else // Win32
207 return CopyFromIconOrCursor(icon);
208#endif // Win16/Win32
10fcf31a
VZ
209}
210
211wxBitmap::~wxBitmap()
2bda0e17
KB
212{
213 if (wxTheBitmapList)
214 wxTheBitmapList->DeleteObject(this);
215}
216
5bd3a2da 217wxBitmap::wxBitmap(const char bits[], int width, int height, int depth)
2bda0e17 218{
4fe5383d 219 Init();
2bda0e17 220
6d167489
VZ
221 wxBitmapRefData *refData = new wxBitmapRefData;
222 m_refData = refData;
2bda0e17 223
5bd3a2da
VZ
224 refData->m_width = width;
225 refData->m_height = height;
226 refData->m_depth = depth;
6d167489
VZ
227 refData->m_numColors = 0;
228 refData->m_selectedInto = NULL;
2bda0e17 229
5bd3a2da
VZ
230 char *data;
231 if ( depth == 1 )
232 {
233 // we assume that it is in XBM format which is not quite the same as
234 // the format CreateBitmap() wants because the order of bytes in the
235 // line is inversed!
236 static const size_t bytesPerLine = (width + 7) / 8;
237 static const size_t padding = bytesPerLine % 2;
238 static const size_t len = height * ( padding + bytesPerLine );
239 data = (char *)malloc(len);
240 const char *src = bits;
241 char *dst = data;
242
243 for ( int rows = 0; rows < height; rows++ )
244 {
0765adca 245 for ( size_t cols = 0; cols < bytesPerLine; cols++ )
5bd3a2da 246 {
0765adca
VZ
247 unsigned char val = *src++;
248 unsigned char reversed = 0;
249
250 for ( int bits = 0; bits < 8; bits++)
251 {
252 reversed <<= 1;
253 reversed |= (val & 0x01);
254 val >>= 1;
255 }
256 *dst++ = reversed;
5bd3a2da
VZ
257 }
258
259 if ( padding )
260 *dst++ = 0;
5bd3a2da
VZ
261 }
262 }
263 else
264 {
265 // bits should already be in Windows standard format
266 data = (char *)bits; // const_cast is harmless
267 }
268
269 HBITMAP hbmp = ::CreateBitmap(width, height, 1, depth, data);
6d167489
VZ
270 if ( !hbmp )
271 {
f6bcfd97 272 wxLogLastError(wxT("CreateBitmap"));
6d167489 273 }
2bda0e17 274
5bd3a2da
VZ
275 if ( data != bits )
276 {
277 free(data);
278 }
279
6d167489 280 SetHBITMAP((WXHBITMAP)hbmp);
2bda0e17
KB
281}
282
2fd284a4 283// Create from XPM data
4b7f2165 284bool wxBitmap::CreateFromXpm(const char **data)
2fd284a4 285{
4fe5383d
VZ
286 Init();
287
4b7f2165 288 return Create((void *)data, wxBITMAP_TYPE_XPM_DATA, 0, 0, 0);
2fd284a4
JS
289}
290
debe6624 291wxBitmap::wxBitmap(int w, int h, int d)
2bda0e17 292{
4fe5383d 293 Init();
2bda0e17 294
4fe5383d 295 (void)Create(w, h, d);
2bda0e17
KB
296}
297
debe6624 298wxBitmap::wxBitmap(void *data, long type, int width, int height, int depth)
2bda0e17 299{
4fe5383d 300 Init();
2bda0e17 301
6d167489 302 (void)Create(data, type, width, height, depth);
2bda0e17
KB
303}
304
debe6624 305wxBitmap::wxBitmap(const wxString& filename, long type)
2bda0e17 306{
4fe5383d 307 Init();
2bda0e17 308
4fe5383d 309 LoadFile(filename, (int)type);
2bda0e17
KB
310}
311
debe6624 312bool wxBitmap::Create(int w, int h, int d)
2bda0e17 313{
6d167489
VZ
314 UnRef();
315
316 m_refData = new wxBitmapRefData;
317
318 GetBitmapData()->m_width = w;
319 GetBitmapData()->m_height = h;
320 GetBitmapData()->m_depth = d;
321
322 HBITMAP hbmp;
323
324 if ( d > 0 )
325 {
326 hbmp = ::CreateBitmap(w, h, 1, d, NULL);
327 if ( !hbmp )
328 {
f6bcfd97 329 wxLogLastError(wxT("CreateBitmap"));
6d167489
VZ
330 }
331 }
332 else
333 {
334 ScreenHDC dc;
335 hbmp = ::CreateCompatibleBitmap(dc, w, h);
336 if ( !hbmp )
337 {
f6bcfd97 338 wxLogLastError(wxT("CreateCompatibleBitmap"));
6d167489
VZ
339 }
340
341 GetBitmapData()->m_depth = wxDisplayDepth();
342 }
2bda0e17 343
6d167489 344 SetHBITMAP((WXHBITMAP)hbmp);
2bda0e17 345
6d167489
VZ
346#if WXWIN_COMPATIBILITY_2
347 GetBitmapData()->m_ok = hbmp != 0;
348#endif // WXWIN_COMPATIBILITY_2
2bda0e17 349
6d167489 350 return Ok();
2bda0e17
KB
351}
352
fec19ea9
VS
353bool wxBitmap::CreateFromImage( const wxImage& image, int depth )
354{
355 wxCHECK_MSG( image.Ok(), FALSE, wxT("invalid image") )
356
357 m_refData = new wxBitmapRefData();
358
359 // sizeLimit is the MS upper limit for the DIB size
360#ifdef WIN32
361 int sizeLimit = 1024*768*3;
362#else
363 int sizeLimit = 0x7fff ;
364#endif
365
366 // width and height of the device-dependent bitmap
367 int width = image.GetWidth();
368 int bmpHeight = image.GetHeight();
369
370 // calc the number of bytes per scanline and padding
371 int bytePerLine = width*3;
372 int sizeDWORD = sizeof( DWORD );
373 int lineBoundary = bytePerLine % sizeDWORD;
374 int padding = 0;
375 if( lineBoundary > 0 )
376 {
377 padding = sizeDWORD - lineBoundary;
378 bytePerLine += padding;
379 }
380 // calc the number of DIBs and heights of DIBs
381 int numDIB = 1;
382 int hRemain = 0;
383 int height = sizeLimit/bytePerLine;
384 if( height >= bmpHeight )
385 height = bmpHeight;
386 else
387 {
388 numDIB = bmpHeight / height;
389 hRemain = bmpHeight % height;
390 if( hRemain >0 ) numDIB++;
391 }
392
393 // set bitmap parameters
097aeb99 394 wxCHECK_MSG( Ok(), FALSE, wxT("invalid image") );
fec19ea9
VS
395 SetWidth( width );
396 SetHeight( bmpHeight );
397 if (depth == -1) depth = wxDisplayDepth();
398 SetDepth( depth );
399
400 // create a DIB header
401 int headersize = sizeof(BITMAPINFOHEADER);
402 BITMAPINFO *lpDIBh = (BITMAPINFO *) malloc( headersize );
097aeb99 403 wxCHECK_MSG( lpDIBh, FALSE, wxT("could not allocate memory for DIB header") );
fec19ea9
VS
404 // Fill in the DIB header
405 lpDIBh->bmiHeader.biSize = headersize;
406 lpDIBh->bmiHeader.biWidth = (DWORD)width;
407 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
408 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
409 // the general formula for biSizeImage:
410 // ( ( ( ((DWORD)width*24) +31 ) & ~31 ) >> 3 ) * height;
411 lpDIBh->bmiHeader.biPlanes = 1;
412 lpDIBh->bmiHeader.biBitCount = 24;
413 lpDIBh->bmiHeader.biCompression = BI_RGB;
414 lpDIBh->bmiHeader.biClrUsed = 0;
415 // These seem not really needed for our purpose here.
416 lpDIBh->bmiHeader.biClrImportant = 0;
417 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
418 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
419 // memory for DIB data
420 unsigned char *lpBits;
421 lpBits = (unsigned char *)malloc( lpDIBh->bmiHeader.biSizeImage );
422 if( !lpBits )
423 {
424 wxFAIL_MSG( wxT("could not allocate memory for DIB") );
425 free( lpDIBh );
426 return FALSE;
427 }
428
429 // create and set the device-dependent bitmap
430 HDC hdc = ::GetDC(NULL);
431 HDC memdc = ::CreateCompatibleDC( hdc );
432 HBITMAP hbitmap;
433 hbitmap = ::CreateCompatibleBitmap( hdc, width, bmpHeight );
434 ::SelectObject( memdc, hbitmap);
435
436 HPALETTE hOldPalette = 0;
437 if (image.GetPalette().Ok())
438 {
439 hOldPalette = ::SelectPalette(memdc, (HPALETTE) image.GetPalette().GetHPALETTE(), FALSE);
440 ::RealizePalette(memdc);
441 }
442
443 // copy image data into DIB data and then into DDB (in a loop)
444 unsigned char *data = image.GetData();
445 int i, j, n;
446 int origin = 0;
447 unsigned char *ptdata = data;
448 unsigned char *ptbits;
449
450 for( n=0; n<numDIB; n++ )
451 {
452 if( numDIB > 1 && n == numDIB-1 && hRemain > 0 )
453 {
454 // redefine height and size of the (possibly) last smaller DIB
455 // memory is not reallocated
456 height = hRemain;
457 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
458 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
459 }
460 ptbits = lpBits;
461
462 for( j=0; j<height; j++ )
463 {
464 for( i=0; i<width; i++ )
465 {
466 *(ptbits++) = *(ptdata+2);
467 *(ptbits++) = *(ptdata+1);
468 *(ptbits++) = *(ptdata );
469 ptdata += 3;
470 }
471 for( i=0; i< padding; i++ ) *(ptbits++) = 0;
472 }
473 ::StretchDIBits( memdc, 0, origin, width, height,\
474 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
475 origin += height;
476 // if numDIB = 1, lines below can also be used
477 // hbitmap = CreateDIBitmap( hdc, &(lpDIBh->bmiHeader), CBM_INIT, lpBits, lpDIBh, DIB_RGB_COLORS );
478 // The above line is equivalent to the following two lines.
479 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
480 // ::SetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS);
481 // or the following lines
482 // hbitmap = ::CreateCompatibleBitmap( hdc, width, height );
483 // HDC memdc = ::CreateCompatibleDC( hdc );
484 // ::SelectObject( memdc, hbitmap);
485 // ::SetDIBitsToDevice( memdc, 0, 0, width, height,
486 // 0, 0, 0, height, (void *)lpBits, lpDIBh, DIB_RGB_COLORS);
487 // ::SelectObject( memdc, 0 );
488 // ::DeleteDC( memdc );
489 }
490 SetHBITMAP( (WXHBITMAP) hbitmap );
491
492 if (hOldPalette)
493 SelectPalette(memdc, hOldPalette, FALSE);
494
495 // similarly, created an mono-bitmap for the possible mask
496 if( image.HasMask() )
497 {
498 hbitmap = ::CreateBitmap( (WORD)width, (WORD)bmpHeight, 1, 1, NULL );
499 HGDIOBJ hbmpOld = ::SelectObject( memdc, hbitmap);
500 if( numDIB == 1 ) height = bmpHeight;
501 else height = sizeLimit/bytePerLine;
502 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
503 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
504 origin = 0;
505 unsigned char r = image.GetMaskRed();
506 unsigned char g = image.GetMaskGreen();
507 unsigned char b = image.GetMaskBlue();
508 unsigned char zero = 0, one = 255;
509 ptdata = data;
510 for( n=0; n<numDIB; n++ )
511 {
512 if( numDIB > 1 && n == numDIB - 1 && hRemain > 0 )
513 {
514 // redefine height and size of the (possibly) last smaller DIB
515 // memory is not reallocated
516 height = hRemain;
517 lpDIBh->bmiHeader.biHeight = (DWORD)(-height);
518 lpDIBh->bmiHeader.biSizeImage = bytePerLine*height;
519 }
520 ptbits = lpBits;
521 for( int j=0; j<height; j++ )
522 {
523 for(i=0; i<width; i++ )
524 {
525 // was causing a code gen bug in cw : if( ( cr !=r) || (cg!=g) || (cb!=b) )
526 unsigned char cr = (*(ptdata++)) ;
527 unsigned char cg = (*(ptdata++)) ;
528 unsigned char cb = (*(ptdata++)) ;
529
530 if( ( cr !=r) || (cg!=g) || (cb!=b) )
531 {
532 *(ptbits++) = one;
533 *(ptbits++) = one;
534 *(ptbits++) = one;
535 }
536 else
537 {
538 *(ptbits++) = zero;
539 *(ptbits++) = zero;
540 *(ptbits++) = zero;
541 }
542 }
543 for( i=0; i< padding; i++ ) *(ptbits++) = zero;
544 }
545 ::StretchDIBits( memdc, 0, origin, width, height,\
546 0, 0, width, height, lpBits, lpDIBh, DIB_RGB_COLORS, SRCCOPY);
547 origin += height;
548 }
549 // create a wxMask object
550 wxMask *mask = new wxMask();
551 mask->SetMaskBitmap( (WXHBITMAP) hbitmap );
552 SetMask( mask );
553 // It will be deleted when the wxBitmap object is deleted (as of 01/1999)
554 /* The following can also be used but is slow to run
555 wxColour colour( GetMaskRed(), GetMaskGreen(), GetMaskBlue());
556 wxMask *mask = new wxMask( *this, colour );
557 SetMask( mask );
558 */
559
560 ::SelectObject( memdc, hbmpOld );
561 }
562
563 // free allocated resources
564 ::DeleteDC( memdc );
565 ::ReleaseDC(NULL, hdc);
566 free(lpDIBh);
567 free(lpBits);
568
569#if WXWIN_COMPATIBILITY_2
570 // check the wxBitmap object
571 GetBitmapData()->SetOk();
572#endif // WXWIN_COMPATIBILITY_2
573
574 if (wxTheBitmapList) wxTheBitmapList->AddBitmap(this);
575
576 return TRUE;
577}
578
579wxImage wxBitmap::ConvertToImage() const
580{
581 wxImage image;
582
583 wxCHECK_MSG( Ok(), wxNullImage, wxT("invalid bitmap") );
584
585 // create an wxImage object
586 int width = GetWidth();
587 int height = GetHeight();
588 image.Create( width, height );
589 unsigned char *data = image.GetData();
590 if( !data )
591 {
592 wxFAIL_MSG( wxT("could not allocate data for image") );
593 return wxNullImage;
594 }
595
596 // calc the number of bytes per scanline and padding in the DIB
597 int bytePerLine = width*3;
598 int sizeDWORD = sizeof( DWORD );
599 int lineBoundary = bytePerLine % sizeDWORD;
600 int padding = 0;
601 if( lineBoundary > 0 )
602 {
603 padding = sizeDWORD - lineBoundary;
604 bytePerLine += padding;
605 }
606
607 // create a DIB header
608 int headersize = sizeof(BITMAPINFOHEADER);
609 BITMAPINFO *lpDIBh = (BITMAPINFO *) malloc( headersize );
610 if( !lpDIBh )
611 {
612 wxFAIL_MSG( wxT("could not allocate data for DIB header") );
613 free( data );
614 return wxNullImage;
615 }
616 // Fill in the DIB header
617 lpDIBh->bmiHeader.biSize = headersize;
618 lpDIBh->bmiHeader.biWidth = width;
619 lpDIBh->bmiHeader.biHeight = -height;
620 lpDIBh->bmiHeader.biSizeImage = bytePerLine * height;
621 lpDIBh->bmiHeader.biPlanes = 1;
622 lpDIBh->bmiHeader.biBitCount = 24;
623 lpDIBh->bmiHeader.biCompression = BI_RGB;
624 lpDIBh->bmiHeader.biClrUsed = 0;
625 // These seem not really needed for our purpose here.
626 lpDIBh->bmiHeader.biClrImportant = 0;
627 lpDIBh->bmiHeader.biXPelsPerMeter = 0;
628 lpDIBh->bmiHeader.biYPelsPerMeter = 0;
629 // memory for DIB data
630 unsigned char *lpBits;
631 lpBits = (unsigned char *) malloc( lpDIBh->bmiHeader.biSizeImage );
632 if( !lpBits )
633 {
634 wxFAIL_MSG( wxT("could not allocate data for DIB") );
635 free( data );
636 free( lpDIBh );
637 return wxNullImage;
638 }
639
640 // copy data from the device-dependent bitmap to the DIB
641 HDC hdc = ::GetDC(NULL);
642 HBITMAP hbitmap;
643 hbitmap = (HBITMAP) GetHBITMAP();
644 ::GetDIBits( hdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
645
646 // copy DIB data into the wxImage object
647 int i, j;
648 unsigned char *ptdata = data;
649 unsigned char *ptbits = lpBits;
650 for( i=0; i<height; i++ )
651 {
652 for( j=0; j<width; j++ )
653 {
654 *(ptdata++) = *(ptbits+2);
655 *(ptdata++) = *(ptbits+1);
656 *(ptdata++) = *(ptbits );
657 ptbits += 3;
658 }
659 ptbits += padding;
660 }
661
662 // similarly, set data according to the possible mask bitmap
663 if( GetMask() && GetMask()->GetMaskBitmap() )
664 {
665 hbitmap = (HBITMAP) GetMask()->GetMaskBitmap();
666 // memory DC created, color set, data copied, and memory DC deleted
667 HDC memdc = ::CreateCompatibleDC( hdc );
668 ::SetTextColor( memdc, RGB( 0, 0, 0 ) );
669 ::SetBkColor( memdc, RGB( 255, 255, 255 ) );
670 ::GetDIBits( memdc, hbitmap, 0, height, lpBits, lpDIBh, DIB_RGB_COLORS );
671 ::DeleteDC( memdc );
672 // background color set to RGB(16,16,16) in consistent with wxGTK
673 unsigned char r=16, g=16, b=16;
674 ptdata = data;
675 ptbits = lpBits;
676 for( i=0; i<height; i++ )
677 {
678 for( j=0; j<width; j++ )
679 {
680 if( *ptbits != 0 )
681 ptdata += 3;
682 else
683 {
684 *(ptdata++) = r;
685 *(ptdata++) = g;
686 *(ptdata++) = b;
687 }
688 ptbits += 3;
689 }
690 ptbits += padding;
691 }
692 image.SetMaskColour( r, g, b );
693 image.SetMask( TRUE );
694 }
695 else
696 {
697 image.SetMask( FALSE );
698 }
699 // free allocated resources
700 ::ReleaseDC(NULL, hdc);
701 free(lpDIBh);
702 free(lpBits);
703
704 return image;
705}
706
debe6624 707bool wxBitmap::LoadFile(const wxString& filename, long type)
2bda0e17 708{
6d167489 709 UnRef();
2bda0e17 710
6d167489 711 wxBitmapHandler *handler = wxDynamicCast(FindHandler(type), wxBitmapHandler);
2bda0e17 712
6d167489
VZ
713 if ( handler )
714 {
715 m_refData = new wxBitmapRefData;
2bda0e17 716
6d167489
VZ
717 return handler->LoadFile(this, filename, type, -1, -1);
718 }
719 else
b75dd496 720 {
6d167489
VZ
721 wxImage image;
722 if ( !image.LoadFile( filename, type ) || !image.Ok() )
723 return FALSE;
724
b75dd496 725 *this = image.ConvertToBitmap();
6d167489 726
b75dd496
VS
727 return TRUE;
728 }
2bda0e17
KB
729}
730
debe6624 731bool wxBitmap::Create(void *data, long type, int width, int height, int depth)
2bda0e17 732{
6d167489 733 UnRef();
2bda0e17 734
6d167489 735 wxBitmapHandler *handler = wxDynamicCast(FindHandler(type), wxBitmapHandler);
2bda0e17 736
6d167489
VZ
737 if ( !handler )
738 {
f6bcfd97 739 wxLogDebug(wxT("Failed to create bitmap: no bitmap handler for type %d defined."), type);
2bda0e17 740
6d167489
VZ
741 return FALSE;
742 }
1d792928 743
6d167489 744 m_refData = new wxBitmapRefData;
1d792928 745
6d167489 746 return handler->Create(this, data, type, width, height, depth);
2bda0e17
KB
747}
748
debe6624 749bool wxBitmap::SaveFile(const wxString& filename, int type, const wxPalette *palette)
2bda0e17 750{
6d167489 751 wxBitmapHandler *handler = wxDynamicCast(FindHandler(type), wxBitmapHandler);
2bda0e17 752
6d167489
VZ
753 if ( handler )
754 {
755 return handler->SaveFile(this, filename, type, palette);
756 }
757 else
758 {
759 // FIXME what about palette? shouldn't we use it?
760 wxImage image( *this );
761 if (!image.Ok())
762 return FALSE;
2bda0e17 763
6d167489
VZ
764 return image.SaveFile( filename, type );
765 }
2bda0e17
KB
766}
767
4b7f2165
VZ
768// ----------------------------------------------------------------------------
769// sub bitmap extraction
770// ----------------------------------------------------------------------------
771
772wxBitmap wxBitmap::GetSubBitmap( const wxRect& rect) const
773{
774 wxCHECK_MSG( Ok() &&
775 (rect.x >= 0) && (rect.y >= 0) &&
776 (rect.x+rect.width <= GetWidth()) &&
777 (rect.y+rect.height <= GetHeight()),
778 wxNullBitmap, wxT("Invalid bitmap or bitmap region") );
779
780 wxBitmap ret( rect.width, rect.height, GetDepth() );
781 wxASSERT_MSG( ret.Ok(), wxT("GetSubBitmap error") );
782
783 // copy bitmap data
784 HDC dcSrc = ::CreateCompatibleDC(NULL);
785 HDC dcDst = ::CreateCompatibleDC(NULL);
786 SelectObject(dcSrc, (HBITMAP) GetHBITMAP());
787 SelectObject(dcDst, (HBITMAP) ret.GetHBITMAP());
788 BitBlt(dcDst, 0, 0, rect.width, rect.height, dcSrc, rect.x, rect.y, SRCCOPY);
789
790 // copy mask if there is one
791 if (GetMask())
792 {
793 HBITMAP hbmpMask = ::CreateBitmap(rect.width, rect.height, 1, 1, 0);
794
795 SelectObject(dcSrc, (HBITMAP) GetMask()->GetMaskBitmap());
796 SelectObject(dcDst, (HBITMAP) hbmpMask);
797 BitBlt(dcDst, 0, 0, rect.width, rect.height, dcSrc, rect.x, rect.y, SRCCOPY);
798
799 wxMask *mask = new wxMask((WXHBITMAP) hbmpMask);
800 ret.SetMask(mask);
801 }
802
803 SelectObject(dcDst, NULL);
804 SelectObject(dcSrc, NULL);
805 DeleteDC(dcDst);
806 DeleteDC(dcSrc);
807
808 return ret;
809}
810
6d167489
VZ
811// ----------------------------------------------------------------------------
812// wxBitmap accessors
813// ----------------------------------------------------------------------------
2bda0e17
KB
814
815void wxBitmap::SetQuality(int q)
816{
6d167489 817 EnsureHasData();
2bda0e17 818
6d167489 819 GetBitmapData()->m_quality = q;
2bda0e17
KB
820}
821
6d167489 822#if WXWIN_COMPATIBILITY_2
2bda0e17
KB
823void wxBitmap::SetOk(bool isOk)
824{
6d167489 825 EnsureHasData();
2bda0e17 826
6d167489 827 GetBitmapData()->m_ok = isOk;
2bda0e17 828}
6d167489 829#endif // WXWIN_COMPATIBILITY_2
2bda0e17
KB
830
831void wxBitmap::SetPalette(const wxPalette& palette)
832{
6d167489 833 EnsureHasData();
2bda0e17 834
6d167489 835 GetBitmapData()->m_bitmapPalette = palette;
2bda0e17
KB
836}
837
838void wxBitmap::SetMask(wxMask *mask)
839{
6d167489 840 EnsureHasData();
2bda0e17 841
6d167489 842 GetBitmapData()->m_bitmapMask = mask;
2bda0e17
KB
843}
844
7b46ecac
JS
845// Creates a bitmap that matches the device context, from
846// an arbitray bitmap. At present, the original bitmap must have an
847// associated palette. TODO: use a default palette if no palette exists.
848// Contributed by Frederic Villeneuve <frederic.villeneuve@natinst.com>
849wxBitmap wxBitmap::GetBitmapForDC(wxDC& dc) const
850{
851 wxMemoryDC memDC;
4b7f2165 852 wxBitmap tmpBitmap(GetWidth(), GetHeight(), dc.GetDepth());
57c208c5 853 HPALETTE hPal = (HPALETTE) NULL;
7b46ecac 854 LPBITMAPINFO lpDib;
57c208c5 855 void *lpBits = (void*) NULL;
7b46ecac 856
6d167489 857 if( GetPalette() && GetPalette()->Ok() )
a367b9b3 858 {
6d167489 859 tmpBitmap.SetPalette(*GetPalette());
a367b9b3 860 memDC.SelectObject(tmpBitmap);
6d167489
VZ
861 memDC.SetPalette(*GetPalette());
862 hPal = (HPALETTE)GetPalette()->GetHPALETTE();
a367b9b3
JS
863 }
864 else
865 {
866 hPal = (HPALETTE) ::GetStockObject(DEFAULT_PALETTE);
867 wxPalette palette;
868 palette.SetHPALETTE( (WXHPALETTE)hPal );
869 tmpBitmap.SetPalette( palette );
870 memDC.SelectObject(tmpBitmap);
871 memDC.SetPalette( palette );
872 }
7b46ecac 873
6d167489
VZ
874 // set the height negative because in a DIB the order of the lines is
875 // reversed
876 if ( !wxCreateDIB(GetWidth(), -GetHeight(), GetDepth(), hPal, &lpDib) )
877 {
878 return wxNullBitmap;
879 }
7b46ecac
JS
880
881 lpBits = malloc(lpDib->bmiHeader.biSizeImage);
882
6d167489 883 ::GetBitmapBits(GetHbitmap(), lpDib->bmiHeader.biSizeImage, lpBits);
7b46ecac 884
6d167489
VZ
885 ::SetDIBitsToDevice(GetHdcOf(memDC), 0, 0,
886 GetWidth(), GetHeight(),
887 0, 0, 0, GetHeight(),
888 lpBits, lpDib, DIB_RGB_COLORS);
7b46ecac
JS
889
890 free(lpBits);
891
6d167489
VZ
892 wxFreeDIB(lpDib);
893
894 return tmpBitmap;
7b46ecac
JS
895}
896
6d167489
VZ
897// ----------------------------------------------------------------------------
898// wxMask
899// ----------------------------------------------------------------------------
2bda0e17 900
10fcf31a 901wxMask::wxMask()
2bda0e17
KB
902{
903 m_maskBitmap = 0;
904}
905
906// Construct a mask from a bitmap and a colour indicating
907// the transparent area
908wxMask::wxMask(const wxBitmap& bitmap, const wxColour& colour)
909{
910 m_maskBitmap = 0;
6d167489 911 Create(bitmap, colour);
2bda0e17
KB
912}
913
914// Construct a mask from a bitmap and a palette index indicating
915// the transparent area
debe6624 916wxMask::wxMask(const wxBitmap& bitmap, int paletteIndex)
2bda0e17
KB
917{
918 m_maskBitmap = 0;
6d167489 919 Create(bitmap, paletteIndex);
2bda0e17
KB
920}
921
922// Construct a mask from a mono bitmap (copies the bitmap).
923wxMask::wxMask(const wxBitmap& bitmap)
924{
925 m_maskBitmap = 0;
6d167489 926 Create(bitmap);
2bda0e17
KB
927}
928
10fcf31a 929wxMask::~wxMask()
2bda0e17
KB
930{
931 if ( m_maskBitmap )
932 ::DeleteObject((HBITMAP) m_maskBitmap);
933}
934
935// Create a mask from a mono bitmap (copies the bitmap).
936bool wxMask::Create(const wxBitmap& bitmap)
937{
a58a12e9
VZ
938 wxCHECK_MSG( bitmap.Ok() && bitmap.GetDepth() == 1, FALSE,
939 _T("can't create mask from invalid or not monochrome bitmap") );
940
2bda0e17 941 if ( m_maskBitmap )
6d167489
VZ
942 {
943 ::DeleteObject((HBITMAP) m_maskBitmap);
944 m_maskBitmap = 0;
945 }
a58a12e9 946
6d167489
VZ
947 m_maskBitmap = (WXHBITMAP) CreateBitmap(
948 bitmap.GetWidth(),
949 bitmap.GetHeight(),
950 1, 1, 0
951 );
952 HDC srcDC = CreateCompatibleDC(0);
953 SelectObject(srcDC, (HBITMAP) bitmap.GetHBITMAP());
954 HDC destDC = CreateCompatibleDC(0);
955 SelectObject(destDC, (HBITMAP) m_maskBitmap);
956 BitBlt(destDC, 0, 0, bitmap.GetWidth(), bitmap.GetHeight(), srcDC, 0, 0, SRCCOPY);
957 SelectObject(srcDC, 0);
958 DeleteDC(srcDC);
959 SelectObject(destDC, 0);
960 DeleteDC(destDC);
961 return TRUE;
2bda0e17
KB
962}
963
964// Create a mask from a bitmap and a palette index indicating
965// the transparent area
debe6624 966bool wxMask::Create(const wxBitmap& bitmap, int paletteIndex)
2bda0e17
KB
967{
968 if ( m_maskBitmap )
1d792928 969 {
6d167489
VZ
970 ::DeleteObject((HBITMAP) m_maskBitmap);
971 m_maskBitmap = 0;
1d792928 972 }
6d167489
VZ
973 if (bitmap.Ok() && bitmap.GetPalette()->Ok())
974 {
975 unsigned char red, green, blue;
976 if (bitmap.GetPalette()->GetRGB(paletteIndex, &red, &green, &blue))
977 {
978 wxColour transparentColour(red, green, blue);
979 return Create(bitmap, transparentColour);
980 }
981 }
982 return FALSE;
2bda0e17
KB
983}
984
985// Create a mask from a bitmap and a colour indicating
986// the transparent area
987bool wxMask::Create(const wxBitmap& bitmap, const wxColour& colour)
988{
4b7f2165
VZ
989 wxCHECK_MSG( bitmap.Ok(), FALSE, _T("invalid bitmap in wxMask::Create") );
990
2bda0e17 991 if ( m_maskBitmap )
1d792928 992 {
6d167489
VZ
993 ::DeleteObject((HBITMAP) m_maskBitmap);
994 m_maskBitmap = 0;
995 }
4b7f2165
VZ
996
997 int width = bitmap.GetWidth(),
998 height = bitmap.GetHeight();
999
1000 // scan the bitmap for the transparent colour and set the corresponding
1001 // pixels in the mask to BLACK and the rest to WHITE
1002 COLORREF maskColour = wxColourToRGB(colour);
1003 m_maskBitmap = (WXHBITMAP)::CreateBitmap(width, height, 1, 1, 0);
1004
1005 HDC srcDC = ::CreateCompatibleDC(NULL);
1006 HDC destDC = ::CreateCompatibleDC(NULL);
1007 if ( !srcDC || !destDC )
1008 {
f6bcfd97 1009 wxLogLastError(wxT("CreateCompatibleDC"));
4b7f2165
VZ
1010 }
1011
0bafad0c
VZ
1012 bool ok = TRUE;
1013
1014 HGDIOBJ hbmpSrcOld = ::SelectObject(srcDC, GetHbitmapOf(bitmap));
1015 if ( !hbmpSrcOld )
6d167489 1016 {
f6bcfd97 1017 wxLogLastError(wxT("SelectObject"));
0bafad0c
VZ
1018
1019 ok = FALSE;
4b7f2165 1020 }
0bafad0c
VZ
1021
1022 HGDIOBJ hbmpDstOld = ::SelectObject(destDC, (HBITMAP)m_maskBitmap);
1023 if ( !hbmpDstOld )
4b7f2165 1024 {
f6bcfd97 1025 wxLogLastError(wxT("SelectObject"));
0bafad0c
VZ
1026
1027 ok = FALSE;
1d792928 1028 }
2bda0e17 1029
4b7f2165
VZ
1030 // this is not very efficient, but I can't think of a better way of doing
1031 // it
0bafad0c 1032 for ( int w = 0; ok && (w < width); w++ )
2bda0e17 1033 {
0bafad0c 1034 for ( int h = 0; ok && (h < height); h++ )
c793fa87 1035 {
6d167489 1036 COLORREF col = GetPixel(srcDC, w, h);
4b7f2165
VZ
1037 if ( col == CLR_INVALID )
1038 {
f6bcfd97 1039 wxLogLastError(wxT("GetPixel"));
4b7f2165
VZ
1040
1041 // doesn't make sense to continue
0bafad0c 1042 ok = FALSE;
4b7f2165 1043
0bafad0c 1044 break;
4b7f2165
VZ
1045 }
1046
1047 if ( col == maskColour )
6d167489
VZ
1048 {
1049 ::SetPixel(destDC, w, h, RGB(0, 0, 0));
1050 }
1051 else
1052 {
1053 ::SetPixel(destDC, w, h, RGB(255, 255, 255));
1054 }
c793fa87 1055 }
2bda0e17 1056 }
4b7f2165 1057
0bafad0c 1058 ::SelectObject(srcDC, hbmpSrcOld);
6d167489 1059 ::DeleteDC(srcDC);
0bafad0c 1060 ::SelectObject(destDC, hbmpDstOld);
6d167489 1061 ::DeleteDC(destDC);
4b7f2165 1062
0bafad0c 1063 return ok;
6d167489
VZ
1064}
1065
1066// ----------------------------------------------------------------------------
1067// wxBitmapHandler
1068// ----------------------------------------------------------------------------
1d792928 1069
6d167489
VZ
1070bool wxBitmapHandler::Create(wxGDIImage *image,
1071 void *data,
1072 long flags,
1073 int width, int height, int depth)
1074{
1075 wxBitmap *bitmap = wxDynamicCast(image, wxBitmap);
1d792928 1076
8c1375b9 1077 return bitmap ? Create(bitmap, data, flags, width, height, depth) : FALSE;
2bda0e17
KB
1078}
1079
6d167489
VZ
1080bool wxBitmapHandler::Load(wxGDIImage *image,
1081 const wxString& name,
1082 long flags,
1083 int width, int height)
2bda0e17 1084{
6d167489 1085 wxBitmap *bitmap = wxDynamicCast(image, wxBitmap);
2bda0e17 1086
6d167489
VZ
1087 return bitmap ? LoadFile(bitmap, name, flags, width, height) : FALSE;
1088}
2bda0e17 1089
6d167489
VZ
1090bool wxBitmapHandler::Save(wxGDIImage *image,
1091 const wxString& name,
1092 int type)
2bda0e17 1093{
6d167489
VZ
1094 wxBitmap *bitmap = wxDynamicCast(image, wxBitmap);
1095
1096 return bitmap ? SaveFile(bitmap, name, type) : FALSE;
2bda0e17
KB
1097}
1098
6d167489
VZ
1099bool wxBitmapHandler::Create(wxBitmap *WXUNUSED(bitmap),
1100 void *WXUNUSED(data),
1101 long WXUNUSED(type),
1102 int WXUNUSED(width),
1103 int WXUNUSED(height),
1104 int WXUNUSED(depth))
2bda0e17 1105{
6d167489 1106 return FALSE;
2bda0e17
KB
1107}
1108
6d167489
VZ
1109bool wxBitmapHandler::LoadFile(wxBitmap *WXUNUSED(bitmap),
1110 const wxString& WXUNUSED(name),
1111 long WXUNUSED(type),
1112 int WXUNUSED(desiredWidth),
1113 int WXUNUSED(desiredHeight))
2bda0e17 1114{
6d167489 1115 return FALSE;
2bda0e17
KB
1116}
1117
6d167489
VZ
1118bool wxBitmapHandler::SaveFile(wxBitmap *WXUNUSED(bitmap),
1119 const wxString& WXUNUSED(name),
1120 int WXUNUSED(type),
1121 const wxPalette *WXUNUSED(palette))
2bda0e17 1122{
6d167489 1123 return FALSE;
7b46ecac
JS
1124}
1125
6d167489
VZ
1126// ----------------------------------------------------------------------------
1127// DIB functions
1128// ----------------------------------------------------------------------------
1129
1130bool wxCreateDIB(long xSize, long ySize, long bitsPerPixel,
1131 HPALETTE hPal, LPBITMAPINFO* lpDIBHeader)
7b46ecac
JS
1132{
1133 unsigned long i, headerSize;
1134 LPBITMAPINFO lpDIBheader = NULL;
1135 LPPALETTEENTRY lpPe = NULL;
1136
1137
1138 // Allocate space for a DIB header
1139 headerSize = (sizeof(BITMAPINFOHEADER) + (256 * sizeof(PALETTEENTRY)));
1140 lpDIBheader = (BITMAPINFO *) malloc(headerSize);
1141 lpPe = (PALETTEENTRY *)((BYTE*)lpDIBheader + sizeof(BITMAPINFOHEADER));
1142
1143 GetPaletteEntries(hPal, 0, 256, lpPe);
1144
7b46ecac
JS
1145 memset(lpDIBheader, 0x00, sizeof(BITMAPINFOHEADER));
1146
7b46ecac
JS
1147 // Fill in the static parts of the DIB header
1148 lpDIBheader->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1149 lpDIBheader->bmiHeader.biWidth = xSize;
1150 lpDIBheader->bmiHeader.biHeight = ySize;
1151 lpDIBheader->bmiHeader.biPlanes = 1;
1152
1153 // this value must be 1, 4, 8 or 24 so PixelDepth can only be
1154 lpDIBheader->bmiHeader.biBitCount = (WORD)(bitsPerPixel);
1155 lpDIBheader->bmiHeader.biCompression = BI_RGB;
6d167489 1156 lpDIBheader->bmiHeader.biSizeImage = xSize * abs(ySize) * bitsPerPixel >> 3;
7b46ecac
JS
1157 lpDIBheader->bmiHeader.biClrUsed = 256;
1158
1159
1160 // Initialize the DIB palette
1161 for (i = 0; i < 256; i++) {
1162 lpDIBheader->bmiColors[i].rgbReserved = lpPe[i].peFlags;
1163 lpDIBheader->bmiColors[i].rgbRed = lpPe[i].peRed;
1164 lpDIBheader->bmiColors[i].rgbGreen = lpPe[i].peGreen;
1165 lpDIBheader->bmiColors[i].rgbBlue = lpPe[i].peBlue;
1166 }
1167
1168 *lpDIBHeader = lpDIBheader;
1169
6d167489 1170 return TRUE;
7b46ecac
JS
1171}
1172
6d167489 1173void wxFreeDIB(LPBITMAPINFO lpDIBHeader)
7b46ecac 1174{
6d167489 1175 free(lpDIBHeader);
7b46ecac
JS
1176}
1177
4b7f2165
VZ
1178// ----------------------------------------------------------------------------
1179// other helper functions
1180// ----------------------------------------------------------------------------
1181
1182extern HBITMAP wxInvertMask(HBITMAP hbmpMask, int w, int h)
1183{
1184 wxCHECK_MSG( hbmpMask, 0, _T("invalid bitmap in wxInvertMask") );
1185
1186 // get width/height from the bitmap if not given
1187 if ( !w || !h )
1188 {
1189 BITMAP bm;
1190 ::GetObject(hbmpMask, sizeof(BITMAP), (LPVOID)&bm);
1191 w = bm.bmWidth;
1192 h = bm.bmHeight;
1193 }
1194
1195 HDC hdcSrc = ::CreateCompatibleDC(NULL);
1196 HDC hdcDst = ::CreateCompatibleDC(NULL);
1197 if ( !hdcSrc || !hdcDst )
1198 {
f6bcfd97 1199 wxLogLastError(wxT("CreateCompatibleDC"));
4b7f2165
VZ
1200 }
1201
1202 HBITMAP hbmpInvMask = ::CreateBitmap(w, h, 1, 1, 0);
1203 if ( !hbmpInvMask )
1204 {
f6bcfd97 1205 wxLogLastError(wxT("CreateBitmap"));
4b7f2165
VZ
1206 }
1207
1208 ::SelectObject(hdcSrc, hbmpMask);
1209 ::SelectObject(hdcDst, hbmpInvMask);
1210 if ( !::BitBlt(hdcDst, 0, 0, w, h,
1211 hdcSrc, 0, 0,
1212 NOTSRCCOPY) )
1213 {
f6bcfd97 1214 wxLogLastError(wxT("BitBlt"));
4b7f2165 1215 }
7b46ecac 1216
4b7f2165
VZ
1217 ::DeleteDC(hdcSrc);
1218 ::DeleteDC(hdcDst);
1219
1220 return hbmpInvMask;
1221}