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 )
1727 int rop
wxDUMMY_INITIALIZE(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;
1749 SetROP2(GetHdc(), rop
);
1752 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1754 // We might be previewing, so return true to let it continue.
1758 void wxMSWDCImpl::EndDoc()
1762 void wxMSWDCImpl::StartPage()
1766 void wxMSWDCImpl::EndPage()
1770 // ---------------------------------------------------------------------------
1772 // ---------------------------------------------------------------------------
1774 wxCoord
wxMSWDCImpl::GetCharHeight() const
1776 WXMICROWIN_CHECK_HDC_RET(0)
1778 TEXTMETRIC lpTextMetric
;
1780 GetTextMetrics(GetHdc(), &lpTextMetric
);
1782 return lpTextMetric
.tmHeight
;
1785 wxCoord
wxMSWDCImpl::GetCharWidth() const
1787 WXMICROWIN_CHECK_HDC_RET(0)
1789 TEXTMETRIC lpTextMetric
;
1791 GetTextMetrics(GetHdc(), &lpTextMetric
);
1793 return lpTextMetric
.tmAveCharWidth
;
1796 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1797 wxCoord
*descent
, wxCoord
*externalLeading
,
1798 const wxFont
*font
) const
1800 #ifdef __WXMICROWIN__
1805 if (descent
) *descent
= 0;
1806 if (externalLeading
) *externalLeading
= 0;
1809 #endif // __WXMICROWIN__
1814 wxASSERT_MSG( font
->IsOk(), _T("invalid font in wxMSWDCImpl::GetTextExtent") );
1816 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1818 else // don't change the font
1824 const size_t len
= string
.length();
1825 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1827 wxLogLastError(_T("GetTextExtentPoint32()"));
1830 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1831 // the result computed by GetTextExtentPoint32() may be too small as it
1832 // accounts for under/overhang of the first/last character while we want
1833 // just the bounding rect for this string so adjust the width as needed
1834 // (using API not available in 2002 SDKs of WinCE)
1838 const wxChar chFirst
= *string
.begin();
1839 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1841 if ( width
.abcA
< 0 )
1842 sizeRect
.cx
-= width
.abcA
;
1846 const wxChar chLast
= *string
.rbegin();
1847 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1849 //else: we already have the width of the last character
1851 if ( width
.abcC
< 0 )
1852 sizeRect
.cx
-= width
.abcC
;
1854 //else: GetCharABCWidths() failed, not a TrueType font?
1856 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1859 ::GetTextMetrics(GetHdc(), &tm
);
1866 *descent
= tm
.tmDescent
;
1867 if (externalLeading
)
1868 *externalLeading
= tm
.tmExternalLeading
;
1872 ::SelectObject(GetHdc(), hfontOld
);
1877 // Each element of the array will be the width of the string up to and
1878 // including the coresoponding character in text.
1880 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1882 static int maxLenText
= -1;
1883 static int maxWidth
= -1;
1886 int stlen
= text
.length();
1888 if (maxLenText
== -1)
1890 // Win9x and WinNT+ have different limits
1891 int version
= wxGetOsVersion();
1892 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1893 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1897 widths
.Add(0, stlen
); // fill the array with zeros
1901 if (!::GetTextExtentExPoint(GetHdc(),
1902 text
.c_str(), // string to check
1903 wxMin(stlen
, maxLenText
),
1905 &fit
, // [out] count of chars
1907 &widths
[0], // array to fill
1911 wxLogLastError(wxT("GetTextExtentExPoint"));
1918 void wxMSWDCImpl::RealizeScaleAndOrigin()
1920 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1921 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1922 // noticeable difference between these mapping modes
1924 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1926 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1927 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1929 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1930 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1932 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1933 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1937 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1939 WXMICROWIN_CHECK_HDC
1941 m_mappingMode
= mode
;
1943 if ( mode
== wxMM_TEXT
)
1946 m_logicalScaleY
= 1.0;
1948 else // need to do some calculations
1950 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1951 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1952 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1953 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1955 if ( (mm_width
== 0) || (mm_height
== 0) )
1957 // we can't calculate mm2pixels[XY] then!
1961 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1962 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1967 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1968 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1972 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1973 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1977 m_logicalScaleX
= mm2pixelsX
;
1978 m_logicalScaleY
= mm2pixelsY
;
1982 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1983 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1987 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1991 ComputeScaleAndOrigin();
1993 RealizeScaleAndOrigin();
1996 void wxMSWDCImpl::SetUserScale(double x
, double y
)
1998 WXMICROWIN_CHECK_HDC
2000 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
2003 wxDCImpl::SetUserScale(x
,y
);
2005 RealizeScaleAndOrigin();
2008 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
2011 WXMICROWIN_CHECK_HDC
2013 int signX
= xLeftRight
? 1 : -1,
2014 signY
= yBottomUp
? -1 : 1;
2016 if (signX
== m_signX
&& signY
== m_signY
)
2019 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
2021 RealizeScaleAndOrigin();
2024 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
2026 WXMICROWIN_CHECK_HDC
2028 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
2031 wxDCImpl::SetLogicalOrigin( x
, y
);
2033 RealizeScaleAndOrigin();
2036 // For use by wxWidgets only, unless custom units are required.
2037 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2039 WXMICROWIN_CHECK_HDC
2041 wxDCImpl::SetLogicalScale(x
,y
);
2044 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2046 WXMICROWIN_CHECK_HDC
2048 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2051 wxDCImpl::SetDeviceOrigin( x
, y
);
2053 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2056 // ---------------------------------------------------------------------------
2058 // ---------------------------------------------------------------------------
2060 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2061 wxCoord dstWidth
, wxCoord dstHeight
,
2063 wxCoord srcX
, wxCoord srcY
,
2064 wxRasterOperationMode rop
, bool useMask
,
2065 wxCoord srcMaskX
, wxCoord srcMaskY
)
2067 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2070 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2071 wxCoord dstWidth
, wxCoord dstHeight
,
2073 wxCoord xsrc
, wxCoord ysrc
,
2074 wxCoord srcWidth
, wxCoord srcHeight
,
2075 wxRasterOperationMode rop
, bool useMask
,
2076 wxCoord xsrcMask
, wxCoord ysrcMask
)
2078 wxCHECK_MSG( source
, false, _T("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2080 WXMICROWIN_CHECK_HDC_RET(false)
2082 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2085 // TODO: Do we want to be able to blit from other DCs too?
2089 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2091 // if either the source or destination has alpha channel, we must use
2092 // AlphaBlt() as other function don't handle it correctly
2093 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2094 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2095 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2097 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2098 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2102 wxMask
*mask
= NULL
;
2105 mask
= bmpSrc
.GetMask();
2107 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2109 // don't give assert here because this would break existing
2110 // programs - just silently ignore useMask parameter
2115 if (xsrcMask
== -1 && ysrcMask
== -1)
2117 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2120 wxTextColoursChanger
textCol(GetHdc(), *this);
2125 case wxXOR
: dwRop
= SRCINVERT
; break;
2126 case wxINVERT
: dwRop
= DSTINVERT
; break;
2127 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2128 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2129 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2130 case wxSET
: dwRop
= WHITENESS
; break;
2131 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2132 case wxAND
: dwRop
= SRCAND
; break;
2133 case wxOR
: dwRop
= SRCPAINT
; break;
2134 case wxEQUIV
: dwRop
= 0x00990066; break;
2135 case wxNAND
: dwRop
= 0x007700E6; break;
2136 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2137 case wxCOPY
: dwRop
= SRCCOPY
; break;
2138 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2139 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2140 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2142 wxFAIL_MSG( wxT("unsupported logical function") );
2146 bool success
= false;
2151 // we want the part of the image corresponding to the mask to be
2152 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2153 // meaning of fg and bg is inverted which corresponds to wxWin notion
2154 // of the mask which is also contrary to the Windows one)
2156 // On some systems, MaskBlt succeeds yet is much much slower
2157 // than the wxWidgets fall-back implementation. So we need
2158 // to be able to switch this on and off at runtime.
2159 #if wxUSE_SYSTEM_OPTIONS
2160 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2163 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2168 xdest
, ydest
, dstWidth
, dstHeight
,
2171 (HBITMAP
)mask
->GetMaskBitmap(),
2173 MAKEROP4(dwRop
, DSTCOPY
)
2181 // Blit bitmap with mask
2184 HBITMAP buffer_bmap
;
2186 #if wxUSE_DC_CACHEING
2187 // create a temp buffer bitmap and DCs to access it and the mask
2188 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2189 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2191 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2192 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2194 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2195 dstWidth
, dstHeight
);
2197 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2198 #else // !wxUSE_DC_CACHEING
2199 // create a temp buffer bitmap and DCs to access it and the mask
2200 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2201 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2202 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2203 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2204 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2205 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2207 // copy dest to buffer
2208 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2209 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2211 wxLogLastError(wxT("BitBlt"));
2215 StretchBltModeChanger
changeMode(dc_buffer
, COLORONCOLOR
);
2218 // copy src to buffer using selected raster op
2219 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2220 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2222 wxLogLastError(wxT("StretchBlt"));
2225 // set masked area in buffer to BLACK
2227 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2228 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2229 dc_mask
, xsrcMask
, ysrcMask
,
2230 srcWidth
, srcHeight
, SRCAND
) )
2232 wxLogLastError(wxT("StretchBlt"));
2235 // set unmasked area in dest to BLACK
2236 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2237 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2238 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2239 dc_mask
, xsrcMask
, ysrcMask
,
2240 srcWidth
, srcHeight
, SRCAND
) )
2242 wxLogLastError(wxT("StretchBlt"));
2244 } // restore the original text and background colours
2246 // OR buffer to dest
2247 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2248 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2251 wxLogLastError(wxT("BitBlt"));
2254 // tidy up temporary DCs and bitmap
2255 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2256 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2258 #if !wxUSE_DC_CACHEING
2260 ::DeleteDC(dc_mask
);
2261 ::DeleteDC(dc_buffer
);
2262 ::DeleteObject(buffer_bmap
);
2267 else // no mask, just BitBlt() it
2269 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2270 // use StretchBlt() if available and finally fall back to BitBlt()
2272 // FIXME: use appropriate WinCE functions
2274 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2275 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2280 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2282 &ds
) == sizeof(ds
) )
2284 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2286 // Figure out what co-ordinate system we're supposed to specify
2288 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2292 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2295 if ( ::StretchDIBits(GetHdc(),
2297 dstWidth
, dstHeight
,
2299 srcWidth
, srcHeight
,
2301 (LPBITMAPINFO
)&ds
.dsBmih
,
2304 ) == (int)GDI_ERROR
)
2306 // On Win9x this API fails most (all?) of the time, so
2307 // logging it becomes quite distracting. Since it falls
2308 // back to the code below this is not really serious, so
2310 //wxLogLastError(wxT("StretchDIBits"));
2319 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2324 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2330 xdest
, ydest
, dstWidth
, dstHeight
,
2332 xsrc
, ysrc
, srcWidth
, srcHeight
,
2336 wxLogLastError(_T("StretchBlt"));
2346 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2347 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2349 wxLogLastError(_T("BitBlt"));
2361 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2363 WXMICROWIN_CHECK_HDC
2366 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2368 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2371 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2373 WXMICROWIN_CHECK_HDC
2375 // if we implement it in terms of DoGetSize() instead of directly using the
2376 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2377 // will also work for wxWindowDC and wxClientDC even though their size is
2378 // not the same as the total size of the screen
2379 int wPixels
, hPixels
;
2380 DoGetSize(&wPixels
, &hPixels
);
2384 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2386 wxCHECK_RET( wTotal
, _T("0 width device?") );
2388 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2393 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2395 wxCHECK_RET( hTotal
, _T("0 height device?") );
2397 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2401 wxSize
wxMSWDCImpl::GetPPI() const
2403 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2405 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2406 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2408 return wxSize(x
, y
);
2411 // ----------------------------------------------------------------------------
2413 // ----------------------------------------------------------------------------
2415 #if wxUSE_DC_CACHEING
2418 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2419 * improve it in due course, either using arrays, or simply storing pointers to one
2420 * entry for the bitmap, and two for the DCs. -- JACS
2423 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2424 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2426 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2435 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2444 wxDCCacheEntry::~wxDCCacheEntry()
2447 ::DeleteObject((HBITMAP
) m_bitmap
);
2449 ::DeleteDC((HDC
) m_dc
);
2452 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2454 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2455 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2458 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2460 if (entry
->m_depth
== depth
)
2462 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2464 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2465 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2466 if ( !entry
->m_bitmap
)
2468 wxLogLastError(wxT("CreateCompatibleBitmap"));
2470 entry
->m_width
= w
; entry
->m_height
= h
;
2476 node
= node
->GetNext();
2478 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2481 wxLogLastError(wxT("CreateCompatibleBitmap"));
2483 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2484 AddToBitmapCache(entry
);
2488 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2490 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2491 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2494 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2496 // Don't return the same one as we already have
2497 if (!notThis
|| (notThis
!= entry
))
2499 if (entry
->m_depth
== depth
)
2505 node
= node
->GetNext();
2507 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2510 wxLogLastError(wxT("CreateCompatibleDC"));
2512 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2513 AddToDCCache(entry
);
2517 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2519 sm_bitmapCache
.Append(entry
);
2522 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2524 sm_dcCache
.Append(entry
);
2527 void wxMSWDCImpl::ClearCache()
2529 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2530 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2533 // Clean up cache at app exit
2534 class wxDCModule
: public wxModule
2537 virtual bool OnInit() { return true; }
2538 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2541 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2544 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2546 #endif // wxUSE_DC_CACHEING
2548 // ----------------------------------------------------------------------------
2549 // alpha channel support
2550 // ----------------------------------------------------------------------------
2552 static bool AlphaBlt(HDC hdcDst
,
2553 int x
, int y
, int dstWidth
, int dstHeight
,
2555 int srcWidth
, int srcHeight
,
2557 const wxBitmap
& bmp
)
2559 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2560 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2562 // do we have AlphaBlend() and company in the headers?
2563 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2564 // yes, now try to see if we have it during run-time
2565 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2566 HDC
,int,int,int,int,
2570 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(_T("AlphaBlend"));
2571 if ( pfnAlphaBlend
)
2574 bf
.BlendOp
= AC_SRC_OVER
;
2576 bf
.SourceConstantAlpha
= 0xff;
2577 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2579 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2580 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2583 // skip wxAlphaBlend() call below
2587 wxLogLastError(_T("AlphaBlend"));
2590 wxUnusedVar(hdcSrc
);
2591 #endif // defined(AC_SRC_OVER)
2593 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2595 #ifdef wxHAS_RAW_BITMAP
2596 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2599 #else // !wxHAS_RAW_BITMAP
2600 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2601 // alpha but at least something will be shown like this)
2604 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2608 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2609 #ifdef wxHAS_RAW_BITMAP
2612 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2613 int dstWidth
, int dstHeight
,
2615 int srcWidth
, int srcHeight
,
2616 const wxBitmap
& bmpSrc
)
2618 // get the destination DC pixels
2619 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2621 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2623 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2625 wxLogLastError(_T("BitBlt"));
2628 // combine them with the source bitmap using alpha
2629 wxAlphaPixelData
dataDst(bmpDst
),
2630 dataSrc((wxBitmap
&)bmpSrc
);
2632 wxCHECK_RET( dataDst
&& dataSrc
,
2633 _T("failed to get raw data in wxAlphaBlend") );
2635 wxAlphaPixelData::Iterator
pDst(dataDst
),
2639 for ( int y
= 0; y
< dstHeight
; y
++ )
2641 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2643 for ( int x
= 0; x
< dstWidth
; x
++ )
2645 // source is point sampled, Alpha StretchBlit is ugly on Win95
2646 // (but does not impact performance)
2647 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2649 // note that source bitmap uses premultiplied alpha (as required by
2650 // the real AlphaBlend)
2651 const unsigned beta
= 255 - pSrc
.Alpha();
2653 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2654 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2655 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2660 pDst
= pDstRowStart
;
2661 pDst
.OffsetY(dataDst
, 1);
2664 // and finally blit them back to the destination DC
2665 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2667 wxLogLastError(_T("BitBlt"));
2671 #endif // wxHAS_RAW_BITMAP
2673 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2674 const wxColour
& initialColour
,
2675 const wxColour
& destColour
,
2676 wxDirection nDirection
)
2678 // use native function if we have compile-time support it and can load it
2679 // during run-time (linking to it statically would make the program
2680 // unusable on earlier Windows versions)
2681 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2683 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2684 static GradientFill_t pfnGradientFill
=
2685 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(_T("GradientFill"));
2687 if ( pfnGradientFill
)
2689 GRADIENT_RECT grect
;
2690 grect
.UpperLeft
= 0;
2691 grect
.LowerRight
= 1;
2693 // invert colours direction if not filling from left-to-right or
2695 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2697 // one vertex for upper left and one for upper-right
2698 TRIVERTEX vertices
[2];
2700 vertices
[0].x
= rect
.GetLeft();
2701 vertices
[0].y
= rect
.GetTop();
2702 vertices
[1].x
= rect
.GetRight()+1;
2703 vertices
[1].y
= rect
.GetBottom()+1;
2705 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2706 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2707 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2708 vertices
[firstVertex
].Alpha
= 0;
2709 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2710 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2711 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2712 vertices
[1 - firstVertex
].Alpha
= 0;
2714 if ( (*pfnGradientFill
)
2721 nDirection
== wxWEST
|| nDirection
== wxEAST
2722 ? GRADIENT_FILL_RECT_H
2723 : GRADIENT_FILL_RECT_V
2726 // skip call of the base class version below
2730 wxLogLastError(_T("GradientFill"));
2732 #endif // wxUSE_DYNLIB_CLASS
2734 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2737 #if wxUSE_DYNLIB_CLASS
2739 static DWORD
wxGetDCLayout(HDC hdc
)
2741 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2743 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2745 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2748 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2750 DWORD layout
= wxGetDCLayout(GetHdc());
2752 if ( layout
== (DWORD
)-1 )
2753 return wxLayout_Default
;
2755 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2758 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2760 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2762 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2763 if ( !s_pfnSetLayout
)
2766 if ( dir
== wxLayout_Default
)
2768 dir
= wxTheApp
->GetLayoutDirection();
2769 if ( dir
== wxLayout_Default
)
2773 DWORD layout
= wxGetDCLayout(GetHdc());
2774 if ( dir
== wxLayout_RightToLeft
)
2775 layout
|= LAYOUT_RTL
;
2777 layout
&= ~LAYOUT_RTL
;
2779 s_pfnSetLayout(GetHdc(), layout
);
2782 #else // !wxUSE_DYNLIB_CLASS
2784 // we can't provide RTL support without dynamic loading, so stub it out
2785 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2787 return wxLayout_Default
;
2790 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2794 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS