don't call SelectObject() twice in SetPen() (slow!); don't do anything if setting...
[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 void wxDC::SetFont(const wxFont& the_font)
1324 {
1325 WXMICROWIN_CHECK_HDC
1326
1327 // Set the old object temporarily, in case the assignment deletes an object
1328 // that's not yet selected out.
1329 if (m_oldFont)
1330 {
1331 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
1332 m_oldFont = 0;
1333 }
1334
1335 m_font = the_font;
1336
1337 if (!the_font.Ok())
1338 {
1339 if (m_oldFont)
1340 ::SelectObject(GetHdc(), (HFONT) m_oldFont);
1341 m_oldFont = 0;
1342 }
1343
1344 if (m_font.Ok() && m_font.GetResourceHandle())
1345 {
1346 HFONT f = (HFONT) ::SelectObject(GetHdc(), (HFONT) m_font.GetResourceHandle());
1347 if (f == (HFONT) NULL)
1348 {
1349 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
1350 }
1351 if (!m_oldFont)
1352 m_oldFont = (WXHFONT) f;
1353 }
1354 }
1355
1356 void wxDC::SetPen(const wxPen& pen)
1357 {
1358 WXMICROWIN_CHECK_HDC
1359
1360 if ( pen == m_pen )
1361 return;
1362
1363 if ( pen.Ok() )
1364 {
1365 HGDIOBJ hpen = ::SelectObject(GetHdc(), GetHpenOf(pen));
1366 if ( hpen == HGDI_ERROR )
1367 {
1368 wxLogLastError(_T("SelectObject(pen)"));
1369 }
1370 else // selected ok
1371 {
1372 if ( !m_oldPen )
1373 m_oldPen = (WXHPEN)hpen;
1374
1375 m_pen = pen;
1376 }
1377 }
1378 else // invalid pen, reset the current pen
1379 {
1380 if ( m_oldPen )
1381 {
1382 if ( ::SelectObject(GetHdc(), (HPEN) m_oldPen) == HGDI_ERROR )
1383 {
1384 wxLogLastError(_T("SelectObject(old pen)"));
1385 }
1386
1387 m_oldPen = NULL;
1388 }
1389
1390 m_pen = wxNullPen;
1391 }
1392 }
1393
1394 void wxDC::SetBrush(const wxBrush& brush)
1395 {
1396 WXMICROWIN_CHECK_HDC
1397
1398 // Set the old object temporarily, in case the assignment deletes an object
1399 // that's not yet selected out.
1400 if (m_oldBrush)
1401 {
1402 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1403 m_oldBrush = 0;
1404 }
1405
1406 m_brush = brush;
1407
1408 if (!m_brush.Ok())
1409 {
1410 if (m_oldBrush)
1411 ::SelectObject(GetHdc(), (HBRUSH) m_oldBrush);
1412 m_oldBrush = 0;
1413 }
1414
1415 if (m_brush.Ok())
1416 {
1417 // to make sure the brush is alligned with the logical coordinates
1418 wxBitmap *stipple = m_brush.GetStipple();
1419 if ( stipple && stipple->Ok() )
1420 {
1421 #ifdef __WIN32__
1422 ::SetBrushOrgEx(GetHdc(),
1423 m_deviceOriginX % stipple->GetWidth(),
1424 m_deviceOriginY % stipple->GetHeight(),
1425 NULL); // don't need previous brush origin
1426 #else
1427 ::SetBrushOrg(GetHdc(),
1428 m_deviceOriginX % stipple->GetWidth(),
1429 m_deviceOriginY % stipple->GetHeight());
1430 #endif
1431 }
1432
1433 if ( m_brush.GetResourceHandle() )
1434 {
1435 HBRUSH b = (HBRUSH) ::SelectObject(GetHdc(), (HBRUSH)m_brush.GetResourceHandle());
1436 if (!m_oldBrush)
1437 m_oldBrush = (WXHBRUSH) b;
1438 }
1439 }
1440 }
1441
1442 void wxDC::SetBackground(const wxBrush& brush)
1443 {
1444 WXMICROWIN_CHECK_HDC
1445
1446 m_backgroundBrush = brush;
1447
1448 if ( m_backgroundBrush.Ok() )
1449 {
1450 (void)SetBkColor(GetHdc(), m_backgroundBrush.GetColour().GetPixel());
1451 }
1452 }
1453
1454 void wxDC::SetBackgroundMode(int mode)
1455 {
1456 WXMICROWIN_CHECK_HDC
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 WXMICROWIN_CHECK_HDC
1467
1468 m_logicalFunction = function;
1469
1470 SetRop(m_hDC);
1471 }
1472
1473 void wxDC::SetRop(WXHDC dc)
1474 {
1475 if ( !dc || m_logicalFunction < 0 )
1476 return;
1477
1478 int rop;
1479
1480 switch (m_logicalFunction)
1481 {
1482 case wxCLEAR: rop = R2_BLACK; break;
1483 case wxXOR: rop = R2_XORPEN; break;
1484 case wxINVERT: rop = R2_NOT; break;
1485 case wxOR_REVERSE: rop = R2_MERGEPENNOT; break;
1486 case wxAND_REVERSE: rop = R2_MASKPENNOT; break;
1487 case wxCOPY: rop = R2_COPYPEN; break;
1488 case wxAND: rop = R2_MASKPEN; break;
1489 case wxAND_INVERT: rop = R2_MASKNOTPEN; break;
1490 case wxNO_OP: rop = R2_NOP; break;
1491 case wxNOR: rop = R2_NOTMERGEPEN; break;
1492 case wxEQUIV: rop = R2_NOTXORPEN; break;
1493 case wxSRC_INVERT: rop = R2_NOTCOPYPEN; break;
1494 case wxOR_INVERT: rop = R2_MERGENOTPEN; break;
1495 case wxNAND: rop = R2_NOTMASKPEN; break;
1496 case wxOR: rop = R2_MERGEPEN; break;
1497 case wxSET: rop = R2_WHITE; break;
1498
1499 default:
1500 wxFAIL_MSG( wxT("unsupported logical function") );
1501 return;
1502 }
1503
1504 SetROP2(GetHdc(), rop);
1505 }
1506
1507 bool wxDC::StartDoc(const wxString& WXUNUSED(message))
1508 {
1509 // We might be previewing, so return TRUE to let it continue.
1510 return TRUE;
1511 }
1512
1513 void wxDC::EndDoc()
1514 {
1515 }
1516
1517 void wxDC::StartPage()
1518 {
1519 }
1520
1521 void wxDC::EndPage()
1522 {
1523 }
1524
1525 // ---------------------------------------------------------------------------
1526 // text metrics
1527 // ---------------------------------------------------------------------------
1528
1529 wxCoord wxDC::GetCharHeight() const
1530 {
1531 WXMICROWIN_CHECK_HDC_RET(0)
1532
1533 TEXTMETRIC lpTextMetric;
1534
1535 GetTextMetrics(GetHdc(), &lpTextMetric);
1536
1537 return lpTextMetric.tmHeight;
1538 }
1539
1540 wxCoord wxDC::GetCharWidth() const
1541 {
1542 WXMICROWIN_CHECK_HDC_RET(0)
1543
1544 TEXTMETRIC lpTextMetric;
1545
1546 GetTextMetrics(GetHdc(), &lpTextMetric);
1547
1548 return lpTextMetric.tmAveCharWidth;
1549 }
1550
1551 void wxDC::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
1552 wxCoord *descent, wxCoord *externalLeading,
1553 wxFont *font) const
1554 {
1555 #ifdef __WXMICROWIN__
1556 if (!GetHDC())
1557 {
1558 if (x) *x = 0;
1559 if (y) *y = 0;
1560 if (descent) *descent = 0;
1561 if (externalLeading) *externalLeading = 0;
1562 return;
1563 }
1564 #endif // __WXMICROWIN__
1565
1566 HFONT hfontOld;
1567 if ( font )
1568 {
1569 wxASSERT_MSG( font->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1570
1571 hfontOld = (HFONT)::SelectObject(GetHdc(), GetHfontOf(*font));
1572 }
1573 else // don't change the font
1574 {
1575 hfontOld = 0;
1576 }
1577
1578 SIZE sizeRect;
1579 TEXTMETRIC tm;
1580
1581 GetTextExtentPoint(GetHdc(), string, string.length(), &sizeRect);
1582 GetTextMetrics(GetHdc(), &tm);
1583
1584 if (x)
1585 *x = sizeRect.cx;
1586 if (y)
1587 *y = sizeRect.cy;
1588 if (descent)
1589 *descent = tm.tmDescent;
1590 if (externalLeading)
1591 *externalLeading = tm.tmExternalLeading;
1592
1593 if ( hfontOld )
1594 {
1595 ::SelectObject(GetHdc(), hfontOld);
1596 }
1597 }
1598
1599 void wxDC::SetMapMode(int mode)
1600 {
1601 WXMICROWIN_CHECK_HDC
1602
1603 m_mappingMode = mode;
1604
1605 if ( mode == wxMM_TEXT )
1606 {
1607 m_logicalScaleX =
1608 m_logicalScaleY = 1.0;
1609 }
1610 else // need to do some calculations
1611 {
1612 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1613 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1614 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1615 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1616
1617 if ( (mm_width == 0) || (mm_height == 0) )
1618 {
1619 // we can't calculate mm2pixels[XY] then!
1620 return;
1621 }
1622
1623 double mm2pixelsX = (double)pixel_width / mm_width,
1624 mm2pixelsY = (double)pixel_height / mm_height;
1625
1626 switch (mode)
1627 {
1628 case wxMM_TWIPS:
1629 m_logicalScaleX = twips2mm * mm2pixelsX;
1630 m_logicalScaleY = twips2mm * mm2pixelsY;
1631 break;
1632
1633 case wxMM_POINTS:
1634 m_logicalScaleX = pt2mm * mm2pixelsX;
1635 m_logicalScaleY = pt2mm * mm2pixelsY;
1636 break;
1637
1638 case wxMM_METRIC:
1639 m_logicalScaleX = mm2pixelsX;
1640 m_logicalScaleY = mm2pixelsY;
1641 break;
1642
1643 case wxMM_LOMETRIC:
1644 m_logicalScaleX = mm2pixelsX / 10.0;
1645 m_logicalScaleY = mm2pixelsY / 10.0;
1646 break;
1647
1648 default:
1649 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1650 }
1651 }
1652
1653 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1654 // cases we could do with MM_TEXT and in the remaining 0.9% with
1655 // MM_ISOTROPIC (TODO!)
1656 #ifndef __WXWINCE__
1657 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1658
1659 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1660 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1661
1662 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1663 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1664
1665 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1666 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1667 #endif
1668 }
1669
1670 void wxDC::SetUserScale(double x, double y)
1671 {
1672 WXMICROWIN_CHECK_HDC
1673
1674 #ifndef __WXWINCE__
1675 if ( x == m_userScaleX && y == m_userScaleY )
1676 return;
1677
1678 m_userScaleX = x;
1679 m_userScaleY = y;
1680
1681 SetMapMode(m_mappingMode);
1682 #endif
1683 }
1684
1685 void wxDC::SetAxisOrientation(bool xLeftRight, bool yBottomUp)
1686 {
1687 WXMICROWIN_CHECK_HDC
1688
1689 #ifndef __WXWINCE__
1690 int signX = xLeftRight ? 1 : -1,
1691 signY = yBottomUp ? -1 : 1;
1692
1693 if ( signX != m_signX || signY != m_signY )
1694 {
1695 m_signX = signX;
1696 m_signY = signY;
1697
1698 SetMapMode(m_mappingMode);
1699 }
1700 #endif
1701 }
1702
1703 void wxDC::SetSystemScale(double x, double y)
1704 {
1705 WXMICROWIN_CHECK_HDC
1706
1707 #ifndef __WXWINCE__
1708 if ( x == m_scaleX && y == m_scaleY )
1709 return;
1710
1711 m_scaleX = x;
1712 m_scaleY = y;
1713
1714 SetMapMode(m_mappingMode);
1715 #endif
1716 }
1717
1718 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1719 {
1720 WXMICROWIN_CHECK_HDC
1721
1722 #ifndef __WXWINCE__
1723 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1724 return;
1725
1726 m_logicalOriginX = x;
1727 m_logicalOriginY = y;
1728
1729 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1730 #endif
1731 }
1732
1733 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1734 {
1735 WXMICROWIN_CHECK_HDC
1736
1737 #ifndef __WXWINCE__
1738 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1739 return;
1740
1741 m_deviceOriginX = x;
1742 m_deviceOriginY = y;
1743
1744 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1745 #endif
1746 }
1747
1748 // ---------------------------------------------------------------------------
1749 // coordinates transformations
1750 // ---------------------------------------------------------------------------
1751
1752 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1753 {
1754 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
1755 }
1756
1757 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1758 {
1759 // axis orientation is not taken into account for conversion of a distance
1760 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
1761 }
1762
1763 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1764 {
1765 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
1766 }
1767
1768 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1769 {
1770 // axis orientation is not taken into account for conversion of a distance
1771 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
1772 }
1773
1774 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1775 {
1776 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
1777 }
1778
1779 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1780 {
1781 // axis orientation is not taken into account for conversion of a distance
1782 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
1783 }
1784
1785 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1786 {
1787 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
1788 }
1789
1790 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1791 {
1792 // axis orientation is not taken into account for conversion of a distance
1793 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
1794 }
1795
1796 // ---------------------------------------------------------------------------
1797 // bit blit
1798 // ---------------------------------------------------------------------------
1799
1800 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1801 wxCoord width, wxCoord height,
1802 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1803 int rop, bool useMask,
1804 wxCoord xsrcMask, wxCoord ysrcMask)
1805 {
1806 wxCHECK_MSG( source, FALSE, _T("wxDC::Blit(): NULL wxDC pointer") );
1807
1808 WXMICROWIN_CHECK_HDC_RET(FALSE)
1809
1810 const wxBitmap& bmpSrc = source->m_selectedBitmap;
1811 if ( bmpSrc.Ok() && bmpSrc.HasAlpha() )
1812 {
1813 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
1814 GetHdcOf(*source), bmpSrc) )
1815 return TRUE;
1816 }
1817
1818 wxMask *mask = NULL;
1819 if ( useMask )
1820 {
1821 mask = bmpSrc.GetMask();
1822
1823 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
1824 {
1825 // don't give assert here because this would break existing
1826 // programs - just silently ignore useMask parameter
1827 useMask = FALSE;
1828 }
1829 }
1830
1831 if (xsrcMask == -1 && ysrcMask == -1)
1832 {
1833 xsrcMask = xsrc; ysrcMask = ysrc;
1834 }
1835
1836 COLORREF old_textground = ::GetTextColor(GetHdc());
1837 COLORREF old_background = ::GetBkColor(GetHdc());
1838 if (m_textForegroundColour.Ok())
1839 {
1840 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1841 }
1842 if (m_textBackgroundColour.Ok())
1843 {
1844 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1845 }
1846
1847 DWORD dwRop;
1848 switch (rop)
1849 {
1850 case wxXOR: dwRop = SRCINVERT; break;
1851 case wxINVERT: dwRop = DSTINVERT; break;
1852 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1853 case wxAND_REVERSE: dwRop = SRCERASE; break;
1854 case wxCLEAR: dwRop = BLACKNESS; break;
1855 case wxSET: dwRop = WHITENESS; break;
1856 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1857 case wxAND: dwRop = SRCAND; break;
1858 case wxOR: dwRop = SRCPAINT; break;
1859 case wxEQUIV: dwRop = 0x00990066; break;
1860 case wxNAND: dwRop = 0x007700E6; break;
1861 case wxAND_INVERT: dwRop = 0x00220326; break;
1862 case wxCOPY: dwRop = SRCCOPY; break;
1863 case wxNO_OP: dwRop = DSTCOPY; break;
1864 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1865 case wxNOR: dwRop = NOTSRCCOPY; break;
1866 default:
1867 wxFAIL_MSG( wxT("unsupported logical function") );
1868 return FALSE;
1869 }
1870
1871 bool success = FALSE;
1872
1873 if (useMask)
1874 {
1875 #ifdef __WIN32__
1876 // we want the part of the image corresponding to the mask to be
1877 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1878 // meaning of fg and bg is inverted which corresponds to wxWin notion
1879 // of the mask which is also contrary to the Windows one)
1880
1881 // On some systems, MaskBlt succeeds yet is much much slower
1882 // than the wxWindows fall-back implementation. So we need
1883 // to be able to switch this on and off at runtime.
1884 #if wxUSE_SYSTEM_OPTIONS
1885 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1886 #endif
1887 {
1888 success = ::MaskBlt
1889 (
1890 GetHdc(),
1891 xdest, ydest, width, height,
1892 GetHdcOf(*source),
1893 xsrc, ysrc,
1894 (HBITMAP)mask->GetMaskBitmap(),
1895 xsrcMask, ysrcMask,
1896 MAKEROP4(dwRop, DSTCOPY)
1897 ) != 0;
1898 }
1899
1900 if ( !success )
1901 #endif // Win32
1902 {
1903 // Blit bitmap with mask
1904 HDC dc_mask ;
1905 HDC dc_buffer ;
1906 HBITMAP buffer_bmap ;
1907
1908 #if wxUSE_DC_CACHEING
1909 // create a temp buffer bitmap and DCs to access it and the mask
1910 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
1911 dc_mask = (HDC) dcCacheEntry1->m_dc;
1912
1913 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
1914 dc_buffer = (HDC) dcCacheEntry2->m_dc;
1915
1916 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
1917 width, height);
1918
1919 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
1920 #else // !wxUSE_DC_CACHEING
1921 // create a temp buffer bitmap and DCs to access it and the mask
1922 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
1923 dc_buffer = ::CreateCompatibleDC(GetHdc());
1924 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
1925 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1926 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
1927 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
1928
1929 // copy dest to buffer
1930 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1931 GetHdc(), xdest, ydest, SRCCOPY) )
1932 {
1933 wxLogLastError(wxT("BitBlt"));
1934 }
1935
1936 // copy src to buffer using selected raster op
1937 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1938 GetHdcOf(*source), xsrc, ysrc, dwRop) )
1939 {
1940 wxLogLastError(wxT("BitBlt"));
1941 }
1942
1943 // set masked area in buffer to BLACK (pixel value 0)
1944 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1945 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1946 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1947 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1948 {
1949 wxLogLastError(wxT("BitBlt"));
1950 }
1951
1952 // set unmasked area in dest to BLACK
1953 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1954 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1955 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
1956 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1957 {
1958 wxLogLastError(wxT("BitBlt"));
1959 }
1960 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
1961 ::SetTextColor(GetHdc(), prevCol);
1962
1963 // OR buffer to dest
1964 success = ::BitBlt(GetHdc(), xdest, ydest,
1965 (int)width, (int)height,
1966 dc_buffer, 0, 0, SRCPAINT) != 0;
1967 if ( !success )
1968 {
1969 wxLogLastError(wxT("BitBlt"));
1970 }
1971
1972 // tidy up temporary DCs and bitmap
1973 ::SelectObject(dc_mask, hOldMaskBitmap);
1974 ::SelectObject(dc_buffer, hOldBufferBitmap);
1975
1976 #if !wxUSE_DC_CACHEING
1977 {
1978 ::DeleteDC(dc_mask);
1979 ::DeleteDC(dc_buffer);
1980 ::DeleteObject(buffer_bmap);
1981 }
1982 #endif
1983 }
1984 }
1985 else // no mask, just BitBlt() it
1986 {
1987 // if we already have a DIB, draw it using StretchDIBits(), otherwise
1988 // use StretchBlt() if available and finally fall back to BitBlt()
1989
1990 // FIXME: use appropriate WinCE functions
1991 #ifndef __WXWINCE__
1992 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
1993 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
1994 {
1995 DIBSECTION ds;
1996 wxZeroMemory(ds);
1997
1998 if ( ::GetObject(GetHbitmapOf(bmpSrc),
1999 sizeof(ds),
2000 &ds) == sizeof(ds) )
2001 {
2002 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2003
2004 // Figure out what co-ordinate system we're supposed to specify
2005 // ysrc in.
2006 const LONG hDIB = ds.dsBmih.biHeight;
2007 if ( hDIB > 0 )
2008 {
2009 // reflect ysrc
2010 ysrc = hDIB - (ysrc + height);
2011 }
2012
2013 if ( ::StretchDIBits(GetHdc(),
2014 xdest, ydest,
2015 width, height,
2016 xsrc, ysrc,
2017 width, height,
2018 ds.dsBm.bmBits,
2019 (LPBITMAPINFO)&ds.dsBmih,
2020 DIB_RGB_COLORS,
2021 SRCCOPY
2022 ) == (int)GDI_ERROR )
2023 {
2024 wxLogLastError(wxT("StretchDIBits"));
2025 }
2026 else
2027 {
2028 success = TRUE;
2029 }
2030 }
2031 }
2032
2033 if ( !success && (caps & RC_STRETCHBLT) )
2034 #endif
2035 // __WXWINCE__
2036 {
2037 #ifndef __WXWINCE__
2038 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2039 #endif
2040
2041 if ( !::StretchBlt
2042 (
2043 GetHdc(),
2044 xdest, ydest, width, height,
2045 GetHdcOf(*source),
2046 xsrc, ysrc, width, height,
2047 dwRop
2048 ) )
2049 {
2050 wxLogLastError(_T("StretchBlt"));
2051 }
2052 else
2053 {
2054 success = TRUE;
2055 }
2056 }
2057
2058 if ( !success )
2059 {
2060 if ( !::BitBlt
2061 (
2062 GetHdc(),
2063 xdest, ydest,
2064 (int)width, (int)height,
2065 GetHdcOf(*source),
2066 xsrc, ysrc,
2067 dwRop
2068 ) )
2069 {
2070 wxLogLastError(_T("BitBlt"));
2071 }
2072 else
2073 {
2074 success = TRUE;
2075 }
2076 }
2077 }
2078
2079 ::SetTextColor(GetHdc(), old_textground);
2080 ::SetBkColor(GetHdc(), old_background);
2081
2082 return success;
2083 }
2084
2085 void wxDC::DoGetSize(int *w, int *h) const
2086 {
2087 WXMICROWIN_CHECK_HDC
2088
2089 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2090 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2091 }
2092
2093 void wxDC::DoGetSizeMM(int *w, int *h) const
2094 {
2095 WXMICROWIN_CHECK_HDC
2096
2097 // if we implement it in terms of DoGetSize() instead of directly using the
2098 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2099 // will also work for wxWindowDC and wxClientDC even though their size is
2100 // not the same as the total size of the screen
2101 int wPixels, hPixels;
2102 DoGetSize(&wPixels, &hPixels);
2103
2104 if ( w )
2105 {
2106 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2107
2108 wxCHECK_RET( wTotal, _T("0 width device?") );
2109
2110 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2111 }
2112
2113 if ( h )
2114 {
2115 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2116
2117 wxCHECK_RET( hTotal, _T("0 height device?") );
2118
2119 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2120 }
2121 }
2122
2123 wxSize wxDC::GetPPI() const
2124 {
2125 WXMICROWIN_CHECK_HDC_RET(wxSize())
2126
2127 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2128 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2129
2130 return wxSize(x, y);
2131 }
2132
2133 // For use by wxWindows only, unless custom units are required.
2134 void wxDC::SetLogicalScale(double x, double y)
2135 {
2136 WXMICROWIN_CHECK_HDC
2137
2138 m_logicalScaleX = x;
2139 m_logicalScaleY = y;
2140 }
2141
2142 // ----------------------------------------------------------------------------
2143 // DC caching
2144 // ----------------------------------------------------------------------------
2145
2146 #if wxUSE_DC_CACHEING
2147
2148 /*
2149 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2150 * improve it in due course, either using arrays, or simply storing pointers to one
2151 * entry for the bitmap, and two for the DCs. -- JACS
2152 */
2153
2154 wxList wxDC::sm_bitmapCache;
2155 wxList wxDC::sm_dcCache;
2156
2157 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2158 {
2159 m_bitmap = hBitmap;
2160 m_dc = 0;
2161 m_width = w;
2162 m_height = h;
2163 m_depth = depth;
2164 }
2165
2166 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2167 {
2168 m_bitmap = 0;
2169 m_dc = hDC;
2170 m_width = 0;
2171 m_height = 0;
2172 m_depth = depth;
2173 }
2174
2175 wxDCCacheEntry::~wxDCCacheEntry()
2176 {
2177 if (m_bitmap)
2178 ::DeleteObject((HBITMAP) m_bitmap);
2179 if (m_dc)
2180 ::DeleteDC((HDC) m_dc);
2181 }
2182
2183 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2184 {
2185 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2186 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2187 while (node)
2188 {
2189 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2190
2191 if (entry->m_depth == depth)
2192 {
2193 if (entry->m_width < w || entry->m_height < h)
2194 {
2195 ::DeleteObject((HBITMAP) entry->m_bitmap);
2196 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2197 if ( !entry->m_bitmap)
2198 {
2199 wxLogLastError(wxT("CreateCompatibleBitmap"));
2200 }
2201 entry->m_width = w; entry->m_height = h;
2202 return entry;
2203 }
2204 return entry;
2205 }
2206
2207 node = node->GetNext();
2208 }
2209 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2210 if ( !hBitmap)
2211 {
2212 wxLogLastError(wxT("CreateCompatibleBitmap"));
2213 }
2214 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2215 AddToBitmapCache(entry);
2216 return entry;
2217 }
2218
2219 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2220 {
2221 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2222 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2223 while (node)
2224 {
2225 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2226
2227 // Don't return the same one as we already have
2228 if (!notThis || (notThis != entry))
2229 {
2230 if (entry->m_depth == depth)
2231 {
2232 return entry;
2233 }
2234 }
2235
2236 node = node->GetNext();
2237 }
2238 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2239 if ( !hDC)
2240 {
2241 wxLogLastError(wxT("CreateCompatibleDC"));
2242 }
2243 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2244 AddToDCCache(entry);
2245 return entry;
2246 }
2247
2248 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2249 {
2250 sm_bitmapCache.Append(entry);
2251 }
2252
2253 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2254 {
2255 sm_dcCache.Append(entry);
2256 }
2257
2258 void wxDC::ClearCache()
2259 {
2260 WX_CLEAR_LIST(wxList, sm_dcCache);
2261 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2262 }
2263
2264 // Clean up cache at app exit
2265 class wxDCModule : public wxModule
2266 {
2267 public:
2268 virtual bool OnInit() { return TRUE; }
2269 virtual void OnExit() { wxDC::ClearCache(); }
2270
2271 private:
2272 DECLARE_DYNAMIC_CLASS(wxDCModule)
2273 };
2274
2275 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2276
2277 #endif // wxUSE_DC_CACHEING
2278
2279 // ----------------------------------------------------------------------------
2280 // alpha channel support
2281 // ----------------------------------------------------------------------------
2282
2283 static bool AlphaBlt(HDC hdcDst,
2284 int x, int y, int width, int height,
2285 HDC hdcSrc,
2286 const wxBitmap& bmp)
2287 {
2288 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2289 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2290
2291 // do we have AlphaBlend() and company in the headers?
2292 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2293 // yes, now try to see if we have it during run-time
2294 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2295 HDC,int,int,int,int,
2296 BLENDFUNCTION);
2297
2298 // bitmaps can be drawn only from GUI thread so there is no need to
2299 // protect this static variable from multiple threads
2300 static bool s_triedToLoad = FALSE;
2301 static AlphaBlend_t pfnAlphaBlend = NULL;
2302 if ( !s_triedToLoad )
2303 {
2304 s_triedToLoad = TRUE;
2305
2306 // don't give errors about the DLL being unavailable, we're
2307 // prepared to handle this
2308 wxLogNull nolog;
2309
2310 wxDynamicLibrary dll(_T("msimg32.dll"));
2311 if ( dll.IsLoaded() )
2312 {
2313 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2314 if ( pfnAlphaBlend )
2315 {
2316 // we must keep the DLL loaded if we want to be able to
2317 // call AlphaBlend() so just never unload it at all, not a
2318 // big deal
2319 dll.Detach();
2320 }
2321 }
2322 }
2323
2324 if ( pfnAlphaBlend )
2325 {
2326 BLENDFUNCTION bf;
2327 bf.BlendOp = AC_SRC_OVER;
2328 bf.BlendFlags = 0;
2329 bf.SourceConstantAlpha = 0xff;
2330 bf.AlphaFormat = AC_SRC_ALPHA;
2331
2332 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2333 hdcSrc, 0, 0, width, height,
2334 bf) )
2335 {
2336 // skip wxAlphaBlend() call below
2337 return TRUE;
2338 }
2339
2340 wxLogLastError(_T("AlphaBlend"));
2341 }
2342 #endif // defined(AC_SRC_OVER)
2343
2344 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2345 // implementation
2346 #ifdef wxHAVE_RAW_BITMAP
2347 wxAlphaBlend(hdcDst, x, y, width, height, bmp);
2348
2349 return TRUE;
2350 #else // !wxHAVE_RAW_BITMAP
2351 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2352 // alpha but at least something will be shown like this)
2353 return FALSE;
2354 #endif // wxHAVE_RAW_BITMAP
2355 }
2356
2357
2358 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2359 #ifdef wxHAVE_RAW_BITMAP
2360
2361 static void
2362 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, const wxBitmap& bmpSrc)
2363 {
2364 // get the destination DC pixels
2365 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2366 MemoryHDC hdcMem;
2367 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2368
2369 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2370 {
2371 wxLogLastError(_T("BitBlt"));
2372 }
2373
2374 // combine them with the source bitmap using alpha
2375 wxAlphaPixelData dataDst(bmpDst),
2376 dataSrc((wxBitmap &)bmpSrc);
2377
2378 wxCHECK_RET( dataDst && dataSrc,
2379 _T("failed to get raw data in wxAlphaBlend") );
2380
2381 wxAlphaPixelData::Iterator pDst(dataDst),
2382 pSrc(dataSrc);
2383
2384 for ( int y = 0; y < h; y++ )
2385 {
2386 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2387 pSrcRowStart = pSrc;
2388
2389 for ( int x = 0; x < w; x++ )
2390 {
2391 // note that source bitmap uses premultiplied alpha (as required by
2392 // the real AlphaBlend)
2393 const unsigned beta = 255 - pSrc.Alpha();
2394
2395 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2396 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2397 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2398
2399 ++pDst;
2400 ++pSrc;
2401 }
2402
2403 pDst = pDstRowStart;
2404 pSrc = pSrcRowStart;
2405 pDst.OffsetY(dataDst, 1);
2406 pSrc.OffsetY(dataSrc, 1);
2407 }
2408
2409 // and finally blit them back to the destination DC
2410 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2411 {
2412 wxLogLastError(_T("BitBlt"));
2413 }
2414 }
2415
2416 #endif // #ifdef wxHAVE_RAW_BITMAP