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"
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
151 // ----------------------------------------------------------------------------
153 // ----------------------------------------------------------------------------
155 // instead of duplicating the same code which sets and then restores text
156 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
157 // encapsulate this in a small helper class
159 // wxBrushAttrsSetter: changes the text colours in the ctor if required and
160 // restores them in the dtor
161 class wxBrushAttrsSetter
: private wxBkModeChanger
,
162 private wxTextColoursChanger
165 wxBrushAttrsSetter(wxMSWDCImpl
& dc
);
168 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter
);
173 #define SET_STRETCH_BLT_MODE(hdc)
175 #else // !__WXWINCE__
177 // this class sets the stretch blit mode to COLORONCOLOR during its lifetime
179 // don't use it directly, use SET_STRETCH_BLT_MODE() macro instead as it
180 // expands to nothing under WinCE which doesn't have SetStretchBltMode()
181 class StretchBltModeChanger
184 StretchBltModeChanger(HDC hdc
)
187 m_modeOld
= ::SetStretchBltMode(m_hdc
, COLORONCOLOR
);
190 wxLogLastError(wxT("SetStretchBltMode"));
194 ~StretchBltModeChanger()
196 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
198 wxLogLastError(wxT("SetStretchBltMode"));
207 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
210 #define SET_STRETCH_BLT_MODE(hdc) \
211 StretchBltModeChanger wxMAKE_UNIQUE_NAME(stretchModeChanger)(hdc)
213 #endif // __WXWINCE__/!__WXWINCE__
215 #if wxUSE_DYNLIB_CLASS
217 // helper class to cache dynamically loaded libraries and not attempt reloading
219 class wxOnceOnlyDLLLoader
222 // ctor argument must be a literal string as we don't make a copy of it!
223 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
229 // return the symbol with the given name or NULL if the DLL not loaded
230 // or symbol not present
231 void *GetSymbol(const wxChar
*name
)
233 // we're prepared to handle errors here
238 m_dll
.Load(m_dllName
);
240 // reset the name whether we succeeded or failed so that we don't
241 // try again the next time
245 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
250 if ( m_dll
.IsLoaded() )
257 wxDynamicLibrary m_dll
;
258 const wxChar
*m_dllName
;
261 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(wxT("msimg32"));
263 // we must ensure that DLLs are unloaded before the static objects cleanup time
264 // because we may hit the notorious DllMain() dead lock in this case if wx is
265 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
266 // under Windows because it tries to reacquire the same lock)
267 class wxGDIDLLsCleanupModule
: public wxModule
270 virtual bool OnInit() { return true; }
271 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
274 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
277 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
282 #if wxUSE_DC_TRANSFORM_MATRIX
284 // Class used to dynamically load world transform related API functions.
285 class GdiWorldTransformFuncs
290 if ( !ms_worldTransformSymbolsLoaded
)
291 LoadWorldTransformSymbols();
293 return ms_pfnSetGraphicsMode
&&
294 ms_pfnSetWorldTransform
&&
295 ms_pfnGetWorldTransform
&&
296 ms_pfnModifyWorldTransform
;
299 typedef int (WINAPI
*SetGraphicsMode_t
)(HDC
, int);
300 static SetGraphicsMode_t
SetGraphicsMode()
302 if ( !ms_worldTransformSymbolsLoaded
)
303 LoadWorldTransformSymbols();
305 return ms_pfnSetGraphicsMode
;
308 typedef BOOL (WINAPI
*SetWorldTransform_t
)(HDC
, const XFORM
*);
309 static SetWorldTransform_t
SetWorldTransform()
311 if ( !ms_worldTransformSymbolsLoaded
)
312 LoadWorldTransformSymbols();
314 return ms_pfnSetWorldTransform
;
317 typedef BOOL (WINAPI
*GetWorldTransform_t
)(HDC
, LPXFORM
);
318 static GetWorldTransform_t
GetWorldTransform()
320 if ( !ms_worldTransformSymbolsLoaded
)
321 LoadWorldTransformSymbols();
323 return ms_pfnGetWorldTransform
;
326 typedef BOOL (WINAPI
*ModifyWorldTransform_t
)(HDC
, const XFORM
*, DWORD
);
327 static ModifyWorldTransform_t
ModifyWorldTransform()
329 if ( !ms_worldTransformSymbolsLoaded
)
330 LoadWorldTransformSymbols();
332 return ms_pfnModifyWorldTransform
;
336 static void LoadWorldTransformSymbols()
338 wxDynamicLibrary
dll(wxT("gdi32.dll"));
340 wxDL_INIT_FUNC(ms_pfn
, SetGraphicsMode
, dll
);
341 wxDL_INIT_FUNC(ms_pfn
, SetWorldTransform
, dll
);
342 wxDL_INIT_FUNC(ms_pfn
, GetWorldTransform
, dll
);
343 wxDL_INIT_FUNC(ms_pfn
, ModifyWorldTransform
, dll
);
345 ms_worldTransformSymbolsLoaded
= true;
348 static SetGraphicsMode_t ms_pfnSetGraphicsMode
;
349 static SetWorldTransform_t ms_pfnSetWorldTransform
;
350 static GetWorldTransform_t ms_pfnGetWorldTransform
;
351 static ModifyWorldTransform_t ms_pfnModifyWorldTransform
;
353 static bool ms_worldTransformSymbolsLoaded
;
356 GdiWorldTransformFuncs::SetGraphicsMode_t
357 GdiWorldTransformFuncs::ms_pfnSetGraphicsMode
= NULL
;
358 GdiWorldTransformFuncs::SetWorldTransform_t
359 GdiWorldTransformFuncs::ms_pfnSetWorldTransform
= NULL
;
360 GdiWorldTransformFuncs::GetWorldTransform_t
361 GdiWorldTransformFuncs::ms_pfnGetWorldTransform
= NULL
;
362 GdiWorldTransformFuncs::ModifyWorldTransform_t
363 GdiWorldTransformFuncs::ms_pfnModifyWorldTransform
= NULL
;
365 bool GdiWorldTransformFuncs::ms_worldTransformSymbolsLoaded
= false;
367 #endif // wxUSE_DC_TRANSFORM_MATRIX
369 } // anonymous namespace
371 #endif // wxUSE_DYNLIB_CLASS
373 // ===========================================================================
375 // ===========================================================================
377 // ----------------------------------------------------------------------------
378 // wxBrushAttrsSetter
379 // ----------------------------------------------------------------------------
381 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
382 : wxBkModeChanger(GetHdcOf(dc
)),
383 wxTextColoursChanger(GetHdcOf(dc
))
385 const wxBrush
& brush
= dc
.GetBrush();
386 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
388 // note that Windows convention is opposite to wxWidgets one, this is
389 // why text colour becomes the background one and vice versa
390 wxTextColoursChanger::Change(dc
.GetTextBackground(),
391 dc
.GetTextForeground());
393 wxBkModeChanger::Change(dc
.GetBackgroundMode());
397 // ----------------------------------------------------------------------------
398 // wxDC MSW-specific methods
399 // ----------------------------------------------------------------------------
401 WXHDC
wxDC::GetHDC() const
403 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
404 return impl
? impl
->GetHDC() : 0;
407 // ---------------------------------------------------------------------------
409 // ---------------------------------------------------------------------------
411 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
418 wxMSWDCImpl::~wxMSWDCImpl()
422 SelectOldObjects(m_hDC
);
424 // if we own the HDC, we delete it, otherwise we just release it
428 ::DeleteDC(GetHdc());
430 else // we don't own our HDC
434 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
438 // Must have been a wxScreenDC
439 ::ReleaseDC((HWND
) NULL
, GetHdc());
445 // This will select current objects out of the DC,
446 // which is what you have to do before deleting the
448 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
454 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
455 if (m_selectedBitmap
.IsOk())
457 m_selectedBitmap
.SetSelectedInto(NULL
);
463 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
468 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
473 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
480 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
483 #endif // wxUSE_PALETTE
486 m_brush
= wxNullBrush
;
489 m_palette
= wxNullPalette
;
490 #endif // wxUSE_PALETTE
492 m_backgroundBrush
= wxNullBrush
;
493 m_selectedBitmap
= wxNullBitmap
;
496 // ---------------------------------------------------------------------------
498 // ---------------------------------------------------------------------------
500 void wxMSWDCImpl::UpdateClipBox()
505 ::GetClipBox(GetHdc(), &rect
);
507 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
508 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
509 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
510 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
514 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
516 // check if we should try to retrieve the clipping region possibly not set
517 // by our SetClippingRegion() but preset by Windows:this can only happen
518 // when we're associated with an existing HDC usign SetHDC(), see there
519 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
521 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
522 self
->UpdateClipBox();
524 if ( !m_clipX1
&& !m_clipX2
)
525 self
->m_clipping
= false;
528 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
531 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
532 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
534 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
538 // note that we combine the new clipping region with the existing one: this
539 // is compatible with what the other ports do and is the documented
540 // behaviour now (starting with 2.3.3)
541 #if defined(__WXWINCE__)
543 if ( !::GetClipBox(GetHdc(), &rectClip
) )
546 // GetClipBox returns logical coordinates, so transform to device
547 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
548 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
549 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
550 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
552 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
553 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
554 rectClip
.right
, rectClip
.bottom
);
556 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
558 ::SelectClipRgn(GetHdc(), hrgnDest
);
561 ::DeleteObject(hrgnClipOld
);
562 ::DeleteObject(hrgnDest
);
564 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
566 wxLogLastError(wxT("ExtSelectClipRgn"));
570 #endif // WinCE/!WinCE
577 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
579 // the region coords are always the device ones, so do the translation
582 // FIXME: possible +/-1 error here, to check!
583 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
585 LogicalToDeviceX(x
+ w
),
586 LogicalToDeviceY(y
+ h
));
589 wxLogLastError(wxT("CreateRectRgn"));
593 SetClippingHrgn((WXHRGN
)hrgn
);
595 ::DeleteObject(hrgn
);
599 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
601 SetClippingHrgn(region
.GetHRGN());
604 void wxMSWDCImpl::DestroyClippingRegion()
608 if (m_clipping
&& m_hDC
)
611 // On a PocketPC device (not necessarily emulator), resetting
612 // the clip region as per the old method causes bad display
613 // problems. In fact setting a null region is probably OK
614 // on desktop WIN32 also, since the WIN32 docs imply that the user
615 // clipping region is independent from the paint clipping region.
616 ::SelectClipRgn(GetHdc(), 0);
618 // TODO: this should restore the previous clipping region,
619 // so that OnPaint processing works correctly, and the update
620 // clipping region doesn't get destroyed after the first
621 // DestroyClippingRegion.
622 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
623 ::SelectClipRgn(GetHdc(), rgn
);
628 wxDCImpl::DestroyClippingRegion();
631 // ---------------------------------------------------------------------------
632 // query capabilities
633 // ---------------------------------------------------------------------------
635 bool wxMSWDCImpl::CanDrawBitmap() const
640 bool wxMSWDCImpl::CanGetTextExtent() const
642 #ifdef __WXMICROWIN__
643 // TODO Extend MicroWindows' GetDeviceCaps function
646 // What sort of display is it?
647 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
649 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
653 int wxMSWDCImpl::GetDepth() const
655 WXMICROWIN_CHECK_HDC_RET(16)
657 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
660 // ---------------------------------------------------------------------------
662 // ---------------------------------------------------------------------------
664 void wxMSWDCImpl::Clear()
671 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
675 // No, I think we should simply ignore this if printing on e.g.
677 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
678 if (!m_selectedBitmap
.IsOk())
681 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
682 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
683 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
687 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
690 DWORD colour
= ::GetBkColor(GetHdc());
691 HBRUSH brush
= ::CreateSolidBrush(colour
);
692 ::FillRect(GetHdc(), &rect
, brush
);
693 ::DeleteObject(brush
);
695 RealizeScaleAndOrigin();
698 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
699 wxCoord
WXUNUSED_IN_WINCE(y
),
700 const wxColour
& WXUNUSED_IN_WINCE(col
),
701 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
706 WXMICROWIN_CHECK_HDC_RET(false)
708 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
710 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
711 : FLOODFILLBORDER
) ) ;
714 // quoting from the MSDN docs:
716 // Following are some of the reasons this function might fail:
718 // * The filling could not be completed.
719 // * The specified point has the boundary color specified by the
720 // crColor parameter (if FLOODFILLBORDER was requested).
721 // * The specified point does not have the color specified by
722 // crColor (if FLOODFILLSURFACE was requested)
723 // * The point is outside the clipping region that is, it is not
724 // visible on the device.
726 wxLogLastError(wxT("ExtFloodFill"));
729 CalcBoundingBox(x
, y
);
735 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
737 WXMICROWIN_CHECK_HDC_RET(false)
739 wxCHECK_MSG( col
, false, wxT("NULL colour parameter in wxMSWDCImpl::GetPixel") );
741 // get the color of the pixel
742 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
744 wxRGBToColour(*col
, pixelcolor
);
749 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
753 wxCoord x1
= x
-VIEWPORT_EXTENT
;
754 wxCoord y1
= y
-VIEWPORT_EXTENT
;
755 wxCoord x2
= x
+VIEWPORT_EXTENT
;
756 wxCoord y2
= y
+VIEWPORT_EXTENT
;
758 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
759 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
761 CalcBoundingBox(x1
, y1
);
762 CalcBoundingBox(x2
, y2
);
765 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
769 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
771 CalcBoundingBox(x1
, y1
);
772 CalcBoundingBox(x2
, y2
);
775 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
776 // and ending at (x2, y2)
777 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
778 wxCoord x2
, wxCoord y2
,
779 wxCoord xc
, wxCoord yc
)
783 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
787 // Slower emulation since WinCE doesn't support Pie and Arc
788 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
790 sa
= -sa
; // below center
791 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
792 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
797 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
799 // treat the special case of full circle separately
800 if ( x1
== x2
&& y1
== y2
)
802 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
806 wxCoord xx1
= XLOG2DEV(x1
);
807 wxCoord yy1
= YLOG2DEV(y1
);
808 wxCoord xx2
= XLOG2DEV(x2
);
809 wxCoord yy2
= YLOG2DEV(y2
);
810 wxCoord xxc
= XLOG2DEV(xc
);
811 wxCoord yyc
= YLOG2DEV(yc
);
814 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
816 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
817 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
818 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
819 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
821 if ( m_brush
.IsNonTransparent() )
823 // Have to add 1 to bottom-right corner of rectangle
824 // to make semi-circles look right (crooked line otherwise).
825 // Unfortunately this is not a reliable method, depends
826 // on the size of shape.
827 // TODO: figure out why this happens!
828 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
832 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
835 CalcBoundingBox(xc
- r
, yc
- r
);
836 CalcBoundingBox(xc
+ r
, yc
+ r
);
840 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
841 wxCoord width
, wxCoord height
)
843 // cases when we don't have DrawFrameControl()
844 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
845 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
847 wxCoord x2
= x1
+ width
,
857 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
859 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
862 CalcBoundingBox(x1
, y1
);
863 CalcBoundingBox(x2
, y2
);
864 #endif // Microwin/Normal
867 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
871 COLORREF color
= 0x00ffffff;
874 color
= m_pen
.GetColour().GetPixel();
877 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
879 CalcBoundingBox(x
, y
);
882 void wxMSWDCImpl::DoDrawPolygon(int n
,
886 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
890 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
892 // Do things less efficiently if we have offsets
893 if (xoffset
!= 0 || yoffset
!= 0)
895 POINT
*cpoints
= new POINT
[n
];
897 for (i
= 0; i
< n
; i
++)
899 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
900 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
902 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
905 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
907 (void)Polygon(GetHdc(), cpoints
, n
);
909 SetPolyFillMode(GetHdc(),prev
);
916 for (i
= 0; i
< n
; i
++)
917 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
920 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
922 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
924 SetPolyFillMode(GetHdc(),prev
);
930 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
935 wxPolygonFillMode fillStyle
)
938 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
942 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
944 for (i
= cnt
= 0; i
< n
; i
++)
947 // Do things less efficiently if we have offsets
948 if (xoffset
!= 0 || yoffset
!= 0)
950 POINT
*cpoints
= new POINT
[cnt
];
951 for (i
= 0; i
< cnt
; i
++)
953 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
954 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
956 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
959 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
961 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
963 SetPolyFillMode(GetHdc(),prev
);
969 for (i
= 0; i
< cnt
; i
++)
970 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
973 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
975 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
977 SetPolyFillMode(GetHdc(),prev
);
984 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
988 // Do things less efficiently if we have offsets
989 if (xoffset
!= 0 || yoffset
!= 0)
991 POINT
*cpoints
= new POINT
[n
];
993 for (i
= 0; i
< n
; i
++)
995 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
996 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
998 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
1000 (void)Polyline(GetHdc(), cpoints
, n
);
1006 for (i
= 0; i
< n
; i
++)
1007 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
1009 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
1013 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1015 WXMICROWIN_CHECK_HDC
1017 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1019 wxCoord x2
= x
+ width
;
1020 wxCoord y2
= y
+ height
;
1022 wxCoord x2dev
= XLOG2DEV(x2
),
1023 y2dev
= YLOG2DEV(y2
);
1025 // Windows (but not Windows CE) draws the filled rectangles without outline
1026 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1027 // and we want them to have the same size regardless of which pen is used
1029 if ( m_pen
.IsTransparent() )
1034 #endif // !__WXWINCE__
1036 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
1038 CalcBoundingBox(x
, y
);
1039 CalcBoundingBox(x2
, y2
);
1042 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
1044 WXMICROWIN_CHECK_HDC
1046 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1048 // Now, a negative radius value is interpreted to mean
1049 // 'the proportion of the smallest X or Y dimension'
1053 double smallest
= (width
< height
) ? width
: height
;
1054 radius
= (- radius
* smallest
);
1057 wxCoord x2
= (x
+width
);
1058 wxCoord y2
= (y
+height
);
1060 // Windows draws the filled rectangles without outline (i.e. drawn with a
1061 // transparent pen) one pixel smaller in both directions and we want them
1062 // to have the same size regardless of which pen is used - adjust
1063 if ( m_pen
.IsTransparent() )
1069 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1070 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1072 CalcBoundingBox(x
, y
);
1073 CalcBoundingBox(x2
, y2
);
1076 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1078 WXMICROWIN_CHECK_HDC
1080 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1082 // +1 below makes the ellipse more similar to other platforms.
1083 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1084 wxCoord x2
= x
+ width
+ 1;
1085 wxCoord y2
= y
+ height
+ 1;
1087 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1088 // pen doesn't draw anything. Should we provide a workaround?
1090 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1092 CalcBoundingBox(x
, y
);
1093 CalcBoundingBox(x2
, y2
);
1096 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1097 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1099 // quadratic b-spline to cubic bezier spline conversion
1101 // quadratic spline with control points P0,P1,P2
1102 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1104 // bezier spline with control points B0,B1,B2,B3
1105 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1107 // control points of bezier spline calculated from b-spline
1109 // B1 = (2*P1 + P0)/3
1110 // B2 = (2*P1 + P2)/3
1113 WXMICROWIN_CHECK_HDC
1115 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1117 const size_t n_points
= points
->GetCount();
1118 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1120 const size_t n_bezier_points
= n_points
* 3 + 1;
1121 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1122 size_t bezier_pos
= 0;
1123 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1125 wxPointList::compatibility_iterator node
= points
->GetFirst();
1126 wxPoint
*p
= node
->GetData();
1127 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1128 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1130 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1133 node
= node
->GetNext();
1134 p
= node
->GetData();
1138 cx1
= ( x1
+ x2
) / 2;
1139 cy1
= ( y1
+ y2
) / 2;
1140 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1141 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1143 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1146 #if !wxUSE_STD_CONTAINERS
1147 while ((node
= node
->GetNext()) != NULL
)
1149 while ((node
= node
->GetNext()))
1150 #endif // !wxUSE_STD_CONTAINERS
1152 p
= (wxPoint
*)node
->GetData();
1157 cx4
= (x1
+ x2
) / 2;
1158 cy4
= (y1
+ y2
) / 2;
1159 // B0 is B3 of previous segment
1161 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1162 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1165 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1166 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1169 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1170 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1176 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1178 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1179 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1181 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1184 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1188 #endif // wxUSE_SPLINES
1190 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1191 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1194 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1197 WXMICROWIN_CHECK_HDC
1199 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1204 int rx1
= XLOG2DEV(x
+w
/2);
1205 int ry1
= YLOG2DEV(y
+h
/2);
1212 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1213 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1214 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1215 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1217 // Swap start and end positions if the end angle is less than the start angle.
1228 // draw pie with NULL_PEN first and then outline otherwise a line is
1229 // drawn from the start and end points to the centre
1230 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1233 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1234 rx1
, ry1
, rx2
, ry2
);
1238 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1239 rx1
, ry1
-1, rx2
, ry2
-1);
1242 ::SelectObject(GetHdc(), hpenOld
);
1244 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1245 rx1
, ry1
, rx2
, ry2
);
1247 CalcBoundingBox(x
, y
);
1248 CalcBoundingBox(x2
, y2
);
1252 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1254 WXMICROWIN_CHECK_HDC
1256 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1259 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1261 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1264 CalcBoundingBox(x
, y
);
1265 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1268 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1270 WXMICROWIN_CHECK_HDC
1272 wxCHECK_RET( bmp
.IsOk(), wxT("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1274 int width
= bmp
.GetWidth(),
1275 height
= bmp
.GetHeight();
1277 HBITMAP hbmpMask
= 0;
1280 HPALETTE oldPal
= 0;
1281 #endif // wxUSE_PALETTE
1283 if ( bmp
.HasAlpha() )
1286 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1288 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1292 SET_STRETCH_BLT_MODE(GetHdc());
1296 wxMask
*mask
= bmp
.GetMask();
1298 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1302 // don't give assert here because this would break existing
1303 // programs - just silently ignore useMask parameter
1310 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1314 #if wxUSE_SYSTEM_OPTIONS
1315 // On some systems, MaskBlt succeeds yet is much much slower
1316 // than the wxWidgets fall-back implementation. So we need
1317 // to be able to switch this on and off at runtime.
1319 // NB: don't query the value of the option every time but do it only
1320 // once as otherwise it can have real (and bad) performance
1321 // implications (see #11172)
1323 s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
1324 if ( s_maskBltAllowed
)
1325 #endif // wxUSE_SYSTEM_OPTIONS
1328 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1329 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1331 wxPalette
*pal
= bmp
.GetPalette();
1332 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1334 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1335 ::RealizePalette(hdcMem
);
1337 #endif // wxUSE_PALETTE
1339 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1342 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1346 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1347 #endif // wxUSE_PALETTE
1349 ::SelectObject(hdcMem
, hOldBitmap
);
1356 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1360 memDC
.SelectObjectAsSource(bmp
);
1362 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1364 memDC
.SelectObject(wxNullBitmap
);
1367 else // no mask, just use BitBlt()
1370 HDC memdc
= ::CreateCompatibleDC( cdc
);
1371 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1373 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1375 wxTextColoursChanger
textCol(GetHdc(), *this);
1378 wxPalette
*pal
= bmp
.GetPalette();
1379 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1381 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1382 ::RealizePalette(memdc
);
1384 #endif // wxUSE_PALETTE
1386 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1387 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1391 ::SelectPalette(memdc
, oldPal
, FALSE
);
1392 #endif // wxUSE_PALETTE
1394 ::SelectObject( memdc
, hOldBitmap
);
1395 ::DeleteDC( memdc
);
1399 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1401 // For compatibility with other ports (notably wxGTK) and because it's
1402 // genuinely useful, we allow passing multiline strings to DrawText().
1403 // However there is no native MSW function to draw them directly so we
1404 // instead reuse the generic DrawLabel() method to render them. Of course,
1405 // DrawLabel() itself will call back to us but with single line strings
1406 // only so there won't be any infinite recursion here.
1407 if ( text
.find('\n') != wxString::npos
)
1409 GetOwner()->DrawLabel(text
, wxRect(x
, y
, 0, 0));
1413 WXMICROWIN_CHECK_HDC
1415 DrawAnyText(text
, x
, y
);
1417 // update the bounding box
1418 CalcBoundingBox(x
, y
);
1421 GetOwner()->GetTextExtent(text
, &w
, &h
);
1422 CalcBoundingBox(x
+ w
, y
+ h
);
1425 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1427 WXMICROWIN_CHECK_HDC
1429 // prepare for drawing the text
1430 wxTextColoursChanger
textCol(GetHdc(), *this);
1432 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1434 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1435 text
.c_str(), text
.length(), NULL
) == 0 )
1437 wxLogLastError(wxT("TextOut"));
1441 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1442 wxCoord x
, wxCoord y
,
1445 WXMICROWIN_CHECK_HDC
1447 // we test that we have some font because otherwise we should still use the
1448 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1449 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1450 // font for drawing rotated fonts unfortunately)
1451 if ( (angle
== 0.0) && m_font
.IsOk() )
1453 DoDrawText(text
, x
, y
);
1455 #ifndef __WXMICROWIN__
1458 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1459 // because it's not TrueType and so can't have non zero
1460 // orientation/escapement under Win9x
1461 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1462 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1464 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1466 wxLogLastError(wxT("GetObject(hfont)"));
1469 // GDI wants the angle in tenth of degree
1470 long angle10
= (long)(angle
* 10);
1471 lf
.lfEscapement
= angle10
;
1472 lf
. lfOrientation
= angle10
;
1474 hfont
= ::CreateFontIndirect(&lf
);
1477 wxLogLastError(wxT("CreateFont"));
1481 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1483 DrawAnyText(text
, x
, y
);
1485 (void)::SelectObject(GetHdc(), hfontOld
);
1486 (void)::DeleteObject(hfont
);
1489 // call the bounding box by adding all four vertices of the rectangle
1490 // containing the text to it (simpler and probably not slower than
1491 // determining which of them is really topmost/leftmost/...)
1493 GetOwner()->GetTextExtent(text
, &w
, &h
);
1495 double rad
= DegToRad(angle
);
1497 // "upper left" and "upper right"
1498 CalcBoundingBox(x
, y
);
1499 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1501 // "bottom left" and "bottom right"
1502 x
+= (wxCoord
)(h
*sin(rad
));
1503 y
+= (wxCoord
)(h
*cos(rad
));
1504 CalcBoundingBox(x
, y
);
1505 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1510 // ---------------------------------------------------------------------------
1512 // ---------------------------------------------------------------------------
1516 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1518 WXMICROWIN_CHECK_HDC
1520 // Set the old object temporarily, in case the assignment deletes an object
1521 // that's not yet selected out.
1524 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1528 if ( m_palette
.IsOk() )
1530 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1531 GetHpaletteOf(m_palette
),
1534 m_oldPalette
= (WXHPALETTE
) oldPal
;
1537 ::RealizePalette(GetHdc());
1541 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1543 if ( palette
.IsOk() )
1545 m_palette
= palette
;
1546 DoSelectPalette(true);
1550 void wxMSWDCImpl::InitializePalette()
1552 if ( wxDisplayDepth() <= 8 )
1554 // look for any window or parent that has a custom palette. If any has
1555 // one then we need to use it in drawing operations
1556 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1558 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1559 if ( m_hasCustomPalette
)
1561 m_palette
= win
->GetPalette();
1563 // turn on MSW translation for this palette
1569 #endif // wxUSE_PALETTE
1571 // SetFont/Pen/Brush() really ask to be implemented as a single template
1572 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1574 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1576 WXMICROWIN_CHECK_HDC
1578 if ( font
== m_font
)
1583 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1584 if ( hfont
== HGDI_ERROR
)
1586 wxLogLastError(wxT("SelectObject(font)"));
1591 m_oldFont
= (WXHFONT
)hfont
;
1596 else // invalid font, reset the current font
1600 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1602 wxLogLastError(wxT("SelectObject(old font)"));
1608 m_font
= wxNullFont
;
1612 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1614 WXMICROWIN_CHECK_HDC
1621 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1622 if ( hpen
== HGDI_ERROR
)
1624 wxLogLastError(wxT("SelectObject(pen)"));
1629 m_oldPen
= (WXHPEN
)hpen
;
1634 else // invalid pen, reset the current pen
1638 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1640 wxLogLastError(wxT("SelectObject(old pen)"));
1650 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1652 WXMICROWIN_CHECK_HDC
1654 if ( brush
== m_brush
)
1659 // we must make sure the brush is aligned with the logical coordinates
1660 // before selecting it or using the same brush for the background of
1661 // different windows would result in discontinuities
1662 wxSize sizeBrushBitmap
= wxDefaultSize
;
1663 wxBitmap
*stipple
= brush
.GetStipple();
1664 if ( stipple
&& stipple
->IsOk() )
1665 sizeBrushBitmap
= stipple
->GetSize();
1666 else if ( brush
.IsHatch() )
1667 sizeBrushBitmap
= wxSize(8, 8);
1669 if ( sizeBrushBitmap
.IsFullySpecified() )
1671 if ( !::SetBrushOrgEx
1674 m_deviceOriginX
% sizeBrushBitmap
.x
,
1675 m_deviceOriginY
% sizeBrushBitmap
.y
,
1676 NULL
// [out] previous brush origin
1679 wxLogLastError(wxT("SetBrushOrgEx()"));
1683 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1684 if ( hbrush
== HGDI_ERROR
)
1686 wxLogLastError(wxT("SelectObject(brush)"));
1691 m_oldBrush
= (WXHBRUSH
)hbrush
;
1696 else // invalid brush, reset the current brush
1700 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1702 wxLogLastError(wxT("SelectObject(old brush)"));
1708 m_brush
= wxNullBrush
;
1712 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1714 WXMICROWIN_CHECK_HDC
1716 m_backgroundBrush
= brush
;
1718 if ( m_backgroundBrush
.IsOk() )
1720 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1724 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1726 WXMICROWIN_CHECK_HDC
1728 m_backgroundMode
= mode
;
1730 // SetBackgroundColour now only refers to text background
1731 // and m_backgroundMode is used there
1734 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1736 WXMICROWIN_CHECK_HDC
1738 m_logicalFunction
= function
;
1743 void wxMSWDCImpl::SetRop(WXHDC dc
)
1745 if ( !dc
|| m_logicalFunction
< 0 )
1750 switch (m_logicalFunction
)
1752 case wxCLEAR
: rop
= R2_BLACK
; break;
1753 case wxXOR
: rop
= R2_XORPEN
; break;
1754 case wxINVERT
: rop
= R2_NOT
; break;
1755 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1756 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1757 case wxCOPY
: rop
= R2_COPYPEN
; break;
1758 case wxAND
: rop
= R2_MASKPEN
; break;
1759 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1760 case wxNO_OP
: rop
= R2_NOP
; break;
1761 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1762 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1763 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1764 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1765 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1766 case wxOR
: rop
= R2_MERGEPEN
; break;
1767 case wxSET
: rop
= R2_WHITE
; break;
1769 wxFAIL_MSG( wxS("unknown logical function") );
1773 SetROP2(GetHdc(), rop
);
1776 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1778 // We might be previewing, so return true to let it continue.
1782 void wxMSWDCImpl::EndDoc()
1786 void wxMSWDCImpl::StartPage()
1790 void wxMSWDCImpl::EndPage()
1794 // ---------------------------------------------------------------------------
1796 // ---------------------------------------------------------------------------
1798 wxCoord
wxMSWDCImpl::GetCharHeight() const
1800 WXMICROWIN_CHECK_HDC_RET(0)
1802 TEXTMETRIC lpTextMetric
;
1804 GetTextMetrics(GetHdc(), &lpTextMetric
);
1806 return lpTextMetric
.tmHeight
;
1809 wxCoord
wxMSWDCImpl::GetCharWidth() const
1811 WXMICROWIN_CHECK_HDC_RET(0)
1813 TEXTMETRIC lpTextMetric
;
1815 GetTextMetrics(GetHdc(), &lpTextMetric
);
1817 return lpTextMetric
.tmAveCharWidth
;
1820 void wxMSWDCImpl::DoGetFontMetrics(int *height
,
1823 int *internalLeading
,
1824 int *externalLeading
,
1825 int *averageWidth
) const
1829 GetTextMetrics(GetHdc(), &tm
);
1832 *height
= tm
.tmHeight
;
1834 *ascent
= tm
.tmAscent
;
1836 *descent
= tm
.tmDescent
;
1837 if ( internalLeading
)
1838 *internalLeading
= tm
.tmInternalLeading
;
1839 if ( externalLeading
)
1840 *externalLeading
= tm
.tmExternalLeading
;
1842 *averageWidth
= tm
.tmAveCharWidth
;
1845 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1846 wxCoord
*descent
, wxCoord
*externalLeading
,
1847 const wxFont
*font
) const
1849 #ifdef __WXMICROWIN__
1854 if (descent
) *descent
= 0;
1855 if (externalLeading
) *externalLeading
= 0;
1858 #endif // __WXMICROWIN__
1863 wxASSERT_MSG( font
->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") );
1865 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1867 else // don't change the font
1873 const size_t len
= string
.length();
1874 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1876 wxLogLastError(wxT("GetTextExtentPoint32()"));
1879 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1880 // the result computed by GetTextExtentPoint32() may be too small as it
1881 // accounts for under/overhang of the first/last character while we want
1882 // just the bounding rect for this string so adjust the width as needed
1883 // (using API not available in 2002 SDKs of WinCE)
1887 const wxChar chFirst
= *string
.begin();
1888 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1890 if ( width
.abcA
< 0 )
1891 sizeRect
.cx
-= width
.abcA
;
1895 const wxChar chLast
= *string
.rbegin();
1896 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1898 //else: we already have the width of the last character
1900 if ( width
.abcC
< 0 )
1901 sizeRect
.cx
-= width
.abcC
;
1903 //else: GetCharABCWidths() failed, not a TrueType font?
1905 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1912 if ( descent
|| externalLeading
)
1914 DoGetFontMetrics(NULL
, NULL
, descent
, NULL
, externalLeading
, NULL
);
1919 ::SelectObject(GetHdc(), hfontOld
);
1924 // Each element of the array will be the width of the string up to and
1925 // including the coresoponding character in text.
1927 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1929 static int maxLenText
= -1;
1930 static int maxWidth
= -1;
1933 int stlen
= text
.length();
1935 if (maxLenText
== -1)
1937 // Win9x and WinNT+ have different limits
1938 int version
= wxGetOsVersion();
1939 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1940 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1944 widths
.Add(0, stlen
); // fill the array with zeros
1948 if (!::GetTextExtentExPoint(GetHdc(),
1949 text
.c_str(), // string to check
1950 wxMin(stlen
, maxLenText
),
1952 &fit
, // [out] count of chars
1954 &widths
[0], // array to fill
1958 wxLogLastError(wxT("GetTextExtentExPoint"));
1968 void ApplyEffectiveScale(double scale
, int sign
, int *device
, int *logical
)
1970 // To reduce rounding errors as much as possible, we try to use the largest
1971 // possible extent (2^27-1) for the device space but we must also avoid
1972 // overflowing the int range i.e. ensure that logical extents are less than
1973 // 2^31 in magnitude. So the minimal scale we can use is 1/16 as for
1974 // anything smaller VIEWPORT_EXTENT/scale would overflow the int range.
1975 static const double MIN_LOGICAL_SCALE
= 1./16;
1977 double physExtent
= VIEWPORT_EXTENT
;
1978 if ( scale
< MIN_LOGICAL_SCALE
)
1980 physExtent
*= scale
/MIN_LOGICAL_SCALE
;
1981 scale
= MIN_LOGICAL_SCALE
;
1984 *device
= wxRound(physExtent
);
1985 *logical
= sign
*wxRound(VIEWPORT_EXTENT
/scale
);
1988 } // anonymous namespace
1990 void wxMSWDCImpl::RealizeScaleAndOrigin()
1992 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1993 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1994 // noticeable difference between these mapping modes
1996 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1998 // wxWidgets API assumes that the coordinate space is "infinite" (i.e. only
1999 // limited by 2^32 range of the integer coordinates) but in MSW API we must
2000 // actually specify the extents that we use so compute them here.
2002 int devExtX
, devExtY
, // Viewport, i.e. device space, extents.
2003 logExtX
, logExtY
; // Window, i.e. logical coordinate space, extents.
2005 ApplyEffectiveScale(m_scaleX
, m_signX
, &devExtX
, &logExtX
);
2006 ApplyEffectiveScale(m_scaleY
, m_signY
, &devExtY
, &logExtY
);
2008 ::SetViewportExtEx(GetHdc(), devExtX
, devExtY
, NULL
);
2009 ::SetWindowExtEx(GetHdc(), logExtX
, logExtY
, NULL
);
2011 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
2012 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
2016 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
2018 WXMICROWIN_CHECK_HDC
2020 m_mappingMode
= mode
;
2022 if ( mode
== wxMM_TEXT
)
2025 m_logicalScaleY
= 1.0;
2027 else // need to do some calculations
2029 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
2030 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
2031 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
2032 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
2034 if ( (mm_width
== 0) || (mm_height
== 0) )
2036 // we can't calculate mm2pixels[XY] then!
2040 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
2041 mm2pixelsY
= (double)pixel_height
/ mm_height
;
2046 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
2047 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
2051 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
2052 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
2056 m_logicalScaleX
= mm2pixelsX
;
2057 m_logicalScaleY
= mm2pixelsY
;
2061 m_logicalScaleX
= mm2pixelsX
/ 10.0;
2062 m_logicalScaleY
= mm2pixelsY
/ 10.0;
2066 wxFAIL_MSG( wxT("unknown mapping mode in SetMapMode") );
2070 ComputeScaleAndOrigin();
2072 RealizeScaleAndOrigin();
2075 void wxMSWDCImpl::SetUserScale(double x
, double y
)
2077 WXMICROWIN_CHECK_HDC
2079 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
2082 wxDCImpl::SetUserScale(x
,y
);
2084 RealizeScaleAndOrigin();
2087 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2090 WXMICROWIN_CHECK_HDC
2092 int signX
= xLeftRight
? 1 : -1,
2093 signY
= yBottomUp
? -1 : 1;
2095 if (signX
== m_signX
&& signY
== m_signY
)
2098 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2100 RealizeScaleAndOrigin();
2103 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2105 WXMICROWIN_CHECK_HDC
2107 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2110 wxDCImpl::SetLogicalOrigin( x
, y
);
2112 RealizeScaleAndOrigin();
2115 // For use by wxWidgets only, unless custom units are required.
2116 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2118 WXMICROWIN_CHECK_HDC
2120 wxDCImpl::SetLogicalScale(x
,y
);
2123 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2125 WXMICROWIN_CHECK_HDC
2127 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2130 wxDCImpl::SetDeviceOrigin( x
, y
);
2132 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2135 // ----------------------------------------------------------------------------
2137 // ----------------------------------------------------------------------------
2139 #if wxUSE_DC_TRANSFORM_MATRIX
2141 bool wxMSWDCImpl::CanUseTransformMatrix() const
2143 return GdiWorldTransformFuncs::IsOk();
2146 bool wxMSWDCImpl::SetTransformMatrix(const wxAffineMatrix2D
&matrix
)
2148 if ( !GdiWorldTransformFuncs::IsOk() )
2151 if ( matrix
.IsIdentity() )
2153 ResetTransformMatrix();
2157 if ( !GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_ADVANCED
) )
2159 wxLogLastError(wxT("SetGraphicsMode"));
2165 matrix
.Get(&mat
, &tr
);
2168 xform
.eM11
= mat
.m_11
;
2169 xform
.eM12
= mat
.m_12
;
2170 xform
.eM21
= mat
.m_21
;
2171 xform
.eM22
= mat
.m_22
;
2175 if ( !GdiWorldTransformFuncs::SetWorldTransform()(GetHdc(), &xform
) )
2177 wxLogLastError(wxT("SetWorldTransform"));
2184 wxAffineMatrix2D
wxMSWDCImpl::GetTransformMatrix() const
2186 wxAffineMatrix2D transform
;
2188 if ( !GdiWorldTransformFuncs::IsOk() )
2192 if ( !GdiWorldTransformFuncs::GetWorldTransform()(GetHdc(), &xform
) )
2194 wxLogLastError(wxT("GetWorldTransform"));
2198 wxMatrix2D
m(xform
.eM11
, xform
.eM12
, xform
.eM21
, xform
.eM22
);
2199 wxPoint2DDouble
p(xform
.eDx
, xform
.eDy
);
2200 transform
.Set(m
, p
);
2205 void wxMSWDCImpl::ResetTransformMatrix()
2207 if ( GdiWorldTransformFuncs::IsOk() )
2209 GdiWorldTransformFuncs::ModifyWorldTransform()(GetHdc(), NULL
, MWT_IDENTITY
);
2210 GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_COMPATIBLE
);
2214 #endif // wxUSE_DC_TRANSFORM_MATRIX
2216 // ---------------------------------------------------------------------------
2218 // ---------------------------------------------------------------------------
2220 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2221 wxCoord dstWidth
, wxCoord dstHeight
,
2223 wxCoord srcX
, wxCoord srcY
,
2224 wxRasterOperationMode rop
, bool useMask
,
2225 wxCoord srcMaskX
, wxCoord srcMaskY
)
2227 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2230 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2231 wxCoord dstWidth
, wxCoord dstHeight
,
2233 wxCoord xsrc
, wxCoord ysrc
,
2234 wxCoord srcWidth
, wxCoord srcHeight
,
2235 wxRasterOperationMode rop
, bool useMask
,
2236 wxCoord xsrcMask
, wxCoord ysrcMask
)
2238 wxCHECK_MSG( source
, false, wxT("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2240 WXMICROWIN_CHECK_HDC_RET(false)
2242 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2245 // TODO: Do we want to be able to blit from other DCs too?
2249 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2251 // if either the source or destination has alpha channel, we must use
2252 // AlphaBlt() as other function don't handle it correctly
2253 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2254 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2255 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2257 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2258 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2262 wxMask
*mask
= NULL
;
2265 mask
= bmpSrc
.GetMask();
2267 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2269 // don't give assert here because this would break existing
2270 // programs - just silently ignore useMask parameter
2275 if (xsrcMask
== -1 && ysrcMask
== -1)
2277 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2280 wxTextColoursChanger
textCol(GetHdc(), *this);
2285 case wxXOR
: dwRop
= SRCINVERT
; break;
2286 case wxINVERT
: dwRop
= DSTINVERT
; break;
2287 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2288 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2289 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2290 case wxSET
: dwRop
= WHITENESS
; break;
2291 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2292 case wxAND
: dwRop
= SRCAND
; break;
2293 case wxOR
: dwRop
= SRCPAINT
; break;
2294 case wxEQUIV
: dwRop
= 0x00990066; break;
2295 case wxNAND
: dwRop
= 0x007700E6; break;
2296 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2297 case wxCOPY
: dwRop
= SRCCOPY
; break;
2298 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2299 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2300 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2302 wxFAIL_MSG( wxT("unsupported logical function") );
2306 bool success
= false;
2311 // we want the part of the image corresponding to the mask to be
2312 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2313 // meaning of fg and bg is inverted which corresponds to wxWin notion
2314 // of the mask which is also contrary to the Windows one)
2316 // On some systems, MaskBlt succeeds yet is much much slower
2317 // than the wxWidgets fall-back implementation. So we need
2318 // to be able to switch this on and off at runtime.
2319 #if wxUSE_SYSTEM_OPTIONS
2320 static bool s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
2321 if ( s_maskBltAllowed
)
2324 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2329 xdest
, ydest
, dstWidth
, dstHeight
,
2332 (HBITMAP
)mask
->GetMaskBitmap(),
2334 MAKEROP4(dwRop
, DSTCOPY
)
2342 // Blit bitmap with mask
2345 HBITMAP buffer_bmap
;
2347 #if wxUSE_DC_CACHEING
2348 // create a temp buffer bitmap and DCs to access it and the mask
2349 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2350 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2352 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2353 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2355 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2356 dstWidth
, dstHeight
);
2358 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2359 #else // !wxUSE_DC_CACHEING
2360 // create a temp buffer bitmap and DCs to access it and the mask
2361 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2362 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2363 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2364 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2365 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2366 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2368 // copy dest to buffer
2369 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2370 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2372 wxLogLastError(wxT("BitBlt"));
2375 SET_STRETCH_BLT_MODE(GetHdc());
2377 // copy src to buffer using selected raster op
2378 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2379 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2381 wxLogLastError(wxT("StretchBlt"));
2384 // set masked area in buffer to BLACK
2386 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2387 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2388 dc_mask
, xsrcMask
, ysrcMask
,
2389 srcWidth
, srcHeight
, SRCAND
) )
2391 wxLogLastError(wxT("StretchBlt"));
2394 // set unmasked area in dest to BLACK
2395 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2396 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2397 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2398 dc_mask
, xsrcMask
, ysrcMask
,
2399 srcWidth
, srcHeight
, SRCAND
) )
2401 wxLogLastError(wxT("StretchBlt"));
2403 } // restore the original text and background colours
2405 // OR buffer to dest
2406 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2407 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2410 wxLogLastError(wxT("BitBlt"));
2413 // tidy up temporary DCs and bitmap
2414 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2415 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2417 #if !wxUSE_DC_CACHEING
2419 ::DeleteDC(dc_mask
);
2420 ::DeleteDC(dc_buffer
);
2421 ::DeleteObject(buffer_bmap
);
2426 else // no mask, just BitBlt() it
2428 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2429 // use StretchBlt() if available and finally fall back to BitBlt()
2431 // FIXME: use appropriate WinCE functions
2433 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2434 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2439 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2441 &ds
) == sizeof(ds
) )
2443 SET_STRETCH_BLT_MODE(GetHdc());
2445 // Figure out what co-ordinate system we're supposed to specify
2447 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2451 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2454 if ( ::StretchDIBits(GetHdc(),
2456 dstWidth
, dstHeight
,
2458 srcWidth
, srcHeight
,
2460 (LPBITMAPINFO
)&ds
.dsBmih
,
2463 ) == (int)GDI_ERROR
)
2465 // On Win9x this API fails most (all?) of the time, so
2466 // logging it becomes quite distracting. Since it falls
2467 // back to the code below this is not really serious, so
2469 //wxLogLastError(wxT("StretchDIBits"));
2478 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2482 SET_STRETCH_BLT_MODE(GetHdc());
2487 xdest
, ydest
, dstWidth
, dstHeight
,
2489 xsrc
, ysrc
, srcWidth
, srcHeight
,
2493 wxLogLastError(wxT("StretchBlt"));
2503 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2504 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2506 wxLogLastError(wxT("BitBlt"));
2518 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2520 WXMICROWIN_CHECK_HDC
2523 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2525 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2528 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2530 WXMICROWIN_CHECK_HDC
2532 // if we implement it in terms of DoGetSize() instead of directly using the
2533 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2534 // will also work for wxWindowDC and wxClientDC even though their size is
2535 // not the same as the total size of the screen
2536 int wPixels
, hPixels
;
2537 DoGetSize(&wPixels
, &hPixels
);
2541 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2543 wxCHECK_RET( wTotal
, wxT("0 width device?") );
2545 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2550 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2552 wxCHECK_RET( hTotal
, wxT("0 height device?") );
2554 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2558 wxSize
wxMSWDCImpl::GetPPI() const
2560 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2562 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2563 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2565 return wxSize(x
, y
);
2568 // ----------------------------------------------------------------------------
2570 // ----------------------------------------------------------------------------
2572 #if wxUSE_DC_CACHEING
2575 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2576 * improve it in due course, either using arrays, or simply storing pointers to one
2577 * entry for the bitmap, and two for the DCs. -- JACS
2580 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2581 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2583 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2592 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2601 wxDCCacheEntry::~wxDCCacheEntry()
2604 ::DeleteObject((HBITMAP
) m_bitmap
);
2606 ::DeleteDC((HDC
) m_dc
);
2609 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2611 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2612 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2615 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2617 if (entry
->m_depth
== depth
)
2619 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2621 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2622 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2623 if ( !entry
->m_bitmap
)
2625 wxLogLastError(wxT("CreateCompatibleBitmap"));
2627 entry
->m_width
= w
; entry
->m_height
= h
;
2633 node
= node
->GetNext();
2635 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2638 wxLogLastError(wxT("CreateCompatibleBitmap"));
2640 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2641 AddToBitmapCache(entry
);
2645 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2647 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2648 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2651 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2653 // Don't return the same one as we already have
2654 if (!notThis
|| (notThis
!= entry
))
2656 if (entry
->m_depth
== depth
)
2662 node
= node
->GetNext();
2664 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2667 wxLogLastError(wxT("CreateCompatibleDC"));
2669 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2670 AddToDCCache(entry
);
2674 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2676 sm_bitmapCache
.Append(entry
);
2679 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2681 sm_dcCache
.Append(entry
);
2684 void wxMSWDCImpl::ClearCache()
2686 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2687 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2690 // Clean up cache at app exit
2691 class wxDCModule
: public wxModule
2694 virtual bool OnInit() { return true; }
2695 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2698 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2701 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2703 #endif // wxUSE_DC_CACHEING
2705 // ----------------------------------------------------------------------------
2706 // alpha channel support
2707 // ----------------------------------------------------------------------------
2709 static bool AlphaBlt(HDC hdcDst
,
2710 int x
, int y
, int dstWidth
, int dstHeight
,
2712 int srcWidth
, int srcHeight
,
2714 const wxBitmap
& bmp
)
2716 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2717 wxASSERT_MSG( hdcDst
&& hdcSrc
, wxT("AlphaBlt(): invalid HDC") );
2719 // do we have AlphaBlend() and company in the headers?
2720 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2721 // yes, now try to see if we have it during run-time
2722 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2723 HDC
,int,int,int,int,
2727 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(wxT("AlphaBlend"));
2728 if ( pfnAlphaBlend
)
2731 bf
.BlendOp
= AC_SRC_OVER
;
2733 bf
.SourceConstantAlpha
= 0xff;
2734 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2736 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2737 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2740 // skip wxAlphaBlend() call below
2744 wxLogLastError(wxT("AlphaBlend"));
2747 wxUnusedVar(hdcSrc
);
2748 #endif // defined(AC_SRC_OVER)
2750 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2752 #ifdef wxHAS_RAW_BITMAP
2753 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2756 #else // !wxHAS_RAW_BITMAP
2757 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2758 // alpha but at least something will be shown like this)
2761 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2765 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2766 #ifdef wxHAS_RAW_BITMAP
2769 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2770 int dstWidth
, int dstHeight
,
2772 int srcWidth
, int srcHeight
,
2773 const wxBitmap
& bmpSrc
)
2775 // get the destination DC pixels
2776 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2778 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2780 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2782 wxLogLastError(wxT("BitBlt"));
2785 // combine them with the source bitmap using alpha
2786 wxAlphaPixelData
dataDst(bmpDst
),
2787 dataSrc((wxBitmap
&)bmpSrc
);
2789 wxCHECK_RET( dataDst
&& dataSrc
,
2790 wxT("failed to get raw data in wxAlphaBlend") );
2792 wxAlphaPixelData::Iterator
pDst(dataDst
),
2796 for ( int y
= 0; y
< dstHeight
; y
++ )
2798 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2800 for ( int x
= 0; x
< dstWidth
; x
++ )
2802 // source is point sampled, Alpha StretchBlit is ugly on Win95
2803 // (but does not impact performance)
2804 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2806 // note that source bitmap uses premultiplied alpha (as required by
2807 // the real AlphaBlend)
2808 const unsigned beta
= 255 - pSrc
.Alpha();
2810 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2811 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2812 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2817 pDst
= pDstRowStart
;
2818 pDst
.OffsetY(dataDst
, 1);
2821 // and finally blit them back to the destination DC
2822 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2824 wxLogLastError(wxT("BitBlt"));
2828 #endif // wxHAS_RAW_BITMAP
2830 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2831 const wxColour
& initialColour
,
2832 const wxColour
& destColour
,
2833 wxDirection nDirection
)
2835 // use native function if we have compile-time support it and can load it
2836 // during run-time (linking to it statically would make the program
2837 // unusable on earlier Windows versions)
2838 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2840 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2841 static GradientFill_t pfnGradientFill
=
2842 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(wxT("GradientFill"));
2844 if ( pfnGradientFill
)
2846 GRADIENT_RECT grect
;
2847 grect
.UpperLeft
= 0;
2848 grect
.LowerRight
= 1;
2850 // invert colours direction if not filling from left-to-right or
2852 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2854 // one vertex for upper left and one for upper-right
2855 TRIVERTEX vertices
[2];
2857 vertices
[0].x
= rect
.GetLeft();
2858 vertices
[0].y
= rect
.GetTop();
2859 vertices
[1].x
= rect
.GetRight()+1;
2860 vertices
[1].y
= rect
.GetBottom()+1;
2862 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2863 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2864 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2865 vertices
[firstVertex
].Alpha
= 0;
2866 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2867 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2868 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2869 vertices
[1 - firstVertex
].Alpha
= 0;
2871 if ( (*pfnGradientFill
)
2878 nDirection
== wxWEST
|| nDirection
== wxEAST
2879 ? GRADIENT_FILL_RECT_H
2880 : GRADIENT_FILL_RECT_V
2883 // skip call of the base class version below
2887 wxLogLastError(wxT("GradientFill"));
2889 #endif // wxUSE_DYNLIB_CLASS
2891 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2894 #if wxUSE_DYNLIB_CLASS
2896 static DWORD
wxGetDCLayout(HDC hdc
)
2898 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2900 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2902 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2905 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2907 DWORD layout
= wxGetDCLayout(GetHdc());
2909 if ( layout
== (DWORD
)-1 )
2910 return wxLayout_Default
;
2912 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2915 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2917 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2919 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2920 if ( !s_pfnSetLayout
)
2923 if ( dir
== wxLayout_Default
)
2925 dir
= wxTheApp
->GetLayoutDirection();
2926 if ( dir
== wxLayout_Default
)
2930 DWORD layout
= wxGetDCLayout(GetHdc());
2931 if ( dir
== wxLayout_RightToLeft
)
2932 layout
|= LAYOUT_RTL
;
2934 layout
&= ~LAYOUT_RTL
;
2936 s_pfnSetLayout(GetHdc(), layout
);
2939 #else // !wxUSE_DYNLIB_CLASS
2941 // we can't provide RTL support without dynamic loading, so stub it out
2942 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2944 return wxLayout_Default
;
2947 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2951 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS