1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
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"
48 #ifdef wxHAVE_RAW_BITMAP
49 #include "wx/rawbmp.h"
55 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
57 #if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__)
66 #define AC_SRC_ALPHA 1
69 /* Quaternary raster codes */
71 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
74 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxDCBase
)
76 // ---------------------------------------------------------------------------
78 // ---------------------------------------------------------------------------
80 static const int VIEWPORT_EXTENT
= 1000;
82 static const int MM_POINTS
= 9;
83 static const int MM_METRIC
= 10;
85 // usually this is defined in math.h
87 static const double M_PI
= 3.14159265358979323846;
90 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
91 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
92 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
94 // ----------------------------------------------------------------------------
95 // macros for logical <-> device coords conversion
96 // ----------------------------------------------------------------------------
99 We currently let Windows do all the translations itself so these macros are
100 not really needed (any more) but keep them to enhance readability of the
101 code by allowing to see where are the logical and where are the device
106 #define XLOG2DEV(x) (x)
107 #define YLOG2DEV(y) (y)
110 #define XDEV2LOG(x) (x)
111 #define YDEV2LOG(y) (y)
113 // ---------------------------------------------------------------------------
115 // ---------------------------------------------------------------------------
117 // convert degrees to radians
118 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
121 #ifdef wxHAVE_RAW_BITMAP
122 // our (limited) AlphaBlend() replacement
124 wxAlphaBlend(wxDC
& dc
, int x
, int y
, int w
, int h
, const wxBitmap
& bmp
);
127 // ----------------------------------------------------------------------------
129 // ----------------------------------------------------------------------------
131 // instead of duplicating the same code which sets and then restores text
132 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
133 // encapsulate this in a small helper class
135 // wxColourChanger: changes the text colours in the ctor if required and
136 // restores them in the dtor
137 class wxColourChanger
140 wxColourChanger(wxDC
& dc
);
146 COLORREF m_colFgOld
, m_colBgOld
;
151 // this class saves the old stretch blit mode during its life time
152 class StretchBltModeChanger
155 StretchBltModeChanger(HDC hdc
, int mode
)
158 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
160 wxLogLastError(_T("SetStretchBltMode"));
163 ~StretchBltModeChanger()
165 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
166 wxLogLastError(_T("SetStretchBltMode"));
175 // ===========================================================================
177 // ===========================================================================
179 // ----------------------------------------------------------------------------
181 // ----------------------------------------------------------------------------
183 wxColourChanger::wxColourChanger(wxDC
& dc
) : m_dc(dc
)
185 const wxBrush
& brush
= dc
.GetBrush();
186 if ( brush
.Ok() && brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
188 HDC hdc
= GetHdcOf(dc
);
189 m_colFgOld
= ::GetTextColor(hdc
);
190 m_colBgOld
= ::GetBkColor(hdc
);
192 // note that Windows convention is opposite to wxWindows one, this is
193 // why text colour becomes the background one and vice versa
194 const wxColour
& colFg
= dc
.GetTextForeground();
197 ::SetBkColor(hdc
, colFg
.GetPixel());
200 const wxColour
& colBg
= dc
.GetTextBackground();
203 ::SetTextColor(hdc
, colBg
.GetPixel());
207 dc
.GetBackgroundMode() == wxTRANSPARENT
? TRANSPARENT
210 // flag which telsl us to undo changes in the dtor
215 // nothing done, nothing to undo
220 wxColourChanger::~wxColourChanger()
224 // restore the colours we changed
225 HDC hdc
= GetHdcOf(m_dc
);
227 ::SetBkMode(hdc
, TRANSPARENT
);
228 ::SetTextColor(hdc
, m_colFgOld
);
229 ::SetBkColor(hdc
, m_colBgOld
);
233 // ---------------------------------------------------------------------------
235 // ---------------------------------------------------------------------------
237 // Default constructor
248 #endif // wxUSE_PALETTE
258 SelectOldObjects(m_hDC
);
260 // if we own the HDC, we delete it, otherwise we just release it
264 ::DeleteDC(GetHdc());
266 else // we don't own our HDC
270 ::ReleaseDC(GetHwndOf(m_canvas
), GetHdc());
274 // Must have been a wxScreenDC
275 ::ReleaseDC((HWND
) NULL
, GetHdc());
281 // This will select current objects out of the DC,
282 // which is what you have to do before deleting the
284 void wxDC::SelectOldObjects(WXHDC dc
)
290 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
292 if (m_selectedBitmap
.Ok())
294 m_selectedBitmap
.SetSelectedInto(NULL
);
301 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
306 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
311 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
318 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
321 #endif // wxUSE_PALETTE
324 m_brush
= wxNullBrush
;
327 m_palette
= wxNullPalette
;
328 #endif // wxUSE_PALETTE
330 m_backgroundBrush
= wxNullBrush
;
331 m_selectedBitmap
= wxNullBitmap
;
334 // ---------------------------------------------------------------------------
336 // ---------------------------------------------------------------------------
338 void wxDC::UpdateClipBox()
340 #ifdef __WXMICROWIN__
341 if (!GetHDC()) return;
345 ::GetClipBox(GetHdc(), &rect
);
347 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
348 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
349 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
350 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
353 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
354 void wxDC::SetClippingHrgn(WXHRGN hrgn
)
356 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
358 #ifdef __WXMICROWIN__
359 if (!GetHdc()) return;
360 #endif // __WXMICROWIN__
362 // note that we combine the new clipping region with the existing one: this
363 // is compatible with what the other ports do and is the documented
364 // behaviour now (starting with 2.3.3)
367 if ( !::GetClipBox(GetHdc(), &rectClip
) )
370 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
371 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
372 rectClip
.right
, rectClip
.bottom
);
374 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
376 ::SelectClipRgn(GetHdc(), hrgnDest
);
379 ::DeleteObject(hrgnClipOld
);
380 ::DeleteObject(hrgnDest
);
382 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
384 wxLogLastError(_T("ExtSelectClipRgn"));
395 void wxDC::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
397 // the region coords are always the device ones, so do the translation
400 // FIXME: possible +/-1 error here, to check!
401 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
403 LogicalToDeviceX(x
+ w
),
404 LogicalToDeviceY(y
+ h
));
407 wxLogLastError(_T("CreateRectRgn"));
411 SetClippingHrgn((WXHRGN
)hrgn
);
413 ::DeleteObject(hrgn
);
417 void wxDC::DoSetClippingRegionAsRegion(const wxRegion
& region
)
419 SetClippingHrgn(region
.GetHRGN());
422 void wxDC::DestroyClippingRegion()
424 #ifdef __WXMICROWIN__
425 if (!GetHDC()) return;
428 if (m_clipping
&& m_hDC
)
430 // TODO: this should restore the previous clipping region,
431 // so that OnPaint processing works correctly, and the update
432 // clipping region doesn't get destroyed after the first
433 // DestroyClippingRegion.
434 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
435 ::SelectClipRgn(GetHdc(), rgn
);
442 // ---------------------------------------------------------------------------
443 // query capabilities
444 // ---------------------------------------------------------------------------
446 bool wxDC::CanDrawBitmap() const
451 bool wxDC::CanGetTextExtent() const
453 #ifdef __WXMICROWIN__
454 // TODO Extend MicroWindows' GetDeviceCaps function
457 // What sort of display is it?
458 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
460 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
464 int wxDC::GetDepth() const
466 #ifdef __WXMICROWIN__
467 if (!GetHDC()) return 16;
470 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
473 // ---------------------------------------------------------------------------
475 // ---------------------------------------------------------------------------
479 #ifdef __WXMICROWIN__
480 if (!GetHDC()) return;
486 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
490 // No, I think we should simply ignore this if printing on e.g.
492 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
493 if (!m_selectedBitmap
.Ok())
496 rect
.left
= 0; rect
.top
= 0;
497 rect
.right
= m_selectedBitmap
.GetWidth();
498 rect
.bottom
= m_selectedBitmap
.GetHeight();
501 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
503 DWORD colour
= ::GetBkColor(GetHdc());
504 HBRUSH brush
= ::CreateSolidBrush(colour
);
505 ::FillRect(GetHdc(), &rect
, brush
);
506 ::DeleteObject(brush
);
508 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
509 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
511 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
512 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
513 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
514 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
515 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
518 bool wxDC::DoFloodFill(wxCoord x
, wxCoord y
, const wxColour
& col
, int style
)
520 #ifdef __WXMICROWIN__
521 if (!GetHDC()) return FALSE
;
524 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
526 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
527 : FLOODFILLBORDER
) ) ;
530 // quoting from the MSDN docs:
532 // Following are some of the reasons this function might fail:
534 // * The filling could not be completed.
535 // * The specified point has the boundary color specified by the
536 // crColor parameter (if FLOODFILLBORDER was requested).
537 // * The specified point does not have the color specified by
538 // crColor (if FLOODFILLSURFACE was requested)
539 // * The point is outside the clipping region that is, it is not
540 // visible on the device.
542 wxLogLastError(wxT("ExtFloodFill"));
545 CalcBoundingBox(x
, y
);
550 bool wxDC::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
552 #ifdef __WXMICROWIN__
553 if (!GetHDC()) return FALSE
;
556 wxCHECK_MSG( col
, FALSE
, _T("NULL colour parameter in wxDC::GetPixel") );
558 // get the color of the pixel
559 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
561 wxRGBToColour(*col
, pixelcolor
);
566 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
568 #ifdef __WXMICROWIN__
569 if (!GetHDC()) return;
572 wxCoord x1
= x
-VIEWPORT_EXTENT
;
573 wxCoord y1
= y
-VIEWPORT_EXTENT
;
574 wxCoord x2
= x
+VIEWPORT_EXTENT
;
575 wxCoord y2
= y
+VIEWPORT_EXTENT
;
577 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), NULL
);
578 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y
));
580 (void)MoveToEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), NULL
);
581 (void)LineTo(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y2
));
583 CalcBoundingBox(x1
, y1
);
584 CalcBoundingBox(x2
, y2
);
587 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
589 #ifdef __WXMICROWIN__
590 if (!GetHDC()) return;
593 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), NULL
);
594 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y2
));
596 CalcBoundingBox(x1
, y1
);
597 CalcBoundingBox(x2
, y2
);
600 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
601 // and ending at (x2, y2)
602 void wxDC::DoDrawArc(wxCoord x1
, wxCoord y1
,
603 wxCoord x2
, wxCoord y2
,
604 wxCoord xc
, wxCoord yc
)
606 #ifdef __WXMICROWIN__
607 if (!GetHDC()) return;
610 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
614 double radius
= (double)sqrt(dx
*dx
+dy
*dy
);
615 wxCoord r
= (wxCoord
)radius
;
617 // treat the special case of full circle separately
618 if ( x1
== x2
&& y1
== y2
)
620 DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
624 wxCoord xx1
= XLOG2DEV(x1
);
625 wxCoord yy1
= YLOG2DEV(y1
);
626 wxCoord xx2
= XLOG2DEV(x2
);
627 wxCoord yy2
= YLOG2DEV(y2
);
628 wxCoord xxc
= XLOG2DEV(xc
);
629 wxCoord yyc
= YLOG2DEV(yc
);
630 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
632 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
633 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
634 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
635 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
637 if ( m_brush
.Ok() && m_brush
.GetStyle() != wxTRANSPARENT
)
639 // Have to add 1 to bottom-right corner of rectangle
640 // to make semi-circles look right (crooked line otherwise).
641 // Unfortunately this is not a reliable method, depends
642 // on the size of shape.
643 // TODO: figure out why this happens!
644 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
648 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
651 CalcBoundingBox(xc
- r
, yc
- r
);
652 CalcBoundingBox(xc
+ r
, yc
+ r
);
655 void wxDC::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
656 wxCoord width
, wxCoord height
)
658 #ifdef __WXMICROWIN__
659 if (!GetHDC()) return;
662 wxCoord x2
= x1
+ width
,
665 #if defined(__WIN32__) && !defined(__SYMANTEC__) && !defined(__WXMICROWIN__)
672 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
674 // In WIN16, draw a cross
675 HPEN blackPen
= ::CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
676 HPEN whiteBrush
= (HPEN
)::GetStockObject(WHITE_BRUSH
);
677 HPEN hPenOld
= (HPEN
)::SelectObject(GetHdc(), blackPen
);
678 HPEN hBrushOld
= (HPEN
)::SelectObject(GetHdc(), whiteBrush
);
679 ::SetROP2(GetHdc(), R2_COPYPEN
);
680 Rectangle(GetHdc(), x1
, y1
, x2
, y2
);
681 MoveToEx(GetHdc(), x1
, y1
, NULL
);
682 LineTo(GetHdc(), x2
, y2
);
683 MoveToEx(GetHdc(), x2
, y1
, NULL
);
684 LineTo(GetHdc(), x1
, y2
);
685 ::SelectObject(GetHdc(), hPenOld
);
686 ::SelectObject(GetHdc(), hBrushOld
);
687 ::DeleteObject(blackPen
);
690 CalcBoundingBox(x1
, y1
);
691 CalcBoundingBox(x2
, y2
);
694 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
696 #ifdef __WXMICROWIN__
697 if (!GetHDC()) return;
700 COLORREF color
= 0x00ffffff;
703 color
= m_pen
.GetColour().GetPixel();
706 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
708 CalcBoundingBox(x
, y
);
711 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
713 #ifdef __WXMICROWIN__
714 if (!GetHDC()) return;
717 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
719 // Do things less efficiently if we have offsets
720 if (xoffset
!= 0 || yoffset
!= 0)
722 POINT
*cpoints
= new POINT
[n
];
724 for (i
= 0; i
< n
; i
++)
726 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
727 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
729 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
731 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
732 (void)Polygon(GetHdc(), cpoints
, n
);
733 SetPolyFillMode(GetHdc(),prev
);
739 for (i
= 0; i
< n
; i
++)
740 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
742 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
743 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
744 SetPolyFillMode(GetHdc(),prev
);
748 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
750 #ifdef __WXMICROWIN__
751 if (!GetHDC()) return;
754 // Do things less efficiently if we have offsets
755 if (xoffset
!= 0 || yoffset
!= 0)
757 POINT
*cpoints
= new POINT
[n
];
759 for (i
= 0; i
< n
; i
++)
761 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
762 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
764 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
766 (void)Polyline(GetHdc(), cpoints
, n
);
772 for (i
= 0; i
< n
; i
++)
773 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
775 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
779 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
781 #ifdef __WXMICROWIN__
782 if (!GetHDC()) return;
785 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
787 wxCoord x2
= x
+ width
;
788 wxCoord y2
= y
+ height
;
790 if ((m_logicalFunction
== wxCOPY
) && (m_pen
.GetStyle() == wxTRANSPARENT
))
793 rect
.left
= XLOG2DEV(x
);
794 rect
.top
= YLOG2DEV(y
);
795 rect
.right
= XLOG2DEV(x2
);
796 rect
.bottom
= YLOG2DEV(y2
);
797 (void)FillRect(GetHdc(), &rect
, (HBRUSH
)m_brush
.GetResourceHandle() );
801 // Windows draws the filled rectangles without outline (i.e. drawn with a
802 // transparent pen) one pixel smaller in both directions and we want them
803 // to have the same size regardless of which pen is used - adjust
805 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
806 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
812 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
816 CalcBoundingBox(x
, y
);
817 CalcBoundingBox(x2
, y2
);
820 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
822 #ifdef __WXMICROWIN__
823 if (!GetHDC()) return;
826 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
828 // Now, a negative radius value is interpreted to mean
829 // 'the proportion of the smallest X or Y dimension'
833 double smallest
= 0.0;
838 radius
= (- radius
* smallest
);
841 wxCoord x2
= (x
+width
);
842 wxCoord y2
= (y
+height
);
844 // Windows draws the filled rectangles without outline (i.e. drawn with a
845 // transparent pen) one pixel smaller in both directions and we want them
846 // to have the same size regardless of which pen is used - adjust
847 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
853 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
854 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
856 CalcBoundingBox(x
, y
);
857 CalcBoundingBox(x2
, y2
);
860 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
862 #ifdef __WXMICROWIN__
863 if (!GetHDC()) return;
866 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
868 wxCoord x2
= (x
+width
);
869 wxCoord y2
= (y
+height
);
871 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
873 CalcBoundingBox(x
, y
);
874 CalcBoundingBox(x2
, y2
);
877 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
878 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
880 #ifdef __WXMICROWIN__
881 if (!GetHDC()) return;
884 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
889 int rx1
= XLOG2DEV(x
+w
/2);
890 int ry1
= YLOG2DEV(y
+h
/2);
897 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
898 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
899 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
900 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
902 // draw pie with NULL_PEN first and then outline otherwise a line is
903 // drawn from the start and end points to the centre
904 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
907 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
912 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
913 rx1
, ry1
-1, rx2
, ry2
-1);
916 ::SelectObject(GetHdc(), hpenOld
);
918 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
921 CalcBoundingBox(x
, y
);
922 CalcBoundingBox(x2
, y2
);
925 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
927 #ifdef __WXMICROWIN__
928 if (!GetHDC()) return;
931 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
934 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
936 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
939 CalcBoundingBox(x
, y
);
940 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
943 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
945 #ifdef __WXMICROWIN__
946 if (!GetHDC()) return;
949 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
951 int width
= bmp
.GetWidth(),
952 height
= bmp
.GetHeight();
954 HBITMAP hbmpMask
= 0;
958 #endif // wxUSE_PALETTE
960 // do we have AlphaBlend() and company in the headers?
962 if ( bmp
.HasAlpha() )
964 // yes, now try to see if we have it during run-time
966 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
970 // bitmaps can be drawn only from GUI thread so there is no need to
971 // protect this static variable from multiple threads
972 static bool s_triedToLoad
= FALSE
;
973 static AlphaBlend_t pfnAlphaBlend
= NULL
;
974 if ( !s_triedToLoad
)
976 s_triedToLoad
= TRUE
;
978 // don't give errors about the DLL being unavailable, we're
979 // prepared to handle this
982 wxDynamicLibrary
dll(_T("msimg32.dll"));
983 if ( dll
.IsLoaded() )
985 pfnAlphaBlend
= (AlphaBlend_t
)dll
.GetSymbol(_T("AlphaBlend"));
988 // we must keep the DLL loaded if we want to be able to
989 // call AlphaBlend() so just never unload it at all, not a
999 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1002 bf
.BlendOp
= AC_SRC_OVER
;
1004 bf
.SourceConstantAlpha
= 0xff;
1005 bf
.AlphaFormat
= AC_SRC_ALPHA
;
1007 if ( !pfnAlphaBlend(GetHdc(), x
, y
, width
, height
,
1008 hdcMem
, 0, 0, width
, height
,
1011 wxLogLastError(_T("AlphaBlend"));
1014 else // use our own (probably much slower) implementation
1016 #ifdef wxHAVE_RAW_BITMAP
1017 wxAlphaBlend(*this, x
, y
, width
, height
, bmp
);
1019 wxLogLastError(_T("AlphaBlend not available with this compiler setup"));
1020 #endif //#ifdef wxHAVE_RAW_BITMAP
1025 #endif // defined(AC_SRC_OVER)
1029 wxMask
*mask
= bmp
.GetMask();
1031 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1035 // don't give assert here because this would break existing
1036 // programs - just silently ignore useMask parameter
1043 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1045 // On some systems, MaskBlt succeeds yet is much much slower
1046 // than the wxWindows fall-back implementation. So we need
1047 // to be able to switch this on and off at runtime.
1049 #if wxUSE_SYSTEM_OPTIONS
1050 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1054 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1055 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1057 wxPalette
*pal
= bmp
.GetPalette();
1058 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1060 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1061 ::RealizePalette(hdcMem
);
1063 #endif // wxUSE_PALETTE
1065 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1068 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1072 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1073 #endif // wxUSE_PALETTE
1075 ::SelectObject(hdcMem
, hOldBitmap
);
1082 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1085 memDC
.SelectObject(bmp
);
1087 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1089 memDC
.SelectObject(wxNullBitmap
);
1092 else // no mask, just use BitBlt()
1095 HDC memdc
= ::CreateCompatibleDC( cdc
);
1096 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1098 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1100 COLORREF old_textground
= ::GetTextColor(GetHdc());
1101 COLORREF old_background
= ::GetBkColor(GetHdc());
1102 if (m_textForegroundColour
.Ok())
1104 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1106 if (m_textBackgroundColour
.Ok())
1108 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1112 wxPalette
*pal
= bmp
.GetPalette();
1113 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1115 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1116 ::RealizePalette(memdc
);
1118 #endif // wxUSE_PALETTE
1120 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1121 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1125 ::SelectPalette(memdc
, oldPal
, FALSE
);
1126 #endif // wxUSE_PALETTE
1128 ::SelectObject( memdc
, hOldBitmap
);
1129 ::DeleteDC( memdc
);
1131 ::SetTextColor(GetHdc(), old_textground
);
1132 ::SetBkColor(GetHdc(), old_background
);
1136 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1138 #ifdef __WXMICROWIN__
1139 if (!GetHDC()) return;
1142 DrawAnyText(text
, x
, y
);
1144 // update the bounding box
1145 CalcBoundingBox(x
, y
);
1148 GetTextExtent(text
, &w
, &h
);
1149 CalcBoundingBox(x
+ w
, y
+ h
);
1152 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1154 #ifdef __WXMICROWIN__
1155 if (!GetHDC()) return;
1158 // prepare for drawing the text
1159 if ( m_textForegroundColour
.Ok() )
1160 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
1162 DWORD old_background
= 0;
1163 if ( m_textBackgroundColour
.Ok() )
1165 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1168 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
1171 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
1172 text
.c_str(), text
.length()) == 0 )
1174 wxLogLastError(wxT("TextOut"));
1177 // restore the old parameters (text foreground colour may be left because
1178 // it never is set to anything else, but background should remain
1179 // transparent even if we just drew an opaque string)
1180 if ( m_textBackgroundColour
.Ok() )
1181 (void)SetBkColor(GetHdc(), old_background
);
1183 SetBkMode(GetHdc(), TRANSPARENT
);
1186 void wxDC::DoDrawRotatedText(const wxString
& text
,
1187 wxCoord x
, wxCoord y
,
1190 #ifdef __WXMICROWIN__
1191 if (!GetHDC()) return;
1194 // we test that we have some font because otherwise we should still use the
1195 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1196 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1197 // font for drawing rotated fonts unfortunately)
1198 if ( (angle
== 0.0) && m_font
.Ok() )
1200 DoDrawText(text
, x
, y
);
1202 #ifndef __WXMICROWIN__
1205 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1206 // because it's not TrueType and so can't have non zero
1207 // orientation/escapement under Win9x
1208 wxFont font
= m_font
.Ok() ? m_font
: *wxSWISS_FONT
;
1209 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1211 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1213 wxLogLastError(wxT("GetObject(hfont)"));
1216 // GDI wants the angle in tenth of degree
1217 long angle10
= (long)(angle
* 10);
1218 lf
.lfEscapement
= angle10
;
1219 lf
. lfOrientation
= angle10
;
1221 hfont
= ::CreateFontIndirect(&lf
);
1224 wxLogLastError(wxT("CreateFont"));
1228 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1230 DrawAnyText(text
, x
, y
);
1232 (void)::SelectObject(GetHdc(), hfontOld
);
1233 (void)::DeleteObject(hfont
);
1236 // call the bounding box by adding all four vertices of the rectangle
1237 // containing the text to it (simpler and probably not slower than
1238 // determining which of them is really topmost/leftmost/...)
1240 GetTextExtent(text
, &w
, &h
);
1242 double rad
= DegToRad(angle
);
1244 // "upper left" and "upper right"
1245 CalcBoundingBox(x
, y
);
1246 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(h
*sin(rad
)));
1248 // "bottom left" and "bottom right"
1249 x
+= (wxCoord
)(h
*sin(rad
));
1250 y
+= (wxCoord
)(h
*cos(rad
));
1251 CalcBoundingBox(x
, y
);
1252 CalcBoundingBox(x
+ wxCoord(h
*sin(rad
)), y
+ wxCoord(h
*cos(rad
)));
1257 // ---------------------------------------------------------------------------
1259 // ---------------------------------------------------------------------------
1263 void wxDC::DoSelectPalette(bool realize
)
1265 #ifdef __WXMICROWIN__
1266 if (!GetHDC()) return;
1269 // Set the old object temporarily, in case the assignment deletes an object
1270 // that's not yet selected out.
1273 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1277 if ( m_palette
.Ok() )
1279 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1280 GetHpaletteOf(m_palette
),
1283 m_oldPalette
= (WXHPALETTE
) oldPal
;
1286 ::RealizePalette(GetHdc());
1290 void wxDC::SetPalette(const wxPalette
& palette
)
1294 m_palette
= palette
;
1295 DoSelectPalette(TRUE
);
1299 void wxDC::InitializePalette()
1301 if ( wxDisplayDepth() <= 8 )
1303 // look for any window or parent that has a custom palette. If any has
1304 // one then we need to use it in drawing operations
1305 wxWindow
*win
= m_canvas
->GetAncestorWithCustomPalette();
1307 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1308 if ( m_hasCustomPalette
)
1310 m_palette
= win
->GetPalette();
1312 // turn on MSW translation for this palette
1318 #endif // wxUSE_PALETTE
1320 void wxDC::SetFont(const wxFont
& the_font
)
1322 #ifdef __WXMICROWIN__
1323 if (!GetHDC()) return;
1326 // Set the old object temporarily, in case the assignment deletes an object
1327 // that's not yet selected out.
1330 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1339 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1343 if (m_font
.Ok() && m_font
.GetResourceHandle())
1345 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
1346 if (f
== (HFONT
) NULL
)
1348 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
1351 m_oldFont
= (WXHFONT
) f
;
1355 void wxDC::SetPen(const wxPen
& pen
)
1357 #ifdef __WXMICROWIN__
1358 if (!GetHDC()) return;
1361 // Set the old object temporarily, in case the assignment deletes an object
1362 // that's not yet selected out.
1365 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1374 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1380 if (m_pen
.GetResourceHandle())
1382 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
1384 m_oldPen
= (WXHPEN
) p
;
1389 void wxDC::SetBrush(const wxBrush
& brush
)
1391 #ifdef __WXMICROWIN__
1392 if (!GetHDC()) return;
1395 // Set the old object temporarily, in case the assignment deletes an object
1396 // that's not yet selected out.
1399 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1408 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1414 // to make sure the brush is alligned with the logical coordinates
1415 wxBitmap
*stipple
= m_brush
.GetStipple();
1416 if ( stipple
&& stipple
->Ok() )
1419 ::SetBrushOrgEx(GetHdc(),
1420 m_deviceOriginX
% stipple
->GetWidth(),
1421 m_deviceOriginY
% stipple
->GetHeight(),
1422 NULL
); // don't need previous brush origin
1424 ::SetBrushOrg(GetHdc(),
1425 m_deviceOriginX
% stipple
->GetWidth(),
1426 m_deviceOriginY
% stipple
->GetHeight());
1430 if ( m_brush
.GetResourceHandle() )
1433 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
1435 m_oldBrush
= (WXHBRUSH
) b
;
1440 void wxDC::SetBackground(const wxBrush
& brush
)
1442 #ifdef __WXMICROWIN__
1443 if (!GetHDC()) return;
1446 m_backgroundBrush
= brush
;
1448 if (!m_backgroundBrush
.Ok())
1453 bool customColours
= TRUE
;
1454 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
1455 // change background colours from the control-panel specified colours.
1456 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
1457 customColours
= FALSE
;
1461 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
1463 m_canvas
->SetTransparent(TRUE
);
1467 // New behaviour, 10/2/99: setting the background brush of a DC
1468 // doesn't affect the window background colour. However,
1469 // I'm leaving in the transparency setting because it's needed by
1470 // various controls (e.g. wxStaticText) to determine whether to draw
1471 // transparently or not. TODO: maybe this should be a new function
1472 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1474 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1475 m_canvas
->SetTransparent(FALSE
);
1479 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1481 (void)SetBkColor(GetHdc(), new_color
);
1485 void wxDC::SetBackgroundMode(int mode
)
1487 #ifdef __WXMICROWIN__
1488 if (!GetHDC()) return;
1491 m_backgroundMode
= mode
;
1493 // SetBackgroundColour now only refers to text background
1494 // and m_backgroundMode is used there
1497 void wxDC::SetLogicalFunction(int function
)
1499 #ifdef __WXMICROWIN__
1500 if (!GetHDC()) return;
1503 m_logicalFunction
= function
;
1508 void wxDC::SetRop(WXHDC dc
)
1510 if ( !dc
|| m_logicalFunction
< 0 )
1515 switch (m_logicalFunction
)
1517 case wxCLEAR
: rop
= R2_BLACK
; break;
1518 case wxXOR
: rop
= R2_XORPEN
; break;
1519 case wxINVERT
: rop
= R2_NOT
; break;
1520 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1521 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1522 case wxCOPY
: rop
= R2_COPYPEN
; break;
1523 case wxAND
: rop
= R2_MASKPEN
; break;
1524 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1525 case wxNO_OP
: rop
= R2_NOP
; break;
1526 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1527 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1528 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1529 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1530 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1531 case wxOR
: rop
= R2_MERGEPEN
; break;
1532 case wxSET
: rop
= R2_WHITE
; break;
1535 wxFAIL_MSG( wxT("unsupported logical function") );
1539 SetROP2(GetHdc(), rop
);
1542 bool wxDC::StartDoc(const wxString
& WXUNUSED(message
))
1544 // We might be previewing, so return TRUE to let it continue.
1552 void wxDC::StartPage()
1556 void wxDC::EndPage()
1560 // ---------------------------------------------------------------------------
1562 // ---------------------------------------------------------------------------
1564 wxCoord
wxDC::GetCharHeight() const
1566 #ifdef __WXMICROWIN__
1567 if (!GetHDC()) return 0;
1570 TEXTMETRIC lpTextMetric
;
1572 GetTextMetrics(GetHdc(), &lpTextMetric
);
1574 return lpTextMetric
.tmHeight
;
1577 wxCoord
wxDC::GetCharWidth() const
1579 #ifdef __WXMICROWIN__
1580 if (!GetHDC()) return 0;
1583 TEXTMETRIC lpTextMetric
;
1585 GetTextMetrics(GetHdc(), &lpTextMetric
);
1587 return lpTextMetric
.tmAveCharWidth
;
1590 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1591 wxCoord
*descent
, wxCoord
*externalLeading
,
1594 #ifdef __WXMICROWIN__
1599 if (descent
) *descent
= 0;
1600 if (externalLeading
) *externalLeading
= 0;
1603 #endif // __WXMICROWIN__
1608 wxASSERT_MSG( font
->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1610 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1612 else // don't change the font
1620 GetTextExtentPoint(GetHdc(), string
, string
.length(), &sizeRect
);
1621 GetTextMetrics(GetHdc(), &tm
);
1628 *descent
= tm
.tmDescent
;
1629 if (externalLeading
)
1630 *externalLeading
= tm
.tmExternalLeading
;
1634 ::SelectObject(GetHdc(), hfontOld
);
1638 void wxDC::SetMapMode(int mode
)
1640 #ifdef __WXMICROWIN__
1641 if (!GetHDC()) return;
1644 m_mappingMode
= mode
;
1646 if ( mode
== wxMM_TEXT
)
1649 m_logicalScaleY
= 1.0;
1651 else // need to do some calculations
1653 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1654 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1655 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1656 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1658 if ( (mm_width
== 0) || (mm_height
== 0) )
1660 // we can't calculate mm2pixels[XY] then!
1664 double mm2pixelsX
= pixel_width
/ mm_width
,
1665 mm2pixelsY
= pixel_height
/ mm_height
;
1670 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1671 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1675 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1676 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1680 m_logicalScaleX
= mm2pixelsX
;
1681 m_logicalScaleY
= mm2pixelsY
;
1685 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1686 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1690 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1694 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1695 // cases we could do with MM_TEXT and in the remaining 0.9% with
1696 // MM_ISOTROPIC (TODO!)
1697 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1699 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1700 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1702 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1703 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1705 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1706 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1709 void wxDC::SetUserScale(double x
, double y
)
1711 #ifdef __WXMICROWIN__
1712 if (!GetHDC()) return;
1715 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1721 SetMapMode(m_mappingMode
);
1724 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1726 #ifdef __WXMICROWIN__
1727 if (!GetHDC()) return;
1730 int signX
= xLeftRight
? 1 : -1,
1731 signY
= yBottomUp
? -1 : 1;
1733 if ( signX
!= m_signX
|| signY
!= m_signY
)
1738 SetMapMode(m_mappingMode
);
1742 void wxDC::SetSystemScale(double x
, double y
)
1744 #ifdef __WXMICROWIN__
1745 if (!GetHDC()) return;
1748 if ( x
== m_scaleX
&& y
== m_scaleY
)
1754 SetMapMode(m_mappingMode
);
1757 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1759 #ifdef __WXMICROWIN__
1760 if (!GetHDC()) return;
1763 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1766 m_logicalOriginX
= x
;
1767 m_logicalOriginY
= y
;
1769 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1772 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1774 #ifdef __WXMICROWIN__
1775 if (!GetHDC()) return;
1778 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
1781 m_deviceOriginX
= x
;
1782 m_deviceOriginY
= y
;
1784 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1787 // ---------------------------------------------------------------------------
1788 // coordinates transformations
1789 // ---------------------------------------------------------------------------
1791 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1793 return DeviceToLogicalXRel(x
- m_deviceOriginX
)*m_signX
+ m_logicalOriginX
;
1796 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1798 // axis orientation is not taken into account for conversion of a distance
1799 return (wxCoord
)(x
/ (m_logicalScaleX
*m_userScaleX
*m_scaleX
));
1802 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1804 return DeviceToLogicalYRel(y
- m_deviceOriginY
)*m_signY
+ m_logicalOriginY
;
1807 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1809 // axis orientation is not taken into account for conversion of a distance
1810 return (wxCoord
)( y
/ (m_logicalScaleY
*m_userScaleY
*m_scaleY
));
1813 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1815 return LogicalToDeviceXRel(x
- m_logicalOriginX
)*m_signX
+ m_deviceOriginX
;
1818 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1820 // axis orientation is not taken into account for conversion of a distance
1821 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_scaleX
);
1824 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1826 return LogicalToDeviceYRel(y
- m_logicalOriginY
)*m_signY
+ m_deviceOriginY
;
1829 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1831 // axis orientation is not taken into account for conversion of a distance
1832 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_scaleY
);
1835 // ---------------------------------------------------------------------------
1837 // ---------------------------------------------------------------------------
1839 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1840 wxCoord width
, wxCoord height
,
1841 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1842 int rop
, bool useMask
,
1843 wxCoord xsrcMask
, wxCoord ysrcMask
)
1845 #ifdef __WXMICROWIN__
1846 if (!GetHDC()) return FALSE
;
1849 const wxBitmap
& bmpSrc
= source
->m_selectedBitmap
;
1851 wxMask
*mask
= NULL
;
1854 mask
= bmpSrc
.GetMask();
1856 if ( !(bmpSrc
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1858 // don't give assert here because this would break existing
1859 // programs - just silently ignore useMask parameter
1864 if (xsrcMask
== -1 && ysrcMask
== -1)
1866 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
1869 COLORREF old_textground
= ::GetTextColor(GetHdc());
1870 COLORREF old_background
= ::GetBkColor(GetHdc());
1871 if (m_textForegroundColour
.Ok())
1873 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1875 if (m_textBackgroundColour
.Ok())
1877 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1880 DWORD dwRop
= SRCCOPY
;
1883 case wxXOR
: dwRop
= SRCINVERT
; break;
1884 case wxINVERT
: dwRop
= DSTINVERT
; break;
1885 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1886 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1887 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1888 case wxSET
: dwRop
= WHITENESS
; break;
1889 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1890 case wxAND
: dwRop
= SRCAND
; break;
1891 case wxOR
: dwRop
= SRCPAINT
; break;
1892 case wxEQUIV
: dwRop
= 0x00990066; break;
1893 case wxNAND
: dwRop
= 0x007700E6; break;
1894 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1895 case wxCOPY
: dwRop
= SRCCOPY
; break;
1896 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1897 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1898 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1900 wxFAIL_MSG( wxT("unsupported logical function") );
1904 bool success
= FALSE
;
1909 // we want the part of the image corresponding to the mask to be
1910 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1911 // meaning of fg and bg is inverted which corresponds to wxWin notion
1912 // of the mask which is also contrary to the Windows one)
1914 // On some systems, MaskBlt succeeds yet is much much slower
1915 // than the wxWindows fall-back implementation. So we need
1916 // to be able to switch this on and off at runtime.
1917 #if wxUSE_SYSTEM_OPTIONS
1918 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1924 xdest
, ydest
, width
, height
,
1927 (HBITMAP
)mask
->GetMaskBitmap(),
1929 MAKEROP4(dwRop
, DSTCOPY
)
1936 // Blit bitmap with mask
1939 HBITMAP buffer_bmap
;
1941 #if wxUSE_DC_CACHEING
1942 // create a temp buffer bitmap and DCs to access it and the mask
1943 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, source
->GetHDC());
1944 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
1946 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
1947 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
1949 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
1952 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
1953 #else // !wxUSE_DC_CACHEING
1954 // create a temp buffer bitmap and DCs to access it and the mask
1955 dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1956 dc_buffer
= ::CreateCompatibleDC(GetHdc());
1957 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1958 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1959 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1960 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
1962 // copy dest to buffer
1963 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1964 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1966 wxLogLastError(wxT("BitBlt"));
1969 // copy src to buffer using selected raster op
1970 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1971 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1973 wxLogLastError(wxT("BitBlt"));
1976 // set masked area in buffer to BLACK (pixel value 0)
1977 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1978 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1979 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1980 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1982 wxLogLastError(wxT("BitBlt"));
1985 // set unmasked area in dest to BLACK
1986 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1987 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1988 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1989 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1991 wxLogLastError(wxT("BitBlt"));
1993 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1994 ::SetTextColor(GetHdc(), prevCol
);
1996 // OR buffer to dest
1997 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1998 (int)width
, (int)height
,
1999 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2002 wxLogLastError(wxT("BitBlt"));
2005 // tidy up temporary DCs and bitmap
2006 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2007 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2009 #if !wxUSE_DC_CACHEING
2011 ::DeleteDC(dc_mask
);
2012 ::DeleteDC(dc_buffer
);
2013 ::DeleteObject(buffer_bmap
);
2018 else // no mask, just BitBlt() it
2020 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2021 // use StretchBlt() if available and finally fall back to BitBlt()
2022 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2023 if ( bmpSrc
.Ok() && (caps
& RC_STRETCHDIB
) )
2028 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2030 &ds
) == sizeof(ds
) )
2032 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2034 if ( ::StretchDIBits(GetHdc(),
2040 (LPBITMAPINFO
)&ds
.dsBmih
,
2043 ) == (int)GDI_ERROR
)
2045 wxLogLastError(wxT("StretchDIBits"));
2054 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2056 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2061 xdest
, ydest
, width
, height
,
2063 xsrc
, ysrc
, width
, height
,
2067 wxLogLastError(_T("StretchBlt"));
2081 (int)width
, (int)height
,
2087 wxLogLastError(_T("BitBlt"));
2096 ::SetTextColor(GetHdc(), old_textground
);
2097 ::SetBkColor(GetHdc(), old_background
);
2102 void wxDC::DoGetSize(int *w
, int *h
) const
2104 #ifdef __WXMICROWIN__
2105 if (!GetHDC()) return;
2108 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2109 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2112 void wxDC::DoGetSizeMM(int *w
, int *h
) const
2114 #ifdef __WXMICROWIN__
2115 if (!GetHDC()) return;
2118 // if we implement it in terms of DoGetSize() instead of directly using the
2119 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2120 // will also work for wxWindowDC and wxClientDC even though their size is
2121 // not the same as the total size of the screen
2122 int wPixels
, hPixels
;
2123 DoGetSize(&wPixels
, &hPixels
);
2127 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2129 wxCHECK_RET( wTotal
, _T("0 width device?") );
2131 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2136 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2138 wxCHECK_RET( hTotal
, _T("0 height device?") );
2140 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2144 wxSize
wxDC::GetPPI() const
2146 #ifdef __WXMICROWIN__
2147 if (!GetHDC()) return wxSize();
2150 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2151 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2153 return wxSize(x
, y
);
2156 // For use by wxWindows only, unless custom units are required.
2157 void wxDC::SetLogicalScale(double x
, double y
)
2159 #ifdef __WXMICROWIN__
2160 if (!GetHDC()) return;
2163 m_logicalScaleX
= x
;
2164 m_logicalScaleY
= y
;
2167 // ----------------------------------------------------------------------------
2169 // ----------------------------------------------------------------------------
2171 #if wxUSE_DC_CACHEING
2174 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2175 * improve it in due course, either using arrays, or simply storing pointers to one
2176 * entry for the bitmap, and two for the DCs. -- JACS
2179 wxList
wxDC::sm_bitmapCache
;
2180 wxList
wxDC::sm_dcCache
;
2182 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2191 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2200 wxDCCacheEntry::~wxDCCacheEntry()
2203 ::DeleteObject((HBITMAP
) m_bitmap
);
2205 ::DeleteDC((HDC
) m_dc
);
2208 wxDCCacheEntry
* wxDC::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2210 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2211 wxNode
* node
= sm_bitmapCache
.GetFirst();
2214 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2216 if (entry
->m_depth
== depth
)
2218 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2220 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2221 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2222 if ( !entry
->m_bitmap
)
2224 wxLogLastError(wxT("CreateCompatibleBitmap"));
2226 entry
->m_width
= w
; entry
->m_height
= h
;
2232 node
= node
->GetNext();
2234 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2237 wxLogLastError(wxT("CreateCompatibleBitmap"));
2239 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2240 AddToBitmapCache(entry
);
2244 wxDCCacheEntry
* wxDC::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2246 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2247 wxNode
* node
= sm_dcCache
.GetFirst();
2250 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2252 // Don't return the same one as we already have
2253 if (!notThis
|| (notThis
!= entry
))
2255 if (entry
->m_depth
== depth
)
2261 node
= node
->GetNext();
2263 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2266 wxLogLastError(wxT("CreateCompatibleDC"));
2268 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2269 AddToDCCache(entry
);
2273 void wxDC::AddToBitmapCache(wxDCCacheEntry
* entry
)
2275 sm_bitmapCache
.Append(entry
);
2278 void wxDC::AddToDCCache(wxDCCacheEntry
* entry
)
2280 sm_dcCache
.Append(entry
);
2283 void wxDC::ClearCache()
2285 sm_dcCache
.DeleteContents(TRUE
);
2287 sm_dcCache
.DeleteContents(FALSE
);
2288 sm_bitmapCache
.DeleteContents(TRUE
);
2289 sm_bitmapCache
.Clear();
2290 sm_bitmapCache
.DeleteContents(FALSE
);
2293 // Clean up cache at app exit
2294 class wxDCModule
: public wxModule
2297 virtual bool OnInit() { return TRUE
; }
2298 virtual void OnExit() { wxDC::ClearCache(); }
2301 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2304 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2306 #endif // wxUSE_DC_CACHEING
2308 // ----------------------------------------------------------------------------
2309 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2310 // ----------------------------------------------------------------------------
2312 #ifdef wxHAVE_RAW_BITMAP
2314 wxAlphaBlend(wxDC
& dc
, int xDst
, int yDst
, int w
, int h
, const wxBitmap
& bmpSrc
)
2316 // get the destination DC pixels
2317 wxBitmap
bmpDst(w
, h
, 32 /* force creating RGBA DIB */);
2319 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2321 if ( !::BitBlt(hdcMem
, 0, 0, w
, h
, GetHdcOf(dc
), 0, 0, SRCCOPY
) )
2323 wxLogLastError(_T("BitBlt"));
2326 // combine them with the source bitmap using alpha
2327 wxAlphaPixelData
dataDst(bmpDst
),
2328 dataSrc((wxBitmap
&)bmpSrc
);
2330 wxCHECK_RET( dataDst
&& dataSrc
,
2331 _T("failed to get raw data in wxAlphaBlend") );
2333 wxAlphaPixelData::Iterator
pDst(dataDst
),
2336 for ( int y
= 0; y
< h
; y
++ )
2338 wxAlphaPixelData::Iterator pDstRowStart
= pDst
,
2339 pSrcRowStart
= pSrc
;
2341 for ( int x
= 0; x
< w
; x
++ )
2343 // note that source bitmap uses premultiplied alpha (as required by
2344 // the real AlphaBlend)
2345 const unsigned beta
= 255 - pSrc
.Alpha();
2347 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2348 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2349 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2355 pDst
= pDstRowStart
;
2356 pSrc
= pSrcRowStart
;
2357 pDst
.OffsetY(dataDst
, 1);
2358 pSrc
.OffsetY(dataSrc
, 1);
2361 // and finally blit them back to the destination DC
2362 if ( !::BitBlt(GetHdcOf(dc
), xDst
, yDst
, w
, h
, hdcMem
, 0, 0, SRCCOPY
) )
2364 wxLogLastError(_T("BitBlt"));
2368 #endif // #ifdef wxHAVE_RAW_BITMAP