1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dc.cpp
3 // Purpose: wxDC class for MSW port
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
28 #include "wx/msw/wrapcdlg.h"
30 #include "wx/window.h"
32 #include "wx/dialog.h"
34 #include "wx/bitmap.h"
35 #include "wx/dcmemory.h"
38 #include "wx/dcprint.h"
39 #include "wx/module.h"
42 #include "wx/msw/dc.h"
43 #include "wx/sysopt.h"
44 #include "wx/dynlib.h"
46 #ifdef wxHAS_RAW_BITMAP
47 #include "wx/rawbmp.h"
57 #define AC_SRC_ALPHA 1
64 /* Quaternary raster codes */
66 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
69 // apparently with MicroWindows it is possible that HDC is 0 so we have to
70 // check for this ourselves
72 #define WXMICROWIN_CHECK_HDC if ( !GetHDC() ) return;
73 #define WXMICROWIN_CHECK_HDC_RET(x) if ( !GetHDC() ) return x;
75 #define WXMICROWIN_CHECK_HDC
76 #define WXMICROWIN_CHECK_HDC_RET(x)
79 IMPLEMENT_ABSTRACT_CLASS(wxMSWDCImpl
, wxDCImpl
)
81 // ---------------------------------------------------------------------------
83 // ---------------------------------------------------------------------------
85 static const int VIEWPORT_EXTENT
= 1000;
87 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
88 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
89 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
91 // ----------------------------------------------------------------------------
92 // macros for logical <-> device coords conversion
93 // ----------------------------------------------------------------------------
96 We currently let Windows do all the translations itself so these macros are
97 not really needed (any more) but keep them to enhance readability of the
98 code by allowing to see where are the logical and where are the device
103 #define XLOG2DEV(x) ((x-m_logicalOriginX)*m_signX)
104 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
105 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
106 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
108 #define XLOG2DEV(x) (x)
109 #define YLOG2DEV(y) (y)
110 #define XDEV2LOG(x) (x)
111 #define YDEV2LOG(y) (y)
114 // ---------------------------------------------------------------------------
116 // ---------------------------------------------------------------------------
118 // convert degrees to radians
119 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
121 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
123 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
124 // to pass it to this function but as we already have it at the point
125 // of call anyhow we do
127 // return true if we could draw the bitmap in one way or the other, false
129 static bool AlphaBlt(HDC hdcDst
,
130 int x
, int y
, int dstWidth
, int dstHeight
,
132 int srcWidth
, int srcHeight
,
134 const wxBitmap
& bmp
);
136 #ifdef wxHAS_RAW_BITMAP
138 // our (limited) AlphaBlend() replacement for Windows versions not providing it
140 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
141 int dstWidth
, int dstHeight
,
143 int srcWidth
, int srcHeight
,
144 const wxBitmap
& bmpSrc
);
146 #endif // wxHAS_RAW_BITMAP
148 // ----------------------------------------------------------------------------
150 // ----------------------------------------------------------------------------
152 // various classes to change some DC property temporarily
154 // text background and foreground colours
155 class wxTextColoursChanger
158 wxTextColoursChanger(HDC hdc
, const wxMSWDCImpl
& dc
)
161 Change(dc
.GetTextForeground(), dc
.GetTextBackground());
164 wxTextColoursChanger(HDC hdc
, const wxColour
& colFg
, const wxColour
& colBg
)
167 Change(colFg
, colBg
);
170 ~wxTextColoursChanger()
172 if ( m_oldColFg
!= CLR_INVALID
)
173 ::SetTextColor(m_hdc
, m_oldColFg
);
174 if ( m_oldColBg
!= CLR_INVALID
)
175 ::SetBkColor(m_hdc
, m_oldColBg
);
179 // this ctor doesn't change mode immediately, call Change() later to do it
181 wxTextColoursChanger(HDC hdc
)
185 m_oldColBg
= CLR_INVALID
;
188 void Change(const wxColour
& colFg
, const wxColour
& colBg
)
192 m_oldColFg
= ::SetTextColor(m_hdc
, colFg
.GetPixel());
193 if ( m_oldColFg
== CLR_INVALID
)
195 wxLogLastError(_T("SetTextColor"));
200 m_oldColFg
= CLR_INVALID
;
205 m_oldColBg
= ::SetBkColor(m_hdc
, colBg
.GetPixel());
206 if ( m_oldColBg
== CLR_INVALID
)
208 wxLogLastError(_T("SetBkColor"));
213 m_oldColBg
= CLR_INVALID
;
222 wxDECLARE_NO_COPY_CLASS(wxTextColoursChanger
);
226 class wxBkModeChanger
229 // set background mode to opaque if mode != wxBRUSHSTYLE_TRANSPARENT
230 wxBkModeChanger(HDC hdc
, int mode
)
239 ::SetBkMode(m_hdc
, m_oldMode
);
243 // this ctor doesn't change mode immediately, call Change() later to do it
245 wxBkModeChanger(HDC hdc
) : m_hdc(hdc
) { m_oldMode
= 0; }
247 void Change(int mode
)
249 m_oldMode
= ::SetBkMode(m_hdc
, mode
== wxBRUSHSTYLE_TRANSPARENT
254 wxLogLastError(_T("SetBkMode"));
262 wxDECLARE_NO_COPY_CLASS(wxBkModeChanger
);
265 // instead of duplicating the same code which sets and then restores text
266 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
267 // encapsulate this in a small helper class
269 // wxBrushAttrsSetter: changes the text colours in the ctor if required and
270 // restores them in the dtor
271 class wxBrushAttrsSetter
: private wxBkModeChanger
,
272 private wxTextColoursChanger
275 wxBrushAttrsSetter(wxMSWDCImpl
& dc
);
278 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter
);
281 // this class saves the old stretch blit mode during its life time
282 class StretchBltModeChanger
285 StretchBltModeChanger(HDC hdc
,
286 int WXUNUSED_IN_WINCE(mode
))
290 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
292 wxLogLastError(_T("SetStretchBltMode"));
296 ~StretchBltModeChanger()
299 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
300 wxLogLastError(_T("SetStretchBltMode"));
309 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
312 #if wxUSE_DYNLIB_CLASS
314 // helper class to cache dynamically loaded libraries and not attempt reloading
316 class wxOnceOnlyDLLLoader
319 // ctor argument must be a literal string as we don't make a copy of it!
320 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
326 // return the symbol with the given name or NULL if the DLL not loaded
327 // or symbol not present
328 void *GetSymbol(const wxChar
*name
)
330 // we're prepared to handle errors here
335 m_dll
.Load(m_dllName
);
337 // reset the name whether we succeeded or failed so that we don't
338 // try again the next time
342 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
347 if ( m_dll
.IsLoaded() )
354 wxDynamicLibrary m_dll
;
355 const wxChar
*m_dllName
;
358 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(_T("msimg32"));
360 // we must ensure that DLLs are unloaded before the static objects cleanup time
361 // because we may hit the notorious DllMain() dead lock in this case if wx is
362 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
363 // under Windows because it tries to reacquire the same lock)
364 class wxGDIDLLsCleanupModule
: public wxModule
367 virtual bool OnInit() { return true; }
368 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
371 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
374 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
376 #endif // wxUSE_DYNLIB_CLASS
378 // ===========================================================================
380 // ===========================================================================
382 // ----------------------------------------------------------------------------
383 // wxBrushAttrsSetter
384 // ----------------------------------------------------------------------------
386 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
387 : wxBkModeChanger(GetHdcOf(dc
)),
388 wxTextColoursChanger(GetHdcOf(dc
))
390 const wxBrush
& brush
= dc
.GetBrush();
391 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
393 // note that Windows convention is opposite to wxWidgets one, this is
394 // why text colour becomes the background one and vice versa
395 wxTextColoursChanger::Change(dc
.GetTextBackground(),
396 dc
.GetTextForeground());
398 wxBkModeChanger::Change(dc
.GetBackgroundMode());
402 // ----------------------------------------------------------------------------
403 // wxDC MSW-specific methods
404 // ----------------------------------------------------------------------------
406 WXHDC
wxDC::GetHDC() const
408 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
409 return impl
? impl
->GetHDC() : 0;
412 // ---------------------------------------------------------------------------
414 // ---------------------------------------------------------------------------
416 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
423 wxMSWDCImpl::~wxMSWDCImpl()
427 SelectOldObjects(m_hDC
);
429 // if we own the HDC, we delete it, otherwise we just release it
433 ::DeleteDC(GetHdc());
435 else // we don't own our HDC
439 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
443 // Must have been a wxScreenDC
444 ::ReleaseDC((HWND
) NULL
, GetHdc());
450 // This will select current objects out of the DC,
451 // which is what you have to do before deleting the
453 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
459 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
460 if (m_selectedBitmap
.IsOk())
462 m_selectedBitmap
.SetSelectedInto(NULL
);
468 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
473 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
478 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
485 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
488 #endif // wxUSE_PALETTE
491 m_brush
= wxNullBrush
;
494 m_palette
= wxNullPalette
;
495 #endif // wxUSE_PALETTE
497 m_backgroundBrush
= wxNullBrush
;
498 m_selectedBitmap
= wxNullBitmap
;
501 // ---------------------------------------------------------------------------
503 // ---------------------------------------------------------------------------
505 void wxMSWDCImpl::UpdateClipBox()
510 ::GetClipBox(GetHdc(), &rect
);
512 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
513 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
514 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
515 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
519 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
521 // check if we should try to retrieve the clipping region possibly not set
522 // by our SetClippingRegion() but preset by Windows:this can only happen
523 // when we're associated with an existing HDC usign SetHDC(), see there
524 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
526 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
527 self
->UpdateClipBox();
529 if ( !m_clipX1
&& !m_clipX2
)
530 self
->m_clipping
= false;
533 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
536 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
537 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
539 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
543 // note that we combine the new clipping region with the existing one: this
544 // is compatible with what the other ports do and is the documented
545 // behaviour now (starting with 2.3.3)
546 #if defined(__WXWINCE__)
548 if ( !::GetClipBox(GetHdc(), &rectClip
) )
551 // GetClipBox returns logical coordinates, so transform to device
552 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
553 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
554 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
555 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
557 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
558 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
559 rectClip
.right
, rectClip
.bottom
);
561 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
563 ::SelectClipRgn(GetHdc(), hrgnDest
);
566 ::DeleteObject(hrgnClipOld
);
567 ::DeleteObject(hrgnDest
);
569 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
571 wxLogLastError(_T("ExtSelectClipRgn"));
575 #endif // WinCE/!WinCE
582 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
584 // the region coords are always the device ones, so do the translation
587 // FIXME: possible +/-1 error here, to check!
588 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
590 LogicalToDeviceX(x
+ w
),
591 LogicalToDeviceY(y
+ h
));
594 wxLogLastError(_T("CreateRectRgn"));
598 SetClippingHrgn((WXHRGN
)hrgn
);
600 ::DeleteObject(hrgn
);
604 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
606 SetClippingHrgn(region
.GetHRGN());
609 void wxMSWDCImpl::DestroyClippingRegion()
613 if (m_clipping
&& m_hDC
)
616 // On a PocketPC device (not necessarily emulator), resetting
617 // the clip region as per the old method causes bad display
618 // problems. In fact setting a null region is probably OK
619 // on desktop WIN32 also, since the WIN32 docs imply that the user
620 // clipping region is independent from the paint clipping region.
621 ::SelectClipRgn(GetHdc(), 0);
623 // TODO: this should restore the previous clipping region,
624 // so that OnPaint processing works correctly, and the update
625 // clipping region doesn't get destroyed after the first
626 // DestroyClippingRegion.
627 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
628 ::SelectClipRgn(GetHdc(), rgn
);
633 wxDCImpl::DestroyClippingRegion();
636 // ---------------------------------------------------------------------------
637 // query capabilities
638 // ---------------------------------------------------------------------------
640 bool wxMSWDCImpl::CanDrawBitmap() const
645 bool wxMSWDCImpl::CanGetTextExtent() const
647 #ifdef __WXMICROWIN__
648 // TODO Extend MicroWindows' GetDeviceCaps function
651 // What sort of display is it?
652 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
654 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
658 int wxMSWDCImpl::GetDepth() const
660 WXMICROWIN_CHECK_HDC_RET(16)
662 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
665 // ---------------------------------------------------------------------------
667 // ---------------------------------------------------------------------------
669 void wxMSWDCImpl::Clear()
676 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
680 // No, I think we should simply ignore this if printing on e.g.
682 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
683 if (!m_selectedBitmap
.IsOk())
686 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
687 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
688 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
692 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
695 DWORD colour
= ::GetBkColor(GetHdc());
696 HBRUSH brush
= ::CreateSolidBrush(colour
);
697 ::FillRect(GetHdc(), &rect
, brush
);
698 ::DeleteObject(brush
);
700 RealizeScaleAndOrigin();
703 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
704 wxCoord
WXUNUSED_IN_WINCE(y
),
705 const wxColour
& WXUNUSED_IN_WINCE(col
),
706 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
711 WXMICROWIN_CHECK_HDC_RET(false)
713 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
715 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
716 : FLOODFILLBORDER
) ) ;
719 // quoting from the MSDN docs:
721 // Following are some of the reasons this function might fail:
723 // * The filling could not be completed.
724 // * The specified point has the boundary color specified by the
725 // crColor parameter (if FLOODFILLBORDER was requested).
726 // * The specified point does not have the color specified by
727 // crColor (if FLOODFILLSURFACE was requested)
728 // * The point is outside the clipping region that is, it is not
729 // visible on the device.
731 wxLogLastError(wxT("ExtFloodFill"));
734 CalcBoundingBox(x
, y
);
740 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
742 WXMICROWIN_CHECK_HDC_RET(false)
744 wxCHECK_MSG( col
, false, _T("NULL colour parameter in wxMSWDCImpl::GetPixel") );
746 // get the color of the pixel
747 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
749 wxRGBToColour(*col
, pixelcolor
);
754 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
758 wxCoord x1
= x
-VIEWPORT_EXTENT
;
759 wxCoord y1
= y
-VIEWPORT_EXTENT
;
760 wxCoord x2
= x
+VIEWPORT_EXTENT
;
761 wxCoord y2
= y
+VIEWPORT_EXTENT
;
763 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
764 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
766 CalcBoundingBox(x1
, y1
);
767 CalcBoundingBox(x2
, y2
);
770 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
774 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
776 CalcBoundingBox(x1
, y1
);
777 CalcBoundingBox(x2
, y2
);
780 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
781 // and ending at (x2, y2)
782 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
783 wxCoord x2
, wxCoord y2
,
784 wxCoord xc
, wxCoord yc
)
788 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
792 // Slower emulation since WinCE doesn't support Pie and Arc
793 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
795 sa
= -sa
; // below center
796 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
797 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
802 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
804 // treat the special case of full circle separately
805 if ( x1
== x2
&& y1
== y2
)
807 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
811 wxCoord xx1
= XLOG2DEV(x1
);
812 wxCoord yy1
= YLOG2DEV(y1
);
813 wxCoord xx2
= XLOG2DEV(x2
);
814 wxCoord yy2
= YLOG2DEV(y2
);
815 wxCoord xxc
= XLOG2DEV(xc
);
816 wxCoord yyc
= YLOG2DEV(yc
);
819 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
821 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
822 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
823 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
824 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
826 if ( m_brush
.IsOk() && m_brush
.GetStyle() != wxBRUSHSTYLE_TRANSPARENT
)
828 // Have to add 1 to bottom-right corner of rectangle
829 // to make semi-circles look right (crooked line otherwise).
830 // Unfortunately this is not a reliable method, depends
831 // on the size of shape.
832 // TODO: figure out why this happens!
833 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
837 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
840 CalcBoundingBox(xc
- r
, yc
- r
);
841 CalcBoundingBox(xc
+ r
, yc
+ r
);
845 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
846 wxCoord width
, wxCoord height
)
848 // cases when we don't have DrawFrameControl()
849 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
850 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
852 wxCoord x2
= x1
+ width
,
862 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
864 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
867 CalcBoundingBox(x1
, y1
);
868 CalcBoundingBox(x2
, y2
);
869 #endif // Microwin/Normal
872 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
876 COLORREF color
= 0x00ffffff;
879 color
= m_pen
.GetColour().GetPixel();
882 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
884 CalcBoundingBox(x
, y
);
887 void wxMSWDCImpl::DoDrawPolygon(int n
,
891 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
895 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
897 // Do things less efficiently if we have offsets
898 if (xoffset
!= 0 || yoffset
!= 0)
900 POINT
*cpoints
= new POINT
[n
];
902 for (i
= 0; i
< n
; i
++)
904 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
905 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
907 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
910 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
912 (void)Polygon(GetHdc(), cpoints
, n
);
914 SetPolyFillMode(GetHdc(),prev
);
921 for (i
= 0; i
< n
; i
++)
922 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
925 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
927 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
929 SetPolyFillMode(GetHdc(),prev
);
935 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
940 wxPolygonFillMode fillStyle
)
943 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
947 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
949 for (i
= cnt
= 0; i
< n
; i
++)
952 // Do things less efficiently if we have offsets
953 if (xoffset
!= 0 || yoffset
!= 0)
955 POINT
*cpoints
= new POINT
[cnt
];
956 for (i
= 0; i
< cnt
; i
++)
958 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
959 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
961 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
964 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
966 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
968 SetPolyFillMode(GetHdc(),prev
);
974 for (i
= 0; i
< cnt
; i
++)
975 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
978 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
980 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
982 SetPolyFillMode(GetHdc(),prev
);
989 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
993 // Do things less efficiently if we have offsets
994 if (xoffset
!= 0 || yoffset
!= 0)
996 POINT
*cpoints
= new POINT
[n
];
998 for (i
= 0; i
< n
; i
++)
1000 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
1001 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
1003 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
1005 (void)Polyline(GetHdc(), cpoints
, n
);
1011 for (i
= 0; i
< n
; i
++)
1012 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
1014 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
1018 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1020 WXMICROWIN_CHECK_HDC
1022 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1024 wxCoord x2
= x
+ width
;
1025 wxCoord y2
= y
+ height
;
1027 wxCoord x2dev
= XLOG2DEV(x2
),
1028 y2dev
= YLOG2DEV(y2
);
1030 // Windows (but not Windows CE) draws the filled rectangles without outline
1031 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1032 // and we want them to have the same size regardless of which pen is used
1034 if ( m_pen
.IsOk() && m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
1039 #endif // !__WXWINCE__
1041 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
1043 CalcBoundingBox(x
, y
);
1044 CalcBoundingBox(x2
, y2
);
1047 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
1049 WXMICROWIN_CHECK_HDC
1051 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1053 // Now, a negative radius value is interpreted to mean
1054 // 'the proportion of the smallest X or Y dimension'
1058 double smallest
= (width
< height
) ? width
: height
;
1059 radius
= (- radius
* smallest
);
1062 wxCoord x2
= (x
+width
);
1063 wxCoord y2
= (y
+height
);
1065 // Windows draws the filled rectangles without outline (i.e. drawn with a
1066 // transparent pen) one pixel smaller in both directions and we want them
1067 // to have the same size regardless of which pen is used - adjust
1068 if ( m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
1074 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1075 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1077 CalcBoundingBox(x
, y
);
1078 CalcBoundingBox(x2
, y2
);
1081 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1083 WXMICROWIN_CHECK_HDC
1085 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1087 // +1 below makes the ellipse more similar to other platforms.
1088 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1089 wxCoord x2
= x
+ width
+ 1;
1090 wxCoord y2
= y
+ height
+ 1;
1092 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1093 // pen doesn't draw anything. Should we provide a workaround?
1095 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1097 CalcBoundingBox(x
, y
);
1098 CalcBoundingBox(x2
, y2
);
1101 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1102 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1104 // quadratic b-spline to cubic bezier spline conversion
1106 // quadratic spline with control points P0,P1,P2
1107 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1109 // bezier spline with control points B0,B1,B2,B3
1110 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1112 // control points of bezier spline calculated from b-spline
1114 // B1 = (2*P1 + P0)/3
1115 // B2 = (2*P1 + P2)/3
1118 WXMICROWIN_CHECK_HDC
1120 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1122 const size_t n_points
= points
->GetCount();
1123 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1125 const size_t n_bezier_points
= n_points
* 3 + 1;
1126 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1127 size_t bezier_pos
= 0;
1128 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1130 wxPointList::compatibility_iterator node
= points
->GetFirst();
1131 wxPoint
*p
= node
->GetData();
1132 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1133 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1135 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1138 node
= node
->GetNext();
1139 p
= node
->GetData();
1143 cx1
= ( x1
+ x2
) / 2;
1144 cy1
= ( y1
+ y2
) / 2;
1145 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1146 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1148 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1152 while ((node
= node
->GetNext()) != NULL
)
1154 while ((node
= node
->GetNext()))
1155 #endif // !wxUSE_STL
1157 p
= (wxPoint
*)node
->GetData();
1162 cx4
= (x1
+ x2
) / 2;
1163 cy4
= (y1
+ y2
) / 2;
1164 // B0 is B3 of previous segment
1166 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1167 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1170 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1171 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1174 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1175 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1181 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1183 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1184 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1186 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1189 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1193 #endif // wxUSE_SPLINES
1195 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1196 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1199 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1202 WXMICROWIN_CHECK_HDC
1204 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1209 int rx1
= XLOG2DEV(x
+w
/2);
1210 int ry1
= YLOG2DEV(y
+h
/2);
1217 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1218 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1219 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1220 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1222 // Swap start and end positions if the end angle is less than the start angle.
1233 // draw pie with NULL_PEN first and then outline otherwise a line is
1234 // drawn from the start and end points to the centre
1235 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1238 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1239 rx1
, ry1
, rx2
, ry2
);
1243 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1244 rx1
, ry1
-1, rx2
, ry2
-1);
1247 ::SelectObject(GetHdc(), hpenOld
);
1249 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1250 rx1
, ry1
, rx2
, ry2
);
1252 CalcBoundingBox(x
, y
);
1253 CalcBoundingBox(x2
, y2
);
1257 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1259 WXMICROWIN_CHECK_HDC
1261 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1264 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1266 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1269 CalcBoundingBox(x
, y
);
1270 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1273 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1275 WXMICROWIN_CHECK_HDC
1277 wxCHECK_RET( bmp
.IsOk(), _T("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1279 int width
= bmp
.GetWidth(),
1280 height
= bmp
.GetHeight();
1282 HBITMAP hbmpMask
= 0;
1285 HPALETTE oldPal
= 0;
1286 #endif // wxUSE_PALETTE
1288 if ( bmp
.HasAlpha() )
1291 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1293 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1298 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
1303 wxMask
*mask
= bmp
.GetMask();
1305 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1309 // don't give assert here because this would break existing
1310 // programs - just silently ignore useMask parameter
1317 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1319 // On some systems, MaskBlt succeeds yet is much much slower
1320 // than the wxWidgets fall-back implementation. So we need
1321 // to be able to switch this on and off at runtime.
1323 #if wxUSE_SYSTEM_OPTIONS
1324 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1328 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1329 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1331 wxPalette
*pal
= bmp
.GetPalette();
1332 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1334 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1335 ::RealizePalette(hdcMem
);
1337 #endif // wxUSE_PALETTE
1339 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1342 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1346 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1347 #endif // wxUSE_PALETTE
1349 ::SelectObject(hdcMem
, hOldBitmap
);
1356 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1360 memDC
.SelectObjectAsSource(bmp
);
1362 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1364 memDC
.SelectObject(wxNullBitmap
);
1367 else // no mask, just use BitBlt()
1370 HDC memdc
= ::CreateCompatibleDC( cdc
);
1371 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1373 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1375 wxTextColoursChanger
textCol(GetHdc(), *this);
1378 wxPalette
*pal
= bmp
.GetPalette();
1379 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1381 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1382 ::RealizePalette(memdc
);
1384 #endif // wxUSE_PALETTE
1386 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1387 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1391 ::SelectPalette(memdc
, oldPal
, FALSE
);
1392 #endif // wxUSE_PALETTE
1394 ::SelectObject( memdc
, hOldBitmap
);
1395 ::DeleteDC( memdc
);
1399 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1401 WXMICROWIN_CHECK_HDC
1403 DrawAnyText(text
, x
, y
);
1405 // update the bounding box
1406 CalcBoundingBox(x
, y
);
1409 GetOwner()->GetTextExtent(text
, &w
, &h
);
1410 CalcBoundingBox(x
+ w
, y
+ h
);
1413 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1415 WXMICROWIN_CHECK_HDC
1417 // prepare for drawing the text
1418 wxTextColoursChanger
textCol(GetHdc(), *this);
1420 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1422 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1423 text
.c_str(), text
.length(), NULL
) == 0 )
1425 wxLogLastError(wxT("TextOut"));
1429 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1430 wxCoord x
, wxCoord y
,
1433 WXMICROWIN_CHECK_HDC
1435 // we test that we have some font because otherwise we should still use the
1436 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1437 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1438 // font for drawing rotated fonts unfortunately)
1439 if ( (angle
== 0.0) && m_font
.IsOk() )
1441 DoDrawText(text
, x
, y
);
1443 #ifndef __WXMICROWIN__
1446 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1447 // because it's not TrueType and so can't have non zero
1448 // orientation/escapement under Win9x
1449 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1450 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1452 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1454 wxLogLastError(wxT("GetObject(hfont)"));
1457 // GDI wants the angle in tenth of degree
1458 long angle10
= (long)(angle
* 10);
1459 lf
.lfEscapement
= angle10
;
1460 lf
. lfOrientation
= angle10
;
1462 hfont
= ::CreateFontIndirect(&lf
);
1465 wxLogLastError(wxT("CreateFont"));
1469 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1471 DrawAnyText(text
, x
, y
);
1473 (void)::SelectObject(GetHdc(), hfontOld
);
1474 (void)::DeleteObject(hfont
);
1477 // call the bounding box by adding all four vertices of the rectangle
1478 // containing the text to it (simpler and probably not slower than
1479 // determining which of them is really topmost/leftmost/...)
1481 GetOwner()->GetTextExtent(text
, &w
, &h
);
1483 double rad
= DegToRad(angle
);
1485 // "upper left" and "upper right"
1486 CalcBoundingBox(x
, y
);
1487 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1489 // "bottom left" and "bottom right"
1490 x
+= (wxCoord
)(h
*sin(rad
));
1491 y
+= (wxCoord
)(h
*cos(rad
));
1492 CalcBoundingBox(x
, y
);
1493 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1498 // ---------------------------------------------------------------------------
1500 // ---------------------------------------------------------------------------
1504 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1506 WXMICROWIN_CHECK_HDC
1508 // Set the old object temporarily, in case the assignment deletes an object
1509 // that's not yet selected out.
1512 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1516 if ( m_palette
.IsOk() )
1518 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1519 GetHpaletteOf(m_palette
),
1522 m_oldPalette
= (WXHPALETTE
) oldPal
;
1525 ::RealizePalette(GetHdc());
1529 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1531 if ( palette
.IsOk() )
1533 m_palette
= palette
;
1534 DoSelectPalette(true);
1538 void wxMSWDCImpl::InitializePalette()
1540 if ( wxDisplayDepth() <= 8 )
1542 // look for any window or parent that has a custom palette. If any has
1543 // one then we need to use it in drawing operations
1544 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1546 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1547 if ( m_hasCustomPalette
)
1549 m_palette
= win
->GetPalette();
1551 // turn on MSW translation for this palette
1557 #endif // wxUSE_PALETTE
1559 // SetFont/Pen/Brush() really ask to be implemented as a single template
1560 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1562 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1564 WXMICROWIN_CHECK_HDC
1566 if ( font
== m_font
)
1571 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1572 if ( hfont
== HGDI_ERROR
)
1574 wxLogLastError(_T("SelectObject(font)"));
1579 m_oldFont
= (WXHFONT
)hfont
;
1584 else // invalid font, reset the current font
1588 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1590 wxLogLastError(_T("SelectObject(old font)"));
1596 m_font
= wxNullFont
;
1600 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1602 WXMICROWIN_CHECK_HDC
1609 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1610 if ( hpen
== HGDI_ERROR
)
1612 wxLogLastError(_T("SelectObject(pen)"));
1617 m_oldPen
= (WXHPEN
)hpen
;
1622 else // invalid pen, reset the current pen
1626 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1628 wxLogLastError(_T("SelectObject(old pen)"));
1638 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1640 WXMICROWIN_CHECK_HDC
1642 if ( brush
== m_brush
)
1647 // we must make sure the brush is aligned with the logical coordinates
1648 // before selecting it
1649 wxBitmap
*stipple
= brush
.GetStipple();
1650 if ( stipple
&& stipple
->IsOk() )
1652 if ( !::SetBrushOrgEx
1655 m_deviceOriginX
% stipple
->GetWidth(),
1656 m_deviceOriginY
% stipple
->GetHeight(),
1657 NULL
// [out] previous brush origin
1660 wxLogLastError(_T("SetBrushOrgEx()"));
1664 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1665 if ( hbrush
== HGDI_ERROR
)
1667 wxLogLastError(_T("SelectObject(brush)"));
1672 m_oldBrush
= (WXHBRUSH
)hbrush
;
1677 else // invalid brush, reset the current brush
1681 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1683 wxLogLastError(_T("SelectObject(old brush)"));
1689 m_brush
= wxNullBrush
;
1693 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1695 WXMICROWIN_CHECK_HDC
1697 m_backgroundBrush
= brush
;
1699 if ( m_backgroundBrush
.IsOk() )
1701 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1705 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1707 WXMICROWIN_CHECK_HDC
1709 m_backgroundMode
= mode
;
1711 // SetBackgroundColour now only refers to text background
1712 // and m_backgroundMode is used there
1715 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1717 WXMICROWIN_CHECK_HDC
1719 m_logicalFunction
= function
;
1724 void wxMSWDCImpl::SetRop(WXHDC dc
)
1726 if ( !dc
|| m_logicalFunction
< 0 )
1731 switch (m_logicalFunction
)
1733 case wxCLEAR
: rop
= R2_BLACK
; break;
1734 case wxXOR
: rop
= R2_XORPEN
; break;
1735 case wxINVERT
: rop
= R2_NOT
; break;
1736 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1737 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1738 case wxCOPY
: rop
= R2_COPYPEN
; break;
1739 case wxAND
: rop
= R2_MASKPEN
; break;
1740 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1741 case wxNO_OP
: rop
= R2_NOP
; break;
1742 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1743 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1744 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1745 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1746 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1747 case wxOR
: rop
= R2_MERGEPEN
; break;
1748 case wxSET
: rop
= R2_WHITE
; break;
1750 wxFAIL_MSG( wxS("unknown logical function") );
1754 SetROP2(GetHdc(), rop
);
1757 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1759 // We might be previewing, so return true to let it continue.
1763 void wxMSWDCImpl::EndDoc()
1767 void wxMSWDCImpl::StartPage()
1771 void wxMSWDCImpl::EndPage()
1775 // ---------------------------------------------------------------------------
1777 // ---------------------------------------------------------------------------
1779 wxCoord
wxMSWDCImpl::GetCharHeight() const
1781 WXMICROWIN_CHECK_HDC_RET(0)
1783 TEXTMETRIC lpTextMetric
;
1785 GetTextMetrics(GetHdc(), &lpTextMetric
);
1787 return lpTextMetric
.tmHeight
;
1790 wxCoord
wxMSWDCImpl::GetCharWidth() const
1792 WXMICROWIN_CHECK_HDC_RET(0)
1794 TEXTMETRIC lpTextMetric
;
1796 GetTextMetrics(GetHdc(), &lpTextMetric
);
1798 return lpTextMetric
.tmAveCharWidth
;
1801 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1802 wxCoord
*descent
, wxCoord
*externalLeading
,
1803 const wxFont
*font
) const
1805 #ifdef __WXMICROWIN__
1810 if (descent
) *descent
= 0;
1811 if (externalLeading
) *externalLeading
= 0;
1814 #endif // __WXMICROWIN__
1819 wxASSERT_MSG( font
->IsOk(), _T("invalid font in wxMSWDCImpl::GetTextExtent") );
1821 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1823 else // don't change the font
1829 const size_t len
= string
.length();
1830 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1832 wxLogLastError(_T("GetTextExtentPoint32()"));
1835 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1836 // the result computed by GetTextExtentPoint32() may be too small as it
1837 // accounts for under/overhang of the first/last character while we want
1838 // just the bounding rect for this string so adjust the width as needed
1839 // (using API not available in 2002 SDKs of WinCE)
1843 const wxChar chFirst
= *string
.begin();
1844 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1846 if ( width
.abcA
< 0 )
1847 sizeRect
.cx
-= width
.abcA
;
1851 const wxChar chLast
= *string
.rbegin();
1852 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1854 //else: we already have the width of the last character
1856 if ( width
.abcC
< 0 )
1857 sizeRect
.cx
-= width
.abcC
;
1859 //else: GetCharABCWidths() failed, not a TrueType font?
1861 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1864 ::GetTextMetrics(GetHdc(), &tm
);
1871 *descent
= tm
.tmDescent
;
1872 if (externalLeading
)
1873 *externalLeading
= tm
.tmExternalLeading
;
1877 ::SelectObject(GetHdc(), hfontOld
);
1882 // Each element of the array will be the width of the string up to and
1883 // including the coresoponding character in text.
1885 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1887 static int maxLenText
= -1;
1888 static int maxWidth
= -1;
1891 int stlen
= text
.length();
1893 if (maxLenText
== -1)
1895 // Win9x and WinNT+ have different limits
1896 int version
= wxGetOsVersion();
1897 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1898 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1902 widths
.Add(0, stlen
); // fill the array with zeros
1906 if (!::GetTextExtentExPoint(GetHdc(),
1907 text
.c_str(), // string to check
1908 wxMin(stlen
, maxLenText
),
1910 &fit
, // [out] count of chars
1912 &widths
[0], // array to fill
1916 wxLogLastError(wxT("GetTextExtentExPoint"));
1923 void wxMSWDCImpl::RealizeScaleAndOrigin()
1925 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1926 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1927 // noticeable difference between these mapping modes
1929 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1931 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1932 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1934 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1935 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1937 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1938 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1942 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1944 WXMICROWIN_CHECK_HDC
1946 m_mappingMode
= mode
;
1948 if ( mode
== wxMM_TEXT
)
1951 m_logicalScaleY
= 1.0;
1953 else // need to do some calculations
1955 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1956 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1957 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1958 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1960 if ( (mm_width
== 0) || (mm_height
== 0) )
1962 // we can't calculate mm2pixels[XY] then!
1966 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1967 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1972 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1973 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1977 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1978 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1982 m_logicalScaleX
= mm2pixelsX
;
1983 m_logicalScaleY
= mm2pixelsY
;
1987 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1988 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1992 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1996 ComputeScaleAndOrigin();
1998 RealizeScaleAndOrigin();
2001 void wxMSWDCImpl::SetUserScale(double x
, double y
)
2003 WXMICROWIN_CHECK_HDC
2005 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
2008 wxDCImpl::SetUserScale(x
,y
);
2010 RealizeScaleAndOrigin();
2013 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2016 WXMICROWIN_CHECK_HDC
2018 int signX
= xLeftRight
? 1 : -1,
2019 signY
= yBottomUp
? -1 : 1;
2021 if (signX
== m_signX
&& signY
== m_signY
)
2024 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2026 RealizeScaleAndOrigin();
2029 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2031 WXMICROWIN_CHECK_HDC
2033 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2036 wxDCImpl::SetLogicalOrigin( x
, y
);
2038 RealizeScaleAndOrigin();
2041 // For use by wxWidgets only, unless custom units are required.
2042 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2044 WXMICROWIN_CHECK_HDC
2046 wxDCImpl::SetLogicalScale(x
,y
);
2049 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2051 WXMICROWIN_CHECK_HDC
2053 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2056 wxDCImpl::SetDeviceOrigin( x
, y
);
2058 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2061 // ---------------------------------------------------------------------------
2063 // ---------------------------------------------------------------------------
2065 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2066 wxCoord dstWidth
, wxCoord dstHeight
,
2068 wxCoord srcX
, wxCoord srcY
,
2069 wxRasterOperationMode rop
, bool useMask
,
2070 wxCoord srcMaskX
, wxCoord srcMaskY
)
2072 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2075 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2076 wxCoord dstWidth
, wxCoord dstHeight
,
2078 wxCoord xsrc
, wxCoord ysrc
,
2079 wxCoord srcWidth
, wxCoord srcHeight
,
2080 wxRasterOperationMode rop
, bool useMask
,
2081 wxCoord xsrcMask
, wxCoord ysrcMask
)
2083 wxCHECK_MSG( source
, false, _T("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2085 WXMICROWIN_CHECK_HDC_RET(false)
2087 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2090 // TODO: Do we want to be able to blit from other DCs too?
2094 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2096 // if either the source or destination has alpha channel, we must use
2097 // AlphaBlt() as other function don't handle it correctly
2098 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2099 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2100 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2102 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2103 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2107 wxMask
*mask
= NULL
;
2110 mask
= bmpSrc
.GetMask();
2112 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2114 // don't give assert here because this would break existing
2115 // programs - just silently ignore useMask parameter
2120 if (xsrcMask
== -1 && ysrcMask
== -1)
2122 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2125 wxTextColoursChanger
textCol(GetHdc(), *this);
2130 case wxXOR
: dwRop
= SRCINVERT
; break;
2131 case wxINVERT
: dwRop
= DSTINVERT
; break;
2132 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2133 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2134 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2135 case wxSET
: dwRop
= WHITENESS
; break;
2136 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2137 case wxAND
: dwRop
= SRCAND
; break;
2138 case wxOR
: dwRop
= SRCPAINT
; break;
2139 case wxEQUIV
: dwRop
= 0x00990066; break;
2140 case wxNAND
: dwRop
= 0x007700E6; break;
2141 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2142 case wxCOPY
: dwRop
= SRCCOPY
; break;
2143 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2144 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2145 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2147 wxFAIL_MSG( wxT("unsupported logical function") );
2151 bool success
= false;
2156 // we want the part of the image corresponding to the mask to be
2157 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2158 // meaning of fg and bg is inverted which corresponds to wxWin notion
2159 // of the mask which is also contrary to the Windows one)
2161 // On some systems, MaskBlt succeeds yet is much much slower
2162 // than the wxWidgets fall-back implementation. So we need
2163 // to be able to switch this on and off at runtime.
2164 #if wxUSE_SYSTEM_OPTIONS
2165 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2168 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2173 xdest
, ydest
, dstWidth
, dstHeight
,
2176 (HBITMAP
)mask
->GetMaskBitmap(),
2178 MAKEROP4(dwRop
, DSTCOPY
)
2186 // Blit bitmap with mask
2189 HBITMAP buffer_bmap
;
2191 #if wxUSE_DC_CACHEING
2192 // create a temp buffer bitmap and DCs to access it and the mask
2193 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2194 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2196 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2197 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2199 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2200 dstWidth
, dstHeight
);
2202 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2203 #else // !wxUSE_DC_CACHEING
2204 // create a temp buffer bitmap and DCs to access it and the mask
2205 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2206 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2207 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2208 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2209 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2210 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2212 // copy dest to buffer
2213 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2214 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2216 wxLogLastError(wxT("BitBlt"));
2220 StretchBltModeChanger
changeMode(dc_buffer
, COLORONCOLOR
);
2223 // copy src to buffer using selected raster op
2224 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2225 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2227 wxLogLastError(wxT("StretchBlt"));
2230 // set masked area in buffer to BLACK
2232 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2233 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2234 dc_mask
, xsrcMask
, ysrcMask
,
2235 srcWidth
, srcHeight
, SRCAND
) )
2237 wxLogLastError(wxT("StretchBlt"));
2240 // set unmasked area in dest to BLACK
2241 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2242 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2243 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2244 dc_mask
, xsrcMask
, ysrcMask
,
2245 srcWidth
, srcHeight
, SRCAND
) )
2247 wxLogLastError(wxT("StretchBlt"));
2249 } // restore the original text and background colours
2251 // OR buffer to dest
2252 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2253 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2256 wxLogLastError(wxT("BitBlt"));
2259 // tidy up temporary DCs and bitmap
2260 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2261 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2263 #if !wxUSE_DC_CACHEING
2265 ::DeleteDC(dc_mask
);
2266 ::DeleteDC(dc_buffer
);
2267 ::DeleteObject(buffer_bmap
);
2272 else // no mask, just BitBlt() it
2274 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2275 // use StretchBlt() if available and finally fall back to BitBlt()
2277 // FIXME: use appropriate WinCE functions
2279 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2280 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2285 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2287 &ds
) == sizeof(ds
) )
2289 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2291 // Figure out what co-ordinate system we're supposed to specify
2293 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2297 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2300 if ( ::StretchDIBits(GetHdc(),
2302 dstWidth
, dstHeight
,
2304 srcWidth
, srcHeight
,
2306 (LPBITMAPINFO
)&ds
.dsBmih
,
2309 ) == (int)GDI_ERROR
)
2311 // On Win9x this API fails most (all?) of the time, so
2312 // logging it becomes quite distracting. Since it falls
2313 // back to the code below this is not really serious, so
2315 //wxLogLastError(wxT("StretchDIBits"));
2324 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2329 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2335 xdest
, ydest
, dstWidth
, dstHeight
,
2337 xsrc
, ysrc
, srcWidth
, srcHeight
,
2341 wxLogLastError(_T("StretchBlt"));
2351 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2352 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2354 wxLogLastError(_T("BitBlt"));
2366 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2368 WXMICROWIN_CHECK_HDC
2371 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2373 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2376 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2378 WXMICROWIN_CHECK_HDC
2380 // if we implement it in terms of DoGetSize() instead of directly using the
2381 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2382 // will also work for wxWindowDC and wxClientDC even though their size is
2383 // not the same as the total size of the screen
2384 int wPixels
, hPixels
;
2385 DoGetSize(&wPixels
, &hPixels
);
2389 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2391 wxCHECK_RET( wTotal
, _T("0 width device?") );
2393 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2398 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2400 wxCHECK_RET( hTotal
, _T("0 height device?") );
2402 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2406 wxSize
wxMSWDCImpl::GetPPI() const
2408 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2410 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2411 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2413 return wxSize(x
, y
);
2416 // ----------------------------------------------------------------------------
2418 // ----------------------------------------------------------------------------
2420 #if wxUSE_DC_CACHEING
2423 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2424 * improve it in due course, either using arrays, or simply storing pointers to one
2425 * entry for the bitmap, and two for the DCs. -- JACS
2428 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2429 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2431 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2440 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2449 wxDCCacheEntry::~wxDCCacheEntry()
2452 ::DeleteObject((HBITMAP
) m_bitmap
);
2454 ::DeleteDC((HDC
) m_dc
);
2457 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2459 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2460 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2463 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2465 if (entry
->m_depth
== depth
)
2467 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2469 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2470 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2471 if ( !entry
->m_bitmap
)
2473 wxLogLastError(wxT("CreateCompatibleBitmap"));
2475 entry
->m_width
= w
; entry
->m_height
= h
;
2481 node
= node
->GetNext();
2483 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2486 wxLogLastError(wxT("CreateCompatibleBitmap"));
2488 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2489 AddToBitmapCache(entry
);
2493 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2495 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2496 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2499 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2501 // Don't return the same one as we already have
2502 if (!notThis
|| (notThis
!= entry
))
2504 if (entry
->m_depth
== depth
)
2510 node
= node
->GetNext();
2512 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2515 wxLogLastError(wxT("CreateCompatibleDC"));
2517 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2518 AddToDCCache(entry
);
2522 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2524 sm_bitmapCache
.Append(entry
);
2527 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2529 sm_dcCache
.Append(entry
);
2532 void wxMSWDCImpl::ClearCache()
2534 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2535 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2538 // Clean up cache at app exit
2539 class wxDCModule
: public wxModule
2542 virtual bool OnInit() { return true; }
2543 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2546 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2549 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2551 #endif // wxUSE_DC_CACHEING
2553 // ----------------------------------------------------------------------------
2554 // alpha channel support
2555 // ----------------------------------------------------------------------------
2557 static bool AlphaBlt(HDC hdcDst
,
2558 int x
, int y
, int dstWidth
, int dstHeight
,
2560 int srcWidth
, int srcHeight
,
2562 const wxBitmap
& bmp
)
2564 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2565 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2567 // do we have AlphaBlend() and company in the headers?
2568 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2569 // yes, now try to see if we have it during run-time
2570 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2571 HDC
,int,int,int,int,
2575 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(_T("AlphaBlend"));
2576 if ( pfnAlphaBlend
)
2579 bf
.BlendOp
= AC_SRC_OVER
;
2581 bf
.SourceConstantAlpha
= 0xff;
2582 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2584 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2585 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2588 // skip wxAlphaBlend() call below
2592 wxLogLastError(_T("AlphaBlend"));
2595 wxUnusedVar(hdcSrc
);
2596 #endif // defined(AC_SRC_OVER)
2598 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2600 #ifdef wxHAS_RAW_BITMAP
2601 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2604 #else // !wxHAS_RAW_BITMAP
2605 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2606 // alpha but at least something will be shown like this)
2609 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2613 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2614 #ifdef wxHAS_RAW_BITMAP
2617 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2618 int dstWidth
, int dstHeight
,
2620 int srcWidth
, int srcHeight
,
2621 const wxBitmap
& bmpSrc
)
2623 // get the destination DC pixels
2624 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2626 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2628 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2630 wxLogLastError(_T("BitBlt"));
2633 // combine them with the source bitmap using alpha
2634 wxAlphaPixelData
dataDst(bmpDst
),
2635 dataSrc((wxBitmap
&)bmpSrc
);
2637 wxCHECK_RET( dataDst
&& dataSrc
,
2638 _T("failed to get raw data in wxAlphaBlend") );
2640 wxAlphaPixelData::Iterator
pDst(dataDst
),
2644 for ( int y
= 0; y
< dstHeight
; y
++ )
2646 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2648 for ( int x
= 0; x
< dstWidth
; x
++ )
2650 // source is point sampled, Alpha StretchBlit is ugly on Win95
2651 // (but does not impact performance)
2652 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2654 // note that source bitmap uses premultiplied alpha (as required by
2655 // the real AlphaBlend)
2656 const unsigned beta
= 255 - pSrc
.Alpha();
2658 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2659 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2660 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2665 pDst
= pDstRowStart
;
2666 pDst
.OffsetY(dataDst
, 1);
2669 // and finally blit them back to the destination DC
2670 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2672 wxLogLastError(_T("BitBlt"));
2676 #endif // wxHAS_RAW_BITMAP
2678 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2679 const wxColour
& initialColour
,
2680 const wxColour
& destColour
,
2681 wxDirection nDirection
)
2683 // use native function if we have compile-time support it and can load it
2684 // during run-time (linking to it statically would make the program
2685 // unusable on earlier Windows versions)
2686 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2688 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2689 static GradientFill_t pfnGradientFill
=
2690 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(_T("GradientFill"));
2692 if ( pfnGradientFill
)
2694 GRADIENT_RECT grect
;
2695 grect
.UpperLeft
= 0;
2696 grect
.LowerRight
= 1;
2698 // invert colours direction if not filling from left-to-right or
2700 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2702 // one vertex for upper left and one for upper-right
2703 TRIVERTEX vertices
[2];
2705 vertices
[0].x
= rect
.GetLeft();
2706 vertices
[0].y
= rect
.GetTop();
2707 vertices
[1].x
= rect
.GetRight()+1;
2708 vertices
[1].y
= rect
.GetBottom()+1;
2710 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2711 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2712 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2713 vertices
[firstVertex
].Alpha
= 0;
2714 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2715 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2716 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2717 vertices
[1 - firstVertex
].Alpha
= 0;
2719 if ( (*pfnGradientFill
)
2726 nDirection
== wxWEST
|| nDirection
== wxEAST
2727 ? GRADIENT_FILL_RECT_H
2728 : GRADIENT_FILL_RECT_V
2731 // skip call of the base class version below
2735 wxLogLastError(_T("GradientFill"));
2737 #endif // wxUSE_DYNLIB_CLASS
2739 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2742 #if wxUSE_DYNLIB_CLASS
2744 static DWORD
wxGetDCLayout(HDC hdc
)
2746 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2748 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2750 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2753 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2755 DWORD layout
= wxGetDCLayout(GetHdc());
2757 if ( layout
== (DWORD
)-1 )
2758 return wxLayout_Default
;
2760 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2763 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2765 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2767 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2768 if ( !s_pfnSetLayout
)
2771 if ( dir
== wxLayout_Default
)
2773 dir
= wxTheApp
->GetLayoutDirection();
2774 if ( dir
== wxLayout_Default
)
2778 DWORD layout
= wxGetDCLayout(GetHdc());
2779 if ( dir
== wxLayout_RightToLeft
)
2780 layout
|= LAYOUT_RTL
;
2782 layout
&= ~LAYOUT_RTL
;
2784 s_pfnSetLayout(GetHdc(), layout
);
2787 #else // !wxUSE_DYNLIB_CLASS
2789 // we can't provide RTL support without dynamic loading, so stub it out
2790 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2792 return wxLayout_Default
;
2795 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2799 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS