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 wxCHECK_MSG( GetFont().IsOk(), false, wxT("Invalid font") );
1872 wxTextMeasure
txm(GetOwner(), NULL
); // don't change the font
1873 return txm
.GetPartialTextExtents(text
, widths
, 1.0);
1879 void ApplyEffectiveScale(double scale
, int sign
, int *device
, int *logical
)
1881 // To reduce rounding errors as much as possible, we try to use the largest
1882 // possible extent (2^27-1) for the device space but we must also avoid
1883 // overflowing the int range i.e. ensure that logical extents are less than
1884 // 2^31 in magnitude. So the minimal scale we can use is 1/16 as for
1885 // anything smaller VIEWPORT_EXTENT/scale would overflow the int range.
1886 static const double MIN_LOGICAL_SCALE
= 1./16;
1888 double physExtent
= VIEWPORT_EXTENT
;
1889 if ( scale
< MIN_LOGICAL_SCALE
)
1891 physExtent
*= scale
/MIN_LOGICAL_SCALE
;
1892 scale
= MIN_LOGICAL_SCALE
;
1895 *device
= wxRound(physExtent
);
1896 *logical
= sign
*wxRound(VIEWPORT_EXTENT
/scale
);
1899 } // anonymous namespace
1901 void wxMSWDCImpl::RealizeScaleAndOrigin()
1903 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1904 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1905 // noticeable difference between these mapping modes
1907 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1909 // wxWidgets API assumes that the coordinate space is "infinite" (i.e. only
1910 // limited by 2^32 range of the integer coordinates) but in MSW API we must
1911 // actually specify the extents that we use so compute them here.
1913 int devExtX
, devExtY
, // Viewport, i.e. device space, extents.
1914 logExtX
, logExtY
; // Window, i.e. logical coordinate space, extents.
1916 ApplyEffectiveScale(m_scaleX
, m_signX
, &devExtX
, &logExtX
);
1917 ApplyEffectiveScale(m_scaleY
, m_signY
, &devExtY
, &logExtY
);
1919 ::SetViewportExtEx(GetHdc(), devExtX
, devExtY
, NULL
);
1920 ::SetWindowExtEx(GetHdc(), logExtX
, logExtY
, NULL
);
1922 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1923 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1927 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1929 WXMICROWIN_CHECK_HDC
1931 m_mappingMode
= mode
;
1933 if ( mode
== wxMM_TEXT
)
1936 m_logicalScaleY
= 1.0;
1938 else // need to do some calculations
1940 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1941 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1942 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1943 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1945 if ( (mm_width
== 0) || (mm_height
== 0) )
1947 // we can't calculate mm2pixels[XY] then!
1951 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1952 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1957 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1958 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1962 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1963 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1967 m_logicalScaleX
= mm2pixelsX
;
1968 m_logicalScaleY
= mm2pixelsY
;
1972 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1973 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1977 wxFAIL_MSG( wxT("unknown mapping mode in SetMapMode") );
1981 ComputeScaleAndOrigin();
1983 RealizeScaleAndOrigin();
1986 void wxMSWDCImpl::SetUserScale(double x
, double y
)
1988 WXMICROWIN_CHECK_HDC
1990 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1993 wxDCImpl::SetUserScale(x
,y
);
1995 RealizeScaleAndOrigin();
1998 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2001 WXMICROWIN_CHECK_HDC
2003 int signX
= xLeftRight
? 1 : -1,
2004 signY
= yBottomUp
? -1 : 1;
2006 if (signX
== m_signX
&& signY
== m_signY
)
2009 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2011 RealizeScaleAndOrigin();
2014 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2016 WXMICROWIN_CHECK_HDC
2018 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2021 wxDCImpl::SetLogicalOrigin( x
, y
);
2023 RealizeScaleAndOrigin();
2026 // For use by wxWidgets only, unless custom units are required.
2027 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2029 WXMICROWIN_CHECK_HDC
2031 wxDCImpl::SetLogicalScale(x
,y
);
2034 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2036 WXMICROWIN_CHECK_HDC
2038 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2041 wxDCImpl::SetDeviceOrigin( x
, y
);
2043 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2046 // ----------------------------------------------------------------------------
2048 // ----------------------------------------------------------------------------
2050 #if wxUSE_DC_TRANSFORM_MATRIX
2052 bool wxMSWDCImpl::CanUseTransformMatrix() const
2054 return GdiWorldTransformFuncs::IsOk();
2057 bool wxMSWDCImpl::SetTransformMatrix(const wxAffineMatrix2D
&matrix
)
2059 if ( !GdiWorldTransformFuncs::IsOk() )
2062 if ( matrix
.IsIdentity() )
2064 ResetTransformMatrix();
2068 if ( !GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_ADVANCED
) )
2070 wxLogLastError(wxT("SetGraphicsMode"));
2076 matrix
.Get(&mat
, &tr
);
2079 xform
.eM11
= mat
.m_11
;
2080 xform
.eM12
= mat
.m_12
;
2081 xform
.eM21
= mat
.m_21
;
2082 xform
.eM22
= mat
.m_22
;
2086 if ( !GdiWorldTransformFuncs::SetWorldTransform()(GetHdc(), &xform
) )
2088 wxLogLastError(wxT("SetWorldTransform"));
2095 wxAffineMatrix2D
wxMSWDCImpl::GetTransformMatrix() const
2097 wxAffineMatrix2D transform
;
2099 if ( !GdiWorldTransformFuncs::IsOk() )
2103 if ( !GdiWorldTransformFuncs::GetWorldTransform()(GetHdc(), &xform
) )
2105 wxLogLastError(wxT("GetWorldTransform"));
2109 wxMatrix2D
m(xform
.eM11
, xform
.eM12
, xform
.eM21
, xform
.eM22
);
2110 wxPoint2DDouble
p(xform
.eDx
, xform
.eDy
);
2111 transform
.Set(m
, p
);
2116 void wxMSWDCImpl::ResetTransformMatrix()
2118 if ( GdiWorldTransformFuncs::IsOk() )
2120 GdiWorldTransformFuncs::ModifyWorldTransform()(GetHdc(), NULL
, MWT_IDENTITY
);
2121 GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_COMPATIBLE
);
2125 #endif // wxUSE_DC_TRANSFORM_MATRIX
2127 // ---------------------------------------------------------------------------
2129 // ---------------------------------------------------------------------------
2131 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2132 wxCoord dstWidth
, wxCoord dstHeight
,
2134 wxCoord srcX
, wxCoord srcY
,
2135 wxRasterOperationMode rop
, bool useMask
,
2136 wxCoord srcMaskX
, wxCoord srcMaskY
)
2138 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2141 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2142 wxCoord dstWidth
, wxCoord dstHeight
,
2144 wxCoord xsrc
, wxCoord ysrc
,
2145 wxCoord srcWidth
, wxCoord srcHeight
,
2146 wxRasterOperationMode rop
, bool useMask
,
2147 wxCoord xsrcMask
, wxCoord ysrcMask
)
2149 wxCHECK_MSG( source
, false, wxT("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2151 WXMICROWIN_CHECK_HDC_RET(false)
2153 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2156 // TODO: Do we want to be able to blit from other DCs too?
2160 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2162 // if either the source or destination has alpha channel, we must use
2163 // AlphaBlt() as other function don't handle it correctly
2164 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2165 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2166 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2168 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2169 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2173 wxMask
*mask
= NULL
;
2176 mask
= bmpSrc
.GetMask();
2178 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2180 // don't give assert here because this would break existing
2181 // programs - just silently ignore useMask parameter
2186 if (xsrcMask
== -1 && ysrcMask
== -1)
2188 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2191 wxTextColoursChanger
textCol(GetHdc(), *this);
2196 case wxXOR
: dwRop
= SRCINVERT
; break;
2197 case wxINVERT
: dwRop
= DSTINVERT
; break;
2198 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2199 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2200 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2201 case wxSET
: dwRop
= WHITENESS
; break;
2202 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2203 case wxAND
: dwRop
= SRCAND
; break;
2204 case wxOR
: dwRop
= SRCPAINT
; break;
2205 case wxEQUIV
: dwRop
= 0x00990066; break;
2206 case wxNAND
: dwRop
= 0x007700E6; break;
2207 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2208 case wxCOPY
: dwRop
= SRCCOPY
; break;
2209 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2210 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2211 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2213 wxFAIL_MSG( wxT("unsupported logical function") );
2217 bool success
= false;
2222 // we want the part of the image corresponding to the mask to be
2223 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2224 // meaning of fg and bg is inverted which corresponds to wxWin notion
2225 // of the mask which is also contrary to the Windows one)
2227 // On some systems, MaskBlt succeeds yet is much much slower
2228 // than the wxWidgets fall-back implementation. So we need
2229 // to be able to switch this on and off at runtime.
2230 #if wxUSE_SYSTEM_OPTIONS
2231 static bool s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
2232 if ( s_maskBltAllowed
)
2235 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2240 xdest
, ydest
, dstWidth
, dstHeight
,
2243 (HBITMAP
)mask
->GetMaskBitmap(),
2245 MAKEROP4(dwRop
, DSTCOPY
)
2253 // Blit bitmap with mask
2256 HBITMAP buffer_bmap
;
2258 #if wxUSE_DC_CACHEING
2259 // create a temp buffer bitmap and DCs to access it and the mask
2260 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2261 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2263 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2264 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2266 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2267 dstWidth
, dstHeight
);
2269 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2270 #else // !wxUSE_DC_CACHEING
2271 // create a temp buffer bitmap and DCs to access it and the mask
2272 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2273 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2274 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2275 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2276 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2277 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2279 // copy dest to buffer
2280 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2281 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2283 wxLogLastError(wxT("BitBlt"));
2286 SET_STRETCH_BLT_MODE(GetHdc());
2288 // copy src to buffer using selected raster op
2289 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2290 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2292 wxLogLastError(wxT("StretchBlt"));
2295 // set masked area in buffer to BLACK
2297 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2298 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2299 dc_mask
, xsrcMask
, ysrcMask
,
2300 srcWidth
, srcHeight
, SRCAND
) )
2302 wxLogLastError(wxT("StretchBlt"));
2305 // set unmasked area in dest to BLACK
2306 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2307 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2308 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2309 dc_mask
, xsrcMask
, ysrcMask
,
2310 srcWidth
, srcHeight
, SRCAND
) )
2312 wxLogLastError(wxT("StretchBlt"));
2314 } // restore the original text and background colours
2316 // OR buffer to dest
2317 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2318 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2321 wxLogLastError(wxT("BitBlt"));
2324 // tidy up temporary DCs and bitmap
2325 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2326 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2328 #if !wxUSE_DC_CACHEING
2330 ::DeleteDC(dc_mask
);
2331 ::DeleteDC(dc_buffer
);
2332 ::DeleteObject(buffer_bmap
);
2337 else // no mask, just BitBlt() it
2339 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2340 // use StretchBlt() if available and finally fall back to BitBlt()
2342 // FIXME: use appropriate WinCE functions
2344 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2345 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2350 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2352 &ds
) == sizeof(ds
) )
2354 SET_STRETCH_BLT_MODE(GetHdc());
2356 // Unlike all the other functions used here (i.e. AlphaBlt(),
2357 // MaskBlt(), BitBlt() and StretchBlt()), StretchDIBits() does
2358 // not take into account the source DC logical coordinates
2359 // automatically as it doesn't even work with the source HDC.
2360 // So do this manually to ensure that the coordinates are
2361 // interpreted in the same way here as in all the other cases.
2362 xsrc
= source
->LogicalToDeviceX(xsrc
);
2363 ysrc
= source
->LogicalToDeviceY(ysrc
);
2364 srcWidth
= source
->LogicalToDeviceXRel(srcWidth
);
2365 srcHeight
= source
->LogicalToDeviceYRel(srcHeight
);
2367 // Figure out what co-ordinate system we're supposed to specify
2369 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2373 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2376 if ( ::StretchDIBits(GetHdc(),
2378 dstWidth
, dstHeight
,
2380 srcWidth
, srcHeight
,
2382 (LPBITMAPINFO
)&ds
.dsBmih
,
2385 ) == (int)GDI_ERROR
)
2387 // On Win9x this API fails most (all?) of the time, so
2388 // logging it becomes quite distracting. Since it falls
2389 // back to the code below this is not really serious, so
2391 //wxLogLastError(wxT("StretchDIBits"));
2400 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2404 SET_STRETCH_BLT_MODE(GetHdc());
2409 xdest
, ydest
, dstWidth
, dstHeight
,
2411 xsrc
, ysrc
, srcWidth
, srcHeight
,
2415 wxLogLastError(wxT("StretchBlt"));
2425 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2426 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2428 wxLogLastError(wxT("BitBlt"));
2440 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2442 WXMICROWIN_CHECK_HDC
2445 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2447 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2450 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2452 WXMICROWIN_CHECK_HDC
2454 // if we implement it in terms of DoGetSize() instead of directly using the
2455 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2456 // will also work for wxWindowDC and wxClientDC even though their size is
2457 // not the same as the total size of the screen
2458 int wPixels
, hPixels
;
2459 DoGetSize(&wPixels
, &hPixels
);
2463 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2465 wxCHECK_RET( wTotal
, wxT("0 width device?") );
2467 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2472 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2474 wxCHECK_RET( hTotal
, wxT("0 height device?") );
2476 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2480 wxSize
wxMSWDCImpl::GetPPI() const
2482 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2484 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2485 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2487 return wxSize(x
, y
);
2490 // ----------------------------------------------------------------------------
2492 // ----------------------------------------------------------------------------
2494 #if wxUSE_DC_CACHEING
2497 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2498 * improve it in due course, either using arrays, or simply storing pointers to one
2499 * entry for the bitmap, and two for the DCs. -- JACS
2502 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2503 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2505 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2514 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2523 wxDCCacheEntry::~wxDCCacheEntry()
2526 ::DeleteObject((HBITMAP
) m_bitmap
);
2528 ::DeleteDC((HDC
) m_dc
);
2531 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2533 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2534 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2537 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2539 if (entry
->m_depth
== depth
)
2541 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2543 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2544 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2545 if ( !entry
->m_bitmap
)
2547 wxLogLastError(wxT("CreateCompatibleBitmap"));
2549 entry
->m_width
= w
; entry
->m_height
= h
;
2555 node
= node
->GetNext();
2557 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2560 wxLogLastError(wxT("CreateCompatibleBitmap"));
2562 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2563 AddToBitmapCache(entry
);
2567 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2569 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2570 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2573 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2575 // Don't return the same one as we already have
2576 if (!notThis
|| (notThis
!= entry
))
2578 if (entry
->m_depth
== depth
)
2584 node
= node
->GetNext();
2586 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2589 wxLogLastError(wxT("CreateCompatibleDC"));
2591 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2592 AddToDCCache(entry
);
2596 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2598 sm_bitmapCache
.Append(entry
);
2601 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2603 sm_dcCache
.Append(entry
);
2606 void wxMSWDCImpl::ClearCache()
2608 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2609 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2612 // Clean up cache at app exit
2613 class wxDCModule
: public wxModule
2616 virtual bool OnInit() { return true; }
2617 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2620 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2623 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2625 #endif // wxUSE_DC_CACHEING
2627 // ----------------------------------------------------------------------------
2628 // alpha channel support
2629 // ----------------------------------------------------------------------------
2631 static bool AlphaBlt(HDC hdcDst
,
2632 int x
, int y
, int dstWidth
, int dstHeight
,
2634 int srcWidth
, int srcHeight
,
2636 const wxBitmap
& bmp
)
2638 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2639 wxASSERT_MSG( hdcDst
&& hdcSrc
, wxT("AlphaBlt(): invalid HDC") );
2641 // do we have AlphaBlend() and company in the headers?
2642 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2643 // yes, now try to see if we have it during run-time
2644 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2645 HDC
,int,int,int,int,
2649 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(wxT("AlphaBlend"));
2650 if ( pfnAlphaBlend
)
2653 bf
.BlendOp
= AC_SRC_OVER
;
2655 bf
.SourceConstantAlpha
= 0xff;
2656 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2658 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2659 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2662 // skip wxAlphaBlend() call below
2666 wxLogLastError(wxT("AlphaBlend"));
2669 wxUnusedVar(hdcSrc
);
2670 #endif // defined(AC_SRC_OVER)
2672 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2674 #ifdef wxHAS_RAW_BITMAP
2675 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2678 #else // !wxHAS_RAW_BITMAP
2679 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2680 // alpha but at least something will be shown like this)
2683 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2687 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2688 #ifdef wxHAS_RAW_BITMAP
2691 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2692 int dstWidth
, int dstHeight
,
2694 int srcWidth
, int srcHeight
,
2695 const wxBitmap
& bmpSrc
)
2697 // get the destination DC pixels
2698 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2700 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2702 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2704 wxLogLastError(wxT("BitBlt"));
2707 // combine them with the source bitmap using alpha
2708 wxAlphaPixelData
dataDst(bmpDst
),
2709 dataSrc((wxBitmap
&)bmpSrc
);
2711 wxCHECK_RET( dataDst
&& dataSrc
,
2712 wxT("failed to get raw data in wxAlphaBlend") );
2714 wxAlphaPixelData::Iterator
pDst(dataDst
),
2718 for ( int y
= 0; y
< dstHeight
; y
++ )
2720 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2722 for ( int x
= 0; x
< dstWidth
; x
++ )
2724 // source is point sampled, Alpha StretchBlit is ugly on Win95
2725 // (but does not impact performance)
2726 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2728 // note that source bitmap uses premultiplied alpha (as required by
2729 // the real AlphaBlend)
2730 const unsigned beta
= 255 - pSrc
.Alpha();
2732 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2733 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2734 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2739 pDst
= pDstRowStart
;
2740 pDst
.OffsetY(dataDst
, 1);
2743 // and finally blit them back to the destination DC
2744 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2746 wxLogLastError(wxT("BitBlt"));
2750 #endif // wxHAS_RAW_BITMAP
2752 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2753 const wxColour
& initialColour
,
2754 const wxColour
& destColour
,
2755 wxDirection nDirection
)
2757 // use native function if we have compile-time support it and can load it
2758 // during run-time (linking to it statically would make the program
2759 // unusable on earlier Windows versions)
2760 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2762 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2763 static GradientFill_t pfnGradientFill
=
2764 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(wxT("GradientFill"));
2766 if ( pfnGradientFill
)
2768 GRADIENT_RECT grect
;
2769 grect
.UpperLeft
= 0;
2770 grect
.LowerRight
= 1;
2772 // invert colours direction if not filling from left-to-right or
2774 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2776 // one vertex for upper left and one for upper-right
2777 TRIVERTEX vertices
[2];
2779 vertices
[0].x
= rect
.GetLeft();
2780 vertices
[0].y
= rect
.GetTop();
2781 vertices
[1].x
= rect
.GetRight()+1;
2782 vertices
[1].y
= rect
.GetBottom()+1;
2784 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2785 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2786 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2787 vertices
[firstVertex
].Alpha
= 0;
2788 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2789 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2790 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2791 vertices
[1 - firstVertex
].Alpha
= 0;
2793 if ( (*pfnGradientFill
)
2800 nDirection
== wxWEST
|| nDirection
== wxEAST
2801 ? GRADIENT_FILL_RECT_H
2802 : GRADIENT_FILL_RECT_V
2805 // skip call of the base class version below
2809 wxLogLastError(wxT("GradientFill"));
2811 #endif // wxUSE_DYNLIB_CLASS
2813 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2816 #if wxUSE_DYNLIB_CLASS
2818 static DWORD
wxGetDCLayout(HDC hdc
)
2820 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2822 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2824 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2827 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2829 DWORD layout
= wxGetDCLayout(GetHdc());
2831 if ( layout
== (DWORD
)-1 )
2832 return wxLayout_Default
;
2834 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2837 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2839 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2841 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2842 if ( !s_pfnSetLayout
)
2845 if ( dir
== wxLayout_Default
)
2847 dir
= wxTheApp
->GetLayoutDirection();
2848 if ( dir
== wxLayout_Default
)
2852 DWORD layout
= wxGetDCLayout(GetHdc());
2853 if ( dir
== wxLayout_RightToLeft
)
2854 layout
|= LAYOUT_RTL
;
2856 layout
&= ~LAYOUT_RTL
;
2858 s_pfnSetLayout(GetHdc(), layout
);
2861 #else // !wxUSE_DYNLIB_CLASS
2863 // we can't provide RTL support without dynamic loading, so stub it out
2864 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2866 return wxLayout_Default
;
2869 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2873 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS