1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dc.cpp
3 // Purpose: wxDC class for MSW port
4 // Author: Julian Smart
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ===========================================================================
13 // ===========================================================================
15 // ---------------------------------------------------------------------------
17 // ---------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
27 #include "wx/msw/wrapcdlg.h"
29 #include "wx/window.h"
31 #include "wx/dialog.h"
33 #include "wx/bitmap.h"
34 #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"
52 #include "wx/msw/private/dc.h"
53 #include "wx/private/textmeasure.h"
55 using namespace wxMSWImpl
;
58 #define AC_SRC_ALPHA 1
65 /* Quaternary raster codes */
67 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
70 // apparently with MicroWindows it is possible that HDC is 0 so we have to
71 // check for this ourselves
73 #define WXMICROWIN_CHECK_HDC if ( !GetHDC() ) return;
74 #define WXMICROWIN_CHECK_HDC_RET(x) if ( !GetHDC() ) return x;
76 #define WXMICROWIN_CHECK_HDC
77 #define WXMICROWIN_CHECK_HDC_RET(x)
80 IMPLEMENT_ABSTRACT_CLASS(wxMSWDCImpl
, wxDCImpl
)
82 // ---------------------------------------------------------------------------
84 // ---------------------------------------------------------------------------
86 // The device space in Win32 GDI measures 2^27*2^27 , so we use 2^27-1 as the
87 // maximal possible view port extent.
88 static const int VIEWPORT_EXTENT
= 134217727;
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-m_logicalOriginX)*m_signX)
107 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
108 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
109 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
111 #define XLOG2DEV(x) (x)
112 #define YLOG2DEV(y) (y)
113 #define XDEV2LOG(x) (x)
114 #define YDEV2LOG(y) (y)
117 // ---------------------------------------------------------------------------
119 // ---------------------------------------------------------------------------
121 // convert degrees to radians
122 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
124 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
126 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
127 // to pass it to this function but as we already have it at the point
128 // of call anyhow we do
130 // return true if we could draw the bitmap in one way or the other, false
132 static bool AlphaBlt(HDC hdcDst
,
133 int x
, int y
, int dstWidth
, int dstHeight
,
135 int srcWidth
, int srcHeight
,
137 const wxBitmap
& bmp
);
139 #ifdef wxHAS_RAW_BITMAP
141 // our (limited) AlphaBlend() replacement for Windows versions not providing it
143 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
144 int dstWidth
, int dstHeight
,
146 int srcWidth
, int srcHeight
,
147 const wxBitmap
& bmpSrc
);
149 #endif // wxHAS_RAW_BITMAP
154 // Wrappers for the dynamically loaded {Set,Get}Layout() functions. They work
155 // in exactly the same way as the standard functions and return GDI_ERROR if
156 // they're not actually available.
157 DWORD
GetLayout(HDC hdc
);
158 DWORD
SetLayout(HDC hdc
, DWORD dwLayout
);
160 // Create a compatible HDC and copy the layout of the source DC to it. This is
161 // necessary in order to draw bitmaps (which are usually blitted from a
162 // temporary compatible memory DC to the real target DC) using the same layout.
163 HDC
CreateCompatibleDCWithLayout(HDC hdc
);
167 // ----------------------------------------------------------------------------
169 // ----------------------------------------------------------------------------
171 // instead of duplicating the same code which sets and then restores text
172 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
173 // encapsulate this in a small helper class
175 // wxBrushAttrsSetter: changes the text colours in the ctor if required and
176 // restores them in the dtor
177 class wxBrushAttrsSetter
: private wxBkModeChanger
,
178 private wxTextColoursChanger
181 wxBrushAttrsSetter(wxMSWDCImpl
& dc
);
184 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter
);
189 #define SET_STRETCH_BLT_MODE(hdc)
191 #else // !__WXWINCE__
193 // this class sets the stretch blit mode to COLORONCOLOR during its lifetime
195 // don't use it directly, use SET_STRETCH_BLT_MODE() macro instead as it
196 // expands to nothing under WinCE which doesn't have SetStretchBltMode()
197 class StretchBltModeChanger
200 StretchBltModeChanger(HDC hdc
)
203 m_modeOld
= ::SetStretchBltMode(m_hdc
, COLORONCOLOR
);
206 wxLogLastError(wxT("SetStretchBltMode"));
210 ~StretchBltModeChanger()
212 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
214 wxLogLastError(wxT("SetStretchBltMode"));
223 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
226 #define SET_STRETCH_BLT_MODE(hdc) \
227 StretchBltModeChanger wxMAKE_UNIQUE_NAME(stretchModeChanger)(hdc)
229 #endif // __WXWINCE__/!__WXWINCE__
231 #if wxUSE_DYNLIB_CLASS
233 // helper class to cache dynamically loaded libraries and not attempt reloading
235 class wxOnceOnlyDLLLoader
238 // ctor argument must be a literal string as we don't make a copy of it!
239 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
245 // return the symbol with the given name or NULL if the DLL not loaded
246 // or symbol not present
247 void *GetSymbol(const wxChar
*name
)
249 // we're prepared to handle errors here
254 m_dll
.Load(m_dllName
);
256 // reset the name whether we succeeded or failed so that we don't
257 // try again the next time
261 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
266 if ( m_dll
.IsLoaded() )
273 wxDynamicLibrary m_dll
;
274 const wxChar
*m_dllName
;
277 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(wxT("msimg32"));
279 // we must ensure that DLLs are unloaded before the static objects cleanup time
280 // because we may hit the notorious DllMain() dead lock in this case if wx is
281 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
282 // under Windows because it tries to reacquire the same lock)
283 class wxGDIDLLsCleanupModule
: public wxModule
286 virtual bool OnInit() { return true; }
287 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
290 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
293 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
298 #if wxUSE_DC_TRANSFORM_MATRIX
300 // Class used to dynamically load world transform related API functions.
301 class GdiWorldTransformFuncs
306 if ( !ms_worldTransformSymbolsLoaded
)
307 LoadWorldTransformSymbols();
309 return ms_pfnSetGraphicsMode
&&
310 ms_pfnSetWorldTransform
&&
311 ms_pfnGetWorldTransform
&&
312 ms_pfnModifyWorldTransform
;
315 typedef int (WINAPI
*SetGraphicsMode_t
)(HDC
, int);
316 static SetGraphicsMode_t
SetGraphicsMode()
318 if ( !ms_worldTransformSymbolsLoaded
)
319 LoadWorldTransformSymbols();
321 return ms_pfnSetGraphicsMode
;
324 typedef BOOL (WINAPI
*SetWorldTransform_t
)(HDC
, const XFORM
*);
325 static SetWorldTransform_t
SetWorldTransform()
327 if ( !ms_worldTransformSymbolsLoaded
)
328 LoadWorldTransformSymbols();
330 return ms_pfnSetWorldTransform
;
333 typedef BOOL (WINAPI
*GetWorldTransform_t
)(HDC
, LPXFORM
);
334 static GetWorldTransform_t
GetWorldTransform()
336 if ( !ms_worldTransformSymbolsLoaded
)
337 LoadWorldTransformSymbols();
339 return ms_pfnGetWorldTransform
;
342 typedef BOOL (WINAPI
*ModifyWorldTransform_t
)(HDC
, const XFORM
*, DWORD
);
343 static ModifyWorldTransform_t
ModifyWorldTransform()
345 if ( !ms_worldTransformSymbolsLoaded
)
346 LoadWorldTransformSymbols();
348 return ms_pfnModifyWorldTransform
;
352 static void LoadWorldTransformSymbols()
354 wxDynamicLibrary
dll(wxT("gdi32.dll"));
356 wxDL_INIT_FUNC(ms_pfn
, SetGraphicsMode
, dll
);
357 wxDL_INIT_FUNC(ms_pfn
, SetWorldTransform
, dll
);
358 wxDL_INIT_FUNC(ms_pfn
, GetWorldTransform
, dll
);
359 wxDL_INIT_FUNC(ms_pfn
, ModifyWorldTransform
, dll
);
361 ms_worldTransformSymbolsLoaded
= true;
364 static SetGraphicsMode_t ms_pfnSetGraphicsMode
;
365 static SetWorldTransform_t ms_pfnSetWorldTransform
;
366 static GetWorldTransform_t ms_pfnGetWorldTransform
;
367 static ModifyWorldTransform_t ms_pfnModifyWorldTransform
;
369 static bool ms_worldTransformSymbolsLoaded
;
372 GdiWorldTransformFuncs::SetGraphicsMode_t
373 GdiWorldTransformFuncs::ms_pfnSetGraphicsMode
= NULL
;
374 GdiWorldTransformFuncs::SetWorldTransform_t
375 GdiWorldTransformFuncs::ms_pfnSetWorldTransform
= NULL
;
376 GdiWorldTransformFuncs::GetWorldTransform_t
377 GdiWorldTransformFuncs::ms_pfnGetWorldTransform
= NULL
;
378 GdiWorldTransformFuncs::ModifyWorldTransform_t
379 GdiWorldTransformFuncs::ms_pfnModifyWorldTransform
= NULL
;
381 bool GdiWorldTransformFuncs::ms_worldTransformSymbolsLoaded
= false;
383 #endif // wxUSE_DC_TRANSFORM_MATRIX
385 } // anonymous namespace
387 #endif // wxUSE_DYNLIB_CLASS
389 // ===========================================================================
391 // ===========================================================================
393 // ----------------------------------------------------------------------------
394 // wxBrushAttrsSetter
395 // ----------------------------------------------------------------------------
397 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
398 : wxBkModeChanger(GetHdcOf(dc
)),
399 wxTextColoursChanger(GetHdcOf(dc
))
401 const wxBrush
& brush
= dc
.GetBrush();
402 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
404 // note that Windows convention is opposite to wxWidgets one, this is
405 // why text colour becomes the background one and vice versa
406 wxTextColoursChanger::Change(dc
.GetTextBackground(),
407 dc
.GetTextForeground());
409 wxBkModeChanger::Change(dc
.GetBackgroundMode());
413 // ----------------------------------------------------------------------------
414 // wxDC MSW-specific methods
415 // ----------------------------------------------------------------------------
417 WXHDC
wxDC::GetHDC() const
419 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
420 return impl
? impl
->GetHDC() : 0;
423 // ---------------------------------------------------------------------------
425 // ---------------------------------------------------------------------------
427 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
434 wxMSWDCImpl::~wxMSWDCImpl()
438 SelectOldObjects(m_hDC
);
440 // if we own the HDC, we delete it, otherwise we just release it
444 ::DeleteDC(GetHdc());
446 else // we don't own our HDC
450 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
454 // Must have been a wxScreenDC
455 ::ReleaseDC((HWND
) NULL
, GetHdc());
461 // This will select current objects out of the DC,
462 // which is what you have to do before deleting the
464 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
470 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
471 if (m_selectedBitmap
.IsOk())
473 m_selectedBitmap
.SetSelectedInto(NULL
);
479 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
484 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
489 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
496 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
499 #endif // wxUSE_PALETTE
502 m_brush
= wxNullBrush
;
505 m_palette
= wxNullPalette
;
506 #endif // wxUSE_PALETTE
508 m_backgroundBrush
= wxNullBrush
;
509 m_selectedBitmap
= wxNullBitmap
;
512 // ---------------------------------------------------------------------------
514 // ---------------------------------------------------------------------------
516 void wxMSWDCImpl::UpdateClipBox()
521 ::GetClipBox(GetHdc(), &rect
);
523 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
524 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
525 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
526 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
530 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
532 // check if we should try to retrieve the clipping region possibly not set
533 // by our SetClippingRegion() but preset by Windows:this can only happen
534 // when we're associated with an existing HDC usign SetHDC(), see there
535 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
537 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
538 self
->UpdateClipBox();
540 if ( !m_clipX1
&& !m_clipX2
)
541 self
->m_clipping
= false;
544 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
547 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
548 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
550 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
554 // note that we combine the new clipping region with the existing one: this
555 // is compatible with what the other ports do and is the documented
556 // behaviour now (starting with 2.3.3)
557 #if defined(__WXWINCE__)
559 if ( !::GetClipBox(GetHdc(), &rectClip
) )
562 // GetClipBox returns logical coordinates, so transform to device
563 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
564 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
565 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
566 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
568 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
569 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
570 rectClip
.right
, rectClip
.bottom
);
572 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
574 ::SelectClipRgn(GetHdc(), hrgnDest
);
577 ::DeleteObject(hrgnClipOld
);
578 ::DeleteObject(hrgnDest
);
580 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
582 wxLogLastError(wxT("ExtSelectClipRgn"));
586 #endif // WinCE/!WinCE
593 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
595 // the region coords are always the device ones, so do the translation
598 // FIXME: possible +/-1 error here, to check!
599 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
601 LogicalToDeviceX(x
+ w
),
602 LogicalToDeviceY(y
+ h
));
605 wxLogLastError(wxT("CreateRectRgn"));
609 SetClippingHrgn((WXHRGN
)hrgn
);
611 ::DeleteObject(hrgn
);
615 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
617 SetClippingHrgn(region
.GetHRGN());
620 void wxMSWDCImpl::DestroyClippingRegion()
624 if (m_clipping
&& m_hDC
)
627 // On a PocketPC device (not necessarily emulator), resetting
628 // the clip region as per the old method causes bad display
629 // problems. In fact setting a null region is probably OK
630 // on desktop WIN32 also, since the WIN32 docs imply that the user
631 // clipping region is independent from the paint clipping region.
632 ::SelectClipRgn(GetHdc(), 0);
634 // TODO: this should restore the previous clipping region,
635 // so that OnPaint processing works correctly, and the update
636 // clipping region doesn't get destroyed after the first
637 // DestroyClippingRegion.
638 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
639 ::SelectClipRgn(GetHdc(), rgn
);
644 wxDCImpl::DestroyClippingRegion();
647 // ---------------------------------------------------------------------------
648 // query capabilities
649 // ---------------------------------------------------------------------------
651 bool wxMSWDCImpl::CanDrawBitmap() const
656 bool wxMSWDCImpl::CanGetTextExtent() const
658 #ifdef __WXMICROWIN__
659 // TODO Extend MicroWindows' GetDeviceCaps function
662 // What sort of display is it?
663 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
665 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
669 int wxMSWDCImpl::GetDepth() const
671 WXMICROWIN_CHECK_HDC_RET(16)
673 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
676 // ---------------------------------------------------------------------------
678 // ---------------------------------------------------------------------------
680 void wxMSWDCImpl::Clear()
687 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
691 // No, I think we should simply ignore this if printing on e.g.
693 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
694 if (!m_selectedBitmap
.IsOk())
697 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
698 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
699 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
703 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
706 DWORD colour
= ::GetBkColor(GetHdc());
707 HBRUSH brush
= ::CreateSolidBrush(colour
);
708 ::FillRect(GetHdc(), &rect
, brush
);
709 ::DeleteObject(brush
);
711 RealizeScaleAndOrigin();
714 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
715 wxCoord
WXUNUSED_IN_WINCE(y
),
716 const wxColour
& WXUNUSED_IN_WINCE(col
),
717 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
722 WXMICROWIN_CHECK_HDC_RET(false)
724 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
726 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
727 : FLOODFILLBORDER
) ) ;
730 // quoting from the MSDN docs:
732 // Following are some of the reasons this function might fail:
734 // * The filling could not be completed.
735 // * The specified point has the boundary color specified by the
736 // crColor parameter (if FLOODFILLBORDER was requested).
737 // * The specified point does not have the color specified by
738 // crColor (if FLOODFILLSURFACE was requested)
739 // * The point is outside the clipping region that is, it is not
740 // visible on the device.
742 wxLogLastError(wxT("ExtFloodFill"));
745 CalcBoundingBox(x
, y
);
751 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
753 WXMICROWIN_CHECK_HDC_RET(false)
755 wxCHECK_MSG( col
, false, wxT("NULL colour parameter in wxMSWDCImpl::GetPixel") );
757 // get the color of the pixel
758 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
760 wxRGBToColour(*col
, pixelcolor
);
765 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
769 wxCoord x1
= x
-VIEWPORT_EXTENT
;
770 wxCoord y1
= y
-VIEWPORT_EXTENT
;
771 wxCoord x2
= x
+VIEWPORT_EXTENT
;
772 wxCoord y2
= y
+VIEWPORT_EXTENT
;
774 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
775 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
777 CalcBoundingBox(x1
, y1
);
778 CalcBoundingBox(x2
, y2
);
781 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
785 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
787 CalcBoundingBox(x1
, y1
);
788 CalcBoundingBox(x2
, y2
);
791 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
792 // and ending at (x2, y2)
793 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
794 wxCoord x2
, wxCoord y2
,
795 wxCoord xc
, wxCoord yc
)
799 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
803 // Slower emulation since WinCE doesn't support Pie and Arc
804 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
806 sa
= -sa
; // below center
807 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
808 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
813 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
815 // treat the special case of full circle separately
816 if ( x1
== x2
&& y1
== y2
)
818 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
822 wxCoord xx1
= XLOG2DEV(x1
);
823 wxCoord yy1
= YLOG2DEV(y1
);
824 wxCoord xx2
= XLOG2DEV(x2
);
825 wxCoord yy2
= YLOG2DEV(y2
);
826 wxCoord xxc
= XLOG2DEV(xc
);
827 wxCoord yyc
= YLOG2DEV(yc
);
830 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
832 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
833 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
834 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
835 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
837 if ( m_brush
.IsNonTransparent() )
839 // Have to add 1 to bottom-right corner of rectangle
840 // to make semi-circles look right (crooked line otherwise).
841 // Unfortunately this is not a reliable method, depends
842 // on the size of shape.
843 // TODO: figure out why this happens!
844 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
848 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
851 CalcBoundingBox(xc
- r
, yc
- r
);
852 CalcBoundingBox(xc
+ r
, yc
+ r
);
856 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
857 wxCoord width
, wxCoord height
)
859 // cases when we don't have DrawFrameControl()
860 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
861 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
863 wxCoord x2
= x1
+ width
,
873 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
875 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
878 CalcBoundingBox(x1
, y1
);
879 CalcBoundingBox(x2
, y2
);
880 #endif // Microwin/Normal
883 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
887 COLORREF color
= 0x00ffffff;
890 color
= m_pen
.GetColour().GetPixel();
893 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
895 CalcBoundingBox(x
, y
);
898 void wxMSWDCImpl::DoDrawPolygon(int n
,
899 const wxPoint points
[],
902 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
906 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
908 // Do things less efficiently if we have offsets
909 if (xoffset
!= 0 || yoffset
!= 0)
911 POINT
*cpoints
= new POINT
[n
];
913 for (i
= 0; i
< n
; i
++)
915 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
916 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
918 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
921 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
923 (void)Polygon(GetHdc(), cpoints
, n
);
925 SetPolyFillMode(GetHdc(),prev
);
932 for (i
= 0; i
< n
; i
++)
933 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
936 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
938 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
940 SetPolyFillMode(GetHdc(),prev
);
946 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
948 const wxPoint points
[],
951 wxPolygonFillMode fillStyle
)
954 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
958 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
960 for (i
= cnt
= 0; i
< n
; i
++)
963 // Do things less efficiently if we have offsets
964 if (xoffset
!= 0 || yoffset
!= 0)
966 POINT
*cpoints
= new POINT
[cnt
];
967 for (i
= 0; i
< cnt
; i
++)
969 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
970 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
972 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
975 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
977 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
979 SetPolyFillMode(GetHdc(),prev
);
985 for (i
= 0; i
< cnt
; i
++)
986 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
989 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
991 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
993 SetPolyFillMode(GetHdc(),prev
);
1000 void wxMSWDCImpl::DoDrawLines(int n
, const wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
1002 WXMICROWIN_CHECK_HDC
1004 // Do things less efficiently if we have offsets
1005 if (xoffset
!= 0 || yoffset
!= 0)
1007 POINT
*cpoints
= new POINT
[n
];
1009 for (i
= 0; i
< n
; i
++)
1011 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
1012 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
1014 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
1016 (void)Polyline(GetHdc(), cpoints
, n
);
1022 for (i
= 0; i
< n
; i
++)
1023 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
1025 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
1029 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1031 WXMICROWIN_CHECK_HDC
1033 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1035 wxCoord x2
= x
+ width
;
1036 wxCoord y2
= y
+ height
;
1038 wxCoord x2dev
= XLOG2DEV(x2
),
1039 y2dev
= YLOG2DEV(y2
);
1041 // Windows (but not Windows CE) draws the filled rectangles without outline
1042 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1043 // and we want them to have the same size regardless of which pen is used
1045 if ( m_pen
.IsTransparent() )
1050 #endif // !__WXWINCE__
1052 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
1054 CalcBoundingBox(x
, y
);
1055 CalcBoundingBox(x2
, y2
);
1058 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
1060 WXMICROWIN_CHECK_HDC
1062 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1064 // Now, a negative radius value is interpreted to mean
1065 // 'the proportion of the smallest X or Y dimension'
1069 double smallest
= (width
< height
) ? width
: height
;
1070 radius
= (- radius
* smallest
);
1073 wxCoord x2
= (x
+width
);
1074 wxCoord y2
= (y
+height
);
1076 // Windows draws the filled rectangles without outline (i.e. drawn with a
1077 // transparent pen) one pixel smaller in both directions and we want them
1078 // to have the same size regardless of which pen is used - adjust
1079 if ( m_pen
.IsTransparent() )
1085 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1086 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1088 CalcBoundingBox(x
, y
);
1089 CalcBoundingBox(x2
, y2
);
1092 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1094 WXMICROWIN_CHECK_HDC
1096 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1098 // +1 below makes the ellipse more similar to other platforms.
1099 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1100 wxCoord x2
= x
+ width
+ 1;
1101 wxCoord y2
= y
+ height
+ 1;
1103 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1104 // pen doesn't draw anything. Should we provide a workaround?
1106 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1108 CalcBoundingBox(x
, y
);
1109 CalcBoundingBox(x2
, y2
);
1112 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1113 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1115 // quadratic b-spline to cubic bezier spline conversion
1117 // quadratic spline with control points P0,P1,P2
1118 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1120 // bezier spline with control points B0,B1,B2,B3
1121 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1123 // control points of bezier spline calculated from b-spline
1125 // B1 = (2*P1 + P0)/3
1126 // B2 = (2*P1 + P2)/3
1129 WXMICROWIN_CHECK_HDC
1131 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1133 const size_t n_points
= points
->GetCount();
1134 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1136 const size_t n_bezier_points
= n_points
* 3 + 1;
1137 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1138 size_t bezier_pos
= 0;
1139 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1141 wxPointList::compatibility_iterator node
= points
->GetFirst();
1142 wxPoint
*p
= node
->GetData();
1143 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1144 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1146 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1149 node
= node
->GetNext();
1150 p
= node
->GetData();
1154 cx1
= ( x1
+ x2
) / 2;
1155 cy1
= ( y1
+ y2
) / 2;
1156 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1157 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1159 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1162 #if !wxUSE_STD_CONTAINERS
1163 while ((node
= node
->GetNext()) != NULL
)
1165 while ((node
= node
->GetNext()))
1166 #endif // !wxUSE_STD_CONTAINERS
1168 p
= (wxPoint
*)node
->GetData();
1173 cx4
= (x1
+ x2
) / 2;
1174 cy4
= (y1
+ y2
) / 2;
1175 // B0 is B3 of previous segment
1177 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1178 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1181 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1182 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1185 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1186 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1192 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1194 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1195 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1197 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1200 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1204 #endif // wxUSE_SPLINES
1206 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1207 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1210 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1213 WXMICROWIN_CHECK_HDC
1215 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1220 int rx1
= XLOG2DEV(x
+w
/2);
1221 int ry1
= YLOG2DEV(y
+h
/2);
1228 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1229 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1230 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1231 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1233 // Swap start and end positions if the end angle is less than the start angle.
1244 // draw pie with NULL_PEN first and then outline otherwise a line is
1245 // drawn from the start and end points to the centre
1246 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1249 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1250 rx1
, ry1
, rx2
, ry2
);
1254 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1255 rx1
, ry1
-1, rx2
, ry2
-1);
1258 ::SelectObject(GetHdc(), hpenOld
);
1260 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1261 rx1
, ry1
, rx2
, ry2
);
1263 CalcBoundingBox(x
, y
);
1264 CalcBoundingBox(x2
, y2
);
1268 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1270 WXMICROWIN_CHECK_HDC
1272 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1275 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1277 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1280 CalcBoundingBox(x
, y
);
1281 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1284 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1286 WXMICROWIN_CHECK_HDC
1288 wxCHECK_RET( bmp
.IsOk(), wxT("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1290 int width
= bmp
.GetWidth(),
1291 height
= bmp
.GetHeight();
1293 HBITMAP hbmpMask
= 0;
1296 HPALETTE oldPal
= 0;
1297 #endif // wxUSE_PALETTE
1299 if ( bmp
.HasAlpha() )
1302 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1304 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1308 SET_STRETCH_BLT_MODE(GetHdc());
1312 wxMask
*mask
= bmp
.GetMask();
1314 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1318 // don't give assert here because this would break existing
1319 // programs - just silently ignore useMask parameter
1326 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1330 #if wxUSE_SYSTEM_OPTIONS
1331 // On some systems, MaskBlt succeeds yet is much much slower
1332 // than the wxWidgets fall-back implementation. So we need
1333 // to be able to switch this on and off at runtime.
1335 // NB: don't query the value of the option every time but do it only
1336 // once as otherwise it can have real (and bad) performance
1337 // implications (see #11172)
1339 s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
1340 if ( s_maskBltAllowed
)
1341 #endif // wxUSE_SYSTEM_OPTIONS
1344 HDC hdcMem
= wxMSW::CreateCompatibleDCWithLayout(cdc
);
1345 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1347 wxPalette
*pal
= bmp
.GetPalette();
1348 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1350 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1351 ::RealizePalette(hdcMem
);
1353 #endif // wxUSE_PALETTE
1355 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1358 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1362 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1363 #endif // wxUSE_PALETTE
1365 ::SelectObject(hdcMem
, hOldBitmap
);
1372 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1376 memDC
.SetLayoutDirection(GetLayoutDirection());
1377 memDC
.SelectObjectAsSource(bmp
);
1379 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1382 else // no mask, just use BitBlt()
1385 HDC memdc
= wxMSW::CreateCompatibleDCWithLayout( cdc
);
1386 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1388 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1390 wxTextColoursChanger
textCol(GetHdc(), *this);
1393 wxPalette
*pal
= bmp
.GetPalette();
1394 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1396 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1397 ::RealizePalette(memdc
);
1399 #endif // wxUSE_PALETTE
1401 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1402 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1406 ::SelectPalette(memdc
, oldPal
, FALSE
);
1407 #endif // wxUSE_PALETTE
1409 ::SelectObject( memdc
, hOldBitmap
);
1410 ::DeleteDC( memdc
);
1414 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1416 // For compatibility with other ports (notably wxGTK) and because it's
1417 // genuinely useful, we allow passing multiline strings to DrawText().
1418 // However there is no native MSW function to draw them directly so we
1419 // instead reuse the generic DrawLabel() method to render them. Of course,
1420 // DrawLabel() itself will call back to us but with single line strings
1421 // only so there won't be any infinite recursion here.
1422 if ( text
.find('\n') != wxString::npos
)
1424 GetOwner()->DrawLabel(text
, wxRect(x
, y
, 0, 0));
1428 WXMICROWIN_CHECK_HDC
1430 DrawAnyText(text
, x
, y
);
1432 // update the bounding box
1433 CalcBoundingBox(x
, y
);
1436 GetOwner()->GetTextExtent(text
, &w
, &h
);
1437 CalcBoundingBox(x
+ w
, y
+ h
);
1440 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1442 WXMICROWIN_CHECK_HDC
1444 // prepare for drawing the text
1445 wxTextColoursChanger
textCol(GetHdc(), *this);
1447 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1449 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1450 text
.c_str(), text
.length(), NULL
) == 0 )
1452 wxLogLastError(wxT("TextOut"));
1456 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1457 wxCoord x
, wxCoord y
,
1460 WXMICROWIN_CHECK_HDC
1462 // we test that we have some font because otherwise we should still use the
1463 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1464 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1465 // font for drawing rotated fonts unfortunately)
1466 if ( (angle
== 0.0) && m_font
.IsOk() )
1468 DoDrawText(text
, x
, y
);
1470 #ifndef __WXMICROWIN__
1473 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1474 // because it's not TrueType and so can't have non zero
1475 // orientation/escapement under Win9x
1476 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1477 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1479 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1481 wxLogLastError(wxT("GetObject(hfont)"));
1484 // GDI wants the angle in tenth of degree
1485 long angle10
= (long)(angle
* 10);
1486 lf
.lfEscapement
= angle10
;
1487 lf
. lfOrientation
= angle10
;
1489 hfont
= ::CreateFontIndirect(&lf
);
1492 wxLogLastError(wxT("CreateFont"));
1496 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1498 DrawAnyText(text
, x
, y
);
1500 (void)::SelectObject(GetHdc(), hfontOld
);
1501 (void)::DeleteObject(hfont
);
1504 // call the bounding box by adding all four vertices of the rectangle
1505 // containing the text to it (simpler and probably not slower than
1506 // determining which of them is really topmost/leftmost/...)
1508 GetOwner()->GetTextExtent(text
, &w
, &h
);
1510 double rad
= DegToRad(angle
);
1512 // "upper left" and "upper right"
1513 CalcBoundingBox(x
, y
);
1514 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1516 // "bottom left" and "bottom right"
1517 x
+= (wxCoord
)(h
*sin(rad
));
1518 y
+= (wxCoord
)(h
*cos(rad
));
1519 CalcBoundingBox(x
, y
);
1520 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1525 // ---------------------------------------------------------------------------
1527 // ---------------------------------------------------------------------------
1531 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1533 WXMICROWIN_CHECK_HDC
1535 // Set the old object temporarily, in case the assignment deletes an object
1536 // that's not yet selected out.
1539 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1543 if ( m_palette
.IsOk() )
1545 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1546 GetHpaletteOf(m_palette
),
1549 m_oldPalette
= (WXHPALETTE
) oldPal
;
1552 ::RealizePalette(GetHdc());
1556 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1558 if ( palette
.IsOk() )
1560 m_palette
= palette
;
1561 DoSelectPalette(true);
1565 void wxMSWDCImpl::InitializePalette()
1567 if ( wxDisplayDepth() <= 8 )
1569 // look for any window or parent that has a custom palette. If any has
1570 // one then we need to use it in drawing operations
1571 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1573 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1574 if ( m_hasCustomPalette
)
1576 m_palette
= win
->GetPalette();
1578 // turn on MSW translation for this palette
1584 #endif // wxUSE_PALETTE
1586 // SetFont/Pen/Brush() really ask to be implemented as a single template
1587 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1589 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1591 WXMICROWIN_CHECK_HDC
1593 if ( font
== m_font
)
1598 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1599 if ( hfont
== HGDI_ERROR
)
1601 wxLogLastError(wxT("SelectObject(font)"));
1606 m_oldFont
= (WXHFONT
)hfont
;
1611 else // invalid font, reset the current font
1615 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1617 wxLogLastError(wxT("SelectObject(old font)"));
1623 m_font
= wxNullFont
;
1627 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1629 WXMICROWIN_CHECK_HDC
1636 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1637 if ( hpen
== HGDI_ERROR
)
1639 wxLogLastError(wxT("SelectObject(pen)"));
1644 m_oldPen
= (WXHPEN
)hpen
;
1649 else // invalid pen, reset the current pen
1653 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1655 wxLogLastError(wxT("SelectObject(old pen)"));
1665 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1667 WXMICROWIN_CHECK_HDC
1669 if ( brush
== m_brush
)
1674 // we must make sure the brush is aligned with the logical coordinates
1675 // before selecting it or using the same brush for the background of
1676 // different windows would result in discontinuities
1677 wxSize sizeBrushBitmap
= wxDefaultSize
;
1678 wxBitmap
*stipple
= brush
.GetStipple();
1679 if ( stipple
&& stipple
->IsOk() )
1680 sizeBrushBitmap
= stipple
->GetSize();
1681 else if ( brush
.IsHatch() )
1682 sizeBrushBitmap
= wxSize(8, 8);
1684 if ( sizeBrushBitmap
.IsFullySpecified() )
1686 if ( !::SetBrushOrgEx
1689 m_deviceOriginX
% sizeBrushBitmap
.x
,
1690 m_deviceOriginY
% sizeBrushBitmap
.y
,
1691 NULL
// [out] previous brush origin
1694 wxLogLastError(wxT("SetBrushOrgEx()"));
1698 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1699 if ( hbrush
== HGDI_ERROR
)
1701 wxLogLastError(wxT("SelectObject(brush)"));
1706 m_oldBrush
= (WXHBRUSH
)hbrush
;
1711 else // invalid brush, reset the current brush
1715 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1717 wxLogLastError(wxT("SelectObject(old brush)"));
1723 m_brush
= wxNullBrush
;
1727 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1729 WXMICROWIN_CHECK_HDC
1731 m_backgroundBrush
= brush
;
1733 if ( m_backgroundBrush
.IsOk() )
1735 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1739 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1741 WXMICROWIN_CHECK_HDC
1743 m_backgroundMode
= mode
;
1745 // SetBackgroundColour now only refers to text background
1746 // and m_backgroundMode is used there
1749 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1751 WXMICROWIN_CHECK_HDC
1753 m_logicalFunction
= function
;
1758 void wxMSWDCImpl::SetRop(WXHDC dc
)
1760 if ( !dc
|| m_logicalFunction
< 0 )
1765 switch (m_logicalFunction
)
1767 case wxCLEAR
: rop
= R2_BLACK
; break;
1768 case wxXOR
: rop
= R2_XORPEN
; break;
1769 case wxINVERT
: rop
= R2_NOT
; break;
1770 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1771 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1772 case wxCOPY
: rop
= R2_COPYPEN
; break;
1773 case wxAND
: rop
= R2_MASKPEN
; break;
1774 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1775 case wxNO_OP
: rop
= R2_NOP
; break;
1776 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1777 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1778 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1779 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1780 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1781 case wxOR
: rop
= R2_MERGEPEN
; break;
1782 case wxSET
: rop
= R2_WHITE
; break;
1784 wxFAIL_MSG( wxS("unknown logical function") );
1788 SetROP2(GetHdc(), rop
);
1791 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1793 // We might be previewing, so return true to let it continue.
1797 void wxMSWDCImpl::EndDoc()
1801 void wxMSWDCImpl::StartPage()
1805 void wxMSWDCImpl::EndPage()
1809 // ---------------------------------------------------------------------------
1811 // ---------------------------------------------------------------------------
1813 wxCoord
wxMSWDCImpl::GetCharHeight() const
1815 WXMICROWIN_CHECK_HDC_RET(0)
1817 TEXTMETRIC lpTextMetric
;
1819 GetTextMetrics(GetHdc(), &lpTextMetric
);
1821 return lpTextMetric
.tmHeight
;
1824 wxCoord
wxMSWDCImpl::GetCharWidth() const
1826 WXMICROWIN_CHECK_HDC_RET(0)
1828 TEXTMETRIC lpTextMetric
;
1830 GetTextMetrics(GetHdc(), &lpTextMetric
);
1832 return lpTextMetric
.tmAveCharWidth
;
1835 void wxMSWDCImpl::DoGetFontMetrics(int *height
,
1838 int *internalLeading
,
1839 int *externalLeading
,
1840 int *averageWidth
) const
1844 GetTextMetrics(GetHdc(), &tm
);
1847 *height
= tm
.tmHeight
;
1849 *ascent
= tm
.tmAscent
;
1851 *descent
= tm
.tmDescent
;
1852 if ( internalLeading
)
1853 *internalLeading
= tm
.tmInternalLeading
;
1854 if ( externalLeading
)
1855 *externalLeading
= tm
.tmExternalLeading
;
1857 *averageWidth
= tm
.tmAveCharWidth
;
1860 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1861 wxCoord
*descent
, wxCoord
*externalLeading
,
1862 const wxFont
*font
) const
1864 #ifdef __WXMICROWIN__
1869 if (descent
) *descent
= 0;
1870 if (externalLeading
) *externalLeading
= 0;
1873 #endif // __WXMICROWIN__
1875 wxASSERT_MSG( !font
|| font
->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") );
1877 wxTextMeasure
txm(GetOwner(), font
);
1878 txm
.GetTextExtent(string
, x
, y
, descent
, externalLeading
);
1882 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1884 wxTextMeasure
txm(GetOwner(), NULL
); // don't change the font
1885 return txm
.GetPartialTextExtents(text
, widths
, 1.0);
1891 void ApplyEffectiveScale(double scale
, int sign
, int *device
, int *logical
)
1893 // To reduce rounding errors as much as possible, we try to use the largest
1894 // possible extent (2^27-1) for the device space but we must also avoid
1895 // overflowing the int range i.e. ensure that logical extents are less than
1896 // 2^31 in magnitude. So the minimal scale we can use is 1/16 as for
1897 // anything smaller VIEWPORT_EXTENT/scale would overflow the int range.
1898 static const double MIN_LOGICAL_SCALE
= 1./16;
1900 double physExtent
= VIEWPORT_EXTENT
;
1901 if ( scale
< MIN_LOGICAL_SCALE
)
1903 physExtent
*= scale
/MIN_LOGICAL_SCALE
;
1904 scale
= MIN_LOGICAL_SCALE
;
1907 *device
= wxRound(physExtent
);
1908 *logical
= sign
*wxRound(VIEWPORT_EXTENT
/scale
);
1911 } // anonymous namespace
1913 void wxMSWDCImpl::RealizeScaleAndOrigin()
1915 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1916 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1917 // noticeable difference between these mapping modes
1919 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1921 // wxWidgets API assumes that the coordinate space is "infinite" (i.e. only
1922 // limited by 2^32 range of the integer coordinates) but in MSW API we must
1923 // actually specify the extents that we use so compute them here.
1925 int devExtX
, devExtY
, // Viewport, i.e. device space, extents.
1926 logExtX
, logExtY
; // Window, i.e. logical coordinate space, extents.
1928 ApplyEffectiveScale(m_scaleX
, m_signX
, &devExtX
, &logExtX
);
1929 ApplyEffectiveScale(m_scaleY
, m_signY
, &devExtY
, &logExtY
);
1931 ::SetViewportExtEx(GetHdc(), devExtX
, devExtY
, NULL
);
1932 ::SetWindowExtEx(GetHdc(), logExtX
, logExtY
, NULL
);
1934 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1935 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1939 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1941 WXMICROWIN_CHECK_HDC
1943 m_mappingMode
= mode
;
1945 if ( mode
== wxMM_TEXT
)
1948 m_logicalScaleY
= 1.0;
1950 else // need to do some calculations
1952 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1953 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1954 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1955 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1957 if ( (mm_width
== 0) || (mm_height
== 0) )
1959 // we can't calculate mm2pixels[XY] then!
1963 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1964 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1969 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1970 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1974 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1975 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1979 m_logicalScaleX
= mm2pixelsX
;
1980 m_logicalScaleY
= mm2pixelsY
;
1984 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1985 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1989 wxFAIL_MSG( wxT("unknown mapping mode in SetMapMode") );
1993 ComputeScaleAndOrigin();
1995 RealizeScaleAndOrigin();
1998 void wxMSWDCImpl::SetUserScale(double x
, double y
)
2000 WXMICROWIN_CHECK_HDC
2002 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
2005 wxDCImpl::SetUserScale(x
,y
);
2007 RealizeScaleAndOrigin();
2010 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2013 WXMICROWIN_CHECK_HDC
2015 int signX
= xLeftRight
? 1 : -1,
2016 signY
= yBottomUp
? -1 : 1;
2018 if (signX
== m_signX
&& signY
== m_signY
)
2021 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2023 RealizeScaleAndOrigin();
2026 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2028 WXMICROWIN_CHECK_HDC
2030 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2033 wxDCImpl::SetLogicalOrigin( x
, y
);
2035 RealizeScaleAndOrigin();
2038 // For use by wxWidgets only, unless custom units are required.
2039 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2041 WXMICROWIN_CHECK_HDC
2043 wxDCImpl::SetLogicalScale(x
,y
);
2046 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2048 WXMICROWIN_CHECK_HDC
2050 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2053 wxDCImpl::SetDeviceOrigin( x
, y
);
2055 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2058 // ----------------------------------------------------------------------------
2060 // ----------------------------------------------------------------------------
2062 #if wxUSE_DC_TRANSFORM_MATRIX
2064 bool wxMSWDCImpl::CanUseTransformMatrix() const
2066 return GdiWorldTransformFuncs::IsOk();
2069 bool wxMSWDCImpl::SetTransformMatrix(const wxAffineMatrix2D
&matrix
)
2071 if ( !GdiWorldTransformFuncs::IsOk() )
2074 if ( matrix
.IsIdentity() )
2076 ResetTransformMatrix();
2080 if ( !GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_ADVANCED
) )
2082 wxLogLastError(wxT("SetGraphicsMode"));
2088 matrix
.Get(&mat
, &tr
);
2091 xform
.eM11
= mat
.m_11
;
2092 xform
.eM12
= mat
.m_12
;
2093 xform
.eM21
= mat
.m_21
;
2094 xform
.eM22
= mat
.m_22
;
2098 if ( !GdiWorldTransformFuncs::SetWorldTransform()(GetHdc(), &xform
) )
2100 wxLogLastError(wxT("SetWorldTransform"));
2107 wxAffineMatrix2D
wxMSWDCImpl::GetTransformMatrix() const
2109 wxAffineMatrix2D transform
;
2111 if ( !GdiWorldTransformFuncs::IsOk() )
2115 if ( !GdiWorldTransformFuncs::GetWorldTransform()(GetHdc(), &xform
) )
2117 wxLogLastError(wxT("GetWorldTransform"));
2121 wxMatrix2D
m(xform
.eM11
, xform
.eM12
, xform
.eM21
, xform
.eM22
);
2122 wxPoint2DDouble
p(xform
.eDx
, xform
.eDy
);
2123 transform
.Set(m
, p
);
2128 void wxMSWDCImpl::ResetTransformMatrix()
2130 if ( GdiWorldTransformFuncs::IsOk() )
2132 GdiWorldTransformFuncs::ModifyWorldTransform()(GetHdc(), NULL
, MWT_IDENTITY
);
2133 GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_COMPATIBLE
);
2137 #endif // wxUSE_DC_TRANSFORM_MATRIX
2139 // ---------------------------------------------------------------------------
2141 // ---------------------------------------------------------------------------
2143 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2144 wxCoord dstWidth
, wxCoord dstHeight
,
2146 wxCoord srcX
, wxCoord srcY
,
2147 wxRasterOperationMode rop
, bool useMask
,
2148 wxCoord srcMaskX
, wxCoord srcMaskY
)
2150 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2153 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2154 wxCoord dstWidth
, wxCoord dstHeight
,
2156 wxCoord xsrc
, wxCoord ysrc
,
2157 wxCoord srcWidth
, wxCoord srcHeight
,
2158 wxRasterOperationMode rop
, bool useMask
,
2159 wxCoord xsrcMask
, wxCoord ysrcMask
)
2161 wxCHECK_MSG( source
, false, wxT("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2163 WXMICROWIN_CHECK_HDC_RET(false)
2165 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2168 // TODO: Do we want to be able to blit from other DCs too?
2172 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2174 // if either the source or destination has alpha channel, we must use
2175 // AlphaBlt() as other function don't handle it correctly
2176 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2177 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2178 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2180 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2181 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2185 wxMask
*mask
= NULL
;
2188 mask
= bmpSrc
.GetMask();
2190 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2192 // don't give assert here because this would break existing
2193 // programs - just silently ignore useMask parameter
2198 if (xsrcMask
== -1 && ysrcMask
== -1)
2200 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2203 wxTextColoursChanger
textCol(GetHdc(), *this);
2208 case wxXOR
: dwRop
= SRCINVERT
; break;
2209 case wxINVERT
: dwRop
= DSTINVERT
; break;
2210 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2211 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2212 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2213 case wxSET
: dwRop
= WHITENESS
; break;
2214 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2215 case wxAND
: dwRop
= SRCAND
; break;
2216 case wxOR
: dwRop
= SRCPAINT
; break;
2217 case wxEQUIV
: dwRop
= 0x00990066; break;
2218 case wxNAND
: dwRop
= 0x007700E6; break;
2219 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2220 case wxCOPY
: dwRop
= SRCCOPY
; break;
2221 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2222 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2223 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2225 wxFAIL_MSG( wxT("unsupported logical function") );
2229 bool success
= false;
2234 // we want the part of the image corresponding to the mask to be
2235 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2236 // meaning of fg and bg is inverted which corresponds to wxWin notion
2237 // of the mask which is also contrary to the Windows one)
2239 // On some systems, MaskBlt succeeds yet is much much slower
2240 // than the wxWidgets fall-back implementation. So we need
2241 // to be able to switch this on and off at runtime.
2242 #if wxUSE_SYSTEM_OPTIONS
2243 static bool s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
2244 if ( s_maskBltAllowed
)
2247 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2252 xdest
, ydest
, dstWidth
, dstHeight
,
2255 (HBITMAP
)mask
->GetMaskBitmap(),
2257 MAKEROP4(dwRop
, DSTCOPY
)
2265 // Blit bitmap with mask
2268 HBITMAP buffer_bmap
;
2270 #if wxUSE_DC_CACHEING
2271 // create a temp buffer bitmap and DCs to access it and the mask
2272 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2273 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2275 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2276 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2278 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2279 dstWidth
, dstHeight
);
2281 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2282 #else // !wxUSE_DC_CACHEING
2283 // create a temp buffer bitmap and DCs to access it and the mask
2284 dc_mask
= wxMSW::CreateCompatibleDCWithLayout(hdcSrc
);
2285 dc_buffer
= wxMSW::CreateCompatibleDCWithLayout(GetHdc());
2286 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2287 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2288 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2289 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2291 // copy dest to buffer
2292 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2293 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2295 wxLogLastError(wxT("BitBlt"));
2298 SET_STRETCH_BLT_MODE(GetHdc());
2300 // copy src to buffer using selected raster op
2301 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2302 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2304 wxLogLastError(wxT("StretchBlt"));
2307 // set masked area in buffer to BLACK
2309 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2310 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2311 dc_mask
, xsrcMask
, ysrcMask
,
2312 srcWidth
, srcHeight
, SRCAND
) )
2314 wxLogLastError(wxT("StretchBlt"));
2317 // set unmasked area in dest to BLACK
2318 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2319 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2320 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2321 dc_mask
, xsrcMask
, ysrcMask
,
2322 srcWidth
, srcHeight
, SRCAND
) )
2324 wxLogLastError(wxT("StretchBlt"));
2326 } // restore the original text and background colours
2328 // OR buffer to dest
2329 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2330 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2333 wxLogLastError(wxT("BitBlt"));
2336 // tidy up temporary DCs and bitmap
2337 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2338 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2340 #if !wxUSE_DC_CACHEING
2342 ::DeleteDC(dc_mask
);
2343 ::DeleteDC(dc_buffer
);
2344 ::DeleteObject(buffer_bmap
);
2349 else // no mask, just BitBlt() it
2351 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2352 // use StretchBlt() if available and finally fall back to BitBlt()
2354 // FIXME: use appropriate WinCE functions
2356 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2357 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2362 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2364 &ds
) == sizeof(ds
) )
2366 SET_STRETCH_BLT_MODE(GetHdc());
2368 // Unlike all the other functions used here (i.e. AlphaBlt(),
2369 // MaskBlt(), BitBlt() and StretchBlt()), StretchDIBits() does
2370 // not take into account the source DC logical coordinates
2371 // automatically as it doesn't even work with the source HDC.
2372 // So do this manually to ensure that the coordinates are
2373 // interpreted in the same way here as in all the other cases.
2374 xsrc
= source
->LogicalToDeviceX(xsrc
);
2375 ysrc
= source
->LogicalToDeviceY(ysrc
);
2376 srcWidth
= source
->LogicalToDeviceXRel(srcWidth
);
2377 srcHeight
= source
->LogicalToDeviceYRel(srcHeight
);
2379 // Figure out what co-ordinate system we're supposed to specify
2381 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2385 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2388 if ( ::StretchDIBits(GetHdc(),
2390 dstWidth
, dstHeight
,
2392 srcWidth
, srcHeight
,
2394 (LPBITMAPINFO
)&ds
.dsBmih
,
2397 ) == (int)GDI_ERROR
)
2399 // On Win9x this API fails most (all?) of the time, so
2400 // logging it becomes quite distracting. Since it falls
2401 // back to the code below this is not really serious, so
2403 //wxLogLastError(wxT("StretchDIBits"));
2412 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2416 SET_STRETCH_BLT_MODE(GetHdc());
2421 xdest
, ydest
, dstWidth
, dstHeight
,
2423 xsrc
, ysrc
, srcWidth
, srcHeight
,
2427 wxLogLastError(wxT("StretchBlt"));
2437 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2438 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2440 wxLogLastError(wxT("BitBlt"));
2452 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2454 WXMICROWIN_CHECK_HDC
2457 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2459 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2462 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2464 WXMICROWIN_CHECK_HDC
2466 // if we implement it in terms of DoGetSize() instead of directly using the
2467 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2468 // will also work for wxWindowDC and wxClientDC even though their size is
2469 // not the same as the total size of the screen
2470 int wPixels
, hPixels
;
2471 DoGetSize(&wPixels
, &hPixels
);
2475 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2477 wxCHECK_RET( wTotal
, wxT("0 width device?") );
2479 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2484 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2486 wxCHECK_RET( hTotal
, wxT("0 height device?") );
2488 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2492 wxSize
wxMSWDCImpl::GetPPI() const
2494 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2496 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2497 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2499 return wxSize(x
, y
);
2502 // ----------------------------------------------------------------------------
2504 // ----------------------------------------------------------------------------
2506 #if wxUSE_DC_CACHEING
2509 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2510 * improve it in due course, either using arrays, or simply storing pointers to one
2511 * entry for the bitmap, and two for the DCs. -- JACS
2514 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2515 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2517 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2526 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2535 wxDCCacheEntry::~wxDCCacheEntry()
2538 ::DeleteObject((HBITMAP
) m_bitmap
);
2540 ::DeleteDC((HDC
) m_dc
);
2543 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2545 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2546 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2549 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2551 if (entry
->m_depth
== depth
)
2553 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2555 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2556 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2557 if ( !entry
->m_bitmap
)
2559 wxLogLastError(wxT("CreateCompatibleBitmap"));
2561 entry
->m_width
= w
; entry
->m_height
= h
;
2567 node
= node
->GetNext();
2569 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2572 wxLogLastError(wxT("CreateCompatibleBitmap"));
2574 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2575 AddToBitmapCache(entry
);
2579 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2581 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2582 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2585 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2587 // Don't return the same one as we already have
2588 if (!notThis
|| (notThis
!= entry
))
2590 if (entry
->m_depth
== depth
)
2596 node
= node
->GetNext();
2598 WXHDC hDC
= (WXHDC
) wxMSW::CreateCompatibleDCWithLayout((HDC
) dc
);
2601 wxLogLastError(wxT("CreateCompatibleDC"));
2603 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2604 AddToDCCache(entry
);
2608 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2610 sm_bitmapCache
.Append(entry
);
2613 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2615 sm_dcCache
.Append(entry
);
2618 void wxMSWDCImpl::ClearCache()
2620 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2621 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2624 // Clean up cache at app exit
2625 class wxDCModule
: public wxModule
2628 virtual bool OnInit() { return true; }
2629 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2632 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2635 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2637 #endif // wxUSE_DC_CACHEING
2639 // ----------------------------------------------------------------------------
2640 // alpha channel support
2641 // ----------------------------------------------------------------------------
2643 static bool AlphaBlt(HDC hdcDst
,
2644 int x
, int y
, int dstWidth
, int dstHeight
,
2646 int srcWidth
, int srcHeight
,
2648 const wxBitmap
& bmp
)
2650 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2651 wxASSERT_MSG( hdcDst
&& hdcSrc
, wxT("AlphaBlt(): invalid HDC") );
2653 // do we have AlphaBlend() and company in the headers?
2654 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2655 // yes, now try to see if we have it during run-time
2656 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2657 HDC
,int,int,int,int,
2661 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(wxT("AlphaBlend"));
2662 if ( pfnAlphaBlend
)
2665 bf
.BlendOp
= AC_SRC_OVER
;
2667 bf
.SourceConstantAlpha
= 0xff;
2668 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2670 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2671 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2674 // skip wxAlphaBlend() call below
2678 wxLogLastError(wxT("AlphaBlend"));
2681 wxUnusedVar(hdcSrc
);
2682 #endif // defined(AC_SRC_OVER)
2684 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2686 #ifdef wxHAS_RAW_BITMAP
2687 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2690 #else // !wxHAS_RAW_BITMAP
2691 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2692 // alpha but at least something will be shown like this)
2695 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2699 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2700 #ifdef wxHAS_RAW_BITMAP
2703 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2704 int dstWidth
, int dstHeight
,
2706 int srcWidth
, int srcHeight
,
2707 const wxBitmap
& bmpSrc
)
2709 // get the destination DC pixels
2710 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2712 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2714 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2716 wxLogLastError(wxT("BitBlt"));
2719 // combine them with the source bitmap using alpha
2720 wxAlphaPixelData
dataDst(bmpDst
),
2721 dataSrc((wxBitmap
&)bmpSrc
);
2723 wxCHECK_RET( dataDst
&& dataSrc
,
2724 wxT("failed to get raw data in wxAlphaBlend") );
2726 wxAlphaPixelData::Iterator
pDst(dataDst
),
2730 for ( int y
= 0; y
< dstHeight
; y
++ )
2732 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2734 for ( int x
= 0; x
< dstWidth
; x
++ )
2736 // source is point sampled, Alpha StretchBlit is ugly on Win95
2737 // (but does not impact performance)
2738 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2740 // note that source bitmap uses premultiplied alpha (as required by
2741 // the real AlphaBlend)
2742 const unsigned beta
= 255 - pSrc
.Alpha();
2744 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2745 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2746 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2751 pDst
= pDstRowStart
;
2752 pDst
.OffsetY(dataDst
, 1);
2755 // and finally blit them back to the destination DC
2756 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2758 wxLogLastError(wxT("BitBlt"));
2762 #endif // wxHAS_RAW_BITMAP
2764 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2765 const wxColour
& initialColour
,
2766 const wxColour
& destColour
,
2767 wxDirection nDirection
)
2769 // use native function if we have compile-time support it and can load it
2770 // during run-time (linking to it statically would make the program
2771 // unusable on earlier Windows versions)
2772 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2774 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2775 static GradientFill_t pfnGradientFill
=
2776 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(wxT("GradientFill"));
2778 if ( pfnGradientFill
)
2780 GRADIENT_RECT grect
;
2781 grect
.UpperLeft
= 0;
2782 grect
.LowerRight
= 1;
2784 // invert colours direction if not filling from left-to-right or
2786 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2788 // one vertex for upper left and one for upper-right
2789 TRIVERTEX vertices
[2];
2791 vertices
[0].x
= rect
.GetLeft();
2792 vertices
[0].y
= rect
.GetTop();
2793 vertices
[1].x
= rect
.GetRight()+1;
2794 vertices
[1].y
= rect
.GetBottom()+1;
2796 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2797 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2798 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2799 vertices
[firstVertex
].Alpha
= 0;
2800 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2801 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2802 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2803 vertices
[1 - firstVertex
].Alpha
= 0;
2805 if ( (*pfnGradientFill
)
2812 nDirection
== wxWEST
|| nDirection
== wxEAST
2813 ? GRADIENT_FILL_RECT_H
2814 : GRADIENT_FILL_RECT_V
2817 // skip call of the base class version below
2821 wxLogLastError(wxT("GradientFill"));
2823 #endif // wxUSE_DYNLIB_CLASS
2825 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2828 #if wxUSE_DYNLIB_CLASS
2833 DWORD
GetLayout(HDC hdc
)
2835 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2837 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2839 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : GDI_ERROR
;
2842 DWORD
SetLayout(HDC hdc
, DWORD dwLayout
)
2844 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2846 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2848 return s_pfnSetLayout
? s_pfnSetLayout(hdc
, dwLayout
) : GDI_ERROR
;
2851 HDC
CreateCompatibleDCWithLayout(HDC hdc
)
2853 HDC hdcNew
= ::CreateCompatibleDC(hdc
);
2856 DWORD dwLayout
= wxMSW::GetLayout(hdc
);
2857 if ( dwLayout
!= GDI_ERROR
)
2858 wxMSW::SetLayout(hdcNew
, dwLayout
);
2864 } // namespace wxMSW
2866 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2868 DWORD layout
= wxMSW::GetLayout(GetHdc());
2870 if ( layout
== GDI_ERROR
)
2871 return wxLayout_Default
;
2873 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2876 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2878 if ( dir
== wxLayout_Default
)
2880 dir
= wxTheApp
->GetLayoutDirection();
2881 if ( dir
== wxLayout_Default
)
2885 DWORD layout
= wxMSW::GetLayout(GetHdc());
2886 if ( layout
== GDI_ERROR
)
2889 if ( dir
== wxLayout_RightToLeft
)
2890 layout
|= LAYOUT_RTL
;
2892 layout
&= ~LAYOUT_RTL
;
2894 wxMSW::SetLayout(GetHdc(), layout
);
2897 #else // !wxUSE_DYNLIB_CLASS
2899 // Provide stubs to avoid ifdefs in the code using these functions.
2903 DWORD
GetLayout(HDC
WXUNUSED(hdc
))
2908 DWORD
SetLayout(HDC
WXUNUSED(hdc
), DWORD
WXUNUSED(dwLayout
))
2913 HDC
CreateCompatibleDCWithLayout(HDC hdc
)
2915 return ::CreateCompatibleDC(hdc
);
2918 } // namespace wxMSW
2920 // we can't provide RTL support without dynamic loading, so stub it out
2921 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2923 return wxLayout_Default
;
2926 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2930 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS