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