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