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