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