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"
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 // various classes to change some DC property temporarily
154 // text background and foreground colours
155 class wxTextColoursChanger
158 wxTextColoursChanger(HDC hdc
, const wxMSWDCImpl
& dc
)
161 Change(dc
.GetTextForeground(), dc
.GetTextBackground());
164 wxTextColoursChanger(HDC hdc
, const wxColour
& colFg
, const wxColour
& colBg
)
167 Change(colFg
, colBg
);
170 ~wxTextColoursChanger()
172 if ( m_oldColFg
!= CLR_INVALID
)
173 ::SetTextColor(m_hdc
, m_oldColFg
);
174 if ( m_oldColBg
!= CLR_INVALID
)
175 ::SetBkColor(m_hdc
, m_oldColBg
);
179 // this ctor doesn't change mode immediately, call Change() later to do it
181 wxTextColoursChanger(HDC hdc
)
185 m_oldColBg
= CLR_INVALID
;
188 void Change(const wxColour
& colFg
, const wxColour
& colBg
)
192 m_oldColFg
= ::SetTextColor(m_hdc
, colFg
.GetPixel());
193 if ( m_oldColFg
== CLR_INVALID
)
195 wxLogLastError(_T("SetTextColor"));
200 m_oldColFg
= CLR_INVALID
;
205 m_oldColBg
= ::SetBkColor(m_hdc
, colBg
.GetPixel());
206 if ( m_oldColBg
== CLR_INVALID
)
208 wxLogLastError(_T("SetBkColor"));
213 m_oldColBg
= CLR_INVALID
;
222 wxDECLARE_NO_COPY_CLASS(wxTextColoursChanger
);
226 class wxBkModeChanger
229 // set background mode to opaque if mode != wxBRUSHSTYLE_TRANSPARENT
230 wxBkModeChanger(HDC hdc
, int mode
)
239 ::SetBkMode(m_hdc
, m_oldMode
);
243 // this ctor doesn't change mode immediately, call Change() later to do it
245 wxBkModeChanger(HDC hdc
) : m_hdc(hdc
) { m_oldMode
= 0; }
247 void Change(int mode
)
249 m_oldMode
= ::SetBkMode(m_hdc
, mode
== wxBRUSHSTYLE_TRANSPARENT
254 wxLogLastError(_T("SetBkMode"));
262 wxDECLARE_NO_COPY_CLASS(wxBkModeChanger
);
265 // instead of duplicating the same code which sets and then restores text
266 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
267 // encapsulate this in a small helper class
269 // wxBrushAttrsSetter: changes the text colours in the ctor if required and
270 // restores them in the dtor
271 class wxBrushAttrsSetter
: private wxBkModeChanger
,
272 private wxTextColoursChanger
275 wxBrushAttrsSetter(wxMSWDCImpl
& dc
);
278 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter
);
283 #define SET_STRETCH_BLT_MODE(hdc)
285 #else // !__WXWINCE__
287 // this class sets the stretch blit mode to COLORONCOLOR during its lifetime
289 // don't use it directly, use SET_STRETCH_BLT_MODE() macro instead as it
290 // expands to nothing under WinCE which doesn't have SetStretchBltMode()
291 class StretchBltModeChanger
294 StretchBltModeChanger(HDC hdc
)
297 m_modeOld
= ::SetStretchBltMode(m_hdc
, COLORONCOLOR
);
299 wxLogLastError(_T("SetStretchBltMode"));
302 ~StretchBltModeChanger()
304 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
305 wxLogLastError(_T("SetStretchBltMode"));
313 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
316 #define SET_STRETCH_BLT_MODE(hdc) \
317 StretchBltModeChanger wxMAKE_UNIQUE_NAME(stretchModeChanger)(hdc)
319 #endif // __WXWINCE__/!__WXWINCE__
321 #if wxUSE_DYNLIB_CLASS
323 // helper class to cache dynamically loaded libraries and not attempt reloading
325 class wxOnceOnlyDLLLoader
328 // ctor argument must be a literal string as we don't make a copy of it!
329 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
335 // return the symbol with the given name or NULL if the DLL not loaded
336 // or symbol not present
337 void *GetSymbol(const wxChar
*name
)
339 // we're prepared to handle errors here
344 m_dll
.Load(m_dllName
);
346 // reset the name whether we succeeded or failed so that we don't
347 // try again the next time
351 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
356 if ( m_dll
.IsLoaded() )
363 wxDynamicLibrary m_dll
;
364 const wxChar
*m_dllName
;
367 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(_T("msimg32"));
369 // we must ensure that DLLs are unloaded before the static objects cleanup time
370 // because we may hit the notorious DllMain() dead lock in this case if wx is
371 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
372 // under Windows because it tries to reacquire the same lock)
373 class wxGDIDLLsCleanupModule
: public wxModule
376 virtual bool OnInit() { return true; }
377 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
380 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
383 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
385 #endif // wxUSE_DYNLIB_CLASS
387 // ===========================================================================
389 // ===========================================================================
391 // ----------------------------------------------------------------------------
392 // wxBrushAttrsSetter
393 // ----------------------------------------------------------------------------
395 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
396 : wxBkModeChanger(GetHdcOf(dc
)),
397 wxTextColoursChanger(GetHdcOf(dc
))
399 const wxBrush
& brush
= dc
.GetBrush();
400 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
402 // note that Windows convention is opposite to wxWidgets one, this is
403 // why text colour becomes the background one and vice versa
404 wxTextColoursChanger::Change(dc
.GetTextBackground(),
405 dc
.GetTextForeground());
407 wxBkModeChanger::Change(dc
.GetBackgroundMode());
411 // ----------------------------------------------------------------------------
412 // wxDC MSW-specific methods
413 // ----------------------------------------------------------------------------
415 WXHDC
wxDC::GetHDC() const
417 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
418 return impl
? impl
->GetHDC() : 0;
421 // ---------------------------------------------------------------------------
423 // ---------------------------------------------------------------------------
425 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
432 wxMSWDCImpl::~wxMSWDCImpl()
436 SelectOldObjects(m_hDC
);
438 // if we own the HDC, we delete it, otherwise we just release it
442 ::DeleteDC(GetHdc());
444 else // we don't own our HDC
448 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
452 // Must have been a wxScreenDC
453 ::ReleaseDC((HWND
) NULL
, GetHdc());
459 // This will select current objects out of the DC,
460 // which is what you have to do before deleting the
462 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
468 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
469 if (m_selectedBitmap
.IsOk())
471 m_selectedBitmap
.SetSelectedInto(NULL
);
477 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
482 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
487 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
494 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
497 #endif // wxUSE_PALETTE
500 m_brush
= wxNullBrush
;
503 m_palette
= wxNullPalette
;
504 #endif // wxUSE_PALETTE
506 m_backgroundBrush
= wxNullBrush
;
507 m_selectedBitmap
= wxNullBitmap
;
510 // ---------------------------------------------------------------------------
512 // ---------------------------------------------------------------------------
514 void wxMSWDCImpl::UpdateClipBox()
519 ::GetClipBox(GetHdc(), &rect
);
521 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
522 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
523 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
524 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
528 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
530 // check if we should try to retrieve the clipping region possibly not set
531 // by our SetClippingRegion() but preset by Windows:this can only happen
532 // when we're associated with an existing HDC usign SetHDC(), see there
533 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
535 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
536 self
->UpdateClipBox();
538 if ( !m_clipX1
&& !m_clipX2
)
539 self
->m_clipping
= false;
542 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
545 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
546 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
548 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
552 // note that we combine the new clipping region with the existing one: this
553 // is compatible with what the other ports do and is the documented
554 // behaviour now (starting with 2.3.3)
555 #if defined(__WXWINCE__)
557 if ( !::GetClipBox(GetHdc(), &rectClip
) )
560 // GetClipBox returns logical coordinates, so transform to device
561 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
562 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
563 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
564 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
566 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
567 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
568 rectClip
.right
, rectClip
.bottom
);
570 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
572 ::SelectClipRgn(GetHdc(), hrgnDest
);
575 ::DeleteObject(hrgnClipOld
);
576 ::DeleteObject(hrgnDest
);
578 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
580 wxLogLastError(_T("ExtSelectClipRgn"));
584 #endif // WinCE/!WinCE
591 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
593 // the region coords are always the device ones, so do the translation
596 // FIXME: possible +/-1 error here, to check!
597 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
599 LogicalToDeviceX(x
+ w
),
600 LogicalToDeviceY(y
+ h
));
603 wxLogLastError(_T("CreateRectRgn"));
607 SetClippingHrgn((WXHRGN
)hrgn
);
609 ::DeleteObject(hrgn
);
613 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
615 SetClippingHrgn(region
.GetHRGN());
618 void wxMSWDCImpl::DestroyClippingRegion()
622 if (m_clipping
&& m_hDC
)
625 // On a PocketPC device (not necessarily emulator), resetting
626 // the clip region as per the old method causes bad display
627 // problems. In fact setting a null region is probably OK
628 // on desktop WIN32 also, since the WIN32 docs imply that the user
629 // clipping region is independent from the paint clipping region.
630 ::SelectClipRgn(GetHdc(), 0);
632 // TODO: this should restore the previous clipping region,
633 // so that OnPaint processing works correctly, and the update
634 // clipping region doesn't get destroyed after the first
635 // DestroyClippingRegion.
636 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
637 ::SelectClipRgn(GetHdc(), rgn
);
642 wxDCImpl::DestroyClippingRegion();
645 // ---------------------------------------------------------------------------
646 // query capabilities
647 // ---------------------------------------------------------------------------
649 bool wxMSWDCImpl::CanDrawBitmap() const
654 bool wxMSWDCImpl::CanGetTextExtent() const
656 #ifdef __WXMICROWIN__
657 // TODO Extend MicroWindows' GetDeviceCaps function
660 // What sort of display is it?
661 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
663 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
667 int wxMSWDCImpl::GetDepth() const
669 WXMICROWIN_CHECK_HDC_RET(16)
671 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
674 // ---------------------------------------------------------------------------
676 // ---------------------------------------------------------------------------
678 void wxMSWDCImpl::Clear()
685 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
689 // No, I think we should simply ignore this if printing on e.g.
691 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
692 if (!m_selectedBitmap
.IsOk())
695 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
696 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
697 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
701 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
704 DWORD colour
= ::GetBkColor(GetHdc());
705 HBRUSH brush
= ::CreateSolidBrush(colour
);
706 ::FillRect(GetHdc(), &rect
, brush
);
707 ::DeleteObject(brush
);
709 RealizeScaleAndOrigin();
712 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
713 wxCoord
WXUNUSED_IN_WINCE(y
),
714 const wxColour
& WXUNUSED_IN_WINCE(col
),
715 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
720 WXMICROWIN_CHECK_HDC_RET(false)
722 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
724 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
725 : FLOODFILLBORDER
) ) ;
728 // quoting from the MSDN docs:
730 // Following are some of the reasons this function might fail:
732 // * The filling could not be completed.
733 // * The specified point has the boundary color specified by the
734 // crColor parameter (if FLOODFILLBORDER was requested).
735 // * The specified point does not have the color specified by
736 // crColor (if FLOODFILLSURFACE was requested)
737 // * The point is outside the clipping region that is, it is not
738 // visible on the device.
740 wxLogLastError(wxT("ExtFloodFill"));
743 CalcBoundingBox(x
, y
);
749 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
751 WXMICROWIN_CHECK_HDC_RET(false)
753 wxCHECK_MSG( col
, false, _T("NULL colour parameter in wxMSWDCImpl::GetPixel") );
755 // get the color of the pixel
756 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
758 wxRGBToColour(*col
, pixelcolor
);
763 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
767 wxCoord x1
= x
-VIEWPORT_EXTENT
;
768 wxCoord y1
= y
-VIEWPORT_EXTENT
;
769 wxCoord x2
= x
+VIEWPORT_EXTENT
;
770 wxCoord y2
= y
+VIEWPORT_EXTENT
;
772 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
773 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
775 CalcBoundingBox(x1
, y1
);
776 CalcBoundingBox(x2
, y2
);
779 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
783 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
785 CalcBoundingBox(x1
, y1
);
786 CalcBoundingBox(x2
, y2
);
789 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
790 // and ending at (x2, y2)
791 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
792 wxCoord x2
, wxCoord y2
,
793 wxCoord xc
, wxCoord yc
)
797 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
801 // Slower emulation since WinCE doesn't support Pie and Arc
802 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
804 sa
= -sa
; // below center
805 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
806 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
811 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
813 // treat the special case of full circle separately
814 if ( x1
== x2
&& y1
== y2
)
816 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
820 wxCoord xx1
= XLOG2DEV(x1
);
821 wxCoord yy1
= YLOG2DEV(y1
);
822 wxCoord xx2
= XLOG2DEV(x2
);
823 wxCoord yy2
= YLOG2DEV(y2
);
824 wxCoord xxc
= XLOG2DEV(xc
);
825 wxCoord yyc
= YLOG2DEV(yc
);
828 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
830 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
831 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
832 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
833 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
835 if ( m_brush
.IsOk() && m_brush
.GetStyle() != wxBRUSHSTYLE_TRANSPARENT
)
837 // Have to add 1 to bottom-right corner of rectangle
838 // to make semi-circles look right (crooked line otherwise).
839 // Unfortunately this is not a reliable method, depends
840 // on the size of shape.
841 // TODO: figure out why this happens!
842 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
846 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
849 CalcBoundingBox(xc
- r
, yc
- r
);
850 CalcBoundingBox(xc
+ r
, yc
+ r
);
854 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
855 wxCoord width
, wxCoord height
)
857 // cases when we don't have DrawFrameControl()
858 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
859 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
861 wxCoord x2
= x1
+ width
,
871 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
873 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
876 CalcBoundingBox(x1
, y1
);
877 CalcBoundingBox(x2
, y2
);
878 #endif // Microwin/Normal
881 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
885 COLORREF color
= 0x00ffffff;
888 color
= m_pen
.GetColour().GetPixel();
891 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
893 CalcBoundingBox(x
, y
);
896 void wxMSWDCImpl::DoDrawPolygon(int n
,
900 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
904 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
906 // Do things less efficiently if we have offsets
907 if (xoffset
!= 0 || yoffset
!= 0)
909 POINT
*cpoints
= new POINT
[n
];
911 for (i
= 0; i
< n
; i
++)
913 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
914 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
916 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
919 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
921 (void)Polygon(GetHdc(), cpoints
, n
);
923 SetPolyFillMode(GetHdc(),prev
);
930 for (i
= 0; i
< n
; i
++)
931 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
934 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
936 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
938 SetPolyFillMode(GetHdc(),prev
);
944 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
949 wxPolygonFillMode fillStyle
)
952 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
956 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
958 for (i
= cnt
= 0; i
< n
; i
++)
961 // Do things less efficiently if we have offsets
962 if (xoffset
!= 0 || yoffset
!= 0)
964 POINT
*cpoints
= new POINT
[cnt
];
965 for (i
= 0; i
< cnt
; i
++)
967 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
968 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
970 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
973 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
975 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
977 SetPolyFillMode(GetHdc(),prev
);
983 for (i
= 0; i
< cnt
; i
++)
984 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
987 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
989 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
991 SetPolyFillMode(GetHdc(),prev
);
998 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
1000 WXMICROWIN_CHECK_HDC
1002 // Do things less efficiently if we have offsets
1003 if (xoffset
!= 0 || yoffset
!= 0)
1005 POINT
*cpoints
= new POINT
[n
];
1007 for (i
= 0; i
< n
; i
++)
1009 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
1010 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
1012 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
1014 (void)Polyline(GetHdc(), cpoints
, n
);
1020 for (i
= 0; i
< n
; i
++)
1021 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
1023 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
1027 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1029 WXMICROWIN_CHECK_HDC
1031 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1033 wxCoord x2
= x
+ width
;
1034 wxCoord y2
= y
+ height
;
1036 wxCoord x2dev
= XLOG2DEV(x2
),
1037 y2dev
= YLOG2DEV(y2
);
1039 // Windows (but not Windows CE) draws the filled rectangles without outline
1040 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1041 // and we want them to have the same size regardless of which pen is used
1043 if ( m_pen
.IsOk() && m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
1048 #endif // !__WXWINCE__
1050 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
1052 CalcBoundingBox(x
, y
);
1053 CalcBoundingBox(x2
, y2
);
1056 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
1058 WXMICROWIN_CHECK_HDC
1060 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1062 // Now, a negative radius value is interpreted to mean
1063 // 'the proportion of the smallest X or Y dimension'
1067 double smallest
= (width
< height
) ? width
: height
;
1068 radius
= (- radius
* smallest
);
1071 wxCoord x2
= (x
+width
);
1072 wxCoord y2
= (y
+height
);
1074 // Windows draws the filled rectangles without outline (i.e. drawn with a
1075 // transparent pen) one pixel smaller in both directions and we want them
1076 // to have the same size regardless of which pen is used - adjust
1077 if ( m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
1083 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1084 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1086 CalcBoundingBox(x
, y
);
1087 CalcBoundingBox(x2
, y2
);
1090 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1092 WXMICROWIN_CHECK_HDC
1094 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1096 // +1 below makes the ellipse more similar to other platforms.
1097 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1098 wxCoord x2
= x
+ width
+ 1;
1099 wxCoord y2
= y
+ height
+ 1;
1101 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1102 // pen doesn't draw anything. Should we provide a workaround?
1104 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1106 CalcBoundingBox(x
, y
);
1107 CalcBoundingBox(x2
, y2
);
1110 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1111 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1113 // quadratic b-spline to cubic bezier spline conversion
1115 // quadratic spline with control points P0,P1,P2
1116 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1118 // bezier spline with control points B0,B1,B2,B3
1119 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1121 // control points of bezier spline calculated from b-spline
1123 // B1 = (2*P1 + P0)/3
1124 // B2 = (2*P1 + P2)/3
1127 WXMICROWIN_CHECK_HDC
1129 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1131 const size_t n_points
= points
->GetCount();
1132 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1134 const size_t n_bezier_points
= n_points
* 3 + 1;
1135 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1136 size_t bezier_pos
= 0;
1137 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1139 wxPointList::compatibility_iterator node
= points
->GetFirst();
1140 wxPoint
*p
= node
->GetData();
1141 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1142 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1144 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1147 node
= node
->GetNext();
1148 p
= node
->GetData();
1152 cx1
= ( x1
+ x2
) / 2;
1153 cy1
= ( y1
+ y2
) / 2;
1154 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1155 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1157 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1161 while ((node
= node
->GetNext()) != NULL
)
1163 while ((node
= node
->GetNext()))
1164 #endif // !wxUSE_STL
1166 p
= (wxPoint
*)node
->GetData();
1171 cx4
= (x1
+ x2
) / 2;
1172 cy4
= (y1
+ y2
) / 2;
1173 // B0 is B3 of previous segment
1175 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1176 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1179 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1180 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1183 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1184 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1190 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1192 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1193 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1195 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1198 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1202 #endif // wxUSE_SPLINES
1204 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1205 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1208 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1211 WXMICROWIN_CHECK_HDC
1213 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1218 int rx1
= XLOG2DEV(x
+w
/2);
1219 int ry1
= YLOG2DEV(y
+h
/2);
1226 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1227 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1228 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1229 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1231 // Swap start and end positions if the end angle is less than the start angle.
1242 // draw pie with NULL_PEN first and then outline otherwise a line is
1243 // drawn from the start and end points to the centre
1244 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1247 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1248 rx1
, ry1
, rx2
, ry2
);
1252 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1253 rx1
, ry1
-1, rx2
, ry2
-1);
1256 ::SelectObject(GetHdc(), hpenOld
);
1258 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1259 rx1
, ry1
, rx2
, ry2
);
1261 CalcBoundingBox(x
, y
);
1262 CalcBoundingBox(x2
, y2
);
1266 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1268 WXMICROWIN_CHECK_HDC
1270 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1273 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1275 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1278 CalcBoundingBox(x
, y
);
1279 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1282 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1284 WXMICROWIN_CHECK_HDC
1286 wxCHECK_RET( bmp
.IsOk(), _T("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1288 int width
= bmp
.GetWidth(),
1289 height
= bmp
.GetHeight();
1291 HBITMAP hbmpMask
= 0;
1294 HPALETTE oldPal
= 0;
1295 #endif // wxUSE_PALETTE
1297 if ( bmp
.HasAlpha() )
1300 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1302 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1306 SET_STRETCH_BLT_MODE(GetHdc());
1310 wxMask
*mask
= bmp
.GetMask();
1312 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1316 // don't give assert here because this would break existing
1317 // programs - just silently ignore useMask parameter
1324 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1326 // On some systems, MaskBlt succeeds yet is much much slower
1327 // than the wxWidgets fall-back implementation. So we need
1328 // to be able to switch this on and off at runtime.
1330 #if wxUSE_SYSTEM_OPTIONS
1331 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1335 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1336 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1338 wxPalette
*pal
= bmp
.GetPalette();
1339 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1341 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1342 ::RealizePalette(hdcMem
);
1344 #endif // wxUSE_PALETTE
1346 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1349 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1353 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1354 #endif // wxUSE_PALETTE
1356 ::SelectObject(hdcMem
, hOldBitmap
);
1363 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1367 memDC
.SelectObjectAsSource(bmp
);
1369 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1371 memDC
.SelectObject(wxNullBitmap
);
1374 else // no mask, just use BitBlt()
1377 HDC memdc
= ::CreateCompatibleDC( cdc
);
1378 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1380 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1382 wxTextColoursChanger
textCol(GetHdc(), *this);
1385 wxPalette
*pal
= bmp
.GetPalette();
1386 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1388 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1389 ::RealizePalette(memdc
);
1391 #endif // wxUSE_PALETTE
1393 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1394 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1398 ::SelectPalette(memdc
, oldPal
, FALSE
);
1399 #endif // wxUSE_PALETTE
1401 ::SelectObject( memdc
, hOldBitmap
);
1402 ::DeleteDC( memdc
);
1406 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1408 WXMICROWIN_CHECK_HDC
1410 DrawAnyText(text
, x
, y
);
1412 // update the bounding box
1413 CalcBoundingBox(x
, y
);
1416 GetOwner()->GetTextExtent(text
, &w
, &h
);
1417 CalcBoundingBox(x
+ w
, y
+ h
);
1420 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1422 WXMICROWIN_CHECK_HDC
1424 // prepare for drawing the text
1425 wxTextColoursChanger
textCol(GetHdc(), *this);
1427 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1429 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1430 text
.c_str(), text
.length(), NULL
) == 0 )
1432 wxLogLastError(wxT("TextOut"));
1436 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1437 wxCoord x
, wxCoord y
,
1440 WXMICROWIN_CHECK_HDC
1442 // we test that we have some font because otherwise we should still use the
1443 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1444 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1445 // font for drawing rotated fonts unfortunately)
1446 if ( (angle
== 0.0) && m_font
.IsOk() )
1448 DoDrawText(text
, x
, y
);
1450 #ifndef __WXMICROWIN__
1453 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1454 // because it's not TrueType and so can't have non zero
1455 // orientation/escapement under Win9x
1456 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1457 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1459 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1461 wxLogLastError(wxT("GetObject(hfont)"));
1464 // GDI wants the angle in tenth of degree
1465 long angle10
= (long)(angle
* 10);
1466 lf
.lfEscapement
= angle10
;
1467 lf
. lfOrientation
= angle10
;
1469 hfont
= ::CreateFontIndirect(&lf
);
1472 wxLogLastError(wxT("CreateFont"));
1476 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1478 DrawAnyText(text
, x
, y
);
1480 (void)::SelectObject(GetHdc(), hfontOld
);
1481 (void)::DeleteObject(hfont
);
1484 // call the bounding box by adding all four vertices of the rectangle
1485 // containing the text to it (simpler and probably not slower than
1486 // determining which of them is really topmost/leftmost/...)
1488 GetOwner()->GetTextExtent(text
, &w
, &h
);
1490 double rad
= DegToRad(angle
);
1492 // "upper left" and "upper right"
1493 CalcBoundingBox(x
, y
);
1494 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1496 // "bottom left" and "bottom right"
1497 x
+= (wxCoord
)(h
*sin(rad
));
1498 y
+= (wxCoord
)(h
*cos(rad
));
1499 CalcBoundingBox(x
, y
);
1500 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1505 // ---------------------------------------------------------------------------
1507 // ---------------------------------------------------------------------------
1511 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1513 WXMICROWIN_CHECK_HDC
1515 // Set the old object temporarily, in case the assignment deletes an object
1516 // that's not yet selected out.
1519 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1523 if ( m_palette
.IsOk() )
1525 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1526 GetHpaletteOf(m_palette
),
1529 m_oldPalette
= (WXHPALETTE
) oldPal
;
1532 ::RealizePalette(GetHdc());
1536 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1538 if ( palette
.IsOk() )
1540 m_palette
= palette
;
1541 DoSelectPalette(true);
1545 void wxMSWDCImpl::InitializePalette()
1547 if ( wxDisplayDepth() <= 8 )
1549 // look for any window or parent that has a custom palette. If any has
1550 // one then we need to use it in drawing operations
1551 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1553 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1554 if ( m_hasCustomPalette
)
1556 m_palette
= win
->GetPalette();
1558 // turn on MSW translation for this palette
1564 #endif // wxUSE_PALETTE
1566 // SetFont/Pen/Brush() really ask to be implemented as a single template
1567 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1569 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1571 WXMICROWIN_CHECK_HDC
1573 if ( font
== m_font
)
1578 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1579 if ( hfont
== HGDI_ERROR
)
1581 wxLogLastError(_T("SelectObject(font)"));
1586 m_oldFont
= (WXHFONT
)hfont
;
1591 else // invalid font, reset the current font
1595 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1597 wxLogLastError(_T("SelectObject(old font)"));
1603 m_font
= wxNullFont
;
1607 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1609 WXMICROWIN_CHECK_HDC
1616 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1617 if ( hpen
== HGDI_ERROR
)
1619 wxLogLastError(_T("SelectObject(pen)"));
1624 m_oldPen
= (WXHPEN
)hpen
;
1629 else // invalid pen, reset the current pen
1633 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1635 wxLogLastError(_T("SelectObject(old pen)"));
1645 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1647 WXMICROWIN_CHECK_HDC
1649 if ( brush
== m_brush
)
1654 // we must make sure the brush is aligned with the logical coordinates
1655 // before selecting it
1656 wxBitmap
*stipple
= brush
.GetStipple();
1657 if ( stipple
&& stipple
->IsOk() )
1659 if ( !::SetBrushOrgEx
1662 m_deviceOriginX
% stipple
->GetWidth(),
1663 m_deviceOriginY
% stipple
->GetHeight(),
1664 NULL
// [out] previous brush origin
1667 wxLogLastError(_T("SetBrushOrgEx()"));
1671 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1672 if ( hbrush
== HGDI_ERROR
)
1674 wxLogLastError(_T("SelectObject(brush)"));
1679 m_oldBrush
= (WXHBRUSH
)hbrush
;
1684 else // invalid brush, reset the current brush
1688 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1690 wxLogLastError(_T("SelectObject(old brush)"));
1696 m_brush
= wxNullBrush
;
1700 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1702 WXMICROWIN_CHECK_HDC
1704 m_backgroundBrush
= brush
;
1706 if ( m_backgroundBrush
.IsOk() )
1708 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1712 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1714 WXMICROWIN_CHECK_HDC
1716 m_backgroundMode
= mode
;
1718 // SetBackgroundColour now only refers to text background
1719 // and m_backgroundMode is used there
1722 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1724 WXMICROWIN_CHECK_HDC
1726 m_logicalFunction
= function
;
1731 void wxMSWDCImpl::SetRop(WXHDC dc
)
1733 if ( !dc
|| m_logicalFunction
< 0 )
1738 switch (m_logicalFunction
)
1740 case wxCLEAR
: rop
= R2_BLACK
; break;
1741 case wxXOR
: rop
= R2_XORPEN
; break;
1742 case wxINVERT
: rop
= R2_NOT
; break;
1743 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1744 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1745 case wxCOPY
: rop
= R2_COPYPEN
; break;
1746 case wxAND
: rop
= R2_MASKPEN
; break;
1747 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1748 case wxNO_OP
: rop
= R2_NOP
; break;
1749 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1750 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1751 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1752 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1753 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1754 case wxOR
: rop
= R2_MERGEPEN
; break;
1755 case wxSET
: rop
= R2_WHITE
; break;
1757 wxFAIL_MSG( wxS("unknown logical function") );
1761 SetROP2(GetHdc(), rop
);
1764 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1766 // We might be previewing, so return true to let it continue.
1770 void wxMSWDCImpl::EndDoc()
1774 void wxMSWDCImpl::StartPage()
1778 void wxMSWDCImpl::EndPage()
1782 // ---------------------------------------------------------------------------
1784 // ---------------------------------------------------------------------------
1786 wxCoord
wxMSWDCImpl::GetCharHeight() const
1788 WXMICROWIN_CHECK_HDC_RET(0)
1790 TEXTMETRIC lpTextMetric
;
1792 GetTextMetrics(GetHdc(), &lpTextMetric
);
1794 return lpTextMetric
.tmHeight
;
1797 wxCoord
wxMSWDCImpl::GetCharWidth() const
1799 WXMICROWIN_CHECK_HDC_RET(0)
1801 TEXTMETRIC lpTextMetric
;
1803 GetTextMetrics(GetHdc(), &lpTextMetric
);
1805 return lpTextMetric
.tmAveCharWidth
;
1808 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1809 wxCoord
*descent
, wxCoord
*externalLeading
,
1810 const wxFont
*font
) const
1812 #ifdef __WXMICROWIN__
1817 if (descent
) *descent
= 0;
1818 if (externalLeading
) *externalLeading
= 0;
1821 #endif // __WXMICROWIN__
1826 wxASSERT_MSG( font
->IsOk(), _T("invalid font in wxMSWDCImpl::GetTextExtent") );
1828 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1830 else // don't change the font
1836 const size_t len
= string
.length();
1837 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1839 wxLogLastError(_T("GetTextExtentPoint32()"));
1842 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1843 // the result computed by GetTextExtentPoint32() may be too small as it
1844 // accounts for under/overhang of the first/last character while we want
1845 // just the bounding rect for this string so adjust the width as needed
1846 // (using API not available in 2002 SDKs of WinCE)
1850 const wxChar chFirst
= *string
.begin();
1851 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1853 if ( width
.abcA
< 0 )
1854 sizeRect
.cx
-= width
.abcA
;
1858 const wxChar chLast
= *string
.rbegin();
1859 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1861 //else: we already have the width of the last character
1863 if ( width
.abcC
< 0 )
1864 sizeRect
.cx
-= width
.abcC
;
1866 //else: GetCharABCWidths() failed, not a TrueType font?
1868 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1871 ::GetTextMetrics(GetHdc(), &tm
);
1878 *descent
= tm
.tmDescent
;
1879 if (externalLeading
)
1880 *externalLeading
= tm
.tmExternalLeading
;
1884 ::SelectObject(GetHdc(), hfontOld
);
1889 // Each element of the array will be the width of the string up to and
1890 // including the coresoponding character in text.
1892 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1894 static int maxLenText
= -1;
1895 static int maxWidth
= -1;
1898 int stlen
= text
.length();
1900 if (maxLenText
== -1)
1902 // Win9x and WinNT+ have different limits
1903 int version
= wxGetOsVersion();
1904 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1905 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1909 widths
.Add(0, stlen
); // fill the array with zeros
1913 if (!::GetTextExtentExPoint(GetHdc(),
1914 text
.c_str(), // string to check
1915 wxMin(stlen
, maxLenText
),
1917 &fit
, // [out] count of chars
1919 &widths
[0], // array to fill
1923 wxLogLastError(wxT("GetTextExtentExPoint"));
1930 void wxMSWDCImpl::RealizeScaleAndOrigin()
1932 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1933 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1934 // noticeable difference between these mapping modes
1936 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1938 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1939 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1941 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1942 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1944 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1945 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1949 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1951 WXMICROWIN_CHECK_HDC
1953 m_mappingMode
= mode
;
1955 if ( mode
== wxMM_TEXT
)
1958 m_logicalScaleY
= 1.0;
1960 else // need to do some calculations
1962 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1963 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1964 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1965 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1967 if ( (mm_width
== 0) || (mm_height
== 0) )
1969 // we can't calculate mm2pixels[XY] then!
1973 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1974 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1979 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1980 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1984 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1985 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1989 m_logicalScaleX
= mm2pixelsX
;
1990 m_logicalScaleY
= mm2pixelsY
;
1994 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1995 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1999 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
2003 ComputeScaleAndOrigin();
2005 RealizeScaleAndOrigin();
2008 void wxMSWDCImpl::SetUserScale(double x
, double y
)
2010 WXMICROWIN_CHECK_HDC
2012 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
2015 wxDCImpl::SetUserScale(x
,y
);
2017 RealizeScaleAndOrigin();
2020 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2023 WXMICROWIN_CHECK_HDC
2025 int signX
= xLeftRight
? 1 : -1,
2026 signY
= yBottomUp
? -1 : 1;
2028 if (signX
== m_signX
&& signY
== m_signY
)
2031 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2033 RealizeScaleAndOrigin();
2036 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2038 WXMICROWIN_CHECK_HDC
2040 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2043 wxDCImpl::SetLogicalOrigin( x
, y
);
2045 RealizeScaleAndOrigin();
2048 // For use by wxWidgets only, unless custom units are required.
2049 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2051 WXMICROWIN_CHECK_HDC
2053 wxDCImpl::SetLogicalScale(x
,y
);
2056 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2058 WXMICROWIN_CHECK_HDC
2060 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2063 wxDCImpl::SetDeviceOrigin( x
, y
);
2065 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2068 // ---------------------------------------------------------------------------
2070 // ---------------------------------------------------------------------------
2072 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2073 wxCoord dstWidth
, wxCoord dstHeight
,
2075 wxCoord srcX
, wxCoord srcY
,
2076 wxRasterOperationMode rop
, bool useMask
,
2077 wxCoord srcMaskX
, wxCoord srcMaskY
)
2079 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2082 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2083 wxCoord dstWidth
, wxCoord dstHeight
,
2085 wxCoord xsrc
, wxCoord ysrc
,
2086 wxCoord srcWidth
, wxCoord srcHeight
,
2087 wxRasterOperationMode rop
, bool useMask
,
2088 wxCoord xsrcMask
, wxCoord ysrcMask
)
2090 wxCHECK_MSG( source
, false, _T("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2092 WXMICROWIN_CHECK_HDC_RET(false)
2094 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2097 // TODO: Do we want to be able to blit from other DCs too?
2101 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2103 // if either the source or destination has alpha channel, we must use
2104 // AlphaBlt() as other function don't handle it correctly
2105 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2106 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2107 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2109 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2110 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2114 wxMask
*mask
= NULL
;
2117 mask
= bmpSrc
.GetMask();
2119 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2121 // don't give assert here because this would break existing
2122 // programs - just silently ignore useMask parameter
2127 if (xsrcMask
== -1 && ysrcMask
== -1)
2129 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2132 wxTextColoursChanger
textCol(GetHdc(), *this);
2137 case wxXOR
: dwRop
= SRCINVERT
; break;
2138 case wxINVERT
: dwRop
= DSTINVERT
; break;
2139 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2140 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2141 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2142 case wxSET
: dwRop
= WHITENESS
; break;
2143 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2144 case wxAND
: dwRop
= SRCAND
; break;
2145 case wxOR
: dwRop
= SRCPAINT
; break;
2146 case wxEQUIV
: dwRop
= 0x00990066; break;
2147 case wxNAND
: dwRop
= 0x007700E6; break;
2148 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2149 case wxCOPY
: dwRop
= SRCCOPY
; break;
2150 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2151 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2152 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2154 wxFAIL_MSG( wxT("unsupported logical function") );
2158 bool success
= false;
2163 // we want the part of the image corresponding to the mask to be
2164 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2165 // meaning of fg and bg is inverted which corresponds to wxWin notion
2166 // of the mask which is also contrary to the Windows one)
2168 // On some systems, MaskBlt succeeds yet is much much slower
2169 // than the wxWidgets fall-back implementation. So we need
2170 // to be able to switch this on and off at runtime.
2171 #if wxUSE_SYSTEM_OPTIONS
2172 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2175 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2180 xdest
, ydest
, dstWidth
, dstHeight
,
2183 (HBITMAP
)mask
->GetMaskBitmap(),
2185 MAKEROP4(dwRop
, DSTCOPY
)
2193 // Blit bitmap with mask
2196 HBITMAP buffer_bmap
;
2198 #if wxUSE_DC_CACHEING
2199 // create a temp buffer bitmap and DCs to access it and the mask
2200 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2201 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2203 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2204 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2206 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2207 dstWidth
, dstHeight
);
2209 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2210 #else // !wxUSE_DC_CACHEING
2211 // create a temp buffer bitmap and DCs to access it and the mask
2212 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2213 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2214 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2215 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2216 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2217 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2219 // copy dest to buffer
2220 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2221 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2223 wxLogLastError(wxT("BitBlt"));
2226 SET_STRETCH_BLT_MODE(GetHdc());
2228 // copy src to buffer using selected raster op
2229 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2230 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2232 wxLogLastError(wxT("StretchBlt"));
2235 // set masked area in buffer to BLACK
2237 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2238 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2239 dc_mask
, xsrcMask
, ysrcMask
,
2240 srcWidth
, srcHeight
, SRCAND
) )
2242 wxLogLastError(wxT("StretchBlt"));
2245 // set unmasked area in dest to BLACK
2246 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2247 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2248 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2249 dc_mask
, xsrcMask
, ysrcMask
,
2250 srcWidth
, srcHeight
, SRCAND
) )
2252 wxLogLastError(wxT("StretchBlt"));
2254 } // restore the original text and background colours
2256 // OR buffer to dest
2257 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2258 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2261 wxLogLastError(wxT("BitBlt"));
2264 // tidy up temporary DCs and bitmap
2265 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2266 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2268 #if !wxUSE_DC_CACHEING
2270 ::DeleteDC(dc_mask
);
2271 ::DeleteDC(dc_buffer
);
2272 ::DeleteObject(buffer_bmap
);
2277 else // no mask, just BitBlt() it
2279 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2280 // use StretchBlt() if available and finally fall back to BitBlt()
2282 // FIXME: use appropriate WinCE functions
2284 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2285 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2290 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2292 &ds
) == sizeof(ds
) )
2294 SET_STRETCH_BLT_MODE(GetHdc());
2296 // Figure out what co-ordinate system we're supposed to specify
2298 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2302 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2305 if ( ::StretchDIBits(GetHdc(),
2307 dstWidth
, dstHeight
,
2309 srcWidth
, srcHeight
,
2311 (LPBITMAPINFO
)&ds
.dsBmih
,
2314 ) == (int)GDI_ERROR
)
2316 // On Win9x this API fails most (all?) of the time, so
2317 // logging it becomes quite distracting. Since it falls
2318 // back to the code below this is not really serious, so
2320 //wxLogLastError(wxT("StretchDIBits"));
2329 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2333 SET_STRETCH_BLT_MODE(GetHdc());
2338 xdest
, ydest
, dstWidth
, dstHeight
,
2340 xsrc
, ysrc
, srcWidth
, srcHeight
,
2344 wxLogLastError(_T("StretchBlt"));
2354 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2355 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2357 wxLogLastError(_T("BitBlt"));
2369 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2371 WXMICROWIN_CHECK_HDC
2374 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2376 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2379 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2381 WXMICROWIN_CHECK_HDC
2383 // if we implement it in terms of DoGetSize() instead of directly using the
2384 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2385 // will also work for wxWindowDC and wxClientDC even though their size is
2386 // not the same as the total size of the screen
2387 int wPixels
, hPixels
;
2388 DoGetSize(&wPixels
, &hPixels
);
2392 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2394 wxCHECK_RET( wTotal
, _T("0 width device?") );
2396 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2401 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2403 wxCHECK_RET( hTotal
, _T("0 height device?") );
2405 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2409 wxSize
wxMSWDCImpl::GetPPI() const
2411 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2413 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2414 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2416 return wxSize(x
, y
);
2419 // ----------------------------------------------------------------------------
2421 // ----------------------------------------------------------------------------
2423 #if wxUSE_DC_CACHEING
2426 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2427 * improve it in due course, either using arrays, or simply storing pointers to one
2428 * entry for the bitmap, and two for the DCs. -- JACS
2431 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2432 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2434 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2443 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2452 wxDCCacheEntry::~wxDCCacheEntry()
2455 ::DeleteObject((HBITMAP
) m_bitmap
);
2457 ::DeleteDC((HDC
) m_dc
);
2460 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2462 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2463 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2466 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2468 if (entry
->m_depth
== depth
)
2470 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2472 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2473 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2474 if ( !entry
->m_bitmap
)
2476 wxLogLastError(wxT("CreateCompatibleBitmap"));
2478 entry
->m_width
= w
; entry
->m_height
= h
;
2484 node
= node
->GetNext();
2486 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2489 wxLogLastError(wxT("CreateCompatibleBitmap"));
2491 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2492 AddToBitmapCache(entry
);
2496 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2498 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2499 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2502 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2504 // Don't return the same one as we already have
2505 if (!notThis
|| (notThis
!= entry
))
2507 if (entry
->m_depth
== depth
)
2513 node
= node
->GetNext();
2515 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2518 wxLogLastError(wxT("CreateCompatibleDC"));
2520 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2521 AddToDCCache(entry
);
2525 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2527 sm_bitmapCache
.Append(entry
);
2530 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2532 sm_dcCache
.Append(entry
);
2535 void wxMSWDCImpl::ClearCache()
2537 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2538 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2541 // Clean up cache at app exit
2542 class wxDCModule
: public wxModule
2545 virtual bool OnInit() { return true; }
2546 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2549 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2552 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2554 #endif // wxUSE_DC_CACHEING
2556 // ----------------------------------------------------------------------------
2557 // alpha channel support
2558 // ----------------------------------------------------------------------------
2560 static bool AlphaBlt(HDC hdcDst
,
2561 int x
, int y
, int dstWidth
, int dstHeight
,
2563 int srcWidth
, int srcHeight
,
2565 const wxBitmap
& bmp
)
2567 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2568 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2570 // do we have AlphaBlend() and company in the headers?
2571 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2572 // yes, now try to see if we have it during run-time
2573 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2574 HDC
,int,int,int,int,
2578 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(_T("AlphaBlend"));
2579 if ( pfnAlphaBlend
)
2582 bf
.BlendOp
= AC_SRC_OVER
;
2584 bf
.SourceConstantAlpha
= 0xff;
2585 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2587 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2588 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2591 // skip wxAlphaBlend() call below
2595 wxLogLastError(_T("AlphaBlend"));
2598 wxUnusedVar(hdcSrc
);
2599 #endif // defined(AC_SRC_OVER)
2601 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2603 #ifdef wxHAS_RAW_BITMAP
2604 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2607 #else // !wxHAS_RAW_BITMAP
2608 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2609 // alpha but at least something will be shown like this)
2612 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2616 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2617 #ifdef wxHAS_RAW_BITMAP
2620 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2621 int dstWidth
, int dstHeight
,
2623 int srcWidth
, int srcHeight
,
2624 const wxBitmap
& bmpSrc
)
2626 // get the destination DC pixels
2627 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2629 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2631 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2633 wxLogLastError(_T("BitBlt"));
2636 // combine them with the source bitmap using alpha
2637 wxAlphaPixelData
dataDst(bmpDst
),
2638 dataSrc((wxBitmap
&)bmpSrc
);
2640 wxCHECK_RET( dataDst
&& dataSrc
,
2641 _T("failed to get raw data in wxAlphaBlend") );
2643 wxAlphaPixelData::Iterator
pDst(dataDst
),
2647 for ( int y
= 0; y
< dstHeight
; y
++ )
2649 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2651 for ( int x
= 0; x
< dstWidth
; x
++ )
2653 // source is point sampled, Alpha StretchBlit is ugly on Win95
2654 // (but does not impact performance)
2655 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2657 // note that source bitmap uses premultiplied alpha (as required by
2658 // the real AlphaBlend)
2659 const unsigned beta
= 255 - pSrc
.Alpha();
2661 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2662 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2663 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2668 pDst
= pDstRowStart
;
2669 pDst
.OffsetY(dataDst
, 1);
2672 // and finally blit them back to the destination DC
2673 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2675 wxLogLastError(_T("BitBlt"));
2679 #endif // wxHAS_RAW_BITMAP
2681 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2682 const wxColour
& initialColour
,
2683 const wxColour
& destColour
,
2684 wxDirection nDirection
)
2686 // use native function if we have compile-time support it and can load it
2687 // during run-time (linking to it statically would make the program
2688 // unusable on earlier Windows versions)
2689 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2691 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2692 static GradientFill_t pfnGradientFill
=
2693 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(_T("GradientFill"));
2695 if ( pfnGradientFill
)
2697 GRADIENT_RECT grect
;
2698 grect
.UpperLeft
= 0;
2699 grect
.LowerRight
= 1;
2701 // invert colours direction if not filling from left-to-right or
2703 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2705 // one vertex for upper left and one for upper-right
2706 TRIVERTEX vertices
[2];
2708 vertices
[0].x
= rect
.GetLeft();
2709 vertices
[0].y
= rect
.GetTop();
2710 vertices
[1].x
= rect
.GetRight()+1;
2711 vertices
[1].y
= rect
.GetBottom()+1;
2713 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2714 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2715 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2716 vertices
[firstVertex
].Alpha
= 0;
2717 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2718 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2719 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2720 vertices
[1 - firstVertex
].Alpha
= 0;
2722 if ( (*pfnGradientFill
)
2729 nDirection
== wxWEST
|| nDirection
== wxEAST
2730 ? GRADIENT_FILL_RECT_H
2731 : GRADIENT_FILL_RECT_V
2734 // skip call of the base class version below
2738 wxLogLastError(_T("GradientFill"));
2740 #endif // wxUSE_DYNLIB_CLASS
2742 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2745 #if wxUSE_DYNLIB_CLASS
2747 static DWORD
wxGetDCLayout(HDC hdc
)
2749 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2751 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2753 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2756 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2758 DWORD layout
= wxGetDCLayout(GetHdc());
2760 if ( layout
== (DWORD
)-1 )
2761 return wxLayout_Default
;
2763 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2766 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2768 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2770 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2771 if ( !s_pfnSetLayout
)
2774 if ( dir
== wxLayout_Default
)
2776 dir
= wxTheApp
->GetLayoutDirection();
2777 if ( dir
== wxLayout_Default
)
2781 DWORD layout
= wxGetDCLayout(GetHdc());
2782 if ( dir
== wxLayout_RightToLeft
)
2783 layout
|= LAYOUT_RTL
;
2785 layout
&= ~LAYOUT_RTL
;
2787 s_pfnSetLayout(GetHdc(), layout
);
2790 #else // !wxUSE_DYNLIB_CLASS
2792 // we can't provide RTL support without dynamic loading, so stub it out
2793 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2795 return wxLayout_Default
;
2798 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2802 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS