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
);
281 // this class saves the old stretch blit mode during its life time
282 class StretchBltModeChanger
285 StretchBltModeChanger(HDC hdc
,
286 int WXUNUSED_IN_WINCE(mode
))
290 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
292 wxLogLastError(_T("SetStretchBltMode"));
296 ~StretchBltModeChanger()
299 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
300 wxLogLastError(_T("SetStretchBltMode"));
309 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
312 #if wxUSE_DYNLIB_CLASS
314 // helper class to cache dynamically loaded libraries and not attempt reloading
316 class wxOnceOnlyDLLLoader
319 // ctor argument must be a literal string as we don't make a copy of it!
320 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
326 // return the symbol with the given name or NULL if the DLL not loaded
327 // or symbol not present
328 void *GetSymbol(const wxChar
*name
)
330 // we're prepared to handle errors here
335 m_dll
.Load(m_dllName
);
337 // reset the name whether we succeeded or failed so that we don't
338 // try again the next time
342 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
347 if ( m_dll
.IsLoaded() )
354 wxDynamicLibrary m_dll
;
355 const wxChar
*m_dllName
;
358 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(_T("msimg32"));
360 // we must ensure that DLLs are unloaded before the static objects cleanup time
361 // because we may hit the notorious DllMain() dead lock in this case if wx is
362 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
363 // under Windows because it tries to reacquire the same lock)
364 class wxGDIDLLsCleanupModule
: public wxModule
367 virtual bool OnInit() { return true; }
368 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
371 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
374 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
376 #endif // wxUSE_DYNLIB_CLASS
378 // ===========================================================================
380 // ===========================================================================
382 // ----------------------------------------------------------------------------
383 // wxBrushAttrsSetter
384 // ----------------------------------------------------------------------------
386 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
387 : wxBkModeChanger(GetHdcOf(dc
)),
388 wxTextColoursChanger(GetHdcOf(dc
))
390 const wxBrush
& brush
= dc
.GetBrush();
391 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
393 // note that Windows convention is opposite to wxWidgets one, this is
394 // why text colour becomes the background one and vice versa
395 wxTextColoursChanger::Change(dc
.GetTextBackground(),
396 dc
.GetTextForeground());
398 wxBkModeChanger::Change(dc
.GetBackgroundMode());
402 // ----------------------------------------------------------------------------
403 // wxDC MSW-specific methods
404 // ----------------------------------------------------------------------------
406 WXHDC
wxDC::GetHDC() const
408 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
409 return impl
? impl
->GetHDC() : 0;
412 // ---------------------------------------------------------------------------
414 // ---------------------------------------------------------------------------
416 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
423 wxMSWDCImpl::~wxMSWDCImpl()
427 SelectOldObjects(m_hDC
);
429 // if we own the HDC, we delete it, otherwise we just release it
433 ::DeleteDC(GetHdc());
435 else // we don't own our HDC
439 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
443 // Must have been a wxScreenDC
444 ::ReleaseDC((HWND
) NULL
, GetHdc());
450 // This will select current objects out of the DC,
451 // which is what you have to do before deleting the
453 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
459 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
461 if (m_selectedBitmap
.IsOk())
463 m_selectedBitmap
.SetSelectedInto(NULL
);
470 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
475 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
480 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
487 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
490 #endif // wxUSE_PALETTE
493 m_brush
= wxNullBrush
;
496 m_palette
= wxNullPalette
;
497 #endif // wxUSE_PALETTE
499 m_backgroundBrush
= wxNullBrush
;
500 m_selectedBitmap
= wxNullBitmap
;
503 // ---------------------------------------------------------------------------
505 // ---------------------------------------------------------------------------
507 void wxMSWDCImpl::UpdateClipBox()
512 ::GetClipBox(GetHdc(), &rect
);
514 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
515 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
516 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
517 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
521 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
523 // check if we should try to retrieve the clipping region possibly not set
524 // by our SetClippingRegion() but preset by Windows:this can only happen
525 // when we're associated with an existing HDC usign SetHDC(), see there
526 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
528 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
529 self
->UpdateClipBox();
531 if ( !m_clipX1
&& !m_clipX2
)
532 self
->m_clipping
= false;
535 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
538 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
539 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
541 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
545 // note that we combine the new clipping region with the existing one: this
546 // is compatible with what the other ports do and is the documented
547 // behaviour now (starting with 2.3.3)
548 #if defined(__WXWINCE__)
550 if ( !::GetClipBox(GetHdc(), &rectClip
) )
553 // GetClipBox returns logical coordinates, so transform to device
554 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
555 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
556 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
557 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
559 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
560 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
561 rectClip
.right
, rectClip
.bottom
);
563 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
565 ::SelectClipRgn(GetHdc(), hrgnDest
);
568 ::DeleteObject(hrgnClipOld
);
569 ::DeleteObject(hrgnDest
);
571 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
573 wxLogLastError(_T("ExtSelectClipRgn"));
577 #endif // WinCE/!WinCE
584 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
586 // the region coords are always the device ones, so do the translation
589 // FIXME: possible +/-1 error here, to check!
590 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
592 LogicalToDeviceX(x
+ w
),
593 LogicalToDeviceY(y
+ h
));
596 wxLogLastError(_T("CreateRectRgn"));
600 SetClippingHrgn((WXHRGN
)hrgn
);
602 ::DeleteObject(hrgn
);
606 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
608 SetClippingHrgn(region
.GetHRGN());
611 void wxMSWDCImpl::DestroyClippingRegion()
615 if (m_clipping
&& m_hDC
)
618 // On a PocketPC device (not necessarily emulator), resetting
619 // the clip region as per the old method causes bad display
620 // problems. In fact setting a null region is probably OK
621 // on desktop WIN32 also, since the WIN32 docs imply that the user
622 // clipping region is independent from the paint clipping region.
623 ::SelectClipRgn(GetHdc(), 0);
625 // TODO: this should restore the previous clipping region,
626 // so that OnPaint processing works correctly, and the update
627 // clipping region doesn't get destroyed after the first
628 // DestroyClippingRegion.
629 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
630 ::SelectClipRgn(GetHdc(), rgn
);
635 wxDCImpl::DestroyClippingRegion();
638 // ---------------------------------------------------------------------------
639 // query capabilities
640 // ---------------------------------------------------------------------------
642 bool wxMSWDCImpl::CanDrawBitmap() const
647 bool wxMSWDCImpl::CanGetTextExtent() const
649 #ifdef __WXMICROWIN__
650 // TODO Extend MicroWindows' GetDeviceCaps function
653 // What sort of display is it?
654 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
656 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
660 int wxMSWDCImpl::GetDepth() const
662 WXMICROWIN_CHECK_HDC_RET(16)
664 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
667 // ---------------------------------------------------------------------------
669 // ---------------------------------------------------------------------------
671 void wxMSWDCImpl::Clear()
678 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
682 // No, I think we should simply ignore this if printing on e.g.
684 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
685 if (!m_selectedBitmap
.IsOk())
688 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
689 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
690 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
694 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
697 DWORD colour
= ::GetBkColor(GetHdc());
698 HBRUSH brush
= ::CreateSolidBrush(colour
);
699 ::FillRect(GetHdc(), &rect
, brush
);
700 ::DeleteObject(brush
);
702 RealizeScaleAndOrigin();
705 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
706 wxCoord
WXUNUSED_IN_WINCE(y
),
707 const wxColour
& WXUNUSED_IN_WINCE(col
),
708 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
713 WXMICROWIN_CHECK_HDC_RET(false)
715 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
717 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
718 : FLOODFILLBORDER
) ) ;
721 // quoting from the MSDN docs:
723 // Following are some of the reasons this function might fail:
725 // * The filling could not be completed.
726 // * The specified point has the boundary color specified by the
727 // crColor parameter (if FLOODFILLBORDER was requested).
728 // * The specified point does not have the color specified by
729 // crColor (if FLOODFILLSURFACE was requested)
730 // * The point is outside the clipping region that is, it is not
731 // visible on the device.
733 wxLogLastError(wxT("ExtFloodFill"));
736 CalcBoundingBox(x
, y
);
742 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
744 WXMICROWIN_CHECK_HDC_RET(false)
746 wxCHECK_MSG( col
, false, _T("NULL colour parameter in wxMSWDCImpl::GetPixel") );
748 // get the color of the pixel
749 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
751 wxRGBToColour(*col
, pixelcolor
);
756 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
760 wxCoord x1
= x
-VIEWPORT_EXTENT
;
761 wxCoord y1
= y
-VIEWPORT_EXTENT
;
762 wxCoord x2
= x
+VIEWPORT_EXTENT
;
763 wxCoord y2
= y
+VIEWPORT_EXTENT
;
765 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
766 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
768 CalcBoundingBox(x1
, y1
);
769 CalcBoundingBox(x2
, y2
);
772 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
776 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
778 CalcBoundingBox(x1
, y1
);
779 CalcBoundingBox(x2
, y2
);
782 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
783 // and ending at (x2, y2)
784 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
785 wxCoord x2
, wxCoord y2
,
786 wxCoord xc
, wxCoord yc
)
790 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
794 // Slower emulation since WinCE doesn't support Pie and Arc
795 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
797 sa
= -sa
; // below center
798 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
799 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
804 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
806 // treat the special case of full circle separately
807 if ( x1
== x2
&& y1
== y2
)
809 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
813 wxCoord xx1
= XLOG2DEV(x1
);
814 wxCoord yy1
= YLOG2DEV(y1
);
815 wxCoord xx2
= XLOG2DEV(x2
);
816 wxCoord yy2
= YLOG2DEV(y2
);
817 wxCoord xxc
= XLOG2DEV(xc
);
818 wxCoord yyc
= YLOG2DEV(yc
);
821 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
823 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
824 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
825 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
826 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
828 if ( m_brush
.IsOk() && m_brush
.GetStyle() != wxBRUSHSTYLE_TRANSPARENT
)
830 // Have to add 1 to bottom-right corner of rectangle
831 // to make semi-circles look right (crooked line otherwise).
832 // Unfortunately this is not a reliable method, depends
833 // on the size of shape.
834 // TODO: figure out why this happens!
835 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
839 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
842 CalcBoundingBox(xc
- r
, yc
- r
);
843 CalcBoundingBox(xc
+ r
, yc
+ r
);
847 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
848 wxCoord width
, wxCoord height
)
850 // cases when we don't have DrawFrameControl()
851 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
852 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
854 wxCoord x2
= x1
+ width
,
864 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
866 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
869 CalcBoundingBox(x1
, y1
);
870 CalcBoundingBox(x2
, y2
);
871 #endif // Microwin/Normal
874 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
878 COLORREF color
= 0x00ffffff;
881 color
= m_pen
.GetColour().GetPixel();
884 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
886 CalcBoundingBox(x
, y
);
889 void wxMSWDCImpl::DoDrawPolygon(int n
,
893 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
897 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
899 // Do things less efficiently if we have offsets
900 if (xoffset
!= 0 || yoffset
!= 0)
902 POINT
*cpoints
= new POINT
[n
];
904 for (i
= 0; i
< n
; i
++)
906 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
907 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
909 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
912 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
914 (void)Polygon(GetHdc(), cpoints
, n
);
916 SetPolyFillMode(GetHdc(),prev
);
923 for (i
= 0; i
< n
; i
++)
924 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
927 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
929 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
931 SetPolyFillMode(GetHdc(),prev
);
937 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
942 wxPolygonFillMode fillStyle
)
945 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
949 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
951 for (i
= cnt
= 0; i
< n
; i
++)
954 // Do things less efficiently if we have offsets
955 if (xoffset
!= 0 || yoffset
!= 0)
957 POINT
*cpoints
= new POINT
[cnt
];
958 for (i
= 0; i
< cnt
; i
++)
960 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
961 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
963 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
966 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
968 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
970 SetPolyFillMode(GetHdc(),prev
);
976 for (i
= 0; i
< cnt
; i
++)
977 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
980 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
982 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
984 SetPolyFillMode(GetHdc(),prev
);
991 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
995 // Do things less efficiently if we have offsets
996 if (xoffset
!= 0 || yoffset
!= 0)
998 POINT
*cpoints
= new POINT
[n
];
1000 for (i
= 0; i
< n
; i
++)
1002 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
1003 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
1005 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
1007 (void)Polyline(GetHdc(), cpoints
, n
);
1013 for (i
= 0; i
< n
; i
++)
1014 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
1016 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
1020 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1022 WXMICROWIN_CHECK_HDC
1024 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1026 wxCoord x2
= x
+ width
;
1027 wxCoord y2
= y
+ height
;
1029 wxCoord x2dev
= XLOG2DEV(x2
),
1030 y2dev
= YLOG2DEV(y2
);
1032 // Windows (but not Windows CE) draws the filled rectangles without outline
1033 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
1034 // and we want them to have the same size regardless of which pen is used
1036 if ( m_pen
.IsOk() && m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
1041 #endif // !__WXWINCE__
1043 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
1045 CalcBoundingBox(x
, y
);
1046 CalcBoundingBox(x2
, y2
);
1049 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
1051 WXMICROWIN_CHECK_HDC
1053 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1055 // Now, a negative radius value is interpreted to mean
1056 // 'the proportion of the smallest X or Y dimension'
1060 double smallest
= (width
< height
) ? width
: height
;
1061 radius
= (- radius
* smallest
);
1064 wxCoord x2
= (x
+width
);
1065 wxCoord y2
= (y
+height
);
1067 // Windows draws the filled rectangles without outline (i.e. drawn with a
1068 // transparent pen) one pixel smaller in both directions and we want them
1069 // to have the same size regardless of which pen is used - adjust
1070 if ( m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
1076 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1077 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1079 CalcBoundingBox(x
, y
);
1080 CalcBoundingBox(x2
, y2
);
1083 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1085 WXMICROWIN_CHECK_HDC
1087 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1089 // +1 below makes the ellipse more similar to other platforms.
1090 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
1091 wxCoord x2
= x
+ width
+ 1;
1092 wxCoord y2
= y
+ height
+ 1;
1094 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
1095 // pen doesn't draw anything. Should we provide a workaround?
1097 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1099 CalcBoundingBox(x
, y
);
1100 CalcBoundingBox(x2
, y2
);
1103 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1104 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1106 // quadratic b-spline to cubic bezier spline conversion
1108 // quadratic spline with control points P0,P1,P2
1109 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1111 // bezier spline with control points B0,B1,B2,B3
1112 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1114 // control points of bezier spline calculated from b-spline
1116 // B1 = (2*P1 + P0)/3
1117 // B2 = (2*P1 + P2)/3
1120 WXMICROWIN_CHECK_HDC
1122 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1124 const size_t n_points
= points
->GetCount();
1125 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1127 const size_t n_bezier_points
= n_points
* 3 + 1;
1128 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1129 size_t bezier_pos
= 0;
1130 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1132 wxPointList::compatibility_iterator node
= points
->GetFirst();
1133 wxPoint
*p
= node
->GetData();
1134 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1135 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1137 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1140 node
= node
->GetNext();
1141 p
= node
->GetData();
1145 cx1
= ( x1
+ x2
) / 2;
1146 cy1
= ( y1
+ y2
) / 2;
1147 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1148 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1150 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1154 while ((node
= node
->GetNext()) != NULL
)
1156 while ((node
= node
->GetNext()))
1157 #endif // !wxUSE_STL
1159 p
= (wxPoint
*)node
->GetData();
1164 cx4
= (x1
+ x2
) / 2;
1165 cy4
= (y1
+ y2
) / 2;
1166 // B0 is B3 of previous segment
1168 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1169 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1172 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1173 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1176 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1177 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1183 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1185 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1186 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1188 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1191 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1195 #endif // wxUSE_SPLINES
1197 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1198 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1201 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1204 WXMICROWIN_CHECK_HDC
1206 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1211 int rx1
= XLOG2DEV(x
+w
/2);
1212 int ry1
= YLOG2DEV(y
+h
/2);
1219 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1220 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1221 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1222 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1224 // Swap start and end positions if the end angle is less than the start angle.
1235 // draw pie with NULL_PEN first and then outline otherwise a line is
1236 // drawn from the start and end points to the centre
1237 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1240 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1241 rx1
, ry1
, rx2
, ry2
);
1245 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1246 rx1
, ry1
-1, rx2
, ry2
-1);
1249 ::SelectObject(GetHdc(), hpenOld
);
1251 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1252 rx1
, ry1
, rx2
, ry2
);
1254 CalcBoundingBox(x
, y
);
1255 CalcBoundingBox(x2
, y2
);
1259 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1261 WXMICROWIN_CHECK_HDC
1263 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1266 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1268 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1271 CalcBoundingBox(x
, y
);
1272 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1275 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1277 WXMICROWIN_CHECK_HDC
1279 wxCHECK_RET( bmp
.IsOk(), _T("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1281 int width
= bmp
.GetWidth(),
1282 height
= bmp
.GetHeight();
1284 HBITMAP hbmpMask
= 0;
1287 HPALETTE oldPal
= 0;
1288 #endif // wxUSE_PALETTE
1290 if ( bmp
.HasAlpha() )
1293 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1295 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1301 wxMask
*mask
= bmp
.GetMask();
1303 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1307 // don't give assert here because this would break existing
1308 // programs - just silently ignore useMask parameter
1315 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1317 // On some systems, MaskBlt succeeds yet is much much slower
1318 // than the wxWidgets fall-back implementation. So we need
1319 // to be able to switch this on and off at runtime.
1321 #if wxUSE_SYSTEM_OPTIONS
1322 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1326 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1327 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1329 wxPalette
*pal
= bmp
.GetPalette();
1330 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1332 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1333 ::RealizePalette(hdcMem
);
1335 #endif // wxUSE_PALETTE
1337 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1340 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1344 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1345 #endif // wxUSE_PALETTE
1347 ::SelectObject(hdcMem
, hOldBitmap
);
1354 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1358 memDC
.SelectObjectAsSource(bmp
);
1360 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1362 memDC
.SelectObject(wxNullBitmap
);
1365 else // no mask, just use BitBlt()
1368 HDC memdc
= ::CreateCompatibleDC( cdc
);
1369 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1371 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1373 wxTextColoursChanger
textCol(GetHdc(), *this);
1376 wxPalette
*pal
= bmp
.GetPalette();
1377 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1379 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1380 ::RealizePalette(memdc
);
1382 #endif // wxUSE_PALETTE
1384 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1385 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1389 ::SelectPalette(memdc
, oldPal
, FALSE
);
1390 #endif // wxUSE_PALETTE
1392 ::SelectObject( memdc
, hOldBitmap
);
1393 ::DeleteDC( memdc
);
1397 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1399 WXMICROWIN_CHECK_HDC
1401 DrawAnyText(text
, x
, y
);
1403 // update the bounding box
1404 CalcBoundingBox(x
, y
);
1407 GetOwner()->GetTextExtent(text
, &w
, &h
);
1408 CalcBoundingBox(x
+ w
, y
+ h
);
1411 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1413 WXMICROWIN_CHECK_HDC
1415 // prepare for drawing the text
1416 wxTextColoursChanger
textCol(GetHdc(), *this);
1418 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1420 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1421 text
.c_str(), text
.length(), NULL
) == 0 )
1423 wxLogLastError(wxT("TextOut"));
1427 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1428 wxCoord x
, wxCoord y
,
1431 WXMICROWIN_CHECK_HDC
1433 // we test that we have some font because otherwise we should still use the
1434 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1435 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1436 // font for drawing rotated fonts unfortunately)
1437 if ( (angle
== 0.0) && m_font
.IsOk() )
1439 DoDrawText(text
, x
, y
);
1441 #ifndef __WXMICROWIN__
1444 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1445 // because it's not TrueType and so can't have non zero
1446 // orientation/escapement under Win9x
1447 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1448 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1450 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1452 wxLogLastError(wxT("GetObject(hfont)"));
1455 // GDI wants the angle in tenth of degree
1456 long angle10
= (long)(angle
* 10);
1457 lf
.lfEscapement
= angle10
;
1458 lf
. lfOrientation
= angle10
;
1460 hfont
= ::CreateFontIndirect(&lf
);
1463 wxLogLastError(wxT("CreateFont"));
1467 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1469 DrawAnyText(text
, x
, y
);
1471 (void)::SelectObject(GetHdc(), hfontOld
);
1472 (void)::DeleteObject(hfont
);
1475 // call the bounding box by adding all four vertices of the rectangle
1476 // containing the text to it (simpler and probably not slower than
1477 // determining which of them is really topmost/leftmost/...)
1479 GetOwner()->GetTextExtent(text
, &w
, &h
);
1481 double rad
= DegToRad(angle
);
1483 // "upper left" and "upper right"
1484 CalcBoundingBox(x
, y
);
1485 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1487 // "bottom left" and "bottom right"
1488 x
+= (wxCoord
)(h
*sin(rad
));
1489 y
+= (wxCoord
)(h
*cos(rad
));
1490 CalcBoundingBox(x
, y
);
1491 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1496 // ---------------------------------------------------------------------------
1498 // ---------------------------------------------------------------------------
1502 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1504 WXMICROWIN_CHECK_HDC
1506 // Set the old object temporarily, in case the assignment deletes an object
1507 // that's not yet selected out.
1510 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1514 if ( m_palette
.IsOk() )
1516 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1517 GetHpaletteOf(m_palette
),
1520 m_oldPalette
= (WXHPALETTE
) oldPal
;
1523 ::RealizePalette(GetHdc());
1527 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1529 if ( palette
.IsOk() )
1531 m_palette
= palette
;
1532 DoSelectPalette(true);
1536 void wxMSWDCImpl::InitializePalette()
1538 if ( wxDisplayDepth() <= 8 )
1540 // look for any window or parent that has a custom palette. If any has
1541 // one then we need to use it in drawing operations
1542 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1544 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1545 if ( m_hasCustomPalette
)
1547 m_palette
= win
->GetPalette();
1549 // turn on MSW translation for this palette
1555 #endif // wxUSE_PALETTE
1557 // SetFont/Pen/Brush() really ask to be implemented as a single template
1558 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1560 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1562 WXMICROWIN_CHECK_HDC
1564 if ( font
== m_font
)
1569 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1570 if ( hfont
== HGDI_ERROR
)
1572 wxLogLastError(_T("SelectObject(font)"));
1577 m_oldFont
= (WXHFONT
)hfont
;
1582 else // invalid font, reset the current font
1586 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1588 wxLogLastError(_T("SelectObject(old font)"));
1594 m_font
= wxNullFont
;
1598 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1600 WXMICROWIN_CHECK_HDC
1607 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1608 if ( hpen
== HGDI_ERROR
)
1610 wxLogLastError(_T("SelectObject(pen)"));
1615 m_oldPen
= (WXHPEN
)hpen
;
1620 else // invalid pen, reset the current pen
1624 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1626 wxLogLastError(_T("SelectObject(old pen)"));
1636 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1638 WXMICROWIN_CHECK_HDC
1640 if ( brush
== m_brush
)
1645 // we must make sure the brush is aligned with the logical coordinates
1646 // before selecting it
1647 wxBitmap
*stipple
= brush
.GetStipple();
1648 if ( stipple
&& stipple
->IsOk() )
1650 if ( !::SetBrushOrgEx
1653 m_deviceOriginX
% stipple
->GetWidth(),
1654 m_deviceOriginY
% stipple
->GetHeight(),
1655 NULL
// [out] previous brush origin
1658 wxLogLastError(_T("SetBrushOrgEx()"));
1662 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1663 if ( hbrush
== HGDI_ERROR
)
1665 wxLogLastError(_T("SelectObject(brush)"));
1670 m_oldBrush
= (WXHBRUSH
)hbrush
;
1675 else // invalid brush, reset the current brush
1679 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1681 wxLogLastError(_T("SelectObject(old brush)"));
1687 m_brush
= wxNullBrush
;
1691 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1693 WXMICROWIN_CHECK_HDC
1695 m_backgroundBrush
= brush
;
1697 if ( m_backgroundBrush
.IsOk() )
1699 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1703 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1705 WXMICROWIN_CHECK_HDC
1707 m_backgroundMode
= mode
;
1709 // SetBackgroundColour now only refers to text background
1710 // and m_backgroundMode is used there
1713 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1715 WXMICROWIN_CHECK_HDC
1717 m_logicalFunction
= function
;
1722 void wxMSWDCImpl::SetRop(WXHDC dc
)
1724 if ( !dc
|| m_logicalFunction
< 0 )
1729 switch (m_logicalFunction
)
1731 case wxCLEAR
: rop
= R2_BLACK
; break;
1732 case wxXOR
: rop
= R2_XORPEN
; break;
1733 case wxINVERT
: rop
= R2_NOT
; break;
1734 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1735 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1736 case wxCOPY
: rop
= R2_COPYPEN
; break;
1737 case wxAND
: rop
= R2_MASKPEN
; break;
1738 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1739 case wxNO_OP
: rop
= R2_NOP
; break;
1740 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1741 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1742 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1743 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1744 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1745 case wxOR
: rop
= R2_MERGEPEN
; break;
1746 case wxSET
: rop
= R2_WHITE
; break;
1748 wxFAIL_MSG( wxS("unknown logical function") );
1752 SetROP2(GetHdc(), rop
);
1755 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1757 // We might be previewing, so return true to let it continue.
1761 void wxMSWDCImpl::EndDoc()
1765 void wxMSWDCImpl::StartPage()
1769 void wxMSWDCImpl::EndPage()
1773 // ---------------------------------------------------------------------------
1775 // ---------------------------------------------------------------------------
1777 wxCoord
wxMSWDCImpl::GetCharHeight() const
1779 WXMICROWIN_CHECK_HDC_RET(0)
1781 TEXTMETRIC lpTextMetric
;
1783 GetTextMetrics(GetHdc(), &lpTextMetric
);
1785 return lpTextMetric
.tmHeight
;
1788 wxCoord
wxMSWDCImpl::GetCharWidth() const
1790 WXMICROWIN_CHECK_HDC_RET(0)
1792 TEXTMETRIC lpTextMetric
;
1794 GetTextMetrics(GetHdc(), &lpTextMetric
);
1796 return lpTextMetric
.tmAveCharWidth
;
1799 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1800 wxCoord
*descent
, wxCoord
*externalLeading
,
1801 const wxFont
*font
) const
1803 #ifdef __WXMICROWIN__
1808 if (descent
) *descent
= 0;
1809 if (externalLeading
) *externalLeading
= 0;
1812 #endif // __WXMICROWIN__
1817 wxASSERT_MSG( font
->IsOk(), _T("invalid font in wxMSWDCImpl::GetTextExtent") );
1819 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1821 else // don't change the font
1827 const size_t len
= string
.length();
1828 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1830 wxLogLastError(_T("GetTextExtentPoint32()"));
1833 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1834 // the result computed by GetTextExtentPoint32() may be too small as it
1835 // accounts for under/overhang of the first/last character while we want
1836 // just the bounding rect for this string so adjust the width as needed
1837 // (using API not available in 2002 SDKs of WinCE)
1841 const wxChar chFirst
= *string
.begin();
1842 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1844 if ( width
.abcA
< 0 )
1845 sizeRect
.cx
-= width
.abcA
;
1849 const wxChar chLast
= *string
.rbegin();
1850 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1852 //else: we already have the width of the last character
1854 if ( width
.abcC
< 0 )
1855 sizeRect
.cx
-= width
.abcC
;
1857 //else: GetCharABCWidths() failed, not a TrueType font?
1859 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1862 ::GetTextMetrics(GetHdc(), &tm
);
1869 *descent
= tm
.tmDescent
;
1870 if (externalLeading
)
1871 *externalLeading
= tm
.tmExternalLeading
;
1875 ::SelectObject(GetHdc(), hfontOld
);
1880 // Each element of the array will be the width of the string up to and
1881 // including the coresoponding character in text.
1883 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1885 static int maxLenText
= -1;
1886 static int maxWidth
= -1;
1889 int stlen
= text
.length();
1891 if (maxLenText
== -1)
1893 // Win9x and WinNT+ have different limits
1894 int version
= wxGetOsVersion();
1895 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1896 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1900 widths
.Add(0, stlen
); // fill the array with zeros
1904 if (!::GetTextExtentExPoint(GetHdc(),
1905 text
.c_str(), // string to check
1906 wxMin(stlen
, maxLenText
),
1908 &fit
, // [out] count of chars
1910 &widths
[0], // array to fill
1914 wxLogLastError(wxT("GetTextExtentExPoint"));
1921 void wxMSWDCImpl::RealizeScaleAndOrigin()
1923 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1924 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1925 // noticeable difference between these mapping modes
1927 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1929 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1930 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1932 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1933 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1935 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1936 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1940 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1942 WXMICROWIN_CHECK_HDC
1944 m_mappingMode
= mode
;
1946 if ( mode
== wxMM_TEXT
)
1949 m_logicalScaleY
= 1.0;
1951 else // need to do some calculations
1953 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1954 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1955 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1956 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1958 if ( (mm_width
== 0) || (mm_height
== 0) )
1960 // we can't calculate mm2pixels[XY] then!
1964 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1965 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1970 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1971 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1975 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1976 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1980 m_logicalScaleX
= mm2pixelsX
;
1981 m_logicalScaleY
= mm2pixelsY
;
1985 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1986 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1990 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1994 ComputeScaleAndOrigin();
1996 RealizeScaleAndOrigin();
1999 void wxMSWDCImpl::SetUserScale(double x
, double y
)
2001 WXMICROWIN_CHECK_HDC
2003 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
2006 wxDCImpl::SetUserScale(x
,y
);
2008 RealizeScaleAndOrigin();
2011 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2014 WXMICROWIN_CHECK_HDC
2016 int signX
= xLeftRight
? 1 : -1,
2017 signY
= yBottomUp
? -1 : 1;
2019 if (signX
== m_signX
&& signY
== m_signY
)
2022 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2024 RealizeScaleAndOrigin();
2027 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2029 WXMICROWIN_CHECK_HDC
2031 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2034 wxDCImpl::SetLogicalOrigin( x
, y
);
2036 RealizeScaleAndOrigin();
2039 // For use by wxWidgets only, unless custom units are required.
2040 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2042 WXMICROWIN_CHECK_HDC
2044 wxDCImpl::SetLogicalScale(x
,y
);
2047 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2049 WXMICROWIN_CHECK_HDC
2051 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2054 wxDCImpl::SetDeviceOrigin( x
, y
);
2056 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2059 // ---------------------------------------------------------------------------
2061 // ---------------------------------------------------------------------------
2063 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2064 wxCoord dstWidth
, wxCoord dstHeight
,
2066 wxCoord srcX
, wxCoord srcY
,
2067 wxRasterOperationMode rop
, bool useMask
,
2068 wxCoord srcMaskX
, wxCoord srcMaskY
)
2070 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2073 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2074 wxCoord dstWidth
, wxCoord dstHeight
,
2076 wxCoord xsrc
, wxCoord ysrc
,
2077 wxCoord srcWidth
, wxCoord srcHeight
,
2078 wxRasterOperationMode rop
, bool useMask
,
2079 wxCoord xsrcMask
, wxCoord ysrcMask
)
2081 wxCHECK_MSG( source
, false, _T("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2083 WXMICROWIN_CHECK_HDC_RET(false)
2085 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2088 // TODO: Do we want to be able to blit from other DCs too?
2092 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2094 // if either the source or destination has alpha channel, we must use
2095 // AlphaBlt() as other function don't handle it correctly
2096 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2097 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2098 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2100 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2101 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2105 wxMask
*mask
= NULL
;
2108 mask
= bmpSrc
.GetMask();
2110 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2112 // don't give assert here because this would break existing
2113 // programs - just silently ignore useMask parameter
2118 if (xsrcMask
== -1 && ysrcMask
== -1)
2120 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2123 wxTextColoursChanger
textCol(GetHdc(), *this);
2128 case wxXOR
: dwRop
= SRCINVERT
; break;
2129 case wxINVERT
: dwRop
= DSTINVERT
; break;
2130 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2131 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2132 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2133 case wxSET
: dwRop
= WHITENESS
; break;
2134 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2135 case wxAND
: dwRop
= SRCAND
; break;
2136 case wxOR
: dwRop
= SRCPAINT
; break;
2137 case wxEQUIV
: dwRop
= 0x00990066; break;
2138 case wxNAND
: dwRop
= 0x007700E6; break;
2139 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2140 case wxCOPY
: dwRop
= SRCCOPY
; break;
2141 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2142 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2143 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2145 wxFAIL_MSG( wxT("unsupported logical function") );
2149 bool success
= false;
2154 // we want the part of the image corresponding to the mask to be
2155 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2156 // meaning of fg and bg is inverted which corresponds to wxWin notion
2157 // of the mask which is also contrary to the Windows one)
2159 // On some systems, MaskBlt succeeds yet is much much slower
2160 // than the wxWidgets fall-back implementation. So we need
2161 // to be able to switch this on and off at runtime.
2162 #if wxUSE_SYSTEM_OPTIONS
2163 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2166 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2171 xdest
, ydest
, dstWidth
, dstHeight
,
2174 (HBITMAP
)mask
->GetMaskBitmap(),
2176 MAKEROP4(dwRop
, DSTCOPY
)
2184 // Blit bitmap with mask
2187 HBITMAP buffer_bmap
;
2189 #if wxUSE_DC_CACHEING
2190 // create a temp buffer bitmap and DCs to access it and the mask
2191 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2192 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2194 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2195 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2197 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2198 dstWidth
, dstHeight
);
2200 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2201 #else // !wxUSE_DC_CACHEING
2202 // create a temp buffer bitmap and DCs to access it and the mask
2203 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2204 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2205 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2206 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2207 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2208 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2210 // copy dest to buffer
2211 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2212 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2214 wxLogLastError(wxT("BitBlt"));
2218 StretchBltModeChanger
changeMode(dc_buffer
, COLORONCOLOR
);
2221 // copy src to buffer using selected raster op
2222 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2223 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2225 wxLogLastError(wxT("StretchBlt"));
2228 // set masked area in buffer to BLACK
2230 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2231 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2232 dc_mask
, xsrcMask
, ysrcMask
,
2233 srcWidth
, srcHeight
, SRCAND
) )
2235 wxLogLastError(wxT("StretchBlt"));
2238 // set unmasked area in dest to BLACK
2239 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2240 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2241 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2242 dc_mask
, xsrcMask
, ysrcMask
,
2243 srcWidth
, srcHeight
, SRCAND
) )
2245 wxLogLastError(wxT("StretchBlt"));
2247 } // restore the original text and background colours
2249 // OR buffer to dest
2250 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2251 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2254 wxLogLastError(wxT("BitBlt"));
2257 // tidy up temporary DCs and bitmap
2258 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2259 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2261 #if !wxUSE_DC_CACHEING
2263 ::DeleteDC(dc_mask
);
2264 ::DeleteDC(dc_buffer
);
2265 ::DeleteObject(buffer_bmap
);
2270 else // no mask, just BitBlt() it
2272 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2273 // use StretchBlt() if available and finally fall back to BitBlt()
2275 // FIXME: use appropriate WinCE functions
2277 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2278 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2283 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2285 &ds
) == sizeof(ds
) )
2287 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2289 // Figure out what co-ordinate system we're supposed to specify
2291 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2295 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2298 if ( ::StretchDIBits(GetHdc(),
2300 dstWidth
, dstHeight
,
2302 srcWidth
, srcHeight
,
2304 (LPBITMAPINFO
)&ds
.dsBmih
,
2307 ) == (int)GDI_ERROR
)
2309 // On Win9x this API fails most (all?) of the time, so
2310 // logging it becomes quite distracting. Since it falls
2311 // back to the code below this is not really serious, so
2313 //wxLogLastError(wxT("StretchDIBits"));
2322 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2327 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2333 xdest
, ydest
, dstWidth
, dstHeight
,
2335 xsrc
, ysrc
, srcWidth
, srcHeight
,
2339 wxLogLastError(_T("StretchBlt"));
2349 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2350 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2352 wxLogLastError(_T("BitBlt"));
2364 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2366 WXMICROWIN_CHECK_HDC
2369 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2371 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2374 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2376 WXMICROWIN_CHECK_HDC
2378 // if we implement it in terms of DoGetSize() instead of directly using the
2379 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2380 // will also work for wxWindowDC and wxClientDC even though their size is
2381 // not the same as the total size of the screen
2382 int wPixels
, hPixels
;
2383 DoGetSize(&wPixels
, &hPixels
);
2387 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2389 wxCHECK_RET( wTotal
, _T("0 width device?") );
2391 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2396 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2398 wxCHECK_RET( hTotal
, _T("0 height device?") );
2400 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2404 wxSize
wxMSWDCImpl::GetPPI() const
2406 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2408 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2409 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2411 return wxSize(x
, y
);
2414 // ----------------------------------------------------------------------------
2416 // ----------------------------------------------------------------------------
2418 #if wxUSE_DC_CACHEING
2421 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2422 * improve it in due course, either using arrays, or simply storing pointers to one
2423 * entry for the bitmap, and two for the DCs. -- JACS
2426 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2427 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2429 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2438 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2447 wxDCCacheEntry::~wxDCCacheEntry()
2450 ::DeleteObject((HBITMAP
) m_bitmap
);
2452 ::DeleteDC((HDC
) m_dc
);
2455 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2457 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2458 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2461 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2463 if (entry
->m_depth
== depth
)
2465 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2467 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2468 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2469 if ( !entry
->m_bitmap
)
2471 wxLogLastError(wxT("CreateCompatibleBitmap"));
2473 entry
->m_width
= w
; entry
->m_height
= h
;
2479 node
= node
->GetNext();
2481 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2484 wxLogLastError(wxT("CreateCompatibleBitmap"));
2486 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2487 AddToBitmapCache(entry
);
2491 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2493 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2494 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2497 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2499 // Don't return the same one as we already have
2500 if (!notThis
|| (notThis
!= entry
))
2502 if (entry
->m_depth
== depth
)
2508 node
= node
->GetNext();
2510 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2513 wxLogLastError(wxT("CreateCompatibleDC"));
2515 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2516 AddToDCCache(entry
);
2520 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2522 sm_bitmapCache
.Append(entry
);
2525 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2527 sm_dcCache
.Append(entry
);
2530 void wxMSWDCImpl::ClearCache()
2532 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2533 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2536 // Clean up cache at app exit
2537 class wxDCModule
: public wxModule
2540 virtual bool OnInit() { return true; }
2541 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2544 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2547 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2549 #endif // wxUSE_DC_CACHEING
2551 // ----------------------------------------------------------------------------
2552 // alpha channel support
2553 // ----------------------------------------------------------------------------
2555 static bool AlphaBlt(HDC hdcDst
,
2556 int x
, int y
, int dstWidth
, int dstHeight
,
2558 int srcWidth
, int srcHeight
,
2560 const wxBitmap
& bmp
)
2562 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2563 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2565 // do we have AlphaBlend() and company in the headers?
2566 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2567 // yes, now try to see if we have it during run-time
2568 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2569 HDC
,int,int,int,int,
2573 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(_T("AlphaBlend"));
2574 if ( pfnAlphaBlend
)
2577 bf
.BlendOp
= AC_SRC_OVER
;
2579 bf
.SourceConstantAlpha
= 0xff;
2580 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2582 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2583 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2586 // skip wxAlphaBlend() call below
2590 wxLogLastError(_T("AlphaBlend"));
2593 wxUnusedVar(hdcSrc
);
2594 #endif // defined(AC_SRC_OVER)
2596 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2598 #ifdef wxHAS_RAW_BITMAP
2599 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2602 #else // !wxHAS_RAW_BITMAP
2603 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2604 // alpha but at least something will be shown like this)
2607 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2611 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2612 #ifdef wxHAS_RAW_BITMAP
2615 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2616 int dstWidth
, int dstHeight
,
2618 int srcWidth
, int srcHeight
,
2619 const wxBitmap
& bmpSrc
)
2621 // get the destination DC pixels
2622 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2624 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2626 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2628 wxLogLastError(_T("BitBlt"));
2631 // combine them with the source bitmap using alpha
2632 wxAlphaPixelData
dataDst(bmpDst
),
2633 dataSrc((wxBitmap
&)bmpSrc
);
2635 wxCHECK_RET( dataDst
&& dataSrc
,
2636 _T("failed to get raw data in wxAlphaBlend") );
2638 wxAlphaPixelData::Iterator
pDst(dataDst
),
2642 for ( int y
= 0; y
< dstHeight
; y
++ )
2644 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2646 for ( int x
= 0; x
< dstWidth
; x
++ )
2648 // source is point sampled, Alpha StretchBlit is ugly on Win95
2649 // (but does not impact performance)
2650 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2652 // note that source bitmap uses premultiplied alpha (as required by
2653 // the real AlphaBlend)
2654 const unsigned beta
= 255 - pSrc
.Alpha();
2656 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2657 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2658 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2663 pDst
= pDstRowStart
;
2664 pDst
.OffsetY(dataDst
, 1);
2667 // and finally blit them back to the destination DC
2668 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2670 wxLogLastError(_T("BitBlt"));
2674 #endif // wxHAS_RAW_BITMAP
2676 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2677 const wxColour
& initialColour
,
2678 const wxColour
& destColour
,
2679 wxDirection nDirection
)
2681 // use native function if we have compile-time support it and can load it
2682 // during run-time (linking to it statically would make the program
2683 // unusable on earlier Windows versions)
2684 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2686 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2687 static GradientFill_t pfnGradientFill
=
2688 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(_T("GradientFill"));
2690 if ( pfnGradientFill
)
2692 GRADIENT_RECT grect
;
2693 grect
.UpperLeft
= 0;
2694 grect
.LowerRight
= 1;
2696 // invert colours direction if not filling from left-to-right or
2698 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2700 // one vertex for upper left and one for upper-right
2701 TRIVERTEX vertices
[2];
2703 vertices
[0].x
= rect
.GetLeft();
2704 vertices
[0].y
= rect
.GetTop();
2705 vertices
[1].x
= rect
.GetRight()+1;
2706 vertices
[1].y
= rect
.GetBottom()+1;
2708 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2709 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2710 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2711 vertices
[firstVertex
].Alpha
= 0;
2712 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2713 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2714 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2715 vertices
[1 - firstVertex
].Alpha
= 0;
2717 if ( (*pfnGradientFill
)
2724 nDirection
== wxWEST
|| nDirection
== wxEAST
2725 ? GRADIENT_FILL_RECT_H
2726 : GRADIENT_FILL_RECT_V
2729 // skip call of the base class version below
2733 wxLogLastError(_T("GradientFill"));
2735 #endif // wxUSE_DYNLIB_CLASS
2737 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2740 #if wxUSE_DYNLIB_CLASS
2742 static DWORD
wxGetDCLayout(HDC hdc
)
2744 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2746 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2748 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2751 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2753 DWORD layout
= wxGetDCLayout(GetHdc());
2755 if ( layout
== (DWORD
)-1 )
2756 return wxLayout_Default
;
2758 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2761 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2763 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2765 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2766 if ( !s_pfnSetLayout
)
2769 if ( dir
== wxLayout_Default
)
2771 dir
= wxTheApp
->GetLayoutDirection();
2772 if ( dir
== wxLayout_Default
)
2776 DWORD layout
= wxGetDCLayout(GetHdc());
2777 if ( dir
== wxLayout_RightToLeft
)
2778 layout
|= LAYOUT_RTL
;
2780 layout
&= ~LAYOUT_RTL
;
2782 s_pfnSetLayout(GetHdc(), layout
);
2785 #else // !wxUSE_DYNLIB_CLASS
2787 // we can't provide RTL support without dynamic loading, so stub it out
2788 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2790 return wxLayout_Default
;
2793 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2797 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS