1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/dc.cpp
3 // Purpose: wxDC class for MSW port
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
28 #include "wx/msw/wrapcdlg.h"
30 #include "wx/window.h"
33 #include "wx/dialog.h"
35 #include "wx/bitmap.h"
36 #include "wx/dcmemory.h"
39 #include "wx/dcprint.h"
40 #include "wx/module.h"
43 #include "wx/sysopt.h"
44 #include "wx/dynlib.h"
46 #ifdef wxHAVE_RAW_BITMAP
47 #include "wx/rawbmp.h"
57 #define AC_SRC_ALPHA 1
64 /* Quaternary raster codes */
66 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
69 // apparently with MicroWindows it is possible that HDC is 0 so we have to
70 // check for this ourselves
72 #define WXMICROWIN_CHECK_HDC if ( !GetHDC() ) return;
73 #define WXMICROWIN_CHECK_HDC_RET(x) if ( !GetHDC() ) return x;
75 #define WXMICROWIN_CHECK_HDC
76 #define WXMICROWIN_CHECK_HDC_RET(x)
79 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxDCBase
)
81 // ---------------------------------------------------------------------------
83 // ---------------------------------------------------------------------------
85 static const int VIEWPORT_EXTENT
= 1000;
87 static const int MM_POINTS
= 9;
88 static const int MM_METRIC
= 10;
90 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
91 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
92 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
94 // ----------------------------------------------------------------------------
95 // macros for logical <-> device coords conversion
96 // ----------------------------------------------------------------------------
99 We currently let Windows do all the translations itself so these macros are
100 not really needed (any more) but keep them to enhance readability of the
101 code by allowing to see where are the logical and where are the device
106 #define XLOG2DEV(x) ((x-m_logicalOriginX)*m_signX)
107 #define YLOG2DEV(y) ((y-m_logicalOriginY)*m_signY)
108 #define XDEV2LOG(x) ((x)*m_signX+m_logicalOriginX)
109 #define YDEV2LOG(y) ((y)*m_signY+m_logicalOriginY)
111 #define XLOG2DEV(x) (x)
112 #define YLOG2DEV(y) (y)
113 #define XDEV2LOG(x) (x)
114 #define YDEV2LOG(y) (y)
117 // ---------------------------------------------------------------------------
119 // ---------------------------------------------------------------------------
121 // convert degrees to radians
122 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
124 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
126 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
127 // to pass it to this function but as we already have it at the point
128 // of call anyhow we do
130 // return true if we could draw the bitmap in one way or the other, false
132 static bool AlphaBlt(HDC hdcDst
,
133 int x
, int y
, int dstWidth
, int dstHeight
,
135 int srcWidth
, int srcHeight
,
137 const wxBitmap
& bmp
);
139 #ifdef wxHAVE_RAW_BITMAP
141 // our (limited) AlphaBlend() replacement for Windows versions not providing it
143 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
144 int dstWidth
, int dstHeight
,
146 int srcWidth
, int srcHeight
,
147 const wxBitmap
& bmpSrc
);
149 #endif // wxHAVE_RAW_BITMAP
151 // ----------------------------------------------------------------------------
153 // ----------------------------------------------------------------------------
155 // instead of duplicating the same code which sets and then restores text
156 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
157 // encapsulate this in a small helper class
159 // wxColourChanger: changes the text colours in the ctor if required and
160 // restores them in the dtor
161 class wxColourChanger
164 wxColourChanger(wxDC
& dc
);
170 COLORREF m_colFgOld
, m_colBgOld
;
174 DECLARE_NO_COPY_CLASS(wxColourChanger
)
177 // this class saves the old stretch blit mode during its life time
178 class StretchBltModeChanger
181 StretchBltModeChanger(HDC hdc
,
182 int WXUNUSED_IN_WINCE(mode
))
186 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
188 wxLogLastError(_T("SetStretchBltMode"));
192 ~StretchBltModeChanger()
195 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
196 wxLogLastError(_T("SetStretchBltMode"));
205 DECLARE_NO_COPY_CLASS(StretchBltModeChanger
)
208 #if wxUSE_DYNLIB_CLASS
210 // helper class to cache dynamically loaded libraries and not attempt reloading
212 class wxOnceOnlyDLLLoader
215 // ctor argument must be a literal string as we don't make a copy of it!
216 wxOnceOnlyDLLLoader(const wxChar
*dllName
)
222 // return the symbol with the given name or NULL if the DLL not loaded
223 // or symbol not present
224 void *GetSymbol(const wxChar
*name
)
226 // we're prepared to handle errors here
231 m_dll
.Load(m_dllName
);
233 // reset the name whether we succeeded or failed so that we don't
234 // try again the next time
238 return m_dll
.IsLoaded() ? m_dll
.GetSymbol(name
) : NULL
;
243 if ( m_dll
.IsLoaded() )
250 wxDynamicLibrary m_dll
;
251 const wxChar
*m_dllName
;
254 static wxOnceOnlyDLLLoader
wxMSIMG32DLL(_T("msimg32"));
256 // we must ensure that DLLs are unloaded before the static objects cleanup time
257 // because we may hit the notorious DllMain() dead lock in this case if wx is
258 // used as a DLL (attempting to unload another DLL from inside DllMain() hangs
259 // under Windows because it tries to reacquire the same lock)
260 class wxGDIDLLsCleanupModule
: public wxModule
263 virtual bool OnInit() { return true; }
264 virtual void OnExit() { wxMSIMG32DLL
.Unload(); }
267 DECLARE_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
)
270 IMPLEMENT_DYNAMIC_CLASS(wxGDIDLLsCleanupModule
, wxModule
)
272 #endif // wxUSE_DYNLIB_CLASS
274 // ===========================================================================
276 // ===========================================================================
278 // ----------------------------------------------------------------------------
280 // ----------------------------------------------------------------------------
282 wxColourChanger::wxColourChanger(wxDC
& dc
) : m_dc(dc
)
284 const wxBrush
& brush
= dc
.GetBrush();
285 if ( brush
.Ok() && brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
287 HDC hdc
= GetHdcOf(dc
);
288 m_colFgOld
= ::GetTextColor(hdc
);
289 m_colBgOld
= ::GetBkColor(hdc
);
291 // note that Windows convention is opposite to wxWidgets one, this is
292 // why text colour becomes the background one and vice versa
293 const wxColour
& colFg
= dc
.GetTextForeground();
296 ::SetBkColor(hdc
, colFg
.GetPixel());
299 const wxColour
& colBg
= dc
.GetTextBackground();
302 ::SetTextColor(hdc
, colBg
.GetPixel());
306 dc
.GetBackgroundMode() == wxTRANSPARENT
? TRANSPARENT
309 // flag which telsl us to undo changes in the dtor
314 // nothing done, nothing to undo
319 wxColourChanger::~wxColourChanger()
323 // restore the colours we changed
324 HDC hdc
= GetHdcOf(m_dc
);
326 ::SetBkMode(hdc
, TRANSPARENT
);
327 ::SetTextColor(hdc
, m_colFgOld
);
328 ::SetBkColor(hdc
, m_colBgOld
);
332 // ---------------------------------------------------------------------------
334 // ---------------------------------------------------------------------------
340 SelectOldObjects(m_hDC
);
342 // if we own the HDC, we delete it, otherwise we just release it
346 ::DeleteDC(GetHdc());
348 else // we don't own our HDC
352 ::ReleaseDC(GetHwndOf(m_canvas
), GetHdc());
356 // Must have been a wxScreenDC
357 ::ReleaseDC((HWND
) NULL
, GetHdc());
363 // This will select current objects out of the DC,
364 // which is what you have to do before deleting the
366 void wxDC::SelectOldObjects(WXHDC dc
)
372 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
374 if (m_selectedBitmap
.Ok())
376 m_selectedBitmap
.SetSelectedInto(NULL
);
383 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
388 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
393 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
400 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
403 #endif // wxUSE_PALETTE
406 m_brush
= wxNullBrush
;
409 m_palette
= wxNullPalette
;
410 #endif // wxUSE_PALETTE
412 m_backgroundBrush
= wxNullBrush
;
413 m_selectedBitmap
= wxNullBitmap
;
416 // ---------------------------------------------------------------------------
418 // ---------------------------------------------------------------------------
420 void wxDC::UpdateClipBox()
425 ::GetClipBox(GetHdc(), &rect
);
427 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
428 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
429 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
430 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
434 wxDC::DoGetClippingBox(wxCoord
*x
, wxCoord
*y
, wxCoord
*w
, wxCoord
*h
) const
436 // check if we should try to retrieve the clipping region possibly not set
437 // by our SetClippingRegion() but preset by Windows:this can only happen
438 // when we're associated with an existing HDC usign SetHDC(), see there
439 if ( m_clipping
&& !m_clipX1
&& !m_clipX2
)
441 wxDC
*self
= wxConstCast(this, wxDC
);
442 self
->UpdateClipBox();
444 if ( !m_clipX1
&& !m_clipX2
)
445 self
->m_clipping
= false;
448 wxDCBase::DoGetClippingBox(x
, y
, w
, h
);
451 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
452 void wxDC::SetClippingHrgn(WXHRGN hrgn
)
454 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
458 // note that we combine the new clipping region with the existing one: this
459 // is compatible with what the other ports do and is the documented
460 // behaviour now (starting with 2.3.3)
461 #if defined(__WXWINCE__)
463 if ( !::GetClipBox(GetHdc(), &rectClip
) )
466 // GetClipBox returns logical coordinates, so transform to device
467 rectClip
.left
= LogicalToDeviceX(rectClip
.left
);
468 rectClip
.top
= LogicalToDeviceY(rectClip
.top
);
469 rectClip
.right
= LogicalToDeviceX(rectClip
.right
);
470 rectClip
.bottom
= LogicalToDeviceY(rectClip
.bottom
);
472 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
473 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
474 rectClip
.right
, rectClip
.bottom
);
476 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
478 ::SelectClipRgn(GetHdc(), hrgnDest
);
481 ::DeleteObject(hrgnClipOld
);
482 ::DeleteObject(hrgnDest
);
484 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
486 wxLogLastError(_T("ExtSelectClipRgn"));
490 #endif // WinCE/!WinCE
497 void wxDC::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
499 // the region coords are always the device ones, so do the translation
502 // FIXME: possible +/-1 error here, to check!
503 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
505 LogicalToDeviceX(x
+ w
),
506 LogicalToDeviceY(y
+ h
));
509 wxLogLastError(_T("CreateRectRgn"));
513 SetClippingHrgn((WXHRGN
)hrgn
);
515 ::DeleteObject(hrgn
);
519 void wxDC::DoSetClippingRegionAsRegion(const wxRegion
& region
)
521 SetClippingHrgn(region
.GetHRGN());
524 void wxDC::DestroyClippingRegion()
528 if (m_clipping
&& m_hDC
)
531 // On a PocketPC device (not necessarily emulator), resetting
532 // the clip region as per the old method causes bad display
533 // problems. In fact setting a null region is probably OK
534 // on desktop WIN32 also, since the WIN32 docs imply that the user
535 // clipping region is independent from the paint clipping region.
536 ::SelectClipRgn(GetHdc(), 0);
538 // TODO: this should restore the previous clipping region,
539 // so that OnPaint processing works correctly, and the update
540 // clipping region doesn't get destroyed after the first
541 // DestroyClippingRegion.
542 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
543 ::SelectClipRgn(GetHdc(), rgn
);
548 wxDCBase::DestroyClippingRegion();
551 // ---------------------------------------------------------------------------
552 // query capabilities
553 // ---------------------------------------------------------------------------
555 bool wxDC::CanDrawBitmap() const
560 bool wxDC::CanGetTextExtent() const
562 #ifdef __WXMICROWIN__
563 // TODO Extend MicroWindows' GetDeviceCaps function
566 // What sort of display is it?
567 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
569 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
573 int wxDC::GetDepth() const
575 WXMICROWIN_CHECK_HDC_RET(16)
577 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
580 // ---------------------------------------------------------------------------
582 // ---------------------------------------------------------------------------
591 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
595 // No, I think we should simply ignore this if printing on e.g.
597 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
598 if (!m_selectedBitmap
.Ok())
601 rect
.left
= -m_deviceOriginX
; rect
.top
= -m_deviceOriginY
;
602 rect
.right
= m_selectedBitmap
.GetWidth()-m_deviceOriginX
;
603 rect
.bottom
= m_selectedBitmap
.GetHeight()-m_deviceOriginY
;
607 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
610 DWORD colour
= ::GetBkColor(GetHdc());
611 HBRUSH brush
= ::CreateSolidBrush(colour
);
612 ::FillRect(GetHdc(), &rect
, brush
);
613 ::DeleteObject(brush
);
615 RealizeScaleAndOrigin();
618 bool wxDC::DoFloodFill(wxCoord
WXUNUSED_IN_WINCE(x
),
619 wxCoord
WXUNUSED_IN_WINCE(y
),
620 const wxColour
& WXUNUSED_IN_WINCE(col
),
621 int WXUNUSED_IN_WINCE(style
))
626 WXMICROWIN_CHECK_HDC_RET(false)
628 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
630 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
631 : FLOODFILLBORDER
) ) ;
634 // quoting from the MSDN docs:
636 // Following are some of the reasons this function might fail:
638 // * The filling could not be completed.
639 // * The specified point has the boundary color specified by the
640 // crColor parameter (if FLOODFILLBORDER was requested).
641 // * The specified point does not have the color specified by
642 // crColor (if FLOODFILLSURFACE was requested)
643 // * The point is outside the clipping region that is, it is not
644 // visible on the device.
646 wxLogLastError(wxT("ExtFloodFill"));
649 CalcBoundingBox(x
, y
);
655 bool wxDC::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
657 WXMICROWIN_CHECK_HDC_RET(false)
659 wxCHECK_MSG( col
, false, _T("NULL colour parameter in wxDC::GetPixel") );
661 // get the color of the pixel
662 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
664 wxRGBToColour(*col
, pixelcolor
);
669 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
673 wxCoord x1
= x
-VIEWPORT_EXTENT
;
674 wxCoord y1
= y
-VIEWPORT_EXTENT
;
675 wxCoord x2
= x
+VIEWPORT_EXTENT
;
676 wxCoord y2
= y
+VIEWPORT_EXTENT
;
678 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
679 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
681 CalcBoundingBox(x1
, y1
);
682 CalcBoundingBox(x2
, y2
);
685 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
689 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
691 CalcBoundingBox(x1
, y1
);
692 CalcBoundingBox(x2
, y2
);
695 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
696 // and ending at (x2, y2)
697 void wxDC::DoDrawArc(wxCoord x1
, wxCoord y1
,
698 wxCoord x2
, wxCoord y2
,
699 wxCoord xc
, wxCoord yc
)
702 // Slower emulation since WinCE doesn't support Pie and Arc
703 double r
= sqrt( (x1
-xc
)*(x1
-xc
) + (y1
-yc
)*(y1
-yc
) );
704 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
705 if( y1
>yc
) sa
= -sa
; // below center
706 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
707 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
712 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
716 double radius
= (double)sqrt(dx
*dx
+dy
*dy
);
717 wxCoord r
= (wxCoord
)radius
;
719 // treat the special case of full circle separately
720 if ( x1
== x2
&& y1
== y2
)
722 DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
726 wxCoord xx1
= XLOG2DEV(x1
);
727 wxCoord yy1
= YLOG2DEV(y1
);
728 wxCoord xx2
= XLOG2DEV(x2
);
729 wxCoord yy2
= YLOG2DEV(y2
);
730 wxCoord xxc
= XLOG2DEV(xc
);
731 wxCoord yyc
= YLOG2DEV(yc
);
732 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
734 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
735 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
736 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
737 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
739 if ( m_brush
.Ok() && m_brush
.GetStyle() != wxTRANSPARENT
)
741 // Have to add 1 to bottom-right corner of rectangle
742 // to make semi-circles look right (crooked line otherwise).
743 // Unfortunately this is not a reliable method, depends
744 // on the size of shape.
745 // TODO: figure out why this happens!
746 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
750 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
753 CalcBoundingBox(xc
- r
, yc
- r
);
754 CalcBoundingBox(xc
+ r
, yc
+ r
);
758 void wxDC::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
759 wxCoord width
, wxCoord height
)
761 // cases when we don't have DrawFrameControl()
762 #if defined(__SYMANTEC__) || defined(__WXMICROWIN__)
763 return wxDCBase::DoDrawCheckMark(x1
, y1
, width
, height
);
765 wxCoord x2
= x1
+ width
,
775 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
);
777 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
780 CalcBoundingBox(x1
, y1
);
781 CalcBoundingBox(x2
, y2
);
782 #endif // Microwin/Normal
785 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
789 COLORREF color
= 0x00ffffff;
792 color
= m_pen
.GetColour().GetPixel();
795 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
797 CalcBoundingBox(x
, y
);
800 void wxDC::DoDrawPolygon(int n
,
804 int WXUNUSED_IN_WINCE(fillStyle
))
808 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
810 // Do things less efficiently if we have offsets
811 if (xoffset
!= 0 || yoffset
!= 0)
813 POINT
*cpoints
= new POINT
[n
];
815 for (i
= 0; i
< n
; i
++)
817 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
818 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
820 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
823 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
825 (void)Polygon(GetHdc(), cpoints
, n
);
827 SetPolyFillMode(GetHdc(),prev
);
834 for (i
= 0; i
< n
; i
++)
835 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
838 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
840 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
842 SetPolyFillMode(GetHdc(),prev
);
848 wxDC::DoDrawPolyPolygon(int n
,
856 wxDCBase::DoDrawPolyPolygon(n
, count
, points
, xoffset
, yoffset
, fillStyle
);
860 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
862 for (i
= cnt
= 0; i
< n
; i
++)
865 // Do things less efficiently if we have offsets
866 if (xoffset
!= 0 || yoffset
!= 0)
868 POINT
*cpoints
= new POINT
[cnt
];
869 for (i
= 0; i
< cnt
; i
++)
871 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
872 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
874 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
877 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
879 (void)PolyPolygon(GetHdc(), cpoints
, count
, n
);
881 SetPolyFillMode(GetHdc(),prev
);
887 for (i
= 0; i
< cnt
; i
++)
888 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
891 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
893 (void)PolyPolygon(GetHdc(), (POINT
*) points
, count
, n
);
895 SetPolyFillMode(GetHdc(),prev
);
902 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
906 // Do things less efficiently if we have offsets
907 if (xoffset
!= 0 || yoffset
!= 0)
909 POINT
*cpoints
= new POINT
[n
];
911 for (i
= 0; i
< n
; i
++)
913 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
914 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
916 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
918 (void)Polyline(GetHdc(), cpoints
, n
);
924 for (i
= 0; i
< n
; i
++)
925 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
927 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
931 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
935 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
937 wxCoord x2
= x
+ width
;
938 wxCoord y2
= y
+ height
;
940 if ((m_logicalFunction
== wxCOPY
) && (m_pen
.GetStyle() == wxTRANSPARENT
))
943 rect
.left
= XLOG2DEV(x
);
944 rect
.top
= YLOG2DEV(y
);
945 rect
.right
= XLOG2DEV(x2
);
946 rect
.bottom
= YLOG2DEV(y2
);
947 (void)FillRect(GetHdc(), &rect
, (HBRUSH
)m_brush
.GetResourceHandle() );
951 // Windows draws the filled rectangles without outline (i.e. drawn with a
952 // transparent pen) one pixel smaller in both directions and we want them
953 // to have the same size regardless of which pen is used - adjust
955 // I wonder if this shouldnt be done after the LOG2DEV() conversions. RR.
956 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
958 // Apparently not needed for WinCE (see e.g. Life! demo)
965 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
969 CalcBoundingBox(x
, y
);
970 CalcBoundingBox(x2
, y2
);
973 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
977 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
979 // Now, a negative radius value is interpreted to mean
980 // 'the proportion of the smallest X or Y dimension'
984 double smallest
= (width
< height
) ? width
: height
;
985 radius
= (- radius
* smallest
);
988 wxCoord x2
= (x
+width
);
989 wxCoord y2
= (y
+height
);
991 // Windows draws the filled rectangles without outline (i.e. drawn with a
992 // transparent pen) one pixel smaller in both directions and we want them
993 // to have the same size regardless of which pen is used - adjust
994 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
1000 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
1001 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
1003 CalcBoundingBox(x
, y
);
1004 CalcBoundingBox(x2
, y2
);
1007 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
1009 WXMICROWIN_CHECK_HDC
1011 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1013 wxCoord x2
= (x
+width
);
1014 wxCoord y2
= (y
+height
);
1016 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
1018 CalcBoundingBox(x
, y
);
1019 CalcBoundingBox(x2
, y2
);
1023 void wxDC::DoDrawSpline(const wxPointList
*points
)
1026 // WinCE does not support ::PolyBezier so use generic version
1027 wxDCBase::DoDrawSpline(points
);
1029 // quadratic b-spline to cubic bezier spline conversion
1031 // quadratic spline with control points P0,P1,P2
1032 // P(s) = P0*(1-s)^2 + P1*2*(1-s)*s + P2*s^2
1034 // bezier spline with control points B0,B1,B2,B3
1035 // B(s) = B0*(1-s)^3 + B1*3*(1-s)^2*s + B2*3*(1-s)*s^2 + B3*s^3
1037 // control points of bezier spline calculated from b-spline
1039 // B1 = (2*P1 + P0)/3
1040 // B2 = (2*P1 + P2)/3
1043 WXMICROWIN_CHECK_HDC
1045 wxASSERT_MSG( points
, wxT("NULL pointer to spline points?") );
1047 const size_t n_points
= points
->GetCount();
1048 wxASSERT_MSG( n_points
> 2 , wxT("incomplete list of spline points?") );
1050 const size_t n_bezier_points
= n_points
* 3 + 1;
1051 POINT
*lppt
= (POINT
*)malloc(n_bezier_points
*sizeof(POINT
));
1052 size_t bezier_pos
= 0;
1053 wxCoord x1
, y1
, x2
, y2
, cx1
, cy1
, cx4
, cy4
;
1055 wxPointList::compatibility_iterator node
= points
->GetFirst();
1056 wxPoint
*p
= node
->GetData();
1057 lppt
[ bezier_pos
].x
= x1
= p
->x
;
1058 lppt
[ bezier_pos
].y
= y1
= p
->y
;
1060 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1063 node
= node
->GetNext();
1064 p
= node
->GetData();
1068 cx1
= ( x1
+ x2
) / 2;
1069 cy1
= ( y1
+ y2
) / 2;
1070 lppt
[ bezier_pos
].x
= XLOG2DEV(cx1
);
1071 lppt
[ bezier_pos
].y
= YLOG2DEV(cy1
);
1073 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1077 while ((node
= node
->GetNext()) != NULL
)
1079 while ((node
= node
->GetNext()))
1080 #endif // !wxUSE_STL
1082 p
= (wxPoint
*)node
->GetData();
1087 cx4
= (x1
+ x2
) / 2;
1088 cy4
= (y1
+ y2
) / 2;
1089 // B0 is B3 of previous segment
1091 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx1
)/3);
1092 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy1
)/3);
1095 lppt
[ bezier_pos
].x
= XLOG2DEV((x1
*2+cx4
)/3);
1096 lppt
[ bezier_pos
].y
= YLOG2DEV((y1
*2+cy4
)/3);
1099 lppt
[ bezier_pos
].x
= XLOG2DEV(cx4
);
1100 lppt
[ bezier_pos
].y
= YLOG2DEV(cy4
);
1106 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1108 lppt
[ bezier_pos
].x
= XLOG2DEV(x2
);
1109 lppt
[ bezier_pos
].y
= YLOG2DEV(y2
);
1111 lppt
[ bezier_pos
] = lppt
[ bezier_pos
-1 ];
1114 ::PolyBezier( GetHdc(), lppt
, bezier_pos
);
1121 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
1122 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
1125 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
1128 WXMICROWIN_CHECK_HDC
1130 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
1135 int rx1
= XLOG2DEV(x
+w
/2);
1136 int ry1
= YLOG2DEV(y
+h
/2);
1143 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
1144 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
1145 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
1146 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
1148 // Swap start and end positions if the end angle is less than the start angle.
1159 // draw pie with NULL_PEN first and then outline otherwise a line is
1160 // drawn from the start and end points to the centre
1161 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
1164 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
1165 rx1
, ry1
, rx2
, ry2
);
1169 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
1170 rx1
, ry1
-1, rx2
, ry2
-1);
1173 ::SelectObject(GetHdc(), hpenOld
);
1175 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
1176 rx1
, ry1
, rx2
, ry2
);
1178 CalcBoundingBox(x
, y
);
1179 CalcBoundingBox(x2
, y2
);
1183 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
1185 WXMICROWIN_CHECK_HDC
1187 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
1190 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
1192 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
1195 CalcBoundingBox(x
, y
);
1196 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
1199 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
1201 WXMICROWIN_CHECK_HDC
1203 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
1205 int width
= bmp
.GetWidth(),
1206 height
= bmp
.GetHeight();
1208 HBITMAP hbmpMask
= 0;
1211 HPALETTE oldPal
= 0;
1212 #endif // wxUSE_PALETTE
1214 if ( bmp
.HasAlpha() )
1217 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1219 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, 0, 0, width
, height
, hdcMem
, bmp
) )
1225 wxMask
*mask
= bmp
.GetMask();
1227 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1231 // don't give assert here because this would break existing
1232 // programs - just silently ignore useMask parameter
1239 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1241 // On some systems, MaskBlt succeeds yet is much much slower
1242 // than the wxWidgets fall-back implementation. So we need
1243 // to be able to switch this on and off at runtime.
1245 #if wxUSE_SYSTEM_OPTIONS
1246 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1250 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1251 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1253 wxPalette
*pal
= bmp
.GetPalette();
1254 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1256 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1257 ::RealizePalette(hdcMem
);
1259 #endif // wxUSE_PALETTE
1261 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1264 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1268 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1269 #endif // wxUSE_PALETTE
1271 ::SelectObject(hdcMem
, hOldBitmap
);
1278 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1282 memDC
.SelectObjectAsSource(bmp
);
1284 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1286 memDC
.SelectObject(wxNullBitmap
);
1289 else // no mask, just use BitBlt()
1292 HDC memdc
= ::CreateCompatibleDC( cdc
);
1293 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1295 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1297 COLORREF old_textground
= ::GetTextColor(GetHdc());
1298 COLORREF old_background
= ::GetBkColor(GetHdc());
1299 if (m_textForegroundColour
.Ok())
1301 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1303 if (m_textBackgroundColour
.Ok())
1305 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1309 wxPalette
*pal
= bmp
.GetPalette();
1310 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1312 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1313 ::RealizePalette(memdc
);
1315 #endif // wxUSE_PALETTE
1317 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1318 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1322 ::SelectPalette(memdc
, oldPal
, FALSE
);
1323 #endif // wxUSE_PALETTE
1325 ::SelectObject( memdc
, hOldBitmap
);
1326 ::DeleteDC( memdc
);
1328 ::SetTextColor(GetHdc(), old_textground
);
1329 ::SetBkColor(GetHdc(), old_background
);
1333 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1335 WXMICROWIN_CHECK_HDC
1337 DrawAnyText(text
, x
, y
);
1339 // update the bounding box
1340 CalcBoundingBox(x
, y
);
1343 GetTextExtent(text
, &w
, &h
);
1344 CalcBoundingBox(x
+ w
, y
+ h
);
1347 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1349 WXMICROWIN_CHECK_HDC
1351 // prepare for drawing the text
1352 if ( m_textForegroundColour
.Ok() )
1353 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
1355 DWORD old_background
= 0;
1356 if ( m_textBackgroundColour
.Ok() )
1358 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1361 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
1365 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1366 text
.c_str(), text
.length(), NULL
) == 0 )
1368 wxLogLastError(wxT("TextOut"));
1371 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
1372 text
.c_str(), text
.length()) == 0 )
1374 wxLogLastError(wxT("TextOut"));
1378 // restore the old parameters (text foreground colour may be left because
1379 // it never is set to anything else, but background should remain
1380 // transparent even if we just drew an opaque string)
1381 if ( m_textBackgroundColour
.Ok() )
1382 (void)SetBkColor(GetHdc(), old_background
);
1384 SetBkMode(GetHdc(), TRANSPARENT
);
1387 void wxDC::DoDrawRotatedText(const wxString
& text
,
1388 wxCoord x
, wxCoord y
,
1391 WXMICROWIN_CHECK_HDC
1393 // we test that we have some font because otherwise we should still use the
1394 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1395 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1396 // font for drawing rotated fonts unfortunately)
1397 if ( (angle
== 0.0) && m_font
.Ok() )
1399 DoDrawText(text
, x
, y
);
1401 #ifndef __WXMICROWIN__
1404 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1405 // because it's not TrueType and so can't have non zero
1406 // orientation/escapement under Win9x
1407 wxFont font
= m_font
.Ok() ? m_font
: *wxSWISS_FONT
;
1408 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1410 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1412 wxLogLastError(wxT("GetObject(hfont)"));
1415 // GDI wants the angle in tenth of degree
1416 long angle10
= (long)(angle
* 10);
1417 lf
.lfEscapement
= angle10
;
1418 lf
. lfOrientation
= angle10
;
1420 hfont
= ::CreateFontIndirect(&lf
);
1423 wxLogLastError(wxT("CreateFont"));
1427 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1429 DrawAnyText(text
, x
, y
);
1431 (void)::SelectObject(GetHdc(), hfontOld
);
1432 (void)::DeleteObject(hfont
);
1435 // call the bounding box by adding all four vertices of the rectangle
1436 // containing the text to it (simpler and probably not slower than
1437 // determining which of them is really topmost/leftmost/...)
1439 GetTextExtent(text
, &w
, &h
);
1441 double rad
= DegToRad(angle
);
1443 // "upper left" and "upper right"
1444 CalcBoundingBox(x
, y
);
1445 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1447 // "bottom left" and "bottom right"
1448 x
+= (wxCoord
)(h
*sin(rad
));
1449 y
+= (wxCoord
)(h
*cos(rad
));
1450 CalcBoundingBox(x
, y
);
1451 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(w
*sin(rad
)));
1456 // ---------------------------------------------------------------------------
1458 // ---------------------------------------------------------------------------
1462 void wxDC::DoSelectPalette(bool realize
)
1464 WXMICROWIN_CHECK_HDC
1466 // Set the old object temporarily, in case the assignment deletes an object
1467 // that's not yet selected out.
1470 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1474 if ( m_palette
.Ok() )
1476 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1477 GetHpaletteOf(m_palette
),
1480 m_oldPalette
= (WXHPALETTE
) oldPal
;
1483 ::RealizePalette(GetHdc());
1487 void wxDC::SetPalette(const wxPalette
& palette
)
1491 m_palette
= palette
;
1492 DoSelectPalette(true);
1496 void wxDC::InitializePalette()
1498 if ( wxDisplayDepth() <= 8 )
1500 // look for any window or parent that has a custom palette. If any has
1501 // one then we need to use it in drawing operations
1502 wxWindow
*win
= m_canvas
->GetAncestorWithCustomPalette();
1504 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1505 if ( m_hasCustomPalette
)
1507 m_palette
= win
->GetPalette();
1509 // turn on MSW translation for this palette
1515 #endif // wxUSE_PALETTE
1517 // SetFont/Pen/Brush() really ask to be implemented as a single template
1518 // function... but doing it is not worth breaking OpenWatcom build <sigh>
1520 void wxDC::SetFont(const wxFont
& font
)
1522 WXMICROWIN_CHECK_HDC
1524 if ( font
== m_font
)
1529 HGDIOBJ hfont
= ::SelectObject(GetHdc(), GetHfontOf(font
));
1530 if ( hfont
== HGDI_ERROR
)
1532 wxLogLastError(_T("SelectObject(font)"));
1537 m_oldFont
= (WXHFONT
)hfont
;
1542 else // invalid font, reset the current font
1546 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldFont
) == HGDI_ERROR
)
1548 wxLogLastError(_T("SelectObject(old font)"));
1554 m_font
= wxNullFont
;
1558 void wxDC::SetPen(const wxPen
& pen
)
1560 WXMICROWIN_CHECK_HDC
1567 HGDIOBJ hpen
= ::SelectObject(GetHdc(), GetHpenOf(pen
));
1568 if ( hpen
== HGDI_ERROR
)
1570 wxLogLastError(_T("SelectObject(pen)"));
1575 m_oldPen
= (WXHPEN
)hpen
;
1580 else // invalid pen, reset the current pen
1584 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldPen
) == HGDI_ERROR
)
1586 wxLogLastError(_T("SelectObject(old pen)"));
1596 void wxDC::SetBrush(const wxBrush
& brush
)
1598 WXMICROWIN_CHECK_HDC
1600 if ( brush
== m_brush
)
1605 // we must make sure the brush is aligned with the logical coordinates
1606 // before selecting it
1607 wxBitmap
*stipple
= brush
.GetStipple();
1608 if ( stipple
&& stipple
->Ok() )
1610 if ( !::SetBrushOrgEx
1613 m_deviceOriginX
% stipple
->GetWidth(),
1614 m_deviceOriginY
% stipple
->GetHeight(),
1615 NULL
// [out] previous brush origin
1618 wxLogLastError(_T("SetBrushOrgEx()"));
1622 HGDIOBJ hbrush
= ::SelectObject(GetHdc(), GetHbrushOf(brush
));
1623 if ( hbrush
== HGDI_ERROR
)
1625 wxLogLastError(_T("SelectObject(brush)"));
1630 m_oldBrush
= (WXHBRUSH
)hbrush
;
1635 else // invalid brush, reset the current brush
1639 if ( ::SelectObject(GetHdc(), (HPEN
) m_oldBrush
) == HGDI_ERROR
)
1641 wxLogLastError(_T("SelectObject(old brush)"));
1647 m_brush
= wxNullBrush
;
1651 void wxDC::SetBackground(const wxBrush
& brush
)
1653 WXMICROWIN_CHECK_HDC
1655 m_backgroundBrush
= brush
;
1657 if ( m_backgroundBrush
.Ok() )
1659 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1663 void wxDC::SetBackgroundMode(int mode
)
1665 WXMICROWIN_CHECK_HDC
1667 m_backgroundMode
= mode
;
1669 // SetBackgroundColour now only refers to text background
1670 // and m_backgroundMode is used there
1673 void wxDC::SetLogicalFunction(int function
)
1675 WXMICROWIN_CHECK_HDC
1677 m_logicalFunction
= function
;
1682 void wxDC::SetRop(WXHDC dc
)
1684 if ( !dc
|| m_logicalFunction
< 0 )
1689 switch (m_logicalFunction
)
1691 case wxCLEAR
: rop
= R2_BLACK
; break;
1692 case wxXOR
: rop
= R2_XORPEN
; break;
1693 case wxINVERT
: rop
= R2_NOT
; break;
1694 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1695 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1696 case wxCOPY
: rop
= R2_COPYPEN
; break;
1697 case wxAND
: rop
= R2_MASKPEN
; break;
1698 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1699 case wxNO_OP
: rop
= R2_NOP
; break;
1700 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1701 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1702 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1703 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1704 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1705 case wxOR
: rop
= R2_MERGEPEN
; break;
1706 case wxSET
: rop
= R2_WHITE
; break;
1709 wxFAIL_MSG( wxT("unsupported logical function") );
1713 SetROP2(GetHdc(), rop
);
1716 bool wxDC::StartDoc(const wxString
& WXUNUSED(message
))
1718 // We might be previewing, so return true to let it continue.
1726 void wxDC::StartPage()
1730 void wxDC::EndPage()
1734 // ---------------------------------------------------------------------------
1736 // ---------------------------------------------------------------------------
1738 wxCoord
wxDC::GetCharHeight() const
1740 WXMICROWIN_CHECK_HDC_RET(0)
1742 TEXTMETRIC lpTextMetric
;
1744 GetTextMetrics(GetHdc(), &lpTextMetric
);
1746 return lpTextMetric
.tmHeight
;
1749 wxCoord
wxDC::GetCharWidth() const
1751 WXMICROWIN_CHECK_HDC_RET(0)
1753 TEXTMETRIC lpTextMetric
;
1755 GetTextMetrics(GetHdc(), &lpTextMetric
);
1757 return lpTextMetric
.tmAveCharWidth
;
1760 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1761 wxCoord
*descent
, wxCoord
*externalLeading
,
1762 const wxFont
*font
) const
1764 #ifdef __WXMICROWIN__
1769 if (descent
) *descent
= 0;
1770 if (externalLeading
) *externalLeading
= 0;
1773 #endif // __WXMICROWIN__
1778 wxASSERT_MSG( font
->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1780 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1782 else // don't change the font
1788 const size_t len
= string
.length();
1789 if ( !::GetTextExtentPoint32(GetHdc(), string
.wx_str(), len
, &sizeRect
) )
1791 wxLogLastError(_T("GetTextExtentPoint32()"));
1794 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1795 // the result computed by GetTextExtentPoint32() may be too small as it
1796 // accounts for under/overhang of the first/last character while we want
1797 // just the bounding rect for this string so adjust the width as needed
1798 // (using API not available in 2002 SDKs of WinCE)
1802 const wxChar chFirst
= *string
.begin();
1803 if ( ::GetCharABCWidths(GetHdc(), chFirst
, chFirst
, &width
) )
1805 if ( width
.abcA
< 0 )
1806 sizeRect
.cx
-= width
.abcA
;
1810 const wxChar chLast
= *string
.rbegin();
1811 ::GetCharABCWidths(GetHdc(), chLast
, chLast
, &width
);
1813 //else: we already have the width of the last character
1815 if ( width
.abcC
< 0 )
1816 sizeRect
.cx
-= width
.abcC
;
1818 //else: GetCharABCWidths() failed, not a TrueType font?
1820 #endif // !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
1823 ::GetTextMetrics(GetHdc(), &tm
);
1830 *descent
= tm
.tmDescent
;
1831 if (externalLeading
)
1832 *externalLeading
= tm
.tmExternalLeading
;
1836 ::SelectObject(GetHdc(), hfontOld
);
1841 // Each element of the array will be the width of the string up to and
1842 // including the coresoponding character in text.
1844 bool wxDC::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
1846 static int maxLenText
= -1;
1847 static int maxWidth
= -1;
1850 int stlen
= text
.length();
1852 if (maxLenText
== -1)
1854 // Win9x and WinNT+ have different limits
1855 int version
= wxGetOsVersion();
1856 maxLenText
= version
== wxOS_WINDOWS_NT
? 65535 : 8192;
1857 maxWidth
= version
== wxOS_WINDOWS_NT
? INT_MAX
: 32767;
1861 widths
.Add(0, stlen
); // fill the array with zeros
1865 if (!::GetTextExtentExPoint(GetHdc(),
1866 text
.c_str(), // string to check
1867 wxMin(stlen
, maxLenText
),
1869 &fit
, // [out] count of chars
1871 &widths
[0], // array to fill
1875 wxLogLastError(wxT("GetTextExtentExPoint"));
1882 void wxDC::RealizeScaleAndOrigin()
1884 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1885 // cases we could do with MM_TEXT and in the remaining 0.9% with
1886 // MM_ISOTROPIC (TODO!)
1888 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1890 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1891 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1893 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1894 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1896 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1897 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1902 void wxDC::SetMapMode(int mode
)
1904 WXMICROWIN_CHECK_HDC
1906 m_mappingMode
= mode
;
1908 if ( mode
== wxMM_TEXT
)
1911 m_logicalScaleY
= 1.0;
1913 else // need to do some calculations
1915 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1916 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1917 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1918 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1920 if ( (mm_width
== 0) || (mm_height
== 0) )
1922 // we can't calculate mm2pixels[XY] then!
1926 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1927 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1932 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1933 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1937 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1938 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1942 m_logicalScaleX
= mm2pixelsX
;
1943 m_logicalScaleY
= mm2pixelsY
;
1947 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1948 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1952 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1956 ComputeScaleAndOrigin();
1958 RealizeScaleAndOrigin();
1961 void wxDC::SetUserScale(double x
, double y
)
1963 WXMICROWIN_CHECK_HDC
1965 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1968 wxDCBase::SetUserScale(x
,y
);
1970 RealizeScaleAndOrigin();
1973 void wxDC::SetAxisOrientation(bool xLeftRight
,
1976 WXMICROWIN_CHECK_HDC
1978 int signX
= xLeftRight
? 1 : -1,
1979 signY
= yBottomUp
? -1 : 1;
1981 if (signX
== m_signX
&& signY
== m_signY
)
1984 wxDCBase::SetAxisOrientation( xLeftRight
, yBottomUp
);
1986 RealizeScaleAndOrigin();
1989 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1991 WXMICROWIN_CHECK_HDC
1993 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1996 wxDCBase::SetLogicalOrigin( x
, y
);
1999 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
2003 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
2005 WXMICROWIN_CHECK_HDC
2007 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
2010 wxDCBase::SetDeviceOrigin( x
, y
);
2012 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
2015 // ---------------------------------------------------------------------------
2017 // ---------------------------------------------------------------------------
2019 bool wxDC::DoBlit(wxCoord dstX
, wxCoord dstY
,
2020 wxCoord dstWidth
, wxCoord dstHeight
,
2022 wxCoord srcX
, wxCoord srcY
,
2023 int rop
, bool useMask
,
2024 wxCoord srcMaskX
, wxCoord srcMaskY
)
2026 return DoStretchBlit(dstX
, dstY
, dstWidth
, dstHeight
, source
, srcX
, srcY
, dstWidth
, dstHeight
, rop
, useMask
, srcMaskX
, srcMaskY
);
2029 bool wxDC::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
2030 wxCoord dstWidth
, wxCoord dstHeight
,
2032 wxCoord xsrc
, wxCoord ysrc
,
2033 wxCoord srcWidth
, wxCoord srcHeight
,
2034 int rop
, bool useMask
,
2035 wxCoord xsrcMask
, wxCoord ysrcMask
)
2037 wxCHECK_MSG( source
, false, _T("wxDC::Blit(): NULL wxDC pointer") );
2039 WXMICROWIN_CHECK_HDC_RET(false)
2041 // if either the source or destination has alpha channel, we must use
2042 // AlphaBlt() as other function don't handle it correctly
2043 const wxBitmap
& bmpSrc
= source
->m_selectedBitmap
;
2044 if ( bmpSrc
.Ok() && (bmpSrc
.HasAlpha() ||
2045 (m_selectedBitmap
.Ok() && m_selectedBitmap
.HasAlpha())) )
2047 if ( AlphaBlt(GetHdc(), xdest
, ydest
, dstWidth
, dstHeight
,
2048 xsrc
, ysrc
, srcWidth
, srcHeight
, GetHdcOf(*source
), bmpSrc
) )
2052 wxMask
*mask
= NULL
;
2055 mask
= bmpSrc
.GetMask();
2057 if ( !(bmpSrc
.Ok() && mask
&& mask
->GetMaskBitmap()) )
2059 // don't give assert here because this would break existing
2060 // programs - just silently ignore useMask parameter
2065 if (xsrcMask
== -1 && ysrcMask
== -1)
2067 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
2070 COLORREF old_textground
= ::GetTextColor(GetHdc());
2071 COLORREF old_background
= ::GetBkColor(GetHdc());
2072 if (m_textForegroundColour
.Ok())
2074 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
2076 if (m_textBackgroundColour
.Ok())
2078 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
2084 case wxXOR
: dwRop
= SRCINVERT
; break;
2085 case wxINVERT
: dwRop
= DSTINVERT
; break;
2086 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
2087 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
2088 case wxCLEAR
: dwRop
= BLACKNESS
; break;
2089 case wxSET
: dwRop
= WHITENESS
; break;
2090 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
2091 case wxAND
: dwRop
= SRCAND
; break;
2092 case wxOR
: dwRop
= SRCPAINT
; break;
2093 case wxEQUIV
: dwRop
= 0x00990066; break;
2094 case wxNAND
: dwRop
= 0x007700E6; break;
2095 case wxAND_INVERT
: dwRop
= 0x00220326; break;
2096 case wxCOPY
: dwRop
= SRCCOPY
; break;
2097 case wxNO_OP
: dwRop
= DSTCOPY
; break;
2098 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
2099 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
2101 wxFAIL_MSG( wxT("unsupported logical function") );
2105 bool success
= false;
2110 // we want the part of the image corresponding to the mask to be
2111 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
2112 // meaning of fg and bg is inverted which corresponds to wxWin notion
2113 // of the mask which is also contrary to the Windows one)
2115 // On some systems, MaskBlt succeeds yet is much much slower
2116 // than the wxWidgets fall-back implementation. So we need
2117 // to be able to switch this on and off at runtime.
2118 #if wxUSE_SYSTEM_OPTIONS
2119 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
2122 if ( dstWidth
== srcWidth
&& dstHeight
== srcHeight
)
2127 xdest
, ydest
, dstWidth
, dstHeight
,
2130 (HBITMAP
)mask
->GetMaskBitmap(),
2132 MAKEROP4(dwRop
, DSTCOPY
)
2140 // Blit bitmap with mask
2143 HBITMAP buffer_bmap
;
2145 #if wxUSE_DC_CACHEING
2146 // create a temp buffer bitmap and DCs to access it and the mask
2147 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, source
->GetHDC());
2148 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
2150 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
2151 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
2153 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
2154 dstWidth
, dstHeight
);
2156 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
2157 #else // !wxUSE_DC_CACHEING
2158 // create a temp buffer bitmap and DCs to access it and the mask
2159 dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
2160 dc_buffer
= ::CreateCompatibleDC(GetHdc());
2161 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), dstWidth
, dstHeight
);
2162 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
2163 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
2164 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
2166 // copy dest to buffer
2167 if ( !::BitBlt(dc_buffer
, 0, 0, (int)dstWidth
, (int)dstHeight
,
2168 GetHdc(), xdest
, ydest
, SRCCOPY
) )
2170 wxLogLastError(wxT("BitBlt"));
2174 StretchBltModeChanger
changeMode(dc_buffer
, COLORONCOLOR
);
2177 // copy src to buffer using selected raster op
2178 if ( !::StretchBlt(dc_buffer
, 0, 0, (int)dstWidth
, (int)dstHeight
,
2179 GetHdcOf(*source
), xsrc
, ysrc
, srcWidth
, srcHeight
, dwRop
) )
2181 wxLogLastError(wxT("StretchBlt"));
2184 // set masked area in buffer to BLACK (pixel value 0)
2185 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
2186 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
2187 if ( !::StretchBlt(dc_buffer
, 0, 0, (int)dstWidth
, (int)dstHeight
,
2188 dc_mask
, xsrcMask
, ysrcMask
, srcWidth
, srcHeight
, SRCAND
) )
2190 wxLogLastError(wxT("StretchBlt"));
2193 // set unmasked area in dest to BLACK
2194 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
2195 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
2196 if ( !::StretchBlt(GetHdc(), xdest
, ydest
, (int)dstWidth
, (int)dstHeight
,
2197 dc_mask
, xsrcMask
, ysrcMask
, srcWidth
, srcHeight
, SRCAND
) )
2199 wxLogLastError(wxT("StretchBlt"));
2201 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
2202 ::SetTextColor(GetHdc(), prevCol
);
2204 // OR buffer to dest
2205 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
2206 (int)dstWidth
, (int)dstHeight
,
2207 dc_buffer
, 0, 0, SRCPAINT
) != 0;
2210 wxLogLastError(wxT("BitBlt"));
2213 // tidy up temporary DCs and bitmap
2214 ::SelectObject(dc_mask
, hOldMaskBitmap
);
2215 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
2217 #if !wxUSE_DC_CACHEING
2219 ::DeleteDC(dc_mask
);
2220 ::DeleteDC(dc_buffer
);
2221 ::DeleteObject(buffer_bmap
);
2226 else // no mask, just BitBlt() it
2228 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2229 // use StretchBlt() if available and finally fall back to BitBlt()
2231 // FIXME: use appropriate WinCE functions
2233 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2234 if ( bmpSrc
.Ok() && (caps
& RC_STRETCHDIB
) )
2239 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2241 &ds
) == sizeof(ds
) )
2243 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2245 // Figure out what co-ordinate system we're supposed to specify
2247 const LONG hDIB
= ds
.dsBmih
.biHeight
;
2251 ysrc
= hDIB
- (ysrc
+ dstHeight
);
2254 if ( ::StretchDIBits(GetHdc(),
2256 dstWidth
, dstHeight
,
2258 srcWidth
, srcHeight
,
2260 (LPBITMAPINFO
)&ds
.dsBmih
,
2263 ) == (int)GDI_ERROR
)
2265 // On Win9x this API fails most (all?) of the time, so
2266 // logging it becomes quite distracting. Since it falls
2267 // back to the code below this is not really serious, so
2269 //wxLogLastError(wxT("StretchDIBits"));
2278 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2283 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2289 xdest
, ydest
, dstWidth
, dstHeight
,
2291 xsrc
, ysrc
, srcWidth
, srcHeight
,
2295 wxLogLastError(_T("StretchBlt"));
2309 (int)dstWidth
, (int)dstHeight
,
2315 wxLogLastError(_T("BitBlt"));
2324 ::SetTextColor(GetHdc(), old_textground
);
2325 ::SetBkColor(GetHdc(), old_background
);
2330 void wxDC::GetDeviceSize(int *width
, int *height
) const
2332 WXMICROWIN_CHECK_HDC
2335 *width
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2337 *height
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2340 void wxDC::DoGetSizeMM(int *w
, int *h
) const
2342 WXMICROWIN_CHECK_HDC
2344 // if we implement it in terms of DoGetSize() instead of directly using the
2345 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2346 // will also work for wxWindowDC and wxClientDC even though their size is
2347 // not the same as the total size of the screen
2348 int wPixels
, hPixels
;
2349 DoGetSize(&wPixels
, &hPixels
);
2353 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2355 wxCHECK_RET( wTotal
, _T("0 width device?") );
2357 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2362 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2364 wxCHECK_RET( hTotal
, _T("0 height device?") );
2366 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2370 wxSize
wxDC::GetPPI() const
2372 WXMICROWIN_CHECK_HDC_RET(wxSize(0,0))
2374 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2375 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2377 return wxSize(x
, y
);
2380 // For use by wxWidgets only, unless custom units are required.
2381 void wxDC::SetLogicalScale(double x
, double y
)
2383 WXMICROWIN_CHECK_HDC
2385 wxDCBase::SetLogicalScale(x
,y
);
2388 // ----------------------------------------------------------------------------
2390 // ----------------------------------------------------------------------------
2392 #if wxUSE_DC_CACHEING
2395 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2396 * improve it in due course, either using arrays, or simply storing pointers to one
2397 * entry for the bitmap, and two for the DCs. -- JACS
2400 wxList
wxDC::sm_bitmapCache
;
2401 wxList
wxDC::sm_dcCache
;
2403 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2412 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2421 wxDCCacheEntry::~wxDCCacheEntry()
2424 ::DeleteObject((HBITMAP
) m_bitmap
);
2426 ::DeleteDC((HDC
) m_dc
);
2429 wxDCCacheEntry
* wxDC::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2431 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2432 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2435 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2437 if (entry
->m_depth
== depth
)
2439 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2441 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2442 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2443 if ( !entry
->m_bitmap
)
2445 wxLogLastError(wxT("CreateCompatibleBitmap"));
2447 entry
->m_width
= w
; entry
->m_height
= h
;
2453 node
= node
->GetNext();
2455 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2458 wxLogLastError(wxT("CreateCompatibleBitmap"));
2460 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2461 AddToBitmapCache(entry
);
2465 wxDCCacheEntry
* wxDC::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2467 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2468 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2471 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2473 // Don't return the same one as we already have
2474 if (!notThis
|| (notThis
!= entry
))
2476 if (entry
->m_depth
== depth
)
2482 node
= node
->GetNext();
2484 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2487 wxLogLastError(wxT("CreateCompatibleDC"));
2489 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2490 AddToDCCache(entry
);
2494 void wxDC::AddToBitmapCache(wxDCCacheEntry
* entry
)
2496 sm_bitmapCache
.Append(entry
);
2499 void wxDC::AddToDCCache(wxDCCacheEntry
* entry
)
2501 sm_dcCache
.Append(entry
);
2504 void wxDC::ClearCache()
2506 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2507 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2510 // Clean up cache at app exit
2511 class wxDCModule
: public wxModule
2514 virtual bool OnInit() { return true; }
2515 virtual void OnExit() { wxDC::ClearCache(); }
2518 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2521 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2523 #endif // wxUSE_DC_CACHEING
2525 // ----------------------------------------------------------------------------
2526 // alpha channel support
2527 // ----------------------------------------------------------------------------
2529 static bool AlphaBlt(HDC hdcDst
,
2530 int x
, int y
, int dstWidth
, int dstHeight
,
2532 int srcWidth
, int srcHeight
,
2534 const wxBitmap
& bmp
)
2536 wxASSERT_MSG( bmp
.Ok() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2537 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2539 // do we have AlphaBlend() and company in the headers?
2540 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2541 // yes, now try to see if we have it during run-time
2542 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2543 HDC
,int,int,int,int,
2547 pfnAlphaBlend
= (AlphaBlend_t
)wxMSIMG32DLL
.GetSymbol(_T("AlphaBlend"));
2548 if ( pfnAlphaBlend
)
2551 bf
.BlendOp
= AC_SRC_OVER
;
2553 bf
.SourceConstantAlpha
= 0xff;
2554 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2556 if ( pfnAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
,
2557 hdcSrc
, srcX
, srcY
, srcWidth
, srcHeight
,
2560 // skip wxAlphaBlend() call below
2564 wxLogLastError(_T("AlphaBlend"));
2567 wxUnusedVar(hdcSrc
);
2568 #endif // defined(AC_SRC_OVER)
2570 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2572 #ifdef wxHAVE_RAW_BITMAP
2573 wxAlphaBlend(hdcDst
, x
, y
, dstWidth
, dstHeight
, srcX
, srcY
, srcWidth
, srcHeight
, bmp
);
2576 #else // !wxHAVE_RAW_BITMAP
2577 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2578 // alpha but at least something will be shown like this)
2581 #endif // wxHAVE_RAW_BITMAP
2585 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2586 #ifdef wxHAVE_RAW_BITMAP
2589 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
,
2590 int dstWidth
, int dstHeight
,
2592 int srcWidth
, int srcHeight
,
2593 const wxBitmap
& bmpSrc
)
2595 // get the destination DC pixels
2596 wxBitmap
bmpDst(dstWidth
, dstHeight
, 32 /* force creating RGBA DIB */);
2598 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2600 if ( !::BitBlt(hdcMem
, 0, 0, dstWidth
, dstHeight
, hdcDst
, xDst
, yDst
, SRCCOPY
) )
2602 wxLogLastError(_T("BitBlt"));
2605 // combine them with the source bitmap using alpha
2606 wxAlphaPixelData
dataDst(bmpDst
),
2607 dataSrc((wxBitmap
&)bmpSrc
);
2609 wxCHECK_RET( dataDst
&& dataSrc
,
2610 _T("failed to get raw data in wxAlphaBlend") );
2612 wxAlphaPixelData::Iterator
pDst(dataDst
),
2616 for ( int y
= 0; y
< dstHeight
; y
++ )
2618 wxAlphaPixelData::Iterator pDstRowStart
= pDst
;
2620 for ( int x
= 0; x
< dstWidth
; x
++ )
2622 // source is point sampled, Alpha StretchBlit is ugly on Win95
2623 // (but does not impact performance)
2624 pSrc
.MoveTo(dataSrc
, srcX
+ (srcWidth
*x
/dstWidth
), srcY
+ (srcHeight
*y
/dstHeight
));
2626 // note that source bitmap uses premultiplied alpha (as required by
2627 // the real AlphaBlend)
2628 const unsigned beta
= 255 - pSrc
.Alpha();
2630 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2631 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2632 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2637 pDst
= pDstRowStart
;
2638 pDst
.OffsetY(dataDst
, 1);
2641 // and finally blit them back to the destination DC
2642 if ( !::BitBlt(hdcDst
, xDst
, yDst
, dstWidth
, dstHeight
, hdcMem
, 0, 0, SRCCOPY
) )
2644 wxLogLastError(_T("BitBlt"));
2648 #endif // #ifdef wxHAVE_RAW_BITMAP
2650 void wxDC::DoGradientFillLinear (const wxRect
& rect
,
2651 const wxColour
& initialColour
,
2652 const wxColour
& destColour
,
2653 wxDirection nDirection
)
2655 // use native function if we have compile-time support it and can load it
2656 // during run-time (linking to it statically would make the program
2657 // unusable on earlier Windows versions)
2658 #if defined(GRADIENT_FILL_RECT_H) && wxUSE_DYNLIB_CLASS
2660 (WINAPI
*GradientFill_t
)(HDC
, PTRIVERTEX
, ULONG
, PVOID
, ULONG
, ULONG
);
2661 static GradientFill_t pfnGradientFill
=
2662 (GradientFill_t
)wxMSIMG32DLL
.GetSymbol(_T("GradientFill"));
2664 if ( pfnGradientFill
)
2666 GRADIENT_RECT grect
;
2667 grect
.UpperLeft
= 0;
2668 grect
.LowerRight
= 1;
2670 // invert colours direction if not filling from left-to-right or
2672 int firstVertex
= nDirection
== wxNORTH
|| nDirection
== wxWEST
? 1 : 0;
2674 // one vertex for upper left and one for upper-right
2675 TRIVERTEX vertices
[2];
2677 vertices
[0].x
= rect
.GetLeft();
2678 vertices
[0].y
= rect
.GetTop();
2679 vertices
[1].x
= rect
.GetRight()+1;
2680 vertices
[1].y
= rect
.GetBottom()+1;
2682 vertices
[firstVertex
].Red
= (COLOR16
)(initialColour
.Red() << 8);
2683 vertices
[firstVertex
].Green
= (COLOR16
)(initialColour
.Green() << 8);
2684 vertices
[firstVertex
].Blue
= (COLOR16
)(initialColour
.Blue() << 8);
2685 vertices
[firstVertex
].Alpha
= 0;
2686 vertices
[1 - firstVertex
].Red
= (COLOR16
)(destColour
.Red() << 8);
2687 vertices
[1 - firstVertex
].Green
= (COLOR16
)(destColour
.Green() << 8);
2688 vertices
[1 - firstVertex
].Blue
= (COLOR16
)(destColour
.Blue() << 8);
2689 vertices
[1 - firstVertex
].Alpha
= 0;
2691 if ( (*pfnGradientFill
)
2698 nDirection
== wxWEST
|| nDirection
== wxEAST
2699 ? GRADIENT_FILL_RECT_H
2700 : GRADIENT_FILL_RECT_V
2703 // skip call of the base class version below
2707 wxLogLastError(_T("GradientFill"));
2709 #endif // wxUSE_DYNLIB_CLASS
2711 wxDCBase::DoGradientFillLinear(rect
, initialColour
, destColour
, nDirection
);
2714 #if wxUSE_DYNLIB_CLASS
2716 static DWORD
wxGetDCLayout(HDC hdc
)
2718 typedef DWORD (WINAPI
*GetLayout_t
)(HDC
);
2720 wxDL_INIT_FUNC(s_pfn
, GetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2722 return s_pfnGetLayout
? s_pfnGetLayout(hdc
) : (DWORD
)-1;
2725 wxLayoutDirection
wxDC::GetLayoutDirection() const
2727 DWORD layout
= wxGetDCLayout(GetHdc());
2729 if ( layout
== (DWORD
)-1 )
2730 return wxLayout_Default
;
2732 return layout
& LAYOUT_RTL
? wxLayout_RightToLeft
: wxLayout_LeftToRight
;
2735 void wxDC::SetLayoutDirection(wxLayoutDirection dir
)
2737 typedef DWORD (WINAPI
*SetLayout_t
)(HDC
, DWORD
);
2739 wxDL_INIT_FUNC(s_pfn
, SetLayout
, wxDynamicLibrary(_T("gdi32.dll")));
2740 if ( !s_pfnSetLayout
)
2743 if ( dir
== wxLayout_Default
)
2745 dir
= wxTheApp
->GetLayoutDirection();
2746 if ( dir
== wxLayout_Default
)
2750 DWORD layout
= wxGetDCLayout(GetHdc());
2751 if ( dir
== wxLayout_RightToLeft
)
2752 layout
|= LAYOUT_RTL
;
2754 layout
&= ~LAYOUT_RTL
;
2756 s_pfnSetLayout(GetHdc(), layout
);
2759 #else // !wxUSE_DYNLIB_CLASS
2761 // we can't provide RTL support without dynamic loading, so stub it out
2762 wxLayoutDirection
wxDC::GetLayoutDirection() const
2764 return wxLayout_Default
;
2767 void wxDC::SetLayoutDirection(wxLayoutDirection
WXUNUSED(dir
))
2771 #endif // wxUSE_DYNLIB_CLASS/!wxUSE_DYNLIB_CLASS