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"
38 #include "wx/dcprint.h"
39 #include "wx/module.h"
42 #include "wx/msw/dc.h"
43 #include "wx/sysopt.h"
44 #include "wx/dynlib.h"
46 #ifdef wxHAS_RAW_BITMAP
47 #include "wx/rawbmp.h"
52 #include "wx/msw/private/dc.h"
54 using namespace wxMSWImpl
;
57 #define AC_SRC_ALPHA 1
64 /* Quaternary raster codes */
66 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
69 // apparently with MicroWindows it is possible that HDC is 0 so we have to
70 // check for this ourselves
72 #define WXMICROWIN_CHECK_HDC if ( !GetHDC() ) return;
73 #define WXMICROWIN_CHECK_HDC_RET(x) if ( !GetHDC() ) return x;
75 #define WXMICROWIN_CHECK_HDC
76 #define WXMICROWIN_CHECK_HDC_RET(x)
79 IMPLEMENT_ABSTRACT_CLASS(wxMSWDCImpl
, wxDCImpl
)
81 // ---------------------------------------------------------------------------
83 // ---------------------------------------------------------------------------
85 static const int VIEWPORT_EXTENT
= 1000;
87 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
88 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
89 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
91 // ----------------------------------------------------------------------------
92 // macros for logical <-> device coords conversion
93 // ----------------------------------------------------------------------------
96 We currently let Windows do all the translations itself so these macros are
97 not really needed (any more) but keep them to enhance readability of the
98 code by allowing to see where are the logical and where are the device
103 #define XLOG2DEV(x) ((x-m_logicalOriginX)*m_signX)
104 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
105 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
106 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
108 #define XLOG2DEV(x) (x)
109 #define YLOG2DEV(y) (y)
110 #define XDEV2LOG(x) (x)
111 #define YDEV2LOG(y) (y)
114 // ---------------------------------------------------------------------------
116 // ---------------------------------------------------------------------------
118 // convert degrees to radians
119 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
121 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
123 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
124 // to pass it to this function but as we already have it at the point
125 // of call anyhow we do
127 // return true if we could draw the bitmap in one way or the other, false
129 static bool AlphaBlt(HDC hdcDst
,
130 int x
, int y
, int dstWidth
, int dstHeight
,
132 int srcWidth
, int srcHeight
,
134 const wxBitmap
& bmp
);
136 #ifdef wxHAS_RAW_BITMAP
138 // our (limited) AlphaBlend() replacement for Windows versions not providing it
140 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
141 int dstWidth
, int dstHeight
,
143 int srcWidth
, int srcHeight
,
144 const wxBitmap
& bmpSrc
);
146 #endif // wxHAS_RAW_BITMAP
148 // ----------------------------------------------------------------------------
150 // ----------------------------------------------------------------------------
152 // instead of duplicating the same code which sets and then restores text
153 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
154 // encapsulate this in a small helper class
156 // wxBrushAttrsSetter: changes the text colours in the ctor if required and
157 // restores them in the dtor
158 class wxBrushAttrsSetter
: private wxBkModeChanger
,
159 private wxTextColoursChanger
162 wxBrushAttrsSetter(wxMSWDCImpl
& dc
);
165 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter
);
170 #define SET_STRETCH_BLT_MODE(hdc)
172 #else // !__WXWINCE__
174 // this class sets the stretch blit mode to COLORONCOLOR during its lifetime
176 // don't use it directly, use SET_STRETCH_BLT_MODE() macro instead as it
177 // expands to nothing under WinCE which doesn't have SetStretchBltMode()
178 class StretchBltModeChanger
181 StretchBltModeChanger(HDC hdc
)
184 m_modeOld
= ::SetStretchBltMode(m_hdc
, COLORONCOLOR
);
187 wxLogLastError(wxT("SetStretchBltMode"));
191 ~StretchBltModeChanger()
193 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
195 wxLogLastError(wxT("SetStretchBltMode"));
204 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
207 #define SET_STRETCH_BLT_MODE(hdc) \
208 StretchBltModeChanger wxMAKE_UNIQUE_NAME(stretchModeChanger)(hdc)
210 #endif // __WXWINCE__/!__WXWINCE__
212 #if wxUSE_DYNLIB_CLASS
214 // helper class to cache dynamically loaded libraries and not attempt reloading
216 class wxOnceOnlyDLLLoader
219 // ctor argument must be a literal string as we don't make a copy of it!
220 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
226 // return the symbol with the given name or NULL if the DLL not loaded
227 // or symbol not present
228 void *GetSymbol(const wxChar
*name
)
230 // we're prepared to handle errors here
235 m_dll
.Load(m_dllName
);
237 // reset the name whether we succeeded or failed so that we don't
238 // try again the next time
242 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
247 if ( m_dll
.IsLoaded() )
254 wxDynamicLibrary m_dll
;
255 const wxChar
*m_dllName
;
258 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(wxT("msimg32"));
260 // we must ensure that DLLs are unloaded before the static objects cleanup time
261 // because we may hit the notorious DllMain() dead lock in this case if wx is
262 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
263 // under Windows because it tries to reacquire the same lock)
264 class wxGDIDLLsCleanupModule
: public wxModule
267 virtual bool OnInit() { return true; }
268 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
271 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
274 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
279 #if wxUSE_DC_TRANSFORM_MATRIX
281 // Class used to dynamically load world transform related API functions.
282 class GdiWorldTransformFuncs
287 if ( !ms_worldTransformSymbolsLoaded
)
288 LoadWorldTransformSymbols();
290 return ms_pfnSetGraphicsMode
&&
291 ms_pfnSetWorldTransform
&&
292 ms_pfnGetWorldTransform
&&
293 ms_pfnModifyWorldTransform
;
296 typedef int (WINAPI
*SetGraphicsMode_t
)(HDC
, int);
297 static SetGraphicsMode_t
SetGraphicsMode()
299 if ( !ms_worldTransformSymbolsLoaded
)
300 LoadWorldTransformSymbols();
302 return ms_pfnSetGraphicsMode
;
305 typedef BOOL (WINAPI
*SetWorldTransform_t
)(HDC
, const XFORM
*);
306 static SetWorldTransform_t
SetWorldTransform()
308 if ( !ms_worldTransformSymbolsLoaded
)
309 LoadWorldTransformSymbols();
311 return ms_pfnSetWorldTransform
;
314 typedef BOOL (WINAPI
*GetWorldTransform_t
)(HDC
, LPXFORM
);
315 static GetWorldTransform_t
GetWorldTransform()
317 if ( !ms_worldTransformSymbolsLoaded
)
318 LoadWorldTransformSymbols();
320 return ms_pfnGetWorldTransform
;
323 typedef BOOL (WINAPI
*ModifyWorldTransform_t
)(HDC
, const XFORM
*, DWORD
);
324 static ModifyWorldTransform_t
ModifyWorldTransform()
326 if ( !ms_worldTransformSymbolsLoaded
)
327 LoadWorldTransformSymbols();
329 return ms_pfnModifyWorldTransform
;
333 static void LoadWorldTransformSymbols()
335 wxDynamicLibrary
dll(wxT("gdi32.dll"));
337 wxDL_INIT_FUNC(ms_pfn
, SetGraphicsMode
, dll
);
338 wxDL_INIT_FUNC(ms_pfn
, SetWorldTransform
, dll
);
339 wxDL_INIT_FUNC(ms_pfn
, GetWorldTransform
, dll
);
340 wxDL_INIT_FUNC(ms_pfn
, ModifyWorldTransform
, dll
);
342 ms_worldTransformSymbolsLoaded
= true;
345 static SetGraphicsMode_t ms_pfnSetGraphicsMode
;
346 static SetWorldTransform_t ms_pfnSetWorldTransform
;
347 static GetWorldTransform_t ms_pfnGetWorldTransform
;
348 static ModifyWorldTransform_t ms_pfnModifyWorldTransform
;
350 static bool ms_worldTransformSymbolsLoaded
;
353 GdiWorldTransformFuncs::SetGraphicsMode_t
354 GdiWorldTransformFuncs::ms_pfnSetGraphicsMode
= NULL
;
355 GdiWorldTransformFuncs::SetWorldTransform_t
356 GdiWorldTransformFuncs::ms_pfnSetWorldTransform
= NULL
;
357 GdiWorldTransformFuncs::GetWorldTransform_t
358 GdiWorldTransformFuncs::ms_pfnGetWorldTransform
= NULL
;
359 GdiWorldTransformFuncs::ModifyWorldTransform_t
360 GdiWorldTransformFuncs::ms_pfnModifyWorldTransform
= NULL
;
362 bool GdiWorldTransformFuncs::ms_worldTransformSymbolsLoaded
= false;
364 #endif // wxUSE_DC_TRANSFORM_MATRIX
366 } // anonymous namespace
368 #endif // wxUSE_DYNLIB_CLASS
370 // ===========================================================================
372 // ===========================================================================
374 // ----------------------------------------------------------------------------
375 // wxBrushAttrsSetter
376 // ----------------------------------------------------------------------------
378 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
379 : wxBkModeChanger(GetHdcOf(dc
)),
380 wxTextColoursChanger(GetHdcOf(dc
))
382 const wxBrush
& brush
= dc
.GetBrush();
383 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
385 // note that Windows convention is opposite to wxWidgets one, this is
386 // why text colour becomes the background one and vice versa
387 wxTextColoursChanger::Change(dc
.GetTextBackground(),
388 dc
.GetTextForeground());
390 wxBkModeChanger::Change(dc
.GetBackgroundMode());
394 // ----------------------------------------------------------------------------
395 // wxDC MSW-specific methods
396 // ----------------------------------------------------------------------------
398 WXHDC
wxDC::GetHDC() const
400 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
401 return impl
? impl
->GetHDC() : 0;
404 // ---------------------------------------------------------------------------
406 // ---------------------------------------------------------------------------
408 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
415 wxMSWDCImpl::~wxMSWDCImpl()
419 SelectOldObjects(m_hDC
);
421 // if we own the HDC, we delete it, otherwise we just release it
425 ::DeleteDC(GetHdc());
427 else // we don't own our HDC
431 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
435 // Must have been a wxScreenDC
436 ::ReleaseDC((HWND
) NULL
, GetHdc());
442 // This will select current objects out of the DC,
443 // which is what you have to do before deleting the
445 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
451 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
452 if (m_selectedBitmap
.IsOk())
454 m_selectedBitmap
.SetSelectedInto(NULL
);
460 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
465 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
470 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
477 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
480 #endif // wxUSE_PALETTE
483 m_brush
= wxNullBrush
;
486 m_palette
= wxNullPalette
;
487 #endif // wxUSE_PALETTE
489 m_backgroundBrush
= wxNullBrush
;
490 m_selectedBitmap
= wxNullBitmap
;
493 // ---------------------------------------------------------------------------
495 // ---------------------------------------------------------------------------
497 void wxMSWDCImpl::UpdateClipBox()
502 ::GetClipBox(GetHdc(), &rect
);
504 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
505 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
506 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
507 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
511 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
513 // check if we should try to retrieve the clipping region possibly not set
514 // by our SetClippingRegion() but preset by Windows:this can only happen
515 // when we're associated with an existing HDC usign SetHDC(), see there
516 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
518 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
519 self
->UpdateClipBox();
521 if ( !m_clipX1
&& !m_clipX2
)
522 self
->m_clipping
= false;
525 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
528 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
529 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
531 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
535 // note that we combine the new clipping region with the existing one: this
536 // is compatible with what the other ports do and is the documented
537 // behaviour now (starting with 2.3.3)
538 #if defined(__WXWINCE__)
540 if ( !::GetClipBox(GetHdc(), &rectClip
) )
543 // GetClipBox returns logical coordinates, so transform to device
544 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
545 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
546 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
547 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
549 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
550 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
551 rectClip
.right
, rectClip
.bottom
);
553 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
555 ::SelectClipRgn(GetHdc(), hrgnDest
);
558 ::DeleteObject(hrgnClipOld
);
559 ::DeleteObject(hrgnDest
);
561 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
563 wxLogLastError(wxT("ExtSelectClipRgn"));
567 #endif // WinCE/!WinCE
574 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
576 // the region coords are always the device ones, so do the translation
579 // FIXME: possible +/-1 error here, to check!
580 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
582 LogicalToDeviceX(x
+ w
),
583 LogicalToDeviceY(y
+ h
));
586 wxLogLastError(wxT("CreateRectRgn"));
590 SetClippingHrgn((WXHRGN
)hrgn
);
592 ::DeleteObject(hrgn
);
596 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
598 SetClippingHrgn(region
.GetHRGN());
601 void wxMSWDCImpl::DestroyClippingRegion()
605 if (m_clipping
&& m_hDC
)
608 // On a PocketPC device (not necessarily emulator), resetting
609 // the clip region as per the old method causes bad display
610 // problems. In fact setting a null region is probably OK
611 // on desktop WIN32 also, since the WIN32 docs imply that the user
612 // clipping region is independent from the paint clipping region.
613 ::SelectClipRgn(GetHdc(), 0);
615 // TODO: this should restore the previous clipping region,
616 // so that OnPaint processing works correctly, and the update
617 // clipping region doesn't get destroyed after the first
618 // DestroyClippingRegion.
619 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
620 ::SelectClipRgn(GetHdc(), rgn
);
625 wxDCImpl::DestroyClippingRegion();
628 // ---------------------------------------------------------------------------
629 // query capabilities
630 // ---------------------------------------------------------------------------
632 bool wxMSWDCImpl::CanDrawBitmap() const
637 bool wxMSWDCImpl::CanGetTextExtent() const
639 #ifdef __WXMICROWIN__
640 // TODO Extend MicroWindows' GetDeviceCaps function
643 // What sort of display is it?
644 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
646 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
650 int wxMSWDCImpl::GetDepth() const
652 WXMICROWIN_CHECK_HDC_RET(16)
654 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
657 // ---------------------------------------------------------------------------
659 // ---------------------------------------------------------------------------
661 void wxMSWDCImpl::Clear()
668 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
672 // No, I think we should simply ignore this if printing on e.g.
674 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
675 if (!m_selectedBitmap
.IsOk())
678 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
679 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
680 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
684 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
687 DWORD colour
= ::GetBkColor(GetHdc());
688 HBRUSH brush
= ::CreateSolidBrush(colour
);
689 ::FillRect(GetHdc(), &rect
, brush
);
690 ::DeleteObject(brush
);
692 RealizeScaleAndOrigin();
695 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
696 wxCoord
WXUNUSED_IN_WINCE(y
),
697 const wxColour
& WXUNUSED_IN_WINCE(col
),
698 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
703 WXMICROWIN_CHECK_HDC_RET(false)
705 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
707 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
708 : FLOODFILLBORDER
) ) ;
711 // quoting from the MSDN docs:
713 // Following are some of the reasons this function might fail:
715 // * The filling could not be completed.
716 // * The specified point has the boundary color specified by the
717 // crColor parameter (if FLOODFILLBORDER was requested).
718 // * The specified point does not have the color specified by
719 // crColor (if FLOODFILLSURFACE was requested)
720 // * The point is outside the clipping region that is, it is not
721 // visible on the device.
723 wxLogLastError(wxT("ExtFloodFill"));
726 CalcBoundingBox(x
, y
);
732 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
734 WXMICROWIN_CHECK_HDC_RET(false)
736 wxCHECK_MSG( col
, false, wxT("NULL colour parameter in wxMSWDCImpl::GetPixel") );
738 // get the color of the pixel
739 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
741 wxRGBToColour(*col
, pixelcolor
);
746 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
750 wxCoord x1
= x
-VIEWPORT_EXTENT
;
751 wxCoord y1
= y
-VIEWPORT_EXTENT
;
752 wxCoord x2
= x
+VIEWPORT_EXTENT
;
753 wxCoord y2
= y
+VIEWPORT_EXTENT
;
755 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
756 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
758 CalcBoundingBox(x1
, y1
);
759 CalcBoundingBox(x2
, y2
);
762 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
766 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
768 CalcBoundingBox(x1
, y1
);
769 CalcBoundingBox(x2
, y2
);
772 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
773 // and ending at (x2, y2)
774 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
775 wxCoord x2
, wxCoord y2
,
776 wxCoord xc
, wxCoord yc
)
780 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
784 // Slower emulation since WinCE doesn't support Pie and Arc
785 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
787 sa
= -sa
; // below center
788 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
789 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
794 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
796 // treat the special case of full circle separately
797 if ( x1
== x2
&& y1
== y2
)
799 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
803 wxCoord xx1
= XLOG2DEV(x1
);
804 wxCoord yy1
= YLOG2DEV(y1
);
805 wxCoord xx2
= XLOG2DEV(x2
);
806 wxCoord yy2
= YLOG2DEV(y2
);
807 wxCoord xxc
= XLOG2DEV(xc
);
808 wxCoord yyc
= YLOG2DEV(yc
);
811 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
813 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
814 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
815 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
816 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
818 if ( m_brush
.IsNonTransparent() )
820 // Have to add 1 to bottom-right corner of rectangle
821 // to make semi-circles look right (crooked line otherwise).
822 // Unfortunately this is not a reliable method, depends
823 // on the size of shape.
824 // TODO: figure out why this happens!
825 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
829 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
832 CalcBoundingBox(xc
- r
, yc
- r
);
833 CalcBoundingBox(xc
+ r
, yc
+ r
);
837 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
838 wxCoord width
, wxCoord height
)
840 // cases when we don't have DrawFrameControl()
841 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
842 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
844 wxCoord x2
= x1
+ width
,
854 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
856 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
859 CalcBoundingBox(x1
, y1
);
860 CalcBoundingBox(x2
, y2
);
861 #endif // Microwin/Normal
864 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
868 COLORREF color
= 0x00ffffff;
871 color
= m_pen
.GetColour().GetPixel();
874 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
876 CalcBoundingBox(x
, y
);
879 void wxMSWDCImpl::DoDrawPolygon(int n
,
883 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
887 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
889 // Do things less efficiently if we have offsets
890 if (xoffset
!= 0 || yoffset
!= 0)
892 POINT
*cpoints
= new POINT
[n
];
894 for (i
= 0; i
< n
; i
++)
896 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
897 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
899 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
902 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
904 (void)Polygon(GetHdc(), cpoints
, n
);
906 SetPolyFillMode(GetHdc(),prev
);
913 for (i
= 0; i
< n
; i
++)
914 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
917 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
919 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
921 SetPolyFillMode(GetHdc(),prev
);
927 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
932 wxPolygonFillMode fillStyle
)
935 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
939 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
941 for (i
= cnt
= 0; i
< n
; i
++)
944 // Do things less efficiently if we have offsets
945 if (xoffset
!= 0 || yoffset
!= 0)
947 POINT
*cpoints
= new POINT
[cnt
];
948 for (i
= 0; i
< cnt
; i
++)
950 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
951 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
953 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
956 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
958 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
960 SetPolyFillMode(GetHdc(),prev
);
966 for (i
= 0; i
< cnt
; i
++)
967 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
970 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
972 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
974 SetPolyFillMode(GetHdc(),prev
);
981 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
985 // Do things less efficiently if we have offsets
986 if (xoffset
!= 0 || yoffset
!= 0)
988 POINT
*cpoints
= new POINT
[n
];
990 for (i
= 0; i
< n
; i
++)
992 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
993 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
995 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
997 (void)Polyline(GetHdc(), cpoints
, n
);
1003 for (i
= 0; i
< n
; i
++)
1004 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
1006 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
1010 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1012 WXMICROWIN_CHECK_HDC
1014 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1016 wxCoord x2
= x
+ width
;
1017 wxCoord y2
= y
+ height
;
1019 wxCoord x2dev
= XLOG2DEV(x2
),
1020 y2dev
= YLOG2DEV(y2
);
1022 // Windows (but not Windows CE) draws the filled rectangles without outline
1023 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1024 // and we want them to have the same size regardless of which pen is used
1026 if ( m_pen
.IsTransparent() )
1031 #endif // !__WXWINCE__
1033 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
1035 CalcBoundingBox(x
, y
);
1036 CalcBoundingBox(x2
, y2
);
1039 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
1041 WXMICROWIN_CHECK_HDC
1043 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1045 // Now, a negative radius value is interpreted to mean
1046 // 'the proportion of the smallest X or Y dimension'
1050 double smallest
= (width
< height
) ? width
: height
;
1051 radius
= (- radius
* smallest
);
1054 wxCoord x2
= (x
+width
);
1055 wxCoord y2
= (y
+height
);
1057 // Windows draws the filled rectangles without outline (i.e. drawn with a
1058 // transparent pen) one pixel smaller in both directions and we want them
1059 // to have the same size regardless of which pen is used - adjust
1060 if ( m_pen
.IsTransparent() )
1066 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1067 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1069 CalcBoundingBox(x
, y
);
1070 CalcBoundingBox(x2
, y2
);
1073 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1075 WXMICROWIN_CHECK_HDC
1077 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1079 // +1 below makes the ellipse more similar to other platforms.
1080 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1081 wxCoord x2
= x
+ width
+ 1;
1082 wxCoord y2
= y
+ height
+ 1;
1084 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1085 // pen doesn't draw anything. Should we provide a workaround?
1087 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1089 CalcBoundingBox(x
, y
);
1090 CalcBoundingBox(x2
, y2
);
1093 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1094 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1096 // quadratic b-spline to cubic bezier spline conversion
1098 // quadratic spline with control points P0,P1,P2
1099 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1101 // bezier spline with control points B0,B1,B2,B3
1102 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1104 // control points of bezier spline calculated from b-spline
1106 // B1 = (2*P1 + P0)/3
1107 // B2 = (2*P1 + P2)/3
1110 WXMICROWIN_CHECK_HDC
1112 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1114 const size_t n_points
= points
->GetCount();
1115 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1117 const size_t n_bezier_points
= n_points
* 3 + 1;
1118 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1119 size_t bezier_pos
= 0;
1120 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1122 wxPointList::compatibility_iterator node
= points
->GetFirst();
1123 wxPoint
*p
= node
->GetData();
1124 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1125 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1127 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1130 node
= node
->GetNext();
1131 p
= node
->GetData();
1135 cx1
= ( x1
+ x2
) / 2;
1136 cy1
= ( y1
+ y2
) / 2;
1137 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1138 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1140 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1143 #if !wxUSE_STD_CONTAINERS
1144 while ((node
= node
->GetNext()) != NULL
)
1146 while ((node
= node
->GetNext()))
1147 #endif // !wxUSE_STD_CONTAINERS
1149 p
= (wxPoint
*)node
->GetData();
1154 cx4
= (x1
+ x2
) / 2;
1155 cy4
= (y1
+ y2
) / 2;
1156 // B0 is B3 of previous segment
1158 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1159 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1162 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1163 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1166 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1167 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1173 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1175 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1176 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1178 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1181 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1185 #endif // wxUSE_SPLINES
1187 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1188 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1191 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1194 WXMICROWIN_CHECK_HDC
1196 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1201 int rx1
= XLOG2DEV(x
+w
/2);
1202 int ry1
= YLOG2DEV(y
+h
/2);
1209 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1210 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1211 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1212 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1214 // Swap start and end positions if the end angle is less than the start angle.
1225 // draw pie with NULL_PEN first and then outline otherwise a line is
1226 // drawn from the start and end points to the centre
1227 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1230 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1231 rx1
, ry1
, rx2
, ry2
);
1235 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1236 rx1
, ry1
-1, rx2
, ry2
-1);
1239 ::SelectObject(GetHdc(), hpenOld
);
1241 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1242 rx1
, ry1
, rx2
, ry2
);
1244 CalcBoundingBox(x
, y
);
1245 CalcBoundingBox(x2
, y2
);
1249 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1251 WXMICROWIN_CHECK_HDC
1253 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1256 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1258 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1261 CalcBoundingBox(x
, y
);
1262 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1265 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1267 WXMICROWIN_CHECK_HDC
1269 wxCHECK_RET( bmp
.IsOk(), wxT("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1271 int width
= bmp
.GetWidth(),
1272 height
= bmp
.GetHeight();
1274 HBITMAP hbmpMask
= 0;
1277 HPALETTE oldPal
= 0;
1278 #endif // wxUSE_PALETTE
1280 if ( bmp
.HasAlpha() )
1283 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1285 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1289 SET_STRETCH_BLT_MODE(GetHdc());
1293 wxMask
*mask
= bmp
.GetMask();
1295 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1299 // don't give assert here because this would break existing
1300 // programs - just silently ignore useMask parameter
1307 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1311 #if wxUSE_SYSTEM_OPTIONS
1312 // On some systems, MaskBlt succeeds yet is much much slower
1313 // than the wxWidgets fall-back implementation. So we need
1314 // to be able to switch this on and off at runtime.
1316 // NB: don't query the value of the option every time but do it only
1317 // once as otherwise it can have real (and bad) performance
1318 // implications (see #11172)
1320 s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
1321 if ( s_maskBltAllowed
)
1322 #endif // wxUSE_SYSTEM_OPTIONS
1325 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1326 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1328 wxPalette
*pal
= bmp
.GetPalette();
1329 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1331 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1332 ::RealizePalette(hdcMem
);
1334 #endif // wxUSE_PALETTE
1336 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1339 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1343 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1344 #endif // wxUSE_PALETTE
1346 ::SelectObject(hdcMem
, hOldBitmap
);
1353 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1357 memDC
.SelectObjectAsSource(bmp
);
1359 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1361 memDC
.SelectObject(wxNullBitmap
);
1364 else // no mask, just use BitBlt()
1367 HDC memdc
= ::CreateCompatibleDC( cdc
);
1368 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1370 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1372 wxTextColoursChanger
textCol(GetHdc(), *this);
1375 wxPalette
*pal
= bmp
.GetPalette();
1376 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1378 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1379 ::RealizePalette(memdc
);
1381 #endif // wxUSE_PALETTE
1383 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1384 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1388 ::SelectPalette(memdc
, oldPal
, FALSE
);
1389 #endif // wxUSE_PALETTE
1391 ::SelectObject( memdc
, hOldBitmap
);
1392 ::DeleteDC( memdc
);
1396 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1398 // For compatibility with other ports (notably wxGTK) and because it's
1399 // genuinely useful, we allow passing multiline strings to DrawText().
1400 // However there is no native MSW function to draw them directly so we
1401 // instead reuse the generic DrawLabel() method to render them. Of course,
1402 // DrawLabel() itself will call back to us but with single line strings
1403 // only so there won't be any infinite recursion here.
1404 if ( text
.find('\n') != wxString::npos
)
1406 GetOwner()->DrawLabel(text
, wxRect(x
, y
, 0, 0));
1410 WXMICROWIN_CHECK_HDC
1412 DrawAnyText(text
, x
, y
);
1414 // update the bounding box
1415 CalcBoundingBox(x
, y
);
1418 GetOwner()->GetTextExtent(text
, &w
, &h
);
1419 CalcBoundingBox(x
+ w
, y
+ h
);
1422 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1424 WXMICROWIN_CHECK_HDC
1426 // prepare for drawing the text
1427 wxTextColoursChanger
textCol(GetHdc(), *this);
1429 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1431 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1432 text
.c_str(), text
.length(), NULL
) == 0 )
1434 wxLogLastError(wxT("TextOut"));
1438 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1439 wxCoord x
, wxCoord y
,
1442 WXMICROWIN_CHECK_HDC
1444 // we test that we have some font because otherwise we should still use the
1445 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1446 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1447 // font for drawing rotated fonts unfortunately)
1448 if ( (angle
== 0.0) && m_font
.IsOk() )
1450 DoDrawText(text
, x
, y
);
1452 #ifndef __WXMICROWIN__
1455 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1456 // because it's not TrueType and so can't have non zero
1457 // orientation/escapement under Win9x
1458 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1459 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1461 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1463 wxLogLastError(wxT("GetObject(hfont)"));
1466 // GDI wants the angle in tenth of degree
1467 long angle10
= (long)(angle
* 10);
1468 lf
.lfEscapement
= angle10
;
1469 lf
. lfOrientation
= angle10
;
1471 hfont
= ::CreateFontIndirect(&lf
);
1474 wxLogLastError(wxT("CreateFont"));
1478 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1480 DrawAnyText(text
, x
, y
);
1482 (void)::SelectObject(GetHdc(), hfontOld
);
1483 (void)::DeleteObject(hfont
);
1486 // call the bounding box by adding all four vertices of the rectangle
1487 // containing the text to it (simpler and probably not slower than
1488 // determining which of them is really topmost/leftmost/...)
1490 GetOwner()->GetTextExtent(text
, &w
, &h
);
1492 double rad
= DegToRad(angle
);
1494 // "upper left" and "upper right"
1495 CalcBoundingBox(x
, y
);
1496 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1498 // "bottom left" and "bottom right"
1499 x
+= (wxCoord
)(h
*sin(rad
));
1500 y
+= (wxCoord
)(h
*cos(rad
));
1501 CalcBoundingBox(x
, y
);
1502 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1507 // ---------------------------------------------------------------------------
1509 // ---------------------------------------------------------------------------
1513 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1515 WXMICROWIN_CHECK_HDC
1517 // Set the old object temporarily, in case the assignment deletes an object
1518 // that's not yet selected out.
1521 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1525 if ( m_palette
.IsOk() )
1527 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1528 GetHpaletteOf(m_palette
),
1531 m_oldPalette
= (WXHPALETTE
) oldPal
;
1534 ::RealizePalette(GetHdc());
1538 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1540 if ( palette
.IsOk() )
1542 m_palette
= palette
;
1543 DoSelectPalette(true);
1547 void wxMSWDCImpl::InitializePalette()
1549 if ( wxDisplayDepth() <= 8 )
1551 // look for any window or parent that has a custom palette. If any has
1552 // one then we need to use it in drawing operations
1553 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1555 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1556 if ( m_hasCustomPalette
)
1558 m_palette
= win
->GetPalette();
1560 // turn on MSW translation for this palette
1566 #endif // wxUSE_PALETTE
1568 // SetFont/Pen/Brush() really ask to be implemented as a single template
1569 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1571 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1573 WXMICROWIN_CHECK_HDC
1575 if ( font
== m_font
)
1580 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1581 if ( hfont
== HGDI_ERROR
)
1583 wxLogLastError(wxT("SelectObject(font)"));
1588 m_oldFont
= (WXHFONT
)hfont
;
1593 else // invalid font, reset the current font
1597 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1599 wxLogLastError(wxT("SelectObject(old font)"));
1605 m_font
= wxNullFont
;
1609 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1611 WXMICROWIN_CHECK_HDC
1618 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1619 if ( hpen
== HGDI_ERROR
)
1621 wxLogLastError(wxT("SelectObject(pen)"));
1626 m_oldPen
= (WXHPEN
)hpen
;
1631 else // invalid pen, reset the current pen
1635 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1637 wxLogLastError(wxT("SelectObject(old pen)"));
1647 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1649 WXMICROWIN_CHECK_HDC
1651 if ( brush
== m_brush
)
1656 // we must make sure the brush is aligned with the logical coordinates
1657 // before selecting it or using the same brush for the background of
1658 // different windows would result in discontinuities
1659 wxSize sizeBrushBitmap
= wxDefaultSize
;
1660 wxBitmap
*stipple
= brush
.GetStipple();
1661 if ( stipple
&& stipple
->IsOk() )
1662 sizeBrushBitmap
= stipple
->GetSize();
1663 else if ( brush
.IsHatch() )
1664 sizeBrushBitmap
= wxSize(8, 8);
1666 if ( sizeBrushBitmap
.IsFullySpecified() )
1668 if ( !::SetBrushOrgEx
1671 m_deviceOriginX
% sizeBrushBitmap
.x
,
1672 m_deviceOriginY
% sizeBrushBitmap
.y
,
1673 NULL
// [out] previous brush origin
1676 wxLogLastError(wxT("SetBrushOrgEx()"));
1680 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1681 if ( hbrush
== HGDI_ERROR
)
1683 wxLogLastError(wxT("SelectObject(brush)"));
1688 m_oldBrush
= (WXHBRUSH
)hbrush
;
1693 else // invalid brush, reset the current brush
1697 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1699 wxLogLastError(wxT("SelectObject(old brush)"));
1705 m_brush
= wxNullBrush
;
1709 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1711 WXMICROWIN_CHECK_HDC
1713 m_backgroundBrush
= brush
;
1715 if ( m_backgroundBrush
.IsOk() )
1717 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1721 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1723 WXMICROWIN_CHECK_HDC
1725 m_backgroundMode
= mode
;
1727 // SetBackgroundColour now only refers to text background
1728 // and m_backgroundMode is used there
1731 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1733 WXMICROWIN_CHECK_HDC
1735 m_logicalFunction
= function
;
1740 void wxMSWDCImpl::SetRop(WXHDC dc
)
1742 if ( !dc
|| m_logicalFunction
< 0 )
1747 switch (m_logicalFunction
)
1749 case wxCLEAR
: rop
= R2_BLACK
; break;
1750 case wxXOR
: rop
= R2_XORPEN
; break;
1751 case wxINVERT
: rop
= R2_NOT
; break;
1752 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1753 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1754 case wxCOPY
: rop
= R2_COPYPEN
; break;
1755 case wxAND
: rop
= R2_MASKPEN
; break;
1756 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1757 case wxNO_OP
: rop
= R2_NOP
; break;
1758 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1759 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1760 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1761 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1762 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1763 case wxOR
: rop
= R2_MERGEPEN
; break;
1764 case wxSET
: rop
= R2_WHITE
; break;
1766 wxFAIL_MSG( wxS("unknown logical function") );
1770 SetROP2(GetHdc(), rop
);
1773 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1775 // We might be previewing, so return true to let it continue.
1779 void wxMSWDCImpl::EndDoc()
1783 void wxMSWDCImpl::StartPage()
1787 void wxMSWDCImpl::EndPage()
1791 // ---------------------------------------------------------------------------
1793 // ---------------------------------------------------------------------------
1795 wxCoord
wxMSWDCImpl::GetCharHeight() const
1797 WXMICROWIN_CHECK_HDC_RET(0)
1799 TEXTMETRIC lpTextMetric
;
1801 GetTextMetrics(GetHdc(), &lpTextMetric
);
1803 return lpTextMetric
.tmHeight
;
1806 wxCoord
wxMSWDCImpl::GetCharWidth() const
1808 WXMICROWIN_CHECK_HDC_RET(0)
1810 TEXTMETRIC lpTextMetric
;
1812 GetTextMetrics(GetHdc(), &lpTextMetric
);
1814 return lpTextMetric
.tmAveCharWidth
;
1817 void wxMSWDCImpl::DoGetFontMetrics(int *height
,
1820 int *internalLeading
,
1821 int *externalLeading
,
1822 int *averageWidth
) const
1826 GetTextMetrics(GetHdc(), &tm
);
1829 *height
= tm
.tmHeight
;
1831 *ascent
= tm
.tmAscent
;
1833 *descent
= tm
.tmDescent
;
1834 if ( internalLeading
)
1835 *internalLeading
= tm
.tmInternalLeading
;
1836 if ( externalLeading
)
1837 *externalLeading
= tm
.tmExternalLeading
;
1839 *averageWidth
= tm
.tmAveCharWidth
;
1842 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1843 wxCoord
*descent
, wxCoord
*externalLeading
,
1844 const wxFont
*font
) const
1846 #ifdef __WXMICROWIN__
1851 if (descent
) *descent
= 0;
1852 if (externalLeading
) *externalLeading
= 0;
1855 #endif // __WXMICROWIN__
1860 wxASSERT_MSG( font
->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") );
1862 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1864 else // don't change the font
1870 const size_t len
= string
.length();
1871 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1873 wxLogLastError(wxT("GetTextExtentPoint32()"));
1876 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1877 // the result computed by GetTextExtentPoint32() may be too small as it
1878 // accounts for under/overhang of the first/last character while we want
1879 // just the bounding rect for this string so adjust the width as needed
1880 // (using API not available in 2002 SDKs of WinCE)
1884 const wxChar chFirst
= *string
.begin();
1885 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1887 if ( width
.abcA
< 0 )
1888 sizeRect
.cx
-= width
.abcA
;
1892 const wxChar chLast
= *string
.rbegin();
1893 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1895 //else: we already have the width of the last character
1897 if ( width
.abcC
< 0 )
1898 sizeRect
.cx
-= width
.abcC
;
1900 //else: GetCharABCWidths() failed, not a TrueType font?
1902 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1909 if ( descent
|| externalLeading
)
1911 DoGetFontMetrics(NULL
, NULL
, descent
, NULL
, externalLeading
, NULL
);
1916 ::SelectObject(GetHdc(), hfontOld
);
1921 // Each element of the array will be the width of the string up to and
1922 // including the coresoponding character in text.
1924 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1926 static int maxLenText
= -1;
1927 static int maxWidth
= -1;
1930 int stlen
= text
.length();
1932 if (maxLenText
== -1)
1934 // Win9x and WinNT+ have different limits
1935 int version
= wxGetOsVersion();
1936 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1937 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1941 widths
.Add(0, stlen
); // fill the array with zeros
1945 if (!::GetTextExtentExPoint(GetHdc(),
1946 text
.c_str(), // string to check
1947 wxMin(stlen
, maxLenText
),
1949 &fit
, // [out] count of chars
1951 &widths
[0], // array to fill
1955 wxLogLastError(wxT("GetTextExtentExPoint"));
1962 void wxMSWDCImpl::RealizeScaleAndOrigin()
1964 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1965 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1966 // noticeable difference between these mapping modes
1968 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1970 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1971 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1973 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1974 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1976 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1977 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1981 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1983 WXMICROWIN_CHECK_HDC
1985 m_mappingMode
= mode
;
1987 if ( mode
== wxMM_TEXT
)
1990 m_logicalScaleY
= 1.0;
1992 else // need to do some calculations
1994 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1995 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1996 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1997 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1999 if ( (mm_width
== 0) || (mm_height
== 0) )
2001 // we can't calculate mm2pixels[XY] then!
2005 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
2006 mm2pixelsY
= (double)pixel_height
/ mm_height
;
2011 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
2012 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
2016 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
2017 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
2021 m_logicalScaleX
= mm2pixelsX
;
2022 m_logicalScaleY
= mm2pixelsY
;
2026 m_logicalScaleX
= mm2pixelsX
/ 10.0;
2027 m_logicalScaleY
= mm2pixelsY
/ 10.0;
2031 wxFAIL_MSG( wxT("unknown mapping mode in SetMapMode") );
2035 ComputeScaleAndOrigin();
2037 RealizeScaleAndOrigin();
2040 void wxMSWDCImpl::SetUserScale(double x
, double y
)
2042 WXMICROWIN_CHECK_HDC
2044 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
2047 wxDCImpl::SetUserScale(x
,y
);
2049 RealizeScaleAndOrigin();
2052 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2055 WXMICROWIN_CHECK_HDC
2057 int signX
= xLeftRight
? 1 : -1,
2058 signY
= yBottomUp
? -1 : 1;
2060 if (signX
== m_signX
&& signY
== m_signY
)
2063 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2065 RealizeScaleAndOrigin();
2068 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2070 WXMICROWIN_CHECK_HDC
2072 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2075 wxDCImpl::SetLogicalOrigin( x
, y
);
2077 RealizeScaleAndOrigin();
2080 // For use by wxWidgets only, unless custom units are required.
2081 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2083 WXMICROWIN_CHECK_HDC
2085 wxDCImpl::SetLogicalScale(x
,y
);
2088 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2090 WXMICROWIN_CHECK_HDC
2092 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2095 wxDCImpl::SetDeviceOrigin( x
, y
);
2097 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2100 // ----------------------------------------------------------------------------
2102 // ----------------------------------------------------------------------------
2104 #if wxUSE_DC_TRANSFORM_MATRIX
2106 bool wxMSWDCImpl::CanUseTransformMatrix() const
2108 return GdiWorldTransformFuncs::IsOk();
2111 bool wxMSWDCImpl::SetTransformMatrix(const wxAffineMatrix2D
&matrix
)
2113 if ( !GdiWorldTransformFuncs::IsOk() )
2116 if ( matrix
.IsIdentity() )
2118 ResetTransformMatrix();
2122 if ( !GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_ADVANCED
) )
2124 wxLogLastError(wxT("SetGraphicsMode"));
2130 matrix
.Get(&mat
, &tr
);
2133 xform
.eM11
= mat
.m_11
;
2134 xform
.eM12
= mat
.m_12
;
2135 xform
.eM21
= mat
.m_21
;
2136 xform
.eM22
= mat
.m_22
;
2140 if ( !GdiWorldTransformFuncs::SetWorldTransform()(GetHdc(), &xform
) )
2142 wxLogLastError(wxT("SetWorldTransform"));
2149 wxAffineMatrix2D
wxMSWDCImpl::GetTransformMatrix() const
2151 wxAffineMatrix2D transform
;
2153 if ( !GdiWorldTransformFuncs::IsOk() )
2157 if ( !GdiWorldTransformFuncs::GetWorldTransform()(GetHdc(), &xform
) )
2159 wxLogLastError(wxT("GetWorldTransform"));
2163 wxMatrix2D
m(xform
.eM11
, xform
.eM12
, xform
.eM21
, xform
.eM22
);
2164 wxPoint2DDouble
p(xform
.eDx
, xform
.eDy
);
2165 transform
.Set(m
, p
);
2170 void wxMSWDCImpl::ResetTransformMatrix()
2172 if ( GdiWorldTransformFuncs::IsOk() )
2174 GdiWorldTransformFuncs::ModifyWorldTransform()(GetHdc(), NULL
, MWT_IDENTITY
);
2175 GdiWorldTransformFuncs::SetGraphicsMode()(GetHdc(), GM_COMPATIBLE
);
2179 #endif // wxUSE_DC_TRANSFORM_MATRIX
2181 // ---------------------------------------------------------------------------
2183 // ---------------------------------------------------------------------------
2185 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2186 wxCoord dstWidth
, wxCoord dstHeight
,
2188 wxCoord srcX
, wxCoord srcY
,
2189 wxRasterOperationMode rop
, bool useMask
,
2190 wxCoord srcMaskX
, wxCoord srcMaskY
)
2192 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2195 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2196 wxCoord dstWidth
, wxCoord dstHeight
,
2198 wxCoord xsrc
, wxCoord ysrc
,
2199 wxCoord srcWidth
, wxCoord srcHeight
,
2200 wxRasterOperationMode rop
, bool useMask
,
2201 wxCoord xsrcMask
, wxCoord ysrcMask
)
2203 wxCHECK_MSG( source
, false, wxT("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2205 WXMICROWIN_CHECK_HDC_RET(false)
2207 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2210 // TODO: Do we want to be able to blit from other DCs too?
2214 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2216 // if either the source or destination has alpha channel, we must use
2217 // AlphaBlt() as other function don't handle it correctly
2218 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2219 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2220 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2222 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2223 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2227 wxMask
*mask
= NULL
;
2230 mask
= bmpSrc
.GetMask();
2232 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2234 // don't give assert here because this would break existing
2235 // programs - just silently ignore useMask parameter
2240 if (xsrcMask
== -1 && ysrcMask
== -1)
2242 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2245 wxTextColoursChanger
textCol(GetHdc(), *this);
2250 case wxXOR
: dwRop
= SRCINVERT
; break;
2251 case wxINVERT
: dwRop
= DSTINVERT
; break;
2252 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2253 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2254 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2255 case wxSET
: dwRop
= WHITENESS
; break;
2256 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2257 case wxAND
: dwRop
= SRCAND
; break;
2258 case wxOR
: dwRop
= SRCPAINT
; break;
2259 case wxEQUIV
: dwRop
= 0x00990066; break;
2260 case wxNAND
: dwRop
= 0x007700E6; break;
2261 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2262 case wxCOPY
: dwRop
= SRCCOPY
; break;
2263 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2264 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2265 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2267 wxFAIL_MSG( wxT("unsupported logical function") );
2271 bool success
= false;
2276 // we want the part of the image corresponding to the mask to be
2277 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2278 // meaning of fg and bg is inverted which corresponds to wxWin notion
2279 // of the mask which is also contrary to the Windows one)
2281 // On some systems, MaskBlt succeeds yet is much much slower
2282 // than the wxWidgets fall-back implementation. So we need
2283 // to be able to switch this on and off at runtime.
2284 #if wxUSE_SYSTEM_OPTIONS
2285 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2288 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2293 xdest
, ydest
, dstWidth
, dstHeight
,
2296 (HBITMAP
)mask
->GetMaskBitmap(),
2298 MAKEROP4(dwRop
, DSTCOPY
)
2306 // Blit bitmap with mask
2309 HBITMAP buffer_bmap
;
2311 #if wxUSE_DC_CACHEING
2312 // create a temp buffer bitmap and DCs to access it and the mask
2313 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2314 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2316 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2317 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2319 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2320 dstWidth
, dstHeight
);
2322 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2323 #else // !wxUSE_DC_CACHEING
2324 // create a temp buffer bitmap and DCs to access it and the mask
2325 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2326 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2327 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2328 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2329 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2330 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2332 // copy dest to buffer
2333 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2334 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2336 wxLogLastError(wxT("BitBlt"));
2339 SET_STRETCH_BLT_MODE(GetHdc());
2341 // copy src to buffer using selected raster op
2342 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2343 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2345 wxLogLastError(wxT("StretchBlt"));
2348 // set masked area in buffer to BLACK
2350 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2351 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2352 dc_mask
, xsrcMask
, ysrcMask
,
2353 srcWidth
, srcHeight
, SRCAND
) )
2355 wxLogLastError(wxT("StretchBlt"));
2358 // set unmasked area in dest to BLACK
2359 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2360 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2361 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2362 dc_mask
, xsrcMask
, ysrcMask
,
2363 srcWidth
, srcHeight
, SRCAND
) )
2365 wxLogLastError(wxT("StretchBlt"));
2367 } // restore the original text and background colours
2369 // OR buffer to dest
2370 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2371 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2374 wxLogLastError(wxT("BitBlt"));
2377 // tidy up temporary DCs and bitmap
2378 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2379 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2381 #if !wxUSE_DC_CACHEING
2383 ::DeleteDC(dc_mask
);
2384 ::DeleteDC(dc_buffer
);
2385 ::DeleteObject(buffer_bmap
);
2390 else // no mask, just BitBlt() it
2392 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2393 // use StretchBlt() if available and finally fall back to BitBlt()
2395 // FIXME: use appropriate WinCE functions
2397 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2398 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2403 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2405 &ds
) == sizeof(ds
) )
2407 SET_STRETCH_BLT_MODE(GetHdc());
2409 // Figure out what co-ordinate system we're supposed to specify
2411 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2415 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2418 if ( ::StretchDIBits(GetHdc(),
2420 dstWidth
, dstHeight
,
2422 srcWidth
, srcHeight
,
2424 (LPBITMAPINFO
)&ds
.dsBmih
,
2427 ) == (int)GDI_ERROR
)
2429 // On Win9x this API fails most (all?) of the time, so
2430 // logging it becomes quite distracting. Since it falls
2431 // back to the code below this is not really serious, so
2433 //wxLogLastError(wxT("StretchDIBits"));
2442 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2446 SET_STRETCH_BLT_MODE(GetHdc());
2451 xdest
, ydest
, dstWidth
, dstHeight
,
2453 xsrc
, ysrc
, srcWidth
, srcHeight
,
2457 wxLogLastError(wxT("StretchBlt"));
2467 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2468 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2470 wxLogLastError(wxT("BitBlt"));
2482 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2484 WXMICROWIN_CHECK_HDC
2487 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2489 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2492 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2494 WXMICROWIN_CHECK_HDC
2496 // if we implement it in terms of DoGetSize() instead of directly using the
2497 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2498 // will also work for wxWindowDC and wxClientDC even though their size is
2499 // not the same as the total size of the screen
2500 int wPixels
, hPixels
;
2501 DoGetSize(&wPixels
, &hPixels
);
2505 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2507 wxCHECK_RET( wTotal
, wxT("0 width device?") );
2509 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2514 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2516 wxCHECK_RET( hTotal
, wxT("0 height device?") );
2518 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2522 wxSize
wxMSWDCImpl::GetPPI() const
2524 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2526 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2527 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2529 return wxSize(x
, y
);
2532 // ----------------------------------------------------------------------------
2534 // ----------------------------------------------------------------------------
2536 #if wxUSE_DC_CACHEING
2539 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2540 * improve it in due course, either using arrays, or simply storing pointers to one
2541 * entry for the bitmap, and two for the DCs. -- JACS
2544 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2545 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2547 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2556 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2565 wxDCCacheEntry::~wxDCCacheEntry()
2568 ::DeleteObject((HBITMAP
) m_bitmap
);
2570 ::DeleteDC((HDC
) m_dc
);
2573 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2575 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2576 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2579 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2581 if (entry
->m_depth
== depth
)
2583 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2585 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2586 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2587 if ( !entry
->m_bitmap
)
2589 wxLogLastError(wxT("CreateCompatibleBitmap"));
2591 entry
->m_width
= w
; entry
->m_height
= h
;
2597 node
= node
->GetNext();
2599 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2602 wxLogLastError(wxT("CreateCompatibleBitmap"));
2604 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2605 AddToBitmapCache(entry
);
2609 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2611 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2612 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2615 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2617 // Don't return the same one as we already have
2618 if (!notThis
|| (notThis
!= entry
))
2620 if (entry
->m_depth
== depth
)
2626 node
= node
->GetNext();
2628 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2631 wxLogLastError(wxT("CreateCompatibleDC"));
2633 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2634 AddToDCCache(entry
);
2638 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2640 sm_bitmapCache
.Append(entry
);
2643 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2645 sm_dcCache
.Append(entry
);
2648 void wxMSWDCImpl::ClearCache()
2650 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2651 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2654 // Clean up cache at app exit
2655 class wxDCModule
: public wxModule
2658 virtual bool OnInit() { return true; }
2659 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2662 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2665 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2667 #endif // wxUSE_DC_CACHEING
2669 // ----------------------------------------------------------------------------
2670 // alpha channel support
2671 // ----------------------------------------------------------------------------
2673 static bool AlphaBlt(HDC hdcDst
,
2674 int x
, int y
, int dstWidth
, int dstHeight
,
2676 int srcWidth
, int srcHeight
,
2678 const wxBitmap
& bmp
)
2680 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2681 wxASSERT_MSG( hdcDst
&& hdcSrc
, wxT("AlphaBlt(): invalid HDC") );
2683 // do we have AlphaBlend() and company in the headers?
2684 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2685 // yes, now try to see if we have it during run-time
2686 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2687 HDC
,int,int,int,int,
2691 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(wxT("AlphaBlend"));
2692 if ( pfnAlphaBlend
)
2695 bf
.BlendOp
= AC_SRC_OVER
;
2697 bf
.SourceConstantAlpha
= 0xff;
2698 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2700 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2701 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2704 // skip wxAlphaBlend() call below
2708 wxLogLastError(wxT("AlphaBlend"));
2711 wxUnusedVar(hdcSrc
);
2712 #endif // defined(AC_SRC_OVER)
2714 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2716 #ifdef wxHAS_RAW_BITMAP
2717 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2720 #else // !wxHAS_RAW_BITMAP
2721 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2722 // alpha but at least something will be shown like this)
2725 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2729 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2730 #ifdef wxHAS_RAW_BITMAP
2733 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2734 int dstWidth
, int dstHeight
,
2736 int srcWidth
, int srcHeight
,
2737 const wxBitmap
& bmpSrc
)
2739 // get the destination DC pixels
2740 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2742 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2744 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2746 wxLogLastError(wxT("BitBlt"));
2749 // combine them with the source bitmap using alpha
2750 wxAlphaPixelData
dataDst(bmpDst
),
2751 dataSrc((wxBitmap
&)bmpSrc
);
2753 wxCHECK_RET( dataDst
&& dataSrc
,
2754 wxT("failed to get raw data in wxAlphaBlend") );
2756 wxAlphaPixelData::Iterator
pDst(dataDst
),
2760 for ( int y
= 0; y
< dstHeight
; y
++ )
2762 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2764 for ( int x
= 0; x
< dstWidth
; x
++ )
2766 // source is point sampled, Alpha StretchBlit is ugly on Win95
2767 // (but does not impact performance)
2768 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2770 // note that source bitmap uses premultiplied alpha (as required by
2771 // the real AlphaBlend)
2772 const unsigned beta
= 255 - pSrc
.Alpha();
2774 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2775 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2776 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2781 pDst
= pDstRowStart
;
2782 pDst
.OffsetY(dataDst
, 1);
2785 // and finally blit them back to the destination DC
2786 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2788 wxLogLastError(wxT("BitBlt"));
2792 #endif // wxHAS_RAW_BITMAP
2794 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2795 const wxColour
& initialColour
,
2796 const wxColour
& destColour
,
2797 wxDirection nDirection
)
2799 // use native function if we have compile-time support it and can load it
2800 // during run-time (linking to it statically would make the program
2801 // unusable on earlier Windows versions)
2802 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2804 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2805 static GradientFill_t pfnGradientFill
=
2806 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(wxT("GradientFill"));
2808 if ( pfnGradientFill
)
2810 GRADIENT_RECT grect
;
2811 grect
.UpperLeft
= 0;
2812 grect
.LowerRight
= 1;
2814 // invert colours direction if not filling from left-to-right or
2816 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2818 // one vertex for upper left and one for upper-right
2819 TRIVERTEX vertices
[2];
2821 vertices
[0].x
= rect
.GetLeft();
2822 vertices
[0].y
= rect
.GetTop();
2823 vertices
[1].x
= rect
.GetRight()+1;
2824 vertices
[1].y
= rect
.GetBottom()+1;
2826 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2827 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2828 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2829 vertices
[firstVertex
].Alpha
= 0;
2830 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2831 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2832 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2833 vertices
[1 - firstVertex
].Alpha
= 0;
2835 if ( (*pfnGradientFill
)
2842 nDirection
== wxWEST
|| nDirection
== wxEAST
2843 ? GRADIENT_FILL_RECT_H
2844 : GRADIENT_FILL_RECT_V
2847 // skip call of the base class version below
2851 wxLogLastError(wxT("GradientFill"));
2853 #endif // wxUSE_DYNLIB_CLASS
2855 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2858 #if wxUSE_DYNLIB_CLASS
2860 static DWORD
wxGetDCLayout(HDC hdc
)
2862 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2864 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2866 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2869 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2871 DWORD layout
= wxGetDCLayout(GetHdc());
2873 if ( layout
== (DWORD
)-1 )
2874 return wxLayout_Default
;
2876 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2879 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2881 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2883 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2884 if ( !s_pfnSetLayout
)
2887 if ( dir
== wxLayout_Default
)
2889 dir
= wxTheApp
->GetLayoutDirection();
2890 if ( dir
== wxLayout_Default
)
2894 DWORD layout
= wxGetDCLayout(GetHdc());
2895 if ( dir
== wxLayout_RightToLeft
)
2896 layout
|= LAYOUT_RTL
;
2898 layout
&= ~LAYOUT_RTL
;
2900 s_pfnSetLayout(GetHdc(), layout
);
2903 #else // !wxUSE_DYNLIB_CLASS
2905 // we can't provide RTL support without dynamic loading, so stub it out
2906 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2908 return wxLayout_Default
;
2911 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2915 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS