1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dc.cpp
3 // Purpose: wxDC class for MSW port
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
28 #include "wx/msw/wrapcdlg.h"
30 #include "wx/window.h"
32 #include "wx/dialog.h"
34 #include "wx/bitmap.h"
35 #include "wx/dcmemory.h"
39 #include "wx/dcprint.h"
40 #include "wx/module.h"
43 #include "wx/msw/dc.h"
44 #include "wx/sysopt.h"
45 #include "wx/dynlib.h"
47 #ifdef wxHAS_RAW_BITMAP
48 #include "wx/rawbmp.h"
53 #include "wx/msw/private/dc.h"
55 using namespace wxMSWImpl
;
58 #define AC_SRC_ALPHA 1
65 /* Quaternary raster codes */
67 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
70 // apparently with MicroWindows it is possible that HDC is 0 so we have to
71 // check for this ourselves
73 #define WXMICROWIN_CHECK_HDC if ( !GetHDC() ) return;
74 #define WXMICROWIN_CHECK_HDC_RET(x) if ( !GetHDC() ) return x;
76 #define WXMICROWIN_CHECK_HDC
77 #define WXMICROWIN_CHECK_HDC_RET(x)
80 IMPLEMENT_ABSTRACT_CLASS(wxMSWDCImpl
, wxDCImpl
)
82 // ---------------------------------------------------------------------------
84 // ---------------------------------------------------------------------------
86 static const int VIEWPORT_EXTENT
= 1024;
88 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
89 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
90 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
92 // ----------------------------------------------------------------------------
93 // macros for logical <-> device coords conversion
94 // ----------------------------------------------------------------------------
97 We currently let Windows do all the translations itself so these macros are
98 not really needed (any more) but keep them to enhance readability of the
99 code by allowing to see where are the logical and where are the device
104 #define XLOG2DEV(x) ((x-m_logicalOriginX)*m_signX)
105 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
106 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
107 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
109 #define XLOG2DEV(x) (x)
110 #define YLOG2DEV(y) (y)
111 #define XDEV2LOG(x) (x)
112 #define YDEV2LOG(y) (y)
115 // ---------------------------------------------------------------------------
117 // ---------------------------------------------------------------------------
119 // convert degrees to radians
120 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
122 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
124 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
125 // to pass it to this function but as we already have it at the point
126 // of call anyhow we do
128 // return true if we could draw the bitmap in one way or the other, false
130 static bool AlphaBlt(HDC hdcDst
,
131 int x
, int y
, int dstWidth
, int dstHeight
,
133 int srcWidth
, int srcHeight
,
135 const wxBitmap
& bmp
);
137 #ifdef wxHAS_RAW_BITMAP
139 // our (limited) AlphaBlend() replacement for Windows versions not providing it
141 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
142 int dstWidth
, int dstHeight
,
144 int srcWidth
, int srcHeight
,
145 const wxBitmap
& bmpSrc
);
147 #endif // wxHAS_RAW_BITMAP
149 // ----------------------------------------------------------------------------
151 // ----------------------------------------------------------------------------
153 // instead of duplicating the same code which sets and then restores text
154 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
155 // encapsulate this in a small helper class
157 // wxBrushAttrsSetter: changes the text colours in the ctor if required and
158 // restores them in the dtor
159 class wxBrushAttrsSetter
: private wxBkModeChanger
,
160 private wxTextColoursChanger
163 wxBrushAttrsSetter(wxMSWDCImpl
& dc
);
166 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter
);
171 #define SET_STRETCH_BLT_MODE(hdc)
173 #else // !__WXWINCE__
175 // this class sets the stretch blit mode to COLORONCOLOR during its lifetime
177 // don't use it directly, use SET_STRETCH_BLT_MODE() macro instead as it
178 // expands to nothing under WinCE which doesn't have SetStretchBltMode()
179 class StretchBltModeChanger
182 StretchBltModeChanger(HDC hdc
)
185 m_modeOld
= ::SetStretchBltMode(m_hdc
, COLORONCOLOR
);
188 wxLogLastError(wxT("SetStretchBltMode"));
192 ~StretchBltModeChanger()
194 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
196 wxLogLastError(wxT("SetStretchBltMode"));
205 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
208 #define SET_STRETCH_BLT_MODE(hdc) \
209 StretchBltModeChanger wxMAKE_UNIQUE_NAME(stretchModeChanger)(hdc)
211 #endif // __WXWINCE__/!__WXWINCE__
213 #if wxUSE_DYNLIB_CLASS
215 // helper class to cache dynamically loaded libraries and not attempt reloading
217 class wxOnceOnlyDLLLoader
220 // ctor argument must be a literal string as we don't make a copy of it!
221 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
227 // return the symbol with the given name or NULL if the DLL not loaded
228 // or symbol not present
229 void *GetSymbol(const wxChar
*name
)
231 // we're prepared to handle errors here
236 m_dll
.Load(m_dllName
);
238 // reset the name whether we succeeded or failed so that we don't
239 // try again the next time
243 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
248 if ( m_dll
.IsLoaded() )
255 wxDynamicLibrary m_dll
;
256 const wxChar
*m_dllName
;
259 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(wxT("msimg32"));
261 // we must ensure that DLLs are unloaded before the static objects cleanup time
262 // because we may hit the notorious DllMain() dead lock in this case if wx is
263 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
264 // under Windows because it tries to reacquire the same lock)
265 class wxGDIDLLsCleanupModule
: public wxModule
268 virtual bool OnInit() { return true; }
269 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
272 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
275 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
280 #if wxUSE_DC_TRANSFORM_MATRIX
282 // Class used to dynamically load world transform related API functions.
283 class GdiWorldTransformFuncs
288 if ( !ms_worldTransformSymbolsLoaded
)
289 LoadWorldTransformSymbols();
291 return ms_pfnSetGraphicsMode
&&
292 ms_pfnSetWorldTransform
&&
293 ms_pfnGetWorldTransform
&&
294 ms_pfnModifyWorldTransform
;
297 typedef int (WINAPI
*SetGraphicsMode_t
)(HDC
, int);
298 static SetGraphicsMode_t
SetGraphicsMode()
300 if ( !ms_worldTransformSymbolsLoaded
)
301 LoadWorldTransformSymbols();
303 return ms_pfnSetGraphicsMode
;
306 typedef BOOL (WINAPI
*SetWorldTransform_t
)(HDC
, const XFORM
*);
307 static SetWorldTransform_t
SetWorldTransform()
309 if ( !ms_worldTransformSymbolsLoaded
)
310 LoadWorldTransformSymbols();
312 return ms_pfnSetWorldTransform
;
315 typedef BOOL (WINAPI
*GetWorldTransform_t
)(HDC
, LPXFORM
);
316 static GetWorldTransform_t
GetWorldTransform()
318 if ( !ms_worldTransformSymbolsLoaded
)
319 LoadWorldTransformSymbols();
321 return ms_pfnGetWorldTransform
;
324 typedef BOOL (WINAPI
*ModifyWorldTransform_t
)(HDC
, const XFORM
*, DWORD
);
325 static ModifyWorldTransform_t
ModifyWorldTransform()
327 if ( !ms_worldTransformSymbolsLoaded
)
328 LoadWorldTransformSymbols();
330 return ms_pfnModifyWorldTransform
;
334 static void LoadWorldTransformSymbols()
336 wxDynamicLibrary
dll(wxT("gdi32.dll"));
338 wxDL_INIT_FUNC(ms_pfn
, SetGraphicsMode
, dll
);
339 wxDL_INIT_FUNC(ms_pfn
, SetWorldTransform
, dll
);
340 wxDL_INIT_FUNC(ms_pfn
, GetWorldTransform
, dll
);
341 wxDL_INIT_FUNC(ms_pfn
, ModifyWorldTransform
, dll
);
343 ms_worldTransformSymbolsLoaded
= true;
346 static SetGraphicsMode_t ms_pfnSetGraphicsMode
;
347 static SetWorldTransform_t ms_pfnSetWorldTransform
;
348 static GetWorldTransform_t ms_pfnGetWorldTransform
;
349 static ModifyWorldTransform_t ms_pfnModifyWorldTransform
;
351 static bool ms_worldTransformSymbolsLoaded
;
354 GdiWorldTransformFuncs::SetGraphicsMode_t
355 GdiWorldTransformFuncs::ms_pfnSetGraphicsMode
= NULL
;
356 GdiWorldTransformFuncs::SetWorldTransform_t
357 GdiWorldTransformFuncs::ms_pfnSetWorldTransform
= NULL
;
358 GdiWorldTransformFuncs::GetWorldTransform_t
359 GdiWorldTransformFuncs::ms_pfnGetWorldTransform
= NULL
;
360 GdiWorldTransformFuncs::ModifyWorldTransform_t
361 GdiWorldTransformFuncs::ms_pfnModifyWorldTransform
= NULL
;
363 bool GdiWorldTransformFuncs::ms_worldTransformSymbolsLoaded
= false;
365 #endif // wxUSE_DC_TRANSFORM_MATRIX
367 } // anonymous namespace
369 #endif // wxUSE_DYNLIB_CLASS
371 // ===========================================================================
373 // ===========================================================================
375 // ----------------------------------------------------------------------------
376 // wxBrushAttrsSetter
377 // ----------------------------------------------------------------------------
379 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
380 : wxBkModeChanger(GetHdcOf(dc
)),
381 wxTextColoursChanger(GetHdcOf(dc
))
383 const wxBrush
& brush
= dc
.GetBrush();
384 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
386 // note that Windows convention is opposite to wxWidgets one, this is
387 // why text colour becomes the background one and vice versa
388 wxTextColoursChanger::Change(dc
.GetTextBackground(),
389 dc
.GetTextForeground());
391 wxBkModeChanger::Change(dc
.GetBackgroundMode());
395 // ----------------------------------------------------------------------------
396 // wxDC MSW-specific methods
397 // ----------------------------------------------------------------------------
399 WXHDC
wxDC::GetHDC() const
401 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
402 return impl
? impl
->GetHDC() : 0;
405 // ---------------------------------------------------------------------------
407 // ---------------------------------------------------------------------------
409 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
416 wxMSWDCImpl::~wxMSWDCImpl()
420 SelectOldObjects(m_hDC
);
422 // if we own the HDC, we delete it, otherwise we just release it
426 ::DeleteDC(GetHdc());
428 else // we don't own our HDC
432 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
436 // Must have been a wxScreenDC
437 ::ReleaseDC((HWND
) NULL
, GetHdc());
443 // This will select current objects out of the DC,
444 // which is what you have to do before deleting the
446 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
452 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
453 if (m_selectedBitmap
.IsOk())
455 m_selectedBitmap
.SetSelectedInto(NULL
);
461 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
466 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
471 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
478 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
481 #endif // wxUSE_PALETTE
484 m_brush
= wxNullBrush
;
487 m_palette
= wxNullPalette
;
488 #endif // wxUSE_PALETTE
490 m_backgroundBrush
= wxNullBrush
;
491 m_selectedBitmap
= wxNullBitmap
;
494 // ---------------------------------------------------------------------------
496 // ---------------------------------------------------------------------------
498 void wxMSWDCImpl::UpdateClipBox()
503 ::GetClipBox(GetHdc(), &rect
);
505 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
506 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
507 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
508 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
512 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
514 // check if we should try to retrieve the clipping region possibly not set
515 // by our SetClippingRegion() but preset by Windows:this can only happen
516 // when we're associated with an existing HDC usign SetHDC(), see there
517 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
519 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
520 self
->UpdateClipBox();
522 if ( !m_clipX1
&& !m_clipX2
)
523 self
->m_clipping
= false;
526 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
529 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
530 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
532 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
536 // note that we combine the new clipping region with the existing one: this
537 // is compatible with what the other ports do and is the documented
538 // behaviour now (starting with 2.3.3)
539 #if defined(__WXWINCE__)
541 if ( !::GetClipBox(GetHdc(), &rectClip
) )
544 // GetClipBox returns logical coordinates, so transform to device
545 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
546 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
547 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
548 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
550 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
551 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
552 rectClip
.right
, rectClip
.bottom
);
554 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
556 ::SelectClipRgn(GetHdc(), hrgnDest
);
559 ::DeleteObject(hrgnClipOld
);
560 ::DeleteObject(hrgnDest
);
562 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
564 wxLogLastError(wxT("ExtSelectClipRgn"));
568 #endif // WinCE/!WinCE
575 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
577 // the region coords are always the device ones, so do the translation
580 // FIXME: possible +/-1 error here, to check!
581 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
583 LogicalToDeviceX(x
+ w
),
584 LogicalToDeviceY(y
+ h
));
587 wxLogLastError(wxT("CreateRectRgn"));
591 SetClippingHrgn((WXHRGN
)hrgn
);
593 ::DeleteObject(hrgn
);
597 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
599 SetClippingHrgn(region
.GetHRGN());
602 void wxMSWDCImpl::DestroyClippingRegion()
606 if (m_clipping
&& m_hDC
)
609 // On a PocketPC device (not necessarily emulator), resetting
610 // the clip region as per the old method causes bad display
611 // problems. In fact setting a null region is probably OK
612 // on desktop WIN32 also, since the WIN32 docs imply that the user
613 // clipping region is independent from the paint clipping region.
614 ::SelectClipRgn(GetHdc(), 0);
616 // TODO: this should restore the previous clipping region,
617 // so that OnPaint processing works correctly, and the update
618 // clipping region doesn't get destroyed after the first
619 // DestroyClippingRegion.
620 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
621 ::SelectClipRgn(GetHdc(), rgn
);
626 wxDCImpl::DestroyClippingRegion();
629 // ---------------------------------------------------------------------------
630 // query capabilities
631 // ---------------------------------------------------------------------------
633 bool wxMSWDCImpl::CanDrawBitmap() const
638 bool wxMSWDCImpl::CanGetTextExtent() const
640 #ifdef __WXMICROWIN__
641 // TODO Extend MicroWindows' GetDeviceCaps function
644 // What sort of display is it?
645 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
647 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
651 int wxMSWDCImpl::GetDepth() const
653 WXMICROWIN_CHECK_HDC_RET(16)
655 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
658 // ---------------------------------------------------------------------------
660 // ---------------------------------------------------------------------------
662 void wxMSWDCImpl::Clear()
669 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
673 // No, I think we should simply ignore this if printing on e.g.
675 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
676 if (!m_selectedBitmap
.IsOk())
679 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
680 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
681 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
685 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
688 DWORD colour
= ::GetBkColor(GetHdc());
689 HBRUSH brush
= ::CreateSolidBrush(colour
);
690 ::FillRect(GetHdc(), &rect
, brush
);
691 ::DeleteObject(brush
);
693 RealizeScaleAndOrigin();
696 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
697 wxCoord
WXUNUSED_IN_WINCE(y
),
698 const wxColour
& WXUNUSED_IN_WINCE(col
),
699 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
704 WXMICROWIN_CHECK_HDC_RET(false)
706 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
708 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
709 : FLOODFILLBORDER
) ) ;
712 // quoting from the MSDN docs:
714 // Following are some of the reasons this function might fail:
716 // * The filling could not be completed.
717 // * The specified point has the boundary color specified by the
718 // crColor parameter (if FLOODFILLBORDER was requested).
719 // * The specified point does not have the color specified by
720 // crColor (if FLOODFILLSURFACE was requested)
721 // * The point is outside the clipping region that is, it is not
722 // visible on the device.
724 wxLogLastError(wxT("ExtFloodFill"));
727 CalcBoundingBox(x
, y
);
733 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
735 WXMICROWIN_CHECK_HDC_RET(false)
737 wxCHECK_MSG( col
, false, wxT("NULL colour parameter in wxMSWDCImpl::GetPixel") );
739 // get the color of the pixel
740 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
742 wxRGBToColour(*col
, pixelcolor
);
747 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
751 wxCoord x1
= x
-VIEWPORT_EXTENT
;
752 wxCoord y1
= y
-VIEWPORT_EXTENT
;
753 wxCoord x2
= x
+VIEWPORT_EXTENT
;
754 wxCoord y2
= y
+VIEWPORT_EXTENT
;
756 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
757 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
759 CalcBoundingBox(x1
, y1
);
760 CalcBoundingBox(x2
, y2
);
763 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
767 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
769 CalcBoundingBox(x1
, y1
);
770 CalcBoundingBox(x2
, y2
);
773 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
774 // and ending at (x2, y2)
775 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
776 wxCoord x2
, wxCoord y2
,
777 wxCoord xc
, wxCoord yc
)
781 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
785 // Slower emulation since WinCE doesn't support Pie and Arc
786 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
788 sa
= -sa
; // below center
789 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
790 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
795 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
797 // treat the special case of full circle separately
798 if ( x1
== x2
&& y1
== y2
)
800 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
804 wxCoord xx1
= XLOG2DEV(x1
);
805 wxCoord yy1
= YLOG2DEV(y1
);
806 wxCoord xx2
= XLOG2DEV(x2
);
807 wxCoord yy2
= YLOG2DEV(y2
);
808 wxCoord xxc
= XLOG2DEV(xc
);
809 wxCoord yyc
= YLOG2DEV(yc
);
812 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
814 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
815 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
816 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
817 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
819 if ( m_brush
.IsNonTransparent() )
821 // Have to add 1 to bottom-right corner of rectangle
822 // to make semi-circles look right (crooked line otherwise).
823 // Unfortunately this is not a reliable method, depends
824 // on the size of shape.
825 // TODO: figure out why this happens!
826 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
830 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
833 CalcBoundingBox(xc
- r
, yc
- r
);
834 CalcBoundingBox(xc
+ r
, yc
+ r
);
838 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
839 wxCoord width
, wxCoord height
)
841 // cases when we don't have DrawFrameControl()
842 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
843 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
845 wxCoord x2
= x1
+ width
,
855 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
857 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
860 CalcBoundingBox(x1
, y1
);
861 CalcBoundingBox(x2
, y2
);
862 #endif // Microwin/Normal
865 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
869 COLORREF color
= 0x00ffffff;
872 color
= m_pen
.GetColour().GetPixel();
875 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
877 CalcBoundingBox(x
, y
);
880 void wxMSWDCImpl::DoDrawPolygon(int n
,
884 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
888 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
890 // Do things less efficiently if we have offsets
891 if (xoffset
!= 0 || yoffset
!= 0)
893 POINT
*cpoints
= new POINT
[n
];
895 for (i
= 0; i
< n
; i
++)
897 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
898 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
900 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
903 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
905 (void)Polygon(GetHdc(), cpoints
, n
);
907 SetPolyFillMode(GetHdc(),prev
);
914 for (i
= 0; i
< n
; i
++)
915 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
918 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
920 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
922 SetPolyFillMode(GetHdc(),prev
);
928 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
933 wxPolygonFillMode fillStyle
)
936 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
940 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
942 for (i
= cnt
= 0; i
< n
; i
++)
945 // Do things less efficiently if we have offsets
946 if (xoffset
!= 0 || yoffset
!= 0)
948 POINT
*cpoints
= new POINT
[cnt
];
949 for (i
= 0; i
< cnt
; i
++)
951 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
952 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
954 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
957 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
959 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
961 SetPolyFillMode(GetHdc(),prev
);
967 for (i
= 0; i
< cnt
; i
++)
968 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
971 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
973 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
975 SetPolyFillMode(GetHdc(),prev
);
982 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
986 // Do things less efficiently if we have offsets
987 if (xoffset
!= 0 || yoffset
!= 0)
989 POINT
*cpoints
= new POINT
[n
];
991 for (i
= 0; i
< n
; i
++)
993 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
994 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
996 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
998 (void)Polyline(GetHdc(), cpoints
, n
);
1004 for (i
= 0; i
< n
; i
++)
1005 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
1007 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
1011 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1013 WXMICROWIN_CHECK_HDC
1015 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1017 wxCoord x2
= x
+ width
;
1018 wxCoord y2
= y
+ height
;
1020 wxCoord x2dev
= XLOG2DEV(x2
),
1021 y2dev
= YLOG2DEV(y2
);
1023 // Windows (but not Windows CE) draws the filled rectangles without outline
1024 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1025 // and we want them to have the same size regardless of which pen is used
1027 if ( m_pen
.IsTransparent() )
1032 #endif // !__WXWINCE__
1034 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
1036 CalcBoundingBox(x
, y
);
1037 CalcBoundingBox(x2
, y2
);
1040 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
1042 WXMICROWIN_CHECK_HDC
1044 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1046 // Now, a negative radius value is interpreted to mean
1047 // 'the proportion of the smallest X or Y dimension'
1051 double smallest
= (width
< height
) ? width
: height
;
1052 radius
= (- radius
* smallest
);
1055 wxCoord x2
= (x
+width
);
1056 wxCoord y2
= (y
+height
);
1058 // Windows draws the filled rectangles without outline (i.e. drawn with a
1059 // transparent pen) one pixel smaller in both directions and we want them
1060 // to have the same size regardless of which pen is used - adjust
1061 if ( m_pen
.IsTransparent() )
1067 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1068 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1070 CalcBoundingBox(x
, y
);
1071 CalcBoundingBox(x2
, y2
);
1074 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1076 WXMICROWIN_CHECK_HDC
1078 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1080 // +1 below makes the ellipse more similar to other platforms.
1081 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1082 wxCoord x2
= x
+ width
+ 1;
1083 wxCoord y2
= y
+ height
+ 1;
1085 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1086 // pen doesn't draw anything. Should we provide a workaround?
1088 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1090 CalcBoundingBox(x
, y
);
1091 CalcBoundingBox(x2
, y2
);
1094 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1095 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1097 // quadratic b-spline to cubic bezier spline conversion
1099 // quadratic spline with control points P0,P1,P2
1100 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1102 // bezier spline with control points B0,B1,B2,B3
1103 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1105 // control points of bezier spline calculated from b-spline
1107 // B1 = (2*P1 + P0)/3
1108 // B2 = (2*P1 + P2)/3
1111 WXMICROWIN_CHECK_HDC
1113 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1115 const size_t n_points
= points
->GetCount();
1116 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1118 const size_t n_bezier_points
= n_points
* 3 + 1;
1119 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1120 size_t bezier_pos
= 0;
1121 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1123 wxPointList::compatibility_iterator node
= points
->GetFirst();
1124 wxPoint
*p
= node
->GetData();
1125 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1126 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1128 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1131 node
= node
->GetNext();
1132 p
= node
->GetData();
1136 cx1
= ( x1
+ x2
) / 2;
1137 cy1
= ( y1
+ y2
) / 2;
1138 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1139 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1141 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1144 #if !wxUSE_STD_CONTAINERS
1145 while ((node
= node
->GetNext()) != NULL
)
1147 while ((node
= node
->GetNext()))
1148 #endif // !wxUSE_STD_CONTAINERS
1150 p
= (wxPoint
*)node
->GetData();
1155 cx4
= (x1
+ x2
) / 2;
1156 cy4
= (y1
+ y2
) / 2;
1157 // B0 is B3 of previous segment
1159 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1160 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1163 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1164 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1167 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1168 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1174 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1176 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1177 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1179 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1182 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1186 #endif // wxUSE_SPLINES
1188 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1189 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1192 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1195 WXMICROWIN_CHECK_HDC
1197 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1202 int rx1
= XLOG2DEV(x
+w
/2);
1203 int ry1
= YLOG2DEV(y
+h
/2);
1210 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1211 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1212 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1213 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1215 // Swap start and end positions if the end angle is less than the start angle.
1226 // draw pie with NULL_PEN first and then outline otherwise a line is
1227 // drawn from the start and end points to the centre
1228 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1231 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1232 rx1
, ry1
, rx2
, ry2
);
1236 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1237 rx1
, ry1
-1, rx2
, ry2
-1);
1240 ::SelectObject(GetHdc(), hpenOld
);
1242 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1243 rx1
, ry1
, rx2
, ry2
);
1245 CalcBoundingBox(x
, y
);
1246 CalcBoundingBox(x2
, y2
);
1250 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1252 WXMICROWIN_CHECK_HDC
1254 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1257 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1259 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1262 CalcBoundingBox(x
, y
);
1263 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1266 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1268 WXMICROWIN_CHECK_HDC
1270 wxCHECK_RET( bmp
.IsOk(), wxT("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1272 int width
= bmp
.GetWidth(),
1273 height
= bmp
.GetHeight();
1275 HBITMAP hbmpMask
= 0;
1278 HPALETTE oldPal
= 0;
1279 #endif // wxUSE_PALETTE
1281 if ( bmp
.HasAlpha() )
1284 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1286 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1290 SET_STRETCH_BLT_MODE(GetHdc());
1294 wxMask
*mask
= bmp
.GetMask();
1296 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1300 // don't give assert here because this would break existing
1301 // programs - just silently ignore useMask parameter
1308 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1312 #if wxUSE_SYSTEM_OPTIONS
1313 // On some systems, MaskBlt succeeds yet is much much slower
1314 // than the wxWidgets fall-back implementation. So we need
1315 // to be able to switch this on and off at runtime.
1317 // NB: don't query the value of the option every time but do it only
1318 // once as otherwise it can have real (and bad) performance
1319 // implications (see #11172)
1321 s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
1322 if ( s_maskBltAllowed
)
1323 #endif // wxUSE_SYSTEM_OPTIONS
1326 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1327 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1329 wxPalette
*pal
= bmp
.GetPalette();
1330 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1332 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1333 ::RealizePalette(hdcMem
);
1335 #endif // wxUSE_PALETTE
1337 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1340 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1344 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1345 #endif // wxUSE_PALETTE
1347 ::SelectObject(hdcMem
, hOldBitmap
);
1354 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1358 memDC
.SelectObjectAsSource(bmp
);
1360 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1362 memDC
.SelectObject(wxNullBitmap
);
1365 else // no mask, just use BitBlt()
1368 HDC memdc
= ::CreateCompatibleDC( cdc
);
1369 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1371 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1373 wxTextColoursChanger
textCol(GetHdc(), *this);
1376 wxPalette
*pal
= bmp
.GetPalette();
1377 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1379 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1380 ::RealizePalette(memdc
);
1382 #endif // wxUSE_PALETTE
1384 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1385 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1389 ::SelectPalette(memdc
, oldPal
, FALSE
);
1390 #endif // wxUSE_PALETTE
1392 ::SelectObject( memdc
, hOldBitmap
);
1393 ::DeleteDC( memdc
);
1397 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1399 // For compatibility with other ports (notably wxGTK) and because it's
1400 // genuinely useful, we allow passing multiline strings to DrawText().
1401 // However there is no native MSW function to draw them directly so we
1402 // instead reuse the generic DrawLabel() method to render them. Of course,
1403 // DrawLabel() itself will call back to us but with single line strings
1404 // only so there won't be any infinite recursion here.
1405 if ( text
.find('\n') != wxString::npos
)
1407 GetOwner()->DrawLabel(text
, wxRect(x
, y
, 0, 0));
1411 WXMICROWIN_CHECK_HDC
1413 DrawAnyText(text
, x
, y
);
1415 // update the bounding box
1416 CalcBoundingBox(x
, y
);
1419 GetOwner()->GetTextExtent(text
, &w
, &h
);
1420 CalcBoundingBox(x
+ w
, y
+ h
);
1423 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1425 WXMICROWIN_CHECK_HDC
1427 // prepare for drawing the text
1428 wxTextColoursChanger
textCol(GetHdc(), *this);
1430 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1432 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1433 text
.c_str(), text
.length(), NULL
) == 0 )
1435 wxLogLastError(wxT("TextOut"));
1439 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1440 wxCoord x
, wxCoord y
,
1443 WXMICROWIN_CHECK_HDC
1445 // we test that we have some font because otherwise we should still use the
1446 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1447 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1448 // font for drawing rotated fonts unfortunately)
1449 if ( (angle
== 0.0) && m_font
.IsOk() )
1451 DoDrawText(text
, x
, y
);
1453 #ifndef __WXMICROWIN__
1456 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1457 // because it's not TrueType and so can't have non zero
1458 // orientation/escapement under Win9x
1459 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1460 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1462 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1464 wxLogLastError(wxT("GetObject(hfont)"));
1467 // GDI wants the angle in tenth of degree
1468 long angle10
= (long)(angle
* 10);
1469 lf
.lfEscapement
= angle10
;
1470 lf
. lfOrientation
= angle10
;
1472 hfont
= ::CreateFontIndirect(&lf
);
1475 wxLogLastError(wxT("CreateFont"));
1479 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1481 DrawAnyText(text
, x
, y
);
1483 (void)::SelectObject(GetHdc(), hfontOld
);
1484 (void)::DeleteObject(hfont
);
1487 // call the bounding box by adding all four vertices of the rectangle
1488 // containing the text to it (simpler and probably not slower than
1489 // determining which of them is really topmost/leftmost/...)
1491 GetOwner()->GetTextExtent(text
, &w
, &h
);
1493 double rad
= DegToRad(angle
);
1495 // "upper left" and "upper right"
1496 CalcBoundingBox(x
, y
);
1497 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1499 // "bottom left" and "bottom right"
1500 x
+= (wxCoord
)(h
*sin(rad
));
1501 y
+= (wxCoord
)(h
*cos(rad
));
1502 CalcBoundingBox(x
, y
);
1503 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1508 // ---------------------------------------------------------------------------
1510 // ---------------------------------------------------------------------------
1514 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1516 WXMICROWIN_CHECK_HDC
1518 // Set the old object temporarily, in case the assignment deletes an object
1519 // that's not yet selected out.
1522 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1526 if ( m_palette
.IsOk() )
1528 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1529 GetHpaletteOf(m_palette
),
1532 m_oldPalette
= (WXHPALETTE
) oldPal
;
1535 ::RealizePalette(GetHdc());
1539 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1541 if ( palette
.IsOk() )
1543 m_palette
= palette
;
1544 DoSelectPalette(true);
1548 void wxMSWDCImpl::InitializePalette()
1550 if ( wxDisplayDepth() <= 8 )
1552 // look for any window or parent that has a custom palette. If any has
1553 // one then we need to use it in drawing operations
1554 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1556 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1557 if ( m_hasCustomPalette
)
1559 m_palette
= win
->GetPalette();
1561 // turn on MSW translation for this palette
1567 #endif // wxUSE_PALETTE
1569 // SetFont/Pen/Brush() really ask to be implemented as a single template
1570 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1572 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1574 WXMICROWIN_CHECK_HDC
1576 if ( font
== m_font
)
1581 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1582 if ( hfont
== HGDI_ERROR
)
1584 wxLogLastError(wxT("SelectObject(font)"));
1589 m_oldFont
= (WXHFONT
)hfont
;
1594 else // invalid font, reset the current font
1598 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1600 wxLogLastError(wxT("SelectObject(old font)"));
1606 m_font
= wxNullFont
;
1610 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1612 WXMICROWIN_CHECK_HDC
1619 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1620 if ( hpen
== HGDI_ERROR
)
1622 wxLogLastError(wxT("SelectObject(pen)"));
1627 m_oldPen
= (WXHPEN
)hpen
;
1632 else // invalid pen, reset the current pen
1636 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1638 wxLogLastError(wxT("SelectObject(old pen)"));
1648 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1650 WXMICROWIN_CHECK_HDC
1652 if ( brush
== m_brush
)
1657 // we must make sure the brush is aligned with the logical coordinates
1658 // before selecting it or using the same brush for the background of
1659 // different windows would result in discontinuities
1660 wxSize sizeBrushBitmap
= wxDefaultSize
;
1661 wxBitmap
*stipple
= brush
.GetStipple();
1662 if ( stipple
&& stipple
->IsOk() )
1663 sizeBrushBitmap
= stipple
->GetSize();
1664 else if ( brush
.IsHatch() )
1665 sizeBrushBitmap
= wxSize(8, 8);
1667 if ( sizeBrushBitmap
.IsFullySpecified() )
1669 if ( !::SetBrushOrgEx
1672 m_deviceOriginX
% sizeBrushBitmap
.x
,
1673 m_deviceOriginY
% sizeBrushBitmap
.y
,
1674 NULL
// [out] previous brush origin
1677 wxLogLastError(wxT("SetBrushOrgEx()"));
1681 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1682 if ( hbrush
== HGDI_ERROR
)
1684 wxLogLastError(wxT("SelectObject(brush)"));
1689 m_oldBrush
= (WXHBRUSH
)hbrush
;
1694 else // invalid brush, reset the current brush
1698 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1700 wxLogLastError(wxT("SelectObject(old brush)"));
1706 m_brush
= wxNullBrush
;
1710 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1712 WXMICROWIN_CHECK_HDC
1714 m_backgroundBrush
= brush
;
1716 if ( m_backgroundBrush
.IsOk() )
1718 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1722 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1724 WXMICROWIN_CHECK_HDC
1726 m_backgroundMode
= mode
;
1728 // SetBackgroundColour now only refers to text background
1729 // and m_backgroundMode is used there
1732 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1734 WXMICROWIN_CHECK_HDC
1736 m_logicalFunction
= function
;
1741 void wxMSWDCImpl::SetRop(WXHDC dc
)
1743 if ( !dc
|| m_logicalFunction
< 0 )
1748 switch (m_logicalFunction
)
1750 case wxCLEAR
: rop
= R2_BLACK
; break;
1751 case wxXOR
: rop
= R2_XORPEN
; break;
1752 case wxINVERT
: rop
= R2_NOT
; break;
1753 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1754 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1755 case wxCOPY
: rop
= R2_COPYPEN
; break;
1756 case wxAND
: rop
= R2_MASKPEN
; break;
1757 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1758 case wxNO_OP
: rop
= R2_NOP
; break;
1759 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1760 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1761 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1762 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1763 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1764 case wxOR
: rop
= R2_MERGEPEN
; break;
1765 case wxSET
: rop
= R2_WHITE
; break;
1767 wxFAIL_MSG( wxS("unknown logical function") );
1771 SetROP2(GetHdc(), rop
);
1774 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1776 // We might be previewing, so return true to let it continue.
1780 void wxMSWDCImpl::EndDoc()
1784 void wxMSWDCImpl::StartPage()
1788 void wxMSWDCImpl::EndPage()
1792 // ---------------------------------------------------------------------------
1794 // ---------------------------------------------------------------------------
1796 wxCoord
wxMSWDCImpl::GetCharHeight() const
1798 WXMICROWIN_CHECK_HDC_RET(0)
1800 TEXTMETRIC lpTextMetric
;
1802 GetTextMetrics(GetHdc(), &lpTextMetric
);
1804 return lpTextMetric
.tmHeight
;
1807 wxCoord
wxMSWDCImpl::GetCharWidth() const
1809 WXMICROWIN_CHECK_HDC_RET(0)
1811 TEXTMETRIC lpTextMetric
;
1813 GetTextMetrics(GetHdc(), &lpTextMetric
);
1815 return lpTextMetric
.tmAveCharWidth
;
1818 void wxMSWDCImpl::DoGetFontMetrics(int *height
,
1821 int *internalLeading
,
1822 int *externalLeading
,
1823 int *averageWidth
) const
1827 GetTextMetrics(GetHdc(), &tm
);
1830 *height
= tm
.tmHeight
;
1832 *ascent
= tm
.tmAscent
;
1834 *descent
= tm
.tmDescent
;
1835 if ( internalLeading
)
1836 *internalLeading
= tm
.tmInternalLeading
;
1837 if ( externalLeading
)
1838 *externalLeading
= tm
.tmExternalLeading
;
1840 *averageWidth
= tm
.tmAveCharWidth
;
1843 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1844 wxCoord
*descent
, wxCoord
*externalLeading
,
1845 const wxFont
*font
) const
1847 #ifdef __WXMICROWIN__
1852 if (descent
) *descent
= 0;
1853 if (externalLeading
) *externalLeading
= 0;
1856 #endif // __WXMICROWIN__
1861 wxASSERT_MSG( font
->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") );
1863 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1865 else // don't change the font
1871 const size_t len
= string
.length();
1872 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1874 wxLogLastError(wxT("GetTextExtentPoint32()"));
1877 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1878 // the result computed by GetTextExtentPoint32() may be too small as it
1879 // accounts for under/overhang of the first/last character while we want
1880 // just the bounding rect for this string so adjust the width as needed
1881 // (using API not available in 2002 SDKs of WinCE)
1885 const wxChar chFirst
= *string
.begin();
1886 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1888 if ( width
.abcA
< 0 )
1889 sizeRect
.cx
-= width
.abcA
;
1893 const wxChar chLast
= *string
.rbegin();
1894 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1896 //else: we already have the width of the last character
1898 if ( width
.abcC
< 0 )
1899 sizeRect
.cx
-= width
.abcC
;
1901 //else: GetCharABCWidths() failed, not a TrueType font?
1903 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1910 if ( descent
|| externalLeading
)
1912 DoGetFontMetrics(NULL
, NULL
, descent
, NULL
, externalLeading
, NULL
);
1917 ::SelectObject(GetHdc(), hfontOld
);
1922 // Each element of the array will be the width of the string up to and
1923 // including the coresoponding character in text.
1925 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1927 static int maxLenText
= -1;
1928 static int maxWidth
= -1;
1931 int stlen
= text
.length();
1933 if (maxLenText
== -1)
1935 // Win9x and WinNT+ have different limits
1936 int version
= wxGetOsVersion();
1937 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1938 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1942 widths
.Add(0, stlen
); // fill the array with zeros
1946 if (!::GetTextExtentExPoint(GetHdc(),
1947 text
.c_str(), // string to check
1948 wxMin(stlen
, maxLenText
),
1950 &fit
, // [out] count of chars
1952 &widths
[0], // array to fill
1956 wxLogLastError(wxT("GetTextExtentExPoint"));
1963 void wxMSWDCImpl::RealizeScaleAndOrigin()
1965 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1966 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1967 // noticeable difference between these mapping modes
1969 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1971 // wxWidgets API assumes that the coordinate space is "infinite" (i.e. only
1972 // limited by 2^32 range of the integer coordinates) but in MSW API we must
1973 // actually specify the extents that we use. So we more or less arbitrarily
1974 // decide to use "base" VIEWPORT_EXTENT and adjust it depending on scale.
1976 // To avoid rounding errors we prefer to multiply by the scale if it's > 1
1977 // and to divide by it if it's < 1.
1978 int devExtX
, devExtY
, // Viewport, i.e. device space, extents.
1979 logExtX
, logExtY
; // Window, i.e. logical coordinate space, extents.
1980 if ( m_scaleX
>= 1 )
1982 devExtX
= wxRound(VIEWPORT_EXTENT
*m_scaleX
);
1983 logExtX
= m_signX
*VIEWPORT_EXTENT
;
1987 devExtX
= VIEWPORT_EXTENT
;
1988 logExtX
= wxRound(m_signX
*VIEWPORT_EXTENT
/m_scaleX
);
1991 if ( m_scaleY
>= 1 )
1993 devExtY
= wxRound(VIEWPORT_EXTENT
*m_scaleY
);
1994 logExtY
= m_signY
*VIEWPORT_EXTENT
;
1998 devExtY
= VIEWPORT_EXTENT
;
1999 logExtY
= wxRound(m_signY
*VIEWPORT_EXTENT
/m_scaleY
);
2002 ::SetViewportExtEx(GetHdc(), devExtX
, devExtY
, NULL
);
2003 ::SetWindowExtEx(GetHdc(), logExtX
, logExtY
, NULL
);
2005 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
2006 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
2010 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
2012 WXMICROWIN_CHECK_HDC
2014 m_mappingMode
= mode
;
2016 if ( mode
== wxMM_TEXT
)
2019 m_logicalScaleY
= 1.0;
2021 else // need to do some calculations
2023 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
2024 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
2025 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
2026 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
2028 if ( (mm_width
== 0) || (mm_height
== 0) )
2030 // we can't calculate mm2pixels[XY] then!
2034 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
2035 mm2pixelsY
= (double)pixel_height
/ mm_height
;
2040 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
2041 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
2045 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
2046 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
2050 m_logicalScaleX
= mm2pixelsX
;
2051 m_logicalScaleY
= mm2pixelsY
;
2055 m_logicalScaleX
= mm2pixelsX
/ 10.0;
2056 m_logicalScaleY
= mm2pixelsY
/ 10.0;
2060 wxFAIL_MSG( wxT("unknown mapping mode in SetMapMode") );
2064 ComputeScaleAndOrigin();
2066 RealizeScaleAndOrigin();
2069 void wxMSWDCImpl::SetUserScale(double x
, double y
)
2071 WXMICROWIN_CHECK_HDC
2073 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
2076 wxDCImpl::SetUserScale(x
,y
);
2078 RealizeScaleAndOrigin();
2081 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2084 WXMICROWIN_CHECK_HDC
2086 int signX
= xLeftRight
? 1 : -1,
2087 signY
= yBottomUp
? -1 : 1;
2089 if (signX
== m_signX
&& signY
== m_signY
)
2092 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2094 RealizeScaleAndOrigin();
2097 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2099 WXMICROWIN_CHECK_HDC
2101 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2104 wxDCImpl::SetLogicalOrigin( x
, y
);
2106 RealizeScaleAndOrigin();
2109 // For use by wxWidgets only, unless custom units are required.
2110 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2112 WXMICROWIN_CHECK_HDC
2114 wxDCImpl::SetLogicalScale(x
,y
);
2117 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2119 WXMICROWIN_CHECK_HDC
2121 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2124 wxDCImpl::SetDeviceOrigin( x
, y
);
2126 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2129 // ----------------------------------------------------------------------------
2131 // ----------------------------------------------------------------------------
2133 #if wxUSE_DC_TRANSFORM_MATRIX
2135 bool wxMSWDCImpl::CanUseTransformMatrix() const
2137 return GdiWorldTransformFuncs::IsOk();
2140 bool wxMSWDCImpl::SetTransformMatrix(const wxAffineMatrix2D
&matrix
)
2142 if ( !GdiWorldTransformFuncs::IsOk() )
2145 if ( matrix
.IsIdentity() )
2147 ResetTransformMatrix();
2151 if ( !GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_ADVANCED
) )
2153 wxLogLastError(wxT("SetGraphicsMode"));
2159 matrix
.Get(&mat
, &tr
);
2162 xform
.eM11
= mat
.m_11
;
2163 xform
.eM12
= mat
.m_12
;
2164 xform
.eM21
= mat
.m_21
;
2165 xform
.eM22
= mat
.m_22
;
2169 if ( !GdiWorldTransformFuncs::SetWorldTransform()(GetHdc(), &xform
) )
2171 wxLogLastError(wxT("SetWorldTransform"));
2178 wxAffineMatrix2D
wxMSWDCImpl::GetTransformMatrix() const
2180 wxAffineMatrix2D transform
;
2182 if ( !GdiWorldTransformFuncs::IsOk() )
2186 if ( !GdiWorldTransformFuncs::GetWorldTransform()(GetHdc(), &xform
) )
2188 wxLogLastError(wxT("GetWorldTransform"));
2192 wxMatrix2D
m(xform
.eM11
, xform
.eM12
, xform
.eM21
, xform
.eM22
);
2193 wxPoint2DDouble
p(xform
.eDx
, xform
.eDy
);
2194 transform
.Set(m
, p
);
2199 void wxMSWDCImpl::ResetTransformMatrix()
2201 if ( GdiWorldTransformFuncs::IsOk() )
2203 GdiWorldTransformFuncs::ModifyWorldTransform()(GetHdc(), NULL
, MWT_IDENTITY
);
2204 GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_COMPATIBLE
);
2208 #endif // wxUSE_DC_TRANSFORM_MATRIX
2210 // ---------------------------------------------------------------------------
2212 // ---------------------------------------------------------------------------
2214 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2215 wxCoord dstWidth
, wxCoord dstHeight
,
2217 wxCoord srcX
, wxCoord srcY
,
2218 wxRasterOperationMode rop
, bool useMask
,
2219 wxCoord srcMaskX
, wxCoord srcMaskY
)
2221 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2224 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2225 wxCoord dstWidth
, wxCoord dstHeight
,
2227 wxCoord xsrc
, wxCoord ysrc
,
2228 wxCoord srcWidth
, wxCoord srcHeight
,
2229 wxRasterOperationMode rop
, bool useMask
,
2230 wxCoord xsrcMask
, wxCoord ysrcMask
)
2232 wxCHECK_MSG( source
, false, wxT("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2234 WXMICROWIN_CHECK_HDC_RET(false)
2236 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2239 // TODO: Do we want to be able to blit from other DCs too?
2243 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2245 // if either the source or destination has alpha channel, we must use
2246 // AlphaBlt() as other function don't handle it correctly
2247 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2248 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2249 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2251 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2252 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2256 wxMask
*mask
= NULL
;
2259 mask
= bmpSrc
.GetMask();
2261 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2263 // don't give assert here because this would break existing
2264 // programs - just silently ignore useMask parameter
2269 if (xsrcMask
== -1 && ysrcMask
== -1)
2271 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2274 wxTextColoursChanger
textCol(GetHdc(), *this);
2279 case wxXOR
: dwRop
= SRCINVERT
; break;
2280 case wxINVERT
: dwRop
= DSTINVERT
; break;
2281 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2282 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2283 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2284 case wxSET
: dwRop
= WHITENESS
; break;
2285 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2286 case wxAND
: dwRop
= SRCAND
; break;
2287 case wxOR
: dwRop
= SRCPAINT
; break;
2288 case wxEQUIV
: dwRop
= 0x00990066; break;
2289 case wxNAND
: dwRop
= 0x007700E6; break;
2290 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2291 case wxCOPY
: dwRop
= SRCCOPY
; break;
2292 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2293 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2294 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2296 wxFAIL_MSG( wxT("unsupported logical function") );
2300 bool success
= false;
2305 // we want the part of the image corresponding to the mask to be
2306 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2307 // meaning of fg and bg is inverted which corresponds to wxWin notion
2308 // of the mask which is also contrary to the Windows one)
2310 // On some systems, MaskBlt succeeds yet is much much slower
2311 // than the wxWidgets fall-back implementation. So we need
2312 // to be able to switch this on and off at runtime.
2313 #if wxUSE_SYSTEM_OPTIONS
2314 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2317 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2322 xdest
, ydest
, dstWidth
, dstHeight
,
2325 (HBITMAP
)mask
->GetMaskBitmap(),
2327 MAKEROP4(dwRop
, DSTCOPY
)
2335 // Blit bitmap with mask
2338 HBITMAP buffer_bmap
;
2340 #if wxUSE_DC_CACHEING
2341 // create a temp buffer bitmap and DCs to access it and the mask
2342 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2343 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2345 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2346 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2348 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2349 dstWidth
, dstHeight
);
2351 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2352 #else // !wxUSE_DC_CACHEING
2353 // create a temp buffer bitmap and DCs to access it and the mask
2354 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2355 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2356 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2357 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2358 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2359 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2361 // copy dest to buffer
2362 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2363 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2365 wxLogLastError(wxT("BitBlt"));
2368 SET_STRETCH_BLT_MODE(GetHdc());
2370 // copy src to buffer using selected raster op
2371 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2372 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2374 wxLogLastError(wxT("StretchBlt"));
2377 // set masked area in buffer to BLACK
2379 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2380 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2381 dc_mask
, xsrcMask
, ysrcMask
,
2382 srcWidth
, srcHeight
, SRCAND
) )
2384 wxLogLastError(wxT("StretchBlt"));
2387 // set unmasked area in dest to BLACK
2388 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2389 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2390 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2391 dc_mask
, xsrcMask
, ysrcMask
,
2392 srcWidth
, srcHeight
, SRCAND
) )
2394 wxLogLastError(wxT("StretchBlt"));
2396 } // restore the original text and background colours
2398 // OR buffer to dest
2399 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2400 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2403 wxLogLastError(wxT("BitBlt"));
2406 // tidy up temporary DCs and bitmap
2407 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2408 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2410 #if !wxUSE_DC_CACHEING
2412 ::DeleteDC(dc_mask
);
2413 ::DeleteDC(dc_buffer
);
2414 ::DeleteObject(buffer_bmap
);
2419 else // no mask, just BitBlt() it
2421 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2422 // use StretchBlt() if available and finally fall back to BitBlt()
2424 // FIXME: use appropriate WinCE functions
2426 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2427 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2432 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2434 &ds
) == sizeof(ds
) )
2436 SET_STRETCH_BLT_MODE(GetHdc());
2438 // Figure out what co-ordinate system we're supposed to specify
2440 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2444 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2447 if ( ::StretchDIBits(GetHdc(),
2449 dstWidth
, dstHeight
,
2451 srcWidth
, srcHeight
,
2453 (LPBITMAPINFO
)&ds
.dsBmih
,
2456 ) == (int)GDI_ERROR
)
2458 // On Win9x this API fails most (all?) of the time, so
2459 // logging it becomes quite distracting. Since it falls
2460 // back to the code below this is not really serious, so
2462 //wxLogLastError(wxT("StretchDIBits"));
2471 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2475 SET_STRETCH_BLT_MODE(GetHdc());
2480 xdest
, ydest
, dstWidth
, dstHeight
,
2482 xsrc
, ysrc
, srcWidth
, srcHeight
,
2486 wxLogLastError(wxT("StretchBlt"));
2496 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2497 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2499 wxLogLastError(wxT("BitBlt"));
2511 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2513 WXMICROWIN_CHECK_HDC
2516 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2518 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2521 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2523 WXMICROWIN_CHECK_HDC
2525 // if we implement it in terms of DoGetSize() instead of directly using the
2526 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2527 // will also work for wxWindowDC and wxClientDC even though their size is
2528 // not the same as the total size of the screen
2529 int wPixels
, hPixels
;
2530 DoGetSize(&wPixels
, &hPixels
);
2534 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2536 wxCHECK_RET( wTotal
, wxT("0 width device?") );
2538 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2543 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2545 wxCHECK_RET( hTotal
, wxT("0 height device?") );
2547 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2551 wxSize
wxMSWDCImpl::GetPPI() const
2553 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2555 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2556 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2558 return wxSize(x
, y
);
2561 // ----------------------------------------------------------------------------
2563 // ----------------------------------------------------------------------------
2565 #if wxUSE_DC_CACHEING
2568 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2569 * improve it in due course, either using arrays, or simply storing pointers to one
2570 * entry for the bitmap, and two for the DCs. -- JACS
2573 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2574 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2576 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2585 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2594 wxDCCacheEntry::~wxDCCacheEntry()
2597 ::DeleteObject((HBITMAP
) m_bitmap
);
2599 ::DeleteDC((HDC
) m_dc
);
2602 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2604 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2605 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2608 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2610 if (entry
->m_depth
== depth
)
2612 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2614 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2615 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2616 if ( !entry
->m_bitmap
)
2618 wxLogLastError(wxT("CreateCompatibleBitmap"));
2620 entry
->m_width
= w
; entry
->m_height
= h
;
2626 node
= node
->GetNext();
2628 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2631 wxLogLastError(wxT("CreateCompatibleBitmap"));
2633 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2634 AddToBitmapCache(entry
);
2638 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2640 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2641 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2644 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2646 // Don't return the same one as we already have
2647 if (!notThis
|| (notThis
!= entry
))
2649 if (entry
->m_depth
== depth
)
2655 node
= node
->GetNext();
2657 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2660 wxLogLastError(wxT("CreateCompatibleDC"));
2662 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2663 AddToDCCache(entry
);
2667 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2669 sm_bitmapCache
.Append(entry
);
2672 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2674 sm_dcCache
.Append(entry
);
2677 void wxMSWDCImpl::ClearCache()
2679 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2680 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2683 // Clean up cache at app exit
2684 class wxDCModule
: public wxModule
2687 virtual bool OnInit() { return true; }
2688 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2691 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2694 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2696 #endif // wxUSE_DC_CACHEING
2698 // ----------------------------------------------------------------------------
2699 // alpha channel support
2700 // ----------------------------------------------------------------------------
2702 static bool AlphaBlt(HDC hdcDst
,
2703 int x
, int y
, int dstWidth
, int dstHeight
,
2705 int srcWidth
, int srcHeight
,
2707 const wxBitmap
& bmp
)
2709 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2710 wxASSERT_MSG( hdcDst
&& hdcSrc
, wxT("AlphaBlt(): invalid HDC") );
2712 // do we have AlphaBlend() and company in the headers?
2713 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2714 // yes, now try to see if we have it during run-time
2715 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2716 HDC
,int,int,int,int,
2720 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(wxT("AlphaBlend"));
2721 if ( pfnAlphaBlend
)
2724 bf
.BlendOp
= AC_SRC_OVER
;
2726 bf
.SourceConstantAlpha
= 0xff;
2727 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2729 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2730 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2733 // skip wxAlphaBlend() call below
2737 wxLogLastError(wxT("AlphaBlend"));
2740 wxUnusedVar(hdcSrc
);
2741 #endif // defined(AC_SRC_OVER)
2743 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2745 #ifdef wxHAS_RAW_BITMAP
2746 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2749 #else // !wxHAS_RAW_BITMAP
2750 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2751 // alpha but at least something will be shown like this)
2754 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2758 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2759 #ifdef wxHAS_RAW_BITMAP
2762 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2763 int dstWidth
, int dstHeight
,
2765 int srcWidth
, int srcHeight
,
2766 const wxBitmap
& bmpSrc
)
2768 // get the destination DC pixels
2769 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2771 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2773 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2775 wxLogLastError(wxT("BitBlt"));
2778 // combine them with the source bitmap using alpha
2779 wxAlphaPixelData
dataDst(bmpDst
),
2780 dataSrc((wxBitmap
&)bmpSrc
);
2782 wxCHECK_RET( dataDst
&& dataSrc
,
2783 wxT("failed to get raw data in wxAlphaBlend") );
2785 wxAlphaPixelData::Iterator
pDst(dataDst
),
2789 for ( int y
= 0; y
< dstHeight
; y
++ )
2791 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2793 for ( int x
= 0; x
< dstWidth
; x
++ )
2795 // source is point sampled, Alpha StretchBlit is ugly on Win95
2796 // (but does not impact performance)
2797 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2799 // note that source bitmap uses premultiplied alpha (as required by
2800 // the real AlphaBlend)
2801 const unsigned beta
= 255 - pSrc
.Alpha();
2803 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2804 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2805 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2810 pDst
= pDstRowStart
;
2811 pDst
.OffsetY(dataDst
, 1);
2814 // and finally blit them back to the destination DC
2815 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2817 wxLogLastError(wxT("BitBlt"));
2821 #endif // wxHAS_RAW_BITMAP
2823 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2824 const wxColour
& initialColour
,
2825 const wxColour
& destColour
,
2826 wxDirection nDirection
)
2828 // use native function if we have compile-time support it and can load it
2829 // during run-time (linking to it statically would make the program
2830 // unusable on earlier Windows versions)
2831 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2833 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2834 static GradientFill_t pfnGradientFill
=
2835 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(wxT("GradientFill"));
2837 if ( pfnGradientFill
)
2839 GRADIENT_RECT grect
;
2840 grect
.UpperLeft
= 0;
2841 grect
.LowerRight
= 1;
2843 // invert colours direction if not filling from left-to-right or
2845 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2847 // one vertex for upper left and one for upper-right
2848 TRIVERTEX vertices
[2];
2850 vertices
[0].x
= rect
.GetLeft();
2851 vertices
[0].y
= rect
.GetTop();
2852 vertices
[1].x
= rect
.GetRight()+1;
2853 vertices
[1].y
= rect
.GetBottom()+1;
2855 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2856 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2857 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2858 vertices
[firstVertex
].Alpha
= 0;
2859 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2860 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2861 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2862 vertices
[1 - firstVertex
].Alpha
= 0;
2864 if ( (*pfnGradientFill
)
2871 nDirection
== wxWEST
|| nDirection
== wxEAST
2872 ? GRADIENT_FILL_RECT_H
2873 : GRADIENT_FILL_RECT_V
2876 // skip call of the base class version below
2880 wxLogLastError(wxT("GradientFill"));
2882 #endif // wxUSE_DYNLIB_CLASS
2884 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2887 #if wxUSE_DYNLIB_CLASS
2889 static DWORD
wxGetDCLayout(HDC hdc
)
2891 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2893 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2895 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2898 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2900 DWORD layout
= wxGetDCLayout(GetHdc());
2902 if ( layout
== (DWORD
)-1 )
2903 return wxLayout_Default
;
2905 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2908 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2910 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2912 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2913 if ( !s_pfnSetLayout
)
2916 if ( dir
== wxLayout_Default
)
2918 dir
= wxTheApp
->GetLayoutDirection();
2919 if ( dir
== wxLayout_Default
)
2923 DWORD layout
= wxGetDCLayout(GetHdc());
2924 if ( dir
== wxLayout_RightToLeft
)
2925 layout
|= LAYOUT_RTL
;
2927 layout
&= ~LAYOUT_RTL
;
2929 s_pfnSetLayout(GetHdc(), layout
);
2932 #else // !wxUSE_DYNLIB_CLASS
2934 // we can't provide RTL support without dynamic loading, so stub it out
2935 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2937 return wxLayout_Default
;
2940 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2944 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS