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