use StretchDIBits() if we're drawing a DIB and not a DDB in Blit()
[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 const wxBitmap& bmpSrc = source->m_selectedBitmap;
1832
1833 wxMask *mask = NULL;
1834 if ( useMask )
1835 {
1836 mask = bmpSrc.GetMask();
1837
1838 if ( !(bmpSrc.Ok() && mask && mask->GetMaskBitmap()) )
1839 {
1840 // don't give assert here because this would break existing
1841 // programs - just silently ignore useMask parameter
1842 useMask = FALSE;
1843 }
1844 }
1845
1846 if (xsrcMask == -1 && ysrcMask == -1)
1847 {
1848 xsrcMask = xsrc; ysrcMask = ysrc;
1849 }
1850
1851 COLORREF old_textground = ::GetTextColor(GetHdc());
1852 COLORREF old_background = ::GetBkColor(GetHdc());
1853 if (m_textForegroundColour.Ok())
1854 {
1855 ::SetTextColor(GetHdc(), m_textForegroundColour.GetPixel() );
1856 }
1857 if (m_textBackgroundColour.Ok())
1858 {
1859 ::SetBkColor(GetHdc(), m_textBackgroundColour.GetPixel() );
1860 }
1861
1862 DWORD dwRop = SRCCOPY;
1863 switch (rop)
1864 {
1865 case wxXOR: dwRop = SRCINVERT; break;
1866 case wxINVERT: dwRop = DSTINVERT; break;
1867 case wxOR_REVERSE: dwRop = 0x00DD0228; break;
1868 case wxAND_REVERSE: dwRop = SRCERASE; break;
1869 case wxCLEAR: dwRop = BLACKNESS; break;
1870 case wxSET: dwRop = WHITENESS; break;
1871 case wxOR_INVERT: dwRop = MERGEPAINT; break;
1872 case wxAND: dwRop = SRCAND; break;
1873 case wxOR: dwRop = SRCPAINT; break;
1874 case wxEQUIV: dwRop = 0x00990066; break;
1875 case wxNAND: dwRop = 0x007700E6; break;
1876 case wxAND_INVERT: dwRop = 0x00220326; break;
1877 case wxCOPY: dwRop = SRCCOPY; break;
1878 case wxNO_OP: dwRop = DSTCOPY; break;
1879 case wxSRC_INVERT: dwRop = NOTSRCCOPY; break;
1880 case wxNOR: dwRop = NOTSRCCOPY; break;
1881 default:
1882 wxFAIL_MSG( wxT("unsupported logical function") );
1883 return FALSE;
1884 }
1885
1886 bool success = FALSE;
1887
1888 if (useMask)
1889 {
1890 #ifdef __WIN32__
1891 // we want the part of the image corresponding to the mask to be
1892 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1893 // meaning of fg and bg is inverted which corresponds to wxWin notion
1894 // of the mask which is also contrary to the Windows one)
1895
1896 // On some systems, MaskBlt succeeds yet is much much slower
1897 // than the wxWindows fall-back implementation. So we need
1898 // to be able to switch this on and off at runtime.
1899 #if wxUSE_SYSTEM_OPTIONS
1900 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1901 #endif
1902 {
1903 success = ::MaskBlt
1904 (
1905 GetHdc(),
1906 xdest, ydest, width, height,
1907 GetHdcOf(*source),
1908 xsrc, ysrc,
1909 (HBITMAP)mask->GetMaskBitmap(),
1910 xsrcMask, ysrcMask,
1911 MAKEROP4(dwRop, DSTCOPY)
1912 ) != 0;
1913 }
1914
1915 if ( !success )
1916 #endif // Win32
1917 {
1918 // Blit bitmap with mask
1919 HDC dc_mask ;
1920 HDC dc_buffer ;
1921 HBITMAP buffer_bmap ;
1922
1923 #if wxUSE_DC_CACHEING
1924 // create a temp buffer bitmap and DCs to access it and the mask
1925 wxDCCacheEntry* dcCacheEntry1 = FindDCInCache(NULL, source->GetHDC());
1926 dc_mask = (HDC) dcCacheEntry1->m_dc;
1927
1928 wxDCCacheEntry* dcCacheEntry2 = FindDCInCache(dcCacheEntry1, GetHDC());
1929 dc_buffer = (HDC) dcCacheEntry2->m_dc;
1930
1931 wxDCCacheEntry* bitmapCacheEntry = FindBitmapInCache(GetHDC(),
1932 width, height);
1933
1934 buffer_bmap = (HBITMAP) bitmapCacheEntry->m_bitmap;
1935 #else // !wxUSE_DC_CACHEING
1936 // create a temp buffer bitmap and DCs to access it and the mask
1937 dc_mask = ::CreateCompatibleDC(GetHdcOf(*source));
1938 dc_buffer = ::CreateCompatibleDC(GetHdc());
1939 buffer_bmap = ::CreateCompatibleBitmap(GetHdc(), width, height);
1940 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1941 HGDIOBJ hOldMaskBitmap = ::SelectObject(dc_mask, (HBITMAP) mask->GetMaskBitmap());
1942 HGDIOBJ hOldBufferBitmap = ::SelectObject(dc_buffer, buffer_bmap);
1943
1944 // copy dest to buffer
1945 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1946 GetHdc(), xdest, ydest, SRCCOPY) )
1947 {
1948 wxLogLastError(wxT("BitBlt"));
1949 }
1950
1951 // copy src to buffer using selected raster op
1952 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1953 GetHdcOf(*source), xsrc, ysrc, dwRop) )
1954 {
1955 wxLogLastError(wxT("BitBlt"));
1956 }
1957
1958 // set masked area in buffer to BLACK (pixel value 0)
1959 COLORREF prevBkCol = ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1960 COLORREF prevCol = ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1961 if ( !::BitBlt(dc_buffer, 0, 0, (int)width, (int)height,
1962 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1963 {
1964 wxLogLastError(wxT("BitBlt"));
1965 }
1966
1967 // set unmasked area in dest to BLACK
1968 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1969 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1970 if ( !::BitBlt(GetHdc(), xdest, ydest, (int)width, (int)height,
1971 dc_mask, xsrcMask, ysrcMask, SRCAND) )
1972 {
1973 wxLogLastError(wxT("BitBlt"));
1974 }
1975 ::SetBkColor(GetHdc(), prevBkCol); // restore colours to original values
1976 ::SetTextColor(GetHdc(), prevCol);
1977
1978 // OR buffer to dest
1979 success = ::BitBlt(GetHdc(), xdest, ydest,
1980 (int)width, (int)height,
1981 dc_buffer, 0, 0, SRCPAINT) != 0;
1982 if ( !success )
1983 {
1984 wxLogLastError(wxT("BitBlt"));
1985 }
1986
1987 // tidy up temporary DCs and bitmap
1988 ::SelectObject(dc_mask, hOldMaskBitmap);
1989 ::SelectObject(dc_buffer, hOldBufferBitmap);
1990
1991 #if !wxUSE_DC_CACHEING
1992 {
1993 ::DeleteDC(dc_mask);
1994 ::DeleteDC(dc_buffer);
1995 ::DeleteObject(buffer_bmap);
1996 }
1997 #endif
1998 }
1999 }
2000 else // no mask, just BitBlt() it
2001 {
2002 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2003 // use StretchBlt() if available and finally fall back to BitBlt()
2004 const int caps = ::GetDeviceCaps(GetHdc(), RASTERCAPS);
2005 if ( bmpSrc.Ok() && (caps & RC_STRETCHDIB) )
2006 {
2007 DIBSECTION ds;
2008 wxZeroMemory(ds);
2009
2010 if ( ::GetObject(GetHbitmapOf(bmpSrc),
2011 sizeof(ds),
2012 &ds) == sizeof(ds) )
2013 {
2014 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2015
2016 if ( ::StretchDIBits(GetHdc(),
2017 xdest, ydest,
2018 width, height,
2019 0, 0,
2020 width, height,
2021 ds.dsBm.bmBits,
2022 (LPBITMAPINFO)&ds.dsBmih,
2023 DIB_RGB_COLORS,
2024 SRCCOPY
2025 ) == (int)GDI_ERROR )
2026 {
2027 wxLogLastError(wxT("StretchDIBits"));
2028 }
2029 else
2030 {
2031 success = TRUE;
2032 }
2033 }
2034 }
2035
2036 if ( !success && (caps & RC_STRETCHBLT) )
2037 {
2038 StretchBltModeChanger changeMode(GetHdc(), COLORONCOLOR);
2039
2040 if ( !::StretchBlt
2041 (
2042 GetHdc(),
2043 xdest, ydest, width, height,
2044 GetHdcOf(*source),
2045 xsrc, ysrc, width, height,
2046 dwRop
2047 ) )
2048 {
2049 wxLogLastError(_T("StretchBlt"));
2050 }
2051 else
2052 {
2053 success = TRUE;
2054 }
2055 }
2056
2057 if ( !success )
2058 {
2059 if ( !::BitBlt
2060 (
2061 GetHdc(),
2062 xdest, ydest,
2063 (int)width, (int)height,
2064 GetHdcOf(*source),
2065 xsrc, ysrc,
2066 dwRop
2067 ) )
2068 {
2069 wxLogLastError(_T("BitBlt"));
2070 }
2071 else
2072 {
2073 success = TRUE;
2074 }
2075 }
2076 }
2077
2078 ::SetTextColor(GetHdc(), old_textground);
2079 ::SetBkColor(GetHdc(), old_background);
2080
2081 return success;
2082 }
2083
2084 void wxDC::DoGetSize(int *w, int *h) const
2085 {
2086 #ifdef __WXMICROWIN__
2087 if (!GetHDC()) return;
2088 #endif
2089
2090 if ( w ) *w = ::GetDeviceCaps(GetHdc(), HORZRES);
2091 if ( h ) *h = ::GetDeviceCaps(GetHdc(), VERTRES);
2092 }
2093
2094 void wxDC::DoGetSizeMM(int *w, int *h) const
2095 {
2096 #ifdef __WXMICROWIN__
2097 if (!GetHDC()) return;
2098 #endif
2099
2100 // if we implement it in terms of DoGetSize() instead of directly using the
2101 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2102 // will also work for wxWindowDC and wxClientDC even though their size is
2103 // not the same as the total size of the screen
2104 int wPixels, hPixels;
2105 DoGetSize(&wPixels, &hPixels);
2106
2107 if ( w )
2108 {
2109 int wTotal = ::GetDeviceCaps(GetHdc(), HORZRES);
2110
2111 wxCHECK_RET( wTotal, _T("0 width device?") );
2112
2113 *w = (wPixels * ::GetDeviceCaps(GetHdc(), HORZSIZE)) / wTotal;
2114 }
2115
2116 if ( h )
2117 {
2118 int hTotal = ::GetDeviceCaps(GetHdc(), VERTRES);
2119
2120 wxCHECK_RET( hTotal, _T("0 height device?") );
2121
2122 *h = (hPixels * ::GetDeviceCaps(GetHdc(), VERTSIZE)) / hTotal;
2123 }
2124 }
2125
2126 wxSize wxDC::GetPPI() const
2127 {
2128 #ifdef __WXMICROWIN__
2129 if (!GetHDC()) return wxSize();
2130 #endif
2131
2132 int x = ::GetDeviceCaps(GetHdc(), LOGPIXELSX);
2133 int y = ::GetDeviceCaps(GetHdc(), LOGPIXELSY);
2134
2135 return wxSize(x, y);
2136 }
2137
2138 // For use by wxWindows only, unless custom units are required.
2139 void wxDC::SetLogicalScale(double x, double y)
2140 {
2141 #ifdef __WXMICROWIN__
2142 if (!GetHDC()) return;
2143 #endif
2144
2145 m_logicalScaleX = x;
2146 m_logicalScaleY = y;
2147 }
2148
2149 #if wxUSE_DC_CACHEING
2150
2151 /*
2152 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2153 * improve it in due course, either using arrays, or simply storing pointers to one
2154 * entry for the bitmap, and two for the DCs. -- JACS
2155 */
2156
2157 wxList wxDC::sm_bitmapCache;
2158 wxList wxDC::sm_dcCache;
2159
2160 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap, int w, int h, int depth)
2161 {
2162 m_bitmap = hBitmap;
2163 m_dc = 0;
2164 m_width = w;
2165 m_height = h;
2166 m_depth = depth;
2167 }
2168
2169 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
2170 {
2171 m_bitmap = 0;
2172 m_dc = hDC;
2173 m_width = 0;
2174 m_height = 0;
2175 m_depth = depth;
2176 }
2177
2178 wxDCCacheEntry::~wxDCCacheEntry()
2179 {
2180 if (m_bitmap)
2181 ::DeleteObject((HBITMAP) m_bitmap);
2182 if (m_dc)
2183 ::DeleteDC((HDC) m_dc);
2184 }
2185
2186 wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
2187 {
2188 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2189 wxNode* node = sm_bitmapCache.First();
2190 while (node)
2191 {
2192 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->Data();
2193
2194 if (entry->m_depth == depth)
2195 {
2196 if (entry->m_width < w || entry->m_height < h)
2197 {
2198 ::DeleteObject((HBITMAP) entry->m_bitmap);
2199 entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2200 if ( !entry->m_bitmap)
2201 {
2202 wxLogLastError(wxT("CreateCompatibleBitmap"));
2203 }
2204 entry->m_width = w; entry->m_height = h;
2205 return entry;
2206 }
2207 return entry;
2208 }
2209
2210 node = node->Next();
2211 }
2212 WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
2213 if ( !hBitmap)
2214 {
2215 wxLogLastError(wxT("CreateCompatibleBitmap"));
2216 }
2217 wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
2218 AddToBitmapCache(entry);
2219 return entry;
2220 }
2221
2222 wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
2223 {
2224 int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
2225 wxNode* node = sm_dcCache.First();
2226 while (node)
2227 {
2228 wxDCCacheEntry* entry = (wxDCCacheEntry*) node->Data();
2229
2230 // Don't return the same one as we already have
2231 if (!notThis || (notThis != entry))
2232 {
2233 if (entry->m_depth == depth)
2234 {
2235 return entry;
2236 }
2237 }
2238
2239 node = node->Next();
2240 }
2241 WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
2242 if ( !hDC)
2243 {
2244 wxLogLastError(wxT("CreateCompatibleDC"));
2245 }
2246 wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
2247 AddToDCCache(entry);
2248 return entry;
2249 }
2250
2251 void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
2252 {
2253 sm_bitmapCache.Append(entry);
2254 }
2255
2256 void wxDC::AddToDCCache(wxDCCacheEntry* entry)
2257 {
2258 sm_dcCache.Append(entry);
2259 }
2260
2261 void wxDC::ClearCache()
2262 {
2263 sm_dcCache.DeleteContents(TRUE);
2264 sm_dcCache.Clear();
2265 sm_dcCache.DeleteContents(FALSE);
2266 sm_bitmapCache.DeleteContents(TRUE);
2267 sm_bitmapCache.Clear();
2268 sm_bitmapCache.DeleteContents(FALSE);
2269 }
2270
2271 // Clean up cache at app exit
2272 class wxDCModule : public wxModule
2273 {
2274 public:
2275 virtual bool OnInit() { return TRUE; }
2276 virtual void OnExit() { wxDC::ClearCache(); }
2277
2278 private:
2279 DECLARE_DYNAMIC_CLASS(wxDCModule)
2280 };
2281
2282 IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2283
2284 #endif
2285 // wxUSE_DC_CACHEING
2286