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