1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
21 #pragma implementation "dc.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
32 #include "wx/window.h"
35 #include "wx/dialog.h"
37 #include "wx/bitmap.h"
38 #include "wx/dcmemory.h"
43 #include "wx/sysopt.h"
44 #include "wx/dcprint.h"
45 #include "wx/module.h"
46 #include "wx/dynload.h"
51 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
53 #if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__)
61 /* Quaternary raster codes */
63 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
66 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxDCBase
)
68 // ---------------------------------------------------------------------------
70 // ---------------------------------------------------------------------------
72 static const int VIEWPORT_EXTENT
= 1000;
74 static const int MM_POINTS
= 9;
75 static const int MM_METRIC
= 10;
77 // usually this is defined in math.h
79 static const double M_PI
= 3.14159265358979323846;
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
86 // ----------------------------------------------------------------------------
87 // macros for logical <-> device coords conversion
88 // ----------------------------------------------------------------------------
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
98 #define XLOG2DEV(x) (x)
99 #define YLOG2DEV(y) (y)
102 #define XDEV2LOG(x) (x)
103 #define YDEV2LOG(y) (y)
105 // ---------------------------------------------------------------------------
107 // ---------------------------------------------------------------------------
109 // convert degrees to radians
110 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
112 // ----------------------------------------------------------------------------
114 // ----------------------------------------------------------------------------
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
120 // wxColourChanger: changes the text colours in the ctor if required and
121 // restores them in the dtor
122 class wxColourChanger
125 wxColourChanger(wxDC
& dc
);
131 COLORREF m_colFgOld
, m_colBgOld
;
136 // this class saves the old stretch blit mode during its life time
137 class StretchBltModeChanger
140 StretchBltModeChanger(HDC hdc
, int mode
)
143 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
145 wxLogLastError(_T("SetStretchBltMode"));
148 ~StretchBltModeChanger()
150 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
151 wxLogLastError(_T("SetStretchBltMode"));
160 // ===========================================================================
162 // ===========================================================================
164 // ----------------------------------------------------------------------------
166 // ----------------------------------------------------------------------------
168 wxColourChanger
::wxColourChanger(wxDC
& dc
) : m_dc(dc
)
170 const wxBrush
& brush
= dc
.GetBrush();
171 if ( brush
.Ok() && brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
173 HDC hdc
= GetHdcOf(dc
);
174 m_colFgOld
= ::GetTextColor(hdc
);
175 m_colBgOld
= ::GetBkColor(hdc
);
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();
182 ::SetBkColor(hdc
, colFg
.GetPixel());
185 const wxColour
& colBg
= dc
.GetTextBackground();
188 ::SetTextColor(hdc
, colBg
.GetPixel());
192 dc
.GetBackgroundMode() == wxTRANSPARENT ? TRANSPARENT
195 // flag which telsl us to undo changes in the dtor
200 // nothing done, nothing to undo
205 wxColourChanger
::~wxColourChanger()
209 // restore the colours we changed
210 HDC hdc
= GetHdcOf(m_dc
);
212 ::SetBkMode(hdc
, TRANSPARENT
);
213 ::SetTextColor(hdc
, m_colFgOld
);
214 ::SetBkColor(hdc
, m_colBgOld
);
218 // ---------------------------------------------------------------------------
220 // ---------------------------------------------------------------------------
222 // Default constructor
233 #endif // wxUSE_PALETTE
243 SelectOldObjects(m_hDC
);
245 // if we own the HDC, we delete it, otherwise we just release it
249 ::DeleteDC(GetHdc());
251 else // we don't own our HDC
255 ::ReleaseDC(GetHwndOf(m_canvas
), GetHdc());
259 // Must have been a wxScreenDC
260 ::ReleaseDC((HWND
) NULL
, GetHdc());
266 // This will select current objects out of the DC,
267 // which is what you have to do before deleting the
269 void wxDC
::SelectOldObjects(WXHDC dc
)
275 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
276 if (m_selectedBitmap
.Ok())
278 m_selectedBitmap
.SetSelectedInto(NULL
);
284 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
289 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
294 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
301 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
304 #endif // wxUSE_PALETTE
307 m_brush
= wxNullBrush
;
310 m_palette
= wxNullPalette
;
311 #endif // wxUSE_PALETTE
313 m_backgroundBrush
= wxNullBrush
;
314 m_selectedBitmap
= wxNullBitmap
;
317 // ---------------------------------------------------------------------------
319 // ---------------------------------------------------------------------------
321 void wxDC
::UpdateClipBox()
323 #ifdef __WXMICROWIN__
324 if (!GetHDC()) return;
328 ::GetClipBox(GetHdc(), &rect
);
330 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
331 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
332 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
333 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
336 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
337 void wxDC
::SetClippingHrgn(WXHRGN hrgn
)
339 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
341 #ifdef __WXMICROWIN__
342 if (!GetHdc()) return;
343 #endif // __WXMICROWIN__
345 // note that we combine the new clipping region with the existing one: this
346 // is compatible with what the other ports do and is the documented
347 // behaviour now (starting with 2.3.3)
350 if ( !::GetClipBox(GetHdc(), &rectClip
) )
353 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
354 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
355 rectClip
.right
, rectClip
.bottom
);
357 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
359 ::SelectClipRgn(GetHdc(), hrgnDest
);
362 ::DeleteObject(hrgnClipOld
);
363 ::DeleteObject(hrgnDest
);
365 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
367 wxLogLastError(_T("ExtSelectClipRgn"));
378 void wxDC
::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
380 // the region coords are always the device ones, so do the translation
383 // FIXME: possible +/-1 error here, to check!
384 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
386 LogicalToDeviceX(x
+ w
),
387 LogicalToDeviceY(y
+ h
));
390 wxLogLastError(_T("CreateRectRgn"));
394 SetClippingHrgn((WXHRGN
)hrgn
);
396 ::DeleteObject(hrgn
);
400 void wxDC
::DoSetClippingRegionAsRegion(const wxRegion
& region
)
402 SetClippingHrgn(region
.GetHRGN());
405 void wxDC
::DestroyClippingRegion()
407 #ifdef __WXMICROWIN__
408 if (!GetHDC()) return;
411 if (m_clipping
&& m_hDC
)
413 // TODO: this should restore the previous clipping region,
414 // so that OnPaint processing works correctly, and the update
415 // clipping region doesn't get destroyed after the first
416 // DestroyClippingRegion.
417 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
418 ::SelectClipRgn(GetHdc(), rgn
);
425 // ---------------------------------------------------------------------------
426 // query capabilities
427 // ---------------------------------------------------------------------------
429 bool wxDC
::CanDrawBitmap() const
434 bool wxDC
::CanGetTextExtent() const
436 #ifdef __WXMICROWIN__
437 // TODO Extend MicroWindows' GetDeviceCaps function
440 // What sort of display is it?
441 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
443 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
447 int wxDC
::GetDepth() const
449 #ifdef __WXMICROWIN__
450 if (!GetHDC()) return 16;
453 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
456 // ---------------------------------------------------------------------------
458 // ---------------------------------------------------------------------------
462 #ifdef __WXMICROWIN__
463 if (!GetHDC()) return;
469 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
473 // No, I think we should simply ignore this if printing on e.g.
475 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
476 if (!m_selectedBitmap
.Ok())
479 rect
.left
= 0; rect
.top
= 0;
480 rect
.right
= m_selectedBitmap
.GetWidth();
481 rect
.bottom
= m_selectedBitmap
.GetHeight();
484 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
486 DWORD colour
= ::GetBkColor(GetHdc());
487 HBRUSH brush
= ::CreateSolidBrush(colour
);
488 ::FillRect(GetHdc(), &rect
, brush
);
489 ::DeleteObject(brush
);
491 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
492 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
494 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
495 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
496 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
497 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
498 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
501 bool wxDC
::DoFloodFill(wxCoord x
, wxCoord y
, const wxColour
& col
, int style
)
503 #ifdef __WXMICROWIN__
504 if (!GetHDC()) return FALSE
;
507 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
509 style
== wxFLOOD_SURFACE ? FLOODFILLSURFACE
510 : FLOODFILLBORDER
) ) ;
513 // quoting from the MSDN docs:
515 // Following are some of the reasons this function might fail:
517 // * The filling could not be completed.
518 // * The specified point has the boundary color specified by the
519 // crColor parameter (if FLOODFILLBORDER was requested).
520 // * The specified point does not have the color specified by
521 // crColor (if FLOODFILLSURFACE was requested)
522 // * The point is outside the clipping region that is, it is not
523 // visible on the device.
525 wxLogLastError(wxT("ExtFloodFill"));
528 CalcBoundingBox(x
, y
);
533 bool wxDC
::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
535 #ifdef __WXMICROWIN__
536 if (!GetHDC()) return FALSE
;
539 wxCHECK_MSG( col
, FALSE
, _T("NULL colour parameter in wxDC::GetPixel") );
541 // get the color of the pixel
542 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
544 wxRGBToColour(*col
, pixelcolor
);
549 void wxDC
::DoCrossHair(wxCoord x
, wxCoord y
)
551 #ifdef __WXMICROWIN__
552 if (!GetHDC()) return;
555 wxCoord x1
= x
-VIEWPORT_EXTENT
;
556 wxCoord y1
= y
-VIEWPORT_EXTENT
;
557 wxCoord x2
= x
+VIEWPORT_EXTENT
;
558 wxCoord y2
= y
+VIEWPORT_EXTENT
;
560 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), NULL
);
561 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y
));
563 (void)MoveToEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), NULL
);
564 (void)LineTo(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y2
));
566 CalcBoundingBox(x1
, y1
);
567 CalcBoundingBox(x2
, y2
);
570 void wxDC
::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
572 #ifdef __WXMICROWIN__
573 if (!GetHDC()) return;
576 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), NULL
);
577 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y2
));
579 CalcBoundingBox(x1
, y1
);
580 CalcBoundingBox(x2
, y2
);
583 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
584 // and ending at (x2, y2)
585 void wxDC
::DoDrawArc(wxCoord x1
, wxCoord y1
,
586 wxCoord x2
, wxCoord y2
,
587 wxCoord xc
, wxCoord yc
)
589 #ifdef __WXMICROWIN__
590 if (!GetHDC()) return;
593 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
597 double radius
= (double)sqrt(dx
*dx
+dy
*dy
);
598 wxCoord r
= (wxCoord
)radius
;
600 // treat the special case of full circle separately
601 if ( x1
== x2
&& y1
== y2
)
603 DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
607 wxCoord xx1
= XLOG2DEV(x1
);
608 wxCoord yy1
= YLOG2DEV(y1
);
609 wxCoord xx2
= XLOG2DEV(x2
);
610 wxCoord yy2
= YLOG2DEV(y2
);
611 wxCoord xxc
= XLOG2DEV(xc
);
612 wxCoord yyc
= YLOG2DEV(yc
);
613 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
615 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
616 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
617 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
618 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
620 if ( m_brush
.Ok() && m_brush
.GetStyle() != wxTRANSPARENT
)
622 // Have to add 1 to bottom-right corner of rectangle
623 // to make semi-circles look right (crooked line otherwise).
624 // Unfortunately this is not a reliable method, depends
625 // on the size of shape.
626 // TODO: figure out why this happens!
627 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
631 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
634 CalcBoundingBox(xc
- r
, yc
- r
);
635 CalcBoundingBox(xc
+ r
, yc
+ r
);
638 void wxDC
::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
639 wxCoord width
, wxCoord height
)
641 #ifdef __WXMICROWIN__
642 if (!GetHDC()) return;
645 wxCoord x2
= x1
+ width
,
648 #if defined(__WIN32__) && !defined(__SC__) && !defined(__WXMICROWIN__)
655 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
657 // In WIN16, draw a cross
658 HPEN blackPen
= ::CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
659 HPEN whiteBrush
= (HPEN
)::GetStockObject(WHITE_BRUSH
);
660 HPEN hPenOld
= (HPEN
)::SelectObject(GetHdc(), blackPen
);
661 HPEN hBrushOld
= (HPEN
)::SelectObject(GetHdc(), whiteBrush
);
662 ::SetROP2(GetHdc(), R2_COPYPEN
);
663 Rectangle(GetHdc(), x1
, y1
, x2
, y2
);
664 MoveToEx(GetHdc(), x1
, y1
, NULL
);
665 LineTo(GetHdc(), x2
, y2
);
666 MoveToEx(GetHdc(), x2
, y1
, NULL
);
667 LineTo(GetHdc(), x1
, y2
);
668 ::SelectObject(GetHdc(), hPenOld
);
669 ::SelectObject(GetHdc(), hBrushOld
);
670 ::DeleteObject(blackPen
);
673 CalcBoundingBox(x1
, y1
);
674 CalcBoundingBox(x2
, y2
);
677 void wxDC
::DoDrawPoint(wxCoord x
, wxCoord y
)
679 #ifdef __WXMICROWIN__
680 if (!GetHDC()) return;
683 COLORREF color
= 0x00ffffff;
686 color
= m_pen
.GetColour().GetPixel();
689 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
691 CalcBoundingBox(x
, y
);
694 void wxDC
::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
696 #ifdef __WXMICROWIN__
697 if (!GetHDC()) return;
700 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
702 // Do things less efficiently if we have offsets
703 if (xoffset
!= 0 || yoffset
!= 0)
705 POINT
*cpoints
= new POINT
[n
];
707 for (i
= 0; i
< n
; i
++)
709 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
710 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
712 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
714 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE?ALTERNATE
:WINDING
);
715 (void)Polygon(GetHdc(), cpoints
, n
);
716 SetPolyFillMode(GetHdc(),prev
);
722 for (i
= 0; i
< n
; i
++)
723 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
725 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE?ALTERNATE
:WINDING
);
726 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
727 SetPolyFillMode(GetHdc(),prev
);
731 void wxDC
::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
733 #ifdef __WXMICROWIN__
734 if (!GetHDC()) return;
737 // Do things less efficiently if we have offsets
738 if (xoffset
!= 0 || yoffset
!= 0)
740 POINT
*cpoints
= new POINT
[n
];
742 for (i
= 0; i
< n
; i
++)
744 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
745 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
747 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
749 (void)Polyline(GetHdc(), cpoints
, n
);
755 for (i
= 0; i
< n
; i
++)
756 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
758 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
762 void wxDC
::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
764 #ifdef __WXMICROWIN__
765 if (!GetHDC()) return;
768 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
770 wxCoord x2
= x
+ width
;
771 wxCoord y2
= y
+ height
;
773 if ((m_logicalFunction
== wxCOPY
) && (m_pen
.GetStyle() == wxTRANSPARENT
))
776 rect
.left
= XLOG2DEV(x
);
777 rect
.top
= YLOG2DEV(y
);
778 rect
.right
= XLOG2DEV(x2
);
779 rect
.bottom
= YLOG2DEV(y2
);
780 (void)FillRect(GetHdc(), &rect
, (HBRUSH
)m_brush
.GetResourceHandle() );
784 // Windows draws the filled rectangles without outline (i.e. drawn with a
785 // transparent pen) one pixel smaller in both directions and we want them
786 // to have the same size regardless of which pen is used - adjust
788 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
789 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
795 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
799 CalcBoundingBox(x
, y
);
800 CalcBoundingBox(x2
, y2
);
803 void wxDC
::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
805 #ifdef __WXMICROWIN__
806 if (!GetHDC()) return;
809 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
811 // Now, a negative radius value is interpreted to mean
812 // 'the proportion of the smallest X or Y dimension'
816 double smallest
= 0.0;
821 radius
= (- radius
* smallest
);
824 wxCoord x2
= (x
+width
);
825 wxCoord y2
= (y
+height
);
827 // Windows draws the filled rectangles without outline (i.e. drawn with a
828 // transparent pen) one pixel smaller in both directions and we want them
829 // to have the same size regardless of which pen is used - adjust
830 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
836 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
837 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
839 CalcBoundingBox(x
, y
);
840 CalcBoundingBox(x2
, y2
);
843 void wxDC
::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
845 #ifdef __WXMICROWIN__
846 if (!GetHDC()) return;
849 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
851 wxCoord x2
= (x
+width
);
852 wxCoord y2
= (y
+height
);
854 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
856 CalcBoundingBox(x
, y
);
857 CalcBoundingBox(x2
, y2
);
860 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
861 void wxDC
::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
863 #ifdef __WXMICROWIN__
864 if (!GetHDC()) return;
867 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
872 int rx1
= XLOG2DEV(x
+w
/2);
873 int ry1
= YLOG2DEV(y
+h
/2);
880 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
881 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
882 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
883 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
885 // draw pie with NULL_PEN first and then outline otherwise a line is
886 // drawn from the start and end points to the centre
887 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
890 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
895 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
896 rx1
, ry1
-1, rx2
, ry2
-1);
899 ::SelectObject(GetHdc(), hpenOld
);
901 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
904 CalcBoundingBox(x
, y
);
905 CalcBoundingBox(x2
, y2
);
908 void wxDC
::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
910 #ifdef __WXMICROWIN__
911 if (!GetHDC()) return;
914 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
917 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
919 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
922 CalcBoundingBox(x
, y
);
923 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
926 void wxDC
::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
928 #ifdef __WXMICROWIN__
929 if (!GetHDC()) return;
932 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
934 int width
= bmp
.GetWidth(),
935 height
= bmp
.GetHeight();
937 HBITMAP hbmpMask
= 0;
941 #endif // wxUSE_PALETTE
943 // do we have AlphaBlend() and company in the headers?
945 if ( bmp
.HasAlpha() )
947 // yes, now try to see if we have it during run-time
949 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
953 // bitmaps can be drawn only from GUI thread so there is no need to
954 // protect this static variable from multiple threads
955 static bool s_triedToLoad
= FALSE
;
956 static AlphaBlend_t pfnAlphaBlend
= NULL
;
957 if ( !s_triedToLoad
)
959 s_triedToLoad
= TRUE
;
961 wxDynamicLibrary
dll(_T("msimg32.dll"));
962 if ( dll
.IsLoaded() )
964 pfnAlphaBlend
= (AlphaBlend_t
)dll
.GetSymbol(_T("AlphaBlend"));
967 // we must keep the DLL loaded if we want to be able to
968 // call AlphaBlend() so just never unload it at all
977 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
980 #define AC_SRC_ALPHA 1
984 bf
.BlendOp
= AC_SRC_OVER
;
986 bf
.SourceConstantAlpha
= 0xff;
987 bf
.AlphaFormat
= AC_SRC_ALPHA
;
989 if ( !pfnAlphaBlend(GetHdc(), x
, y
, width
, height
,
990 hdcMem
, 0, 0, width
, height
,
993 wxLogLastError(_T("AlphaBlend"));
998 //else: AlphaBlend() not available
1000 #endif // defined(AC_SRC_OVER)
1004 wxMask
*mask
= bmp
.GetMask();
1006 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1010 // don't give assert here because this would break existing
1011 // programs - just silently ignore useMask parameter
1018 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1020 // On some systems, MaskBlt succeeds yet is much much slower
1021 // than the wxWindows fall-back implementation. So we need
1022 // to be able to switch this on and off at runtime.
1024 #if wxUSE_SYSTEM_OPTIONS
1025 if (wxSystemOptions
::GetOptionInt(wxT("no-maskblt")) == 0)
1029 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1030 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1032 wxPalette
*pal
= bmp
.GetPalette();
1033 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1035 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1036 ::RealizePalette(hdcMem
);
1038 #endif // wxUSE_PALETTE
1040 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1043 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1047 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1048 #endif // wxUSE_PALETTE
1050 ::SelectObject(hdcMem
, hOldBitmap
);
1057 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1060 memDC
.SelectObject(bmp
);
1062 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1064 memDC
.SelectObject(wxNullBitmap
);
1067 else // no mask, just use BitBlt()
1070 HDC memdc
= ::CreateCompatibleDC( cdc
);
1071 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1073 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1075 COLORREF old_textground
= ::GetTextColor(GetHdc());
1076 COLORREF old_background
= ::GetBkColor(GetHdc());
1077 if (m_textForegroundColour
.Ok())
1079 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1081 if (m_textBackgroundColour
.Ok())
1083 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1087 wxPalette
*pal
= bmp
.GetPalette();
1088 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1090 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1091 ::RealizePalette(memdc
);
1093 #endif // wxUSE_PALETTE
1095 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1096 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1100 ::SelectPalette(memdc
, oldPal
, FALSE
);
1101 #endif // wxUSE_PALETTE
1103 ::SelectObject( memdc
, hOldBitmap
);
1104 ::DeleteDC( memdc
);
1106 ::SetTextColor(GetHdc(), old_textground
);
1107 ::SetBkColor(GetHdc(), old_background
);
1111 void wxDC
::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1113 #ifdef __WXMICROWIN__
1114 if (!GetHDC()) return;
1117 DrawAnyText(text
, x
, y
);
1119 // update the bounding box
1120 CalcBoundingBox(x
, y
);
1123 GetTextExtent(text
, &w
, &h
);
1124 CalcBoundingBox(x
+ w
, y
+ h
);
1127 void wxDC
::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1129 #ifdef __WXMICROWIN__
1130 if (!GetHDC()) return;
1133 // prepare for drawing the text
1134 if ( m_textForegroundColour
.Ok() )
1135 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
1137 DWORD old_background
= 0;
1138 if ( m_textBackgroundColour
.Ok() )
1140 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1143 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT ? TRANSPARENT
1146 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
1147 text
.c_str(), text
.length()) == 0 )
1149 wxLogLastError(wxT("TextOut"));
1152 // restore the old parameters (text foreground colour may be left because
1153 // it never is set to anything else, but background should remain
1154 // transparent even if we just drew an opaque string)
1155 if ( m_textBackgroundColour
.Ok() )
1156 (void)SetBkColor(GetHdc(), old_background
);
1158 SetBkMode(GetHdc(), TRANSPARENT
);
1161 void wxDC
::DoDrawRotatedText(const wxString
& text
,
1162 wxCoord x
, wxCoord y
,
1165 #ifdef __WXMICROWIN__
1166 if (!GetHDC()) return;
1169 // we test that we have some font because otherwise we should still use the
1170 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1171 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1172 // font for drawing rotated fonts unfortunately)
1173 if ( (angle
== 0.0) && m_font
.Ok() )
1175 DoDrawText(text
, x
, y
);
1177 #ifndef __WXMICROWIN__
1180 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1181 // because it's not TrueType and so can't have non zero
1182 // orientation/escapement under Win9x
1183 wxFont font
= m_font
.Ok() ? m_font
: *wxSWISS_FONT
;
1184 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1186 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1188 wxLogLastError(wxT("GetObject(hfont)"));
1191 // GDI wants the angle in tenth of degree
1192 long angle10
= (long)(angle
* 10);
1193 lf
.lfEscapement
= angle10
;
1194 lf
. lfOrientation
= angle10
;
1196 hfont
= ::CreateFontIndirect(&lf
);
1199 wxLogLastError(wxT("CreateFont"));
1203 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1205 DrawAnyText(text
, x
, y
);
1207 (void)::SelectObject(GetHdc(), hfontOld
);
1208 (void)::DeleteObject(hfont
);
1211 // call the bounding box by adding all four vertices of the rectangle
1212 // containing the text to it (simpler and probably not slower than
1213 // determining which of them is really topmost/leftmost/...)
1215 GetTextExtent(text
, &w
, &h
);
1217 double rad
= DegToRad(angle
);
1219 // "upper left" and "upper right"
1220 CalcBoundingBox(x
, y
);
1221 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(h
*sin(rad
)));
1223 // "bottom left" and "bottom right"
1224 x
+= (wxCoord
)(h
*sin(rad
));
1225 y
+= (wxCoord
)(h
*cos(rad
));
1226 CalcBoundingBox(x
, y
);
1227 CalcBoundingBox(x
+ wxCoord(h
*sin(rad
)), y
+ wxCoord(h
*cos(rad
)));
1232 // ---------------------------------------------------------------------------
1234 // ---------------------------------------------------------------------------
1238 void wxDC
::DoSelectPalette(bool realize
)
1240 #ifdef __WXMICROWIN__
1241 if (!GetHDC()) return;
1244 // Set the old object temporarily, in case the assignment deletes an object
1245 // that's not yet selected out.
1248 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1252 if ( m_palette
.Ok() )
1254 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1255 GetHpaletteOf(m_palette
),
1258 m_oldPalette
= (WXHPALETTE
) oldPal
;
1261 ::RealizePalette(GetHdc());
1265 void wxDC
::SetPalette(const wxPalette
& palette
)
1269 m_palette
= palette
;
1270 DoSelectPalette(TRUE
);
1274 void wxDC
::InitializePalette()
1276 if ( wxDisplayDepth() <= 8 )
1278 // look for any window or parent that has a custom palette. If any has
1279 // one then we need to use it in drawing operations
1280 wxWindow
*win
= m_canvas
->GetAncestorWithCustomPalette();
1282 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1283 if ( m_hasCustomPalette
)
1285 m_palette
= win
->GetPalette();
1287 // turn on MSW translation for this palette
1293 #endif // wxUSE_PALETTE
1295 void wxDC
::SetFont(const wxFont
& the_font
)
1297 #ifdef __WXMICROWIN__
1298 if (!GetHDC()) return;
1301 // Set the old object temporarily, in case the assignment deletes an object
1302 // that's not yet selected out.
1305 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1314 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1318 if (m_font
.Ok() && m_font
.GetResourceHandle())
1320 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
1321 if (f
== (HFONT
) NULL
)
1323 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
1326 m_oldFont
= (WXHFONT
) f
;
1330 void wxDC
::SetPen(const wxPen
& pen
)
1332 #ifdef __WXMICROWIN__
1333 if (!GetHDC()) return;
1336 // Set the old object temporarily, in case the assignment deletes an object
1337 // that's not yet selected out.
1340 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1349 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1355 if (m_pen
.GetResourceHandle())
1357 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
1359 m_oldPen
= (WXHPEN
) p
;
1364 void wxDC
::SetBrush(const wxBrush
& brush
)
1366 #ifdef __WXMICROWIN__
1367 if (!GetHDC()) return;
1370 // Set the old object temporarily, in case the assignment deletes an object
1371 // that's not yet selected out.
1374 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1383 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1389 // to make sure the brush is alligned with the logical coordinates
1390 wxBitmap
*stipple
= m_brush
.GetStipple();
1391 if ( stipple
&& stipple
->Ok() )
1394 ::SetBrushOrgEx(GetHdc(),
1395 m_deviceOriginX
% stipple
->GetWidth(),
1396 m_deviceOriginY
% stipple
->GetHeight(),
1397 NULL
); // don't need previous brush origin
1399 ::SetBrushOrg(GetHdc(),
1400 m_deviceOriginX
% stipple
->GetWidth(),
1401 m_deviceOriginY
% stipple
->GetHeight());
1405 if ( m_brush
.GetResourceHandle() )
1408 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
1410 m_oldBrush
= (WXHBRUSH
) b
;
1415 void wxDC
::SetBackground(const wxBrush
& brush
)
1417 #ifdef __WXMICROWIN__
1418 if (!GetHDC()) return;
1421 m_backgroundBrush
= brush
;
1423 if (!m_backgroundBrush
.Ok())
1428 bool customColours
= TRUE
;
1429 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
1430 // change background colours from the control-panel specified colours.
1431 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
1432 customColours
= FALSE
;
1436 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
1438 m_canvas
->SetTransparent(TRUE
);
1442 // New behaviour, 10/2/99: setting the background brush of a DC
1443 // doesn't affect the window background colour. However,
1444 // I'm leaving in the transparency setting because it's needed by
1445 // various controls (e.g. wxStaticText) to determine whether to draw
1446 // transparently or not. TODO: maybe this should be a new function
1447 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1449 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1450 m_canvas
->SetTransparent(FALSE
);
1454 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1456 (void)SetBkColor(GetHdc(), new_color
);
1460 void wxDC
::SetBackgroundMode(int mode
)
1462 #ifdef __WXMICROWIN__
1463 if (!GetHDC()) return;
1466 m_backgroundMode
= mode
;
1468 // SetBackgroundColour now only refers to text background
1469 // and m_backgroundMode is used there
1472 void wxDC
::SetLogicalFunction(int function
)
1474 #ifdef __WXMICROWIN__
1475 if (!GetHDC()) return;
1478 m_logicalFunction
= function
;
1483 void wxDC
::SetRop(WXHDC dc
)
1485 if ( !dc
|| m_logicalFunction
< 0 )
1490 switch (m_logicalFunction
)
1492 case wxCLEAR
: rop
= R2_BLACK
; break;
1493 case wxXOR
: rop
= R2_XORPEN
; break;
1494 case wxINVERT
: rop
= R2_NOT
; break;
1495 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1496 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1497 case wxCOPY
: rop
= R2_COPYPEN
; break;
1498 case wxAND
: rop
= R2_MASKPEN
; break;
1499 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1500 case wxNO_OP
: rop
= R2_NOP
; break;
1501 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1502 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1503 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1504 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1505 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1506 case wxOR
: rop
= R2_MERGEPEN
; break;
1507 case wxSET
: rop
= R2_WHITE
; break;
1510 wxFAIL_MSG( wxT("unsupported logical function") );
1514 SetROP2(GetHdc(), rop
);
1517 bool wxDC
::StartDoc(const wxString
& WXUNUSED(message
))
1519 // We might be previewing, so return TRUE to let it continue.
1527 void wxDC
::StartPage()
1531 void wxDC
::EndPage()
1535 // ---------------------------------------------------------------------------
1537 // ---------------------------------------------------------------------------
1539 wxCoord wxDC
::GetCharHeight() const
1541 #ifdef __WXMICROWIN__
1542 if (!GetHDC()) return 0;
1545 TEXTMETRIC lpTextMetric
;
1547 GetTextMetrics(GetHdc(), &lpTextMetric
);
1549 return lpTextMetric
.tmHeight
;
1552 wxCoord wxDC
::GetCharWidth() const
1554 #ifdef __WXMICROWIN__
1555 if (!GetHDC()) return 0;
1558 TEXTMETRIC lpTextMetric
;
1560 GetTextMetrics(GetHdc(), &lpTextMetric
);
1562 return lpTextMetric
.tmAveCharWidth
;
1565 void wxDC
::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1566 wxCoord
*descent
, wxCoord
*externalLeading
,
1569 #ifdef __WXMICROWIN__
1574 if (descent
) *descent
= 0;
1575 if (externalLeading
) *externalLeading
= 0;
1578 #endif // __WXMICROWIN__
1583 wxASSERT_MSG( font
->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1585 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1587 else // don't change the font
1595 GetTextExtentPoint(GetHdc(), string
, string
.length(), &sizeRect
);
1596 GetTextMetrics(GetHdc(), &tm
);
1603 *descent
= tm
.tmDescent
;
1604 if (externalLeading
)
1605 *externalLeading
= tm
.tmExternalLeading
;
1609 ::SelectObject(GetHdc(), hfontOld
);
1613 void wxDC
::SetMapMode(int mode
)
1615 #ifdef __WXMICROWIN__
1616 if (!GetHDC()) return;
1619 m_mappingMode
= mode
;
1621 if ( mode
== wxMM_TEXT
)
1624 m_logicalScaleY
= 1.0;
1626 else // need to do some calculations
1628 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1629 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1630 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1631 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1633 if ( (mm_width
== 0) || (mm_height
== 0) )
1635 // we can't calculate mm2pixels[XY] then!
1639 double mm2pixelsX
= pixel_width
/ mm_width
,
1640 mm2pixelsY
= pixel_height
/ mm_height
;
1645 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1646 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1650 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1651 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1655 m_logicalScaleX
= mm2pixelsX
;
1656 m_logicalScaleY
= mm2pixelsY
;
1660 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1661 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1665 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1669 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1670 // cases we could do with MM_TEXT and in the remaining 0.9% with
1671 // MM_ISOTROPIC (TODO!)
1672 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1674 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1675 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1677 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1678 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1680 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1681 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1684 void wxDC
::SetUserScale(double x
, double y
)
1686 #ifdef __WXMICROWIN__
1687 if (!GetHDC()) return;
1690 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1696 SetMapMode(m_mappingMode
);
1699 void wxDC
::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1701 #ifdef __WXMICROWIN__
1702 if (!GetHDC()) return;
1705 int signX
= xLeftRight ?
1 : -1,
1706 signY
= yBottomUp ?
-1 : 1;
1708 if ( signX
!= m_signX
|| signY
!= m_signY
)
1713 SetMapMode(m_mappingMode
);
1717 void wxDC
::SetSystemScale(double x
, double y
)
1719 #ifdef __WXMICROWIN__
1720 if (!GetHDC()) return;
1723 if ( x
== m_scaleX
&& y
== m_scaleY
)
1729 SetMapMode(m_mappingMode
);
1732 void wxDC
::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1734 #ifdef __WXMICROWIN__
1735 if (!GetHDC()) return;
1738 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1741 m_logicalOriginX
= x
;
1742 m_logicalOriginY
= y
;
1744 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1747 void wxDC
::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1749 #ifdef __WXMICROWIN__
1750 if (!GetHDC()) return;
1753 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
1756 m_deviceOriginX
= x
;
1757 m_deviceOriginY
= y
;
1759 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1762 // ---------------------------------------------------------------------------
1763 // coordinates transformations
1764 // ---------------------------------------------------------------------------
1766 wxCoord wxDCBase
::DeviceToLogicalX(wxCoord x
) const
1768 return DeviceToLogicalXRel(x
- m_deviceOriginX
)*m_signX
+ m_logicalOriginX
;
1771 wxCoord wxDCBase
::DeviceToLogicalXRel(wxCoord x
) const
1773 // axis orientation is not taken into account for conversion of a distance
1774 return (wxCoord
)(x
/ (m_logicalScaleX
*m_userScaleX
*m_scaleX
));
1777 wxCoord wxDCBase
::DeviceToLogicalY(wxCoord y
) const
1779 return DeviceToLogicalYRel(y
- m_deviceOriginY
)*m_signY
+ m_logicalOriginY
;
1782 wxCoord wxDCBase
::DeviceToLogicalYRel(wxCoord y
) const
1784 // axis orientation is not taken into account for conversion of a distance
1785 return (wxCoord
)( y
/ (m_logicalScaleY
*m_userScaleY
*m_scaleY
));
1788 wxCoord wxDCBase
::LogicalToDeviceX(wxCoord x
) const
1790 return LogicalToDeviceXRel(x
- m_logicalOriginX
)*m_signX
+ m_deviceOriginX
;
1793 wxCoord wxDCBase
::LogicalToDeviceXRel(wxCoord x
) const
1795 // axis orientation is not taken into account for conversion of a distance
1796 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_scaleX
);
1799 wxCoord wxDCBase
::LogicalToDeviceY(wxCoord y
) const
1801 return LogicalToDeviceYRel(y
- m_logicalOriginY
)*m_signY
+ m_deviceOriginY
;
1804 wxCoord wxDCBase
::LogicalToDeviceYRel(wxCoord y
) const
1806 // axis orientation is not taken into account for conversion of a distance
1807 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_scaleY
);
1810 // ---------------------------------------------------------------------------
1812 // ---------------------------------------------------------------------------
1814 bool wxDC
::DoBlit(wxCoord xdest
, wxCoord ydest
,
1815 wxCoord width
, wxCoord height
,
1816 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1817 int rop
, bool useMask
,
1818 wxCoord xsrcMask
, wxCoord ysrcMask
)
1820 #ifdef __WXMICROWIN__
1821 if (!GetHDC()) return FALSE
;
1824 wxMask
*mask
= NULL
;
1827 const wxBitmap
& bmp
= source
->m_selectedBitmap
;
1828 mask
= bmp
.GetMask();
1830 if ( !(bmp
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1832 // don't give assert here because this would break existing
1833 // programs - just silently ignore useMask parameter
1838 if (xsrcMask
== -1 && ysrcMask
== -1)
1840 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
1843 COLORREF old_textground
= ::GetTextColor(GetHdc());
1844 COLORREF old_background
= ::GetBkColor(GetHdc());
1845 if (m_textForegroundColour
.Ok())
1847 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1849 if (m_textBackgroundColour
.Ok())
1851 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1854 DWORD dwRop
= SRCCOPY
;
1857 case wxXOR
: dwRop
= SRCINVERT
; break;
1858 case wxINVERT
: dwRop
= DSTINVERT
; break;
1859 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1860 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1861 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1862 case wxSET
: dwRop
= WHITENESS
; break;
1863 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1864 case wxAND
: dwRop
= SRCAND
; break;
1865 case wxOR
: dwRop
= SRCPAINT
; break;
1866 case wxEQUIV
: dwRop
= 0x00990066; break;
1867 case wxNAND
: dwRop
= 0x007700E6; break;
1868 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1869 case wxCOPY
: dwRop
= SRCCOPY
; break;
1870 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1871 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1872 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1874 wxFAIL_MSG( wxT("unsupported logical function") );
1878 bool success
= FALSE
;
1883 // we want the part of the image corresponding to the mask to be
1884 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1885 // meaning of fg and bg is inverted which corresponds to wxWin notion
1886 // of the mask which is also contrary to the Windows one)
1888 // On some systems, MaskBlt succeeds yet is much much slower
1889 // than the wxWindows fall-back implementation. So we need
1890 // to be able to switch this on and off at runtime.
1891 #if wxUSE_SYSTEM_OPTIONS
1892 if (wxSystemOptions
::GetOptionInt(wxT("no-maskblt")) == 0)
1898 xdest
, ydest
, width
, height
,
1901 (HBITMAP
)mask
->GetMaskBitmap(),
1903 MAKEROP4(dwRop
, DSTCOPY
)
1910 // Blit bitmap with mask
1913 HBITMAP buffer_bmap
;
1915 #if wxUSE_DC_CACHEING
1916 // create a temp buffer bitmap and DCs to access it and the mask
1917 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, source
->GetHDC());
1918 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
1920 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
1921 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
1923 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
1926 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
1927 #else // !wxUSE_DC_CACHEING
1928 // create a temp buffer bitmap and DCs to access it and the mask
1929 dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1930 dc_buffer
= ::CreateCompatibleDC(GetHdc());
1931 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1932 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1933 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1934 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
1936 // copy dest to buffer
1937 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1938 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1940 wxLogLastError(wxT("BitBlt"));
1943 // copy src to buffer using selected raster op
1944 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1945 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1947 wxLogLastError(wxT("BitBlt"));
1950 // set masked area in buffer to BLACK (pixel value 0)
1951 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1952 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1953 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1954 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1956 wxLogLastError(wxT("BitBlt"));
1959 // set unmasked area in dest to BLACK
1960 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1961 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1962 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1963 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1965 wxLogLastError(wxT("BitBlt"));
1967 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1968 ::SetTextColor(GetHdc(), prevCol
);
1970 // OR buffer to dest
1971 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1972 (int)width
, (int)height
,
1973 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1976 wxLogLastError(wxT("BitBlt"));
1979 // tidy up temporary DCs and bitmap
1980 ::SelectObject(dc_mask
, hOldMaskBitmap
);
1981 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
1983 #if !wxUSE_DC_CACHEING
1985 ::DeleteDC(dc_mask
);
1986 ::DeleteDC(dc_buffer
);
1987 ::DeleteObject(buffer_bmap
);
1992 else // no mask, just BitBlt() it
1994 // use StretchBlt() if available
1995 if ( ::GetDeviceCaps(GetHdc(), RASTERCAPS
) & RC_STRETCHBLT
)
1997 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
1999 success
= ::StretchBlt
2002 xdest
, ydest
, width
, height
,
2004 xsrc
, ysrc
, width
, height
,
2014 (int)width
, (int)height
,
2023 wxLogLastError(wxT("BitBlt/StretchBlt"));
2027 ::SetTextColor(GetHdc(), old_textground
);
2028 ::SetBkColor(GetHdc(), old_background
);
2033 void wxDC
::DoGetSize(int *w
, int *h
) const
2035 #ifdef __WXMICROWIN__
2036 if (!GetHDC()) return;
2039 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2040 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2043 void wxDC
::DoGetSizeMM(int *w
, int *h
) const
2045 #ifdef __WXMICROWIN__
2046 if (!GetHDC()) return;
2049 // if we implement it in terms of DoGetSize() instead of directly using the
2050 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2051 // will also work for wxWindowDC and wxClientDC even though their size is
2052 // not the same as the total size of the screen
2053 int wPixels
, hPixels
;
2054 DoGetSize(&wPixels
, &hPixels
);
2058 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2060 wxCHECK_RET( wTotal
, _T("0 width device?") );
2062 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2067 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2069 wxCHECK_RET( hTotal
, _T("0 height device?") );
2071 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2075 wxSize wxDC
::GetPPI() const
2077 #ifdef __WXMICROWIN__
2078 if (!GetHDC()) return wxSize();
2081 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2082 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2084 return wxSize(x
, y
);
2087 // For use by wxWindows only, unless custom units are required.
2088 void wxDC
::SetLogicalScale(double x
, double y
)
2090 #ifdef __WXMICROWIN__
2091 if (!GetHDC()) return;
2094 m_logicalScaleX
= x
;
2095 m_logicalScaleY
= y
;
2098 #if wxUSE_DC_CACHEING
2101 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2102 * improve it in due course, either using arrays, or simply storing pointers to one
2103 * entry for the bitmap, and two for the DCs. -- JACS
2106 wxList wxDC
::sm_bitmapCache
;
2107 wxList wxDC
::sm_dcCache
;
2109 wxDCCacheEntry
::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2118 wxDCCacheEntry
::wxDCCacheEntry(WXHDC hDC
, int depth
)
2127 wxDCCacheEntry
::~wxDCCacheEntry()
2130 ::DeleteObject((HBITMAP
) m_bitmap
);
2132 ::DeleteDC((HDC
) m_dc
);
2135 wxDCCacheEntry
* wxDC
::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2137 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2138 wxNode
* node
= sm_bitmapCache
.First();
2141 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->Data();
2143 if (entry
->m_depth
== depth
)
2145 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2147 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2148 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2149 if ( !entry
->m_bitmap
)
2151 wxLogLastError(wxT("CreateCompatibleBitmap"));
2153 entry
->m_width
= w
; entry
->m_height
= h
;
2159 node
= node
->Next();
2161 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2164 wxLogLastError(wxT("CreateCompatibleBitmap"));
2166 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2167 AddToBitmapCache(entry
);
2171 wxDCCacheEntry
* wxDC
::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2173 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2174 wxNode
* node
= sm_dcCache
.First();
2177 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->Data();
2179 // Don't return the same one as we already have
2180 if (!notThis
|| (notThis
!= entry
))
2182 if (entry
->m_depth
== depth
)
2188 node
= node
->Next();
2190 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2193 wxLogLastError(wxT("CreateCompatibleDC"));
2195 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2196 AddToDCCache(entry
);
2200 void wxDC
::AddToBitmapCache(wxDCCacheEntry
* entry
)
2202 sm_bitmapCache
.Append(entry
);
2205 void wxDC
::AddToDCCache(wxDCCacheEntry
* entry
)
2207 sm_dcCache
.Append(entry
);
2210 void wxDC
::ClearCache()
2212 sm_dcCache
.DeleteContents(TRUE
);
2214 sm_dcCache
.DeleteContents(FALSE
);
2215 sm_bitmapCache
.DeleteContents(TRUE
);
2216 sm_bitmapCache
.Clear();
2217 sm_bitmapCache
.DeleteContents(FALSE
);
2220 // Clean up cache at app exit
2221 class wxDCModule
: public wxModule
2224 virtual bool OnInit() { return TRUE
; }
2225 virtual void OnExit() { wxDC
::ClearCache(); }
2228 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2231 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2234 // wxUSE_DC_CACHEING