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
152 // ----------------------------------------------------------------------------
154 // ----------------------------------------------------------------------------
156 // instead of duplicating the same code which sets and then restores text
157 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
158 // encapsulate this in a small helper class
160 // wxBrushAttrsSetter: changes the text colours in the ctor if required and
161 // restores them in the dtor
162 class wxBrushAttrsSetter
: private wxBkModeChanger
,
163 private wxTextColoursChanger
166 wxBrushAttrsSetter(wxMSWDCImpl
& dc
);
169 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter
);
174 #define SET_STRETCH_BLT_MODE(hdc)
176 #else // !__WXWINCE__
178 // this class sets the stretch blit mode to COLORONCOLOR during its lifetime
180 // don't use it directly, use SET_STRETCH_BLT_MODE() macro instead as it
181 // expands to nothing under WinCE which doesn't have SetStretchBltMode()
182 class StretchBltModeChanger
185 StretchBltModeChanger(HDC hdc
)
188 m_modeOld
= ::SetStretchBltMode(m_hdc
, COLORONCOLOR
);
191 wxLogLastError(wxT("SetStretchBltMode"));
195 ~StretchBltModeChanger()
197 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
199 wxLogLastError(wxT("SetStretchBltMode"));
208 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
211 #define SET_STRETCH_BLT_MODE(hdc) \
212 StretchBltModeChanger wxMAKE_UNIQUE_NAME(stretchModeChanger)(hdc)
214 #endif // __WXWINCE__/!__WXWINCE__
216 #if wxUSE_DYNLIB_CLASS
218 // helper class to cache dynamically loaded libraries and not attempt reloading
220 class wxOnceOnlyDLLLoader
223 // ctor argument must be a literal string as we don't make a copy of it!
224 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
230 // return the symbol with the given name or NULL if the DLL not loaded
231 // or symbol not present
232 void *GetSymbol(const wxChar
*name
)
234 // we're prepared to handle errors here
239 m_dll
.Load(m_dllName
);
241 // reset the name whether we succeeded or failed so that we don't
242 // try again the next time
246 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
251 if ( m_dll
.IsLoaded() )
258 wxDynamicLibrary m_dll
;
259 const wxChar
*m_dllName
;
262 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(wxT("msimg32"));
264 // we must ensure that DLLs are unloaded before the static objects cleanup time
265 // because we may hit the notorious DllMain() dead lock in this case if wx is
266 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
267 // under Windows because it tries to reacquire the same lock)
268 class wxGDIDLLsCleanupModule
: public wxModule
271 virtual bool OnInit() { return true; }
272 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
275 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
278 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
283 #if wxUSE_DC_TRANSFORM_MATRIX
285 // Class used to dynamically load world transform related API functions.
286 class GdiWorldTransformFuncs
291 if ( !ms_worldTransformSymbolsLoaded
)
292 LoadWorldTransformSymbols();
294 return ms_pfnSetGraphicsMode
&&
295 ms_pfnSetWorldTransform
&&
296 ms_pfnGetWorldTransform
&&
297 ms_pfnModifyWorldTransform
;
300 typedef int (WINAPI
*SetGraphicsMode_t
)(HDC
, int);
301 static SetGraphicsMode_t
SetGraphicsMode()
303 if ( !ms_worldTransformSymbolsLoaded
)
304 LoadWorldTransformSymbols();
306 return ms_pfnSetGraphicsMode
;
309 typedef BOOL (WINAPI
*SetWorldTransform_t
)(HDC
, const XFORM
*);
310 static SetWorldTransform_t
SetWorldTransform()
312 if ( !ms_worldTransformSymbolsLoaded
)
313 LoadWorldTransformSymbols();
315 return ms_pfnSetWorldTransform
;
318 typedef BOOL (WINAPI
*GetWorldTransform_t
)(HDC
, LPXFORM
);
319 static GetWorldTransform_t
GetWorldTransform()
321 if ( !ms_worldTransformSymbolsLoaded
)
322 LoadWorldTransformSymbols();
324 return ms_pfnGetWorldTransform
;
327 typedef BOOL (WINAPI
*ModifyWorldTransform_t
)(HDC
, const XFORM
*, DWORD
);
328 static ModifyWorldTransform_t
ModifyWorldTransform()
330 if ( !ms_worldTransformSymbolsLoaded
)
331 LoadWorldTransformSymbols();
333 return ms_pfnModifyWorldTransform
;
337 static void LoadWorldTransformSymbols()
339 wxDynamicLibrary
dll(wxT("gdi32.dll"));
341 wxDL_INIT_FUNC(ms_pfn
, SetGraphicsMode
, dll
);
342 wxDL_INIT_FUNC(ms_pfn
, SetWorldTransform
, dll
);
343 wxDL_INIT_FUNC(ms_pfn
, GetWorldTransform
, dll
);
344 wxDL_INIT_FUNC(ms_pfn
, ModifyWorldTransform
, dll
);
346 ms_worldTransformSymbolsLoaded
= true;
349 static SetGraphicsMode_t ms_pfnSetGraphicsMode
;
350 static SetWorldTransform_t ms_pfnSetWorldTransform
;
351 static GetWorldTransform_t ms_pfnGetWorldTransform
;
352 static ModifyWorldTransform_t ms_pfnModifyWorldTransform
;
354 static bool ms_worldTransformSymbolsLoaded
;
357 GdiWorldTransformFuncs::SetGraphicsMode_t
358 GdiWorldTransformFuncs::ms_pfnSetGraphicsMode
= NULL
;
359 GdiWorldTransformFuncs::SetWorldTransform_t
360 GdiWorldTransformFuncs::ms_pfnSetWorldTransform
= NULL
;
361 GdiWorldTransformFuncs::GetWorldTransform_t
362 GdiWorldTransformFuncs::ms_pfnGetWorldTransform
= NULL
;
363 GdiWorldTransformFuncs::ModifyWorldTransform_t
364 GdiWorldTransformFuncs::ms_pfnModifyWorldTransform
= NULL
;
366 bool GdiWorldTransformFuncs::ms_worldTransformSymbolsLoaded
= false;
368 #endif // wxUSE_DC_TRANSFORM_MATRIX
370 } // anonymous namespace
372 #endif // wxUSE_DYNLIB_CLASS
374 // ===========================================================================
376 // ===========================================================================
378 // ----------------------------------------------------------------------------
379 // wxBrushAttrsSetter
380 // ----------------------------------------------------------------------------
382 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
383 : wxBkModeChanger(GetHdcOf(dc
)),
384 wxTextColoursChanger(GetHdcOf(dc
))
386 const wxBrush
& brush
= dc
.GetBrush();
387 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
389 // note that Windows convention is opposite to wxWidgets one, this is
390 // why text colour becomes the background one and vice versa
391 wxTextColoursChanger::Change(dc
.GetTextBackground(),
392 dc
.GetTextForeground());
394 wxBkModeChanger::Change(dc
.GetBackgroundMode());
398 // ----------------------------------------------------------------------------
399 // wxDC MSW-specific methods
400 // ----------------------------------------------------------------------------
402 WXHDC
wxDC::GetHDC() const
404 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
405 return impl
? impl
->GetHDC() : 0;
408 // ---------------------------------------------------------------------------
410 // ---------------------------------------------------------------------------
412 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
419 wxMSWDCImpl::~wxMSWDCImpl()
423 SelectOldObjects(m_hDC
);
425 // if we own the HDC, we delete it, otherwise we just release it
429 ::DeleteDC(GetHdc());
431 else // we don't own our HDC
435 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
439 // Must have been a wxScreenDC
440 ::ReleaseDC((HWND
) NULL
, GetHdc());
446 // This will select current objects out of the DC,
447 // which is what you have to do before deleting the
449 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
455 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
456 if (m_selectedBitmap
.IsOk())
458 m_selectedBitmap
.SetSelectedInto(NULL
);
464 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
469 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
474 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
481 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
484 #endif // wxUSE_PALETTE
487 m_brush
= wxNullBrush
;
490 m_palette
= wxNullPalette
;
491 #endif // wxUSE_PALETTE
493 m_backgroundBrush
= wxNullBrush
;
494 m_selectedBitmap
= wxNullBitmap
;
497 // ---------------------------------------------------------------------------
499 // ---------------------------------------------------------------------------
501 void wxMSWDCImpl::UpdateClipBox()
506 ::GetClipBox(GetHdc(), &rect
);
508 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
509 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
510 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
511 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
515 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
517 // check if we should try to retrieve the clipping region possibly not set
518 // by our SetClippingRegion() but preset by Windows:this can only happen
519 // when we're associated with an existing HDC usign SetHDC(), see there
520 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
522 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
523 self
->UpdateClipBox();
525 if ( !m_clipX1
&& !m_clipX2
)
526 self
->m_clipping
= false;
529 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
532 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
533 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
535 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
539 // note that we combine the new clipping region with the existing one: this
540 // is compatible with what the other ports do and is the documented
541 // behaviour now (starting with 2.3.3)
542 #if defined(__WXWINCE__)
544 if ( !::GetClipBox(GetHdc(), &rectClip
) )
547 // GetClipBox returns logical coordinates, so transform to device
548 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
549 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
550 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
551 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
553 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
554 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
555 rectClip
.right
, rectClip
.bottom
);
557 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
559 ::SelectClipRgn(GetHdc(), hrgnDest
);
562 ::DeleteObject(hrgnClipOld
);
563 ::DeleteObject(hrgnDest
);
565 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
567 wxLogLastError(wxT("ExtSelectClipRgn"));
571 #endif // WinCE/!WinCE
578 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
580 // the region coords are always the device ones, so do the translation
583 // FIXME: possible +/-1 error here, to check!
584 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
586 LogicalToDeviceX(x
+ w
),
587 LogicalToDeviceY(y
+ h
));
590 wxLogLastError(wxT("CreateRectRgn"));
594 SetClippingHrgn((WXHRGN
)hrgn
);
596 ::DeleteObject(hrgn
);
600 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
602 SetClippingHrgn(region
.GetHRGN());
605 void wxMSWDCImpl::DestroyClippingRegion()
609 if (m_clipping
&& m_hDC
)
612 // On a PocketPC device (not necessarily emulator), resetting
613 // the clip region as per the old method causes bad display
614 // problems. In fact setting a null region is probably OK
615 // on desktop WIN32 also, since the WIN32 docs imply that the user
616 // clipping region is independent from the paint clipping region.
617 ::SelectClipRgn(GetHdc(), 0);
619 // TODO: this should restore the previous clipping region,
620 // so that OnPaint processing works correctly, and the update
621 // clipping region doesn't get destroyed after the first
622 // DestroyClippingRegion.
623 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
624 ::SelectClipRgn(GetHdc(), rgn
);
629 wxDCImpl::DestroyClippingRegion();
632 // ---------------------------------------------------------------------------
633 // query capabilities
634 // ---------------------------------------------------------------------------
636 bool wxMSWDCImpl::CanDrawBitmap() const
641 bool wxMSWDCImpl::CanGetTextExtent() const
643 #ifdef __WXMICROWIN__
644 // TODO Extend MicroWindows' GetDeviceCaps function
647 // What sort of display is it?
648 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
650 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
654 int wxMSWDCImpl::GetDepth() const
656 WXMICROWIN_CHECK_HDC_RET(16)
658 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
661 // ---------------------------------------------------------------------------
663 // ---------------------------------------------------------------------------
665 void wxMSWDCImpl::Clear()
672 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
676 // No, I think we should simply ignore this if printing on e.g.
678 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
679 if (!m_selectedBitmap
.IsOk())
682 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
683 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
684 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
688 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
691 DWORD colour
= ::GetBkColor(GetHdc());
692 HBRUSH brush
= ::CreateSolidBrush(colour
);
693 ::FillRect(GetHdc(), &rect
, brush
);
694 ::DeleteObject(brush
);
696 RealizeScaleAndOrigin();
699 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
700 wxCoord
WXUNUSED_IN_WINCE(y
),
701 const wxColour
& WXUNUSED_IN_WINCE(col
),
702 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
707 WXMICROWIN_CHECK_HDC_RET(false)
709 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
711 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
712 : FLOODFILLBORDER
) ) ;
715 // quoting from the MSDN docs:
717 // Following are some of the reasons this function might fail:
719 // * The filling could not be completed.
720 // * The specified point has the boundary color specified by the
721 // crColor parameter (if FLOODFILLBORDER was requested).
722 // * The specified point does not have the color specified by
723 // crColor (if FLOODFILLSURFACE was requested)
724 // * The point is outside the clipping region that is, it is not
725 // visible on the device.
727 wxLogLastError(wxT("ExtFloodFill"));
730 CalcBoundingBox(x
, y
);
736 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
738 WXMICROWIN_CHECK_HDC_RET(false)
740 wxCHECK_MSG( col
, false, wxT("NULL colour parameter in wxMSWDCImpl::GetPixel") );
742 // get the color of the pixel
743 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
745 wxRGBToColour(*col
, pixelcolor
);
750 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
754 wxCoord x1
= x
-VIEWPORT_EXTENT
;
755 wxCoord y1
= y
-VIEWPORT_EXTENT
;
756 wxCoord x2
= x
+VIEWPORT_EXTENT
;
757 wxCoord y2
= y
+VIEWPORT_EXTENT
;
759 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
760 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
762 CalcBoundingBox(x1
, y1
);
763 CalcBoundingBox(x2
, y2
);
766 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
770 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
772 CalcBoundingBox(x1
, y1
);
773 CalcBoundingBox(x2
, y2
);
776 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
777 // and ending at (x2, y2)
778 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
779 wxCoord x2
, wxCoord y2
,
780 wxCoord xc
, wxCoord yc
)
784 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
788 // Slower emulation since WinCE doesn't support Pie and Arc
789 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
791 sa
= -sa
; // below center
792 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
793 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
798 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
800 // treat the special case of full circle separately
801 if ( x1
== x2
&& y1
== y2
)
803 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
807 wxCoord xx1
= XLOG2DEV(x1
);
808 wxCoord yy1
= YLOG2DEV(y1
);
809 wxCoord xx2
= XLOG2DEV(x2
);
810 wxCoord yy2
= YLOG2DEV(y2
);
811 wxCoord xxc
= XLOG2DEV(xc
);
812 wxCoord yyc
= YLOG2DEV(yc
);
815 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
817 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
818 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
819 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
820 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
822 if ( m_brush
.IsNonTransparent() )
824 // Have to add 1 to bottom-right corner of rectangle
825 // to make semi-circles look right (crooked line otherwise).
826 // Unfortunately this is not a reliable method, depends
827 // on the size of shape.
828 // TODO: figure out why this happens!
829 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
833 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
836 CalcBoundingBox(xc
- r
, yc
- r
);
837 CalcBoundingBox(xc
+ r
, yc
+ r
);
841 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
842 wxCoord width
, wxCoord height
)
844 // cases when we don't have DrawFrameControl()
845 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
846 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
848 wxCoord x2
= x1
+ width
,
858 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
860 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
863 CalcBoundingBox(x1
, y1
);
864 CalcBoundingBox(x2
, y2
);
865 #endif // Microwin/Normal
868 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
872 COLORREF color
= 0x00ffffff;
875 color
= m_pen
.GetColour().GetPixel();
878 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
880 CalcBoundingBox(x
, y
);
883 void wxMSWDCImpl::DoDrawPolygon(int n
,
887 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
891 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
893 // Do things less efficiently if we have offsets
894 if (xoffset
!= 0 || yoffset
!= 0)
896 POINT
*cpoints
= new POINT
[n
];
898 for (i
= 0; i
< n
; i
++)
900 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
901 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
903 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
906 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
908 (void)Polygon(GetHdc(), cpoints
, n
);
910 SetPolyFillMode(GetHdc(),prev
);
917 for (i
= 0; i
< n
; i
++)
918 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
921 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
923 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
925 SetPolyFillMode(GetHdc(),prev
);
931 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
936 wxPolygonFillMode fillStyle
)
939 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
943 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
945 for (i
= cnt
= 0; i
< n
; i
++)
948 // Do things less efficiently if we have offsets
949 if (xoffset
!= 0 || yoffset
!= 0)
951 POINT
*cpoints
= new POINT
[cnt
];
952 for (i
= 0; i
< cnt
; i
++)
954 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
955 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
957 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
960 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
962 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
964 SetPolyFillMode(GetHdc(),prev
);
970 for (i
= 0; i
< cnt
; i
++)
971 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
974 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
976 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
978 SetPolyFillMode(GetHdc(),prev
);
985 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
989 // Do things less efficiently if we have offsets
990 if (xoffset
!= 0 || yoffset
!= 0)
992 POINT
*cpoints
= new POINT
[n
];
994 for (i
= 0; i
< n
; i
++)
996 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
997 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
999 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
1001 (void)Polyline(GetHdc(), cpoints
, n
);
1007 for (i
= 0; i
< n
; i
++)
1008 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
1010 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
1014 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1016 WXMICROWIN_CHECK_HDC
1018 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1020 wxCoord x2
= x
+ width
;
1021 wxCoord y2
= y
+ height
;
1023 wxCoord x2dev
= XLOG2DEV(x2
),
1024 y2dev
= YLOG2DEV(y2
);
1026 // Windows (but not Windows CE) draws the filled rectangles without outline
1027 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1028 // and we want them to have the same size regardless of which pen is used
1030 if ( m_pen
.IsTransparent() )
1035 #endif // !__WXWINCE__
1037 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
1039 CalcBoundingBox(x
, y
);
1040 CalcBoundingBox(x2
, y2
);
1043 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
1045 WXMICROWIN_CHECK_HDC
1047 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1049 // Now, a negative radius value is interpreted to mean
1050 // 'the proportion of the smallest X or Y dimension'
1054 double smallest
= (width
< height
) ? width
: height
;
1055 radius
= (- radius
* smallest
);
1058 wxCoord x2
= (x
+width
);
1059 wxCoord y2
= (y
+height
);
1061 // Windows draws the filled rectangles without outline (i.e. drawn with a
1062 // transparent pen) one pixel smaller in both directions and we want them
1063 // to have the same size regardless of which pen is used - adjust
1064 if ( m_pen
.IsTransparent() )
1070 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1071 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1073 CalcBoundingBox(x
, y
);
1074 CalcBoundingBox(x2
, y2
);
1077 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1079 WXMICROWIN_CHECK_HDC
1081 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1083 // +1 below makes the ellipse more similar to other platforms.
1084 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1085 wxCoord x2
= x
+ width
+ 1;
1086 wxCoord y2
= y
+ height
+ 1;
1088 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1089 // pen doesn't draw anything. Should we provide a workaround?
1091 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1093 CalcBoundingBox(x
, y
);
1094 CalcBoundingBox(x2
, y2
);
1097 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1098 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1100 // quadratic b-spline to cubic bezier spline conversion
1102 // quadratic spline with control points P0,P1,P2
1103 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1105 // bezier spline with control points B0,B1,B2,B3
1106 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1108 // control points of bezier spline calculated from b-spline
1110 // B1 = (2*P1 + P0)/3
1111 // B2 = (2*P1 + P2)/3
1114 WXMICROWIN_CHECK_HDC
1116 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1118 const size_t n_points
= points
->GetCount();
1119 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1121 const size_t n_bezier_points
= n_points
* 3 + 1;
1122 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1123 size_t bezier_pos
= 0;
1124 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1126 wxPointList::compatibility_iterator node
= points
->GetFirst();
1127 wxPoint
*p
= node
->GetData();
1128 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1129 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1131 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1134 node
= node
->GetNext();
1135 p
= node
->GetData();
1139 cx1
= ( x1
+ x2
) / 2;
1140 cy1
= ( y1
+ y2
) / 2;
1141 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1142 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1144 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1147 #if !wxUSE_STD_CONTAINERS
1148 while ((node
= node
->GetNext()) != NULL
)
1150 while ((node
= node
->GetNext()))
1151 #endif // !wxUSE_STD_CONTAINERS
1153 p
= (wxPoint
*)node
->GetData();
1158 cx4
= (x1
+ x2
) / 2;
1159 cy4
= (y1
+ y2
) / 2;
1160 // B0 is B3 of previous segment
1162 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1163 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1166 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1167 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1170 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1171 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1177 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1179 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1180 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1182 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1185 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1189 #endif // wxUSE_SPLINES
1191 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1192 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1195 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1198 WXMICROWIN_CHECK_HDC
1200 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1205 int rx1
= XLOG2DEV(x
+w
/2);
1206 int ry1
= YLOG2DEV(y
+h
/2);
1213 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1214 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1215 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1216 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1218 // Swap start and end positions if the end angle is less than the start angle.
1229 // draw pie with NULL_PEN first and then outline otherwise a line is
1230 // drawn from the start and end points to the centre
1231 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1234 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1235 rx1
, ry1
, rx2
, ry2
);
1239 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1240 rx1
, ry1
-1, rx2
, ry2
-1);
1243 ::SelectObject(GetHdc(), hpenOld
);
1245 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1246 rx1
, ry1
, rx2
, ry2
);
1248 CalcBoundingBox(x
, y
);
1249 CalcBoundingBox(x2
, y2
);
1253 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1255 WXMICROWIN_CHECK_HDC
1257 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1260 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1262 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1265 CalcBoundingBox(x
, y
);
1266 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1269 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1271 WXMICROWIN_CHECK_HDC
1273 wxCHECK_RET( bmp
.IsOk(), wxT("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1275 int width
= bmp
.GetWidth(),
1276 height
= bmp
.GetHeight();
1278 HBITMAP hbmpMask
= 0;
1281 HPALETTE oldPal
= 0;
1282 #endif // wxUSE_PALETTE
1284 if ( bmp
.HasAlpha() )
1287 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1289 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1293 SET_STRETCH_BLT_MODE(GetHdc());
1297 wxMask
*mask
= bmp
.GetMask();
1299 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1303 // don't give assert here because this would break existing
1304 // programs - just silently ignore useMask parameter
1311 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1315 #if wxUSE_SYSTEM_OPTIONS
1316 // On some systems, MaskBlt succeeds yet is much much slower
1317 // than the wxWidgets fall-back implementation. So we need
1318 // to be able to switch this on and off at runtime.
1320 // NB: don't query the value of the option every time but do it only
1321 // once as otherwise it can have real (and bad) performance
1322 // implications (see #11172)
1324 s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
1325 if ( s_maskBltAllowed
)
1326 #endif // wxUSE_SYSTEM_OPTIONS
1329 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1330 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1332 wxPalette
*pal
= bmp
.GetPalette();
1333 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1335 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1336 ::RealizePalette(hdcMem
);
1338 #endif // wxUSE_PALETTE
1340 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1343 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1347 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1348 #endif // wxUSE_PALETTE
1350 ::SelectObject(hdcMem
, hOldBitmap
);
1357 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1361 memDC
.SelectObjectAsSource(bmp
);
1363 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1365 memDC
.SelectObject(wxNullBitmap
);
1368 else // no mask, just use BitBlt()
1371 HDC memdc
= ::CreateCompatibleDC( cdc
);
1372 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1374 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1376 wxTextColoursChanger
textCol(GetHdc(), *this);
1379 wxPalette
*pal
= bmp
.GetPalette();
1380 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1382 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1383 ::RealizePalette(memdc
);
1385 #endif // wxUSE_PALETTE
1387 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1388 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1392 ::SelectPalette(memdc
, oldPal
, FALSE
);
1393 #endif // wxUSE_PALETTE
1395 ::SelectObject( memdc
, hOldBitmap
);
1396 ::DeleteDC( memdc
);
1400 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1402 // For compatibility with other ports (notably wxGTK) and because it's
1403 // genuinely useful, we allow passing multiline strings to DrawText().
1404 // However there is no native MSW function to draw them directly so we
1405 // instead reuse the generic DrawLabel() method to render them. Of course,
1406 // DrawLabel() itself will call back to us but with single line strings
1407 // only so there won't be any infinite recursion here.
1408 if ( text
.find('\n') != wxString::npos
)
1410 GetOwner()->DrawLabel(text
, wxRect(x
, y
, 0, 0));
1414 WXMICROWIN_CHECK_HDC
1416 DrawAnyText(text
, x
, y
);
1418 // update the bounding box
1419 CalcBoundingBox(x
, y
);
1422 GetOwner()->GetTextExtent(text
, &w
, &h
);
1423 CalcBoundingBox(x
+ w
, y
+ h
);
1426 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1428 WXMICROWIN_CHECK_HDC
1430 // prepare for drawing the text
1431 wxTextColoursChanger
textCol(GetHdc(), *this);
1433 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1435 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1436 text
.c_str(), text
.length(), NULL
) == 0 )
1438 wxLogLastError(wxT("TextOut"));
1442 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1443 wxCoord x
, wxCoord y
,
1446 WXMICROWIN_CHECK_HDC
1448 // we test that we have some font because otherwise we should still use the
1449 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1450 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1451 // font for drawing rotated fonts unfortunately)
1452 if ( (angle
== 0.0) && m_font
.IsOk() )
1454 DoDrawText(text
, x
, y
);
1456 #ifndef __WXMICROWIN__
1459 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1460 // because it's not TrueType and so can't have non zero
1461 // orientation/escapement under Win9x
1462 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1463 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1465 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1467 wxLogLastError(wxT("GetObject(hfont)"));
1470 // GDI wants the angle in tenth of degree
1471 long angle10
= (long)(angle
* 10);
1472 lf
.lfEscapement
= angle10
;
1473 lf
. lfOrientation
= angle10
;
1475 hfont
= ::CreateFontIndirect(&lf
);
1478 wxLogLastError(wxT("CreateFont"));
1482 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1484 DrawAnyText(text
, x
, y
);
1486 (void)::SelectObject(GetHdc(), hfontOld
);
1487 (void)::DeleteObject(hfont
);
1490 // call the bounding box by adding all four vertices of the rectangle
1491 // containing the text to it (simpler and probably not slower than
1492 // determining which of them is really topmost/leftmost/...)
1494 GetOwner()->GetTextExtent(text
, &w
, &h
);
1496 double rad
= DegToRad(angle
);
1498 // "upper left" and "upper right"
1499 CalcBoundingBox(x
, y
);
1500 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1502 // "bottom left" and "bottom right"
1503 x
+= (wxCoord
)(h
*sin(rad
));
1504 y
+= (wxCoord
)(h
*cos(rad
));
1505 CalcBoundingBox(x
, y
);
1506 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1511 // ---------------------------------------------------------------------------
1513 // ---------------------------------------------------------------------------
1517 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1519 WXMICROWIN_CHECK_HDC
1521 // Set the old object temporarily, in case the assignment deletes an object
1522 // that's not yet selected out.
1525 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1529 if ( m_palette
.IsOk() )
1531 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1532 GetHpaletteOf(m_palette
),
1535 m_oldPalette
= (WXHPALETTE
) oldPal
;
1538 ::RealizePalette(GetHdc());
1542 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1544 if ( palette
.IsOk() )
1546 m_palette
= palette
;
1547 DoSelectPalette(true);
1551 void wxMSWDCImpl::InitializePalette()
1553 if ( wxDisplayDepth() <= 8 )
1555 // look for any window or parent that has a custom palette. If any has
1556 // one then we need to use it in drawing operations
1557 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1559 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1560 if ( m_hasCustomPalette
)
1562 m_palette
= win
->GetPalette();
1564 // turn on MSW translation for this palette
1570 #endif // wxUSE_PALETTE
1572 // SetFont/Pen/Brush() really ask to be implemented as a single template
1573 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1575 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1577 WXMICROWIN_CHECK_HDC
1579 if ( font
== m_font
)
1584 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1585 if ( hfont
== HGDI_ERROR
)
1587 wxLogLastError(wxT("SelectObject(font)"));
1592 m_oldFont
= (WXHFONT
)hfont
;
1597 else // invalid font, reset the current font
1601 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1603 wxLogLastError(wxT("SelectObject(old font)"));
1609 m_font
= wxNullFont
;
1613 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1615 WXMICROWIN_CHECK_HDC
1622 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1623 if ( hpen
== HGDI_ERROR
)
1625 wxLogLastError(wxT("SelectObject(pen)"));
1630 m_oldPen
= (WXHPEN
)hpen
;
1635 else // invalid pen, reset the current pen
1639 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1641 wxLogLastError(wxT("SelectObject(old pen)"));
1651 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1653 WXMICROWIN_CHECK_HDC
1655 if ( brush
== m_brush
)
1660 // we must make sure the brush is aligned with the logical coordinates
1661 // before selecting it or using the same brush for the background of
1662 // different windows would result in discontinuities
1663 wxSize sizeBrushBitmap
= wxDefaultSize
;
1664 wxBitmap
*stipple
= brush
.GetStipple();
1665 if ( stipple
&& stipple
->IsOk() )
1666 sizeBrushBitmap
= stipple
->GetSize();
1667 else if ( brush
.IsHatch() )
1668 sizeBrushBitmap
= wxSize(8, 8);
1670 if ( sizeBrushBitmap
.IsFullySpecified() )
1672 if ( !::SetBrushOrgEx
1675 m_deviceOriginX
% sizeBrushBitmap
.x
,
1676 m_deviceOriginY
% sizeBrushBitmap
.y
,
1677 NULL
// [out] previous brush origin
1680 wxLogLastError(wxT("SetBrushOrgEx()"));
1684 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1685 if ( hbrush
== HGDI_ERROR
)
1687 wxLogLastError(wxT("SelectObject(brush)"));
1692 m_oldBrush
= (WXHBRUSH
)hbrush
;
1697 else // invalid brush, reset the current brush
1701 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1703 wxLogLastError(wxT("SelectObject(old brush)"));
1709 m_brush
= wxNullBrush
;
1713 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1715 WXMICROWIN_CHECK_HDC
1717 m_backgroundBrush
= brush
;
1719 if ( m_backgroundBrush
.IsOk() )
1721 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1725 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1727 WXMICROWIN_CHECK_HDC
1729 m_backgroundMode
= mode
;
1731 // SetBackgroundColour now only refers to text background
1732 // and m_backgroundMode is used there
1735 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1737 WXMICROWIN_CHECK_HDC
1739 m_logicalFunction
= function
;
1744 void wxMSWDCImpl::SetRop(WXHDC dc
)
1746 if ( !dc
|| m_logicalFunction
< 0 )
1751 switch (m_logicalFunction
)
1753 case wxCLEAR
: rop
= R2_BLACK
; break;
1754 case wxXOR
: rop
= R2_XORPEN
; break;
1755 case wxINVERT
: rop
= R2_NOT
; break;
1756 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1757 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1758 case wxCOPY
: rop
= R2_COPYPEN
; break;
1759 case wxAND
: rop
= R2_MASKPEN
; break;
1760 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1761 case wxNO_OP
: rop
= R2_NOP
; break;
1762 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1763 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1764 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1765 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1766 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1767 case wxOR
: rop
= R2_MERGEPEN
; break;
1768 case wxSET
: rop
= R2_WHITE
; break;
1770 wxFAIL_MSG( wxS("unknown logical function") );
1774 SetROP2(GetHdc(), rop
);
1777 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1779 // We might be previewing, so return true to let it continue.
1783 void wxMSWDCImpl::EndDoc()
1787 void wxMSWDCImpl::StartPage()
1791 void wxMSWDCImpl::EndPage()
1795 // ---------------------------------------------------------------------------
1797 // ---------------------------------------------------------------------------
1799 wxCoord
wxMSWDCImpl::GetCharHeight() const
1801 WXMICROWIN_CHECK_HDC_RET(0)
1803 TEXTMETRIC lpTextMetric
;
1805 GetTextMetrics(GetHdc(), &lpTextMetric
);
1807 return lpTextMetric
.tmHeight
;
1810 wxCoord
wxMSWDCImpl::GetCharWidth() const
1812 WXMICROWIN_CHECK_HDC_RET(0)
1814 TEXTMETRIC lpTextMetric
;
1816 GetTextMetrics(GetHdc(), &lpTextMetric
);
1818 return lpTextMetric
.tmAveCharWidth
;
1821 void wxMSWDCImpl::DoGetFontMetrics(int *height
,
1824 int *internalLeading
,
1825 int *externalLeading
,
1826 int *averageWidth
) const
1830 GetTextMetrics(GetHdc(), &tm
);
1833 *height
= tm
.tmHeight
;
1835 *ascent
= tm
.tmAscent
;
1837 *descent
= tm
.tmDescent
;
1838 if ( internalLeading
)
1839 *internalLeading
= tm
.tmInternalLeading
;
1840 if ( externalLeading
)
1841 *externalLeading
= tm
.tmExternalLeading
;
1843 *averageWidth
= tm
.tmAveCharWidth
;
1846 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1847 wxCoord
*descent
, wxCoord
*externalLeading
,
1848 const wxFont
*font
) const
1850 #ifdef __WXMICROWIN__
1855 if (descent
) *descent
= 0;
1856 if (externalLeading
) *externalLeading
= 0;
1859 #endif // __WXMICROWIN__
1861 wxASSERT_MSG( !font
|| font
->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") );
1863 wxTextMeasure
txm(GetOwner(), font
);
1864 txm
.GetTextExtent(string
, x
, y
, descent
, externalLeading
);
1868 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1870 wxTextMeasure
txm(GetOwner(), NULL
); // don't change the font
1871 return txm
.GetPartialTextExtents(text
, widths
, 1.0);
1877 void ApplyEffectiveScale(double scale
, int sign
, int *device
, int *logical
)
1879 // To reduce rounding errors as much as possible, we try to use the largest
1880 // possible extent (2^27-1) for the device space but we must also avoid
1881 // overflowing the int range i.e. ensure that logical extents are less than
1882 // 2^31 in magnitude. So the minimal scale we can use is 1/16 as for
1883 // anything smaller VIEWPORT_EXTENT/scale would overflow the int range.
1884 static const double MIN_LOGICAL_SCALE
= 1./16;
1886 double physExtent
= VIEWPORT_EXTENT
;
1887 if ( scale
< MIN_LOGICAL_SCALE
)
1889 physExtent
*= scale
/MIN_LOGICAL_SCALE
;
1890 scale
= MIN_LOGICAL_SCALE
;
1893 *device
= wxRound(physExtent
);
1894 *logical
= sign
*wxRound(VIEWPORT_EXTENT
/scale
);
1897 } // anonymous namespace
1899 void wxMSWDCImpl::RealizeScaleAndOrigin()
1901 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1902 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1903 // noticeable difference between these mapping modes
1905 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1907 // wxWidgets API assumes that the coordinate space is "infinite" (i.e. only
1908 // limited by 2^32 range of the integer coordinates) but in MSW API we must
1909 // actually specify the extents that we use so compute them here.
1911 int devExtX
, devExtY
, // Viewport, i.e. device space, extents.
1912 logExtX
, logExtY
; // Window, i.e. logical coordinate space, extents.
1914 ApplyEffectiveScale(m_scaleX
, m_signX
, &devExtX
, &logExtX
);
1915 ApplyEffectiveScale(m_scaleY
, m_signY
, &devExtY
, &logExtY
);
1917 ::SetViewportExtEx(GetHdc(), devExtX
, devExtY
, NULL
);
1918 ::SetWindowExtEx(GetHdc(), logExtX
, logExtY
, NULL
);
1920 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1921 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1925 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1927 WXMICROWIN_CHECK_HDC
1929 m_mappingMode
= mode
;
1931 if ( mode
== wxMM_TEXT
)
1934 m_logicalScaleY
= 1.0;
1936 else // need to do some calculations
1938 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1939 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1940 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1941 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1943 if ( (mm_width
== 0) || (mm_height
== 0) )
1945 // we can't calculate mm2pixels[XY] then!
1949 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1950 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1955 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1956 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1960 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1961 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1965 m_logicalScaleX
= mm2pixelsX
;
1966 m_logicalScaleY
= mm2pixelsY
;
1970 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1971 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1975 wxFAIL_MSG( wxT("unknown mapping mode in SetMapMode") );
1979 ComputeScaleAndOrigin();
1981 RealizeScaleAndOrigin();
1984 void wxMSWDCImpl::SetUserScale(double x
, double y
)
1986 WXMICROWIN_CHECK_HDC
1988 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1991 wxDCImpl::SetUserScale(x
,y
);
1993 RealizeScaleAndOrigin();
1996 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
1999 WXMICROWIN_CHECK_HDC
2001 int signX
= xLeftRight
? 1 : -1,
2002 signY
= yBottomUp
? -1 : 1;
2004 if (signX
== m_signX
&& signY
== m_signY
)
2007 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2009 RealizeScaleAndOrigin();
2012 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2014 WXMICROWIN_CHECK_HDC
2016 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2019 wxDCImpl::SetLogicalOrigin( x
, y
);
2021 RealizeScaleAndOrigin();
2024 // For use by wxWidgets only, unless custom units are required.
2025 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2027 WXMICROWIN_CHECK_HDC
2029 wxDCImpl::SetLogicalScale(x
,y
);
2032 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2034 WXMICROWIN_CHECK_HDC
2036 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2039 wxDCImpl::SetDeviceOrigin( x
, y
);
2041 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2044 // ----------------------------------------------------------------------------
2046 // ----------------------------------------------------------------------------
2048 #if wxUSE_DC_TRANSFORM_MATRIX
2050 bool wxMSWDCImpl::CanUseTransformMatrix() const
2052 return GdiWorldTransformFuncs::IsOk();
2055 bool wxMSWDCImpl::SetTransformMatrix(const wxAffineMatrix2D
&matrix
)
2057 if ( !GdiWorldTransformFuncs::IsOk() )
2060 if ( matrix
.IsIdentity() )
2062 ResetTransformMatrix();
2066 if ( !GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_ADVANCED
) )
2068 wxLogLastError(wxT("SetGraphicsMode"));
2074 matrix
.Get(&mat
, &tr
);
2077 xform
.eM11
= mat
.m_11
;
2078 xform
.eM12
= mat
.m_12
;
2079 xform
.eM21
= mat
.m_21
;
2080 xform
.eM22
= mat
.m_22
;
2084 if ( !GdiWorldTransformFuncs::SetWorldTransform()(GetHdc(), &xform
) )
2086 wxLogLastError(wxT("SetWorldTransform"));
2093 wxAffineMatrix2D
wxMSWDCImpl::GetTransformMatrix() const
2095 wxAffineMatrix2D transform
;
2097 if ( !GdiWorldTransformFuncs::IsOk() )
2101 if ( !GdiWorldTransformFuncs::GetWorldTransform()(GetHdc(), &xform
) )
2103 wxLogLastError(wxT("GetWorldTransform"));
2107 wxMatrix2D
m(xform
.eM11
, xform
.eM12
, xform
.eM21
, xform
.eM22
);
2108 wxPoint2DDouble
p(xform
.eDx
, xform
.eDy
);
2109 transform
.Set(m
, p
);
2114 void wxMSWDCImpl::ResetTransformMatrix()
2116 if ( GdiWorldTransformFuncs::IsOk() )
2118 GdiWorldTransformFuncs::ModifyWorldTransform()(GetHdc(), NULL
, MWT_IDENTITY
);
2119 GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_COMPATIBLE
);
2123 #endif // wxUSE_DC_TRANSFORM_MATRIX
2125 // ---------------------------------------------------------------------------
2127 // ---------------------------------------------------------------------------
2129 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2130 wxCoord dstWidth
, wxCoord dstHeight
,
2132 wxCoord srcX
, wxCoord srcY
,
2133 wxRasterOperationMode rop
, bool useMask
,
2134 wxCoord srcMaskX
, wxCoord srcMaskY
)
2136 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2139 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2140 wxCoord dstWidth
, wxCoord dstHeight
,
2142 wxCoord xsrc
, wxCoord ysrc
,
2143 wxCoord srcWidth
, wxCoord srcHeight
,
2144 wxRasterOperationMode rop
, bool useMask
,
2145 wxCoord xsrcMask
, wxCoord ysrcMask
)
2147 wxCHECK_MSG( source
, false, wxT("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2149 WXMICROWIN_CHECK_HDC_RET(false)
2151 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2154 // TODO: Do we want to be able to blit from other DCs too?
2158 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2160 // if either the source or destination has alpha channel, we must use
2161 // AlphaBlt() as other function don't handle it correctly
2162 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2163 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2164 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2166 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2167 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2171 wxMask
*mask
= NULL
;
2174 mask
= bmpSrc
.GetMask();
2176 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2178 // don't give assert here because this would break existing
2179 // programs - just silently ignore useMask parameter
2184 if (xsrcMask
== -1 && ysrcMask
== -1)
2186 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2189 wxTextColoursChanger
textCol(GetHdc(), *this);
2194 case wxXOR
: dwRop
= SRCINVERT
; break;
2195 case wxINVERT
: dwRop
= DSTINVERT
; break;
2196 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2197 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2198 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2199 case wxSET
: dwRop
= WHITENESS
; break;
2200 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2201 case wxAND
: dwRop
= SRCAND
; break;
2202 case wxOR
: dwRop
= SRCPAINT
; break;
2203 case wxEQUIV
: dwRop
= 0x00990066; break;
2204 case wxNAND
: dwRop
= 0x007700E6; break;
2205 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2206 case wxCOPY
: dwRop
= SRCCOPY
; break;
2207 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2208 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2209 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2211 wxFAIL_MSG( wxT("unsupported logical function") );
2215 bool success
= false;
2220 // we want the part of the image corresponding to the mask to be
2221 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2222 // meaning of fg and bg is inverted which corresponds to wxWin notion
2223 // of the mask which is also contrary to the Windows one)
2225 // On some systems, MaskBlt succeeds yet is much much slower
2226 // than the wxWidgets fall-back implementation. So we need
2227 // to be able to switch this on and off at runtime.
2228 #if wxUSE_SYSTEM_OPTIONS
2229 static bool s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
2230 if ( s_maskBltAllowed
)
2233 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2238 xdest
, ydest
, dstWidth
, dstHeight
,
2241 (HBITMAP
)mask
->GetMaskBitmap(),
2243 MAKEROP4(dwRop
, DSTCOPY
)
2251 // Blit bitmap with mask
2254 HBITMAP buffer_bmap
;
2256 #if wxUSE_DC_CACHEING
2257 // create a temp buffer bitmap and DCs to access it and the mask
2258 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2259 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2261 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2262 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2264 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2265 dstWidth
, dstHeight
);
2267 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2268 #else // !wxUSE_DC_CACHEING
2269 // create a temp buffer bitmap and DCs to access it and the mask
2270 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2271 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2272 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2273 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2274 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2275 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2277 // copy dest to buffer
2278 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2279 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2281 wxLogLastError(wxT("BitBlt"));
2284 SET_STRETCH_BLT_MODE(GetHdc());
2286 // copy src to buffer using selected raster op
2287 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2288 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2290 wxLogLastError(wxT("StretchBlt"));
2293 // set masked area in buffer to BLACK
2295 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2296 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2297 dc_mask
, xsrcMask
, ysrcMask
,
2298 srcWidth
, srcHeight
, SRCAND
) )
2300 wxLogLastError(wxT("StretchBlt"));
2303 // set unmasked area in dest to BLACK
2304 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2305 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2306 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2307 dc_mask
, xsrcMask
, ysrcMask
,
2308 srcWidth
, srcHeight
, SRCAND
) )
2310 wxLogLastError(wxT("StretchBlt"));
2312 } // restore the original text and background colours
2314 // OR buffer to dest
2315 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2316 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2319 wxLogLastError(wxT("BitBlt"));
2322 // tidy up temporary DCs and bitmap
2323 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2324 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2326 #if !wxUSE_DC_CACHEING
2328 ::DeleteDC(dc_mask
);
2329 ::DeleteDC(dc_buffer
);
2330 ::DeleteObject(buffer_bmap
);
2335 else // no mask, just BitBlt() it
2337 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2338 // use StretchBlt() if available and finally fall back to BitBlt()
2340 // FIXME: use appropriate WinCE functions
2342 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2343 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2348 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2350 &ds
) == sizeof(ds
) )
2352 SET_STRETCH_BLT_MODE(GetHdc());
2354 // Unlike all the other functions used here (i.e. AlphaBlt(),
2355 // MaskBlt(), BitBlt() and StretchBlt()), StretchDIBits() does
2356 // not take into account the source DC logical coordinates
2357 // automatically as it doesn't even work with the source HDC.
2358 // So do this manually to ensure that the coordinates are
2359 // interpreted in the same way here as in all the other cases.
2360 xsrc
= source
->LogicalToDeviceX(xsrc
);
2361 ysrc
= source
->LogicalToDeviceY(ysrc
);
2362 srcWidth
= source
->LogicalToDeviceXRel(srcWidth
);
2363 srcHeight
= source
->LogicalToDeviceYRel(srcHeight
);
2365 // Figure out what co-ordinate system we're supposed to specify
2367 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2371 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2374 if ( ::StretchDIBits(GetHdc(),
2376 dstWidth
, dstHeight
,
2378 srcWidth
, srcHeight
,
2380 (LPBITMAPINFO
)&ds
.dsBmih
,
2383 ) == (int)GDI_ERROR
)
2385 // On Win9x this API fails most (all?) of the time, so
2386 // logging it becomes quite distracting. Since it falls
2387 // back to the code below this is not really serious, so
2389 //wxLogLastError(wxT("StretchDIBits"));
2398 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2402 SET_STRETCH_BLT_MODE(GetHdc());
2407 xdest
, ydest
, dstWidth
, dstHeight
,
2409 xsrc
, ysrc
, srcWidth
, srcHeight
,
2413 wxLogLastError(wxT("StretchBlt"));
2423 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2424 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2426 wxLogLastError(wxT("BitBlt"));
2438 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2440 WXMICROWIN_CHECK_HDC
2443 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2445 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2448 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2450 WXMICROWIN_CHECK_HDC
2452 // if we implement it in terms of DoGetSize() instead of directly using the
2453 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2454 // will also work for wxWindowDC and wxClientDC even though their size is
2455 // not the same as the total size of the screen
2456 int wPixels
, hPixels
;
2457 DoGetSize(&wPixels
, &hPixels
);
2461 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2463 wxCHECK_RET( wTotal
, wxT("0 width device?") );
2465 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2470 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2472 wxCHECK_RET( hTotal
, wxT("0 height device?") );
2474 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2478 wxSize
wxMSWDCImpl::GetPPI() const
2480 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2482 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2483 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2485 return wxSize(x
, y
);
2488 // ----------------------------------------------------------------------------
2490 // ----------------------------------------------------------------------------
2492 #if wxUSE_DC_CACHEING
2495 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2496 * improve it in due course, either using arrays, or simply storing pointers to one
2497 * entry for the bitmap, and two for the DCs. -- JACS
2500 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2501 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2503 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2512 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2521 wxDCCacheEntry::~wxDCCacheEntry()
2524 ::DeleteObject((HBITMAP
) m_bitmap
);
2526 ::DeleteDC((HDC
) m_dc
);
2529 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2531 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2532 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2535 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2537 if (entry
->m_depth
== depth
)
2539 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2541 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2542 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2543 if ( !entry
->m_bitmap
)
2545 wxLogLastError(wxT("CreateCompatibleBitmap"));
2547 entry
->m_width
= w
; entry
->m_height
= h
;
2553 node
= node
->GetNext();
2555 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2558 wxLogLastError(wxT("CreateCompatibleBitmap"));
2560 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2561 AddToBitmapCache(entry
);
2565 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2567 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2568 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2571 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2573 // Don't return the same one as we already have
2574 if (!notThis
|| (notThis
!= entry
))
2576 if (entry
->m_depth
== depth
)
2582 node
= node
->GetNext();
2584 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2587 wxLogLastError(wxT("CreateCompatibleDC"));
2589 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2590 AddToDCCache(entry
);
2594 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2596 sm_bitmapCache
.Append(entry
);
2599 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2601 sm_dcCache
.Append(entry
);
2604 void wxMSWDCImpl::ClearCache()
2606 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2607 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2610 // Clean up cache at app exit
2611 class wxDCModule
: public wxModule
2614 virtual bool OnInit() { return true; }
2615 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2618 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2621 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2623 #endif // wxUSE_DC_CACHEING
2625 // ----------------------------------------------------------------------------
2626 // alpha channel support
2627 // ----------------------------------------------------------------------------
2629 static bool AlphaBlt(HDC hdcDst
,
2630 int x
, int y
, int dstWidth
, int dstHeight
,
2632 int srcWidth
, int srcHeight
,
2634 const wxBitmap
& bmp
)
2636 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2637 wxASSERT_MSG( hdcDst
&& hdcSrc
, wxT("AlphaBlt(): invalid HDC") );
2639 // do we have AlphaBlend() and company in the headers?
2640 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2641 // yes, now try to see if we have it during run-time
2642 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2643 HDC
,int,int,int,int,
2647 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(wxT("AlphaBlend"));
2648 if ( pfnAlphaBlend
)
2651 bf
.BlendOp
= AC_SRC_OVER
;
2653 bf
.SourceConstantAlpha
= 0xff;
2654 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2656 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2657 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2660 // skip wxAlphaBlend() call below
2664 wxLogLastError(wxT("AlphaBlend"));
2667 wxUnusedVar(hdcSrc
);
2668 #endif // defined(AC_SRC_OVER)
2670 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2672 #ifdef wxHAS_RAW_BITMAP
2673 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2676 #else // !wxHAS_RAW_BITMAP
2677 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2678 // alpha but at least something will be shown like this)
2681 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2685 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2686 #ifdef wxHAS_RAW_BITMAP
2689 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2690 int dstWidth
, int dstHeight
,
2692 int srcWidth
, int srcHeight
,
2693 const wxBitmap
& bmpSrc
)
2695 // get the destination DC pixels
2696 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2698 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2700 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2702 wxLogLastError(wxT("BitBlt"));
2705 // combine them with the source bitmap using alpha
2706 wxAlphaPixelData
dataDst(bmpDst
),
2707 dataSrc((wxBitmap
&)bmpSrc
);
2709 wxCHECK_RET( dataDst
&& dataSrc
,
2710 wxT("failed to get raw data in wxAlphaBlend") );
2712 wxAlphaPixelData::Iterator
pDst(dataDst
),
2716 for ( int y
= 0; y
< dstHeight
; y
++ )
2718 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2720 for ( int x
= 0; x
< dstWidth
; x
++ )
2722 // source is point sampled, Alpha StretchBlit is ugly on Win95
2723 // (but does not impact performance)
2724 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2726 // note that source bitmap uses premultiplied alpha (as required by
2727 // the real AlphaBlend)
2728 const unsigned beta
= 255 - pSrc
.Alpha();
2730 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2731 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2732 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2737 pDst
= pDstRowStart
;
2738 pDst
.OffsetY(dataDst
, 1);
2741 // and finally blit them back to the destination DC
2742 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2744 wxLogLastError(wxT("BitBlt"));
2748 #endif // wxHAS_RAW_BITMAP
2750 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2751 const wxColour
& initialColour
,
2752 const wxColour
& destColour
,
2753 wxDirection nDirection
)
2755 // use native function if we have compile-time support it and can load it
2756 // during run-time (linking to it statically would make the program
2757 // unusable on earlier Windows versions)
2758 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2760 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2761 static GradientFill_t pfnGradientFill
=
2762 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(wxT("GradientFill"));
2764 if ( pfnGradientFill
)
2766 GRADIENT_RECT grect
;
2767 grect
.UpperLeft
= 0;
2768 grect
.LowerRight
= 1;
2770 // invert colours direction if not filling from left-to-right or
2772 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2774 // one vertex for upper left and one for upper-right
2775 TRIVERTEX vertices
[2];
2777 vertices
[0].x
= rect
.GetLeft();
2778 vertices
[0].y
= rect
.GetTop();
2779 vertices
[1].x
= rect
.GetRight()+1;
2780 vertices
[1].y
= rect
.GetBottom()+1;
2782 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2783 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2784 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2785 vertices
[firstVertex
].Alpha
= 0;
2786 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2787 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2788 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2789 vertices
[1 - firstVertex
].Alpha
= 0;
2791 if ( (*pfnGradientFill
)
2798 nDirection
== wxWEST
|| nDirection
== wxEAST
2799 ? GRADIENT_FILL_RECT_H
2800 : GRADIENT_FILL_RECT_V
2803 // skip call of the base class version below
2807 wxLogLastError(wxT("GradientFill"));
2809 #endif // wxUSE_DYNLIB_CLASS
2811 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2814 #if wxUSE_DYNLIB_CLASS
2816 static DWORD
wxGetDCLayout(HDC hdc
)
2818 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2820 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2822 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2825 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2827 DWORD layout
= wxGetDCLayout(GetHdc());
2829 if ( layout
== (DWORD
)-1 )
2830 return wxLayout_Default
;
2832 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2835 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2837 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2839 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2840 if ( !s_pfnSetLayout
)
2843 if ( dir
== wxLayout_Default
)
2845 dir
= wxTheApp
->GetLayoutDirection();
2846 if ( dir
== wxLayout_Default
)
2850 DWORD layout
= wxGetDCLayout(GetHdc());
2851 if ( dir
== wxLayout_RightToLeft
)
2852 layout
|= LAYOUT_RTL
;
2854 layout
&= ~LAYOUT_RTL
;
2856 s_pfnSetLayout(GetHdc(), layout
);
2859 #else // !wxUSE_DYNLIB_CLASS
2861 // we can't provide RTL support without dynamic loading, so stub it out
2862 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2864 return wxLayout_Default
;
2867 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2871 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS