]> git.saurik.com Git - wxWidgets.git/blob - src/msw/dc.cpp
wxCocoa: Better icon support
[wxWidgets.git] / src / msw / dc.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dc.cpp
3 // Purpose: wxDC class
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "dc.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 "wx/window.h"
33 #include "wx/dc.h"
34 #include "wx/utils.h"
35 #include "wx/dialog.h"
36 #include "wx/app.h"
37 #include "wx/bitmap.h"
38 #include "wx/dcmemory.h"
39 #include "wx/log.h"
40 #include "wx/icon.h"
41 #endif
42
43 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
44
45 #include "wx/sysopt.h"
46 #include "wx/dcprint.h"
47 #include "wx/module.h"
48 #include "wx/dynload.h"
49
50 #ifdef wxHAVE_RAW_BITMAP
51 #include "wx/rawbmp.h"
52 #endif
53
54 #include <string.h>
55 #include <math.h>
56
57 #if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__)
58 #include <commdlg.h>
59 #endif
60
61 #ifndef __WIN32__
62 #include <print.h>
63 #endif
64
65 #ifndef AC_SRC_ALPHA
66 #define AC_SRC_ALPHA 1
67 #endif
68
69 /* Quaternary raster codes */
70 #ifndef MAKEROP4
71 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
72 #endif
73
74 IMPLEMENT_ABSTRACT_CLASS(wxDC, wxDCBase)
75
76 // ---------------------------------------------------------------------------
77 // constants
78 // ---------------------------------------------------------------------------
79
80 static const int VIEWPORT_EXTENT = 1000;
81
82 static const int MM_POINTS = 9;
83 static const int MM_METRIC = 10;
84
85 // usually this is defined in math.h
86 #ifndef M_PI
87 static const double M_PI = 3.14159265358979323846;
88 #endif // M_PI
89
90 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
91 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
92 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
93
94 // ----------------------------------------------------------------------------
95 // macros for logical <-> device coords conversion
96 // ----------------------------------------------------------------------------
97
98 /*
99 We currently let Windows do all the translations itself so these macros are
100 not really needed (any more) but keep them to enhance readability of the
101 code by allowing to see where are the logical and where are the device
102 coordinates used.
103 */
104
105 // logical to device
106 #define XLOG2DEV(x) (x)
107 #define YLOG2DEV(y) (y)
108
109 // device to logical
110 #define XDEV2LOG(x) (x)
111 #define YDEV2LOG(y) (y)
112
113 // ---------------------------------------------------------------------------
114 // private functions
115 // ---------------------------------------------------------------------------
116
117 // convert degrees to radians
118 static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
119
120 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
121 //
122 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
123 // to pass it to this function but as we already have it at the point
124 // of call anyhow we do
125 //
126 // return true if we could draw the bitmap in one way or the other, false
127 // otherwise
128 static bool AlphaBlt(HDC hdcDst,
129 int x, int y, int w, int h,
130 HDC hdcSrc,
131 const wxBitmap& bmpSrc);
132
133 #ifdef wxHAVE_RAW_BITMAP
134 // our (limited) AlphaBlend() replacement
135 static void
136 wxAlphaBlend(HDC hdcDst, int x, int y, int w, int h, const wxBitmap& bmp);
137 #endif
138
139 // ----------------------------------------------------------------------------
140 // private classes
141 // ----------------------------------------------------------------------------
142
143 // instead of duplicating the same code which sets and then restores text
144 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
145 // encapsulate this in a small helper class
146
147 // wxColourChanger: changes the text colours in the ctor if required and
148 // restores them in the dtor
149 class wxColourChanger
150 {
151 public:
152 wxColourChanger(wxDC& dc);
153 ~wxColourChanger();
154
155 private:
156 wxDC& m_dc;
157
158 COLORREF m_colFgOld, m_colBgOld;
159
160 bool m_changed;
161
162 DECLARE_NO_COPY_CLASS(wxColourChanger)
163 };
164
165 // this class saves the old stretch blit mode during its life time
166 class StretchBltModeChanger
167 {
168 public:
169 StretchBltModeChanger(HDC hdc, int mode)
170 : m_hdc(hdc)
171 {
172 #ifndef __WXWINCE__
173 m_modeOld = ::SetStretchBltMode(m_hdc, mode);
174 if ( !m_modeOld )
175 wxLogLastError(_T("SetStretchBltMode"));
176 #endif
177 }
178
179 ~StretchBltModeChanger()
180 {
181 #ifndef __WXWINCE__
182 if ( !::SetStretchBltMode(m_hdc, m_modeOld) )
183 wxLogLastError(_T("SetStretchBltMode"));
184 #endif
185 }
186
187 private:
188 const HDC m_hdc;
189
190 int m_modeOld;
191
192 DECLARE_NO_COPY_CLASS(StretchBltModeChanger)
193 };
194
195 // ===========================================================================
196 // implementation
197 // ===========================================================================
198
199 // ----------------------------------------------------------------------------
200 // wxColourChanger
201 // ----------------------------------------------------------------------------
202
203 wxColourChanger::wxColourChanger(wxDC& dc) : m_dc(dc)
204 {
205 const wxBrush& brush = dc.GetBrush();
206 if ( brush.Ok() && brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE )
207 {
208 HDC hdc = GetHdcOf(dc);
209 m_colFgOld = ::GetTextColor(hdc);
210 m_colBgOld = ::GetBkColor(hdc);
211
212 // note that Windows convention is opposite to wxWindows one, this is
213 // why text colour becomes the background one and vice versa
214 const wxColour& colFg = dc.GetTextForeground();
215 if ( colFg.Ok() )
216 {
217 ::SetBkColor(hdc, colFg.GetPixel());
218 }
219
220 const wxColour& colBg = dc.GetTextBackground();
221 if ( colBg.Ok() )
222 {
223 ::SetTextColor(hdc, colBg.GetPixel());
224 }
225
226 SetBkMode(hdc,
227 dc.GetBackgroundMode() == wxTRANSPARENT ? TRANSPARENT
228 : OPAQUE);
229
230 // flag which telsl us to undo changes in the dtor
231 m_changed = TRUE;
232 }
233 else
234 {
235 // nothing done, nothing to undo
236 m_changed = FALSE;
237 }
238 }
239
240 wxColourChanger::~wxColourChanger()
241 {
242 if ( m_changed )
243 {
244 // restore the colours we changed
245 HDC hdc = GetHdcOf(m_dc);
246
247 ::SetBkMode(hdc, TRANSPARENT);
248 ::SetTextColor(hdc, m_colFgOld);
249 ::SetBkColor(hdc, m_colBgOld);
250 }
251 }
252
253 // ---------------------------------------------------------------------------
254 // wxDC
255 // ---------------------------------------------------------------------------
256
257 // Default constructor
258 wxDC::wxDC()
259 {
260 m_canvas = NULL;
261
262 m_oldBitmap = 0;
263 m_oldPen = 0;
264 m_oldBrush = 0;
265 m_oldFont = 0;
266 #if wxUSE_PALETTE
267 m_oldPalette = 0;
268 #endif // wxUSE_PALETTE
269
270 m_bOwnsDC = FALSE;
271 m_hDC = 0;
272 }
273
274 wxDC::~wxDC()
275 {
276 if ( m_hDC != 0 )
277 {
278 SelectOldObjects(m_hDC);
279
280 // if we own the HDC, we delete it, otherwise we just release it
281
282 if ( m_bOwnsDC )
283 {
284 ::DeleteDC(GetHdc());
285 }
286 else // we don't own our HDC
287 {
288 if (m_canvas)
289 {
290 ::ReleaseDC(GetHwndOf(m_canvas), GetHdc());
291 }
292 else
293 {
294 // Must have been a wxScreenDC
295 ::ReleaseDC((HWND) NULL, GetHdc());
296 }
297 }
298 }
299 }
300
301 // This will select current objects out of the DC,
302 // which is what you have to do before deleting the
303 // DC.
304 void wxDC::SelectOldObjects(WXHDC dc)
305 {
306 if (dc)
307 {
308 if (m_oldBitmap)
309 {
310 ::SelectObject((HDC) dc, (HBITMAP) m_oldBitmap);
311 #ifdef __WXDEBUG__
312 if (m_selectedBitmap.Ok())
313 {
314 m_selectedBitmap.SetSelectedInto(NULL);
315 }
316 #endif
317 }
318 m_oldBitmap = 0;
319 if (m_oldPen)
320 {
321 ::SelectObject((HDC) dc, (HPEN) m_oldPen);
322 }
323 m_oldPen = 0;
324 if (m_oldBrush)
325 {
326 ::SelectObject((HDC) dc, (HBRUSH) m_oldBrush);
327 }
328 m_oldBrush = 0;
329 if (m_oldFont)
330 {
331 ::SelectObject((HDC) dc, (HFONT) m_oldFont);
332 }
333 m_oldFont = 0;
334
335 #if wxUSE_PALETTE
336 if (m_oldPalette)
337 {
338 ::SelectPalette((HDC) dc, (HPALETTE) m_oldPalette, FALSE);
339 }
340 m_oldPalette = 0;
341 #endif // wxUSE_PALETTE
342 }
343
344 m_brush = wxNullBrush;
345 m_pen = wxNullPen;
346 #if wxUSE_PALETTE
347 m_palette = wxNullPalette;
348 #endif // wxUSE_PALETTE
349 m_font = wxNullFont;
350 m_backgroundBrush = wxNullBrush;
351 m_selectedBitmap = wxNullBitmap;
352 }
353
354 // ---------------------------------------------------------------------------
355 // clipping
356 // ---------------------------------------------------------------------------
357
358 void wxDC::UpdateClipBox()
359 {
360 #ifdef __WXMICROWIN__
361 if (!GetHDC()) return;
362 #endif
363
364 RECT rect;
365 ::GetClipBox(GetHdc(), &rect);
366
367 m_clipX1 = (wxCoord) XDEV2LOG(rect.left);
368 m_clipY1 = (wxCoord) YDEV2LOG(rect.top);
369 m_clipX2 = (wxCoord) XDEV2LOG(rect.right);
370 m_clipY2 = (wxCoord) YDEV2LOG(rect.bottom);
371 }
372
373 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
374 void wxDC::SetClippingHrgn(WXHRGN hrgn)
375 {
376 wxCHECK_RET( hrgn, wxT("invalid clipping region") );
377
378 #ifdef __WXMICROWIN__
379 if (!GetHdc()) return;
380 #endif // __WXMICROWIN__
381
382 // note that we combine the new clipping region with the existing one: this
383 // is compatible with what the other ports do and is the documented
384 // behaviour now (starting with 2.3.3)
385 #if defined(__WIN16__) || defined(__WXWINCE__)
386 RECT rectClip;
387 if ( !::GetClipBox(GetHdc(), &rectClip) )
388 return;
389
390 HRGN hrgnDest = ::CreateRectRgn(0, 0, 0, 0);
391 HRGN hrgnClipOld = ::CreateRectRgn(rectClip.left, rectClip.top,
392 rectClip.right, rectClip.bottom);
393
394 if ( ::CombineRgn(hrgnDest, hrgnClipOld, (HRGN)hrgn, RGN_AND) != ERROR )
395 {
396 ::SelectClipRgn(GetHdc(), hrgnDest);
397 }
398
399 ::DeleteObject(hrgnClipOld);
400 ::DeleteObject(hrgnDest);
401 #else // Win32
402 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN)hrgn, RGN_AND) == ERROR )
403 {
404 wxLogLastError(_T("ExtSelectClipRgn"));
405
406 return;
407 }
408 #endif // Win16/32
409
410 m_clipping = TRUE;
411
412 UpdateClipBox();
413 }
414
415 void wxDC::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
416 {
417 // the region coords are always the device ones, so do the translation
418 // manually
419 //
420 // FIXME: possible +/-1 error here, to check!
421 HRGN hrgn = ::CreateRectRgn(LogicalToDeviceX(x),
422 LogicalToDeviceY(y),
423 LogicalToDeviceX(x + w),
424 LogicalToDeviceY(y + h));
425 if ( !hrgn )
426 {
427 wxLogLastError(_T("CreateRectRgn"));
428 }
429 else
430 {
431 SetClippingHrgn((WXHRGN)hrgn);
432
433 ::DeleteObject(hrgn);
434 }
435 }
436
437 void wxDC::DoSetClippingRegionAsRegion(const wxRegion& region)
438 {
439 SetClippingHrgn(region.GetHRGN());
440 }
441
442 void wxDC::DestroyClippingRegion()
443 {
444 #ifdef __WXMICROWIN__
445 if (!GetHDC()) return;
446 #endif
447
448 if (m_clipping && m_hDC)
449 {
450 // TODO: this should restore the previous clipping region,
451 // so that OnPaint processing works correctly, and the update
452 // clipping region doesn't get destroyed after the first
453 // DestroyClippingRegion.
454 HRGN rgn = CreateRectRgn(0, 0, 32000, 32000);
455 ::SelectClipRgn(GetHdc(), rgn);
456 ::DeleteObject(rgn);
457 }
458
459 m_clipping = FALSE;
460 }
461
462 // ---------------------------------------------------------------------------
463 // query capabilities
464 // ---------------------------------------------------------------------------
465
466 bool wxDC::CanDrawBitmap() const
467 {
468 return TRUE;
469 }
470
471 bool wxDC::CanGetTextExtent() const
472 {
473 #ifdef __WXMICROWIN__
474 // TODO Extend MicroWindows' GetDeviceCaps function
475 return TRUE;
476 #else
477 // What sort of display is it?
478 int technology = ::GetDeviceCaps(GetHdc(), TECHNOLOGY);
479
480 return (technology == DT_RASDISPLAY) || (technology == DT_RASPRINTER);
481 #endif
482 }
483
484 int wxDC::GetDepth() const
485 {
486 #ifdef __WXMICROWIN__
487 if (!GetHDC()) return 16;
488 #endif
489
490 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL);
491 }
492
493 // ---------------------------------------------------------------------------
494 // drawing
495 // ---------------------------------------------------------------------------
496
497 void wxDC::Clear()
498 {
499 #ifdef __WXMICROWIN__
500 if (!GetHDC()) return;
501 #endif
502
503 RECT rect;
504 if ( m_canvas )
505 {
506 GetClientRect((HWND) m_canvas->GetHWND(), &rect);
507 }
508 else
509 {
510 // No, I think we should simply ignore this if printing on e.g.
511 // a printer DC.
512 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
513 if (!m_selectedBitmap.Ok())
514 return;
515
516 rect.left = 0; rect.top = 0;
517 rect.right = m_selectedBitmap.GetWidth();
518 rect.bottom = m_selectedBitmap.GetHeight();
519 }
520
521 #ifndef __WXWINCE__
522 (void) ::SetMapMode(GetHdc(), MM_TEXT);
523 #endif
524
525 DWORD colour = ::GetBkColor(GetHdc());
526 HBRUSH brush = ::CreateSolidBrush(colour);
527 ::FillRect(GetHdc(), &rect, brush);
528 ::DeleteObject(brush);
529
530 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
531 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
532
533 #ifndef __WXWINCE__
534 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
535
536 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
537 ::SetWindowExtEx(GetHdc(), width, height, NULL);
538 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
539 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
540 #endif
541 }
542
543 bool wxDC::DoFloodFill(wxCoord x, wxCoord y, const wxColour& col, int style)
544 {
545 #ifdef __WXWINCE__
546 return FALSE;
547 #else
548
549 #ifdef __WXMICROWIN__
550 if (!GetHDC()) return FALSE;
551 #endif
552
553 bool success = (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
554 col.GetPixel(),
555 style == wxFLOOD_SURFACE ? FLOODFILLSURFACE
556 : FLOODFILLBORDER) ) ;
557 if (!success)
558 {
559 // quoting from the MSDN docs:
560 //
561 // Following are some of the reasons this function might fail:
562 //
563 // * The filling could not be completed.
564 // * The specified point has the boundary color specified by the
565 // crColor parameter (if FLOODFILLBORDER was requested).
566 // * The specified point does not have the color specified by
567 // crColor (if FLOODFILLSURFACE was requested)
568 // * The point is outside the clipping region that is, it is not
569 // visible on the device.
570 //
571 wxLogLastError(wxT("ExtFloodFill"));
572 }
573
574 CalcBoundingBox(x, y);
575
576 return success;
577 #endif
578 }
579
580 bool wxDC::DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const
581 {
582 #ifdef __WXMICROWIN__
583 if (!GetHDC()) return FALSE;
584 #endif
585
586 wxCHECK_MSG( col, FALSE, _T("NULL colour parameter in wxDC::GetPixel") );
587
588 // get the color of the pixel
589 COLORREF pixelcolor = ::GetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y));
590
591 wxRGBToColour(*col, pixelcolor);
592
593 return TRUE;
594 }
595
596 void wxDC::DoCrossHair(wxCoord x, wxCoord y)
597 {
598 #ifdef __WXMICROWIN__
599 if (!GetHDC()) return;
600 #endif
601
602 wxCoord x1 = x-VIEWPORT_EXTENT;
603 wxCoord y1 = y-VIEWPORT_EXTENT;
604 wxCoord x2 = x+VIEWPORT_EXTENT;
605 wxCoord y2 = y+VIEWPORT_EXTENT;
606
607 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y));
608 wxDrawLine(GetHdc(), XLOG2DEV(x), YLOG2DEV(y1), XLOG2DEV(x), YLOG2DEV(y2));
609
610 CalcBoundingBox(x1, y1);
611 CalcBoundingBox(x2, y2);
612 }
613
614 void wxDC::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
615 {
616 #ifdef __WXMICROWIN__
617 if (!GetHDC()) return;
618 #endif
619
620 wxDrawLine(GetHdc(), XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2));
621
622 CalcBoundingBox(x1, y1);
623 CalcBoundingBox(x2, y2);
624 }
625
626 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
627 // and ending at (x2, y2)
628 void wxDC::DoDrawArc(wxCoord x1, wxCoord y1,
629 wxCoord x2, wxCoord y2,
630 wxCoord xc, wxCoord yc)
631 {
632 #ifdef __WXWINCE__
633 // Slower emulation since WinCE doesn't support Pie and Arc
634 double r = sqrt( (x1-xc)*(x1-xc) + (y1-yc)*(y1-yc) );
635 double sa = acos((x1-xc)/r)/M_PI*180; // between 0 and 180
636 if( y1>yc ) sa = -sa; // below center
637 double ea = atan2(yc-y2, x2-xc)/M_PI*180;
638 DoDrawEllipticArcRot( xc-r, yc-r, 2*r, 2*r, sa, ea );
639 #else
640
641 #ifdef __WXMICROWIN__
642 if (!GetHDC()) return;
643 #endif
644
645 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
646
647 double dx = xc - x1;
648 double dy = yc - y1;
649 double radius = (double)sqrt(dx*dx+dy*dy);
650 wxCoord r = (wxCoord)radius;
651
652 // treat the special case of full circle separately
653 if ( x1 == x2 && y1 == y2 )
654 {
655 DrawEllipse(xc - r, yc - r, 2*r, 2*r);
656 return;
657 }
658
659 wxCoord xx1 = XLOG2DEV(x1);
660 wxCoord yy1 = YLOG2DEV(y1);
661 wxCoord xx2 = XLOG2DEV(x2);
662 wxCoord yy2 = YLOG2DEV(y2);
663 wxCoord xxc = XLOG2DEV(xc);
664 wxCoord yyc = YLOG2DEV(yc);
665 wxCoord ray = (wxCoord) sqrt(double((xxc-xx1)*(xxc-xx1)+(yyc-yy1)*(yyc-yy1)));
666
667 wxCoord xxx1 = (wxCoord) (xxc-ray);
668 wxCoord yyy1 = (wxCoord) (yyc-ray);
669 wxCoord xxx2 = (wxCoord) (xxc+ray);
670 wxCoord yyy2 = (wxCoord) (yyc+ray);
671
672 if ( m_brush.Ok() && m_brush.GetStyle() != wxTRANSPARENT )
673 {
674 // Have to add 1 to bottom-right corner of rectangle
675 // to make semi-circles look right (crooked line otherwise).
676 // Unfortunately this is not a reliable method, depends
677 // on the size of shape.
678 // TODO: figure out why this happens!
679 Pie(GetHdc(),xxx1,yyy1,xxx2+1,yyy2+1, xx1,yy1,xx2,yy2);
680 }
681 else
682 {
683 Arc(GetHdc(),xxx1,yyy1,xxx2,yyy2, xx1,yy1,xx2,yy2);
684 }
685
686 CalcBoundingBox(xc - r, yc - r);
687 CalcBoundingBox(xc + r, yc + r);
688 #endif
689 }
690
691 void wxDC::DoDrawCheckMark(wxCoord x1, wxCoord y1,
692 wxCoord width, wxCoord height)
693 {
694 #ifdef __WXMICROWIN__
695 if (!GetHDC()) return;
696 #endif
697
698 wxCoord x2 = x1 + width,
699 y2 = y1 + height;
700
701 #if defined(__WIN32__) && !defined(__SYMANTEC__) && !defined(__WXMICROWIN__)
702 RECT rect;
703 rect.left = x1;
704 rect.top = y1;
705 rect.right = x2;
706 rect.bottom = y2;
707
708 #ifdef __WXWINCE__
709 DrawFrameControl(GetHdc(), &rect, DFC_BUTTON, DFCS_BUTTONCHECK);
710 #else
711 DrawFrameControl(GetHdc(), &rect, DFC_MENU, DFCS_MENUCHECK);
712 #endif
713 #else // Win16
714 // In WIN16, draw a cross
715 HPEN blackPen = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
716 HPEN whiteBrush = (HPEN)::GetStockObject(WHITE_BRUSH);
717 HPEN hPenOld = (HPEN)::SelectObject(GetHdc(), blackPen);
718 HPEN hBrushOld = (HPEN)::SelectObject(GetHdc(), whiteBrush);
719 ::SetROP2(GetHdc(), R2_COPYPEN);
720 Rectangle(GetHdc(), x1, y1, x2, y2);
721 MoveToEx(GetHdc(), x1, y1, NULL);
722 LineTo(GetHdc(), x2, y2);
723 MoveToEx(GetHdc(), x2, y1, NULL);
724 LineTo(GetHdc(), x1, y2);
725 ::SelectObject(GetHdc(), hPenOld);
726 ::SelectObject(GetHdc(), hBrushOld);
727 ::DeleteObject(blackPen);
728 #endif // Win32/16
729
730 CalcBoundingBox(x1, y1);
731 CalcBoundingBox(x2, y2);
732 }
733
734 void wxDC::DoDrawPoint(wxCoord x, wxCoord y)
735 {
736 #ifdef __WXMICROWIN__
737 if (!GetHDC()) return;
738 #endif
739
740 COLORREF color = 0x00ffffff;
741 if (m_pen.Ok())
742 {
743 color = m_pen.GetColour().GetPixel();
744 }
745
746 SetPixel(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), color);
747
748 CalcBoundingBox(x, y);
749 }
750
751 void wxDC::DoDrawPolygon(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset,int fillStyle)
752 {
753 #ifdef __WXMICROWIN__
754 if (!GetHDC()) return;
755 #endif
756
757 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
758
759 // Do things less efficiently if we have offsets
760 if (xoffset != 0 || yoffset != 0)
761 {
762 POINT *cpoints = new POINT[n];
763 int i;
764 for (i = 0; i < n; i++)
765 {
766 cpoints[i].x = (int)(points[i].x + xoffset);
767 cpoints[i].y = (int)(points[i].y + yoffset);
768
769 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
770 }
771 #ifndef __WXWINCE__
772 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
773 #endif
774 (void)Polygon(GetHdc(), cpoints, n);
775 #ifndef __WXWINCE__
776 SetPolyFillMode(GetHdc(),prev);
777 #endif
778 delete[] cpoints;
779 }
780 else
781 {
782 int i;
783 for (i = 0; i < n; i++)
784 CalcBoundingBox(points[i].x, points[i].y);
785
786 #ifndef __WXWINCE__
787 int prev = SetPolyFillMode(GetHdc(),fillStyle==wxODDEVEN_RULE?ALTERNATE:WINDING);
788 #endif
789 (void)Polygon(GetHdc(), (POINT*) points, n);
790 #ifndef __WXWINCE__
791 SetPolyFillMode(GetHdc(),prev);
792 #endif
793 }
794 }
795
796 void wxDC::DoDrawLines(int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset)
797 {
798 #ifdef __WXMICROWIN__
799 if (!GetHDC()) return;
800 #endif
801
802 // Do things less efficiently if we have offsets
803 if (xoffset != 0 || yoffset != 0)
804 {
805 POINT *cpoints = new POINT[n];
806 int i;
807 for (i = 0; i < n; i++)
808 {
809 cpoints[i].x = (int)(points[i].x + xoffset);
810 cpoints[i].y = (int)(points[i].y + yoffset);
811
812 CalcBoundingBox(cpoints[i].x, cpoints[i].y);
813 }
814 (void)Polyline(GetHdc(), cpoints, n);
815 delete[] cpoints;
816 }
817 else
818 {
819 int i;
820 for (i = 0; i < n; i++)
821 CalcBoundingBox(points[i].x, points[i].y);
822
823 (void)Polyline(GetHdc(), (POINT*) points, n);
824 }
825 }
826
827 void wxDC::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
828 {
829 #ifdef __WXMICROWIN__
830 if (!GetHDC()) return;
831 #endif
832
833 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
834
835 wxCoord x2 = x + width;
836 wxCoord y2 = y + height;
837
838 if ((m_logicalFunction == wxCOPY) && (m_pen.GetStyle() == wxTRANSPARENT))
839 {
840 RECT rect;
841 rect.left = XLOG2DEV(x);
842 rect.top = YLOG2DEV(y);
843 rect.right = XLOG2DEV(x2);
844 rect.bottom = YLOG2DEV(y2);
845 (void)FillRect(GetHdc(), &rect, (HBRUSH)m_brush.GetResourceHandle() );
846 }
847 else
848 {
849 // Windows draws the filled rectangles without outline (i.e. drawn with a
850 // transparent pen) one pixel smaller in both directions and we want them
851 // to have the same size regardless of which pen is used - adjust
852
853 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
854 if ( m_pen.GetStyle() == wxTRANSPARENT )
855 {
856 x2++;
857 y2++;
858 }
859
860 (void)Rectangle(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
861 }
862
863
864 CalcBoundingBox(x, y);
865 CalcBoundingBox(x2, y2);
866 }
867
868 void wxDC::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
869 {
870 #ifdef __WXMICROWIN__
871 if (!GetHDC()) return;
872 #endif
873
874 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
875
876 // Now, a negative radius value is interpreted to mean
877 // 'the proportion of the smallest X or Y dimension'
878
879 if (radius < 0.0)
880 {
881 double smallest = (width < height) ? width : height;
882 radius = (- radius * smallest);
883 }
884
885 wxCoord x2 = (x+width);
886 wxCoord y2 = (y+height);
887
888 // Windows draws the filled rectangles without outline (i.e. drawn with a
889 // transparent pen) one pixel smaller in both directions and we want them
890 // to have the same size regardless of which pen is used - adjust
891 if ( m_pen.GetStyle() == wxTRANSPARENT )
892 {
893 x2++;
894 y2++;
895 }
896
897 (void)RoundRect(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2),
898 YLOG2DEV(y2), (int) (2*XLOG2DEV(radius)), (int)( 2*YLOG2DEV(radius)));
899
900 CalcBoundingBox(x, y);
901 CalcBoundingBox(x2, y2);
902 }
903
904 void wxDC::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
905 {
906 #ifdef __WXMICROWIN__
907 if (!GetHDC()) return;
908 #endif
909
910 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
911
912 wxCoord x2 = (x+width);
913 wxCoord y2 = (y+height);
914
915 (void)Ellipse(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2));
916
917 CalcBoundingBox(x, y);
918 CalcBoundingBox(x2, y2);
919 }
920
921 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
922 void wxDC::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
923 {
924 #ifdef __WXWINCE__
925 DoDrawEllipticArcRot( x, y, w, h, sa, ea );
926 #else
927
928 #ifdef __WXMICROWIN__
929 if (!GetHDC()) return;
930 #endif
931
932 wxColourChanger cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
933
934 wxCoord x2 = x + w;
935 wxCoord y2 = y + h;
936
937 int rx1 = XLOG2DEV(x+w/2);
938 int ry1 = YLOG2DEV(y+h/2);
939 int rx2 = rx1;
940 int ry2 = ry1;
941
942 sa = DegToRad(sa);
943 ea = DegToRad(ea);
944
945 rx1 += (int)(100.0 * abs(w) * cos(sa));
946 ry1 -= (int)(100.0 * abs(h) * m_signY * sin(sa));
947 rx2 += (int)(100.0 * abs(w) * cos(ea));
948 ry2 -= (int)(100.0 * abs(h) * m_signY * sin(ea));
949
950 // draw pie with NULL_PEN first and then outline otherwise a line is
951 // drawn from the start and end points to the centre
952 HPEN hpenOld = (HPEN) ::SelectObject(GetHdc(), (HPEN) ::GetStockObject(NULL_PEN));
953 if (m_signY > 0)
954 {
955 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2)+1, YLOG2DEV(y2)+1,
956 rx1, ry1, rx2, ry2);
957 }
958 else
959 {
960 (void)Pie(GetHdc(), XLOG2DEV(x), YLOG2DEV(y)-1, XLOG2DEV(x2)+1, YLOG2DEV(y2),
961 rx1, ry1-1, rx2, ry2-1);
962 }
963
964 ::SelectObject(GetHdc(), hpenOld);
965
966 (void)Arc(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), XLOG2DEV(x2), YLOG2DEV(y2),
967 rx1, ry1, rx2, ry2);
968
969 CalcBoundingBox(x, y);
970 CalcBoundingBox(x2, y2);
971 #endif
972 }
973
974 void wxDC::DoDrawIcon(const wxIcon& icon, wxCoord x, wxCoord y)
975 {
976 #ifdef __WXMICROWIN__
977 if (!GetHDC()) return;
978 #endif
979
980 wxCHECK_RET( icon.Ok(), wxT("invalid icon in DrawIcon") );
981
982 #ifdef __WIN32__
983 ::DrawIconEx(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon), icon.GetWidth(), icon.GetHeight(), 0, NULL, DI_NORMAL);
984 #else
985 ::DrawIcon(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), GetHiconOf(icon));
986 #endif
987
988 CalcBoundingBox(x, y);
989 CalcBoundingBox(x + icon.GetWidth(), y + icon.GetHeight());
990 }
991
992 void wxDC::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool useMask )
993 {
994 #ifdef __WXMICROWIN__
995 if (!GetHDC()) return;
996 #endif
997
998 wxCHECK_RET( bmp.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
999
1000 int width = bmp.GetWidth(),
1001 height = bmp.GetHeight();
1002
1003 HBITMAP hbmpMask = 0;
1004
1005 #if wxUSE_PALETTE
1006 HPALETTE oldPal = 0;
1007 #endif // wxUSE_PALETTE
1008
1009 if ( bmp.HasAlpha() )
1010 {
1011 MemoryHDC hdcMem;
1012 SelectInHDC select(hdcMem, GetHbitmapOf(bmp));
1013
1014 if ( AlphaBlt(GetHdc(), x, y, width, height, hdcMem, bmp) )
1015 return;
1016 }
1017
1018 if ( useMask )
1019 {
1020 wxMask *mask = bmp.GetMask();
1021 if ( mask )
1022 hbmpMask = (HBITMAP)mask->GetMaskBitmap();
1023
1024 if ( !hbmpMask )
1025 {
1026 // don't give assert here because this would break existing
1027 // programs - just silently ignore useMask parameter
1028 useMask = FALSE;
1029 }
1030 }
1031 if ( useMask )
1032 {
1033 #ifdef __WIN32__
1034 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1035 // points
1036 // On some systems, MaskBlt succeeds yet is much much slower
1037 // than the wxWindows fall-back implementation. So we need
1038 // to be able to switch this on and off at runtime.
1039 bool ok = FALSE;
1040 #if wxUSE_SYSTEM_OPTIONS
1041 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1042 #endif
1043 {
1044 HDC cdc = GetHdc();
1045 HDC hdcMem = ::CreateCompatibleDC(GetHdc());
1046 HGDIOBJ hOldBitmap = ::SelectObject(hdcMem, GetHbitmapOf(bmp));
1047 #if wxUSE_PALETTE
1048 wxPalette *pal = bmp.GetPalette();
1049 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1050 {
1051 oldPal = ::SelectPalette(hdcMem, GetHpaletteOf(*pal), FALSE);
1052 ::RealizePalette(hdcMem);
1053 }
1054 #endif // wxUSE_PALETTE
1055
1056 ok = ::MaskBlt(cdc, x, y, width, height,
1057 hdcMem, 0, 0,
1058 hbmpMask, 0, 0,
1059 MAKEROP4(SRCCOPY, DSTCOPY)) != 0;
1060
1061 #if wxUSE_PALETTE
1062 if (oldPal)
1063 ::SelectPalette(hdcMem, oldPal, FALSE);
1064 #endif // wxUSE_PALETTE
1065
1066 ::SelectObject(hdcMem, hOldBitmap);
1067 ::DeleteDC(hdcMem);
1068 }
1069
1070 if ( !ok )
1071 #endif // Win32
1072 {
1073 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1074 // level
1075 wxMemoryDC memDC;
1076 memDC.SelectObject(bmp);
1077
1078 Blit(x, y, width, height, &memDC, 0, 0, wxCOPY, useMask);
1079
1080 memDC.SelectObject(wxNullBitmap);
1081 }
1082 }
1083 else // no mask, just use BitBlt()
1084 {
1085 HDC cdc = GetHdc();
1086 HDC memdc = ::CreateCompatibleDC( cdc );
1087 HBITMAP hbitmap = (HBITMAP) bmp.GetHBITMAP( );
1088
1089 wxASSERT_MSG( hbitmap, wxT("bitmap is ok but HBITMAP is NULL?") );
1090
1091 COLORREF old_textground = ::GetTextColor(GetHdc());
1092 COLORREF old_background = ::GetBkColor(GetHdc());
1093 if (m_textForegroundColour.Ok())
1094 {
1095 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1096 }
1097 if (m_textBackgroundColour.Ok())
1098 {
1099 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1100 }
1101
1102 #if wxUSE_PALETTE
1103 wxPalette *pal = bmp.GetPalette();
1104 if ( pal && ::GetDeviceCaps(cdc,BITSPIXEL) <= 8 )
1105 {
1106 oldPal = ::SelectPalette(memdc, GetHpaletteOf(*pal), FALSE);
1107 ::RealizePalette(memdc);
1108 }
1109 #endif // wxUSE_PALETTE
1110
1111 HGDIOBJ hOldBitmap = ::SelectObject( memdc, hbitmap );
1112 ::BitBlt( cdc, x, y, width, height, memdc, 0, 0, SRCCOPY);
1113
1114 #if wxUSE_PALETTE
1115 if (oldPal)
1116 ::SelectPalette(memdc, oldPal, FALSE);
1117 #endif // wxUSE_PALETTE
1118
1119 ::SelectObject( memdc, hOldBitmap );
1120 ::DeleteDC( memdc );
1121
1122 ::SetTextColor(GetHdc(), old_textground);
1123 ::SetBkColor(GetHdc(), old_background);
1124 }
1125 }
1126
1127 void wxDC::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
1128 {
1129 #ifdef __WXMICROWIN__
1130 if (!GetHDC()) return;
1131 #endif
1132
1133 DrawAnyText(text, x, y);
1134
1135 // update the bounding box
1136 CalcBoundingBox(x, y);
1137
1138 wxCoord w, h;
1139 GetTextExtent(text, &w, &h);
1140 CalcBoundingBox(x + w, y + h);
1141 }
1142
1143 void wxDC::DrawAnyText(const wxString& text, wxCoord x, wxCoord y)
1144 {
1145 #ifdef __WXMICROWIN__
1146 if (!GetHDC()) return;
1147 #endif
1148
1149 // prepare for drawing the text
1150 if ( m_textForegroundColour.Ok() )
1151 SetTextColor(GetHdc(), m_textForegroundColour.GetPixel());
1152
1153 DWORD old_background = 0;
1154 if ( m_textBackgroundColour.Ok() )
1155 {
1156 old_background = SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1157 }
1158
1159 SetBkMode(GetHdc(), m_backgroundMode == wxTRANSPARENT ? TRANSPARENT
1160 : OPAQUE);
1161
1162 #ifdef __WXWINCE__
1163 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y), 0, NULL,
1164 text.c_str(), text.length(), NULL) == 0 )
1165 {
1166 wxLogLastError(wxT("TextOut"));
1167 }
1168 #else
1169 if ( ::TextOut(GetHdc(), XLOG2DEV(x), YLOG2DEV(y),
1170 text.c_str(), text.length()) == 0 )
1171 {
1172 wxLogLastError(wxT("TextOut"));
1173 }
1174 #endif
1175
1176 // restore the old parameters (text foreground colour may be left because
1177 // it never is set to anything else, but background should remain
1178 // transparent even if we just drew an opaque string)
1179 if ( m_textBackgroundColour.Ok() )
1180 (void)SetBkColor(GetHdc(), old_background);
1181
1182 SetBkMode(GetHdc(), TRANSPARENT);
1183 }
1184
1185 void wxDC::DoDrawRotatedText(const wxString& text,
1186 wxCoord x, wxCoord y,
1187 double angle)
1188 {
1189 #ifdef __WXMICROWIN__
1190 if (!GetHDC()) return;
1191 #endif
1192
1193 // we test that we have some font because otherwise we should still use the
1194 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1195 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1196 // font for drawing rotated fonts unfortunately)
1197 if ( (angle == 0.0) && m_font.Ok() )
1198 {
1199 DoDrawText(text, x, y);
1200 }
1201 #ifndef __WXMICROWIN__
1202 else
1203 {
1204 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1205 // because it's not TrueType and so can't have non zero
1206 // orientation/escapement under Win9x
1207 wxFont font = m_font.Ok() ? m_font : *wxSWISS_FONT;
1208 HFONT hfont = (HFONT)font.GetResourceHandle();
1209 LOGFONT lf;
1210 if ( ::GetObject(hfont, sizeof(lf), &lf) == 0 )
1211 {
1212 wxLogLastError(wxT("GetObject(hfont)"));
1213 }
1214
1215 // GDI wants the angle in tenth of degree
1216 long angle10 = (long)(angle * 10);
1217 lf.lfEscapement = angle10;
1218 lf. lfOrientation = angle10;
1219
1220 hfont = ::CreateFontIndirect(&lf);
1221 if ( !hfont )
1222 {
1223 wxLogLastError(wxT("CreateFont"));
1224 }
1225 else
1226 {
1227 HFONT hfontOld = (HFONT)::SelectObject(GetHdc(), hfont);
1228
1229 DrawAnyText(text, x, y);
1230
1231 (void)::SelectObject(GetHdc(), hfontOld);
1232 (void)::DeleteObject(hfont);
1233 }
1234
1235 // call the bounding box by adding all four vertices of the rectangle
1236 // containing the text to it (simpler and probably not slower than
1237 // determining which of them is really topmost/leftmost/...)
1238 wxCoord w, h;
1239 GetTextExtent(text, &w, &h);
1240
1241 double rad = DegToRad(angle);
1242
1243 // "upper left" and "upper right"
1244 CalcBoundingBox(x, y);
1245 CalcBoundingBox(x + wxCoord(w*cos(rad)), y - wxCoord(h*sin(rad)));
1246
1247 // "bottom left" and "bottom right"
1248 x += (wxCoord)(h*sin(rad));
1249 y += (wxCoord)(h*cos(rad));
1250 CalcBoundingBox(x, y);
1251 CalcBoundingBox(x + wxCoord(h*sin(rad)), y + wxCoord(h*cos(rad)));
1252 }
1253 #endif
1254 }
1255
1256 // ---------------------------------------------------------------------------
1257 // set GDI objects
1258 // ---------------------------------------------------------------------------
1259
1260 #if wxUSE_PALETTE
1261
1262 void wxDC::DoSelectPalette(bool realize)
1263 {
1264 #ifdef __WXMICROWIN__
1265 if (!GetHDC()) return;
1266 #endif
1267
1268 // Set the old object temporarily, in case the assignment deletes an object
1269 // that's not yet selected out.
1270 if (m_oldPalette)
1271 {
1272 ::SelectPalette(GetHdc(), (HPALETTE) m_oldPalette, FALSE);
1273 m_oldPalette = 0;
1274 }
1275
1276 if ( m_palette.Ok() )
1277 {
1278 HPALETTE oldPal = ::SelectPalette(GetHdc(),
1279 GetHpaletteOf(m_palette),
1280 FALSE);
1281 if (!m_oldPalette)
1282 m_oldPalette = (WXHPALETTE) oldPal;
1283
1284 if (realize)
1285 ::RealizePalette(GetHdc());
1286 }
1287 }
1288
1289 void wxDC::SetPalette(const wxPalette& palette)
1290 {
1291 if ( palette.Ok() )
1292 {
1293 m_palette = palette;
1294 DoSelectPalette(TRUE);
1295 }
1296 }
1297
1298 void wxDC::InitializePalette()
1299 {
1300 if ( wxDisplayDepth() <= 8 )
1301 {
1302 // look for any window or parent that has a custom palette. If any has
1303 // one then we need to use it in drawing operations
1304 wxWindow *win = m_canvas->GetAncestorWithCustomPalette();
1305
1306 m_hasCustomPalette = win && win->HasCustomPalette();
1307 if ( m_hasCustomPalette )
1308 {
1309 m_palette = win->GetPalette();
1310
1311 // turn on MSW translation for this palette
1312 DoSelectPalette();
1313 }
1314 }
1315 }
1316
1317 #endif // wxUSE_PALETTE
1318
1319 void wxDC::SetFont(const wxFont& the_font)
1320 {
1321 #ifdef __WXMICROWIN__
1322 if (!GetHDC()) return;
1323 #endif
1324
1325 // Set the old object temporarily, in case the assignment deletes an object
1326 // that's not yet selected out.
1327 if (m_oldFont)
1328 {
1329 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
1330 m_oldFont = 0;
1331 }
1332
1333 m_font = the_font;
1334
1335 if (!the_font.Ok())
1336 {
1337 if (m_oldFont)
1338 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
1339 m_oldFont = 0;
1340 }
1341
1342 if (m_font.Ok() && m_font.GetResourceHandle())
1343 {
1344 HFONT f = (HFONT) ::SelectObject(GetHdc(), (HFONT) m_font.GetResourceHandle());
1345 if (f == (HFONT) NULL)
1346 {
1347 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
1348 }
1349 if (!m_oldFont)
1350 m_oldFont = (WXHFONT) f;
1351 }
1352 }
1353
1354 void wxDC::SetPen(const wxPen& pen)
1355 {
1356 #ifdef __WXMICROWIN__
1357 if (!GetHDC()) return;
1358 #endif
1359
1360 // Set the old object temporarily, in case the assignment deletes an object
1361 // that's not yet selected out.
1362 if (m_oldPen)
1363 {
1364 ::SelectObject(GetHdc(), (HPEN) m_oldPen);
1365 m_oldPen = 0;
1366 }
1367
1368 m_pen = pen;
1369
1370 if (!m_pen.Ok())
1371 {
1372 if (m_oldPen)
1373 ::SelectObject(GetHdc(), (HPEN) m_oldPen);
1374 m_oldPen = 0;
1375 }
1376
1377 if (m_pen.Ok())
1378 {
1379 if (m_pen.GetResourceHandle())
1380 {
1381 HPEN p = (HPEN) ::SelectObject(GetHdc(), (HPEN)m_pen.GetResourceHandle());
1382 if (!m_oldPen)
1383 m_oldPen = (WXHPEN) p;
1384 }
1385 }
1386 }
1387
1388 void wxDC::SetBrush(const wxBrush& brush)
1389 {
1390 #ifdef __WXMICROWIN__
1391 if (!GetHDC()) return;
1392 #endif
1393
1394 // Set the old object temporarily, in case the assignment deletes an object
1395 // that's not yet selected out.
1396 if (m_oldBrush)
1397 {
1398 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1399 m_oldBrush = 0;
1400 }
1401
1402 m_brush = brush;
1403
1404 if (!m_brush.Ok())
1405 {
1406 if (m_oldBrush)
1407 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1408 m_oldBrush = 0;
1409 }
1410
1411 if (m_brush.Ok())
1412 {
1413 // to make sure the brush is alligned with the logical coordinates
1414 wxBitmap *stipple = m_brush.GetStipple();
1415 if ( stipple && stipple->Ok() )
1416 {
1417 #ifdef __WIN32__
1418 ::SetBrushOrgEx(GetHdc(),
1419 m_deviceOriginX % stipple->GetWidth(),
1420 m_deviceOriginY % stipple->GetHeight(),
1421 NULL); // don't need previous brush origin
1422 #else
1423 ::SetBrushOrg(GetHdc(),
1424 m_deviceOriginX % stipple->GetWidth(),
1425 m_deviceOriginY % stipple->GetHeight());
1426 #endif
1427 }
1428
1429 if ( m_brush.GetResourceHandle() )
1430 {
1431 HBRUSH b = (HBRUSH) ::SelectObject(GetHdc(), (HBRUSH)m_brush.GetResourceHandle());
1432 if (!m_oldBrush)
1433 m_oldBrush = (WXHBRUSH) b;
1434 }
1435 }
1436 }
1437
1438 void wxDC::SetBackground(const wxBrush& brush)
1439 {
1440 #ifdef __WXMICROWIN__
1441 if (!GetHDC()) return;
1442 #endif
1443
1444 m_backgroundBrush = brush;
1445
1446 if ( m_backgroundBrush.Ok() )
1447 {
1448 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
1449 }
1450 }
1451
1452 void wxDC::SetBackgroundMode(int mode)
1453 {
1454 #ifdef __WXMICROWIN__
1455 if (!GetHDC()) return;
1456 #endif
1457
1458 m_backgroundMode = mode;
1459
1460 // SetBackgroundColour now only refers to text background
1461 // and m_backgroundMode is used there
1462 }
1463
1464 void wxDC::SetLogicalFunction(int function)
1465 {
1466 #ifdef __WXMICROWIN__
1467 if (!GetHDC()) return;
1468 #endif
1469
1470 m_logicalFunction = function;
1471
1472 SetRop(m_hDC);
1473 }
1474
1475 void wxDC::SetRop(WXHDC dc)
1476 {
1477 if ( !dc || m_logicalFunction < 0 )
1478 return;
1479
1480 int rop;
1481
1482 switch (m_logicalFunction)
1483 {
1484 case wxCLEAR: rop = R2_BLACK; break;
1485 case wxXOR: rop = R2_XORPEN; break;
1486 case wxINVERT: rop = R2_NOT; break;
1487 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1488 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1489 case wxCOPY: rop = R2_COPYPEN; break;
1490 case wxAND: rop = R2_MASKPEN; break;
1491 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1492 case wxNO_OP: rop = R2_NOP; break;
1493 case wxNOR: rop = R2_NOTMERGEPEN; break;
1494 case wxEQUIV: rop = R2_NOTXORPEN; break;
1495 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1496 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1497 case wxNAND: rop = R2_NOTMASKPEN; break;
1498 case wxOR: rop = R2_MERGEPEN; break;
1499 case wxSET: rop = R2_WHITE; break;
1500
1501 default:
1502 wxFAIL_MSG( wxT("unsupported logical function") );
1503 return;
1504 }
1505
1506 SetROP2(GetHdc(), rop);
1507 }
1508
1509 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1510 {
1511 // We might be previewing, so return TRUE to let it continue.
1512 return TRUE;
1513 }
1514
1515 void wxDC::EndDoc()
1516 {
1517 }
1518
1519 void wxDC::StartPage()
1520 {
1521 }
1522
1523 void wxDC::EndPage()
1524 {
1525 }
1526
1527 // ---------------------------------------------------------------------------
1528 // text metrics
1529 // ---------------------------------------------------------------------------
1530
1531 wxCoord wxDC::GetCharHeight() const
1532 {
1533 #ifdef __WXMICROWIN__
1534 if (!GetHDC()) return 0;
1535 #endif
1536
1537 TEXTMETRIC lpTextMetric;
1538
1539 GetTextMetrics(GetHdc(), &lpTextMetric);
1540
1541 return lpTextMetric.tmHeight;
1542 }
1543
1544 wxCoord wxDC::GetCharWidth() const
1545 {
1546 #ifdef __WXMICROWIN__
1547 if (!GetHDC()) return 0;
1548 #endif
1549
1550 TEXTMETRIC lpTextMetric;
1551
1552 GetTextMetrics(GetHdc(), &lpTextMetric);
1553
1554 return lpTextMetric.tmAveCharWidth;
1555 }
1556
1557 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1558 wxCoord *descent, wxCoord *externalLeading,
1559 wxFont *font) const
1560 {
1561 #ifdef __WXMICROWIN__
1562 if (!GetHDC())
1563 {
1564 if (x) *x = 0;
1565 if (y) *y = 0;
1566 if (descent) *descent = 0;
1567 if (externalLeading) *externalLeading = 0;
1568 return;
1569 }
1570 #endif // __WXMICROWIN__
1571
1572 HFONT hfontOld;
1573 if ( font )
1574 {
1575 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1576
1577 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1578 }
1579 else // don't change the font
1580 {
1581 hfontOld = 0;
1582 }
1583
1584 SIZE sizeRect;
1585 TEXTMETRIC tm;
1586
1587 GetTextExtentPoint(GetHdc(), string, string.length(), &sizeRect);
1588 GetTextMetrics(GetHdc(), &tm);
1589
1590 if (x)
1591 *x = sizeRect.cx;
1592 if (y)
1593 *y = sizeRect.cy;
1594 if (descent)
1595 *descent = tm.tmDescent;
1596 if (externalLeading)
1597 *externalLeading = tm.tmExternalLeading;
1598
1599 if ( hfontOld )
1600 {
1601 ::SelectObject(GetHdc(), hfontOld);
1602 }
1603 }
1604
1605 void wxDC::SetMapMode(int mode)
1606 {
1607 #ifdef __WXMICROWIN__
1608 if (!GetHDC()) return;
1609 #endif
1610
1611 m_mappingMode = mode;
1612
1613 if ( mode == wxMM_TEXT )
1614 {
1615 m_logicalScaleX =
1616 m_logicalScaleY = 1.0;
1617 }
1618 else // need to do some calculations
1619 {
1620 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1621 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1622 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1623 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1624
1625 if ( (mm_width == 0) || (mm_height == 0) )
1626 {
1627 // we can't calculate mm2pixels[XY] then!
1628 return;
1629 }
1630
1631 double mm2pixelsX = (double)pixel_width / mm_width,
1632 mm2pixelsY = (double)pixel_height / mm_height;
1633
1634 switch (mode)
1635 {
1636 case wxMM_TWIPS:
1637 m_logicalScaleX = twips2mm * mm2pixelsX;
1638 m_logicalScaleY = twips2mm * mm2pixelsY;
1639 break;
1640
1641 case wxMM_POINTS:
1642 m_logicalScaleX = pt2mm * mm2pixelsX;
1643 m_logicalScaleY = pt2mm * mm2pixelsY;
1644 break;
1645
1646 case wxMM_METRIC:
1647 m_logicalScaleX = mm2pixelsX;
1648 m_logicalScaleY = mm2pixelsY;
1649 break;
1650
1651 case wxMM_LOMETRIC:
1652 m_logicalScaleX = mm2pixelsX / 10.0;
1653 m_logicalScaleY = mm2pixelsY / 10.0;
1654 break;
1655
1656 default:
1657 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1658 }
1659 }
1660
1661 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1662 // cases we could do with MM_TEXT and in the remaining 0.9% with
1663 // MM_ISOTROPIC (TODO!)
1664 #ifndef __WXWINCE__
1665 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1666
1667 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1668 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1669
1670 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1671 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1672
1673 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1674 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1675 #endif
1676 }
1677
1678 void wxDC::SetUserScale(double x, double y)
1679 {
1680 #ifdef __WXMICROWIN__
1681 if (!GetHDC()) return;
1682 #endif
1683
1684 #ifndef __WXWINCE__
1685 if ( x == m_userScaleX && y == m_userScaleY )
1686 return;
1687
1688 m_userScaleX = x;
1689 m_userScaleY = y;
1690
1691 SetMapMode(m_mappingMode);
1692 #endif
1693 }
1694
1695 void wxDC::SetAxisOrientation(bool xLeftRight, bool yBottomUp)
1696 {
1697 #ifdef __WXMICROWIN__
1698 if (!GetHDC()) return;
1699 #endif
1700
1701 #ifndef __WXWINCE__
1702 int signX = xLeftRight ? 1 : -1,
1703 signY = yBottomUp ? -1 : 1;
1704
1705 if ( signX != m_signX || signY != m_signY )
1706 {
1707 m_signX = signX;
1708 m_signY = signY;
1709
1710 SetMapMode(m_mappingMode);
1711 }
1712 #endif
1713 }
1714
1715 void wxDC::SetSystemScale(double x, double y)
1716 {
1717 #ifdef __WXMICROWIN__
1718 if (!GetHDC()) return;
1719 #endif
1720
1721 #ifndef __WXWINCE__
1722 if ( x == m_scaleX && y == m_scaleY )
1723 return;
1724
1725 m_scaleX = x;
1726 m_scaleY = y;
1727
1728 SetMapMode(m_mappingMode);
1729 #endif
1730 }
1731
1732 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1733 {
1734 #ifdef __WXMICROWIN__
1735 if (!GetHDC()) return;
1736 #endif
1737
1738 #ifndef __WXWINCE__
1739 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1740 return;
1741
1742 m_logicalOriginX = x;
1743 m_logicalOriginY = y;
1744
1745 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1746 #endif
1747 }
1748
1749 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1750 {
1751 #ifdef __WXMICROWIN__
1752 if (!GetHDC()) return;
1753 #endif
1754
1755 #ifndef __WXWINCE__
1756 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1757 return;
1758
1759 m_deviceOriginX = x;
1760 m_deviceOriginY = y;
1761
1762 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1763 #endif
1764 }
1765
1766 // ---------------------------------------------------------------------------
1767 // coordinates transformations
1768 // ---------------------------------------------------------------------------
1769
1770 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1771 {
1772 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
1773 }
1774
1775 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1776 {
1777 // axis orientation is not taken into account for conversion of a distance
1778 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
1779 }
1780
1781 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1782 {
1783 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
1784 }
1785
1786 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1787 {
1788 // axis orientation is not taken into account for conversion of a distance
1789 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
1790 }
1791
1792 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1793 {
1794 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
1795 }
1796
1797 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1798 {
1799 // axis orientation is not taken into account for conversion of a distance
1800 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
1801 }
1802
1803 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1804 {
1805 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
1806 }
1807
1808 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1809 {
1810 // axis orientation is not taken into account for conversion of a distance
1811 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
1812 }
1813
1814 // ---------------------------------------------------------------------------
1815 // bit blit
1816 // ---------------------------------------------------------------------------
1817
1818 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1819 wxCoord width, wxCoord height,
1820 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1821 int rop, bool useMask,
1822 wxCoord xsrcMask, wxCoord ysrcMask)
1823 {
1824 wxCHECK_MSG( source, FALSE, _T("wxDC::Blit(): NULL wxDC pointer") );
1825
1826 #ifdef __WXMICROWIN__
1827 if (!GetHDC()) return FALSE;
1828 #endif
1829
1830 const wxBitmap& bmpSrc = source->m_selectedBitmap;
1831 if ( bmpSrc.Ok() && bmpSrc.HasAlpha() )
1832 {
1833 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
1834 GetHdcOf(*source), bmpSrc) )
1835 return TRUE;
1836 }
1837
1838 wxMask *mask = NULL;
1839 if ( useMask )
1840 {
1841 mask = bmpSrc.GetMask();
1842
1843 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
1844 {
1845 // don't give assert here because this would break existing
1846 // programs - just silently ignore useMask parameter
1847 useMask = FALSE;
1848 }
1849 }
1850
1851 if (xsrcMask == -1 && ysrcMask == -1)
1852 {
1853 xsrcMask = xsrc; ysrcMask = ysrc;
1854 }
1855
1856 COLORREF old_textground = ::GetTextColor(GetHdc());
1857 COLORREF old_background = ::GetBkColor(GetHdc());
1858 if (m_textForegroundColour.Ok())
1859 {
1860 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1861 }
1862 if (m_textBackgroundColour.Ok())
1863 {
1864 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1865 }
1866
1867 DWORD dwRop;
1868 switch (rop)
1869 {
1870 case wxXOR: dwRop = SRCINVERT; break;
1871 case wxINVERT: dwRop = DSTINVERT; break;
1872 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1873 case wxAND_REVERSE: dwRop = SRCERASE; break;
1874 case wxCLEAR: dwRop = BLACKNESS; break;
1875 case wxSET: dwRop = WHITENESS; break;
1876 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1877 case wxAND: dwRop = SRCAND; break;
1878 case wxOR: dwRop = SRCPAINT; break;
1879 case wxEQUIV: dwRop = 0x00990066; break;
1880 case wxNAND: dwRop = 0x007700E6; break;
1881 case wxAND_INVERT: dwRop = 0x00220326; break;
1882 case wxCOPY: dwRop = SRCCOPY; break;
1883 case wxNO_OP: dwRop = DSTCOPY; break;
1884 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1885 case wxNOR: dwRop = NOTSRCCOPY; break;
1886 default:
1887 wxFAIL_MSG( wxT("unsupported logical function") );
1888 return FALSE;
1889 }
1890
1891 bool success = FALSE;
1892
1893 if (useMask)
1894 {
1895 #ifdef __WIN32__
1896 // we want the part of the image corresponding to the mask to be
1897 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1898 // meaning of fg and bg is inverted which corresponds to wxWin notion
1899 // of the mask which is also contrary to the Windows one)
1900
1901 // On some systems, MaskBlt succeeds yet is much much slower
1902 // than the wxWindows fall-back implementation. So we need
1903 // to be able to switch this on and off at runtime.
1904 #if wxUSE_SYSTEM_OPTIONS
1905 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1906 #endif
1907 {
1908 success = ::MaskBlt
1909 (
1910 GetHdc(),
1911 xdest, ydest, width, height,
1912 GetHdcOf(*source),
1913 xsrc, ysrc,
1914 (HBITMAP)mask->GetMaskBitmap(),
1915 xsrcMask, ysrcMask,
1916 MAKEROP4(dwRop, DSTCOPY)
1917 ) != 0;
1918 }
1919
1920 if ( !success )
1921 #endif // Win32
1922 {
1923 // Blit bitmap with mask
1924 HDC dc_mask ;
1925 HDC dc_buffer ;
1926 HBITMAP buffer_bmap ;
1927
1928 #if wxUSE_DC_CACHEING
1929 // create a temp buffer bitmap and DCs to access it and the mask
1930 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
1931 dc_mask = (HDC) dcCacheEntry1->m_dc;
1932
1933 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
1934 dc_buffer = (HDC) dcCacheEntry2->m_dc;
1935
1936 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
1937 width, height);
1938
1939 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
1940 #else // !wxUSE_DC_CACHEING
1941 // create a temp buffer bitmap and DCs to access it and the mask
1942 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
1943 dc_buffer = ::CreateCompatibleDC(GetHdc());
1944 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
1945 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1946 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
1947 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
1948
1949 // copy dest to buffer
1950 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1951 GetHdc(), xdest, ydest, SRCCOPY) )
1952 {
1953 wxLogLastError(wxT("BitBlt"));
1954 }
1955
1956 // copy src to buffer using selected raster op
1957 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1958 GetHdcOf(*source), xsrc, ysrc, dwRop) )
1959 {
1960 wxLogLastError(wxT("BitBlt"));
1961 }
1962
1963 // set masked area in buffer to BLACK (pixel value 0)
1964 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1965 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1966 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1967 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1968 {
1969 wxLogLastError(wxT("BitBlt"));
1970 }
1971
1972 // set unmasked area in dest to BLACK
1973 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1974 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1975 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
1976 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1977 {
1978 wxLogLastError(wxT("BitBlt"));
1979 }
1980 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
1981 ::SetTextColor(GetHdc(), prevCol);
1982
1983 // OR buffer to dest
1984 success = ::BitBlt(GetHdc(), xdest, ydest,
1985 (int)width, (int)height,
1986 dc_buffer, 0, 0, SRCPAINT) != 0;
1987 if ( !success )
1988 {
1989 wxLogLastError(wxT("BitBlt"));
1990 }
1991
1992 // tidy up temporary DCs and bitmap
1993 ::SelectObject(dc_mask, hOldMaskBitmap);
1994 ::SelectObject(dc_buffer, hOldBufferBitmap);
1995
1996 #if !wxUSE_DC_CACHEING
1997 {
1998 ::DeleteDC(dc_mask);
1999 ::DeleteDC(dc_buffer);
2000 ::DeleteObject(buffer_bmap);
2001 }
2002 #endif
2003 }
2004 }
2005 else // no mask, just BitBlt() it
2006 {
2007 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2008 // use StretchBlt() if available and finally fall back to BitBlt()
2009
2010 // FIXME: use appropriate WinCE functions
2011 #ifndef __WXWINCE__
2012 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2013 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
2014 {
2015 DIBSECTION ds;
2016 wxZeroMemory(ds);
2017
2018 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2019 sizeof(ds),
2020 &ds) == sizeof(ds) )
2021 {
2022 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2023
2024 if ( ::StretchDIBits(GetHdc(),
2025 xdest, ydest,
2026 width, height,
2027 0, 0,
2028 width, height,
2029 ds.dsBm.bmBits,
2030 (LPBITMAPINFO)&ds.dsBmih,
2031 DIB_RGB_COLORS,
2032 SRCCOPY
2033 ) == (int)GDI_ERROR )
2034 {
2035 wxLogLastError(wxT("StretchDIBits"));
2036 }
2037 else
2038 {
2039 success = TRUE;
2040 }
2041 }
2042 }
2043
2044 if ( !success && (caps & RC_STRETCHBLT) )
2045 #endif
2046 // __WXWINCE__
2047 {
2048 #ifndef __WXWINCE__
2049 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2050 #endif
2051
2052 if ( !::StretchBlt
2053 (
2054 GetHdc(),
2055 xdest, ydest, width, height,
2056 GetHdcOf(*source),
2057 xsrc, ysrc, width, height,
2058 dwRop
2059 ) )
2060 {
2061 wxLogLastError(_T("StretchBlt"));
2062 }
2063 else
2064 {
2065 success = TRUE;
2066 }
2067 }
2068
2069 if ( !success )
2070 {
2071 if ( !::BitBlt
2072 (
2073 GetHdc(),
2074 xdest, ydest,
2075 (int)width, (int)height,
2076 GetHdcOf(*source),
2077 xsrc, ysrc,
2078 dwRop
2079 ) )
2080 {
2081 wxLogLastError(_T("BitBlt"));
2082 }
2083 else
2084 {
2085 success = TRUE;
2086 }
2087 }
2088 }
2089
2090 ::SetTextColor(GetHdc(), old_textground);
2091 ::SetBkColor(GetHdc(), old_background);
2092
2093 return success;
2094 }
2095
2096 void wxDC::DoGetSize(int *w, int *h) const
2097 {
2098 #ifdef __WXMICROWIN__
2099 if (!GetHDC()) return;
2100 #endif
2101
2102 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2103 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2104 }
2105
2106 void wxDC::DoGetSizeMM(int *w, int *h) const
2107 {
2108 #ifdef __WXMICROWIN__
2109 if (!GetHDC()) return;
2110 #endif
2111
2112 // if we implement it in terms of DoGetSize() instead of directly using the
2113 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2114 // will also work for wxWindowDC and wxClientDC even though their size is
2115 // not the same as the total size of the screen
2116 int wPixels, hPixels;
2117 DoGetSize(&wPixels, &hPixels);
2118
2119 if ( w )
2120 {
2121 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2122
2123 wxCHECK_RET( wTotal, _T("0 width device?") );
2124
2125 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2126 }
2127
2128 if ( h )
2129 {
2130 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2131
2132 wxCHECK_RET( hTotal, _T("0 height device?") );
2133
2134 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2135 }
2136 }
2137
2138 wxSize wxDC::GetPPI() const
2139 {
2140 #ifdef __WXMICROWIN__
2141 if (!GetHDC()) return wxSize();
2142 #endif
2143
2144 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2145 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2146
2147 return wxSize(x, y);
2148 }
2149
2150 // For use by wxWindows only, unless custom units are required.
2151 void wxDC::SetLogicalScale(double x, double y)
2152 {
2153 #ifdef __WXMICROWIN__
2154 if (!GetHDC()) return;
2155 #endif
2156
2157 m_logicalScaleX = x;
2158 m_logicalScaleY = y;
2159 }
2160
2161 // ----------------------------------------------------------------------------
2162 // DC caching
2163 // ----------------------------------------------------------------------------
2164
2165 #if wxUSE_DC_CACHEING
2166
2167 /*
2168 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2169 * improve it in due course, either using arrays, or simply storing pointers to one
2170 * entry for the bitmap, and two for the DCs. -- JACS
2171 */
2172
2173 wxList wxDC::sm_bitmapCache;
2174 wxList wxDC::sm_dcCache;
2175
2176 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2177 {
2178 m_bitmap = hBitmap;
2179 m_dc = 0;
2180 m_width = w;
2181 m_height = h;
2182 m_depth = depth;
2183 }
2184
2185 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2186 {
2187 m_bitmap = 0;
2188 m_dc = hDC;
2189 m_width = 0;
2190 m_height = 0;
2191 m_depth = depth;
2192 }
2193
2194 wxDCCacheEntry::~wxDCCacheEntry()
2195 {
2196 if (m_bitmap)
2197 ::DeleteObject((HBITMAP) m_bitmap);
2198 if (m_dc)
2199 ::DeleteDC((HDC) m_dc);
2200 }
2201
2202 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2203 {
2204 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2205 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2206 while (node)
2207 {
2208 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2209
2210 if (entry->m_depth == depth)
2211 {
2212 if (entry->m_width < w || entry->m_height < h)
2213 {
2214 ::DeleteObject((HBITMAP) entry->m_bitmap);
2215 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2216 if ( !entry->m_bitmap)
2217 {
2218 wxLogLastError(wxT("CreateCompatibleBitmap"));
2219 }
2220 entry->m_width = w; entry->m_height = h;
2221 return entry;
2222 }
2223 return entry;
2224 }
2225
2226 node = node->GetNext();
2227 }
2228 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2229 if ( !hBitmap)
2230 {
2231 wxLogLastError(wxT("CreateCompatibleBitmap"));
2232 }
2233 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2234 AddToBitmapCache(entry);
2235 return entry;
2236 }
2237
2238 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2239 {
2240 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2241 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2242 while (node)
2243 {
2244 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2245
2246 // Don't return the same one as we already have
2247 if (!notThis || (notThis != entry))
2248 {
2249 if (entry->m_depth == depth)
2250 {
2251 return entry;
2252 }
2253 }
2254
2255 node = node->GetNext();
2256 }
2257 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2258 if ( !hDC)
2259 {
2260 wxLogLastError(wxT("CreateCompatibleDC"));
2261 }
2262 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2263 AddToDCCache(entry);
2264 return entry;
2265 }
2266
2267 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2268 {
2269 sm_bitmapCache.Append(entry);
2270 }
2271
2272 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2273 {
2274 sm_dcCache.Append(entry);
2275 }
2276
2277 void wxDC::ClearCache()
2278 {
2279 WX_CLEAR_LIST(wxList, sm_dcCache);
2280 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2281 }
2282
2283 // Clean up cache at app exit
2284 class wxDCModule : public wxModule
2285 {
2286 public:
2287 virtual bool OnInit() { return TRUE; }
2288 virtual void OnExit() { wxDC::ClearCache(); }
2289
2290 private:
2291 DECLARE_DYNAMIC_CLASS(wxDCModule)
2292 };
2293
2294 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2295
2296 #endif // wxUSE_DC_CACHEING
2297
2298 // ----------------------------------------------------------------------------
2299 // alpha channel support
2300 // ----------------------------------------------------------------------------
2301
2302 static bool AlphaBlt(HDC hdcDst,
2303 int x, int y, int width, int height,
2304 HDC hdcSrc,
2305 const wxBitmap& bmp)
2306 {
2307 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2308 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2309
2310 // do we have AlphaBlend() and company in the headers?
2311 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2312 // yes, now try to see if we have it during run-time
2313 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2314 HDC,int,int,int,int,
2315 BLENDFUNCTION);
2316
2317 // bitmaps can be drawn only from GUI thread so there is no need to
2318 // protect this static variable from multiple threads
2319 static bool s_triedToLoad = FALSE;
2320 static AlphaBlend_t pfnAlphaBlend = NULL;
2321 if ( !s_triedToLoad )
2322 {
2323 s_triedToLoad = TRUE;
2324
2325 // don't give errors about the DLL being unavailable, we're
2326 // prepared to handle this
2327 wxLogNull nolog;
2328
2329 wxDynamicLibrary dll(_T("msimg32.dll"));
2330 if ( dll.IsLoaded() )
2331 {
2332 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2333 if ( pfnAlphaBlend )
2334 {
2335 // we must keep the DLL loaded if we want to be able to
2336 // call AlphaBlend() so just never unload it at all, not a
2337 // big deal
2338 dll.Detach();
2339 }
2340 }
2341 }
2342
2343 if ( pfnAlphaBlend )
2344 {
2345 BLENDFUNCTION bf;
2346 bf.BlendOp = AC_SRC_OVER;
2347 bf.BlendFlags = 0;
2348 bf.SourceConstantAlpha = 0xff;
2349 bf.AlphaFormat = AC_SRC_ALPHA;
2350
2351 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2352 hdcSrc, 0, 0, width, height,
2353 bf) )
2354 {
2355 // skip wxAlphaBlend() call below
2356 return TRUE;
2357 }
2358
2359 wxLogLastError(_T("AlphaBlend"));
2360 }
2361 #endif // defined(AC_SRC_OVER)
2362
2363 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2364 // implementation
2365 #ifdef wxHAVE_RAW_BITMAP
2366 wxAlphaBlend(hdcDst, x, y, width, height, bmp);
2367
2368 return TRUE;
2369 #else // !wxHAVE_RAW_BITMAP
2370 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2371 // alpha but at least something will be shown like this)
2372 return FALSE;
2373 #endif // wxHAVE_RAW_BITMAP
2374 }
2375
2376
2377 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2378 #ifdef wxHAVE_RAW_BITMAP
2379
2380 static void
2381 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, const wxBitmap& bmpSrc)
2382 {
2383 // get the destination DC pixels
2384 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2385 MemoryHDC hdcMem;
2386 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2387
2388 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2389 {
2390 wxLogLastError(_T("BitBlt"));
2391 }
2392
2393 // combine them with the source bitmap using alpha
2394 wxAlphaPixelData dataDst(bmpDst),
2395 dataSrc((wxBitmap &)bmpSrc);
2396
2397 wxCHECK_RET( dataDst && dataSrc,
2398 _T("failed to get raw data in wxAlphaBlend") );
2399
2400 wxAlphaPixelData::Iterator pDst(dataDst),
2401 pSrc(dataSrc);
2402
2403 for ( int y = 0; y < h; y++ )
2404 {
2405 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2406 pSrcRowStart = pSrc;
2407
2408 for ( int x = 0; x < w; x++ )
2409 {
2410 // note that source bitmap uses premultiplied alpha (as required by
2411 // the real AlphaBlend)
2412 const unsigned beta = 255 - pSrc.Alpha();
2413
2414 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2415 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2416 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2417
2418 ++pDst;
2419 ++pSrc;
2420 }
2421
2422 pDst = pDstRowStart;
2423 pSrc = pSrcRowStart;
2424 pDst.OffsetY(dataDst, 1);
2425 pSrc.OffsetY(dataSrc, 1);
2426 }
2427
2428 // and finally blit them back to the destination DC
2429 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2430 {
2431 wxLogLastError(_T("BitBlt"));
2432 }
2433 }
2434
2435 #endif // #ifdef wxHAVE_RAW_BITMAP