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