7f7aa14f75bd0a673e8aa3d46d5a09878d0d3039
[wxWidgets.git] / src / msw / bitmap.cpp
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
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "bitmap.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
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"
41 #endif
42
43 #include "wx/msw/private.h"
44 #include "wx/log.h"
45
46 #include "wx/msw/dib.h"
47 #include "wx/image.h"
48
49 // ----------------------------------------------------------------------------
50 // macros
51 // ----------------------------------------------------------------------------
52
53 #if !USE_SHARED_LIBRARIES
54 IMPLEMENT_DYNAMIC_CLASS(wxBitmap, wxGDIObject)
55 IMPLEMENT_DYNAMIC_CLASS(wxMask, wxObject)
56
57 IMPLEMENT_DYNAMIC_CLASS(wxBitmapHandler, wxObject)
58 #endif
59
60 // ============================================================================
61 // implementation
62 // ============================================================================
63
64 // ----------------------------------------------------------------------------
65 // wxBitmapRefData
66 // ----------------------------------------------------------------------------
67
68 wxBitmapRefData::wxBitmapRefData()
69 {
70 m_quality = 0;
71 m_selectedInto = NULL;
72 m_numColors = 0;
73 m_bitmapMask = NULL;
74 m_hBitmap = (WXHBITMAP) NULL;
75 }
76
77 void wxBitmapRefData::Free()
78 {
79 wxASSERT_MSG( !m_selectedInto,
80 wxT("deleting bitmap still selected into wxMemoryDC") );
81
82 if ( m_hBitmap)
83 {
84 if ( !::DeleteObject((HBITMAP)m_hBitmap) )
85 {
86 wxLogLastError("DeleteObject(hbitmap)");
87 }
88 }
89
90 delete m_bitmapMask;
91 m_bitmapMask = NULL;
92 }
93
94 // ----------------------------------------------------------------------------
95 // wxBitmap creation
96 // ----------------------------------------------------------------------------
97
98 // this function should be called from all wxBitmap ctors
99 void wxBitmap::Init()
100 {
101 // m_refData = NULL; done in the base class ctor
102
103 if ( wxTheBitmapList )
104 wxTheBitmapList->AddBitmap(this);
105 }
106
107 #ifdef __WIN32__
108
109 bool wxBitmap::CopyFromIconOrCursor(const wxGDIImage& icon)
110 {
111 // it may be either HICON or HCURSOR
112 HICON hicon = (HICON)icon.GetHandle();
113
114 ICONINFO iconInfo;
115 if ( !::GetIconInfo(hicon, &iconInfo) )
116 {
117 wxLogLastError("GetIconInfo");
118
119 return FALSE;
120 }
121
122 wxBitmapRefData *refData = new wxBitmapRefData;
123 m_refData = refData;
124
125 int w = icon.GetWidth(),
126 h = icon.GetHeight();
127
128 refData->m_width = w;
129 refData->m_height = h;
130 refData->m_depth = wxDisplayDepth();
131
132 refData->m_hBitmap = (WXHBITMAP)iconInfo.hbmColor;
133
134 // the mask returned by GetIconInfo() is inversed compared to the usual
135 // wxWin convention
136 HBITMAP hbmpMask = ::CreateBitmap(w, h, 1, 1, 0);
137
138 // the icons mask is opposite to the usual wxWin convention
139 HDC dcSrc = ::CreateCompatibleDC(NULL);
140 HDC dcDst = ::CreateCompatibleDC(NULL);
141 (void)SelectObject(dcSrc, iconInfo.hbmMask);
142 (void)SelectObject(dcDst, hbmpMask);
143
144 HBRUSH brush = ::CreateSolidBrush(RGB(255, 255, 255));
145 RECT rect = { 0, 0, w, h };
146 FillRect(dcDst, &rect, brush);
147
148 BitBlt(dcDst, 0, 0, w, h, dcSrc, 0, 0, SRCINVERT);
149
150 SelectObject(dcDst, NULL);
151 SelectObject(dcSrc, NULL);
152 DeleteDC(dcDst);
153 DeleteDC(dcSrc);
154
155 refData->m_bitmapMask = new wxMask((WXHBITMAP)hbmpMask);
156
157 #if WXWIN_COMPATIBILITY_2
158 refData->m_ok = TRUE;
159 #endif // WXWIN_COMPATIBILITY_2
160
161 return TRUE;
162 }
163
164 #endif // Win32
165
166 bool wxBitmap::CopyFromCursor(const wxCursor& cursor)
167 {
168 UnRef();
169
170 if ( !cursor.Ok() )
171 return FALSE;
172
173 #ifdef __WIN16__
174 wxFAIL_MSG( _T("don't know how to convert cursor to bitmap") );
175
176 return FALSE;
177 #endif // Win16
178
179 return CopyFromIconOrCursor(cursor);
180 }
181
182 bool wxBitmap::CopyFromIcon(const wxIcon& icon)
183 {
184 UnRef();
185
186 if ( !icon.Ok() )
187 return FALSE;
188
189 // GetIconInfo() doesn't exist under Win16 and I don't know any other way
190 // to create a bitmap from icon there - but using this way we won't have
191 // the mask (FIXME)
192 #ifdef __WIN16__
193 int width = icon.GetWidth(),
194 height = icon.GetHeight();
195
196 // copy the icon to the bitmap
197 ScreenHDC hdcScreen;
198 HDC hdc = ::CreateCompatibleDC(hdcScreen);
199 HBITMAP hbitmap = ::CreateCompatibleBitmap(hdcScreen, width, height);
200 HBITMAP hbmpOld = (HBITMAP)::SelectObject(hdc, hbitmap);
201
202 ::DrawIcon(hdc, 0, 0, GetHiconOf(icon));
203
204 ::SelectObject(hdc, hbmpOld);
205 ::DeleteDC(hdc);
206
207 wxBitmapRefData *refData = new wxBitmapRefData;
208 m_refData = refData;
209
210 refData->m_width = width;
211 refData->m_height = height;
212 refData->m_depth = wxDisplayDepth();
213
214 refData->m_hBitmap = (WXHBITMAP)hbitmap;
215
216 #if WXWIN_COMPATIBILITY_2
217 refData->m_ok = TRUE;
218 #endif // WXWIN_COMPATIBILITY_2
219
220 return TRUE;
221 #else // Win32
222 return CopyFromIconOrCursor(icon);
223 #endif // Win16/Win32
224 }
225
226 wxBitmap::~wxBitmap()
227 {
228 if (wxTheBitmapList)
229 wxTheBitmapList->DeleteObject(this);
230 }
231
232 wxBitmap::wxBitmap(const char bits[], int the_width, int the_height, int no_bits)
233 {
234 Init();
235
236 wxBitmapRefData *refData = new wxBitmapRefData;
237 m_refData = refData;
238
239 refData->m_width = the_width;
240 refData->m_height = the_height;
241 refData->m_depth = no_bits;
242 refData->m_numColors = 0;
243 refData->m_selectedInto = NULL;
244
245 HBITMAP hbmp = ::CreateBitmap(the_width, the_height, 1, no_bits, bits);
246 if ( !hbmp )
247 {
248 wxLogLastError("CreateBitmap");
249 }
250
251 SetHBITMAP((WXHBITMAP)hbmp);
252 }
253
254 // Create from XPM data
255 wxBitmap::wxBitmap(char **data, wxControl *WXUNUSED(anItem))
256 {
257 Init();
258
259 (void)Create((void *)data, wxBITMAP_TYPE_XPM_DATA, 0, 0, 0);
260 }
261
262 wxBitmap::wxBitmap(int w, int h, int d)
263 {
264 Init();
265
266 (void)Create(w, h, d);
267 }
268
269 wxBitmap::wxBitmap(void *data, long type, int width, int height, int depth)
270 {
271 Init();
272
273 (void)Create(data, type, width, height, depth);
274 }
275
276 wxBitmap::wxBitmap(const wxString& filename, long type)
277 {
278 Init();
279
280 LoadFile(filename, (int)type);
281 }
282
283 bool wxBitmap::Create(int w, int h, int d)
284 {
285 UnRef();
286
287 m_refData = new wxBitmapRefData;
288
289 GetBitmapData()->m_width = w;
290 GetBitmapData()->m_height = h;
291 GetBitmapData()->m_depth = d;
292
293 HBITMAP hbmp;
294
295 if ( d > 0 )
296 {
297 hbmp = ::CreateBitmap(w, h, 1, d, NULL);
298 if ( !hbmp )
299 {
300 wxLogLastError("CreateBitmap");
301 }
302 }
303 else
304 {
305 ScreenHDC dc;
306 hbmp = ::CreateCompatibleBitmap(dc, w, h);
307 if ( !hbmp )
308 {
309 wxLogLastError("CreateCompatibleBitmap");
310 }
311
312 GetBitmapData()->m_depth = wxDisplayDepth();
313 }
314
315 SetHBITMAP((WXHBITMAP)hbmp);
316
317 #if WXWIN_COMPATIBILITY_2
318 GetBitmapData()->m_ok = hbmp != 0;
319 #endif // WXWIN_COMPATIBILITY_2
320
321 return Ok();
322 }
323
324 bool wxBitmap::LoadFile(const wxString& filename, long type)
325 {
326 UnRef();
327
328 wxBitmapHandler *handler = wxDynamicCast(FindHandler(type), wxBitmapHandler);
329
330 if ( handler )
331 {
332 m_refData = new wxBitmapRefData;
333
334 return handler->LoadFile(this, filename, type, -1, -1);
335 }
336 else
337 {
338 wxImage image;
339 if ( !image.LoadFile( filename, type ) || !image.Ok() )
340 return FALSE;
341
342 *this = image.ConvertToBitmap();
343
344 return TRUE;
345 }
346 }
347
348 bool wxBitmap::Create(void *data, long type, int width, int height, int depth)
349 {
350 UnRef();
351
352 wxBitmapHandler *handler = wxDynamicCast(FindHandler(type), wxBitmapHandler);
353
354 if ( !handler )
355 {
356 wxLogDebug(wxT("Failed to create bitmap: no bitmap handler for "
357 "type %d defined."), type);
358
359 return FALSE;
360 }
361
362 m_refData = new wxBitmapRefData;
363
364 return handler->Create(this, data, type, width, height, depth);
365 }
366
367 bool wxBitmap::SaveFile(const wxString& filename, int type, const wxPalette *palette)
368 {
369 wxBitmapHandler *handler = wxDynamicCast(FindHandler(type), wxBitmapHandler);
370
371 if ( handler )
372 {
373 return handler->SaveFile(this, filename, type, palette);
374 }
375 else
376 {
377 // FIXME what about palette? shouldn't we use it?
378 wxImage image( *this );
379 if (!image.Ok())
380 return FALSE;
381
382 return image.SaveFile( filename, type );
383 }
384 }
385
386 // ----------------------------------------------------------------------------
387 // wxBitmap accessors
388 // ----------------------------------------------------------------------------
389
390 void wxBitmap::SetQuality(int q)
391 {
392 EnsureHasData();
393
394 GetBitmapData()->m_quality = q;
395 }
396
397 #if WXWIN_COMPATIBILITY_2
398 void wxBitmap::SetOk(bool isOk)
399 {
400 EnsureHasData();
401
402 GetBitmapData()->m_ok = isOk;
403 }
404 #endif // WXWIN_COMPATIBILITY_2
405
406 void wxBitmap::SetPalette(const wxPalette& palette)
407 {
408 EnsureHasData();
409
410 GetBitmapData()->m_bitmapPalette = palette;
411 }
412
413 void wxBitmap::SetMask(wxMask *mask)
414 {
415 EnsureHasData();
416
417 GetBitmapData()->m_bitmapMask = mask;
418 }
419
420 // Creates a bitmap that matches the device context, from
421 // an arbitray bitmap. At present, the original bitmap must have an
422 // associated palette. TODO: use a default palette if no palette exists.
423 // Contributed by Frederic Villeneuve <frederic.villeneuve@natinst.com>
424 wxBitmap wxBitmap::GetBitmapForDC(wxDC& dc) const
425 {
426 wxMemoryDC memDC;
427 wxBitmap tmpBitmap(this->GetWidth(), this->GetHeight(), dc.GetDepth());
428 HPALETTE hPal = (HPALETTE) NULL;
429 LPBITMAPINFO lpDib;
430 void *lpBits = (void*) NULL;
431
432 if( GetPalette() && GetPalette()->Ok() )
433 {
434 tmpBitmap.SetPalette(*GetPalette());
435 memDC.SelectObject(tmpBitmap);
436 memDC.SetPalette(*GetPalette());
437 hPal = (HPALETTE)GetPalette()->GetHPALETTE();
438 }
439 else
440 {
441 hPal = (HPALETTE) ::GetStockObject(DEFAULT_PALETTE);
442 wxPalette palette;
443 palette.SetHPALETTE( (WXHPALETTE)hPal );
444 tmpBitmap.SetPalette( palette );
445 memDC.SelectObject(tmpBitmap);
446 memDC.SetPalette( palette );
447 }
448
449 // set the height negative because in a DIB the order of the lines is
450 // reversed
451 if ( !wxCreateDIB(GetWidth(), -GetHeight(), GetDepth(), hPal, &lpDib) )
452 {
453 return wxNullBitmap;
454 }
455
456 lpBits = malloc(lpDib->bmiHeader.biSizeImage);
457
458 ::GetBitmapBits(GetHbitmap(), lpDib->bmiHeader.biSizeImage, lpBits);
459
460 ::SetDIBitsToDevice(GetHdcOf(memDC), 0, 0,
461 GetWidth(), GetHeight(),
462 0, 0, 0, GetHeight(),
463 lpBits, lpDib, DIB_RGB_COLORS);
464
465 free(lpBits);
466
467 wxFreeDIB(lpDib);
468
469 return tmpBitmap;
470 }
471
472 // ----------------------------------------------------------------------------
473 // wxMask
474 // ----------------------------------------------------------------------------
475
476 wxMask::wxMask()
477 {
478 m_maskBitmap = 0;
479 }
480
481 // Construct a mask from a bitmap and a colour indicating
482 // the transparent area
483 wxMask::wxMask(const wxBitmap& bitmap, const wxColour& colour)
484 {
485 m_maskBitmap = 0;
486 Create(bitmap, colour);
487 }
488
489 // Construct a mask from a bitmap and a palette index indicating
490 // the transparent area
491 wxMask::wxMask(const wxBitmap& bitmap, int paletteIndex)
492 {
493 m_maskBitmap = 0;
494 Create(bitmap, paletteIndex);
495 }
496
497 // Construct a mask from a mono bitmap (copies the bitmap).
498 wxMask::wxMask(const wxBitmap& bitmap)
499 {
500 m_maskBitmap = 0;
501 Create(bitmap);
502 }
503
504 wxMask::~wxMask()
505 {
506 if ( m_maskBitmap )
507 ::DeleteObject((HBITMAP) m_maskBitmap);
508 }
509
510 // Create a mask from a mono bitmap (copies the bitmap).
511 bool wxMask::Create(const wxBitmap& bitmap)
512 {
513 if ( m_maskBitmap )
514 {
515 ::DeleteObject((HBITMAP) m_maskBitmap);
516 m_maskBitmap = 0;
517 }
518 if (!bitmap.Ok() || bitmap.GetDepth() != 1)
519 {
520 return FALSE;
521 }
522 m_maskBitmap = (WXHBITMAP) CreateBitmap(
523 bitmap.GetWidth(),
524 bitmap.GetHeight(),
525 1, 1, 0
526 );
527 HDC srcDC = CreateCompatibleDC(0);
528 SelectObject(srcDC, (HBITMAP) bitmap.GetHBITMAP());
529 HDC destDC = CreateCompatibleDC(0);
530 SelectObject(destDC, (HBITMAP) m_maskBitmap);
531 BitBlt(destDC, 0, 0, bitmap.GetWidth(), bitmap.GetHeight(), srcDC, 0, 0, SRCCOPY);
532 SelectObject(srcDC, 0);
533 DeleteDC(srcDC);
534 SelectObject(destDC, 0);
535 DeleteDC(destDC);
536 return TRUE;
537 }
538
539 // Create a mask from a bitmap and a palette index indicating
540 // the transparent area
541 bool wxMask::Create(const wxBitmap& bitmap, int paletteIndex)
542 {
543 if ( m_maskBitmap )
544 {
545 ::DeleteObject((HBITMAP) m_maskBitmap);
546 m_maskBitmap = 0;
547 }
548 if (bitmap.Ok() && bitmap.GetPalette()->Ok())
549 {
550 unsigned char red, green, blue;
551 if (bitmap.GetPalette()->GetRGB(paletteIndex, &red, &green, &blue))
552 {
553 wxColour transparentColour(red, green, blue);
554 return Create(bitmap, transparentColour);
555 }
556 }
557 return FALSE;
558 }
559
560 // Create a mask from a bitmap and a colour indicating
561 // the transparent area
562 bool wxMask::Create(const wxBitmap& bitmap, const wxColour& colour)
563 {
564 if ( m_maskBitmap )
565 {
566 ::DeleteObject((HBITMAP) m_maskBitmap);
567 m_maskBitmap = 0;
568 }
569 if (!bitmap.Ok())
570 {
571 return FALSE;
572 }
573
574 // scan the bitmap for the transparent colour and set
575 // the corresponding pixels in the mask to BLACK and
576 // the rest to WHITE
577 COLORREF maskColour = RGB(colour.Red(), colour.Green(), colour.Blue());
578 m_maskBitmap = (WXHBITMAP) ::CreateBitmap(
579 bitmap.GetWidth(),
580 bitmap.GetHeight(),
581 1, 1, 0
582 );
583 HDC srcDC = ::CreateCompatibleDC(0);
584 ::SelectObject(srcDC, (HBITMAP) bitmap.GetHBITMAP());
585 HDC destDC = ::CreateCompatibleDC(0);
586 ::SelectObject(destDC, (HBITMAP) m_maskBitmap);
587
588 // this is not very efficient, but I can't think
589 // of a better way of doing it
590 for (int w = 0; w < bitmap.GetWidth(); w++)
591 {
592 for (int h = 0; h < bitmap.GetHeight(); h++)
593 {
594 COLORREF col = GetPixel(srcDC, w, h);
595 if (col == maskColour)
596 {
597 ::SetPixel(destDC, w, h, RGB(0, 0, 0));
598 }
599 else
600 {
601 ::SetPixel(destDC, w, h, RGB(255, 255, 255));
602 }
603 }
604 }
605 ::SelectObject(srcDC, 0);
606 ::DeleteDC(srcDC);
607 ::SelectObject(destDC, 0);
608 ::DeleteDC(destDC);
609 return TRUE;
610 }
611
612 // ----------------------------------------------------------------------------
613 // wxBitmapHandler
614 // ----------------------------------------------------------------------------
615
616 bool wxBitmapHandler::Create(wxGDIImage *image,
617 void *data,
618 long flags,
619 int width, int height, int depth)
620 {
621 wxBitmap *bitmap = wxDynamicCast(image, wxBitmap);
622
623 return bitmap ? Create(bitmap, data, width, height, depth) : FALSE;
624 }
625
626 bool wxBitmapHandler::Load(wxGDIImage *image,
627 const wxString& name,
628 long flags,
629 int width, int height)
630 {
631 wxBitmap *bitmap = wxDynamicCast(image, wxBitmap);
632
633 return bitmap ? LoadFile(bitmap, name, flags, width, height) : FALSE;
634 }
635
636 bool wxBitmapHandler::Save(wxGDIImage *image,
637 const wxString& name,
638 int type)
639 {
640 wxBitmap *bitmap = wxDynamicCast(image, wxBitmap);
641
642 return bitmap ? SaveFile(bitmap, name, type) : FALSE;
643 }
644
645 bool wxBitmapHandler::Create(wxBitmap *WXUNUSED(bitmap),
646 void *WXUNUSED(data),
647 long WXUNUSED(type),
648 int WXUNUSED(width),
649 int WXUNUSED(height),
650 int WXUNUSED(depth))
651 {
652 return FALSE;
653 }
654
655 bool wxBitmapHandler::LoadFile(wxBitmap *WXUNUSED(bitmap),
656 const wxString& WXUNUSED(name),
657 long WXUNUSED(type),
658 int WXUNUSED(desiredWidth),
659 int WXUNUSED(desiredHeight))
660 {
661 return FALSE;
662 }
663
664 bool wxBitmapHandler::SaveFile(wxBitmap *WXUNUSED(bitmap),
665 const wxString& WXUNUSED(name),
666 int WXUNUSED(type),
667 const wxPalette *WXUNUSED(palette))
668 {
669 return FALSE;
670 }
671
672 // ----------------------------------------------------------------------------
673 // DIB functions
674 // ----------------------------------------------------------------------------
675
676 bool wxCreateDIB(long xSize, long ySize, long bitsPerPixel,
677 HPALETTE hPal, LPBITMAPINFO* lpDIBHeader)
678 {
679 unsigned long i, headerSize;
680 LPBITMAPINFO lpDIBheader = NULL;
681 LPPALETTEENTRY lpPe = NULL;
682
683
684 // Allocate space for a DIB header
685 headerSize = (sizeof(BITMAPINFOHEADER) + (256 * sizeof(PALETTEENTRY)));
686 lpDIBheader = (BITMAPINFO *) malloc(headerSize);
687 lpPe = (PALETTEENTRY *)((BYTE*)lpDIBheader + sizeof(BITMAPINFOHEADER));
688
689 GetPaletteEntries(hPal, 0, 256, lpPe);
690
691 memset(lpDIBheader, 0x00, sizeof(BITMAPINFOHEADER));
692
693 // Fill in the static parts of the DIB header
694 lpDIBheader->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
695 lpDIBheader->bmiHeader.biWidth = xSize;
696 lpDIBheader->bmiHeader.biHeight = ySize;
697 lpDIBheader->bmiHeader.biPlanes = 1;
698
699 // this value must be 1, 4, 8 or 24 so PixelDepth can only be
700 lpDIBheader->bmiHeader.biBitCount = (WORD)(bitsPerPixel);
701 lpDIBheader->bmiHeader.biCompression = BI_RGB;
702 lpDIBheader->bmiHeader.biSizeImage = xSize * abs(ySize) * bitsPerPixel >> 3;
703 lpDIBheader->bmiHeader.biClrUsed = 256;
704
705
706 // Initialize the DIB palette
707 for (i = 0; i < 256; i++) {
708 lpDIBheader->bmiColors[i].rgbReserved = lpPe[i].peFlags;
709 lpDIBheader->bmiColors[i].rgbRed = lpPe[i].peRed;
710 lpDIBheader->bmiColors[i].rgbGreen = lpPe[i].peGreen;
711 lpDIBheader->bmiColors[i].rgbBlue = lpPe[i].peBlue;
712 }
713
714 *lpDIBHeader = lpDIBheader;
715
716 return TRUE;
717 }
718
719 void wxFreeDIB(LPBITMAPINFO lpDIBHeader)
720 {
721 free(lpDIBHeader);
722 }
723
724