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"
39 #include "wx/dcprint.h"
40 #include "wx/module.h"
43 #include "wx/msw/dc.h"
44 #include "wx/sysopt.h"
45 #include "wx/dynlib.h"
47 #ifdef wxHAS_RAW_BITMAP
48 #include "wx/rawbmp.h"
53 #include "wx/msw/private/dc.h"
54 #include "wx/private/textmeasure.h"
56 using namespace wxMSWImpl
;
59 #define AC_SRC_ALPHA 1
66 /* Quaternary raster codes */
68 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
71 // apparently with MicroWindows it is possible that HDC is 0 so we have to
72 // check for this ourselves
74 #define WXMICROWIN_CHECK_HDC if ( !GetHDC() ) return;
75 #define WXMICROWIN_CHECK_HDC_RET(x) if ( !GetHDC() ) return x;
77 #define WXMICROWIN_CHECK_HDC
78 #define WXMICROWIN_CHECK_HDC_RET(x)
81 IMPLEMENT_ABSTRACT_CLASS(wxMSWDCImpl
, wxDCImpl
)
83 // ---------------------------------------------------------------------------
85 // ---------------------------------------------------------------------------
87 // The device space in Win32 GDI measures 2^27*2^27 , so we use 2^27-1 as the
88 // maximal possible view port extent.
89 static const int VIEWPORT_EXTENT
= 134217727;
91 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
92 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
93 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
95 // ----------------------------------------------------------------------------
96 // macros for logical <-> device coords conversion
97 // ----------------------------------------------------------------------------
100 We currently let Windows do all the translations itself so these macros are
101 not really needed (any more) but keep them to enhance readability of the
102 code by allowing to see where are the logical and where are the device
107 #define XLOG2DEV(x) ((x-m_logicalOriginX)*m_signX)
108 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
109 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
110 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
112 #define XLOG2DEV(x) (x)
113 #define YLOG2DEV(y) (y)
114 #define XDEV2LOG(x) (x)
115 #define YDEV2LOG(y) (y)
118 // ---------------------------------------------------------------------------
120 // ---------------------------------------------------------------------------
122 // convert degrees to radians
123 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
125 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
127 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
128 // to pass it to this function but as we already have it at the point
129 // of call anyhow we do
131 // return true if we could draw the bitmap in one way or the other, false
133 static bool AlphaBlt(HDC hdcDst
,
134 int x
, int y
, int dstWidth
, int dstHeight
,
136 int srcWidth
, int srcHeight
,
138 const wxBitmap
& bmp
);
140 #ifdef wxHAS_RAW_BITMAP
142 // our (limited) AlphaBlend() replacement for Windows versions not providing it
144 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
145 int dstWidth
, int dstHeight
,
147 int srcWidth
, int srcHeight
,
148 const wxBitmap
& bmpSrc
);
150 #endif // wxHAS_RAW_BITMAP
155 // Wrappers for the dynamically loaded {Set,Get}Layout() functions. They work
156 // in exactly the same way as the standard functions and return GDI_ERROR if
157 // they're not actually available.
158 DWORD
GetLayout(HDC hdc
);
159 DWORD
SetLayout(HDC hdc
, DWORD dwLayout
);
161 // Create a compatible HDC and copy the layout of the source DC to it. This is
162 // necessary in order to draw bitmaps (which are usually blitted from a
163 // temporary compatible memory DC to the real target DC) using the same layout.
164 HDC
CreateCompatibleDCWithLayout(HDC hdc
);
168 // ----------------------------------------------------------------------------
170 // ----------------------------------------------------------------------------
172 // instead of duplicating the same code which sets and then restores text
173 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
174 // encapsulate this in a small helper class
176 // wxBrushAttrsSetter: changes the text colours in the ctor if required and
177 // restores them in the dtor
178 class wxBrushAttrsSetter
: private wxBkModeChanger
,
179 private wxTextColoursChanger
182 wxBrushAttrsSetter(wxMSWDCImpl
& dc
);
185 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter
);
190 #define SET_STRETCH_BLT_MODE(hdc)
192 #else // !__WXWINCE__
194 // this class sets the stretch blit mode to COLORONCOLOR during its lifetime
196 // don't use it directly, use SET_STRETCH_BLT_MODE() macro instead as it
197 // expands to nothing under WinCE which doesn't have SetStretchBltMode()
198 class StretchBltModeChanger
201 StretchBltModeChanger(HDC hdc
)
204 m_modeOld
= ::SetStretchBltMode(m_hdc
, COLORONCOLOR
);
207 wxLogLastError(wxT("SetStretchBltMode"));
211 ~StretchBltModeChanger()
213 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
215 wxLogLastError(wxT("SetStretchBltMode"));
224 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
227 #define SET_STRETCH_BLT_MODE(hdc) \
228 StretchBltModeChanger wxMAKE_UNIQUE_NAME(stretchModeChanger)(hdc)
230 #endif // __WXWINCE__/!__WXWINCE__
232 #if wxUSE_DYNLIB_CLASS
234 // helper class to cache dynamically loaded libraries and not attempt reloading
236 class wxOnceOnlyDLLLoader
239 // ctor argument must be a literal string as we don't make a copy of it!
240 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
246 // return the symbol with the given name or NULL if the DLL not loaded
247 // or symbol not present
248 void *GetSymbol(const wxChar
*name
)
250 // we're prepared to handle errors here
255 m_dll
.Load(m_dllName
);
257 // reset the name whether we succeeded or failed so that we don't
258 // try again the next time
262 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
267 if ( m_dll
.IsLoaded() )
274 wxDynamicLibrary m_dll
;
275 const wxChar
*m_dllName
;
278 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(wxT("msimg32"));
280 // we must ensure that DLLs are unloaded before the static objects cleanup time
281 // because we may hit the notorious DllMain() dead lock in this case if wx is
282 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
283 // under Windows because it tries to reacquire the same lock)
284 class wxGDIDLLsCleanupModule
: public wxModule
287 virtual bool OnInit() { return true; }
288 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
291 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
294 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
299 #if wxUSE_DC_TRANSFORM_MATRIX
301 // Class used to dynamically load world transform related API functions.
302 class GdiWorldTransformFuncs
307 if ( !ms_worldTransformSymbolsLoaded
)
308 LoadWorldTransformSymbols();
310 return ms_pfnSetGraphicsMode
&&
311 ms_pfnSetWorldTransform
&&
312 ms_pfnGetWorldTransform
&&
313 ms_pfnModifyWorldTransform
;
316 typedef int (WINAPI
*SetGraphicsMode_t
)(HDC
, int);
317 static SetGraphicsMode_t
SetGraphicsMode()
319 if ( !ms_worldTransformSymbolsLoaded
)
320 LoadWorldTransformSymbols();
322 return ms_pfnSetGraphicsMode
;
325 typedef BOOL (WINAPI
*SetWorldTransform_t
)(HDC
, const XFORM
*);
326 static SetWorldTransform_t
SetWorldTransform()
328 if ( !ms_worldTransformSymbolsLoaded
)
329 LoadWorldTransformSymbols();
331 return ms_pfnSetWorldTransform
;
334 typedef BOOL (WINAPI
*GetWorldTransform_t
)(HDC
, LPXFORM
);
335 static GetWorldTransform_t
GetWorldTransform()
337 if ( !ms_worldTransformSymbolsLoaded
)
338 LoadWorldTransformSymbols();
340 return ms_pfnGetWorldTransform
;
343 typedef BOOL (WINAPI
*ModifyWorldTransform_t
)(HDC
, const XFORM
*, DWORD
);
344 static ModifyWorldTransform_t
ModifyWorldTransform()
346 if ( !ms_worldTransformSymbolsLoaded
)
347 LoadWorldTransformSymbols();
349 return ms_pfnModifyWorldTransform
;
353 static void LoadWorldTransformSymbols()
355 wxDynamicLibrary
dll(wxT("gdi32.dll"));
357 wxDL_INIT_FUNC(ms_pfn
, SetGraphicsMode
, dll
);
358 wxDL_INIT_FUNC(ms_pfn
, SetWorldTransform
, dll
);
359 wxDL_INIT_FUNC(ms_pfn
, GetWorldTransform
, dll
);
360 wxDL_INIT_FUNC(ms_pfn
, ModifyWorldTransform
, dll
);
362 ms_worldTransformSymbolsLoaded
= true;
365 static SetGraphicsMode_t ms_pfnSetGraphicsMode
;
366 static SetWorldTransform_t ms_pfnSetWorldTransform
;
367 static GetWorldTransform_t ms_pfnGetWorldTransform
;
368 static ModifyWorldTransform_t ms_pfnModifyWorldTransform
;
370 static bool ms_worldTransformSymbolsLoaded
;
373 GdiWorldTransformFuncs::SetGraphicsMode_t
374 GdiWorldTransformFuncs::ms_pfnSetGraphicsMode
= NULL
;
375 GdiWorldTransformFuncs::SetWorldTransform_t
376 GdiWorldTransformFuncs::ms_pfnSetWorldTransform
= NULL
;
377 GdiWorldTransformFuncs::GetWorldTransform_t
378 GdiWorldTransformFuncs::ms_pfnGetWorldTransform
= NULL
;
379 GdiWorldTransformFuncs::ModifyWorldTransform_t
380 GdiWorldTransformFuncs::ms_pfnModifyWorldTransform
= NULL
;
382 bool GdiWorldTransformFuncs::ms_worldTransformSymbolsLoaded
= false;
384 #endif // wxUSE_DC_TRANSFORM_MATRIX
386 } // anonymous namespace
388 #endif // wxUSE_DYNLIB_CLASS
390 // ===========================================================================
392 // ===========================================================================
394 // ----------------------------------------------------------------------------
395 // wxBrushAttrsSetter
396 // ----------------------------------------------------------------------------
398 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
399 : wxBkModeChanger(GetHdcOf(dc
)),
400 wxTextColoursChanger(GetHdcOf(dc
))
402 const wxBrush
& brush
= dc
.GetBrush();
403 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
405 // note that Windows convention is opposite to wxWidgets one, this is
406 // why text colour becomes the background one and vice versa
407 wxTextColoursChanger::Change(dc
.GetTextBackground(),
408 dc
.GetTextForeground());
410 wxBkModeChanger::Change(dc
.GetBackgroundMode());
414 // ----------------------------------------------------------------------------
415 // wxDC MSW-specific methods
416 // ----------------------------------------------------------------------------
418 WXHDC
wxDC::GetHDC() const
420 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
421 return impl
? impl
->GetHDC() : 0;
424 // ---------------------------------------------------------------------------
426 // ---------------------------------------------------------------------------
428 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
435 wxMSWDCImpl::~wxMSWDCImpl()
439 SelectOldObjects(m_hDC
);
441 // if we own the HDC, we delete it, otherwise we just release it
445 ::DeleteDC(GetHdc());
447 else // we don't own our HDC
451 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
455 // Must have been a wxScreenDC
456 ::ReleaseDC((HWND
) NULL
, GetHdc());
462 // This will select current objects out of the DC,
463 // which is what you have to do before deleting the
465 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
471 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
472 if (m_selectedBitmap
.IsOk())
474 m_selectedBitmap
.SetSelectedInto(NULL
);
480 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
485 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
490 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
497 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
500 #endif // wxUSE_PALETTE
503 m_brush
= wxNullBrush
;
506 m_palette
= wxNullPalette
;
507 #endif // wxUSE_PALETTE
509 m_backgroundBrush
= wxNullBrush
;
510 m_selectedBitmap
= wxNullBitmap
;
513 // ---------------------------------------------------------------------------
515 // ---------------------------------------------------------------------------
517 void wxMSWDCImpl::UpdateClipBox()
522 ::GetClipBox(GetHdc(), &rect
);
524 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
525 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
526 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
527 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
531 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
533 // check if we should try to retrieve the clipping region possibly not set
534 // by our SetClippingRegion() but preset by Windows:this can only happen
535 // when we're associated with an existing HDC usign SetHDC(), see there
536 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
538 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
539 self
->UpdateClipBox();
541 if ( !m_clipX1
&& !m_clipX2
)
542 self
->m_clipping
= false;
545 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
548 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
549 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
551 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
555 // note that we combine the new clipping region with the existing one: this
556 // is compatible with what the other ports do and is the documented
557 // behaviour now (starting with 2.3.3)
558 #if defined(__WXWINCE__)
560 if ( !::GetClipBox(GetHdc(), &rectClip
) )
563 // GetClipBox returns logical coordinates, so transform to device
564 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
565 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
566 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
567 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
569 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
570 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
571 rectClip
.right
, rectClip
.bottom
);
573 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
575 ::SelectClipRgn(GetHdc(), hrgnDest
);
578 ::DeleteObject(hrgnClipOld
);
579 ::DeleteObject(hrgnDest
);
581 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
583 wxLogLastError(wxT("ExtSelectClipRgn"));
587 #endif // WinCE/!WinCE
594 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
596 // the region coords are always the device ones, so do the translation
599 // FIXME: possible +/-1 error here, to check!
600 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
602 LogicalToDeviceX(x
+ w
),
603 LogicalToDeviceY(y
+ h
));
606 wxLogLastError(wxT("CreateRectRgn"));
610 SetClippingHrgn((WXHRGN
)hrgn
);
612 ::DeleteObject(hrgn
);
616 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
618 SetClippingHrgn(region
.GetHRGN());
621 void wxMSWDCImpl::DestroyClippingRegion()
625 if (m_clipping
&& m_hDC
)
628 // On a PocketPC device (not necessarily emulator), resetting
629 // the clip region as per the old method causes bad display
630 // problems. In fact setting a null region is probably OK
631 // on desktop WIN32 also, since the WIN32 docs imply that the user
632 // clipping region is independent from the paint clipping region.
633 ::SelectClipRgn(GetHdc(), 0);
635 // TODO: this should restore the previous clipping region,
636 // so that OnPaint processing works correctly, and the update
637 // clipping region doesn't get destroyed after the first
638 // DestroyClippingRegion.
639 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
640 ::SelectClipRgn(GetHdc(), rgn
);
645 wxDCImpl::DestroyClippingRegion();
648 // ---------------------------------------------------------------------------
649 // query capabilities
650 // ---------------------------------------------------------------------------
652 bool wxMSWDCImpl::CanDrawBitmap() const
657 bool wxMSWDCImpl::CanGetTextExtent() const
659 #ifdef __WXMICROWIN__
660 // TODO Extend MicroWindows' GetDeviceCaps function
663 // What sort of display is it?
664 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
666 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
670 int wxMSWDCImpl::GetDepth() const
672 WXMICROWIN_CHECK_HDC_RET(16)
674 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
677 // ---------------------------------------------------------------------------
679 // ---------------------------------------------------------------------------
681 void wxMSWDCImpl::Clear()
688 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
692 // No, I think we should simply ignore this if printing on e.g.
694 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
695 if (!m_selectedBitmap
.IsOk())
698 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
699 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
700 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
704 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
707 DWORD colour
= ::GetBkColor(GetHdc());
708 HBRUSH brush
= ::CreateSolidBrush(colour
);
709 ::FillRect(GetHdc(), &rect
, brush
);
710 ::DeleteObject(brush
);
712 RealizeScaleAndOrigin();
715 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
716 wxCoord
WXUNUSED_IN_WINCE(y
),
717 const wxColour
& WXUNUSED_IN_WINCE(col
),
718 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
723 WXMICROWIN_CHECK_HDC_RET(false)
725 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
727 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
728 : FLOODFILLBORDER
) ) ;
731 // quoting from the MSDN docs:
733 // Following are some of the reasons this function might fail:
735 // * The filling could not be completed.
736 // * The specified point has the boundary color specified by the
737 // crColor parameter (if FLOODFILLBORDER was requested).
738 // * The specified point does not have the color specified by
739 // crColor (if FLOODFILLSURFACE was requested)
740 // * The point is outside the clipping region that is, it is not
741 // visible on the device.
743 wxLogLastError(wxT("ExtFloodFill"));
746 CalcBoundingBox(x
, y
);
752 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
754 WXMICROWIN_CHECK_HDC_RET(false)
756 wxCHECK_MSG( col
, false, wxT("NULL colour parameter in wxMSWDCImpl::GetPixel") );
758 // get the color of the pixel
759 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
761 wxRGBToColour(*col
, pixelcolor
);
766 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
770 wxCoord x1
= x
-VIEWPORT_EXTENT
;
771 wxCoord y1
= y
-VIEWPORT_EXTENT
;
772 wxCoord x2
= x
+VIEWPORT_EXTENT
;
773 wxCoord y2
= y
+VIEWPORT_EXTENT
;
775 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
776 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
778 CalcBoundingBox(x1
, y1
);
779 CalcBoundingBox(x2
, y2
);
782 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
786 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
788 CalcBoundingBox(x1
, y1
);
789 CalcBoundingBox(x2
, y2
);
792 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
793 // and ending at (x2, y2)
794 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
795 wxCoord x2
, wxCoord y2
,
796 wxCoord xc
, wxCoord yc
)
800 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
804 // Slower emulation since WinCE doesn't support Pie and Arc
805 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
807 sa
= -sa
; // below center
808 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
809 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
814 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
816 // treat the special case of full circle separately
817 if ( x1
== x2
&& y1
== y2
)
819 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
823 wxCoord xx1
= XLOG2DEV(x1
);
824 wxCoord yy1
= YLOG2DEV(y1
);
825 wxCoord xx2
= XLOG2DEV(x2
);
826 wxCoord yy2
= YLOG2DEV(y2
);
827 wxCoord xxc
= XLOG2DEV(xc
);
828 wxCoord yyc
= YLOG2DEV(yc
);
831 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
833 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
834 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
835 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
836 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
838 if ( m_brush
.IsNonTransparent() )
840 // Have to add 1 to bottom-right corner of rectangle
841 // to make semi-circles look right (crooked line otherwise).
842 // Unfortunately this is not a reliable method, depends
843 // on the size of shape.
844 // TODO: figure out why this happens!
845 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
849 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
852 CalcBoundingBox(xc
- r
, yc
- r
);
853 CalcBoundingBox(xc
+ r
, yc
+ r
);
857 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
858 wxCoord width
, wxCoord height
)
860 // cases when we don't have DrawFrameControl()
861 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
862 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
864 wxCoord x2
= x1
+ width
,
874 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
876 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
879 CalcBoundingBox(x1
, y1
);
880 CalcBoundingBox(x2
, y2
);
881 #endif // Microwin/Normal
884 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
888 COLORREF color
= 0x00ffffff;
891 color
= m_pen
.GetColour().GetPixel();
894 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
896 CalcBoundingBox(x
, y
);
899 void wxMSWDCImpl::DoDrawPolygon(int n
,
900 const wxPoint points
[],
903 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
907 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
909 // Do things less efficiently if we have offsets
910 if (xoffset
!= 0 || yoffset
!= 0)
912 POINT
*cpoints
= new POINT
[n
];
914 for (i
= 0; i
< n
; i
++)
916 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
917 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
919 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
922 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
924 (void)Polygon(GetHdc(), cpoints
, n
);
926 SetPolyFillMode(GetHdc(),prev
);
933 for (i
= 0; i
< n
; i
++)
934 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
937 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
939 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
941 SetPolyFillMode(GetHdc(),prev
);
947 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
949 const wxPoint points
[],
952 wxPolygonFillMode fillStyle
)
955 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
959 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
961 for (i
= cnt
= 0; i
< n
; i
++)
964 // Do things less efficiently if we have offsets
965 if (xoffset
!= 0 || yoffset
!= 0)
967 POINT
*cpoints
= new POINT
[cnt
];
968 for (i
= 0; i
< cnt
; i
++)
970 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
971 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
973 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
976 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
978 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
980 SetPolyFillMode(GetHdc(),prev
);
986 for (i
= 0; i
< cnt
; i
++)
987 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
990 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
992 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
994 SetPolyFillMode(GetHdc(),prev
);
1001 void wxMSWDCImpl::DoDrawLines(int n
, const wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
1003 WXMICROWIN_CHECK_HDC
1005 // Do things less efficiently if we have offsets
1006 if (xoffset
!= 0 || yoffset
!= 0)
1008 POINT
*cpoints
= new POINT
[n
];
1010 for (i
= 0; i
< n
; i
++)
1012 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
1013 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
1015 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
1017 (void)Polyline(GetHdc(), cpoints
, n
);
1023 for (i
= 0; i
< n
; i
++)
1024 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
1026 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
1030 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1032 WXMICROWIN_CHECK_HDC
1034 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1036 wxCoord x2
= x
+ width
;
1037 wxCoord y2
= y
+ height
;
1039 wxCoord x2dev
= XLOG2DEV(x2
),
1040 y2dev
= YLOG2DEV(y2
);
1042 // Windows (but not Windows CE) draws the filled rectangles without outline
1043 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1044 // and we want them to have the same size regardless of which pen is used
1046 if ( m_pen
.IsTransparent() )
1051 #endif // !__WXWINCE__
1053 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
1055 CalcBoundingBox(x
, y
);
1056 CalcBoundingBox(x2
, y2
);
1059 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
1061 WXMICROWIN_CHECK_HDC
1063 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1065 // Now, a negative radius value is interpreted to mean
1066 // 'the proportion of the smallest X or Y dimension'
1070 double smallest
= (width
< height
) ? width
: height
;
1071 radius
= (- radius
* smallest
);
1074 wxCoord x2
= (x
+width
);
1075 wxCoord y2
= (y
+height
);
1077 // Windows draws the filled rectangles without outline (i.e. drawn with a
1078 // transparent pen) one pixel smaller in both directions and we want them
1079 // to have the same size regardless of which pen is used - adjust
1080 if ( m_pen
.IsTransparent() )
1086 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1087 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1089 CalcBoundingBox(x
, y
);
1090 CalcBoundingBox(x2
, y2
);
1093 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1095 WXMICROWIN_CHECK_HDC
1097 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1099 // +1 below makes the ellipse more similar to other platforms.
1100 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1101 wxCoord x2
= x
+ width
+ 1;
1102 wxCoord y2
= y
+ height
+ 1;
1104 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1105 // pen doesn't draw anything. Should we provide a workaround?
1107 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1109 CalcBoundingBox(x
, y
);
1110 CalcBoundingBox(x2
, y2
);
1113 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1114 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1116 // quadratic b-spline to cubic bezier spline conversion
1118 // quadratic spline with control points P0,P1,P2
1119 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1121 // bezier spline with control points B0,B1,B2,B3
1122 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1124 // control points of bezier spline calculated from b-spline
1126 // B1 = (2*P1 + P0)/3
1127 // B2 = (2*P1 + P2)/3
1130 WXMICROWIN_CHECK_HDC
1132 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1134 const size_t n_points
= points
->GetCount();
1135 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1137 const size_t n_bezier_points
= n_points
* 3 + 1;
1138 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1139 size_t bezier_pos
= 0;
1140 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1142 wxPointList::compatibility_iterator node
= points
->GetFirst();
1143 wxPoint
*p
= node
->GetData();
1144 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1145 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1147 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1150 node
= node
->GetNext();
1151 p
= node
->GetData();
1155 cx1
= ( x1
+ x2
) / 2;
1156 cy1
= ( y1
+ y2
) / 2;
1157 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1158 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1160 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1163 #if !wxUSE_STD_CONTAINERS
1164 while ((node
= node
->GetNext()) != NULL
)
1166 while ((node
= node
->GetNext()))
1167 #endif // !wxUSE_STD_CONTAINERS
1169 p
= (wxPoint
*)node
->GetData();
1174 cx4
= (x1
+ x2
) / 2;
1175 cy4
= (y1
+ y2
) / 2;
1176 // B0 is B3 of previous segment
1178 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1179 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1182 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1183 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1186 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1187 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1193 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1195 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1196 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1198 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1201 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1205 #endif // wxUSE_SPLINES
1207 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1208 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1211 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1214 WXMICROWIN_CHECK_HDC
1216 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1221 int rx1
= XLOG2DEV(x
+w
/2);
1222 int ry1
= YLOG2DEV(y
+h
/2);
1229 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1230 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1231 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1232 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1234 // Swap start and end positions if the end angle is less than the start angle.
1245 // draw pie with NULL_PEN first and then outline otherwise a line is
1246 // drawn from the start and end points to the centre
1247 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1250 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1251 rx1
, ry1
, rx2
, ry2
);
1255 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1256 rx1
, ry1
-1, rx2
, ry2
-1);
1259 ::SelectObject(GetHdc(), hpenOld
);
1261 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1262 rx1
, ry1
, rx2
, ry2
);
1264 CalcBoundingBox(x
, y
);
1265 CalcBoundingBox(x2
, y2
);
1269 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1271 WXMICROWIN_CHECK_HDC
1273 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1276 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1278 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1281 CalcBoundingBox(x
, y
);
1282 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1285 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1287 WXMICROWIN_CHECK_HDC
1289 wxCHECK_RET( bmp
.IsOk(), wxT("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1291 int width
= bmp
.GetWidth(),
1292 height
= bmp
.GetHeight();
1294 HBITMAP hbmpMask
= 0;
1297 HPALETTE oldPal
= 0;
1298 #endif // wxUSE_PALETTE
1300 if ( bmp
.HasAlpha() )
1303 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1305 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1309 SET_STRETCH_BLT_MODE(GetHdc());
1313 wxMask
*mask
= bmp
.GetMask();
1315 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1319 // don't give assert here because this would break existing
1320 // programs - just silently ignore useMask parameter
1327 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1331 #if wxUSE_SYSTEM_OPTIONS
1332 // On some systems, MaskBlt succeeds yet is much much slower
1333 // than the wxWidgets fall-back implementation. So we need
1334 // to be able to switch this on and off at runtime.
1336 // NB: don't query the value of the option every time but do it only
1337 // once as otherwise it can have real (and bad) performance
1338 // implications (see #11172)
1340 s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
1341 if ( s_maskBltAllowed
)
1342 #endif // wxUSE_SYSTEM_OPTIONS
1345 HDC hdcMem
= wxMSW::CreateCompatibleDCWithLayout(cdc
);
1346 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1348 wxPalette
*pal
= bmp
.GetPalette();
1349 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1351 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1352 ::RealizePalette(hdcMem
);
1354 #endif // wxUSE_PALETTE
1356 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1359 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1363 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1364 #endif // wxUSE_PALETTE
1366 ::SelectObject(hdcMem
, hOldBitmap
);
1373 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1377 memDC
.SetLayoutDirection(GetLayoutDirection());
1378 memDC
.SelectObjectAsSource(bmp
);
1380 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1383 else // no mask, just use BitBlt()
1386 HDC memdc
= wxMSW::CreateCompatibleDCWithLayout( cdc
);
1387 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1389 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1391 wxTextColoursChanger
textCol(GetHdc(), *this);
1394 wxPalette
*pal
= bmp
.GetPalette();
1395 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1397 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1398 ::RealizePalette(memdc
);
1400 #endif // wxUSE_PALETTE
1402 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1403 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1407 ::SelectPalette(memdc
, oldPal
, FALSE
);
1408 #endif // wxUSE_PALETTE
1410 ::SelectObject( memdc
, hOldBitmap
);
1411 ::DeleteDC( memdc
);
1415 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1417 // For compatibility with other ports (notably wxGTK) and because it's
1418 // genuinely useful, we allow passing multiline strings to DrawText().
1419 // However there is no native MSW function to draw them directly so we
1420 // instead reuse the generic DrawLabel() method to render them. Of course,
1421 // DrawLabel() itself will call back to us but with single line strings
1422 // only so there won't be any infinite recursion here.
1423 if ( text
.find('\n') != wxString::npos
)
1425 GetOwner()->DrawLabel(text
, wxRect(x
, y
, 0, 0));
1429 WXMICROWIN_CHECK_HDC
1431 DrawAnyText(text
, x
, y
);
1433 // update the bounding box
1434 CalcBoundingBox(x
, y
);
1437 GetOwner()->GetTextExtent(text
, &w
, &h
);
1438 CalcBoundingBox(x
+ w
, y
+ h
);
1441 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1443 WXMICROWIN_CHECK_HDC
1445 // prepare for drawing the text
1446 wxTextColoursChanger
textCol(GetHdc(), *this);
1448 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1450 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1451 text
.c_str(), text
.length(), NULL
) == 0 )
1453 wxLogLastError(wxT("TextOut"));
1457 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1458 wxCoord x
, wxCoord y
,
1461 WXMICROWIN_CHECK_HDC
1463 // we test that we have some font because otherwise we should still use the
1464 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1465 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1466 // font for drawing rotated fonts unfortunately)
1467 if ( (angle
== 0.0) && m_font
.IsOk() )
1469 DoDrawText(text
, x
, y
);
1471 #ifndef __WXMICROWIN__
1474 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1475 // because it's not TrueType and so can't have non zero
1476 // orientation/escapement under Win9x
1477 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1478 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1480 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1482 wxLogLastError(wxT("GetObject(hfont)"));
1485 // GDI wants the angle in tenth of degree
1486 long angle10
= (long)(angle
* 10);
1487 lf
.lfEscapement
= angle10
;
1488 lf
. lfOrientation
= angle10
;
1490 hfont
= ::CreateFontIndirect(&lf
);
1493 wxLogLastError(wxT("CreateFont"));
1497 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1499 DrawAnyText(text
, x
, y
);
1501 (void)::SelectObject(GetHdc(), hfontOld
);
1502 (void)::DeleteObject(hfont
);
1505 // call the bounding box by adding all four vertices of the rectangle
1506 // containing the text to it (simpler and probably not slower than
1507 // determining which of them is really topmost/leftmost/...)
1509 GetOwner()->GetTextExtent(text
, &w
, &h
);
1511 double rad
= DegToRad(angle
);
1513 // "upper left" and "upper right"
1514 CalcBoundingBox(x
, y
);
1515 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1517 // "bottom left" and "bottom right"
1518 x
+= (wxCoord
)(h
*sin(rad
));
1519 y
+= (wxCoord
)(h
*cos(rad
));
1520 CalcBoundingBox(x
, y
);
1521 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1526 // ---------------------------------------------------------------------------
1528 // ---------------------------------------------------------------------------
1532 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1534 WXMICROWIN_CHECK_HDC
1536 // Set the old object temporarily, in case the assignment deletes an object
1537 // that's not yet selected out.
1540 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1544 if ( m_palette
.IsOk() )
1546 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1547 GetHpaletteOf(m_palette
),
1550 m_oldPalette
= (WXHPALETTE
) oldPal
;
1553 ::RealizePalette(GetHdc());
1557 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1559 if ( palette
.IsOk() )
1561 m_palette
= palette
;
1562 DoSelectPalette(true);
1566 void wxMSWDCImpl::InitializePalette()
1568 if ( wxDisplayDepth() <= 8 )
1570 // look for any window or parent that has a custom palette. If any has
1571 // one then we need to use it in drawing operations
1572 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1574 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1575 if ( m_hasCustomPalette
)
1577 m_palette
= win
->GetPalette();
1579 // turn on MSW translation for this palette
1585 #endif // wxUSE_PALETTE
1587 // SetFont/Pen/Brush() really ask to be implemented as a single template
1588 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1590 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1592 WXMICROWIN_CHECK_HDC
1594 if ( font
== m_font
)
1599 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1600 if ( hfont
== HGDI_ERROR
)
1602 wxLogLastError(wxT("SelectObject(font)"));
1607 m_oldFont
= (WXHFONT
)hfont
;
1612 else // invalid font, reset the current font
1616 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1618 wxLogLastError(wxT("SelectObject(old font)"));
1624 m_font
= wxNullFont
;
1628 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1630 WXMICROWIN_CHECK_HDC
1637 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1638 if ( hpen
== HGDI_ERROR
)
1640 wxLogLastError(wxT("SelectObject(pen)"));
1645 m_oldPen
= (WXHPEN
)hpen
;
1650 else // invalid pen, reset the current pen
1654 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1656 wxLogLastError(wxT("SelectObject(old pen)"));
1666 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1668 WXMICROWIN_CHECK_HDC
1670 if ( brush
== m_brush
)
1675 // we must make sure the brush is aligned with the logical coordinates
1676 // before selecting it or using the same brush for the background of
1677 // different windows would result in discontinuities
1678 wxSize sizeBrushBitmap
= wxDefaultSize
;
1679 wxBitmap
*stipple
= brush
.GetStipple();
1680 if ( stipple
&& stipple
->IsOk() )
1681 sizeBrushBitmap
= stipple
->GetSize();
1682 else if ( brush
.IsHatch() )
1683 sizeBrushBitmap
= wxSize(8, 8);
1685 if ( sizeBrushBitmap
.IsFullySpecified() )
1687 if ( !::SetBrushOrgEx
1690 m_deviceOriginX
% sizeBrushBitmap
.x
,
1691 m_deviceOriginY
% sizeBrushBitmap
.y
,
1692 NULL
// [out] previous brush origin
1695 wxLogLastError(wxT("SetBrushOrgEx()"));
1699 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1700 if ( hbrush
== HGDI_ERROR
)
1702 wxLogLastError(wxT("SelectObject(brush)"));
1707 m_oldBrush
= (WXHBRUSH
)hbrush
;
1712 else // invalid brush, reset the current brush
1716 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1718 wxLogLastError(wxT("SelectObject(old brush)"));
1724 m_brush
= wxNullBrush
;
1728 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1730 WXMICROWIN_CHECK_HDC
1732 m_backgroundBrush
= brush
;
1734 if ( m_backgroundBrush
.IsOk() )
1736 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1740 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1742 WXMICROWIN_CHECK_HDC
1744 m_backgroundMode
= mode
;
1746 // SetBackgroundColour now only refers to text background
1747 // and m_backgroundMode is used there
1750 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1752 WXMICROWIN_CHECK_HDC
1754 m_logicalFunction
= function
;
1759 void wxMSWDCImpl::SetRop(WXHDC dc
)
1761 if ( !dc
|| m_logicalFunction
< 0 )
1766 switch (m_logicalFunction
)
1768 case wxCLEAR
: rop
= R2_BLACK
; break;
1769 case wxXOR
: rop
= R2_XORPEN
; break;
1770 case wxINVERT
: rop
= R2_NOT
; break;
1771 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1772 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1773 case wxCOPY
: rop
= R2_COPYPEN
; break;
1774 case wxAND
: rop
= R2_MASKPEN
; break;
1775 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1776 case wxNO_OP
: rop
= R2_NOP
; break;
1777 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1778 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1779 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1780 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1781 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1782 case wxOR
: rop
= R2_MERGEPEN
; break;
1783 case wxSET
: rop
= R2_WHITE
; break;
1785 wxFAIL_MSG( wxS("unknown logical function") );
1789 SetROP2(GetHdc(), rop
);
1792 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1794 // We might be previewing, so return true to let it continue.
1798 void wxMSWDCImpl::EndDoc()
1802 void wxMSWDCImpl::StartPage()
1806 void wxMSWDCImpl::EndPage()
1810 // ---------------------------------------------------------------------------
1812 // ---------------------------------------------------------------------------
1814 wxCoord
wxMSWDCImpl::GetCharHeight() const
1816 WXMICROWIN_CHECK_HDC_RET(0)
1818 TEXTMETRIC lpTextMetric
;
1820 GetTextMetrics(GetHdc(), &lpTextMetric
);
1822 return lpTextMetric
.tmHeight
;
1825 wxCoord
wxMSWDCImpl::GetCharWidth() const
1827 WXMICROWIN_CHECK_HDC_RET(0)
1829 TEXTMETRIC lpTextMetric
;
1831 GetTextMetrics(GetHdc(), &lpTextMetric
);
1833 return lpTextMetric
.tmAveCharWidth
;
1836 void wxMSWDCImpl::DoGetFontMetrics(int *height
,
1839 int *internalLeading
,
1840 int *externalLeading
,
1841 int *averageWidth
) const
1845 GetTextMetrics(GetHdc(), &tm
);
1848 *height
= tm
.tmHeight
;
1850 *ascent
= tm
.tmAscent
;
1852 *descent
= tm
.tmDescent
;
1853 if ( internalLeading
)
1854 *internalLeading
= tm
.tmInternalLeading
;
1855 if ( externalLeading
)
1856 *externalLeading
= tm
.tmExternalLeading
;
1858 *averageWidth
= tm
.tmAveCharWidth
;
1861 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1862 wxCoord
*descent
, wxCoord
*externalLeading
,
1863 const wxFont
*font
) const
1865 #ifdef __WXMICROWIN__
1870 if (descent
) *descent
= 0;
1871 if (externalLeading
) *externalLeading
= 0;
1874 #endif // __WXMICROWIN__
1876 wxASSERT_MSG( !font
|| font
->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") );
1878 wxTextMeasure
txm(GetOwner(), font
);
1879 txm
.GetTextExtent(string
, x
, y
, descent
, externalLeading
);
1883 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1885 wxTextMeasure
txm(GetOwner(), NULL
); // don't change the font
1886 return txm
.GetPartialTextExtents(text
, widths
, 1.0);
1892 void ApplyEffectiveScale(double scale
, int sign
, int *device
, int *logical
)
1894 // To reduce rounding errors as much as possible, we try to use the largest
1895 // possible extent (2^27-1) for the device space but we must also avoid
1896 // overflowing the int range i.e. ensure that logical extents are less than
1897 // 2^31 in magnitude. So the minimal scale we can use is 1/16 as for
1898 // anything smaller VIEWPORT_EXTENT/scale would overflow the int range.
1899 static const double MIN_LOGICAL_SCALE
= 1./16;
1901 double physExtent
= VIEWPORT_EXTENT
;
1902 if ( scale
< MIN_LOGICAL_SCALE
)
1904 physExtent
*= scale
/MIN_LOGICAL_SCALE
;
1905 scale
= MIN_LOGICAL_SCALE
;
1908 *device
= wxRound(physExtent
);
1909 *logical
= sign
*wxRound(VIEWPORT_EXTENT
/scale
);
1912 } // anonymous namespace
1914 void wxMSWDCImpl::RealizeScaleAndOrigin()
1916 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1917 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1918 // noticeable difference between these mapping modes
1920 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1922 // wxWidgets API assumes that the coordinate space is "infinite" (i.e. only
1923 // limited by 2^32 range of the integer coordinates) but in MSW API we must
1924 // actually specify the extents that we use so compute them here.
1926 int devExtX
, devExtY
, // Viewport, i.e. device space, extents.
1927 logExtX
, logExtY
; // Window, i.e. logical coordinate space, extents.
1929 ApplyEffectiveScale(m_scaleX
, m_signX
, &devExtX
, &logExtX
);
1930 ApplyEffectiveScale(m_scaleY
, m_signY
, &devExtY
, &logExtY
);
1932 ::SetViewportExtEx(GetHdc(), devExtX
, devExtY
, NULL
);
1933 ::SetWindowExtEx(GetHdc(), logExtX
, logExtY
, NULL
);
1935 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1936 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1940 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1942 WXMICROWIN_CHECK_HDC
1944 m_mappingMode
= mode
;
1946 if ( mode
== wxMM_TEXT
)
1949 m_logicalScaleY
= 1.0;
1951 else // need to do some calculations
1953 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1954 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1955 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1956 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1958 if ( (mm_width
== 0) || (mm_height
== 0) )
1960 // we can't calculate mm2pixels[XY] then!
1964 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1965 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1970 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1971 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1975 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1976 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1980 m_logicalScaleX
= mm2pixelsX
;
1981 m_logicalScaleY
= mm2pixelsY
;
1985 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1986 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1990 wxFAIL_MSG( wxT("unknown mapping mode in SetMapMode") );
1994 ComputeScaleAndOrigin();
1996 RealizeScaleAndOrigin();
1999 void wxMSWDCImpl::SetUserScale(double x
, double y
)
2001 WXMICROWIN_CHECK_HDC
2003 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
2006 wxDCImpl::SetUserScale(x
,y
);
2008 RealizeScaleAndOrigin();
2011 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2014 WXMICROWIN_CHECK_HDC
2016 int signX
= xLeftRight
? 1 : -1,
2017 signY
= yBottomUp
? -1 : 1;
2019 if (signX
== m_signX
&& signY
== m_signY
)
2022 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2024 RealizeScaleAndOrigin();
2027 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2029 WXMICROWIN_CHECK_HDC
2031 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2034 wxDCImpl::SetLogicalOrigin( x
, y
);
2036 RealizeScaleAndOrigin();
2039 // For use by wxWidgets only, unless custom units are required.
2040 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2042 WXMICROWIN_CHECK_HDC
2044 wxDCImpl::SetLogicalScale(x
,y
);
2047 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2049 WXMICROWIN_CHECK_HDC
2051 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2054 wxDCImpl::SetDeviceOrigin( x
, y
);
2056 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2059 // ----------------------------------------------------------------------------
2061 // ----------------------------------------------------------------------------
2063 #if wxUSE_DC_TRANSFORM_MATRIX
2065 bool wxMSWDCImpl::CanUseTransformMatrix() const
2067 return GdiWorldTransformFuncs::IsOk();
2070 bool wxMSWDCImpl::SetTransformMatrix(const wxAffineMatrix2D
&matrix
)
2072 if ( !GdiWorldTransformFuncs::IsOk() )
2075 if ( matrix
.IsIdentity() )
2077 ResetTransformMatrix();
2081 if ( !GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_ADVANCED
) )
2083 wxLogLastError(wxT("SetGraphicsMode"));
2089 matrix
.Get(&mat
, &tr
);
2092 xform
.eM11
= mat
.m_11
;
2093 xform
.eM12
= mat
.m_12
;
2094 xform
.eM21
= mat
.m_21
;
2095 xform
.eM22
= mat
.m_22
;
2099 if ( !GdiWorldTransformFuncs::SetWorldTransform()(GetHdc(), &xform
) )
2101 wxLogLastError(wxT("SetWorldTransform"));
2108 wxAffineMatrix2D
wxMSWDCImpl::GetTransformMatrix() const
2110 wxAffineMatrix2D transform
;
2112 if ( !GdiWorldTransformFuncs::IsOk() )
2116 if ( !GdiWorldTransformFuncs::GetWorldTransform()(GetHdc(), &xform
) )
2118 wxLogLastError(wxT("GetWorldTransform"));
2122 wxMatrix2D
m(xform
.eM11
, xform
.eM12
, xform
.eM21
, xform
.eM22
);
2123 wxPoint2DDouble
p(xform
.eDx
, xform
.eDy
);
2124 transform
.Set(m
, p
);
2129 void wxMSWDCImpl::ResetTransformMatrix()
2131 if ( GdiWorldTransformFuncs::IsOk() )
2133 GdiWorldTransformFuncs::ModifyWorldTransform()(GetHdc(), NULL
, MWT_IDENTITY
);
2134 GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_COMPATIBLE
);
2138 #endif // wxUSE_DC_TRANSFORM_MATRIX
2140 // ---------------------------------------------------------------------------
2142 // ---------------------------------------------------------------------------
2144 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2145 wxCoord dstWidth
, wxCoord dstHeight
,
2147 wxCoord srcX
, wxCoord srcY
,
2148 wxRasterOperationMode rop
, bool useMask
,
2149 wxCoord srcMaskX
, wxCoord srcMaskY
)
2151 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2154 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2155 wxCoord dstWidth
, wxCoord dstHeight
,
2157 wxCoord xsrc
, wxCoord ysrc
,
2158 wxCoord srcWidth
, wxCoord srcHeight
,
2159 wxRasterOperationMode rop
, bool useMask
,
2160 wxCoord xsrcMask
, wxCoord ysrcMask
)
2162 wxCHECK_MSG( source
, false, wxT("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2164 WXMICROWIN_CHECK_HDC_RET(false)
2166 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2169 // TODO: Do we want to be able to blit from other DCs too?
2173 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2175 // if either the source or destination has alpha channel, we must use
2176 // AlphaBlt() as other function don't handle it correctly
2177 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2178 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2179 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2181 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2182 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2186 wxMask
*mask
= NULL
;
2189 mask
= bmpSrc
.GetMask();
2191 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2193 // don't give assert here because this would break existing
2194 // programs - just silently ignore useMask parameter
2199 if (xsrcMask
== -1 && ysrcMask
== -1)
2201 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2204 wxTextColoursChanger
textCol(GetHdc(), *this);
2209 case wxXOR
: dwRop
= SRCINVERT
; break;
2210 case wxINVERT
: dwRop
= DSTINVERT
; break;
2211 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2212 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2213 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2214 case wxSET
: dwRop
= WHITENESS
; break;
2215 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2216 case wxAND
: dwRop
= SRCAND
; break;
2217 case wxOR
: dwRop
= SRCPAINT
; break;
2218 case wxEQUIV
: dwRop
= 0x00990066; break;
2219 case wxNAND
: dwRop
= 0x007700E6; break;
2220 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2221 case wxCOPY
: dwRop
= SRCCOPY
; break;
2222 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2223 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2224 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2226 wxFAIL_MSG( wxT("unsupported logical function") );
2230 bool success
= false;
2235 // we want the part of the image corresponding to the mask to be
2236 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2237 // meaning of fg and bg is inverted which corresponds to wxWin notion
2238 // of the mask which is also contrary to the Windows one)
2240 // On some systems, MaskBlt succeeds yet is much much slower
2241 // than the wxWidgets fall-back implementation. So we need
2242 // to be able to switch this on and off at runtime.
2243 #if wxUSE_SYSTEM_OPTIONS
2244 static bool s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
2245 if ( s_maskBltAllowed
)
2248 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2253 xdest
, ydest
, dstWidth
, dstHeight
,
2256 (HBITMAP
)mask
->GetMaskBitmap(),
2258 MAKEROP4(dwRop
, DSTCOPY
)
2266 // Blit bitmap with mask
2269 HBITMAP buffer_bmap
;
2271 #if wxUSE_DC_CACHEING
2272 // create a temp buffer bitmap and DCs to access it and the mask
2273 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2274 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2276 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2277 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2279 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2280 dstWidth
, dstHeight
);
2282 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2283 #else // !wxUSE_DC_CACHEING
2284 // create a temp buffer bitmap and DCs to access it and the mask
2285 dc_mask
= wxMSW::CreateCompatibleDCWithLayout(hdcSrc
);
2286 dc_buffer
= wxMSW::CreateCompatibleDCWithLayout(GetHdc());
2287 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2288 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2289 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2290 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2292 // copy dest to buffer
2293 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2294 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2296 wxLogLastError(wxT("BitBlt"));
2299 SET_STRETCH_BLT_MODE(GetHdc());
2301 // copy src to buffer using selected raster op
2302 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2303 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2305 wxLogLastError(wxT("StretchBlt"));
2308 // set masked area in buffer to BLACK
2310 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2311 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2312 dc_mask
, xsrcMask
, ysrcMask
,
2313 srcWidth
, srcHeight
, SRCAND
) )
2315 wxLogLastError(wxT("StretchBlt"));
2318 // set unmasked area in dest to BLACK
2319 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2320 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2321 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2322 dc_mask
, xsrcMask
, ysrcMask
,
2323 srcWidth
, srcHeight
, SRCAND
) )
2325 wxLogLastError(wxT("StretchBlt"));
2327 } // restore the original text and background colours
2329 // OR buffer to dest
2330 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2331 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2334 wxLogLastError(wxT("BitBlt"));
2337 // tidy up temporary DCs and bitmap
2338 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2339 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2341 #if !wxUSE_DC_CACHEING
2343 ::DeleteDC(dc_mask
);
2344 ::DeleteDC(dc_buffer
);
2345 ::DeleteObject(buffer_bmap
);
2350 else // no mask, just BitBlt() it
2352 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2353 // use StretchBlt() if available and finally fall back to BitBlt()
2355 // FIXME: use appropriate WinCE functions
2357 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2358 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2363 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2365 &ds
) == sizeof(ds
) )
2367 SET_STRETCH_BLT_MODE(GetHdc());
2369 // Unlike all the other functions used here (i.e. AlphaBlt(),
2370 // MaskBlt(), BitBlt() and StretchBlt()), StretchDIBits() does
2371 // not take into account the source DC logical coordinates
2372 // automatically as it doesn't even work with the source HDC.
2373 // So do this manually to ensure that the coordinates are
2374 // interpreted in the same way here as in all the other cases.
2375 xsrc
= source
->LogicalToDeviceX(xsrc
);
2376 ysrc
= source
->LogicalToDeviceY(ysrc
);
2377 srcWidth
= source
->LogicalToDeviceXRel(srcWidth
);
2378 srcHeight
= source
->LogicalToDeviceYRel(srcHeight
);
2380 // Figure out what co-ordinate system we're supposed to specify
2382 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2386 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2389 if ( ::StretchDIBits(GetHdc(),
2391 dstWidth
, dstHeight
,
2393 srcWidth
, srcHeight
,
2395 (LPBITMAPINFO
)&ds
.dsBmih
,
2398 ) == (int)GDI_ERROR
)
2400 // On Win9x this API fails most (all?) of the time, so
2401 // logging it becomes quite distracting. Since it falls
2402 // back to the code below this is not really serious, so
2404 //wxLogLastError(wxT("StretchDIBits"));
2413 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2417 SET_STRETCH_BLT_MODE(GetHdc());
2422 xdest
, ydest
, dstWidth
, dstHeight
,
2424 xsrc
, ysrc
, srcWidth
, srcHeight
,
2428 wxLogLastError(wxT("StretchBlt"));
2438 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2439 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2441 wxLogLastError(wxT("BitBlt"));
2453 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2455 WXMICROWIN_CHECK_HDC
2458 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2460 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2463 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2465 WXMICROWIN_CHECK_HDC
2467 // if we implement it in terms of DoGetSize() instead of directly using the
2468 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2469 // will also work for wxWindowDC and wxClientDC even though their size is
2470 // not the same as the total size of the screen
2471 int wPixels
, hPixels
;
2472 DoGetSize(&wPixels
, &hPixels
);
2476 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2478 wxCHECK_RET( wTotal
, wxT("0 width device?") );
2480 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2485 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2487 wxCHECK_RET( hTotal
, wxT("0 height device?") );
2489 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2493 wxSize
wxMSWDCImpl::GetPPI() const
2495 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2497 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2498 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2500 return wxSize(x
, y
);
2503 // ----------------------------------------------------------------------------
2505 // ----------------------------------------------------------------------------
2507 #if wxUSE_DC_CACHEING
2510 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2511 * improve it in due course, either using arrays, or simply storing pointers to one
2512 * entry for the bitmap, and two for the DCs. -- JACS
2515 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2516 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2518 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2527 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2536 wxDCCacheEntry::~wxDCCacheEntry()
2539 ::DeleteObject((HBITMAP
) m_bitmap
);
2541 ::DeleteDC((HDC
) m_dc
);
2544 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2546 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2547 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2550 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2552 if (entry
->m_depth
== depth
)
2554 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2556 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2557 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2558 if ( !entry
->m_bitmap
)
2560 wxLogLastError(wxT("CreateCompatibleBitmap"));
2562 entry
->m_width
= w
; entry
->m_height
= h
;
2568 node
= node
->GetNext();
2570 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2573 wxLogLastError(wxT("CreateCompatibleBitmap"));
2575 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2576 AddToBitmapCache(entry
);
2580 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2582 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2583 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2586 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2588 // Don't return the same one as we already have
2589 if (!notThis
|| (notThis
!= entry
))
2591 if (entry
->m_depth
== depth
)
2597 node
= node
->GetNext();
2599 WXHDC hDC
= (WXHDC
) wxMSW::CreateCompatibleDCWithLayout((HDC
) dc
);
2602 wxLogLastError(wxT("CreateCompatibleDC"));
2604 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2605 AddToDCCache(entry
);
2609 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2611 sm_bitmapCache
.Append(entry
);
2614 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2616 sm_dcCache
.Append(entry
);
2619 void wxMSWDCImpl::ClearCache()
2621 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2622 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2625 // Clean up cache at app exit
2626 class wxDCModule
: public wxModule
2629 virtual bool OnInit() { return true; }
2630 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2633 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2636 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2638 #endif // wxUSE_DC_CACHEING
2640 // ----------------------------------------------------------------------------
2641 // alpha channel support
2642 // ----------------------------------------------------------------------------
2644 static bool AlphaBlt(HDC hdcDst
,
2645 int x
, int y
, int dstWidth
, int dstHeight
,
2647 int srcWidth
, int srcHeight
,
2649 const wxBitmap
& bmp
)
2651 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2652 wxASSERT_MSG( hdcDst
&& hdcSrc
, wxT("AlphaBlt(): invalid HDC") );
2654 // do we have AlphaBlend() and company in the headers?
2655 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2656 // yes, now try to see if we have it during run-time
2657 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2658 HDC
,int,int,int,int,
2662 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(wxT("AlphaBlend"));
2663 if ( pfnAlphaBlend
)
2666 bf
.BlendOp
= AC_SRC_OVER
;
2668 bf
.SourceConstantAlpha
= 0xff;
2669 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2671 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2672 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2675 // skip wxAlphaBlend() call below
2679 wxLogLastError(wxT("AlphaBlend"));
2682 wxUnusedVar(hdcSrc
);
2683 #endif // defined(AC_SRC_OVER)
2685 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2687 #ifdef wxHAS_RAW_BITMAP
2688 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2691 #else // !wxHAS_RAW_BITMAP
2692 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2693 // alpha but at least something will be shown like this)
2696 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2700 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2701 #ifdef wxHAS_RAW_BITMAP
2704 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2705 int dstWidth
, int dstHeight
,
2707 int srcWidth
, int srcHeight
,
2708 const wxBitmap
& bmpSrc
)
2710 // get the destination DC pixels
2711 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2713 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2715 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2717 wxLogLastError(wxT("BitBlt"));
2720 // combine them with the source bitmap using alpha
2721 wxAlphaPixelData
dataDst(bmpDst
),
2722 dataSrc((wxBitmap
&)bmpSrc
);
2724 wxCHECK_RET( dataDst
&& dataSrc
,
2725 wxT("failed to get raw data in wxAlphaBlend") );
2727 wxAlphaPixelData::Iterator
pDst(dataDst
),
2731 for ( int y
= 0; y
< dstHeight
; y
++ )
2733 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2735 for ( int x
= 0; x
< dstWidth
; x
++ )
2737 // source is point sampled, Alpha StretchBlit is ugly on Win95
2738 // (but does not impact performance)
2739 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2741 // note that source bitmap uses premultiplied alpha (as required by
2742 // the real AlphaBlend)
2743 const unsigned beta
= 255 - pSrc
.Alpha();
2745 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2746 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2747 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2752 pDst
= pDstRowStart
;
2753 pDst
.OffsetY(dataDst
, 1);
2756 // and finally blit them back to the destination DC
2757 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2759 wxLogLastError(wxT("BitBlt"));
2763 #endif // wxHAS_RAW_BITMAP
2765 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2766 const wxColour
& initialColour
,
2767 const wxColour
& destColour
,
2768 wxDirection nDirection
)
2770 // use native function if we have compile-time support it and can load it
2771 // during run-time (linking to it statically would make the program
2772 // unusable on earlier Windows versions)
2773 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2775 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2776 static GradientFill_t pfnGradientFill
=
2777 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(wxT("GradientFill"));
2779 if ( pfnGradientFill
)
2781 GRADIENT_RECT grect
;
2782 grect
.UpperLeft
= 0;
2783 grect
.LowerRight
= 1;
2785 // invert colours direction if not filling from left-to-right or
2787 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2789 // one vertex for upper left and one for upper-right
2790 TRIVERTEX vertices
[2];
2792 vertices
[0].x
= rect
.GetLeft();
2793 vertices
[0].y
= rect
.GetTop();
2794 vertices
[1].x
= rect
.GetRight()+1;
2795 vertices
[1].y
= rect
.GetBottom()+1;
2797 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2798 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2799 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2800 vertices
[firstVertex
].Alpha
= 0;
2801 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2802 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2803 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2804 vertices
[1 - firstVertex
].Alpha
= 0;
2806 if ( (*pfnGradientFill
)
2813 nDirection
== wxWEST
|| nDirection
== wxEAST
2814 ? GRADIENT_FILL_RECT_H
2815 : GRADIENT_FILL_RECT_V
2818 // skip call of the base class version below
2822 wxLogLastError(wxT("GradientFill"));
2824 #endif // wxUSE_DYNLIB_CLASS
2826 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2829 #if wxUSE_DYNLIB_CLASS
2834 DWORD
GetLayout(HDC hdc
)
2836 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2838 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2840 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : GDI_ERROR
;
2843 DWORD
SetLayout(HDC hdc
, DWORD dwLayout
)
2845 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2847 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2849 return s_pfnSetLayout
? s_pfnSetLayout(hdc
, dwLayout
) : GDI_ERROR
;
2852 HDC
CreateCompatibleDCWithLayout(HDC hdc
)
2854 HDC hdcNew
= ::CreateCompatibleDC(hdc
);
2857 DWORD dwLayout
= wxMSW::GetLayout(hdc
);
2858 if ( dwLayout
!= GDI_ERROR
)
2859 wxMSW::SetLayout(hdcNew
, dwLayout
);
2865 } // namespace wxMSW
2867 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2869 DWORD layout
= wxMSW::GetLayout(GetHdc());
2871 if ( layout
== GDI_ERROR
)
2872 return wxLayout_Default
;
2874 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2877 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2879 if ( dir
== wxLayout_Default
)
2881 dir
= wxTheApp
->GetLayoutDirection();
2882 if ( dir
== wxLayout_Default
)
2886 DWORD layout
= wxMSW::GetLayout(GetHdc());
2887 if ( layout
== GDI_ERROR
)
2890 if ( dir
== wxLayout_RightToLeft
)
2891 layout
|= LAYOUT_RTL
;
2893 layout
&= ~LAYOUT_RTL
;
2895 wxMSW::SetLayout(GetHdc(), layout
);
2898 #else // !wxUSE_DYNLIB_CLASS
2900 // Provide stubs to avoid ifdefs in the code using these functions.
2904 DWORD
GetLayout(HDC
WXUNUSED(hdc
))
2909 DWORD
SetLayout(HDC
WXUNUSED(hdc
), DWORD
WXUNUSED(dwLayout
))
2914 HDC
CreateCompatibleDCWithLayout(HDC hdc
)
2916 return ::CreateCompatibleDC(hdc
);
2919 } // namespace wxMSW
2921 // we can't provide RTL support without dynamic loading, so stub it out
2922 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2924 return wxLayout_Default
;
2927 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2931 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS