1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dc.cpp
3 // Purpose: wxDC class for MSW port
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
28 #include "wx/msw/wrapcdlg.h"
30 #include "wx/window.h"
32 #include "wx/dialog.h"
34 #include "wx/bitmap.h"
35 #include "wx/dcmemory.h"
38 #include "wx/dcprint.h"
39 #include "wx/module.h"
42 #include "wx/msw/dc.h"
43 #include "wx/sysopt.h"
44 #include "wx/dynlib.h"
46 #ifdef wxHAS_RAW_BITMAP
47 #include "wx/rawbmp.h"
52 #include "wx/msw/private/dc.h"
54 using namespace wxMSWImpl
;
57 #define AC_SRC_ALPHA 1
64 /* Quaternary raster codes */
66 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
69 // apparently with MicroWindows it is possible that HDC is 0 so we have to
70 // check for this ourselves
72 #define WXMICROWIN_CHECK_HDC if ( !GetHDC() ) return;
73 #define WXMICROWIN_CHECK_HDC_RET(x) if ( !GetHDC() ) return x;
75 #define WXMICROWIN_CHECK_HDC
76 #define WXMICROWIN_CHECK_HDC_RET(x)
79 IMPLEMENT_ABSTRACT_CLASS(wxMSWDCImpl
, wxDCImpl
)
81 // ---------------------------------------------------------------------------
83 // ---------------------------------------------------------------------------
85 static const int VIEWPORT_EXTENT
= 1000;
87 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
88 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
89 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
91 // ----------------------------------------------------------------------------
92 // macros for logical <-> device coords conversion
93 // ----------------------------------------------------------------------------
96 We currently let Windows do all the translations itself so these macros are
97 not really needed (any more) but keep them to enhance readability of the
98 code by allowing to see where are the logical and where are the device
103 #define XLOG2DEV(x) ((x-m_logicalOriginX)*m_signX)
104 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
105 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
106 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
108 #define XLOG2DEV(x) (x)
109 #define YLOG2DEV(y) (y)
110 #define XDEV2LOG(x) (x)
111 #define YDEV2LOG(y) (y)
114 // ---------------------------------------------------------------------------
116 // ---------------------------------------------------------------------------
118 // convert degrees to radians
119 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
121 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
123 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
124 // to pass it to this function but as we already have it at the point
125 // of call anyhow we do
127 // return true if we could draw the bitmap in one way or the other, false
129 static bool AlphaBlt(HDC hdcDst
,
130 int x
, int y
, int dstWidth
, int dstHeight
,
132 int srcWidth
, int srcHeight
,
134 const wxBitmap
& bmp
);
136 #ifdef wxHAS_RAW_BITMAP
138 // our (limited) AlphaBlend() replacement for Windows versions not providing it
140 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
141 int dstWidth
, int dstHeight
,
143 int srcWidth
, int srcHeight
,
144 const wxBitmap
& bmpSrc
);
146 #endif // wxHAS_RAW_BITMAP
148 // ----------------------------------------------------------------------------
150 // ----------------------------------------------------------------------------
152 // 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 // wxBrushAttrsSetter: changes the text colours in the ctor if required and
157 // restores them in the dtor
158 class wxBrushAttrsSetter
: private wxBkModeChanger
,
159 private wxTextColoursChanger
162 wxBrushAttrsSetter(wxMSWDCImpl
& dc
);
165 wxDECLARE_NO_COPY_CLASS(wxBrushAttrsSetter
);
170 #define SET_STRETCH_BLT_MODE(hdc)
172 #else // !__WXWINCE__
174 // this class sets the stretch blit mode to COLORONCOLOR during its lifetime
176 // don't use it directly, use SET_STRETCH_BLT_MODE() macro instead as it
177 // expands to nothing under WinCE which doesn't have SetStretchBltMode()
178 class StretchBltModeChanger
181 StretchBltModeChanger(HDC hdc
)
184 m_modeOld
= ::SetStretchBltMode(m_hdc
, COLORONCOLOR
);
187 wxLogLastError(wxT("SetStretchBltMode"));
191 ~StretchBltModeChanger()
193 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
195 wxLogLastError(wxT("SetStretchBltMode"));
204 wxDECLARE_NO_COPY_CLASS(StretchBltModeChanger
);
207 #define SET_STRETCH_BLT_MODE(hdc) \
208 StretchBltModeChanger wxMAKE_UNIQUE_NAME(stretchModeChanger)(hdc)
210 #endif // __WXWINCE__/!__WXWINCE__
212 #if wxUSE_DYNLIB_CLASS
214 // helper class to cache dynamically loaded libraries and not attempt reloading
216 class wxOnceOnlyDLLLoader
219 // ctor argument must be a literal string as we don't make a copy of it!
220 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
226 // return the symbol with the given name or NULL if the DLL not loaded
227 // or symbol not present
228 void *GetSymbol(const wxChar
*name
)
230 // we're prepared to handle errors here
235 m_dll
.Load(m_dllName
);
237 // reset the name whether we succeeded or failed so that we don't
238 // try again the next time
242 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
247 if ( m_dll
.IsLoaded() )
254 wxDynamicLibrary m_dll
;
255 const wxChar
*m_dllName
;
258 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(wxT("msimg32"));
260 // we must ensure that DLLs are unloaded before the static objects cleanup time
261 // because we may hit the notorious DllMain() dead lock in this case if wx is
262 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
263 // under Windows because it tries to reacquire the same lock)
264 class wxGDIDLLsCleanupModule
: public wxModule
267 virtual bool OnInit() { return true; }
268 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
271 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
274 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
276 #endif // wxUSE_DYNLIB_CLASS
278 // ===========================================================================
280 // ===========================================================================
282 // ----------------------------------------------------------------------------
283 // wxBrushAttrsSetter
284 // ----------------------------------------------------------------------------
286 wxBrushAttrsSetter::wxBrushAttrsSetter(wxMSWDCImpl
& dc
)
287 : wxBkModeChanger(GetHdcOf(dc
)),
288 wxTextColoursChanger(GetHdcOf(dc
))
290 const wxBrush
& brush
= dc
.GetBrush();
291 if ( brush
.IsOk() && brush
.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE
)
293 // note that Windows convention is opposite to wxWidgets one, this is
294 // why text colour becomes the background one and vice versa
295 wxTextColoursChanger::Change(dc
.GetTextBackground(),
296 dc
.GetTextForeground());
298 wxBkModeChanger::Change(dc
.GetBackgroundMode());
302 // ----------------------------------------------------------------------------
303 // wxDC MSW-specific methods
304 // ----------------------------------------------------------------------------
306 WXHDC
wxDC::GetHDC() const
308 wxMSWDCImpl
* const impl
= wxDynamicCast(GetImpl(), wxMSWDCImpl
);
309 return impl
? impl
->GetHDC() : 0;
312 // ---------------------------------------------------------------------------
314 // ---------------------------------------------------------------------------
316 wxMSWDCImpl::wxMSWDCImpl( wxDC
*owner
, WXHDC hDC
) :
323 wxMSWDCImpl::~wxMSWDCImpl()
327 SelectOldObjects(m_hDC
);
329 // if we own the HDC, we delete it, otherwise we just release it
333 ::DeleteDC(GetHdc());
335 else // we don't own our HDC
339 ::ReleaseDC(GetHwndOf(m_window
), GetHdc());
343 // Must have been a wxScreenDC
344 ::ReleaseDC((HWND
) NULL
, GetHdc());
350 // This will select current objects out of the DC,
351 // which is what you have to do before deleting the
353 void wxMSWDCImpl::SelectOldObjects(WXHDC dc
)
359 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
360 if (m_selectedBitmap
.IsOk())
362 m_selectedBitmap
.SetSelectedInto(NULL
);
368 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
373 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
378 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
385 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
388 #endif // wxUSE_PALETTE
391 m_brush
= wxNullBrush
;
394 m_palette
= wxNullPalette
;
395 #endif // wxUSE_PALETTE
397 m_backgroundBrush
= wxNullBrush
;
398 m_selectedBitmap
= wxNullBitmap
;
401 // ---------------------------------------------------------------------------
403 // ---------------------------------------------------------------------------
405 void wxMSWDCImpl::UpdateClipBox()
410 ::GetClipBox(GetHdc(), &rect
);
412 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
413 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
414 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
415 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
419 wxMSWDCImpl::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
421 // check if we should try to retrieve the clipping region possibly not set
422 // by our SetClippingRegion() but preset by Windows:this can only happen
423 // when we're associated with an existing HDC usign SetHDC(), see there
424 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
426 wxMSWDCImpl
*self
= wxConstCast(this, wxMSWDCImpl
);
427 self
->UpdateClipBox();
429 if ( !m_clipX1
&& !m_clipX2
)
430 self
->m_clipping
= false;
433 wxDCImpl::DoGetClippingBox(x
, y
, w
, h
);
436 // common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
437 void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn
)
439 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
443 // note that we combine the new clipping region with the existing one: this
444 // is compatible with what the other ports do and is the documented
445 // behaviour now (starting with 2.3.3)
446 #if defined(__WXWINCE__)
448 if ( !::GetClipBox(GetHdc(), &rectClip
) )
451 // GetClipBox returns logical coordinates, so transform to device
452 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
453 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
454 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
455 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
457 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
458 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
459 rectClip
.right
, rectClip
.bottom
);
461 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
463 ::SelectClipRgn(GetHdc(), hrgnDest
);
466 ::DeleteObject(hrgnClipOld
);
467 ::DeleteObject(hrgnDest
);
469 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
471 wxLogLastError(wxT("ExtSelectClipRgn"));
475 #endif // WinCE/!WinCE
482 void wxMSWDCImpl::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
484 // the region coords are always the device ones, so do the translation
487 // FIXME: possible +/-1 error here, to check!
488 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
490 LogicalToDeviceX(x
+ w
),
491 LogicalToDeviceY(y
+ h
));
494 wxLogLastError(wxT("CreateRectRgn"));
498 SetClippingHrgn((WXHRGN
)hrgn
);
500 ::DeleteObject(hrgn
);
504 void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion
& region
)
506 SetClippingHrgn(region
.GetHRGN());
509 void wxMSWDCImpl::DestroyClippingRegion()
513 if (m_clipping
&& m_hDC
)
516 // On a PocketPC device (not necessarily emulator), resetting
517 // the clip region as per the old method causes bad display
518 // problems. In fact setting a null region is probably OK
519 // on desktop WIN32 also, since the WIN32 docs imply that the user
520 // clipping region is independent from the paint clipping region.
521 ::SelectClipRgn(GetHdc(), 0);
523 // TODO: this should restore the previous clipping region,
524 // so that OnPaint processing works correctly, and the update
525 // clipping region doesn't get destroyed after the first
526 // DestroyClippingRegion.
527 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
528 ::SelectClipRgn(GetHdc(), rgn
);
533 wxDCImpl::DestroyClippingRegion();
536 // ---------------------------------------------------------------------------
537 // query capabilities
538 // ---------------------------------------------------------------------------
540 bool wxMSWDCImpl::CanDrawBitmap() const
545 bool wxMSWDCImpl::CanGetTextExtent() const
547 #ifdef __WXMICROWIN__
548 // TODO Extend MicroWindows' GetDeviceCaps function
551 // What sort of display is it?
552 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
554 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
558 int wxMSWDCImpl::GetDepth() const
560 WXMICROWIN_CHECK_HDC_RET(16)
562 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
565 // ---------------------------------------------------------------------------
567 // ---------------------------------------------------------------------------
569 void wxMSWDCImpl::Clear()
576 GetClientRect((HWND
) m_window
->GetHWND(), &rect
);
580 // No, I think we should simply ignore this if printing on e.g.
582 // wxCHECK_RET( m_selectedBitmap.IsOk(), wxT("this DC can't be cleared") );
583 if (!m_selectedBitmap
.IsOk())
586 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
587 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
588 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
592 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
595 DWORD colour
= ::GetBkColor(GetHdc());
596 HBRUSH brush
= ::CreateSolidBrush(colour
);
597 ::FillRect(GetHdc(), &rect
, brush
);
598 ::DeleteObject(brush
);
600 RealizeScaleAndOrigin();
603 bool wxMSWDCImpl::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
604 wxCoord
WXUNUSED_IN_WINCE(y
),
605 const wxColour
& WXUNUSED_IN_WINCE(col
),
606 wxFloodFillStyle
WXUNUSED_IN_WINCE(style
))
611 WXMICROWIN_CHECK_HDC_RET(false)
613 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
615 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
616 : FLOODFILLBORDER
) ) ;
619 // quoting from the MSDN docs:
621 // Following are some of the reasons this function might fail:
623 // * The filling could not be completed.
624 // * The specified point has the boundary color specified by the
625 // crColor parameter (if FLOODFILLBORDER was requested).
626 // * The specified point does not have the color specified by
627 // crColor (if FLOODFILLSURFACE was requested)
628 // * The point is outside the clipping region that is, it is not
629 // visible on the device.
631 wxLogLastError(wxT("ExtFloodFill"));
634 CalcBoundingBox(x
, y
);
640 bool wxMSWDCImpl::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
642 WXMICROWIN_CHECK_HDC_RET(false)
644 wxCHECK_MSG( col
, false, wxT("NULL colour parameter in wxMSWDCImpl::GetPixel") );
646 // get the color of the pixel
647 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
649 wxRGBToColour(*col
, pixelcolor
);
654 void wxMSWDCImpl::DoCrossHair(wxCoord x
, wxCoord y
)
658 wxCoord x1
= x
-VIEWPORT_EXTENT
;
659 wxCoord y1
= y
-VIEWPORT_EXTENT
;
660 wxCoord x2
= x
+VIEWPORT_EXTENT
;
661 wxCoord y2
= y
+VIEWPORT_EXTENT
;
663 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
664 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
666 CalcBoundingBox(x1
, y1
);
667 CalcBoundingBox(x2
, y2
);
670 void wxMSWDCImpl::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
674 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
676 CalcBoundingBox(x1
, y1
);
677 CalcBoundingBox(x2
, y2
);
680 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
681 // and ending at (x2, y2)
682 void wxMSWDCImpl::DoDrawArc(wxCoord x1
, wxCoord y1
,
683 wxCoord x2
, wxCoord y2
,
684 wxCoord xc
, wxCoord yc
)
688 wxCoord r
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
692 // Slower emulation since WinCE doesn't support Pie and Arc
693 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
695 sa
= -sa
; // below center
696 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
697 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
702 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
704 // treat the special case of full circle separately
705 if ( x1
== x2
&& y1
== y2
)
707 GetOwner()->DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
711 wxCoord xx1
= XLOG2DEV(x1
);
712 wxCoord yy1
= YLOG2DEV(y1
);
713 wxCoord xx2
= XLOG2DEV(x2
);
714 wxCoord yy2
= YLOG2DEV(y2
);
715 wxCoord xxc
= XLOG2DEV(xc
);
716 wxCoord yyc
= YLOG2DEV(yc
);
719 wxCoord ray
= (wxCoord
)sqrt(dx
*dx
+ dy
*dy
);
721 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
722 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
723 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
724 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
726 if ( m_brush
.IsOk() && m_brush
.GetStyle() != wxBRUSHSTYLE_TRANSPARENT
)
728 // Have to add 1 to bottom-right corner of rectangle
729 // to make semi-circles look right (crooked line otherwise).
730 // Unfortunately this is not a reliable method, depends
731 // on the size of shape.
732 // TODO: figure out why this happens!
733 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
737 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
740 CalcBoundingBox(xc
- r
, yc
- r
);
741 CalcBoundingBox(xc
+ r
, yc
+ r
);
745 void wxMSWDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
746 wxCoord width
, wxCoord height
)
748 // cases when we don't have DrawFrameControl()
749 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
750 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
752 wxCoord x2
= x1
+ width
,
762 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_CHECKED
);
764 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
767 CalcBoundingBox(x1
, y1
);
768 CalcBoundingBox(x2
, y2
);
769 #endif // Microwin/Normal
772 void wxMSWDCImpl::DoDrawPoint(wxCoord x
, wxCoord y
)
776 COLORREF color
= 0x00ffffff;
779 color
= m_pen
.GetColour().GetPixel();
782 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
784 CalcBoundingBox(x
, y
);
787 void wxMSWDCImpl::DoDrawPolygon(int n
,
791 wxPolygonFillMode
WXUNUSED_IN_WINCE(fillStyle
))
795 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
797 // Do things less efficiently if we have offsets
798 if (xoffset
!= 0 || yoffset
!= 0)
800 POINT
*cpoints
= new POINT
[n
];
802 for (i
= 0; i
< n
; i
++)
804 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
805 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
807 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
810 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
812 (void)Polygon(GetHdc(), cpoints
, n
);
814 SetPolyFillMode(GetHdc(),prev
);
821 for (i
= 0; i
< n
; i
++)
822 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
825 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
827 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
829 SetPolyFillMode(GetHdc(),prev
);
835 wxMSWDCImpl::DoDrawPolyPolygon(int n
,
840 wxPolygonFillMode fillStyle
)
843 wxDCImpl::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
847 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
849 for (i
= cnt
= 0; i
< n
; i
++)
852 // Do things less efficiently if we have offsets
853 if (xoffset
!= 0 || yoffset
!= 0)
855 POINT
*cpoints
= new POINT
[cnt
];
856 for (i
= 0; i
< cnt
; i
++)
858 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
859 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
861 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
864 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
866 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
868 SetPolyFillMode(GetHdc(),prev
);
874 for (i
= 0; i
< cnt
; i
++)
875 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
878 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
880 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
882 SetPolyFillMode(GetHdc(),prev
);
889 void wxMSWDCImpl::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
893 // Do things less efficiently if we have offsets
894 if (xoffset
!= 0 || yoffset
!= 0)
896 POINT
*cpoints
= new POINT
[n
];
898 for (i
= 0; i
< n
; i
++)
900 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
901 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
903 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
905 (void)Polyline(GetHdc(), cpoints
, n
);
911 for (i
= 0; i
< n
; i
++)
912 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
914 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
918 void wxMSWDCImpl::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
922 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
924 wxCoord x2
= x
+ width
;
925 wxCoord y2
= y
+ height
;
927 wxCoord x2dev
= XLOG2DEV(x2
),
928 y2dev
= YLOG2DEV(y2
);
930 // Windows (but not Windows CE) draws the filled rectangles without outline
931 // (i.e. drawn with a transparent pen) one pixel smaller in both directions
932 // and we want them to have the same size regardless of which pen is used
934 if ( m_pen
.IsOk() && m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
939 #endif // !__WXWINCE__
941 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), x2dev
, y2dev
);
943 CalcBoundingBox(x
, y
);
944 CalcBoundingBox(x2
, y2
);
947 void wxMSWDCImpl::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
951 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
953 // Now, a negative radius value is interpreted to mean
954 // 'the proportion of the smallest X or Y dimension'
958 double smallest
= (width
< height
) ? width
: height
;
959 radius
= (- radius
* smallest
);
962 wxCoord x2
= (x
+width
);
963 wxCoord y2
= (y
+height
);
965 // Windows draws the filled rectangles without outline (i.e. drawn with a
966 // transparent pen) one pixel smaller in both directions and we want them
967 // to have the same size regardless of which pen is used - adjust
968 if ( m_pen
.IsOk() && m_pen
.GetStyle() == wxPENSTYLE_TRANSPARENT
)
974 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
975 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
977 CalcBoundingBox(x
, y
);
978 CalcBoundingBox(x2
, y2
);
981 void wxMSWDCImpl::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
985 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
987 // +1 below makes the ellipse more similar to other platforms.
988 // In particular, DoDrawEllipse(x,y,1,1) should draw one point.
989 wxCoord x2
= x
+ width
+ 1;
990 wxCoord y2
= y
+ height
+ 1;
992 // Problem: Windows GDI Ellipse() with x2-x == y2-y == 3 and transparent
993 // pen doesn't draw anything. Should we provide a workaround?
995 ::Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
997 CalcBoundingBox(x
, y
);
998 CalcBoundingBox(x2
, y2
);
1001 #if wxUSE_SPLINES && !defined(__WXWINCE__)
1002 void wxMSWDCImpl::DoDrawSpline(const wxPointList
*points
)
1004 // quadratic b-spline to cubic bezier spline conversion
1006 // quadratic spline with control points P0,P1,P2
1007 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1009 // bezier spline with control points B0,B1,B2,B3
1010 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1012 // control points of bezier spline calculated from b-spline
1014 // B1 = (2*P1 + P0)/3
1015 // B2 = (2*P1 + P2)/3
1018 WXMICROWIN_CHECK_HDC
1020 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1022 const size_t n_points
= points
->GetCount();
1023 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1025 const size_t n_bezier_points
= n_points
* 3 + 1;
1026 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1027 size_t bezier_pos
= 0;
1028 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1030 wxPointList::compatibility_iterator node
= points
->GetFirst();
1031 wxPoint
*p
= node
->GetData();
1032 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1033 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1035 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1038 node
= node
->GetNext();
1039 p
= node
->GetData();
1043 cx1
= ( x1
+ x2
) / 2;
1044 cy1
= ( y1
+ y2
) / 2;
1045 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1046 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1048 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1052 while ((node
= node
->GetNext()) != NULL
)
1054 while ((node
= node
->GetNext()))
1055 #endif // !wxUSE_STL
1057 p
= (wxPoint
*)node
->GetData();
1062 cx4
= (x1
+ x2
) / 2;
1063 cy4
= (y1
+ y2
) / 2;
1064 // B0 is B3 of previous segment
1066 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1067 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1070 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1071 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1074 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1075 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1081 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1083 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1084 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1086 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1089 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1093 #endif // wxUSE_SPLINES
1095 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1096 void wxMSWDCImpl::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1099 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1102 WXMICROWIN_CHECK_HDC
1104 wxBrushAttrsSetter
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1109 int rx1
= XLOG2DEV(x
+w
/2);
1110 int ry1
= YLOG2DEV(y
+h
/2);
1117 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1118 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1119 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1120 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1122 // Swap start and end positions if the end angle is less than the start angle.
1133 // draw pie with NULL_PEN first and then outline otherwise a line is
1134 // drawn from the start and end points to the centre
1135 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1138 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1139 rx1
, ry1
, rx2
, ry2
);
1143 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1144 rx1
, ry1
-1, rx2
, ry2
-1);
1147 ::SelectObject(GetHdc(), hpenOld
);
1149 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1150 rx1
, ry1
, rx2
, ry2
);
1152 CalcBoundingBox(x
, y
);
1153 CalcBoundingBox(x2
, y2
);
1157 void wxMSWDCImpl::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1159 WXMICROWIN_CHECK_HDC
1161 wxCHECK_RET( icon
.IsOk(), wxT("invalid icon in DrawIcon") );
1164 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1166 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1169 CalcBoundingBox(x
, y
);
1170 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1173 void wxMSWDCImpl::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1175 WXMICROWIN_CHECK_HDC
1177 wxCHECK_RET( bmp
.IsOk(), wxT("invalid bitmap in wxMSWDCImpl::DrawBitmap") );
1179 int width
= bmp
.GetWidth(),
1180 height
= bmp
.GetHeight();
1182 HBITMAP hbmpMask
= 0;
1185 HPALETTE oldPal
= 0;
1186 #endif // wxUSE_PALETTE
1188 if ( bmp
.HasAlpha() )
1191 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1193 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1197 SET_STRETCH_BLT_MODE(GetHdc());
1201 wxMask
*mask
= bmp
.GetMask();
1203 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1207 // don't give assert here because this would break existing
1208 // programs - just silently ignore useMask parameter
1215 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1219 #if wxUSE_SYSTEM_OPTIONS
1220 // On some systems, MaskBlt succeeds yet is much much slower
1221 // than the wxWidgets fall-back implementation. So we need
1222 // to be able to switch this on and off at runtime.
1224 // NB: don't query the value of the option every time but do it only
1225 // once as otherwise it can have real (and bad) performance
1226 // implications (see #11172)
1228 s_maskBltAllowed
= wxSystemOptions::GetOptionInt("no-maskblt") == 0;
1229 if ( s_maskBltAllowed
)
1230 #endif // wxUSE_SYSTEM_OPTIONS
1233 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1234 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1236 wxPalette
*pal
= bmp
.GetPalette();
1237 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1239 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1240 ::RealizePalette(hdcMem
);
1242 #endif // wxUSE_PALETTE
1244 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1247 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1251 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1252 #endif // wxUSE_PALETTE
1254 ::SelectObject(hdcMem
, hOldBitmap
);
1261 // Rather than reproduce wxMSWDCImpl::Blit, let's do it at the wxWin API
1265 memDC
.SelectObjectAsSource(bmp
);
1267 GetOwner()->Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1269 memDC
.SelectObject(wxNullBitmap
);
1272 else // no mask, just use BitBlt()
1275 HDC memdc
= ::CreateCompatibleDC( cdc
);
1276 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1278 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1280 wxTextColoursChanger
textCol(GetHdc(), *this);
1283 wxPalette
*pal
= bmp
.GetPalette();
1284 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1286 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1287 ::RealizePalette(memdc
);
1289 #endif // wxUSE_PALETTE
1291 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1292 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1296 ::SelectPalette(memdc
, oldPal
, FALSE
);
1297 #endif // wxUSE_PALETTE
1299 ::SelectObject( memdc
, hOldBitmap
);
1300 ::DeleteDC( memdc
);
1304 void wxMSWDCImpl::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1306 // For compatibility with other ports (notably wxGTK) and because it's
1307 // genuinely useful, we allow passing multiline strings to DrawText().
1308 // However there is no native MSW function to draw them directly so we
1309 // instead reuse the generic DrawLabel() method to render them. Of course,
1310 // DrawLabel() itself will call back to us but with single line strings
1311 // only so there won't be any infinite recursion here.
1312 if ( text
.find('\n') != wxString::npos
)
1314 GetOwner()->DrawLabel(text
, wxRect(x
, y
, 0, 0));
1318 WXMICROWIN_CHECK_HDC
1320 DrawAnyText(text
, x
, y
);
1322 // update the bounding box
1323 CalcBoundingBox(x
, y
);
1326 GetOwner()->GetTextExtent(text
, &w
, &h
);
1327 CalcBoundingBox(x
+ w
, y
+ h
);
1330 void wxMSWDCImpl::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1332 WXMICROWIN_CHECK_HDC
1334 // prepare for drawing the text
1335 wxTextColoursChanger
textCol(GetHdc(), *this);
1337 wxBkModeChanger
bkMode(GetHdc(), m_backgroundMode
);
1339 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1340 text
.c_str(), text
.length(), NULL
) == 0 )
1342 wxLogLastError(wxT("TextOut"));
1346 void wxMSWDCImpl::DoDrawRotatedText(const wxString
& text
,
1347 wxCoord x
, wxCoord y
,
1350 WXMICROWIN_CHECK_HDC
1352 // we test that we have some font because otherwise we should still use the
1353 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1354 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1355 // font for drawing rotated fonts unfortunately)
1356 if ( (angle
== 0.0) && m_font
.IsOk() )
1358 DoDrawText(text
, x
, y
);
1360 #ifndef __WXMICROWIN__
1363 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1364 // because it's not TrueType and so can't have non zero
1365 // orientation/escapement under Win9x
1366 wxFont font
= m_font
.IsOk() ? m_font
: *wxSWISS_FONT
;
1367 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1369 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1371 wxLogLastError(wxT("GetObject(hfont)"));
1374 // GDI wants the angle in tenth of degree
1375 long angle10
= (long)(angle
* 10);
1376 lf
.lfEscapement
= angle10
;
1377 lf
. lfOrientation
= angle10
;
1379 hfont
= ::CreateFontIndirect(&lf
);
1382 wxLogLastError(wxT("CreateFont"));
1386 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1388 DrawAnyText(text
, x
, y
);
1390 (void)::SelectObject(GetHdc(), hfontOld
);
1391 (void)::DeleteObject(hfont
);
1394 // call the bounding box by adding all four vertices of the rectangle
1395 // containing the text to it (simpler and probably not slower than
1396 // determining which of them is really topmost/leftmost/...)
1398 GetOwner()->GetTextExtent(text
, &w
, &h
);
1400 double rad
= DegToRad(angle
);
1402 // "upper left" and "upper right"
1403 CalcBoundingBox(x
, y
);
1404 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1406 // "bottom left" and "bottom right"
1407 x
+= (wxCoord
)(h
*sin(rad
));
1408 y
+= (wxCoord
)(h
*cos(rad
));
1409 CalcBoundingBox(x
, y
);
1410 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1415 // ---------------------------------------------------------------------------
1417 // ---------------------------------------------------------------------------
1421 void wxMSWDCImpl::DoSelectPalette(bool realize
)
1423 WXMICROWIN_CHECK_HDC
1425 // Set the old object temporarily, in case the assignment deletes an object
1426 // that's not yet selected out.
1429 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1433 if ( m_palette
.IsOk() )
1435 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1436 GetHpaletteOf(m_palette
),
1439 m_oldPalette
= (WXHPALETTE
) oldPal
;
1442 ::RealizePalette(GetHdc());
1446 void wxMSWDCImpl::SetPalette(const wxPalette
& palette
)
1448 if ( palette
.IsOk() )
1450 m_palette
= palette
;
1451 DoSelectPalette(true);
1455 void wxMSWDCImpl::InitializePalette()
1457 if ( wxDisplayDepth() <= 8 )
1459 // look for any window or parent that has a custom palette. If any has
1460 // one then we need to use it in drawing operations
1461 wxWindow
*win
= m_window
->GetAncestorWithCustomPalette();
1463 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1464 if ( m_hasCustomPalette
)
1466 m_palette
= win
->GetPalette();
1468 // turn on MSW translation for this palette
1474 #endif // wxUSE_PALETTE
1476 // SetFont/Pen/Brush() really ask to be implemented as a single template
1477 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1479 void wxMSWDCImpl::SetFont(const wxFont
& font
)
1481 WXMICROWIN_CHECK_HDC
1483 if ( font
== m_font
)
1488 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1489 if ( hfont
== HGDI_ERROR
)
1491 wxLogLastError(wxT("SelectObject(font)"));
1496 m_oldFont
= (WXHFONT
)hfont
;
1501 else // invalid font, reset the current font
1505 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1507 wxLogLastError(wxT("SelectObject(old font)"));
1513 m_font
= wxNullFont
;
1517 void wxMSWDCImpl::SetPen(const wxPen
& pen
)
1519 WXMICROWIN_CHECK_HDC
1526 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1527 if ( hpen
== HGDI_ERROR
)
1529 wxLogLastError(wxT("SelectObject(pen)"));
1534 m_oldPen
= (WXHPEN
)hpen
;
1539 else // invalid pen, reset the current pen
1543 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1545 wxLogLastError(wxT("SelectObject(old pen)"));
1555 void wxMSWDCImpl::SetBrush(const wxBrush
& brush
)
1557 WXMICROWIN_CHECK_HDC
1559 if ( brush
== m_brush
)
1564 // we must make sure the brush is aligned with the logical coordinates
1565 // before selecting it or using the same brush for the background of
1566 // different windows would result in discontinuities
1567 wxSize sizeBrushBitmap
= wxDefaultSize
;
1568 wxBitmap
*stipple
= brush
.GetStipple();
1569 if ( stipple
&& stipple
->IsOk() )
1570 sizeBrushBitmap
= stipple
->GetSize();
1571 else if ( brush
.IsHatch() )
1572 sizeBrushBitmap
= wxSize(8, 8);
1574 if ( sizeBrushBitmap
.IsFullySpecified() )
1576 if ( !::SetBrushOrgEx
1579 m_deviceOriginX
% sizeBrushBitmap
.x
,
1580 m_deviceOriginY
% sizeBrushBitmap
.y
,
1581 NULL
// [out] previous brush origin
1584 wxLogLastError(wxT("SetBrushOrgEx()"));
1588 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1589 if ( hbrush
== HGDI_ERROR
)
1591 wxLogLastError(wxT("SelectObject(brush)"));
1596 m_oldBrush
= (WXHBRUSH
)hbrush
;
1601 else // invalid brush, reset the current brush
1605 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1607 wxLogLastError(wxT("SelectObject(old brush)"));
1613 m_brush
= wxNullBrush
;
1617 void wxMSWDCImpl::SetBackground(const wxBrush
& brush
)
1619 WXMICROWIN_CHECK_HDC
1621 m_backgroundBrush
= brush
;
1623 if ( m_backgroundBrush
.IsOk() )
1625 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1629 void wxMSWDCImpl::SetBackgroundMode(int mode
)
1631 WXMICROWIN_CHECK_HDC
1633 m_backgroundMode
= mode
;
1635 // SetBackgroundColour now only refers to text background
1636 // and m_backgroundMode is used there
1639 void wxMSWDCImpl::SetLogicalFunction(wxRasterOperationMode function
)
1641 WXMICROWIN_CHECK_HDC
1643 m_logicalFunction
= function
;
1648 void wxMSWDCImpl::SetRop(WXHDC dc
)
1650 if ( !dc
|| m_logicalFunction
< 0 )
1655 switch (m_logicalFunction
)
1657 case wxCLEAR
: rop
= R2_BLACK
; break;
1658 case wxXOR
: rop
= R2_XORPEN
; break;
1659 case wxINVERT
: rop
= R2_NOT
; break;
1660 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1661 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1662 case wxCOPY
: rop
= R2_COPYPEN
; break;
1663 case wxAND
: rop
= R2_MASKPEN
; break;
1664 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1665 case wxNO_OP
: rop
= R2_NOP
; break;
1666 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1667 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1668 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1669 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1670 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1671 case wxOR
: rop
= R2_MERGEPEN
; break;
1672 case wxSET
: rop
= R2_WHITE
; break;
1674 wxFAIL_MSG( wxS("unknown logical function") );
1678 SetROP2(GetHdc(), rop
);
1681 bool wxMSWDCImpl::StartDoc(const wxString
& WXUNUSED(message
))
1683 // We might be previewing, so return true to let it continue.
1687 void wxMSWDCImpl::EndDoc()
1691 void wxMSWDCImpl::StartPage()
1695 void wxMSWDCImpl::EndPage()
1699 // ---------------------------------------------------------------------------
1701 // ---------------------------------------------------------------------------
1703 wxCoord
wxMSWDCImpl::GetCharHeight() const
1705 WXMICROWIN_CHECK_HDC_RET(0)
1707 TEXTMETRIC lpTextMetric
;
1709 GetTextMetrics(GetHdc(), &lpTextMetric
);
1711 return lpTextMetric
.tmHeight
;
1714 wxCoord
wxMSWDCImpl::GetCharWidth() const
1716 WXMICROWIN_CHECK_HDC_RET(0)
1718 TEXTMETRIC lpTextMetric
;
1720 GetTextMetrics(GetHdc(), &lpTextMetric
);
1722 return lpTextMetric
.tmAveCharWidth
;
1725 void wxMSWDCImpl::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1726 wxCoord
*descent
, wxCoord
*externalLeading
,
1727 const wxFont
*font
) const
1729 #ifdef __WXMICROWIN__
1734 if (descent
) *descent
= 0;
1735 if (externalLeading
) *externalLeading
= 0;
1738 #endif // __WXMICROWIN__
1743 wxASSERT_MSG( font
->IsOk(), wxT("invalid font in wxMSWDCImpl::GetTextExtent") );
1745 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1747 else // don't change the font
1753 const size_t len
= string
.length();
1754 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1756 wxLogLastError(wxT("GetTextExtentPoint32()"));
1759 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1760 // the result computed by GetTextExtentPoint32() may be too small as it
1761 // accounts for under/overhang of the first/last character while we want
1762 // just the bounding rect for this string so adjust the width as needed
1763 // (using API not available in 2002 SDKs of WinCE)
1767 const wxChar chFirst
= *string
.begin();
1768 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1770 if ( width
.abcA
< 0 )
1771 sizeRect
.cx
-= width
.abcA
;
1775 const wxChar chLast
= *string
.rbegin();
1776 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1778 //else: we already have the width of the last character
1780 if ( width
.abcC
< 0 )
1781 sizeRect
.cx
-= width
.abcC
;
1783 //else: GetCharABCWidths() failed, not a TrueType font?
1785 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1792 if ( descent
|| externalLeading
)
1795 ::GetTextMetrics(GetHdc(), &tm
);
1798 *descent
= tm
.tmDescent
;
1799 if (externalLeading
)
1800 *externalLeading
= tm
.tmExternalLeading
;
1805 ::SelectObject(GetHdc(), hfontOld
);
1810 // Each element of the array will be the width of the string up to and
1811 // including the coresoponding character in text.
1813 bool wxMSWDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1815 static int maxLenText
= -1;
1816 static int maxWidth
= -1;
1819 int stlen
= text
.length();
1821 if (maxLenText
== -1)
1823 // Win9x and WinNT+ have different limits
1824 int version
= wxGetOsVersion();
1825 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1826 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1830 widths
.Add(0, stlen
); // fill the array with zeros
1834 if (!::GetTextExtentExPoint(GetHdc(),
1835 text
.c_str(), // string to check
1836 wxMin(stlen
, maxLenText
),
1838 &fit
, // [out] count of chars
1840 &widths
[0], // array to fill
1844 wxLogLastError(wxT("GetTextExtentExPoint"));
1851 void wxMSWDCImpl::RealizeScaleAndOrigin()
1853 // although it may seem wasteful to always use MM_ANISOTROPIC here instead
1854 // of using MM_TEXT if there is no scaling, benchmarking doesn't detect any
1855 // noticeable difference between these mapping modes
1857 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1859 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1860 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1862 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1863 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1865 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1866 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1870 void wxMSWDCImpl::SetMapMode(wxMappingMode mode
)
1872 WXMICROWIN_CHECK_HDC
1874 m_mappingMode
= mode
;
1876 if ( mode
== wxMM_TEXT
)
1879 m_logicalScaleY
= 1.0;
1881 else // need to do some calculations
1883 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1884 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1885 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1886 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1888 if ( (mm_width
== 0) || (mm_height
== 0) )
1890 // we can't calculate mm2pixels[XY] then!
1894 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1895 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1900 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1901 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1905 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1906 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1910 m_logicalScaleX
= mm2pixelsX
;
1911 m_logicalScaleY
= mm2pixelsY
;
1915 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1916 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1920 wxFAIL_MSG( wxT("unknown mapping mode in SetMapMode") );
1924 ComputeScaleAndOrigin();
1926 RealizeScaleAndOrigin();
1929 void wxMSWDCImpl::SetUserScale(double x
, double y
)
1931 WXMICROWIN_CHECK_HDC
1933 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1936 wxDCImpl::SetUserScale(x
,y
);
1938 RealizeScaleAndOrigin();
1941 void wxMSWDCImpl::SetAxisOrientation(bool xLeftRight
,
1944 WXMICROWIN_CHECK_HDC
1946 int signX
= xLeftRight
? 1 : -1,
1947 signY
= yBottomUp
? -1 : 1;
1949 if (signX
== m_signX
&& signY
== m_signY
)
1952 wxDCImpl::SetAxisOrientation( xLeftRight
, yBottomUp
);
1954 RealizeScaleAndOrigin();
1957 void wxMSWDCImpl::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1959 WXMICROWIN_CHECK_HDC
1961 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1964 wxDCImpl::SetLogicalOrigin( x
, y
);
1966 RealizeScaleAndOrigin();
1969 // For use by wxWidgets only, unless custom units are required.
1970 void wxMSWDCImpl::SetLogicalScale(double x
, double y
)
1972 WXMICROWIN_CHECK_HDC
1974 wxDCImpl::SetLogicalScale(x
,y
);
1977 void wxMSWDCImpl::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1979 WXMICROWIN_CHECK_HDC
1981 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
1984 wxDCImpl::SetDeviceOrigin( x
, y
);
1986 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1989 // ---------------------------------------------------------------------------
1991 // ---------------------------------------------------------------------------
1993 bool wxMSWDCImpl::DoBlit(wxCoord dstX
, wxCoord dstY
,
1994 wxCoord dstWidth
, wxCoord dstHeight
,
1996 wxCoord srcX
, wxCoord srcY
,
1997 wxRasterOperationMode rop
, bool useMask
,
1998 wxCoord srcMaskX
, wxCoord srcMaskY
)
2000 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2003 bool wxMSWDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2004 wxCoord dstWidth
, wxCoord dstHeight
,
2006 wxCoord xsrc
, wxCoord ysrc
,
2007 wxCoord srcWidth
, wxCoord srcHeight
,
2008 wxRasterOperationMode rop
, bool useMask
,
2009 wxCoord xsrcMask
, wxCoord ysrcMask
)
2011 wxCHECK_MSG( source
, false, wxT("wxMSWDCImpl::Blit(): NULL wxDC pointer") );
2013 WXMICROWIN_CHECK_HDC_RET(false)
2015 wxMSWDCImpl
*implSrc
= wxDynamicCast( source
->GetImpl(), wxMSWDCImpl
);
2018 // TODO: Do we want to be able to blit from other DCs too?
2022 const HDC hdcSrc
= GetHdcOf(*implSrc
);
2024 // if either the source or destination has alpha channel, we must use
2025 // AlphaBlt() as other function don't handle it correctly
2026 const wxBitmap
& bmpSrc
= implSrc
->GetSelectedBitmap();
2027 if ( bmpSrc
.IsOk() && (bmpSrc
.HasAlpha() ||
2028 (m_selectedBitmap
.IsOk() && m_selectedBitmap
.HasAlpha())) )
2030 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2031 xsrc
, ysrc
, srcWidth
, srcHeight
, hdcSrc
, bmpSrc
) )
2035 wxMask
*mask
= NULL
;
2038 mask
= bmpSrc
.GetMask();
2040 if ( !(bmpSrc
.IsOk() && mask
&& mask
->GetMaskBitmap()) )
2042 // don't give assert here because this would break existing
2043 // programs - just silently ignore useMask parameter
2048 if (xsrcMask
== -1 && ysrcMask
== -1)
2050 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2053 wxTextColoursChanger
textCol(GetHdc(), *this);
2058 case wxXOR
: dwRop
= SRCINVERT
; break;
2059 case wxINVERT
: dwRop
= DSTINVERT
; break;
2060 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2061 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2062 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2063 case wxSET
: dwRop
= WHITENESS
; break;
2064 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2065 case wxAND
: dwRop
= SRCAND
; break;
2066 case wxOR
: dwRop
= SRCPAINT
; break;
2067 case wxEQUIV
: dwRop
= 0x00990066; break;
2068 case wxNAND
: dwRop
= 0x007700E6; break;
2069 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2070 case wxCOPY
: dwRop
= SRCCOPY
; break;
2071 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2072 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2073 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2075 wxFAIL_MSG( wxT("unsupported logical function") );
2079 bool success
= false;
2084 // we want the part of the image corresponding to the mask to be
2085 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2086 // meaning of fg and bg is inverted which corresponds to wxWin notion
2087 // of the mask which is also contrary to the Windows one)
2089 // On some systems, MaskBlt succeeds yet is much much slower
2090 // than the wxWidgets fall-back implementation. So we need
2091 // to be able to switch this on and off at runtime.
2092 #if wxUSE_SYSTEM_OPTIONS
2093 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2096 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2101 xdest
, ydest
, dstWidth
, dstHeight
,
2104 (HBITMAP
)mask
->GetMaskBitmap(),
2106 MAKEROP4(dwRop
, DSTCOPY
)
2114 // Blit bitmap with mask
2117 HBITMAP buffer_bmap
;
2119 #if wxUSE_DC_CACHEING
2120 // create a temp buffer bitmap and DCs to access it and the mask
2121 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, hdcSrc
);
2122 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2124 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2125 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2127 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2128 dstWidth
, dstHeight
);
2130 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2131 #else // !wxUSE_DC_CACHEING
2132 // create a temp buffer bitmap and DCs to access it and the mask
2133 dc_mask
= ::CreateCompatibleDC(hdcSrc
);
2134 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2135 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2136 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2137 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2138 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2140 // copy dest to buffer
2141 if ( !::BitBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2142 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2144 wxLogLastError(wxT("BitBlt"));
2147 SET_STRETCH_BLT_MODE(GetHdc());
2149 // copy src to buffer using selected raster op
2150 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2151 hdcSrc
, xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2153 wxLogLastError(wxT("StretchBlt"));
2156 // set masked area in buffer to BLACK
2158 wxTextColoursChanger
textCol2(GetHdc(), *wxBLACK
, *wxWHITE
);
2159 if ( !::StretchBlt(dc_buffer
, 0, 0, dstWidth
, dstHeight
,
2160 dc_mask
, xsrcMask
, ysrcMask
,
2161 srcWidth
, srcHeight
, SRCAND
) )
2163 wxLogLastError(wxT("StretchBlt"));
2166 // set unmasked area in dest to BLACK
2167 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2168 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2169 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2170 dc_mask
, xsrcMask
, ysrcMask
,
2171 srcWidth
, srcHeight
, SRCAND
) )
2173 wxLogLastError(wxT("StretchBlt"));
2175 } // restore the original text and background colours
2177 // OR buffer to dest
2178 success
= ::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2179 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2182 wxLogLastError(wxT("BitBlt"));
2185 // tidy up temporary DCs and bitmap
2186 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2187 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2189 #if !wxUSE_DC_CACHEING
2191 ::DeleteDC(dc_mask
);
2192 ::DeleteDC(dc_buffer
);
2193 ::DeleteObject(buffer_bmap
);
2198 else // no mask, just BitBlt() it
2200 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2201 // use StretchBlt() if available and finally fall back to BitBlt()
2203 // FIXME: use appropriate WinCE functions
2205 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2206 if ( bmpSrc
.IsOk() && (caps
& RC_STRETCHDIB
) )
2211 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2213 &ds
) == sizeof(ds
) )
2215 SET_STRETCH_BLT_MODE(GetHdc());
2217 // Figure out what co-ordinate system we're supposed to specify
2219 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2223 ysrc
= hDIB
- (ysrc
+ srcHeight
);
2226 if ( ::StretchDIBits(GetHdc(),
2228 dstWidth
, dstHeight
,
2230 srcWidth
, srcHeight
,
2232 (LPBITMAPINFO
)&ds
.dsBmih
,
2235 ) == (int)GDI_ERROR
)
2237 // On Win9x this API fails most (all?) of the time, so
2238 // logging it becomes quite distracting. Since it falls
2239 // back to the code below this is not really serious, so
2241 //wxLogLastError(wxT("StretchDIBits"));
2250 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2254 SET_STRETCH_BLT_MODE(GetHdc());
2259 xdest
, ydest
, dstWidth
, dstHeight
,
2261 xsrc
, ysrc
, srcWidth
, srcHeight
,
2265 wxLogLastError(wxT("StretchBlt"));
2275 if ( !::BitBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2276 hdcSrc
, xsrc
, ysrc
, dwRop
) )
2278 wxLogLastError(wxT("BitBlt"));
2290 void wxMSWDCImpl::GetDeviceSize(int *width
, int *height
) const
2292 WXMICROWIN_CHECK_HDC
2295 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2297 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2300 void wxMSWDCImpl::DoGetSizeMM(int *w
, int *h
) const
2302 WXMICROWIN_CHECK_HDC
2304 // if we implement it in terms of DoGetSize() instead of directly using the
2305 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2306 // will also work for wxWindowDC and wxClientDC even though their size is
2307 // not the same as the total size of the screen
2308 int wPixels
, hPixels
;
2309 DoGetSize(&wPixels
, &hPixels
);
2313 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2315 wxCHECK_RET( wTotal
, wxT("0 width device?") );
2317 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2322 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2324 wxCHECK_RET( hTotal
, wxT("0 height device?") );
2326 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2330 wxSize
wxMSWDCImpl::GetPPI() const
2332 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2334 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2335 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2337 return wxSize(x
, y
);
2340 // ----------------------------------------------------------------------------
2342 // ----------------------------------------------------------------------------
2344 #if wxUSE_DC_CACHEING
2347 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2348 * improve it in due course, either using arrays, or simply storing pointers to one
2349 * entry for the bitmap, and two for the DCs. -- JACS
2352 wxObjectList
wxMSWDCImpl::sm_bitmapCache
;
2353 wxObjectList
wxMSWDCImpl::sm_dcCache
;
2355 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2364 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2373 wxDCCacheEntry::~wxDCCacheEntry()
2376 ::DeleteObject((HBITMAP
) m_bitmap
);
2378 ::DeleteDC((HDC
) m_dc
);
2381 wxDCCacheEntry
* wxMSWDCImpl::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2383 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2384 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2387 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2389 if (entry
->m_depth
== depth
)
2391 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2393 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2394 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2395 if ( !entry
->m_bitmap
)
2397 wxLogLastError(wxT("CreateCompatibleBitmap"));
2399 entry
->m_width
= w
; entry
->m_height
= h
;
2405 node
= node
->GetNext();
2407 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2410 wxLogLastError(wxT("CreateCompatibleBitmap"));
2412 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2413 AddToBitmapCache(entry
);
2417 wxDCCacheEntry
* wxMSWDCImpl::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2419 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2420 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2423 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2425 // Don't return the same one as we already have
2426 if (!notThis
|| (notThis
!= entry
))
2428 if (entry
->m_depth
== depth
)
2434 node
= node
->GetNext();
2436 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2439 wxLogLastError(wxT("CreateCompatibleDC"));
2441 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2442 AddToDCCache(entry
);
2446 void wxMSWDCImpl::AddToBitmapCache(wxDCCacheEntry
* entry
)
2448 sm_bitmapCache
.Append(entry
);
2451 void wxMSWDCImpl::AddToDCCache(wxDCCacheEntry
* entry
)
2453 sm_dcCache
.Append(entry
);
2456 void wxMSWDCImpl::ClearCache()
2458 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2459 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2462 // Clean up cache at app exit
2463 class wxDCModule
: public wxModule
2466 virtual bool OnInit() { return true; }
2467 virtual void OnExit() { wxMSWDCImpl::ClearCache(); }
2470 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2473 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2475 #endif // wxUSE_DC_CACHEING
2477 // ----------------------------------------------------------------------------
2478 // alpha channel support
2479 // ----------------------------------------------------------------------------
2481 static bool AlphaBlt(HDC hdcDst
,
2482 int x
, int y
, int dstWidth
, int dstHeight
,
2484 int srcWidth
, int srcHeight
,
2486 const wxBitmap
& bmp
)
2488 wxASSERT_MSG( bmp
.IsOk() && bmp
.HasAlpha(), wxT("AlphaBlt(): invalid bitmap") );
2489 wxASSERT_MSG( hdcDst
&& hdcSrc
, wxT("AlphaBlt(): invalid HDC") );
2491 // do we have AlphaBlend() and company in the headers?
2492 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2493 // yes, now try to see if we have it during run-time
2494 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2495 HDC
,int,int,int,int,
2499 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(wxT("AlphaBlend"));
2500 if ( pfnAlphaBlend
)
2503 bf
.BlendOp
= AC_SRC_OVER
;
2505 bf
.SourceConstantAlpha
= 0xff;
2506 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2508 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2509 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2512 // skip wxAlphaBlend() call below
2516 wxLogLastError(wxT("AlphaBlend"));
2519 wxUnusedVar(hdcSrc
);
2520 #endif // defined(AC_SRC_OVER)
2522 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2524 #ifdef wxHAS_RAW_BITMAP
2525 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2528 #else // !wxHAS_RAW_BITMAP
2529 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2530 // alpha but at least something will be shown like this)
2533 #endif // wxHAS_RAW_BITMAP/!wxHAS_RAW_BITMAP
2537 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2538 #ifdef wxHAS_RAW_BITMAP
2541 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2542 int dstWidth
, int dstHeight
,
2544 int srcWidth
, int srcHeight
,
2545 const wxBitmap
& bmpSrc
)
2547 // get the destination DC pixels
2548 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2550 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2552 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2554 wxLogLastError(wxT("BitBlt"));
2557 // combine them with the source bitmap using alpha
2558 wxAlphaPixelData
dataDst(bmpDst
),
2559 dataSrc((wxBitmap
&)bmpSrc
);
2561 wxCHECK_RET( dataDst
&& dataSrc
,
2562 wxT("failed to get raw data in wxAlphaBlend") );
2564 wxAlphaPixelData::Iterator
pDst(dataDst
),
2568 for ( int y
= 0; y
< dstHeight
; y
++ )
2570 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2572 for ( int x
= 0; x
< dstWidth
; x
++ )
2574 // source is point sampled, Alpha StretchBlit is ugly on Win95
2575 // (but does not impact performance)
2576 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2578 // note that source bitmap uses premultiplied alpha (as required by
2579 // the real AlphaBlend)
2580 const unsigned beta
= 255 - pSrc
.Alpha();
2582 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2583 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2584 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2589 pDst
= pDstRowStart
;
2590 pDst
.OffsetY(dataDst
, 1);
2593 // and finally blit them back to the destination DC
2594 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2596 wxLogLastError(wxT("BitBlt"));
2600 #endif // wxHAS_RAW_BITMAP
2602 void wxMSWDCImpl::DoGradientFillLinear (const wxRect
& rect
,
2603 const wxColour
& initialColour
,
2604 const wxColour
& destColour
,
2605 wxDirection nDirection
)
2607 // use native function if we have compile-time support it and can load it
2608 // during run-time (linking to it statically would make the program
2609 // unusable on earlier Windows versions)
2610 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2612 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2613 static GradientFill_t pfnGradientFill
=
2614 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(wxT("GradientFill"));
2616 if ( pfnGradientFill
)
2618 GRADIENT_RECT grect
;
2619 grect
.UpperLeft
= 0;
2620 grect
.LowerRight
= 1;
2622 // invert colours direction if not filling from left-to-right or
2624 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2626 // one vertex for upper left and one for upper-right
2627 TRIVERTEX vertices
[2];
2629 vertices
[0].x
= rect
.GetLeft();
2630 vertices
[0].y
= rect
.GetTop();
2631 vertices
[1].x
= rect
.GetRight()+1;
2632 vertices
[1].y
= rect
.GetBottom()+1;
2634 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2635 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2636 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2637 vertices
[firstVertex
].Alpha
= 0;
2638 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2639 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2640 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2641 vertices
[1 - firstVertex
].Alpha
= 0;
2643 if ( (*pfnGradientFill
)
2650 nDirection
== wxWEST
|| nDirection
== wxEAST
2651 ? GRADIENT_FILL_RECT_H
2652 : GRADIENT_FILL_RECT_V
2655 // skip call of the base class version below
2659 wxLogLastError(wxT("GradientFill"));
2661 #endif // wxUSE_DYNLIB_CLASS
2663 wxDCImpl::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2666 #if wxUSE_DYNLIB_CLASS
2668 static DWORD
wxGetDCLayout(HDC hdc
)
2670 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2672 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2674 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2677 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2679 DWORD layout
= wxGetDCLayout(GetHdc());
2681 if ( layout
== (DWORD
)-1 )
2682 return wxLayout_Default
;
2684 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2687 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection dir
)
2689 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2691 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(wxT("gdi32.dll")));
2692 if ( !s_pfnSetLayout
)
2695 if ( dir
== wxLayout_Default
)
2697 dir
= wxTheApp
->GetLayoutDirection();
2698 if ( dir
== wxLayout_Default
)
2702 DWORD layout
= wxGetDCLayout(GetHdc());
2703 if ( dir
== wxLayout_RightToLeft
)
2704 layout
|= LAYOUT_RTL
;
2706 layout
&= ~LAYOUT_RTL
;
2708 s_pfnSetLayout(GetHdc(), layout
);
2711 #else // !wxUSE_DYNLIB_CLASS
2713 // we can't provide RTL support without dynamic loading, so stub it out
2714 wxLayoutDirection
wxMSWDCImpl::GetLayoutDirection() const
2716 return wxLayout_Default
;
2719 void wxMSWDCImpl::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2723 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS