Added wxDC::GetPartialTextExtents
[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 = 0;
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 = 0;
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 = 0;
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
1615 // Each element of the array will be the width of the string up to and
1616 // including the coresoponding character in text.
1617
1618 bool wxDC::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
1619 {
1620 static int maxLenText = -1;
1621 static int maxWidth = -1;
1622 int fit = 0;
1623 SIZE sz = {0,0};
1624 int stlen = text.Length();
1625
1626 if (maxLenText == -1)
1627 {
1628 // Win9x and WinNT+ have different limits
1629 int version = wxGetOsVersion();
1630 maxLenText = version == wxWINDOWS_NT ? 65535 : 8192;
1631 maxWidth = version == wxWINDOWS_NT ? INT_MAX : 32767;
1632 }
1633
1634 widths.Empty();
1635 widths.Add(0, stlen); // fill the array with zeros
1636
1637 if (!::GetTextExtentExPoint(GetHdc(),
1638 text.c_str(), // string to check
1639 wxMin(stlen, maxLenText),
1640 maxWidth,
1641 &fit, // receives count of chars
1642 // that will fit
1643 widths.begin(), // array to fill
1644 &sz)) {
1645 // API failed
1646 wxLogLastError(wxT("GetTextExtentExPoint"));
1647 return false;
1648 }
1649 return true;
1650 }
1651
1652
1653
1654
1655 void wxDC::SetMapMode(int mode)
1656 {
1657 WXMICROWIN_CHECK_HDC
1658
1659 m_mappingMode = mode;
1660
1661 if ( mode == wxMM_TEXT )
1662 {
1663 m_logicalScaleX =
1664 m_logicalScaleY = 1.0;
1665 }
1666 else // need to do some calculations
1667 {
1668 int pixel_width = ::GetDeviceCaps(GetHdc(), HORZRES),
1669 pixel_height = ::GetDeviceCaps(GetHdc(), VERTRES),
1670 mm_width = ::GetDeviceCaps(GetHdc(), HORZSIZE),
1671 mm_height = ::GetDeviceCaps(GetHdc(), VERTSIZE);
1672
1673 if ( (mm_width == 0) || (mm_height == 0) )
1674 {
1675 // we can't calculate mm2pixels[XY] then!
1676 return;
1677 }
1678
1679 double mm2pixelsX = (double)pixel_width / mm_width,
1680 mm2pixelsY = (double)pixel_height / mm_height;
1681
1682 switch (mode)
1683 {
1684 case wxMM_TWIPS:
1685 m_logicalScaleX = twips2mm * mm2pixelsX;
1686 m_logicalScaleY = twips2mm * mm2pixelsY;
1687 break;
1688
1689 case wxMM_POINTS:
1690 m_logicalScaleX = pt2mm * mm2pixelsX;
1691 m_logicalScaleY = pt2mm * mm2pixelsY;
1692 break;
1693
1694 case wxMM_METRIC:
1695 m_logicalScaleX = mm2pixelsX;
1696 m_logicalScaleY = mm2pixelsY;
1697 break;
1698
1699 case wxMM_LOMETRIC:
1700 m_logicalScaleX = mm2pixelsX / 10.0;
1701 m_logicalScaleY = mm2pixelsY / 10.0;
1702 break;
1703
1704 default:
1705 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1706 }
1707 }
1708
1709 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1710 // cases we could do with MM_TEXT and in the remaining 0.9% with
1711 // MM_ISOTROPIC (TODO!)
1712 #ifndef __WXWINCE__
1713 ::SetMapMode(GetHdc(), MM_ANISOTROPIC);
1714
1715 int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
1716 height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
1717
1718 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
1719 ::SetWindowExtEx(GetHdc(), width, height, NULL);
1720
1721 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX, m_deviceOriginY, NULL);
1722 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX, m_logicalOriginY, NULL);
1723 #endif
1724 }
1725
1726 void wxDC::SetUserScale(double x, double y)
1727 {
1728 WXMICROWIN_CHECK_HDC
1729
1730 #ifndef __WXWINCE__
1731 if ( x == m_userScaleX && y == m_userScaleY )
1732 return;
1733
1734 m_userScaleX = x;
1735 m_userScaleY = y;
1736
1737 SetMapMode(m_mappingMode);
1738 #endif
1739 }
1740
1741 void wxDC::SetAxisOrientation(bool xLeftRight, bool yBottomUp)
1742 {
1743 WXMICROWIN_CHECK_HDC
1744
1745 #ifndef __WXWINCE__
1746 int signX = xLeftRight ? 1 : -1,
1747 signY = yBottomUp ? -1 : 1;
1748
1749 if ( signX != m_signX || signY != m_signY )
1750 {
1751 m_signX = signX;
1752 m_signY = signY;
1753
1754 SetMapMode(m_mappingMode);
1755 }
1756 #endif
1757 }
1758
1759 void wxDC::SetSystemScale(double x, double y)
1760 {
1761 WXMICROWIN_CHECK_HDC
1762
1763 #ifndef __WXWINCE__
1764 if ( x == m_scaleX && y == m_scaleY )
1765 return;
1766
1767 m_scaleX = x;
1768 m_scaleY = y;
1769
1770 SetMapMode(m_mappingMode);
1771 #endif
1772 }
1773
1774 void wxDC::SetLogicalOrigin(wxCoord x, wxCoord y)
1775 {
1776 WXMICROWIN_CHECK_HDC
1777
1778 #ifndef __WXWINCE__
1779 if ( x == m_logicalOriginX && y == m_logicalOriginY )
1780 return;
1781
1782 m_logicalOriginX = x;
1783 m_logicalOriginY = y;
1784
1785 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
1786 #endif
1787 }
1788
1789 void wxDC::SetDeviceOrigin(wxCoord x, wxCoord y)
1790 {
1791 WXMICROWIN_CHECK_HDC
1792
1793 #ifndef __WXWINCE__
1794 if ( x == m_deviceOriginX && y == m_deviceOriginY )
1795 return;
1796
1797 m_deviceOriginX = x;
1798 m_deviceOriginY = y;
1799
1800 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
1801 #endif
1802 }
1803
1804 // ---------------------------------------------------------------------------
1805 // coordinates transformations
1806 // ---------------------------------------------------------------------------
1807
1808 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
1809 {
1810 return DeviceToLogicalXRel(x - m_deviceOriginX)*m_signX + m_logicalOriginX;
1811 }
1812
1813 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
1814 {
1815 // axis orientation is not taken into account for conversion of a distance
1816 return (wxCoord)(x / (m_logicalScaleX*m_userScaleX*m_scaleX));
1817 }
1818
1819 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
1820 {
1821 return DeviceToLogicalYRel(y - m_deviceOriginY)*m_signY + m_logicalOriginY;
1822 }
1823
1824 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
1825 {
1826 // axis orientation is not taken into account for conversion of a distance
1827 return (wxCoord)( y / (m_logicalScaleY*m_userScaleY*m_scaleY));
1828 }
1829
1830 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
1831 {
1832 return LogicalToDeviceXRel(x - m_logicalOriginX)*m_signX + m_deviceOriginX;
1833 }
1834
1835 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
1836 {
1837 // axis orientation is not taken into account for conversion of a distance
1838 return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
1839 }
1840
1841 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
1842 {
1843 return LogicalToDeviceYRel(y - m_logicalOriginY)*m_signY + m_deviceOriginY;
1844 }
1845
1846 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
1847 {
1848 // axis orientation is not taken into account for conversion of a distance
1849 return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
1850 }
1851
1852 // ---------------------------------------------------------------------------
1853 // bit blit
1854 // ---------------------------------------------------------------------------
1855
1856 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest,
1857 wxCoord width, wxCoord height,
1858 wxDC *source, wxCoord xsrc, wxCoord ysrc,
1859 int rop, bool useMask,
1860 wxCoord xsrcMask, wxCoord ysrcMask)
1861 {
1862 wxCHECK_MSG( source, false, _T("wxDC::Blit(): NULL wxDC pointer") );
1863
1864 WXMICROWIN_CHECK_HDC_RET(false)
1865
1866 const wxBitmap& bmpSrc = source->m_selectedBitmap;
1867 if ( bmpSrc.Ok() && bmpSrc.HasAlpha() )
1868 {
1869 if ( AlphaBlt(GetHdc(), xdest, ydest, width, height,
1870 GetHdcOf(*source), bmpSrc) )
1871 return true;
1872 }
1873
1874 wxMask *mask = NULL;
1875 if ( useMask )
1876 {
1877 mask = bmpSrc.GetMask();
1878
1879 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
1880 {
1881 // don't give assert here because this would break existing
1882 // programs - just silently ignore useMask parameter
1883 useMask = false;
1884 }
1885 }
1886
1887 if (xsrcMask == -1 && ysrcMask == -1)
1888 {
1889 xsrcMask = xsrc; ysrcMask = ysrc;
1890 }
1891
1892 COLORREF old_textground = ::GetTextColor(GetHdc());
1893 COLORREF old_background = ::GetBkColor(GetHdc());
1894 if (m_textForegroundColour.Ok())
1895 {
1896 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1897 }
1898 if (m_textBackgroundColour.Ok())
1899 {
1900 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1901 }
1902
1903 DWORD dwRop;
1904 switch (rop)
1905 {
1906 case wxXOR: dwRop = SRCINVERT; break;
1907 case wxINVERT: dwRop = DSTINVERT; break;
1908 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1909 case wxAND_REVERSE: dwRop = SRCERASE; break;
1910 case wxCLEAR: dwRop = BLACKNESS; break;
1911 case wxSET: dwRop = WHITENESS; break;
1912 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1913 case wxAND: dwRop = SRCAND; break;
1914 case wxOR: dwRop = SRCPAINT; break;
1915 case wxEQUIV: dwRop = 0x00990066; break;
1916 case wxNAND: dwRop = 0x007700E6; break;
1917 case wxAND_INVERT: dwRop = 0x00220326; break;
1918 case wxCOPY: dwRop = SRCCOPY; break;
1919 case wxNO_OP: dwRop = DSTCOPY; break;
1920 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1921 case wxNOR: dwRop = NOTSRCCOPY; break;
1922 default:
1923 wxFAIL_MSG( wxT("unsupported logical function") );
1924 return false;
1925 }
1926
1927 bool success = false;
1928
1929 if (useMask)
1930 {
1931 #ifdef __WIN32__
1932 // we want the part of the image corresponding to the mask to be
1933 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1934 // meaning of fg and bg is inverted which corresponds to wxWin notion
1935 // of the mask which is also contrary to the Windows one)
1936
1937 // On some systems, MaskBlt succeeds yet is much much slower
1938 // than the wxWindows fall-back implementation. So we need
1939 // to be able to switch this on and off at runtime.
1940 #if wxUSE_SYSTEM_OPTIONS
1941 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1942 #endif
1943 {
1944 success = ::MaskBlt
1945 (
1946 GetHdc(),
1947 xdest, ydest, width, height,
1948 GetHdcOf(*source),
1949 xsrc, ysrc,
1950 (HBITMAP)mask->GetMaskBitmap(),
1951 xsrcMask, ysrcMask,
1952 MAKEROP4(dwRop, DSTCOPY)
1953 ) != 0;
1954 }
1955
1956 if ( !success )
1957 #endif // Win32
1958 {
1959 // Blit bitmap with mask
1960 HDC dc_mask ;
1961 HDC dc_buffer ;
1962 HBITMAP buffer_bmap ;
1963
1964 #if wxUSE_DC_CACHEING
1965 // create a temp buffer bitmap and DCs to access it and the mask
1966 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
1967 dc_mask = (HDC) dcCacheEntry1->m_dc;
1968
1969 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
1970 dc_buffer = (HDC) dcCacheEntry2->m_dc;
1971
1972 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
1973 width, height);
1974
1975 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
1976 #else // !wxUSE_DC_CACHEING
1977 // create a temp buffer bitmap and DCs to access it and the mask
1978 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
1979 dc_buffer = ::CreateCompatibleDC(GetHdc());
1980 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
1981 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1982 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
1983 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
1984
1985 // copy dest to buffer
1986 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1987 GetHdc(), xdest, ydest, SRCCOPY) )
1988 {
1989 wxLogLastError(wxT("BitBlt"));
1990 }
1991
1992 // copy src to buffer using selected raster op
1993 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1994 GetHdcOf(*source), xsrc, ysrc, dwRop) )
1995 {
1996 wxLogLastError(wxT("BitBlt"));
1997 }
1998
1999 // set masked area in buffer to BLACK (pixel value 0)
2000 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
2001 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
2002 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
2003 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2004 {
2005 wxLogLastError(wxT("BitBlt"));
2006 }
2007
2008 // set unmasked area in dest to BLACK
2009 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2010 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2011 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
2012 dc_mask, xsrcMask, ysrcMask, SRCAND) )
2013 {
2014 wxLogLastError(wxT("BitBlt"));
2015 }
2016 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
2017 ::SetTextColor(GetHdc(), prevCol);
2018
2019 // OR buffer to dest
2020 success = ::BitBlt(GetHdc(), xdest, ydest,
2021 (int)width, (int)height,
2022 dc_buffer, 0, 0, SRCPAINT) != 0;
2023 if ( !success )
2024 {
2025 wxLogLastError(wxT("BitBlt"));
2026 }
2027
2028 // tidy up temporary DCs and bitmap
2029 ::SelectObject(dc_mask, hOldMaskBitmap);
2030 ::SelectObject(dc_buffer, hOldBufferBitmap);
2031
2032 #if !wxUSE_DC_CACHEING
2033 {
2034 ::DeleteDC(dc_mask);
2035 ::DeleteDC(dc_buffer);
2036 ::DeleteObject(buffer_bmap);
2037 }
2038 #endif
2039 }
2040 }
2041 else // no mask, just BitBlt() it
2042 {
2043 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2044 // use StretchBlt() if available and finally fall back to BitBlt()
2045
2046 // FIXME: use appropriate WinCE functions
2047 #ifndef __WXWINCE__
2048 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2049 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
2050 {
2051 DIBSECTION ds;
2052 wxZeroMemory(ds);
2053
2054 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2055 sizeof(ds),
2056 &ds) == sizeof(ds) )
2057 {
2058 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2059
2060 // Figure out what co-ordinate system we're supposed to specify
2061 // ysrc in.
2062 const LONG hDIB = ds.dsBmih.biHeight;
2063 if ( hDIB > 0 )
2064 {
2065 // reflect ysrc
2066 ysrc = hDIB - (ysrc + height);
2067 }
2068
2069 if ( ::StretchDIBits(GetHdc(),
2070 xdest, ydest,
2071 width, height,
2072 xsrc, ysrc,
2073 width, height,
2074 ds.dsBm.bmBits,
2075 (LPBITMAPINFO)&ds.dsBmih,
2076 DIB_RGB_COLORS,
2077 SRCCOPY
2078 ) == (int)GDI_ERROR )
2079 {
2080 wxLogLastError(wxT("StretchDIBits"));
2081 }
2082 else
2083 {
2084 success = true;
2085 }
2086 }
2087 }
2088
2089 if ( !success && (caps & RC_STRETCHBLT) )
2090 #endif
2091 // __WXWINCE__
2092 {
2093 #ifndef __WXWINCE__
2094 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2095 #endif
2096
2097 if ( !::StretchBlt
2098 (
2099 GetHdc(),
2100 xdest, ydest, width, height,
2101 GetHdcOf(*source),
2102 xsrc, ysrc, width, height,
2103 dwRop
2104 ) )
2105 {
2106 wxLogLastError(_T("StretchBlt"));
2107 }
2108 else
2109 {
2110 success = true;
2111 }
2112 }
2113
2114 if ( !success )
2115 {
2116 if ( !::BitBlt
2117 (
2118 GetHdc(),
2119 xdest, ydest,
2120 (int)width, (int)height,
2121 GetHdcOf(*source),
2122 xsrc, ysrc,
2123 dwRop
2124 ) )
2125 {
2126 wxLogLastError(_T("BitBlt"));
2127 }
2128 else
2129 {
2130 success = true;
2131 }
2132 }
2133 }
2134
2135 ::SetTextColor(GetHdc(), old_textground);
2136 ::SetBkColor(GetHdc(), old_background);
2137
2138 return success;
2139 }
2140
2141 void wxDC::DoGetSize(int *w, int *h) const
2142 {
2143 WXMICROWIN_CHECK_HDC
2144
2145 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2146 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2147 }
2148
2149 void wxDC::DoGetSizeMM(int *w, int *h) const
2150 {
2151 WXMICROWIN_CHECK_HDC
2152
2153 // if we implement it in terms of DoGetSize() instead of directly using the
2154 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2155 // will also work for wxWindowDC and wxClientDC even though their size is
2156 // not the same as the total size of the screen
2157 int wPixels, hPixels;
2158 DoGetSize(&wPixels, &hPixels);
2159
2160 if ( w )
2161 {
2162 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2163
2164 wxCHECK_RET( wTotal, _T("0 width device?") );
2165
2166 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2167 }
2168
2169 if ( h )
2170 {
2171 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2172
2173 wxCHECK_RET( hTotal, _T("0 height device?") );
2174
2175 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2176 }
2177 }
2178
2179 wxSize wxDC::GetPPI() const
2180 {
2181 WXMICROWIN_CHECK_HDC_RET(wxSize())
2182
2183 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2184 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2185
2186 return wxSize(x, y);
2187 }
2188
2189 // For use by wxWindows only, unless custom units are required.
2190 void wxDC::SetLogicalScale(double x, double y)
2191 {
2192 WXMICROWIN_CHECK_HDC
2193
2194 m_logicalScaleX = x;
2195 m_logicalScaleY = y;
2196 }
2197
2198 // ----------------------------------------------------------------------------
2199 // DC caching
2200 // ----------------------------------------------------------------------------
2201
2202 #if wxUSE_DC_CACHEING
2203
2204 /*
2205 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2206 * improve it in due course, either using arrays, or simply storing pointers to one
2207 * entry for the bitmap, and two for the DCs. -- JACS
2208 */
2209
2210 wxList wxDC::sm_bitmapCache;
2211 wxList wxDC::sm_dcCache;
2212
2213 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2214 {
2215 m_bitmap = hBitmap;
2216 m_dc = 0;
2217 m_width = w;
2218 m_height = h;
2219 m_depth = depth;
2220 }
2221
2222 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2223 {
2224 m_bitmap = 0;
2225 m_dc = hDC;
2226 m_width = 0;
2227 m_height = 0;
2228 m_depth = depth;
2229 }
2230
2231 wxDCCacheEntry::~wxDCCacheEntry()
2232 {
2233 if (m_bitmap)
2234 ::DeleteObject((HBITMAP) m_bitmap);
2235 if (m_dc)
2236 ::DeleteDC((HDC) m_dc);
2237 }
2238
2239 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2240 {
2241 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2242 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2243 while (node)
2244 {
2245 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2246
2247 if (entry->m_depth == depth)
2248 {
2249 if (entry->m_width < w || entry->m_height < h)
2250 {
2251 ::DeleteObject((HBITMAP) entry->m_bitmap);
2252 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2253 if ( !entry->m_bitmap)
2254 {
2255 wxLogLastError(wxT("CreateCompatibleBitmap"));
2256 }
2257 entry->m_width = w; entry->m_height = h;
2258 return entry;
2259 }
2260 return entry;
2261 }
2262
2263 node = node->GetNext();
2264 }
2265 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2266 if ( !hBitmap)
2267 {
2268 wxLogLastError(wxT("CreateCompatibleBitmap"));
2269 }
2270 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2271 AddToBitmapCache(entry);
2272 return entry;
2273 }
2274
2275 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2276 {
2277 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2278 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2279 while (node)
2280 {
2281 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2282
2283 // Don't return the same one as we already have
2284 if (!notThis || (notThis != entry))
2285 {
2286 if (entry->m_depth == depth)
2287 {
2288 return entry;
2289 }
2290 }
2291
2292 node = node->GetNext();
2293 }
2294 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2295 if ( !hDC)
2296 {
2297 wxLogLastError(wxT("CreateCompatibleDC"));
2298 }
2299 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2300 AddToDCCache(entry);
2301 return entry;
2302 }
2303
2304 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2305 {
2306 sm_bitmapCache.Append(entry);
2307 }
2308
2309 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2310 {
2311 sm_dcCache.Append(entry);
2312 }
2313
2314 void wxDC::ClearCache()
2315 {
2316 WX_CLEAR_LIST(wxList, sm_dcCache);
2317 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2318 }
2319
2320 // Clean up cache at app exit
2321 class wxDCModule : public wxModule
2322 {
2323 public:
2324 virtual bool OnInit() { return true; }
2325 virtual void OnExit() { wxDC::ClearCache(); }
2326
2327 private:
2328 DECLARE_DYNAMIC_CLASS(wxDCModule)
2329 };
2330
2331 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2332
2333 #endif // wxUSE_DC_CACHEING
2334
2335 // ----------------------------------------------------------------------------
2336 // alpha channel support
2337 // ----------------------------------------------------------------------------
2338
2339 static bool AlphaBlt(HDC hdcDst,
2340 int x, int y, int width, int height,
2341 HDC hdcSrc,
2342 const wxBitmap& bmp)
2343 {
2344 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2345 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2346
2347 // do we have AlphaBlend() and company in the headers?
2348 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2349 // yes, now try to see if we have it during run-time
2350 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2351 HDC,int,int,int,int,
2352 BLENDFUNCTION);
2353
2354 // bitmaps can be drawn only from GUI thread so there is no need to
2355 // protect this static variable from multiple threads
2356 static bool s_triedToLoad = false;
2357 static AlphaBlend_t pfnAlphaBlend = NULL;
2358 if ( !s_triedToLoad )
2359 {
2360 s_triedToLoad = true;
2361
2362 // don't give errors about the DLL being unavailable, we're
2363 // prepared to handle this
2364 wxLogNull nolog;
2365
2366 wxDynamicLibrary dll(_T("msimg32.dll"));
2367 if ( dll.IsLoaded() )
2368 {
2369 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2370 if ( pfnAlphaBlend )
2371 {
2372 // we must keep the DLL loaded if we want to be able to
2373 // call AlphaBlend() so just never unload it at all, not a
2374 // big deal
2375 dll.Detach();
2376 }
2377 }
2378 }
2379
2380 if ( pfnAlphaBlend )
2381 {
2382 BLENDFUNCTION bf;
2383 bf.BlendOp = AC_SRC_OVER;
2384 bf.BlendFlags = 0;
2385 bf.SourceConstantAlpha = 0xff;
2386 bf.AlphaFormat = AC_SRC_ALPHA;
2387
2388 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2389 hdcSrc, 0, 0, width, height,
2390 bf) )
2391 {
2392 // skip wxAlphaBlend() call below
2393 return true;
2394 }
2395
2396 wxLogLastError(_T("AlphaBlend"));
2397 }
2398 #endif // defined(AC_SRC_OVER)
2399
2400 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2401 // implementation
2402 #ifdef wxHAVE_RAW_BITMAP
2403 wxAlphaBlend(hdcDst, x, y, width, height, bmp);
2404
2405 return true;
2406 #else // !wxHAVE_RAW_BITMAP
2407 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2408 // alpha but at least something will be shown like this)
2409 return false;
2410 #endif // wxHAVE_RAW_BITMAP
2411 }
2412
2413
2414 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2415 #ifdef wxHAVE_RAW_BITMAP
2416
2417 static void
2418 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, const wxBitmap& bmpSrc)
2419 {
2420 // get the destination DC pixels
2421 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2422 MemoryHDC hdcMem;
2423 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2424
2425 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2426 {
2427 wxLogLastError(_T("BitBlt"));
2428 }
2429
2430 // combine them with the source bitmap using alpha
2431 wxAlphaPixelData dataDst(bmpDst),
2432 dataSrc((wxBitmap &)bmpSrc);
2433
2434 wxCHECK_RET( dataDst && dataSrc,
2435 _T("failed to get raw data in wxAlphaBlend") );
2436
2437 wxAlphaPixelData::Iterator pDst(dataDst),
2438 pSrc(dataSrc);
2439
2440 for ( int y = 0; y < h; y++ )
2441 {
2442 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2443 pSrcRowStart = pSrc;
2444
2445 for ( int x = 0; x < w; x++ )
2446 {
2447 // note that source bitmap uses premultiplied alpha (as required by
2448 // the real AlphaBlend)
2449 const unsigned beta = 255 - pSrc.Alpha();
2450
2451 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2452 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2453 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2454
2455 ++pDst;
2456 ++pSrc;
2457 }
2458
2459 pDst = pDstRowStart;
2460 pSrc = pSrcRowStart;
2461 pDst.OffsetY(dataDst, 1);
2462 pSrc.OffsetY(dataSrc, 1);
2463 }
2464
2465 // and finally blit them back to the destination DC
2466 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2467 {
2468 wxLogLastError(_T("BitBlt"));
2469 }
2470 }
2471
2472 #endif // #ifdef wxHAVE_RAW_BITMAP