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"
33 #include "wx/dialog.h"
35 #include "wx/bitmap.h"
36 #include "wx/dcmemory.h"
39 #include "wx/dcprint.h"
40 #include "wx/module.h"
43 #include "wx/sysopt.h"
44 #include "wx/dynlib.h"
46 #ifdef wxHAVE_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 static const int MM_POINTS
= 9;
88 static const int MM_METRIC
= 10;
90 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
91 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
92 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
94 // ----------------------------------------------------------------------------
95 // macros for logical <-> device coords conversion
96 // ----------------------------------------------------------------------------
99 We currently let Windows do all the translations itself so these macros are
100 not really needed (any more) but keep them to enhance readability of the
101 code by allowing to see where are the logical and where are the device
106 #define XLOG2DEV(x) ((x-m_logicalOriginX)*m_signX)
107 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
108 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
109 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
111 #define XLOG2DEV(x) (x)
112 #define YLOG2DEV(y) (y)
113 #define XDEV2LOG(x) (x)
114 #define YDEV2LOG(y) (y)
117 // ---------------------------------------------------------------------------
119 // ---------------------------------------------------------------------------
121 // convert degrees to radians
122 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
124 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
126 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
127 // to pass it to this function but as we already have it at the point
128 // of call anyhow we do
130 // return true if we could draw the bitmap in one way or the other, false
132 static bool AlphaBlt(HDC hdcDst
,
133 int x
, int y
, int dstWidth
, int dstHeight
,
135 int srcWidth
, int srcHeight
,
137 const wxBitmap
& bmp
);
139 #ifdef wxHAVE_RAW_BITMAP
141 // our (limited) AlphaBlend() replacement for Windows versions not providing it
143 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
144 int dstWidth
, int dstHeight
,
146 int srcWidth
, int srcHeight
,
147 const wxBitmap
& bmpSrc
);
149 #endif // wxHAVE_RAW_BITMAP
151 // ----------------------------------------------------------------------------
153 // ----------------------------------------------------------------------------
155 // instead of duplicating the same code which sets and then restores text
156 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
157 // encapsulate this in a small helper class
159 // wxColourChanger: changes the text colours in the ctor if required and
160 // restores them in the dtor
161 class wxColourChanger
164 wxColourChanger(wxMSWDCImpl
& dc
);
170 COLORREF m_colFgOld
, m_colBgOld
;
174 DECLARE_NO_COPY_CLASS(wxColourChanger
)
177 // this class saves the old stretch blit mode during its life time
178 class StretchBltModeChanger
181 StretchBltModeChanger(HDC hdc
,
182 int WXUNUSED_IN_WINCE(mode
))
186 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
188 wxLogLastError(_T("SetStretchBltMode"));
192 ~StretchBltModeChanger()
195 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
196 wxLogLastError(_T("SetStretchBltMode"));
205 DECLARE_NO_COPY_CLASS(StretchBltModeChanger
)
208 #if wxUSE_DYNLIB_CLASS
210 // helper class to cache dynamically loaded libraries and not attempt reloading
212 class wxOnceOnlyDLLLoader
215 // ctor argument must be a literal string as we don't make a copy of it!
216 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
222 // return the symbol with the given name or NULL if the DLL not loaded
223 // or symbol not present
224 void *GetSymbol(const wxChar
*name
)
226 // we're prepared to handle errors here
231 m_dll
.Load(m_dllName
);
233 // reset the name whether we succeeded or failed so that we don't
234 // try again the next time
238 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
243 if ( m_dll
.IsLoaded() )
250 wxDynamicLibrary m_dll
;
251 const wxChar
*m_dllName
;
254 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(_T("msimg32"));
256 // we must ensure that DLLs are unloaded before the static objects cleanup time
257 // because we may hit the notorious DllMain() dead lock in this case if wx is
258 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
259 // under Windows because it tries to reacquire the same lock)
260 class wxGDIDLLsCleanupModule
: public wxModule
263 virtual bool OnInit() { return true; }
264 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
267 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
270 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
272 #endif // wxUSE_DYNLIB_CLASS
274 // ===========================================================================
276 // ===========================================================================
278 // ----------------------------------------------------------------------------
280 // ----------------------------------------------------------------------------
282 wxColourChanger::wxColourChanger(wxMSWDCImpl
& dc
) : m_dc(dc
)
284 const wxBrush
& brush
= dc
.GetBrush();
285 if ( brush
.IsOk() && brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
287 HDC hdc
= GetHdcOf(dc
);
288 m_colFgOld
= ::GetTextColor(hdc
);
289 m_colBgOld
= ::GetBkColor(hdc
);
291 // note that Windows convention is opposite to wxWidgets one, this is
292 // why text colour becomes the background one and vice versa
293 const wxColour
& colFg
= dc
.GetTextForeground();
296 ::SetBkColor(hdc
, colFg
.GetPixel());
299 const wxColour
& colBg
= dc
.GetTextBackground();
302 ::SetTextColor(hdc
, colBg
.GetPixel());
306 dc
.GetBackgroundMode() == wxTRANSPARENT
? TRANSPARENT
309 // flag which telsl us to undo changes in the dtor
314 // nothing done, nothing to undo
319 wxColourChanger::~wxColourChanger()
323 // restore the colours we changed
324 HDC hdc
= GetHdcOf(m_dc
);
326 ::SetBkMode(hdc
, TRANSPARENT
);
327 ::SetTextColor(hdc
, m_colFgOld
);
328 ::SetBkColor(hdc
, m_colBgOld
);
332 // ---------------------------------------------------------------------------
334 // ---------------------------------------------------------------------------
336 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
343 wxMSWDCImpl::~wxMSWDCImpl()
347 SelectOldObjects(m_hDC
);
349 // if we own the HDC, we delete it, otherwise we just release it
353 ::DeleteDC(GetHdc());
355 else // we don't own our HDC
359 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
363 // Must have been a wxScreenDC
364 ::ReleaseDC((HWND
) NULL
, GetHdc());
370 // This will select current objects out of the DC,
371 // which is what you have to do before deleting the
373 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
379 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
381 if (m_selectedBitmap
.IsOk())
383 m_selectedBitmap
.SetSelectedInto(NULL
);
390 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
395 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
400 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
407 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
410 #endif // wxUSE_PALETTE
413 m_brush
= wxNullBrush
;
416 m_palette
= wxNullPalette
;
417 #endif // wxUSE_PALETTE
419 m_backgroundBrush
= wxNullBrush
;
420 m_selectedBitmap
= wxNullBitmap
;
423 // ---------------------------------------------------------------------------
425 // ---------------------------------------------------------------------------
427 void wxMSWDCImpl::UpdateClipBox()
432 ::GetClipBox(GetHdc(), &rect
);
434 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
435 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
436 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
437 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
441 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
443 // check if we should try to retrieve the clipping region possibly not set
444 // by our SetClippingRegion() but preset by Windows:this can only happen
445 // when we're associated with an existing HDC usign SetHDC(), see there
446 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
448 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
449 self
->UpdateClipBox();
451 if ( !m_clipX1
&& !m_clipX2
)
452 self
->m_clipping
= false;
455 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
458 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
459 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
461 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
465 // note that we combine the new clipping region with the existing one: this
466 // is compatible with what the other ports do and is the documented
467 // behaviour now (starting with 2.3.3)
468 #if defined(__WXWINCE__)
470 if ( !::GetClipBox(GetHdc(), &rectClip
) )
473 // GetClipBox returns logical coordinates, so transform to device
474 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
475 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
476 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
477 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
479 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
480 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
481 rectClip
.right
, rectClip
.bottom
);
483 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
485 ::SelectClipRgn(GetHdc(), hrgnDest
);
488 ::DeleteObject(hrgnClipOld
);
489 ::DeleteObject(hrgnDest
);
491 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
493 wxLogLastError(_T("ExtSelectClipRgn"));
497 #endif // WinCE/!WinCE
504 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
506 // the region coords are always the device ones, so do the translation
509 // FIXME: possible +/-1 error here, to check!
510 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
512 LogicalToDeviceX(x
+ w
),
513 LogicalToDeviceY(y
+ h
));
516 wxLogLastError(_T("CreateRectRgn"));
520 SetClippingHrgn((WXHRGN
)hrgn
);
522 ::DeleteObject(hrgn
);
526 void wxMSWDCImpl::DoSetClippingRegionAsRegion(const wxRegion
& region
)
528 SetClippingHrgn(region
.GetHRGN());
531 void wxMSWDCImpl::DestroyClippingRegion()
535 if (m_clipping
&& m_hDC
)
538 // On a PocketPC device (not necessarily emulator), resetting
539 // the clip region as per the old method causes bad display
540 // problems. In fact setting a null region is probably OK
541 // on desktop WIN32 also, since the WIN32 docs imply that the user
542 // clipping region is independent from the paint clipping region.
543 ::SelectClipRgn(GetHdc(), 0);
545 // TODO: this should restore the previous clipping region,
546 // so that OnPaint processing works correctly, and the update
547 // clipping region doesn't get destroyed after the first
548 // DestroyClippingRegion.
549 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
550 ::SelectClipRgn(GetHdc(), rgn
);
555 wxDCImpl::DestroyClippingRegion();
558 // ---------------------------------------------------------------------------
559 // query capabilities
560 // ---------------------------------------------------------------------------
562 bool wxMSWDCImpl::CanDrawBitmap() const
567 bool wxMSWDCImpl::CanGetTextExtent() const
569 #ifdef __WXMICROWIN__
570 // TODO Extend MicroWindows' GetDeviceCaps function
573 // What sort of display is it?
574 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
576 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
580 int wxMSWDCImpl::GetDepth() const
582 WXMICROWIN_CHECK_HDC_RET(16)
584 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
587 // ---------------------------------------------------------------------------
589 // ---------------------------------------------------------------------------
591 void wxMSWDCImpl::Clear()
598 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
602 // No, I think we should simply ignore this if printing on e.g.
604 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
605 if (!m_selectedBitmap
.IsOk())
608 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
609 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
610 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
614 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
617 DWORD colour
= ::GetBkColor(GetHdc());
618 HBRUSH brush
= ::CreateSolidBrush(colour
);
619 ::FillRect(GetHdc(), &rect
, brush
);
620 ::DeleteObject(brush
);
622 RealizeScaleAndOrigin();
625 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
626 wxCoord
WXUNUSED_IN_WINCE(y
),
627 const wxColour
& WXUNUSED_IN_WINCE(col
),
628 int WXUNUSED_IN_WINCE(style
))
633 WXMICROWIN_CHECK_HDC_RET(false)
635 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
637 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
638 : FLOODFILLBORDER
) ) ;
641 // quoting from the MSDN docs:
643 // Following are some of the reasons this function might fail:
645 // * The filling could not be completed.
646 // * The specified point has the boundary color specified by the
647 // crColor parameter (if FLOODFILLBORDER was requested).
648 // * The specified point does not have the color specified by
649 // crColor (if FLOODFILLSURFACE was requested)
650 // * The point is outside the clipping region that is, it is not
651 // visible on the device.
653 wxLogLastError(wxT("ExtFloodFill"));
656 CalcBoundingBox(x
, y
);
662 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
664 WXMICROWIN_CHECK_HDC_RET(false)
666 wxCHECK_MSG( col
, false, _T("NULL colour parameter in wxMSWDCImpl::GetPixel") );
668 // get the color of the pixel
669 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
671 wxRGBToColour(*col
, pixelcolor
);
676 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
680 wxCoord x1
= x
-VIEWPORT_EXTENT
;
681 wxCoord y1
= y
-VIEWPORT_EXTENT
;
682 wxCoord x2
= x
+VIEWPORT_EXTENT
;
683 wxCoord y2
= y
+VIEWPORT_EXTENT
;
685 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
686 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
688 CalcBoundingBox(x1
, y1
);
689 CalcBoundingBox(x2
, y2
);
692 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
696 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
698 CalcBoundingBox(x1
, y1
);
699 CalcBoundingBox(x2
, y2
);
702 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
703 // and ending at (x2, y2)
704 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
705 wxCoord x2
, wxCoord y2
,
706 wxCoord xc
, wxCoord yc
)
709 // Slower emulation since WinCE doesn't support Pie and Arc
710 double r
= sqrt( (x1
-xc
)*(x1
-xc
) + (y1
-yc
)*(y1
-yc
) );
711 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
712 if( y1
>yc
) sa
= -sa
; // below center
713 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
714 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
719 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
723 double radius
= (double)sqrt(dx
*dx
+dy
*dy
);
724 wxCoord r
= (wxCoord
)radius
;
726 // treat the special case of full circle separately
727 if ( x1
== x2
&& y1
== y2
)
729 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
733 wxCoord xx1
= XLOG2DEV(x1
);
734 wxCoord yy1
= YLOG2DEV(y1
);
735 wxCoord xx2
= XLOG2DEV(x2
);
736 wxCoord yy2
= YLOG2DEV(y2
);
737 wxCoord xxc
= XLOG2DEV(xc
);
738 wxCoord yyc
= YLOG2DEV(yc
);
739 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
741 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
742 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
743 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
744 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
746 if ( m_brush
.IsOk() && m_brush
.GetStyle() != wxTRANSPARENT
)
748 // Have to add 1 to bottom-right corner of rectangle
749 // to make semi-circles look right (crooked line otherwise).
750 // Unfortunately this is not a reliable method, depends
751 // on the size of shape.
752 // TODO: figure out why this happens!
753 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
757 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
760 CalcBoundingBox(xc
- r
, yc
- r
);
761 CalcBoundingBox(xc
+ r
, yc
+ r
);
765 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
766 wxCoord width
, wxCoord height
)
768 // cases when we don't have DrawFrameControl()
769 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
770 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
772 wxCoord x2
= x1
+ width
,
782 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
);
784 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
787 CalcBoundingBox(x1
, y1
);
788 CalcBoundingBox(x2
, y2
);
789 #endif // Microwin/Normal
792 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
796 COLORREF color
= 0x00ffffff;
799 color
= m_pen
.GetColour().GetPixel();
802 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
804 CalcBoundingBox(x
, y
);
807 void wxMSWDCImpl::DoDrawPolygon(int n
,
811 int WXUNUSED_IN_WINCE(fillStyle
))
815 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
817 // Do things less efficiently if we have offsets
818 if (xoffset
!= 0 || yoffset
!= 0)
820 POINT
*cpoints
= new POINT
[n
];
822 for (i
= 0; i
< n
; i
++)
824 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
825 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
827 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
830 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
832 (void)Polygon(GetHdc(), cpoints
, n
);
834 SetPolyFillMode(GetHdc(),prev
);
841 for (i
= 0; i
< n
; i
++)
842 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
845 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
847 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
849 SetPolyFillMode(GetHdc(),prev
);
855 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
863 wxDCBase::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
867 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
869 for (i
= cnt
= 0; i
< n
; i
++)
872 // Do things less efficiently if we have offsets
873 if (xoffset
!= 0 || yoffset
!= 0)
875 POINT
*cpoints
= new POINT
[cnt
];
876 for (i
= 0; i
< cnt
; i
++)
878 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
879 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
881 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
884 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
886 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
888 SetPolyFillMode(GetHdc(),prev
);
894 for (i
= 0; i
< cnt
; i
++)
895 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
898 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
900 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
902 SetPolyFillMode(GetHdc(),prev
);
909 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
913 // Do things less efficiently if we have offsets
914 if (xoffset
!= 0 || yoffset
!= 0)
916 POINT
*cpoints
= new POINT
[n
];
918 for (i
= 0; i
< n
; i
++)
920 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
921 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
923 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
925 (void)Polyline(GetHdc(), cpoints
, n
);
931 for (i
= 0; i
< n
; i
++)
932 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
934 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
938 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
942 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
944 wxCoord x2
= x
+ width
;
945 wxCoord y2
= y
+ height
;
947 wxCoord x2dev
= XLOG2DEV(x2
),
948 y2dev
= YLOG2DEV(y2
);
950 // Windows (but not Windows CE) draws the filled rectangles without outline
951 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
952 // and we want them to have the same size regardless of which pen is used
954 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
959 #endif // !__WXWINCE__
961 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
963 CalcBoundingBox(x
, y
);
964 CalcBoundingBox(x2
, y2
);
967 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
971 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
973 // Now, a negative radius value is interpreted to mean
974 // 'the proportion of the smallest X or Y dimension'
978 double smallest
= (width
< height
) ? width
: height
;
979 radius
= (- radius
* smallest
);
982 wxCoord x2
= (x
+width
);
983 wxCoord y2
= (y
+height
);
985 // Windows draws the filled rectangles without outline (i.e. drawn with a
986 // transparent pen) one pixel smaller in both directions and we want them
987 // to have the same size regardless of which pen is used - adjust
988 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
994 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
995 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
997 CalcBoundingBox(x
, y
);
998 CalcBoundingBox(x2
, y2
);
1001 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1003 WXMICROWIN_CHECK_HDC
1005 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1007 wxCoord x2
= (x
+width
);
1008 wxCoord y2
= (y
+height
);
1010 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1012 CalcBoundingBox(x
, y
);
1013 CalcBoundingBox(x2
, y2
);
1017 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1020 // WinCE does not support ::PolyBezier so use generic version
1021 wxDCBase::DoDrawSpline(points
);
1023 // quadratic b-spline to cubic bezier spline conversion
1025 // quadratic spline with control points P0,P1,P2
1026 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1028 // bezier spline with control points B0,B1,B2,B3
1029 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1031 // control points of bezier spline calculated from b-spline
1033 // B1 = (2*P1 + P0)/3
1034 // B2 = (2*P1 + P2)/3
1037 WXMICROWIN_CHECK_HDC
1039 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1041 const size_t n_points
= points
->GetCount();
1042 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1044 const size_t n_bezier_points
= n_points
* 3 + 1;
1045 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1046 size_t bezier_pos
= 0;
1047 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1049 wxPointList::compatibility_iterator node
= points
->GetFirst();
1050 wxPoint
*p
= node
->GetData();
1051 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1052 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1054 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1057 node
= node
->GetNext();
1058 p
= node
->GetData();
1062 cx1
= ( x1
+ x2
) / 2;
1063 cy1
= ( y1
+ y2
) / 2;
1064 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1065 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1067 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1071 while ((node
= node
->GetNext()) != NULL
)
1073 while ((node
= node
->GetNext()))
1074 #endif // !wxUSE_STL
1076 p
= (wxPoint
*)node
->GetData();
1081 cx4
= (x1
+ x2
) / 2;
1082 cy4
= (y1
+ y2
) / 2;
1083 // B0 is B3 of previous segment
1085 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1086 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1089 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1090 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1093 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1094 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1100 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1102 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1103 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1105 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1108 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1115 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1116 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1119 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1122 WXMICROWIN_CHECK_HDC
1124 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1129 int rx1
= XLOG2DEV(x
+w
/2);
1130 int ry1
= YLOG2DEV(y
+h
/2);
1137 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1138 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1139 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1140 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1142 // Swap start and end positions if the end angle is less than the start angle.
1153 // draw pie with NULL_PEN first and then outline otherwise a line is
1154 // drawn from the start and end points to the centre
1155 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1158 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1159 rx1
, ry1
, rx2
, ry2
);
1163 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1164 rx1
, ry1
-1, rx2
, ry2
-1);
1167 ::SelectObject(GetHdc(), hpenOld
);
1169 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1170 rx1
, ry1
, rx2
, ry2
);
1172 CalcBoundingBox(x
, y
);
1173 CalcBoundingBox(x2
, y2
);
1177 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1179 WXMICROWIN_CHECK_HDC
1181 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1184 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1186 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1189 CalcBoundingBox(x
, y
);
1190 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1193 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1195 WXMICROWIN_CHECK_HDC
1197 wxCHECK_RET( bmp
.IsOk(), _T("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1199 int width
= bmp
.GetWidth(),
1200 height
= bmp
.GetHeight();
1202 HBITMAP hbmpMask
= 0;
1205 HPALETTE oldPal
= 0;
1206 #endif // wxUSE_PALETTE
1208 if ( bmp
.HasAlpha() )
1211 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1213 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1219 wxMask
*mask
= bmp
.GetMask();
1221 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1225 // don't give assert here because this would break existing
1226 // programs - just silently ignore useMask parameter
1233 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1235 // On some systems, MaskBlt succeeds yet is much much slower
1236 // than the wxWidgets fall-back implementation. So we need
1237 // to be able to switch this on and off at runtime.
1239 #if wxUSE_SYSTEM_OPTIONS
1240 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1244 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1245 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1247 wxPalette
*pal
= bmp
.GetPalette();
1248 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1250 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1251 ::RealizePalette(hdcMem
);
1253 #endif // wxUSE_PALETTE
1255 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1258 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1262 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1263 #endif // wxUSE_PALETTE
1265 ::SelectObject(hdcMem
, hOldBitmap
);
1272 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1276 memDC
.SelectObjectAsSource(bmp
);
1278 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1280 memDC
.SelectObject(wxNullBitmap
);
1283 else // no mask, just use BitBlt()
1286 HDC memdc
= ::CreateCompatibleDC( cdc
);
1287 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1289 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1291 COLORREF old_textground
= ::GetTextColor(GetHdc());
1292 COLORREF old_background
= ::GetBkColor(GetHdc());
1293 if (m_textForegroundColour
.IsOk())
1295 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1297 if (m_textBackgroundColour
.IsOk())
1299 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1303 wxPalette
*pal
= bmp
.GetPalette();
1304 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1306 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1307 ::RealizePalette(memdc
);
1309 #endif // wxUSE_PALETTE
1311 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1312 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1316 ::SelectPalette(memdc
, oldPal
, FALSE
);
1317 #endif // wxUSE_PALETTE
1319 ::SelectObject( memdc
, hOldBitmap
);
1320 ::DeleteDC( memdc
);
1322 ::SetTextColor(GetHdc(), old_textground
);
1323 ::SetBkColor(GetHdc(), old_background
);
1327 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1329 WXMICROWIN_CHECK_HDC
1331 DrawAnyText(text
, x
, y
);
1333 // update the bounding box
1334 CalcBoundingBox(x
, y
);
1337 GetOwner()->GetTextExtent(text
, &w
, &h
);
1338 CalcBoundingBox(x
+ w
, y
+ h
);
1341 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1343 WXMICROWIN_CHECK_HDC
1345 // prepare for drawing the text
1346 if ( m_textForegroundColour
.IsOk() )
1347 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
1349 DWORD old_background
= 0;
1350 if ( m_textBackgroundColour
.IsOk() )
1352 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1355 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
1359 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1360 text
.c_str(), text
.length(), NULL
) == 0 )
1362 wxLogLastError(wxT("TextOut"));
1365 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
1366 text
.c_str(), text
.length()) == 0 )
1368 wxLogLastError(wxT("TextOut"));
1372 // restore the old parameters (text foreground colour may be left because
1373 // it never is set to anything else, but background should remain
1374 // transparent even if we just drew an opaque string)
1375 if ( m_textBackgroundColour
.IsOk() )
1376 (void)SetBkColor(GetHdc(), old_background
);
1378 SetBkMode(GetHdc(), TRANSPARENT
);
1381 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1382 wxCoord x
, wxCoord y
,
1385 WXMICROWIN_CHECK_HDC
1387 // we test that we have some font because otherwise we should still use the
1388 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1389 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1390 // font for drawing rotated fonts unfortunately)
1391 if ( (angle
== 0.0) && m_font
.IsOk() )
1393 DoDrawText(text
, x
, y
);
1395 #ifndef __WXMICROWIN__
1398 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1399 // because it's not TrueType and so can't have non zero
1400 // orientation/escapement under Win9x
1401 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1402 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1404 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1406 wxLogLastError(wxT("GetObject(hfont)"));
1409 // GDI wants the angle in tenth of degree
1410 long angle10
= (long)(angle
* 10);
1411 lf
.lfEscapement
= angle10
;
1412 lf
. lfOrientation
= angle10
;
1414 hfont
= ::CreateFontIndirect(&lf
);
1417 wxLogLastError(wxT("CreateFont"));
1421 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1423 DrawAnyText(text
, x
, y
);
1425 (void)::SelectObject(GetHdc(), hfontOld
);
1426 (void)::DeleteObject(hfont
);
1429 // call the bounding box by adding all four vertices of the rectangle
1430 // containing the text to it (simpler and probably not slower than
1431 // determining which of them is really topmost/leftmost/...)
1433 GetOwner()->GetTextExtent(text
, &w
, &h
);
1435 double rad
= DegToRad(angle
);
1437 // "upper left" and "upper right"
1438 CalcBoundingBox(x
, y
);
1439 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1441 // "bottom left" and "bottom right"
1442 x
+= (wxCoord
)(h
*sin(rad
));
1443 y
+= (wxCoord
)(h
*cos(rad
));
1444 CalcBoundingBox(x
, y
);
1445 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1450 // ---------------------------------------------------------------------------
1452 // ---------------------------------------------------------------------------
1456 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1458 WXMICROWIN_CHECK_HDC
1460 // Set the old object temporarily, in case the assignment deletes an object
1461 // that's not yet selected out.
1464 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1468 if ( m_palette
.IsOk() )
1470 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1471 GetHpaletteOf(m_palette
),
1474 m_oldPalette
= (WXHPALETTE
) oldPal
;
1477 ::RealizePalette(GetHdc());
1481 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1483 if ( palette
.IsOk() )
1485 m_palette
= palette
;
1486 DoSelectPalette(true);
1490 void wxMSWDCImpl::InitializePalette()
1492 if ( wxDisplayDepth() <= 8 )
1494 // look for any window or parent that has a custom palette. If any has
1495 // one then we need to use it in drawing operations
1496 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1498 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1499 if ( m_hasCustomPalette
)
1501 m_palette
= win
->GetPalette();
1503 // turn on MSW translation for this palette
1509 #endif // wxUSE_PALETTE
1511 // SetFont/Pen/Brush() really ask to be implemented as a single template
1512 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1514 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1516 WXMICROWIN_CHECK_HDC
1518 if ( font
== m_font
)
1523 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1524 if ( hfont
== HGDI_ERROR
)
1526 wxLogLastError(_T("SelectObject(font)"));
1531 m_oldFont
= (WXHFONT
)hfont
;
1536 else // invalid font, reset the current font
1540 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1542 wxLogLastError(_T("SelectObject(old font)"));
1548 m_font
= wxNullFont
;
1552 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1554 WXMICROWIN_CHECK_HDC
1561 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1562 if ( hpen
== HGDI_ERROR
)
1564 wxLogLastError(_T("SelectObject(pen)"));
1569 m_oldPen
= (WXHPEN
)hpen
;
1574 else // invalid pen, reset the current pen
1578 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1580 wxLogLastError(_T("SelectObject(old pen)"));
1590 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1592 WXMICROWIN_CHECK_HDC
1594 if ( brush
== m_brush
)
1599 // we must make sure the brush is aligned with the logical coordinates
1600 // before selecting it
1601 wxBitmap
*stipple
= brush
.GetStipple();
1602 if ( stipple
&& stipple
->IsOk() )
1604 if ( !::SetBrushOrgEx
1607 m_deviceOriginX
% stipple
->GetWidth(),
1608 m_deviceOriginY
% stipple
->GetHeight(),
1609 NULL
// [out] previous brush origin
1612 wxLogLastError(_T("SetBrushOrgEx()"));
1616 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1617 if ( hbrush
== HGDI_ERROR
)
1619 wxLogLastError(_T("SelectObject(brush)"));
1624 m_oldBrush
= (WXHBRUSH
)hbrush
;
1629 else // invalid brush, reset the current brush
1633 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1635 wxLogLastError(_T("SelectObject(old brush)"));
1641 m_brush
= wxNullBrush
;
1645 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1647 WXMICROWIN_CHECK_HDC
1649 m_backgroundBrush
= brush
;
1651 if ( m_backgroundBrush
.IsOk() )
1653 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1657 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1659 WXMICROWIN_CHECK_HDC
1661 m_backgroundMode
= mode
;
1663 // SetBackgroundColour now only refers to text background
1664 // and m_backgroundMode is used there
1667 void wxMSWDCImpl::SetLogicalFunction(int function
)
1669 WXMICROWIN_CHECK_HDC
1671 m_logicalFunction
= function
;
1676 void wxMSWDCImpl::SetRop(WXHDC dc
)
1678 if ( !dc
|| m_logicalFunction
< 0 )
1683 switch (m_logicalFunction
)
1685 case wxCLEAR
: rop
= R2_BLACK
; break;
1686 case wxXOR
: rop
= R2_XORPEN
; break;
1687 case wxINVERT
: rop
= R2_NOT
; break;
1688 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1689 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1690 case wxCOPY
: rop
= R2_COPYPEN
; break;
1691 case wxAND
: rop
= R2_MASKPEN
; break;
1692 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1693 case wxNO_OP
: rop
= R2_NOP
; break;
1694 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1695 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1696 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1697 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1698 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1699 case wxOR
: rop
= R2_MERGEPEN
; break;
1700 case wxSET
: rop
= R2_WHITE
; break;
1703 wxFAIL_MSG( wxT("unsupported logical function") );
1707 SetROP2(GetHdc(), rop
);
1710 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1712 // We might be previewing, so return true to let it continue.
1716 void wxMSWDCImpl::EndDoc()
1720 void wxMSWDCImpl::StartPage()
1724 void wxMSWDCImpl::EndPage()
1728 // ---------------------------------------------------------------------------
1730 // ---------------------------------------------------------------------------
1732 wxCoord
wxMSWDCImpl::GetCharHeight() const
1734 WXMICROWIN_CHECK_HDC_RET(0)
1736 TEXTMETRIC lpTextMetric
;
1738 GetTextMetrics(GetHdc(), &lpTextMetric
);
1740 return lpTextMetric
.tmHeight
;
1743 wxCoord
wxMSWDCImpl::GetCharWidth() const
1745 WXMICROWIN_CHECK_HDC_RET(0)
1747 TEXTMETRIC lpTextMetric
;
1749 GetTextMetrics(GetHdc(), &lpTextMetric
);
1751 return lpTextMetric
.tmAveCharWidth
;
1754 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1755 wxCoord
*descent
, wxCoord
*externalLeading
,
1756 const wxFont
*font
) const
1758 #ifdef __WXMICROWIN__
1763 if (descent
) *descent
= 0;
1764 if (externalLeading
) *externalLeading
= 0;
1767 #endif // __WXMICROWIN__
1772 wxASSERT_MSG( font
->IsOk(), _T("invalid font in wxMSWDCImpl::GetTextExtent") );
1774 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1776 else // don't change the font
1782 const size_t len
= string
.length();
1783 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1785 wxLogLastError(_T("GetTextExtentPoint32()"));
1788 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1789 // the result computed by GetTextExtentPoint32() may be too small as it
1790 // accounts for under/overhang of the first/last character while we want
1791 // just the bounding rect for this string so adjust the width as needed
1792 // (using API not available in 2002 SDKs of WinCE)
1796 const wxChar chFirst
= *string
.begin();
1797 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1799 if ( width
.abcA
< 0 )
1800 sizeRect
.cx
-= width
.abcA
;
1804 const wxChar chLast
= *string
.rbegin();
1805 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1807 //else: we already have the width of the last character
1809 if ( width
.abcC
< 0 )
1810 sizeRect
.cx
-= width
.abcC
;
1812 //else: GetCharABCWidths() failed, not a TrueType font?
1814 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1817 ::GetTextMetrics(GetHdc(), &tm
);
1824 *descent
= tm
.tmDescent
;
1825 if (externalLeading
)
1826 *externalLeading
= tm
.tmExternalLeading
;
1830 ::SelectObject(GetHdc(), hfontOld
);
1835 // Each element of the array will be the width of the string up to and
1836 // including the coresoponding character in text.
1838 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1840 static int maxLenText
= -1;
1841 static int maxWidth
= -1;
1844 int stlen
= text
.length();
1846 if (maxLenText
== -1)
1848 // Win9x and WinNT+ have different limits
1849 int version
= wxGetOsVersion();
1850 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1851 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1855 widths
.Add(0, stlen
); // fill the array with zeros
1859 if (!::GetTextExtentExPoint(GetHdc(),
1860 text
.c_str(), // string to check
1861 wxMin(stlen
, maxLenText
),
1863 &fit
, // [out] count of chars
1865 &widths
[0], // array to fill
1869 wxLogLastError(wxT("GetTextExtentExPoint"));
1876 void wxMSWDCImpl::RealizeScaleAndOrigin()
1878 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1879 // cases we could do with MM_TEXT and in the remaining 0.9% with
1880 // MM_ISOTROPIC (TODO!)
1882 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1884 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1885 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1887 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1888 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1890 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1891 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1896 void wxMSWDCImpl::SetMapMode(int mode
)
1898 WXMICROWIN_CHECK_HDC
1900 m_mappingMode
= mode
;
1902 if ( mode
== wxMM_TEXT
)
1905 m_logicalScaleY
= 1.0;
1907 else // need to do some calculations
1909 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1910 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1911 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1912 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1914 if ( (mm_width
== 0) || (mm_height
== 0) )
1916 // we can't calculate mm2pixels[XY] then!
1920 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1921 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1926 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1927 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1931 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1932 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1936 m_logicalScaleX
= mm2pixelsX
;
1937 m_logicalScaleY
= mm2pixelsY
;
1941 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1942 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1946 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1950 ComputeScaleAndOrigin();
1952 RealizeScaleAndOrigin();
1955 void wxMSWDCImpl::SetUserScale(double x
, double y
)
1957 WXMICROWIN_CHECK_HDC
1959 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1962 wxDCImpl::SetUserScale(x
,y
);
1964 RealizeScaleAndOrigin();
1967 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
1970 WXMICROWIN_CHECK_HDC
1972 int signX
= xLeftRight
? 1 : -1,
1973 signY
= yBottomUp
? -1 : 1;
1975 if (signX
== m_signX
&& signY
== m_signY
)
1978 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
1980 RealizeScaleAndOrigin();
1983 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1985 WXMICROWIN_CHECK_HDC
1987 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1990 wxDCImpl::SetLogicalOrigin( x
, y
);
1993 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1997 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1999 WXMICROWIN_CHECK_HDC
2001 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2004 wxDCImpl::SetDeviceOrigin( x
, y
);
2006 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2009 // ---------------------------------------------------------------------------
2011 // ---------------------------------------------------------------------------
2013 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2014 wxCoord dstWidth
, wxCoord dstHeight
,
2016 wxCoord srcX
, wxCoord srcY
,
2017 int rop
, bool useMask
,
2018 wxCoord srcMaskX
, wxCoord srcMaskY
)
2020 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2023 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2024 wxCoord dstWidth
, wxCoord dstHeight
,
2026 wxCoord xsrc
, wxCoord ysrc
,
2027 wxCoord srcWidth
, wxCoord srcHeight
,
2028 int rop
, bool useMask
,
2029 wxCoord xsrcMask
, wxCoord ysrcMask
)
2031 wxCHECK_MSG( source
, false, _T("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2033 WXMICROWIN_CHECK_HDC_RET(false)
2035 wxDCImpl
*impl
= source
->GetImpl();
2036 wxMSWDCImpl
*msw_impl
= wxDynamicCast( impl
, wxMSWDCImpl
);
2039 // TODO: Do we want to be able to blit
2040 // from other DCs too?
2044 // if either the source or destination has alpha channel, we must use
2045 // AlphaBlt() as other function don't handle it correctly
2046 const wxBitmap
& bmpSrc
= msw_impl
->GetSelectedBitmap();
2047 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2048 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2050 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2051 xsrc
, ysrc
, srcWidth
, srcHeight
, GetHdcOf(*msw_impl
), bmpSrc
) )
2055 wxMask
*mask
= NULL
;
2058 mask
= bmpSrc
.GetMask();
2060 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2062 // don't give assert here because this would break existing
2063 // programs - just silently ignore useMask parameter
2068 if (xsrcMask
== -1 && ysrcMask
== -1)
2070 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2073 COLORREF old_textground
= ::GetTextColor(GetHdc());
2074 COLORREF old_background
= ::GetBkColor(GetHdc());
2075 if (m_textForegroundColour
.IsOk())
2077 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
2079 if (m_textBackgroundColour
.IsOk())
2081 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
2087 case wxXOR
: dwRop
= SRCINVERT
; break;
2088 case wxINVERT
: dwRop
= DSTINVERT
; break;
2089 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2090 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2091 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2092 case wxSET
: dwRop
= WHITENESS
; break;
2093 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2094 case wxAND
: dwRop
= SRCAND
; break;
2095 case wxOR
: dwRop
= SRCPAINT
; break;
2096 case wxEQUIV
: dwRop
= 0x00990066; break;
2097 case wxNAND
: dwRop
= 0x007700E6; break;
2098 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2099 case wxCOPY
: dwRop
= SRCCOPY
; break;
2100 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2101 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2102 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2104 wxFAIL_MSG( wxT("unsupported logical function") );
2108 bool success
= false;
2113 // we want the part of the image corresponding to the mask to be
2114 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2115 // meaning of fg and bg is inverted which corresponds to wxWin notion
2116 // of the mask which is also contrary to the Windows one)
2118 // On some systems, MaskBlt succeeds yet is much much slower
2119 // than the wxWidgets fall-back implementation. So we need
2120 // to be able to switch this on and off at runtime.
2121 #if wxUSE_SYSTEM_OPTIONS
2122 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2125 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2130 xdest
, ydest
, dstWidth
, dstHeight
,
2131 GetHdcOf(*msw_impl
),
2133 (HBITMAP
)mask
->GetMaskBitmap(),
2135 MAKEROP4(dwRop
, DSTCOPY
)
2143 // Blit bitmap with mask
2146 HBITMAP buffer_bmap
;
2148 #if wxUSE_DC_CACHEING
2149 // create a temp buffer bitmap and DCs to access it and the mask
2150 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, msw_impl
->GetHDC());
2151 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2153 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2154 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2156 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2157 dstWidth
, dstHeight
);
2159 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2160 #else // !wxUSE_DC_CACHEING
2161 // create a temp buffer bitmap and DCs to access it and the mask
2162 dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
2163 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2164 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2165 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2166 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2167 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2169 // copy dest to buffer
2170 if ( !::BitBlt(dc_buffer
, 0, 0, (int)dstWidth
, (int)dstHeight
,
2171 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2173 wxLogLastError(wxT("BitBlt"));
2177 StretchBltModeChanger
changeMode(dc_buffer
, COLORONCOLOR
);
2180 // copy src to buffer using selected raster op
2181 if ( !::StretchBlt(dc_buffer
, 0, 0, (int)dstWidth
, (int)dstHeight
,
2182 GetHdcOf(*msw_impl
), xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2184 wxLogLastError(wxT("StretchBlt"));
2187 // set masked area in buffer to BLACK (pixel value 0)
2188 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
2189 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
2190 if ( !::StretchBlt(dc_buffer
, 0, 0, (int)dstWidth
, (int)dstHeight
,
2191 dc_mask
, xsrcMask
, ysrcMask
, srcWidth
, srcHeight
, SRCAND
) )
2193 wxLogLastError(wxT("StretchBlt"));
2196 // set unmasked area in dest to BLACK
2197 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2198 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2199 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, (int)dstWidth
, (int)dstHeight
,
2200 dc_mask
, xsrcMask
, ysrcMask
, srcWidth
, srcHeight
, SRCAND
) )
2202 wxLogLastError(wxT("StretchBlt"));
2204 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
2205 ::SetTextColor(GetHdc(), prevCol
);
2207 // OR buffer to dest
2208 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
2209 (int)dstWidth
, (int)dstHeight
,
2210 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2213 wxLogLastError(wxT("BitBlt"));
2216 // tidy up temporary DCs and bitmap
2217 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2218 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2220 #if !wxUSE_DC_CACHEING
2222 ::DeleteDC(dc_mask
);
2223 ::DeleteDC(dc_buffer
);
2224 ::DeleteObject(buffer_bmap
);
2229 else // no mask, just BitBlt() it
2231 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2232 // use StretchBlt() if available and finally fall back to BitBlt()
2234 // FIXME: use appropriate WinCE functions
2236 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2237 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2242 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2244 &ds
) == sizeof(ds
) )
2246 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2248 // Figure out what co-ordinate system we're supposed to specify
2250 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2254 ysrc
= hDIB
- (ysrc
+ dstHeight
);
2257 if ( ::StretchDIBits(GetHdc(),
2259 dstWidth
, dstHeight
,
2261 srcWidth
, srcHeight
,
2263 (LPBITMAPINFO
)&ds
.dsBmih
,
2266 ) == (int)GDI_ERROR
)
2268 // On Win9x this API fails most (all?) of the time, so
2269 // logging it becomes quite distracting. Since it falls
2270 // back to the code below this is not really serious, so
2272 //wxLogLastError(wxT("StretchDIBits"));
2281 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2286 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2292 xdest
, ydest
, dstWidth
, dstHeight
,
2293 GetHdcOf(*msw_impl
),
2294 xsrc
, ysrc
, srcWidth
, srcHeight
,
2298 wxLogLastError(_T("StretchBlt"));
2312 (int)dstWidth
, (int)dstHeight
,
2313 GetHdcOf(*msw_impl
),
2318 wxLogLastError(_T("BitBlt"));
2327 ::SetTextColor(GetHdc(), old_textground
);
2328 ::SetBkColor(GetHdc(), old_background
);
2333 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2335 WXMICROWIN_CHECK_HDC
2338 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2340 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2343 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2345 WXMICROWIN_CHECK_HDC
2347 // if we implement it in terms of DoGetSize() instead of directly using the
2348 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2349 // will also work for wxWindowDC and wxClientDC even though their size is
2350 // not the same as the total size of the screen
2351 int wPixels
, hPixels
;
2352 DoGetSize(&wPixels
, &hPixels
);
2356 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2358 wxCHECK_RET( wTotal
, _T("0 width device?") );
2360 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2365 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2367 wxCHECK_RET( hTotal
, _T("0 height device?") );
2369 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2373 wxSize
wxMSWDCImpl::GetPPI() const
2375 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2377 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2378 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2380 return wxSize(x
, y
);
2383 // For use by wxWidgets only, unless custom units are required.
2384 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2386 WXMICROWIN_CHECK_HDC
2388 wxDCImpl::SetLogicalScale(x
,y
);
2391 // ----------------------------------------------------------------------------
2393 // ----------------------------------------------------------------------------
2395 #if wxUSE_DC_CACHEING
2398 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2399 * improve it in due course, either using arrays, or simply storing pointers to one
2400 * entry for the bitmap, and two for the DCs. -- JACS
2403 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2404 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2406 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2415 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2424 wxDCCacheEntry::~wxDCCacheEntry()
2427 ::DeleteObject((HBITMAP
) m_bitmap
);
2429 ::DeleteDC((HDC
) m_dc
);
2432 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2434 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2435 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2438 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2440 if (entry
->m_depth
== depth
)
2442 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2444 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2445 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2446 if ( !entry
->m_bitmap
)
2448 wxLogLastError(wxT("CreateCompatibleBitmap"));
2450 entry
->m_width
= w
; entry
->m_height
= h
;
2456 node
= node
->GetNext();
2458 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2461 wxLogLastError(wxT("CreateCompatibleBitmap"));
2463 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2464 AddToBitmapCache(entry
);
2468 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2470 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2471 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2474 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2476 // Don't return the same one as we already have
2477 if (!notThis
|| (notThis
!= entry
))
2479 if (entry
->m_depth
== depth
)
2485 node
= node
->GetNext();
2487 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2490 wxLogLastError(wxT("CreateCompatibleDC"));
2492 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2493 AddToDCCache(entry
);
2497 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2499 sm_bitmapCache
.Append(entry
);
2502 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2504 sm_dcCache
.Append(entry
);
2507 void wxMSWDCImpl::ClearCache()
2509 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2510 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2513 // Clean up cache at app exit
2514 class wxDCModule
: public wxModule
2517 virtual bool OnInit() { return true; }
2518 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2521 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2524 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2526 #endif // wxUSE_DC_CACHEING
2528 // ----------------------------------------------------------------------------
2529 // alpha channel support
2530 // ----------------------------------------------------------------------------
2532 static bool AlphaBlt(HDC hdcDst
,
2533 int x
, int y
, int dstWidth
, int dstHeight
,
2535 int srcWidth
, int srcHeight
,
2537 const wxBitmap
& bmp
)
2539 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2540 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2542 // do we have AlphaBlend() and company in the headers?
2543 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2544 // yes, now try to see if we have it during run-time
2545 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2546 HDC
,int,int,int,int,
2550 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(_T("AlphaBlend"));
2551 if ( pfnAlphaBlend
)
2554 bf
.BlendOp
= AC_SRC_OVER
;
2556 bf
.SourceConstantAlpha
= 0xff;
2557 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2559 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2560 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2563 // skip wxAlphaBlend() call below
2567 wxLogLastError(_T("AlphaBlend"));
2570 wxUnusedVar(hdcSrc
);
2571 #endif // defined(AC_SRC_OVER)
2573 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2575 #ifdef wxHAVE_RAW_BITMAP
2576 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2579 #else // !wxHAVE_RAW_BITMAP
2580 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2581 // alpha but at least something will be shown like this)
2584 #endif // wxHAVE_RAW_BITMAP
2588 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2589 #ifdef wxHAVE_RAW_BITMAP
2592 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2593 int dstWidth
, int dstHeight
,
2595 int srcWidth
, int srcHeight
,
2596 const wxBitmap
& bmpSrc
)
2598 // get the destination DC pixels
2599 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2601 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2603 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2605 wxLogLastError(_T("BitBlt"));
2608 // combine them with the source bitmap using alpha
2609 wxAlphaPixelData
dataDst(bmpDst
),
2610 dataSrc((wxBitmap
&)bmpSrc
);
2612 wxCHECK_RET( dataDst
&& dataSrc
,
2613 _T("failed to get raw data in wxAlphaBlend") );
2615 wxAlphaPixelData::Iterator
pDst(dataDst
),
2619 for ( int y
= 0; y
< dstHeight
; y
++ )
2621 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2623 for ( int x
= 0; x
< dstWidth
; x
++ )
2625 // source is point sampled, Alpha StretchBlit is ugly on Win95
2626 // (but does not impact performance)
2627 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2629 // note that source bitmap uses premultiplied alpha (as required by
2630 // the real AlphaBlend)
2631 const unsigned beta
= 255 - pSrc
.Alpha();
2633 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2634 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2635 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2640 pDst
= pDstRowStart
;
2641 pDst
.OffsetY(dataDst
, 1);
2644 // and finally blit them back to the destination DC
2645 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2647 wxLogLastError(_T("BitBlt"));
2651 #endif // #ifdef wxHAVE_RAW_BITMAP
2653 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2654 const wxColour
& initialColour
,
2655 const wxColour
& destColour
,
2656 wxDirection nDirection
)
2658 // use native function if we have compile-time support it and can load it
2659 // during run-time (linking to it statically would make the program
2660 // unusable on earlier Windows versions)
2661 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2663 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2664 static GradientFill_t pfnGradientFill
=
2665 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(_T("GradientFill"));
2667 if ( pfnGradientFill
)
2669 GRADIENT_RECT grect
;
2670 grect
.UpperLeft
= 0;
2671 grect
.LowerRight
= 1;
2673 // invert colours direction if not filling from left-to-right or
2675 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2677 // one vertex for upper left and one for upper-right
2678 TRIVERTEX vertices
[2];
2680 vertices
[0].x
= rect
.GetLeft();
2681 vertices
[0].y
= rect
.GetTop();
2682 vertices
[1].x
= rect
.GetRight()+1;
2683 vertices
[1].y
= rect
.GetBottom()+1;
2685 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2686 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2687 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2688 vertices
[firstVertex
].Alpha
= 0;
2689 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2690 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2691 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2692 vertices
[1 - firstVertex
].Alpha
= 0;
2694 if ( (*pfnGradientFill
)
2701 nDirection
== wxWEST
|| nDirection
== wxEAST
2702 ? GRADIENT_FILL_RECT_H
2703 : GRADIENT_FILL_RECT_V
2706 // skip call of the base class version below
2710 wxLogLastError(_T("GradientFill"));
2712 #endif // wxUSE_DYNLIB_CLASS
2714 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2717 #if wxUSE_DYNLIB_CLASS
2719 static DWORD
wxGetDCLayout(HDC hdc
)
2721 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2723 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2725 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2728 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2730 DWORD layout
= wxGetDCLayout(GetHdc());
2732 if ( layout
== (DWORD
)-1 )
2733 return wxLayout_Default
;
2735 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2738 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2740 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2742 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2743 if ( !s_pfnSetLayout
)
2746 if ( dir
== wxLayout_Default
)
2748 dir
= wxTheApp
->GetLayoutDirection();
2749 if ( dir
== wxLayout_Default
)
2753 DWORD layout
= wxGetDCLayout(GetHdc());
2754 if ( dir
== wxLayout_RightToLeft
)
2755 layout
|= LAYOUT_RTL
;
2757 layout
&= ~LAYOUT_RTL
;
2759 s_pfnSetLayout(GetHdc(), layout
);
2762 #else // !wxUSE_DYNLIB_CLASS
2764 // we can't provide RTL support without dynamic loading, so stub it out
2765 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2767 return wxLayout_Default
;
2770 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2774 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS