removed Win16 code (patch 905241), last version with remains of Win16 support tagged...
[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(__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 // !WinCE
409 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN)hrgn, RGN_AND) == ERROR )
410 {
411 wxLogLastError(_T("ExtSelectClipRgn"));
412
413 return;
414 }
415 #endif // WinCE/!WinCE
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 // Symantec-MicroWin
703 // 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/Symantec-MicroWin
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, count, 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 // On Win9x this API fails most (all?) of the time, so
2097 // logging it becomes quite distracting. Since it falls
2098 // back to the code below this is not really serious, so
2099 // don't log it.
2100 //wxLogLastError(wxT("StretchDIBits"));
2101 }
2102 else
2103 {
2104 success = true;
2105 }
2106 }
2107 }
2108
2109 if ( !success && (caps & RC_STRETCHBLT) )
2110 #endif
2111 // __WXWINCE__
2112 {
2113 #ifndef __WXWINCE__
2114 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2115 #endif
2116
2117 if ( !::StretchBlt
2118 (
2119 GetHdc(),
2120 xdest, ydest, width, height,
2121 GetHdcOf(*source),
2122 xsrc, ysrc, width, height,
2123 dwRop
2124 ) )
2125 {
2126 wxLogLastError(_T("StretchBlt"));
2127 }
2128 else
2129 {
2130 success = true;
2131 }
2132 }
2133
2134 if ( !success )
2135 {
2136 if ( !::BitBlt
2137 (
2138 GetHdc(),
2139 xdest, ydest,
2140 (int)width, (int)height,
2141 GetHdcOf(*source),
2142 xsrc, ysrc,
2143 dwRop
2144 ) )
2145 {
2146 wxLogLastError(_T("BitBlt"));
2147 }
2148 else
2149 {
2150 success = true;
2151 }
2152 }
2153 }
2154
2155 ::SetTextColor(GetHdc(), old_textground);
2156 ::SetBkColor(GetHdc(), old_background);
2157
2158 return success;
2159 }
2160
2161 void wxDC::DoGetSize(int *w, int *h) const
2162 {
2163 WXMICROWIN_CHECK_HDC
2164
2165 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2166 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2167 }
2168
2169 void wxDC::DoGetSizeMM(int *w, int *h) const
2170 {
2171 WXMICROWIN_CHECK_HDC
2172
2173 // if we implement it in terms of DoGetSize() instead of directly using the
2174 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2175 // will also work for wxWindowDC and wxClientDC even though their size is
2176 // not the same as the total size of the screen
2177 int wPixels, hPixels;
2178 DoGetSize(&wPixels, &hPixels);
2179
2180 if ( w )
2181 {
2182 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2183
2184 wxCHECK_RET( wTotal, _T("0 width device?") );
2185
2186 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2187 }
2188
2189 if ( h )
2190 {
2191 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2192
2193 wxCHECK_RET( hTotal, _T("0 height device?") );
2194
2195 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2196 }
2197 }
2198
2199 wxSize wxDC::GetPPI() const
2200 {
2201 WXMICROWIN_CHECK_HDC_RET(wxSize())
2202
2203 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2204 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2205
2206 return wxSize(x, y);
2207 }
2208
2209 // For use by wxWindows only, unless custom units are required.
2210 void wxDC::SetLogicalScale(double x, double y)
2211 {
2212 WXMICROWIN_CHECK_HDC
2213
2214 m_logicalScaleX = x;
2215 m_logicalScaleY = y;
2216 }
2217
2218 // ----------------------------------------------------------------------------
2219 // DC caching
2220 // ----------------------------------------------------------------------------
2221
2222 #if wxUSE_DC_CACHEING
2223
2224 /*
2225 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2226 * improve it in due course, either using arrays, or simply storing pointers to one
2227 * entry for the bitmap, and two for the DCs. -- JACS
2228 */
2229
2230 wxList wxDC::sm_bitmapCache;
2231 wxList wxDC::sm_dcCache;
2232
2233 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2234 {
2235 m_bitmap = hBitmap;
2236 m_dc = 0;
2237 m_width = w;
2238 m_height = h;
2239 m_depth = depth;
2240 }
2241
2242 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2243 {
2244 m_bitmap = 0;
2245 m_dc = hDC;
2246 m_width = 0;
2247 m_height = 0;
2248 m_depth = depth;
2249 }
2250
2251 wxDCCacheEntry::~wxDCCacheEntry()
2252 {
2253 if (m_bitmap)
2254 ::DeleteObject((HBITMAP) m_bitmap);
2255 if (m_dc)
2256 ::DeleteDC((HDC) m_dc);
2257 }
2258
2259 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2260 {
2261 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2262 wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
2263 while (node)
2264 {
2265 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2266
2267 if (entry->m_depth == depth)
2268 {
2269 if (entry->m_width < w || entry->m_height < h)
2270 {
2271 ::DeleteObject((HBITMAP) entry->m_bitmap);
2272 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2273 if ( !entry->m_bitmap)
2274 {
2275 wxLogLastError(wxT("CreateCompatibleBitmap"));
2276 }
2277 entry->m_width = w; entry->m_height = h;
2278 return entry;
2279 }
2280 return entry;
2281 }
2282
2283 node = node->GetNext();
2284 }
2285 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2286 if ( !hBitmap)
2287 {
2288 wxLogLastError(wxT("CreateCompatibleBitmap"));
2289 }
2290 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2291 AddToBitmapCache(entry);
2292 return entry;
2293 }
2294
2295 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2296 {
2297 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2298 wxList::compatibility_iterator node = sm_dcCache.GetFirst();
2299 while (node)
2300 {
2301 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
2302
2303 // Don't return the same one as we already have
2304 if (!notThis || (notThis != entry))
2305 {
2306 if (entry->m_depth == depth)
2307 {
2308 return entry;
2309 }
2310 }
2311
2312 node = node->GetNext();
2313 }
2314 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2315 if ( !hDC)
2316 {
2317 wxLogLastError(wxT("CreateCompatibleDC"));
2318 }
2319 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2320 AddToDCCache(entry);
2321 return entry;
2322 }
2323
2324 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2325 {
2326 sm_bitmapCache.Append(entry);
2327 }
2328
2329 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2330 {
2331 sm_dcCache.Append(entry);
2332 }
2333
2334 void wxDC::ClearCache()
2335 {
2336 WX_CLEAR_LIST(wxList, sm_dcCache);
2337 WX_CLEAR_LIST(wxList, sm_bitmapCache);
2338 }
2339
2340 // Clean up cache at app exit
2341 class wxDCModule : public wxModule
2342 {
2343 public:
2344 virtual bool OnInit() { return true; }
2345 virtual void OnExit() { wxDC::ClearCache(); }
2346
2347 private:
2348 DECLARE_DYNAMIC_CLASS(wxDCModule)
2349 };
2350
2351 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2352
2353 #endif // wxUSE_DC_CACHEING
2354
2355 // ----------------------------------------------------------------------------
2356 // alpha channel support
2357 // ----------------------------------------------------------------------------
2358
2359 static bool AlphaBlt(HDC hdcDst,
2360 int x, int y, int width, int height,
2361 HDC hdcSrc,
2362 const wxBitmap& bmp)
2363 {
2364 wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2365 wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
2366
2367 // do we have AlphaBlend() and company in the headers?
2368 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2369 // yes, now try to see if we have it during run-time
2370 typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
2371 HDC,int,int,int,int,
2372 BLENDFUNCTION);
2373
2374 // bitmaps can be drawn only from GUI thread so there is no need to
2375 // protect this static variable from multiple threads
2376 static bool s_triedToLoad = false;
2377 static AlphaBlend_t pfnAlphaBlend = NULL;
2378 if ( !s_triedToLoad )
2379 {
2380 s_triedToLoad = true;
2381
2382 // don't give errors about the DLL being unavailable, we're
2383 // prepared to handle this
2384 wxLogNull nolog;
2385
2386 wxDynamicLibrary dll(_T("msimg32.dll"));
2387 if ( dll.IsLoaded() )
2388 {
2389 pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
2390 if ( pfnAlphaBlend )
2391 {
2392 // we must keep the DLL loaded if we want to be able to
2393 // call AlphaBlend() so just never unload it at all, not a
2394 // big deal
2395 dll.Detach();
2396 }
2397 }
2398 }
2399
2400 if ( pfnAlphaBlend )
2401 {
2402 BLENDFUNCTION bf;
2403 bf.BlendOp = AC_SRC_OVER;
2404 bf.BlendFlags = 0;
2405 bf.SourceConstantAlpha = 0xff;
2406 bf.AlphaFormat = AC_SRC_ALPHA;
2407
2408 if ( pfnAlphaBlend(hdcDst, x, y, width, height,
2409 hdcSrc, 0, 0, width, height,
2410 bf) )
2411 {
2412 // skip wxAlphaBlend() call below
2413 return true;
2414 }
2415
2416 wxLogLastError(_T("AlphaBlend"));
2417 }
2418 #endif // defined(AC_SRC_OVER)
2419
2420 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2421 // implementation
2422 #ifdef wxHAVE_RAW_BITMAP
2423 wxAlphaBlend(hdcDst, x, y, width, height, bmp);
2424
2425 return true;
2426 #else // !wxHAVE_RAW_BITMAP
2427 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2428 // alpha but at least something will be shown like this)
2429 return false;
2430 #endif // wxHAVE_RAW_BITMAP
2431 }
2432
2433
2434 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2435 #ifdef wxHAVE_RAW_BITMAP
2436
2437 static void
2438 wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, const wxBitmap& bmpSrc)
2439 {
2440 // get the destination DC pixels
2441 wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
2442 MemoryHDC hdcMem;
2443 SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
2444
2445 if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
2446 {
2447 wxLogLastError(_T("BitBlt"));
2448 }
2449
2450 // combine them with the source bitmap using alpha
2451 wxAlphaPixelData dataDst(bmpDst),
2452 dataSrc((wxBitmap &)bmpSrc);
2453
2454 wxCHECK_RET( dataDst && dataSrc,
2455 _T("failed to get raw data in wxAlphaBlend") );
2456
2457 wxAlphaPixelData::Iterator pDst(dataDst),
2458 pSrc(dataSrc);
2459
2460 for ( int y = 0; y < h; y++ )
2461 {
2462 wxAlphaPixelData::Iterator pDstRowStart = pDst,
2463 pSrcRowStart = pSrc;
2464
2465 for ( int x = 0; x < w; x++ )
2466 {
2467 // note that source bitmap uses premultiplied alpha (as required by
2468 // the real AlphaBlend)
2469 const unsigned beta = 255 - pSrc.Alpha();
2470
2471 pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
2472 pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
2473 pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
2474
2475 ++pDst;
2476 ++pSrc;
2477 }
2478
2479 pDst = pDstRowStart;
2480 pSrc = pSrcRowStart;
2481 pDst.OffsetY(dataDst, 1);
2482 pSrc.OffsetY(dataSrc, 1);
2483 }
2484
2485 // and finally blit them back to the destination DC
2486 if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
2487 {
2488 wxLogLastError(_T("BitBlt"));
2489 }
2490 }
2491
2492 #endif // #ifdef wxHAVE_RAW_BITMAP