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 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 // 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 wxHAVE_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 // wxHAVE_RAW_BITMAP
148 // ----------------------------------------------------------------------------
150 // ----------------------------------------------------------------------------
152 // instead of duplicating the same code which sets and then restores text
153 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
154 // encapsulate this in a small helper class
156 // wxColourChanger: changes the text colours in the ctor if required and
157 // restores them in the dtor
158 class wxColourChanger
161 wxColourChanger(wxMSWDCImpl
& dc
);
167 COLORREF m_colFgOld
, m_colBgOld
;
171 DECLARE_NO_COPY_CLASS(wxColourChanger
)
174 // this class saves the old stretch blit mode during its life time
175 class StretchBltModeChanger
178 StretchBltModeChanger(HDC hdc
,
179 int WXUNUSED_IN_WINCE(mode
))
183 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
185 wxLogLastError(_T("SetStretchBltMode"));
189 ~StretchBltModeChanger()
192 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
193 wxLogLastError(_T("SetStretchBltMode"));
202 DECLARE_NO_COPY_CLASS(StretchBltModeChanger
)
205 #if wxUSE_DYNLIB_CLASS
207 // helper class to cache dynamically loaded libraries and not attempt reloading
209 class wxOnceOnlyDLLLoader
212 // ctor argument must be a literal string as we don't make a copy of it!
213 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
219 // return the symbol with the given name or NULL if the DLL not loaded
220 // or symbol not present
221 void *GetSymbol(const wxChar
*name
)
223 // we're prepared to handle errors here
228 m_dll
.Load(m_dllName
);
230 // reset the name whether we succeeded or failed so that we don't
231 // try again the next time
235 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
240 if ( m_dll
.IsLoaded() )
247 wxDynamicLibrary m_dll
;
248 const wxChar
*m_dllName
;
251 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(_T("msimg32"));
253 // we must ensure that DLLs are unloaded before the static objects cleanup time
254 // because we may hit the notorious DllMain() dead lock in this case if wx is
255 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
256 // under Windows because it tries to reacquire the same lock)
257 class wxGDIDLLsCleanupModule
: public wxModule
260 virtual bool OnInit() { return true; }
261 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
264 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
267 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
269 #endif // wxUSE_DYNLIB_CLASS
271 // ===========================================================================
273 // ===========================================================================
275 // ----------------------------------------------------------------------------
277 // ----------------------------------------------------------------------------
279 wxColourChanger::wxColourChanger(wxMSWDCImpl
& dc
) : m_dc(dc
)
281 const wxBrush
& brush
= dc
.GetBrush();
282 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
284 HDC hdc
= GetHdcOf(dc
);
285 m_colFgOld
= ::GetTextColor(hdc
);
286 m_colBgOld
= ::GetBkColor(hdc
);
288 // note that Windows convention is opposite to wxWidgets one, this is
289 // why text colour becomes the background one and vice versa
290 const wxColour
& colFg
= dc
.GetTextForeground();
293 ::SetBkColor(hdc
, colFg
.GetPixel());
296 const wxColour
& colBg
= dc
.GetTextBackground();
299 ::SetTextColor(hdc
, colBg
.GetPixel());
303 dc
.GetBackgroundMode() == wxBRUSHSTYLE_TRANSPARENT
304 ? TRANSPARENT
: OPAQUE
);
306 // flag which telsl us to undo changes in the dtor
311 // nothing done, nothing to undo
316 wxColourChanger::~wxColourChanger()
320 // restore the colours we changed
321 HDC hdc
= GetHdcOf(m_dc
);
323 ::SetBkMode(hdc
, TRANSPARENT
);
324 ::SetTextColor(hdc
, m_colFgOld
);
325 ::SetBkColor(hdc
, m_colBgOld
);
329 // ---------------------------------------------------------------------------
331 // ---------------------------------------------------------------------------
333 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
340 wxMSWDCImpl::~wxMSWDCImpl()
344 SelectOldObjects(m_hDC
);
346 // if we own the HDC, we delete it, otherwise we just release it
350 ::DeleteDC(GetHdc());
352 else // we don't own our HDC
356 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
360 // Must have been a wxScreenDC
361 ::ReleaseDC((HWND
) NULL
, GetHdc());
367 // This will select current objects out of the DC,
368 // which is what you have to do before deleting the
370 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
376 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
378 if (m_selectedBitmap
.IsOk())
380 m_selectedBitmap
.SetSelectedInto(NULL
);
387 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
392 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
397 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
404 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
407 #endif // wxUSE_PALETTE
410 m_brush
= wxNullBrush
;
413 m_palette
= wxNullPalette
;
414 #endif // wxUSE_PALETTE
416 m_backgroundBrush
= wxNullBrush
;
417 m_selectedBitmap
= wxNullBitmap
;
420 // ---------------------------------------------------------------------------
422 // ---------------------------------------------------------------------------
424 void wxMSWDCImpl::UpdateClipBox()
429 ::GetClipBox(GetHdc(), &rect
);
431 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
432 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
433 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
434 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
438 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
440 // check if we should try to retrieve the clipping region possibly not set
441 // by our SetClippingRegion() but preset by Windows:this can only happen
442 // when we're associated with an existing HDC usign SetHDC(), see there
443 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
445 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
446 self
->UpdateClipBox();
448 if ( !m_clipX1
&& !m_clipX2
)
449 self
->m_clipping
= false;
452 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
455 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
456 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
458 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
462 // note that we combine the new clipping region with the existing one: this
463 // is compatible with what the other ports do and is the documented
464 // behaviour now (starting with 2.3.3)
465 #if defined(__WXWINCE__)
467 if ( !::GetClipBox(GetHdc(), &rectClip
) )
470 // GetClipBox returns logical coordinates, so transform to device
471 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
472 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
473 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
474 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
476 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
477 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
478 rectClip
.right
, rectClip
.bottom
);
480 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
482 ::SelectClipRgn(GetHdc(), hrgnDest
);
485 ::DeleteObject(hrgnClipOld
);
486 ::DeleteObject(hrgnDest
);
488 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
490 wxLogLastError(_T("ExtSelectClipRgn"));
494 #endif // WinCE/!WinCE
501 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
503 // the region coords are always the device ones, so do the translation
506 // FIXME: possible +/-1 error here, to check!
507 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
509 LogicalToDeviceX(x
+ w
),
510 LogicalToDeviceY(y
+ h
));
513 wxLogLastError(_T("CreateRectRgn"));
517 SetClippingHrgn((WXHRGN
)hrgn
);
519 ::DeleteObject(hrgn
);
523 void wxMSWDCImpl::DoSetClippingRegionAsRegion(const wxRegion
& region
)
525 SetClippingHrgn(region
.GetHRGN());
528 void wxMSWDCImpl::DestroyClippingRegion()
532 if (m_clipping
&& m_hDC
)
535 // On a PocketPC device (not necessarily emulator), resetting
536 // the clip region as per the old method causes bad display
537 // problems. In fact setting a null region is probably OK
538 // on desktop WIN32 also, since the WIN32 docs imply that the user
539 // clipping region is independent from the paint clipping region.
540 ::SelectClipRgn(GetHdc(), 0);
542 // TODO: this should restore the previous clipping region,
543 // so that OnPaint processing works correctly, and the update
544 // clipping region doesn't get destroyed after the first
545 // DestroyClippingRegion.
546 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
547 ::SelectClipRgn(GetHdc(), rgn
);
552 wxDCImpl::DestroyClippingRegion();
555 // ---------------------------------------------------------------------------
556 // query capabilities
557 // ---------------------------------------------------------------------------
559 bool wxMSWDCImpl::CanDrawBitmap() const
564 bool wxMSWDCImpl::CanGetTextExtent() const
566 #ifdef __WXMICROWIN__
567 // TODO Extend MicroWindows' GetDeviceCaps function
570 // What sort of display is it?
571 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
573 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
577 int wxMSWDCImpl::GetDepth() const
579 WXMICROWIN_CHECK_HDC_RET(16)
581 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
584 // ---------------------------------------------------------------------------
586 // ---------------------------------------------------------------------------
588 void wxMSWDCImpl::Clear()
595 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
599 // No, I think we should simply ignore this if printing on e.g.
601 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
602 if (!m_selectedBitmap
.IsOk())
605 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
606 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
607 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
611 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
614 DWORD colour
= ::GetBkColor(GetHdc());
615 HBRUSH brush
= ::CreateSolidBrush(colour
);
616 ::FillRect(GetHdc(), &rect
, brush
);
617 ::DeleteObject(brush
);
619 RealizeScaleAndOrigin();
622 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
623 wxCoord
WXUNUSED_IN_WINCE(y
),
624 const wxColour
& WXUNUSED_IN_WINCE(col
),
625 int WXUNUSED_IN_WINCE(style
))
630 WXMICROWIN_CHECK_HDC_RET(false)
632 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
634 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
635 : FLOODFILLBORDER
) ) ;
638 // quoting from the MSDN docs:
640 // Following are some of the reasons this function might fail:
642 // * The filling could not be completed.
643 // * The specified point has the boundary color specified by the
644 // crColor parameter (if FLOODFILLBORDER was requested).
645 // * The specified point does not have the color specified by
646 // crColor (if FLOODFILLSURFACE was requested)
647 // * The point is outside the clipping region that is, it is not
648 // visible on the device.
650 wxLogLastError(wxT("ExtFloodFill"));
653 CalcBoundingBox(x
, y
);
659 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
661 WXMICROWIN_CHECK_HDC_RET(false)
663 wxCHECK_MSG( col
, false, _T("NULL colour parameter in wxMSWDCImpl::GetPixel") );
665 // get the color of the pixel
666 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
668 wxRGBToColour(*col
, pixelcolor
);
673 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
677 wxCoord x1
= x
-VIEWPORT_EXTENT
;
678 wxCoord y1
= y
-VIEWPORT_EXTENT
;
679 wxCoord x2
= x
+VIEWPORT_EXTENT
;
680 wxCoord y2
= y
+VIEWPORT_EXTENT
;
682 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
683 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
685 CalcBoundingBox(x1
, y1
);
686 CalcBoundingBox(x2
, y2
);
689 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
693 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
695 CalcBoundingBox(x1
, y1
);
696 CalcBoundingBox(x2
, y2
);
699 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
700 // and ending at (x2, y2)
701 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
702 wxCoord x2
, wxCoord y2
,
703 wxCoord xc
, wxCoord yc
)
706 // Slower emulation since WinCE doesn't support Pie and Arc
707 double r
= sqrt( (x1
-xc
)*(x1
-xc
) + (y1
-yc
)*(y1
-yc
) );
708 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
709 if( y1
>yc
) sa
= -sa
; // below center
710 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
711 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
716 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
720 double radius
= (double)sqrt(dx
*dx
+dy
*dy
);
721 wxCoord r
= (wxCoord
)radius
;
723 // treat the special case of full circle separately
724 if ( x1
== x2
&& y1
== y2
)
726 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
730 wxCoord xx1
= XLOG2DEV(x1
);
731 wxCoord yy1
= YLOG2DEV(y1
);
732 wxCoord xx2
= XLOG2DEV(x2
);
733 wxCoord yy2
= YLOG2DEV(y2
);
734 wxCoord xxc
= XLOG2DEV(xc
);
735 wxCoord yyc
= YLOG2DEV(yc
);
736 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
738 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
739 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
740 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
741 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
743 if ( m_brush
.IsOk() && m_brush
.GetStyle() != wxBRUSHSTYLE_TRANSPARENT
)
745 // Have to add 1 to bottom-right corner of rectangle
746 // to make semi-circles look right (crooked line otherwise).
747 // Unfortunately this is not a reliable method, depends
748 // on the size of shape.
749 // TODO: figure out why this happens!
750 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
754 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
757 CalcBoundingBox(xc
- r
, yc
- r
);
758 CalcBoundingBox(xc
+ r
, yc
+ r
);
762 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
763 wxCoord width
, wxCoord height
)
765 // cases when we don't have DrawFrameControl()
766 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
767 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
769 wxCoord x2
= x1
+ width
,
779 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
781 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
784 CalcBoundingBox(x1
, y1
);
785 CalcBoundingBox(x2
, y2
);
786 #endif // Microwin/Normal
789 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
793 COLORREF color
= 0x00ffffff;
796 color
= m_pen
.GetColour().GetPixel();
799 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
801 CalcBoundingBox(x
, y
);
804 void wxMSWDCImpl::DoDrawPolygon(int n
,
808 int WXUNUSED_IN_WINCE(fillStyle
))
812 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
814 // Do things less efficiently if we have offsets
815 if (xoffset
!= 0 || yoffset
!= 0)
817 POINT
*cpoints
= new POINT
[n
];
819 for (i
= 0; i
< n
; i
++)
821 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
822 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
824 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
827 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
829 (void)Polygon(GetHdc(), cpoints
, n
);
831 SetPolyFillMode(GetHdc(),prev
);
838 for (i
= 0; i
< n
; i
++)
839 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
842 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
844 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
846 SetPolyFillMode(GetHdc(),prev
);
852 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
860 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
864 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
866 for (i
= cnt
= 0; i
< n
; i
++)
869 // Do things less efficiently if we have offsets
870 if (xoffset
!= 0 || yoffset
!= 0)
872 POINT
*cpoints
= new POINT
[cnt
];
873 for (i
= 0; i
< cnt
; i
++)
875 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
876 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
878 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
881 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
883 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
885 SetPolyFillMode(GetHdc(),prev
);
891 for (i
= 0; i
< cnt
; i
++)
892 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
895 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
897 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
899 SetPolyFillMode(GetHdc(),prev
);
906 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
910 // Do things less efficiently if we have offsets
911 if (xoffset
!= 0 || yoffset
!= 0)
913 POINT
*cpoints
= new POINT
[n
];
915 for (i
= 0; i
< n
; i
++)
917 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
918 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
920 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
922 (void)Polyline(GetHdc(), cpoints
, n
);
928 for (i
= 0; i
< n
; i
++)
929 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
931 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
935 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
939 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
941 wxCoord x2
= x
+ width
;
942 wxCoord y2
= y
+ height
;
944 wxCoord x2dev
= XLOG2DEV(x2
),
945 y2dev
= YLOG2DEV(y2
);
947 // Windows (but not Windows CE) draws the filled rectangles without outline
948 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
949 // and we want them to have the same size regardless of which pen is used
951 if ( m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
956 #endif // !__WXWINCE__
958 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
960 CalcBoundingBox(x
, y
);
961 CalcBoundingBox(x2
, y2
);
964 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
968 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
970 // Now, a negative radius value is interpreted to mean
971 // 'the proportion of the smallest X or Y dimension'
975 double smallest
= (width
< height
) ? width
: height
;
976 radius
= (- radius
* smallest
);
979 wxCoord x2
= (x
+width
);
980 wxCoord y2
= (y
+height
);
982 // Windows draws the filled rectangles without outline (i.e. drawn with a
983 // transparent pen) one pixel smaller in both directions and we want them
984 // to have the same size regardless of which pen is used - adjust
985 if ( m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
991 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
992 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
994 CalcBoundingBox(x
, y
);
995 CalcBoundingBox(x2
, y2
);
998 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1000 WXMICROWIN_CHECK_HDC
1002 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1004 wxCoord x2
= (x
+width
);
1005 wxCoord y2
= (y
+height
);
1007 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1009 CalcBoundingBox(x
, y
);
1010 CalcBoundingBox(x2
, y2
);
1014 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1017 // WinCE does not support ::PolyBezier so use generic version
1018 wxDCImpl::DoDrawSpline(points
);
1020 // quadratic b-spline to cubic bezier spline conversion
1022 // quadratic spline with control points P0,P1,P2
1023 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1025 // bezier spline with control points B0,B1,B2,B3
1026 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1028 // control points of bezier spline calculated from b-spline
1030 // B1 = (2*P1 + P0)/3
1031 // B2 = (2*P1 + P2)/3
1034 WXMICROWIN_CHECK_HDC
1036 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1038 const size_t n_points
= points
->GetCount();
1039 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1041 const size_t n_bezier_points
= n_points
* 3 + 1;
1042 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1043 size_t bezier_pos
= 0;
1044 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1046 wxPointList::compatibility_iterator node
= points
->GetFirst();
1047 wxPoint
*p
= node
->GetData();
1048 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1049 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1051 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1054 node
= node
->GetNext();
1055 p
= node
->GetData();
1059 cx1
= ( x1
+ x2
) / 2;
1060 cy1
= ( y1
+ y2
) / 2;
1061 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1062 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1064 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1068 while ((node
= node
->GetNext()) != NULL
)
1070 while ((node
= node
->GetNext()))
1071 #endif // !wxUSE_STL
1073 p
= (wxPoint
*)node
->GetData();
1078 cx4
= (x1
+ x2
) / 2;
1079 cy4
= (y1
+ y2
) / 2;
1080 // B0 is B3 of previous segment
1082 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1083 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1086 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1087 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1090 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1091 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1097 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1099 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1100 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1102 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1105 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1112 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1113 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1116 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1119 WXMICROWIN_CHECK_HDC
1121 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1126 int rx1
= XLOG2DEV(x
+w
/2);
1127 int ry1
= YLOG2DEV(y
+h
/2);
1134 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1135 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1136 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1137 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1139 // Swap start and end positions if the end angle is less than the start angle.
1150 // draw pie with NULL_PEN first and then outline otherwise a line is
1151 // drawn from the start and end points to the centre
1152 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1155 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1156 rx1
, ry1
, rx2
, ry2
);
1160 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1161 rx1
, ry1
-1, rx2
, ry2
-1);
1164 ::SelectObject(GetHdc(), hpenOld
);
1166 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1167 rx1
, ry1
, rx2
, ry2
);
1169 CalcBoundingBox(x
, y
);
1170 CalcBoundingBox(x2
, y2
);
1174 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1176 WXMICROWIN_CHECK_HDC
1178 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1181 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1183 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1186 CalcBoundingBox(x
, y
);
1187 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1190 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1192 WXMICROWIN_CHECK_HDC
1194 wxCHECK_RET( bmp
.IsOk(), _T("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1196 int width
= bmp
.GetWidth(),
1197 height
= bmp
.GetHeight();
1199 HBITMAP hbmpMask
= 0;
1202 HPALETTE oldPal
= 0;
1203 #endif // wxUSE_PALETTE
1205 if ( bmp
.HasAlpha() )
1208 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1210 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1216 wxMask
*mask
= bmp
.GetMask();
1218 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1222 // don't give assert here because this would break existing
1223 // programs - just silently ignore useMask parameter
1230 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1232 // On some systems, MaskBlt succeeds yet is much much slower
1233 // than the wxWidgets fall-back implementation. So we need
1234 // to be able to switch this on and off at runtime.
1236 #if wxUSE_SYSTEM_OPTIONS
1237 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1241 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1242 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1244 wxPalette
*pal
= bmp
.GetPalette();
1245 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1247 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1248 ::RealizePalette(hdcMem
);
1250 #endif // wxUSE_PALETTE
1252 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1255 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1259 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1260 #endif // wxUSE_PALETTE
1262 ::SelectObject(hdcMem
, hOldBitmap
);
1269 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1273 memDC
.SelectObjectAsSource(bmp
);
1275 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1277 memDC
.SelectObject(wxNullBitmap
);
1280 else // no mask, just use BitBlt()
1283 HDC memdc
= ::CreateCompatibleDC( cdc
);
1284 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1286 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1288 COLORREF old_textground
= ::GetTextColor(GetHdc());
1289 COLORREF old_background
= ::GetBkColor(GetHdc());
1290 if (m_textForegroundColour
.IsOk())
1292 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1294 if (m_textBackgroundColour
.IsOk())
1296 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1300 wxPalette
*pal
= bmp
.GetPalette();
1301 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1303 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1304 ::RealizePalette(memdc
);
1306 #endif // wxUSE_PALETTE
1308 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1309 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1313 ::SelectPalette(memdc
, oldPal
, FALSE
);
1314 #endif // wxUSE_PALETTE
1316 ::SelectObject( memdc
, hOldBitmap
);
1317 ::DeleteDC( memdc
);
1319 ::SetTextColor(GetHdc(), old_textground
);
1320 ::SetBkColor(GetHdc(), old_background
);
1324 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1326 WXMICROWIN_CHECK_HDC
1328 DrawAnyText(text
, x
, y
);
1330 // update the bounding box
1331 CalcBoundingBox(x
, y
);
1334 GetOwner()->GetTextExtent(text
, &w
, &h
);
1335 CalcBoundingBox(x
+ w
, y
+ h
);
1338 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1340 WXMICROWIN_CHECK_HDC
1342 // prepare for drawing the text
1343 if ( m_textForegroundColour
.IsOk() )
1344 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
1346 DWORD old_background
= 0;
1347 if ( m_textBackgroundColour
.IsOk() )
1349 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1352 SetBkMode(GetHdc(), m_backgroundMode
== wxBRUSHSTYLE_TRANSPARENT
? TRANSPARENT
1356 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1357 text
.c_str(), text
.length(), NULL
) == 0 )
1359 wxLogLastError(wxT("TextOut"));
1362 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
1363 text
.c_str(), text
.length()) == 0 )
1365 wxLogLastError(wxT("TextOut"));
1369 // restore the old parameters (text foreground colour may be left because
1370 // it never is set to anything else, but background should remain
1371 // transparent even if we just drew an opaque string)
1372 if ( m_textBackgroundColour
.IsOk() )
1373 (void)SetBkColor(GetHdc(), old_background
);
1375 SetBkMode(GetHdc(), TRANSPARENT
);
1378 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1379 wxCoord x
, wxCoord y
,
1382 WXMICROWIN_CHECK_HDC
1384 // we test that we have some font because otherwise we should still use the
1385 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1386 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1387 // font for drawing rotated fonts unfortunately)
1388 if ( (angle
== 0.0) && m_font
.IsOk() )
1390 DoDrawText(text
, x
, y
);
1392 #ifndef __WXMICROWIN__
1395 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1396 // because it's not TrueType and so can't have non zero
1397 // orientation/escapement under Win9x
1398 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1399 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1401 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1403 wxLogLastError(wxT("GetObject(hfont)"));
1406 // GDI wants the angle in tenth of degree
1407 long angle10
= (long)(angle
* 10);
1408 lf
.lfEscapement
= angle10
;
1409 lf
. lfOrientation
= angle10
;
1411 hfont
= ::CreateFontIndirect(&lf
);
1414 wxLogLastError(wxT("CreateFont"));
1418 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1420 DrawAnyText(text
, x
, y
);
1422 (void)::SelectObject(GetHdc(), hfontOld
);
1423 (void)::DeleteObject(hfont
);
1426 // call the bounding box by adding all four vertices of the rectangle
1427 // containing the text to it (simpler and probably not slower than
1428 // determining which of them is really topmost/leftmost/...)
1430 GetOwner()->GetTextExtent(text
, &w
, &h
);
1432 double rad
= DegToRad(angle
);
1434 // "upper left" and "upper right"
1435 CalcBoundingBox(x
, y
);
1436 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1438 // "bottom left" and "bottom right"
1439 x
+= (wxCoord
)(h
*sin(rad
));
1440 y
+= (wxCoord
)(h
*cos(rad
));
1441 CalcBoundingBox(x
, y
);
1442 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1447 // ---------------------------------------------------------------------------
1449 // ---------------------------------------------------------------------------
1453 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1455 WXMICROWIN_CHECK_HDC
1457 // Set the old object temporarily, in case the assignment deletes an object
1458 // that's not yet selected out.
1461 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1465 if ( m_palette
.IsOk() )
1467 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1468 GetHpaletteOf(m_palette
),
1471 m_oldPalette
= (WXHPALETTE
) oldPal
;
1474 ::RealizePalette(GetHdc());
1478 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1480 if ( palette
.IsOk() )
1482 m_palette
= palette
;
1483 DoSelectPalette(true);
1487 void wxMSWDCImpl::InitializePalette()
1489 if ( wxDisplayDepth() <= 8 )
1491 // look for any window or parent that has a custom palette. If any has
1492 // one then we need to use it in drawing operations
1493 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1495 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1496 if ( m_hasCustomPalette
)
1498 m_palette
= win
->GetPalette();
1500 // turn on MSW translation for this palette
1506 #endif // wxUSE_PALETTE
1508 // SetFont/Pen/Brush() really ask to be implemented as a single template
1509 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1511 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1513 WXMICROWIN_CHECK_HDC
1515 if ( font
== m_font
)
1520 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1521 if ( hfont
== HGDI_ERROR
)
1523 wxLogLastError(_T("SelectObject(font)"));
1528 m_oldFont
= (WXHFONT
)hfont
;
1533 else // invalid font, reset the current font
1537 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1539 wxLogLastError(_T("SelectObject(old font)"));
1545 m_font
= wxNullFont
;
1549 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1551 WXMICROWIN_CHECK_HDC
1558 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1559 if ( hpen
== HGDI_ERROR
)
1561 wxLogLastError(_T("SelectObject(pen)"));
1566 m_oldPen
= (WXHPEN
)hpen
;
1571 else // invalid pen, reset the current pen
1575 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1577 wxLogLastError(_T("SelectObject(old pen)"));
1587 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1589 WXMICROWIN_CHECK_HDC
1591 if ( brush
== m_brush
)
1596 // we must make sure the brush is aligned with the logical coordinates
1597 // before selecting it
1598 wxBitmap
*stipple
= brush
.GetStipple();
1599 if ( stipple
&& stipple
->IsOk() )
1601 if ( !::SetBrushOrgEx
1604 m_deviceOriginX
% stipple
->GetWidth(),
1605 m_deviceOriginY
% stipple
->GetHeight(),
1606 NULL
// [out] previous brush origin
1609 wxLogLastError(_T("SetBrushOrgEx()"));
1613 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1614 if ( hbrush
== HGDI_ERROR
)
1616 wxLogLastError(_T("SelectObject(brush)"));
1621 m_oldBrush
= (WXHBRUSH
)hbrush
;
1626 else // invalid brush, reset the current brush
1630 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1632 wxLogLastError(_T("SelectObject(old brush)"));
1638 m_brush
= wxNullBrush
;
1642 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1644 WXMICROWIN_CHECK_HDC
1646 m_backgroundBrush
= brush
;
1648 if ( m_backgroundBrush
.IsOk() )
1650 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1654 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1656 WXMICROWIN_CHECK_HDC
1658 m_backgroundMode
= mode
;
1660 // SetBackgroundColour now only refers to text background
1661 // and m_backgroundMode is used there
1664 void wxMSWDCImpl::SetLogicalFunction(int function
)
1666 WXMICROWIN_CHECK_HDC
1668 m_logicalFunction
= function
;
1673 void wxMSWDCImpl::SetRop(WXHDC dc
)
1675 if ( !dc
|| m_logicalFunction
< 0 )
1680 switch (m_logicalFunction
)
1682 case wxCLEAR
: rop
= R2_BLACK
; break;
1683 case wxXOR
: rop
= R2_XORPEN
; break;
1684 case wxINVERT
: rop
= R2_NOT
; break;
1685 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1686 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1687 case wxCOPY
: rop
= R2_COPYPEN
; break;
1688 case wxAND
: rop
= R2_MASKPEN
; break;
1689 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1690 case wxNO_OP
: rop
= R2_NOP
; break;
1691 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1692 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1693 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1694 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1695 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1696 case wxOR
: rop
= R2_MERGEPEN
; break;
1697 case wxSET
: rop
= R2_WHITE
; break;
1700 wxFAIL_MSG( wxT("unsupported logical function") );
1704 SetROP2(GetHdc(), rop
);
1707 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1709 // We might be previewing, so return true to let it continue.
1713 void wxMSWDCImpl::EndDoc()
1717 void wxMSWDCImpl::StartPage()
1721 void wxMSWDCImpl::EndPage()
1725 // ---------------------------------------------------------------------------
1727 // ---------------------------------------------------------------------------
1729 wxCoord
wxMSWDCImpl::GetCharHeight() const
1731 WXMICROWIN_CHECK_HDC_RET(0)
1733 TEXTMETRIC lpTextMetric
;
1735 GetTextMetrics(GetHdc(), &lpTextMetric
);
1737 return lpTextMetric
.tmHeight
;
1740 wxCoord
wxMSWDCImpl::GetCharWidth() const
1742 WXMICROWIN_CHECK_HDC_RET(0)
1744 TEXTMETRIC lpTextMetric
;
1746 GetTextMetrics(GetHdc(), &lpTextMetric
);
1748 return lpTextMetric
.tmAveCharWidth
;
1751 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1752 wxCoord
*descent
, wxCoord
*externalLeading
,
1753 const wxFont
*font
) const
1755 #ifdef __WXMICROWIN__
1760 if (descent
) *descent
= 0;
1761 if (externalLeading
) *externalLeading
= 0;
1764 #endif // __WXMICROWIN__
1769 wxASSERT_MSG( font
->IsOk(), _T("invalid font in wxMSWDCImpl::GetTextExtent") );
1771 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1773 else // don't change the font
1779 const size_t len
= string
.length();
1780 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1782 wxLogLastError(_T("GetTextExtentPoint32()"));
1785 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1786 // the result computed by GetTextExtentPoint32() may be too small as it
1787 // accounts for under/overhang of the first/last character while we want
1788 // just the bounding rect for this string so adjust the width as needed
1789 // (using API not available in 2002 SDKs of WinCE)
1793 const wxChar chFirst
= *string
.begin();
1794 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1796 if ( width
.abcA
< 0 )
1797 sizeRect
.cx
-= width
.abcA
;
1801 const wxChar chLast
= *string
.rbegin();
1802 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1804 //else: we already have the width of the last character
1806 if ( width
.abcC
< 0 )
1807 sizeRect
.cx
-= width
.abcC
;
1809 //else: GetCharABCWidths() failed, not a TrueType font?
1811 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1814 ::GetTextMetrics(GetHdc(), &tm
);
1821 *descent
= tm
.tmDescent
;
1822 if (externalLeading
)
1823 *externalLeading
= tm
.tmExternalLeading
;
1827 ::SelectObject(GetHdc(), hfontOld
);
1832 // Each element of the array will be the width of the string up to and
1833 // including the coresoponding character in text.
1835 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1837 static int maxLenText
= -1;
1838 static int maxWidth
= -1;
1841 int stlen
= text
.length();
1843 if (maxLenText
== -1)
1845 // Win9x and WinNT+ have different limits
1846 int version
= wxGetOsVersion();
1847 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1848 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1852 widths
.Add(0, stlen
); // fill the array with zeros
1856 if (!::GetTextExtentExPoint(GetHdc(),
1857 text
.c_str(), // string to check
1858 wxMin(stlen
, maxLenText
),
1860 &fit
, // [out] count of chars
1862 &widths
[0], // array to fill
1866 wxLogLastError(wxT("GetTextExtentExPoint"));
1873 void wxMSWDCImpl::RealizeScaleAndOrigin()
1875 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1876 // cases we could do with MM_TEXT and in the remaining 0.9% with
1877 // MM_ISOTROPIC (TODO!)
1879 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1881 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1882 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1884 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1885 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1887 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1888 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1892 void wxMSWDCImpl::SetMapMode(int mode
)
1894 WXMICROWIN_CHECK_HDC
1896 m_mappingMode
= mode
;
1898 if ( mode
== wxMM_TEXT
)
1901 m_logicalScaleY
= 1.0;
1903 else // need to do some calculations
1905 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1906 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1907 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1908 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1910 if ( (mm_width
== 0) || (mm_height
== 0) )
1912 // we can't calculate mm2pixels[XY] then!
1916 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1917 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1922 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1923 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1927 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1928 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1932 m_logicalScaleX
= mm2pixelsX
;
1933 m_logicalScaleY
= mm2pixelsY
;
1937 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1938 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1942 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1946 ComputeScaleAndOrigin();
1948 RealizeScaleAndOrigin();
1951 void wxMSWDCImpl::SetUserScale(double x
, double y
)
1953 WXMICROWIN_CHECK_HDC
1955 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1958 wxDCImpl::SetUserScale(x
,y
);
1960 RealizeScaleAndOrigin();
1963 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
1966 WXMICROWIN_CHECK_HDC
1968 int signX
= xLeftRight
? 1 : -1,
1969 signY
= yBottomUp
? -1 : 1;
1971 if (signX
== m_signX
&& signY
== m_signY
)
1974 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
1976 RealizeScaleAndOrigin();
1979 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1981 WXMICROWIN_CHECK_HDC
1983 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1986 wxDCImpl::SetLogicalOrigin( x
, y
);
1989 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1993 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1995 WXMICROWIN_CHECK_HDC
1997 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2000 wxDCImpl::SetDeviceOrigin( x
, y
);
2002 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2005 // ---------------------------------------------------------------------------
2007 // ---------------------------------------------------------------------------
2009 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
2010 wxCoord dstWidth
, wxCoord dstHeight
,
2012 wxCoord srcX
, wxCoord srcY
,
2013 int rop
, bool useMask
,
2014 wxCoord srcMaskX
, wxCoord srcMaskY
)
2016 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2019 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2020 wxCoord dstWidth
, wxCoord dstHeight
,
2022 wxCoord xsrc
, wxCoord ysrc
,
2023 wxCoord srcWidth
, wxCoord srcHeight
,
2024 int rop
, bool useMask
,
2025 wxCoord xsrcMask
, wxCoord ysrcMask
)
2027 wxCHECK_MSG( source
, false, _T("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2029 WXMICROWIN_CHECK_HDC_RET(false)
2031 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2034 // TODO: Do we want to be able to blit from other DCs too?
2038 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2040 // if either the source or destination has alpha channel, we must use
2041 // AlphaBlt() as other function don't handle it correctly
2042 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2043 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2044 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2046 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2047 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2051 wxMask
*mask
= NULL
;
2054 mask
= bmpSrc
.GetMask();
2056 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2058 // don't give assert here because this would break existing
2059 // programs - just silently ignore useMask parameter
2064 if (xsrcMask
== -1 && ysrcMask
== -1)
2066 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2069 COLORREF old_textground
= ::GetTextColor(GetHdc());
2070 COLORREF old_background
= ::GetBkColor(GetHdc());
2071 if (m_textForegroundColour
.IsOk())
2073 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
2075 if (m_textBackgroundColour
.IsOk())
2077 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
2083 case wxXOR
: dwRop
= SRCINVERT
; break;
2084 case wxINVERT
: dwRop
= DSTINVERT
; break;
2085 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2086 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2087 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2088 case wxSET
: dwRop
= WHITENESS
; break;
2089 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2090 case wxAND
: dwRop
= SRCAND
; break;
2091 case wxOR
: dwRop
= SRCPAINT
; break;
2092 case wxEQUIV
: dwRop
= 0x00990066; break;
2093 case wxNAND
: dwRop
= 0x007700E6; break;
2094 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2095 case wxCOPY
: dwRop
= SRCCOPY
; break;
2096 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2097 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2098 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2100 wxFAIL_MSG( wxT("unsupported logical function") );
2104 bool success
= false;
2109 // we want the part of the image corresponding to the mask to be
2110 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2111 // meaning of fg and bg is inverted which corresponds to wxWin notion
2112 // of the mask which is also contrary to the Windows one)
2114 // On some systems, MaskBlt succeeds yet is much much slower
2115 // than the wxWidgets fall-back implementation. So we need
2116 // to be able to switch this on and off at runtime.
2117 #if wxUSE_SYSTEM_OPTIONS
2118 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2121 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2126 xdest
, ydest
, dstWidth
, dstHeight
,
2129 (HBITMAP
)mask
->GetMaskBitmap(),
2131 MAKEROP4(dwRop
, DSTCOPY
)
2139 // Blit bitmap with mask
2142 HBITMAP buffer_bmap
;
2144 #if wxUSE_DC_CACHEING
2145 // create a temp buffer bitmap and DCs to access it and the mask
2146 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2147 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2149 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2150 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2152 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2153 dstWidth
, dstHeight
);
2155 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2156 #else // !wxUSE_DC_CACHEING
2157 // create a temp buffer bitmap and DCs to access it and the mask
2158 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2159 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2160 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2161 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2162 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2163 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2165 // copy dest to buffer
2166 if ( !::BitBlt(dc_buffer
, 0, 0, (int)dstWidth
, (int)dstHeight
,
2167 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2169 wxLogLastError(wxT("BitBlt"));
2173 StretchBltModeChanger
changeMode(dc_buffer
, COLORONCOLOR
);
2176 // copy src to buffer using selected raster op
2177 if ( !::StretchBlt(dc_buffer
, 0, 0, (int)dstWidth
, (int)dstHeight
,
2178 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2180 wxLogLastError(wxT("StretchBlt"));
2183 // set masked area in buffer to BLACK (pixel value 0)
2184 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
2185 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
2186 if ( !::StretchBlt(dc_buffer
, 0, 0, (int)dstWidth
, (int)dstHeight
,
2187 dc_mask
, xsrcMask
, ysrcMask
, srcWidth
, srcHeight
, SRCAND
) )
2189 wxLogLastError(wxT("StretchBlt"));
2192 // set unmasked area in dest to BLACK
2193 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2194 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2195 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, (int)dstWidth
, (int)dstHeight
,
2196 dc_mask
, xsrcMask
, ysrcMask
, srcWidth
, srcHeight
, SRCAND
) )
2198 wxLogLastError(wxT("StretchBlt"));
2200 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
2201 ::SetTextColor(GetHdc(), prevCol
);
2203 // OR buffer to dest
2204 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
2205 (int)dstWidth
, (int)dstHeight
,
2206 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2209 wxLogLastError(wxT("BitBlt"));
2212 // tidy up temporary DCs and bitmap
2213 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2214 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2216 #if !wxUSE_DC_CACHEING
2218 ::DeleteDC(dc_mask
);
2219 ::DeleteDC(dc_buffer
);
2220 ::DeleteObject(buffer_bmap
);
2225 else // no mask, just BitBlt() it
2227 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2228 // use StretchBlt() if available and finally fall back to BitBlt()
2230 // FIXME: use appropriate WinCE functions
2232 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2233 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2238 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2240 &ds
) == sizeof(ds
) )
2242 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2244 // Figure out what co-ordinate system we're supposed to specify
2246 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2250 ysrc
= hDIB
- (ysrc
+ dstHeight
);
2253 if ( ::StretchDIBits(GetHdc(),
2255 dstWidth
, dstHeight
,
2257 srcWidth
, srcHeight
,
2259 (LPBITMAPINFO
)&ds
.dsBmih
,
2262 ) == (int)GDI_ERROR
)
2264 // On Win9x this API fails most (all?) of the time, so
2265 // logging it becomes quite distracting. Since it falls
2266 // back to the code below this is not really serious, so
2268 //wxLogLastError(wxT("StretchDIBits"));
2277 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2282 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2288 xdest
, ydest
, dstWidth
, dstHeight
,
2290 xsrc
, ysrc
, srcWidth
, srcHeight
,
2294 wxLogLastError(_T("StretchBlt"));
2308 (int)dstWidth
, (int)dstHeight
,
2314 wxLogLastError(_T("BitBlt"));
2323 ::SetTextColor(GetHdc(), old_textground
);
2324 ::SetBkColor(GetHdc(), old_background
);
2329 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2331 WXMICROWIN_CHECK_HDC
2334 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2336 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2339 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2341 WXMICROWIN_CHECK_HDC
2343 // if we implement it in terms of DoGetSize() instead of directly using the
2344 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2345 // will also work for wxWindowDC and wxClientDC even though their size is
2346 // not the same as the total size of the screen
2347 int wPixels
, hPixels
;
2348 DoGetSize(&wPixels
, &hPixels
);
2352 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2354 wxCHECK_RET( wTotal
, _T("0 width device?") );
2356 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2361 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2363 wxCHECK_RET( hTotal
, _T("0 height device?") );
2365 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2369 wxSize
wxMSWDCImpl::GetPPI() const
2371 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2373 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2374 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2376 return wxSize(x
, y
);
2379 // For use by wxWidgets only, unless custom units are required.
2380 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
2382 WXMICROWIN_CHECK_HDC
2384 wxDCImpl::SetLogicalScale(x
,y
);
2387 // ----------------------------------------------------------------------------
2389 // ----------------------------------------------------------------------------
2391 #if wxUSE_DC_CACHEING
2394 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2395 * improve it in due course, either using arrays, or simply storing pointers to one
2396 * entry for the bitmap, and two for the DCs. -- JACS
2399 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2400 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2402 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2411 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2420 wxDCCacheEntry::~wxDCCacheEntry()
2423 ::DeleteObject((HBITMAP
) m_bitmap
);
2425 ::DeleteDC((HDC
) m_dc
);
2428 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2430 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2431 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2434 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2436 if (entry
->m_depth
== depth
)
2438 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2440 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2441 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2442 if ( !entry
->m_bitmap
)
2444 wxLogLastError(wxT("CreateCompatibleBitmap"));
2446 entry
->m_width
= w
; entry
->m_height
= h
;
2452 node
= node
->GetNext();
2454 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2457 wxLogLastError(wxT("CreateCompatibleBitmap"));
2459 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2460 AddToBitmapCache(entry
);
2464 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2466 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2467 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2470 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2472 // Don't return the same one as we already have
2473 if (!notThis
|| (notThis
!= entry
))
2475 if (entry
->m_depth
== depth
)
2481 node
= node
->GetNext();
2483 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2486 wxLogLastError(wxT("CreateCompatibleDC"));
2488 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2489 AddToDCCache(entry
);
2493 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2495 sm_bitmapCache
.Append(entry
);
2498 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2500 sm_dcCache
.Append(entry
);
2503 void wxMSWDCImpl::ClearCache()
2505 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2506 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2509 // Clean up cache at app exit
2510 class wxDCModule
: public wxModule
2513 virtual bool OnInit() { return true; }
2514 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2517 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2520 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2522 #endif // wxUSE_DC_CACHEING
2524 // ----------------------------------------------------------------------------
2525 // alpha channel support
2526 // ----------------------------------------------------------------------------
2528 static bool AlphaBlt(HDC hdcDst
,
2529 int x
, int y
, int dstWidth
, int dstHeight
,
2531 int srcWidth
, int srcHeight
,
2533 const wxBitmap
& bmp
)
2535 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2536 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2538 // do we have AlphaBlend() and company in the headers?
2539 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2540 // yes, now try to see if we have it during run-time
2541 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2542 HDC
,int,int,int,int,
2546 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(_T("AlphaBlend"));
2547 if ( pfnAlphaBlend
)
2550 bf
.BlendOp
= AC_SRC_OVER
;
2552 bf
.SourceConstantAlpha
= 0xff;
2553 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2555 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2556 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2559 // skip wxAlphaBlend() call below
2563 wxLogLastError(_T("AlphaBlend"));
2566 wxUnusedVar(hdcSrc
);
2567 #endif // defined(AC_SRC_OVER)
2569 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2571 #ifdef wxHAVE_RAW_BITMAP
2572 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2575 #else // !wxHAVE_RAW_BITMAP
2576 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2577 // alpha but at least something will be shown like this)
2580 #endif // wxHAVE_RAW_BITMAP
2584 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2585 #ifdef wxHAVE_RAW_BITMAP
2588 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2589 int dstWidth
, int dstHeight
,
2591 int srcWidth
, int srcHeight
,
2592 const wxBitmap
& bmpSrc
)
2594 // get the destination DC pixels
2595 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2597 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2599 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2601 wxLogLastError(_T("BitBlt"));
2604 // combine them with the source bitmap using alpha
2605 wxAlphaPixelData
dataDst(bmpDst
),
2606 dataSrc((wxBitmap
&)bmpSrc
);
2608 wxCHECK_RET( dataDst
&& dataSrc
,
2609 _T("failed to get raw data in wxAlphaBlend") );
2611 wxAlphaPixelData::Iterator
pDst(dataDst
),
2615 for ( int y
= 0; y
< dstHeight
; y
++ )
2617 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2619 for ( int x
= 0; x
< dstWidth
; x
++ )
2621 // source is point sampled, Alpha StretchBlit is ugly on Win95
2622 // (but does not impact performance)
2623 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2625 // note that source bitmap uses premultiplied alpha (as required by
2626 // the real AlphaBlend)
2627 const unsigned beta
= 255 - pSrc
.Alpha();
2629 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2630 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2631 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2636 pDst
= pDstRowStart
;
2637 pDst
.OffsetY(dataDst
, 1);
2640 // and finally blit them back to the destination DC
2641 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2643 wxLogLastError(_T("BitBlt"));
2647 #endif // #ifdef wxHAVE_RAW_BITMAP
2649 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2650 const wxColour
& initialColour
,
2651 const wxColour
& destColour
,
2652 wxDirection nDirection
)
2654 // use native function if we have compile-time support it and can load it
2655 // during run-time (linking to it statically would make the program
2656 // unusable on earlier Windows versions)
2657 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2659 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2660 static GradientFill_t pfnGradientFill
=
2661 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(_T("GradientFill"));
2663 if ( pfnGradientFill
)
2665 GRADIENT_RECT grect
;
2666 grect
.UpperLeft
= 0;
2667 grect
.LowerRight
= 1;
2669 // invert colours direction if not filling from left-to-right or
2671 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2673 // one vertex for upper left and one for upper-right
2674 TRIVERTEX vertices
[2];
2676 vertices
[0].x
= rect
.GetLeft();
2677 vertices
[0].y
= rect
.GetTop();
2678 vertices
[1].x
= rect
.GetRight()+1;
2679 vertices
[1].y
= rect
.GetBottom()+1;
2681 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2682 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2683 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2684 vertices
[firstVertex
].Alpha
= 0;
2685 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2686 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2687 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2688 vertices
[1 - firstVertex
].Alpha
= 0;
2690 if ( (*pfnGradientFill
)
2697 nDirection
== wxWEST
|| nDirection
== wxEAST
2698 ? GRADIENT_FILL_RECT_H
2699 : GRADIENT_FILL_RECT_V
2702 // skip call of the base class version below
2706 wxLogLastError(_T("GradientFill"));
2708 #endif // wxUSE_DYNLIB_CLASS
2710 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2713 #if wxUSE_DYNLIB_CLASS
2715 static DWORD
wxGetDCLayout(HDC hdc
)
2717 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2719 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2721 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2724 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2726 DWORD layout
= wxGetDCLayout(GetHdc());
2728 if ( layout
== (DWORD
)-1 )
2729 return wxLayout_Default
;
2731 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2734 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2736 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2738 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2739 if ( !s_pfnSetLayout
)
2742 if ( dir
== wxLayout_Default
)
2744 dir
= wxTheApp
->GetLayoutDirection();
2745 if ( dir
== wxLayout_Default
)
2749 DWORD layout
= wxGetDCLayout(GetHdc());
2750 if ( dir
== wxLayout_RightToLeft
)
2751 layout
|= LAYOUT_RTL
;
2753 layout
&= ~LAYOUT_RTL
;
2755 s_pfnSetLayout(GetHdc(), layout
);
2758 #else // !wxUSE_DYNLIB_CLASS
2760 // we can't provide RTL support without dynamic loading, so stub it out
2761 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2763 return wxLayout_Default
;
2766 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2770 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS