1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
21 #pragma implementation "dc.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
32 #include "wx/window.h"
35 #include "wx/dialog.h"
37 #include "wx/bitmap.h"
38 #include "wx/dcmemory.h"
43 #include "wx/sysopt.h"
44 #include "wx/dcprint.h"
45 #include "wx/module.h"
46 #include "wx/dynload.h"
47 #include "wx/rawbmp.h"
52 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
54 #if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__)
63 #define AC_SRC_ALPHA 1
66 /* Quaternary raster codes */
68 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
71 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxDCBase
)
73 // ---------------------------------------------------------------------------
75 // ---------------------------------------------------------------------------
77 static const int VIEWPORT_EXTENT
= 1000;
79 static const int MM_POINTS
= 9;
80 static const int MM_METRIC
= 10;
82 // usually this is defined in math.h
84 static const double M_PI
= 3.14159265358979323846;
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)
104 #define YLOG2DEV(y) (y)
107 #define XDEV2LOG(x) (x)
108 #define YDEV2LOG(y) (y)
110 // ---------------------------------------------------------------------------
112 // ---------------------------------------------------------------------------
114 // convert degrees to radians
115 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
117 // our (limited) AlphaBlend() replacement
119 wxAlphaBlend(wxDC
& dc
, int x
, int y
, int w
, int h
, const wxBitmap
& bmp
);
121 // ----------------------------------------------------------------------------
123 // ----------------------------------------------------------------------------
125 // instead of duplicating the same code which sets and then restores text
126 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
127 // encapsulate this in a small helper class
129 // wxColourChanger: changes the text colours in the ctor if required and
130 // restores them in the dtor
131 class wxColourChanger
134 wxColourChanger(wxDC
& dc
);
140 COLORREF m_colFgOld
, m_colBgOld
;
145 // this class saves the old stretch blit mode during its life time
146 class StretchBltModeChanger
149 StretchBltModeChanger(HDC hdc
, int mode
)
152 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
154 wxLogLastError(_T("SetStretchBltMode"));
157 ~StretchBltModeChanger()
159 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
160 wxLogLastError(_T("SetStretchBltMode"));
169 // ===========================================================================
171 // ===========================================================================
173 // ----------------------------------------------------------------------------
175 // ----------------------------------------------------------------------------
177 wxColourChanger::wxColourChanger(wxDC
& dc
) : m_dc(dc
)
179 const wxBrush
& brush
= dc
.GetBrush();
180 if ( brush
.Ok() && brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
182 HDC hdc
= GetHdcOf(dc
);
183 m_colFgOld
= ::GetTextColor(hdc
);
184 m_colBgOld
= ::GetBkColor(hdc
);
186 // note that Windows convention is opposite to wxWindows one, this is
187 // why text colour becomes the background one and vice versa
188 const wxColour
& colFg
= dc
.GetTextForeground();
191 ::SetBkColor(hdc
, colFg
.GetPixel());
194 const wxColour
& colBg
= dc
.GetTextBackground();
197 ::SetTextColor(hdc
, colBg
.GetPixel());
201 dc
.GetBackgroundMode() == wxTRANSPARENT
? TRANSPARENT
204 // flag which telsl us to undo changes in the dtor
209 // nothing done, nothing to undo
214 wxColourChanger::~wxColourChanger()
218 // restore the colours we changed
219 HDC hdc
= GetHdcOf(m_dc
);
221 ::SetBkMode(hdc
, TRANSPARENT
);
222 ::SetTextColor(hdc
, m_colFgOld
);
223 ::SetBkColor(hdc
, m_colBgOld
);
227 // ---------------------------------------------------------------------------
229 // ---------------------------------------------------------------------------
231 // Default constructor
242 #endif // wxUSE_PALETTE
252 SelectOldObjects(m_hDC
);
254 // if we own the HDC, we delete it, otherwise we just release it
258 ::DeleteDC(GetHdc());
260 else // we don't own our HDC
264 ::ReleaseDC(GetHwndOf(m_canvas
), GetHdc());
268 // Must have been a wxScreenDC
269 ::ReleaseDC((HWND
) NULL
, GetHdc());
275 // This will select current objects out of the DC,
276 // which is what you have to do before deleting the
278 void wxDC::SelectOldObjects(WXHDC dc
)
284 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
286 if (m_selectedBitmap
.Ok())
288 m_selectedBitmap
.SetSelectedInto(NULL
);
295 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
300 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
305 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
312 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
315 #endif // wxUSE_PALETTE
318 m_brush
= wxNullBrush
;
321 m_palette
= wxNullPalette
;
322 #endif // wxUSE_PALETTE
324 m_backgroundBrush
= wxNullBrush
;
325 m_selectedBitmap
= wxNullBitmap
;
328 // ---------------------------------------------------------------------------
330 // ---------------------------------------------------------------------------
332 void wxDC::UpdateClipBox()
334 #ifdef __WXMICROWIN__
335 if (!GetHDC()) return;
339 ::GetClipBox(GetHdc(), &rect
);
341 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
342 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
343 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
344 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
347 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
348 void wxDC::SetClippingHrgn(WXHRGN hrgn
)
350 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
352 #ifdef __WXMICROWIN__
353 if (!GetHdc()) return;
354 #endif // __WXMICROWIN__
356 // note that we combine the new clipping region with the existing one: this
357 // is compatible with what the other ports do and is the documented
358 // behaviour now (starting with 2.3.3)
361 if ( !::GetClipBox(GetHdc(), &rectClip
) )
364 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
365 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
366 rectClip
.right
, rectClip
.bottom
);
368 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
370 ::SelectClipRgn(GetHdc(), hrgnDest
);
373 ::DeleteObject(hrgnClipOld
);
374 ::DeleteObject(hrgnDest
);
376 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
378 wxLogLastError(_T("ExtSelectClipRgn"));
389 void wxDC::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
391 // the region coords are always the device ones, so do the translation
394 // FIXME: possible +/-1 error here, to check!
395 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
397 LogicalToDeviceX(x
+ w
),
398 LogicalToDeviceY(y
+ h
));
401 wxLogLastError(_T("CreateRectRgn"));
405 SetClippingHrgn((WXHRGN
)hrgn
);
407 ::DeleteObject(hrgn
);
411 void wxDC::DoSetClippingRegionAsRegion(const wxRegion
& region
)
413 SetClippingHrgn(region
.GetHRGN());
416 void wxDC::DestroyClippingRegion()
418 #ifdef __WXMICROWIN__
419 if (!GetHDC()) return;
422 if (m_clipping
&& m_hDC
)
424 // TODO: this should restore the previous clipping region,
425 // so that OnPaint processing works correctly, and the update
426 // clipping region doesn't get destroyed after the first
427 // DestroyClippingRegion.
428 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
429 ::SelectClipRgn(GetHdc(), rgn
);
436 // ---------------------------------------------------------------------------
437 // query capabilities
438 // ---------------------------------------------------------------------------
440 bool wxDC::CanDrawBitmap() const
445 bool wxDC::CanGetTextExtent() const
447 #ifdef __WXMICROWIN__
448 // TODO Extend MicroWindows' GetDeviceCaps function
451 // What sort of display is it?
452 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
454 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
458 int wxDC::GetDepth() const
460 #ifdef __WXMICROWIN__
461 if (!GetHDC()) return 16;
464 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
467 // ---------------------------------------------------------------------------
469 // ---------------------------------------------------------------------------
473 #ifdef __WXMICROWIN__
474 if (!GetHDC()) return;
480 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
484 // No, I think we should simply ignore this if printing on e.g.
486 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
487 if (!m_selectedBitmap
.Ok())
490 rect
.left
= 0; rect
.top
= 0;
491 rect
.right
= m_selectedBitmap
.GetWidth();
492 rect
.bottom
= m_selectedBitmap
.GetHeight();
495 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
497 DWORD colour
= ::GetBkColor(GetHdc());
498 HBRUSH brush
= ::CreateSolidBrush(colour
);
499 ::FillRect(GetHdc(), &rect
, brush
);
500 ::DeleteObject(brush
);
502 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
503 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
505 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
506 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
507 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
508 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
509 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
512 bool wxDC::DoFloodFill(wxCoord x
, wxCoord y
, const wxColour
& col
, int style
)
514 #ifdef __WXMICROWIN__
515 if (!GetHDC()) return FALSE
;
518 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
520 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
521 : FLOODFILLBORDER
) ) ;
524 // quoting from the MSDN docs:
526 // Following are some of the reasons this function might fail:
528 // * The filling could not be completed.
529 // * The specified point has the boundary color specified by the
530 // crColor parameter (if FLOODFILLBORDER was requested).
531 // * The specified point does not have the color specified by
532 // crColor (if FLOODFILLSURFACE was requested)
533 // * The point is outside the clipping region that is, it is not
534 // visible on the device.
536 wxLogLastError(wxT("ExtFloodFill"));
539 CalcBoundingBox(x
, y
);
544 bool wxDC::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
546 #ifdef __WXMICROWIN__
547 if (!GetHDC()) return FALSE
;
550 wxCHECK_MSG( col
, FALSE
, _T("NULL colour parameter in wxDC::GetPixel") );
552 // get the color of the pixel
553 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
555 wxRGBToColour(*col
, pixelcolor
);
560 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
562 #ifdef __WXMICROWIN__
563 if (!GetHDC()) return;
566 wxCoord x1
= x
-VIEWPORT_EXTENT
;
567 wxCoord y1
= y
-VIEWPORT_EXTENT
;
568 wxCoord x2
= x
+VIEWPORT_EXTENT
;
569 wxCoord y2
= y
+VIEWPORT_EXTENT
;
571 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), NULL
);
572 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y
));
574 (void)MoveToEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), NULL
);
575 (void)LineTo(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y2
));
577 CalcBoundingBox(x1
, y1
);
578 CalcBoundingBox(x2
, y2
);
581 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
583 #ifdef __WXMICROWIN__
584 if (!GetHDC()) return;
587 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), NULL
);
588 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y2
));
590 CalcBoundingBox(x1
, y1
);
591 CalcBoundingBox(x2
, y2
);
594 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
595 // and ending at (x2, y2)
596 void wxDC::DoDrawArc(wxCoord x1
, wxCoord y1
,
597 wxCoord x2
, wxCoord y2
,
598 wxCoord xc
, wxCoord yc
)
600 #ifdef __WXMICROWIN__
601 if (!GetHDC()) return;
604 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
608 double radius
= (double)sqrt(dx
*dx
+dy
*dy
);
609 wxCoord r
= (wxCoord
)radius
;
611 // treat the special case of full circle separately
612 if ( x1
== x2
&& y1
== y2
)
614 DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
618 wxCoord xx1
= XLOG2DEV(x1
);
619 wxCoord yy1
= YLOG2DEV(y1
);
620 wxCoord xx2
= XLOG2DEV(x2
);
621 wxCoord yy2
= YLOG2DEV(y2
);
622 wxCoord xxc
= XLOG2DEV(xc
);
623 wxCoord yyc
= YLOG2DEV(yc
);
624 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
626 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
627 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
628 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
629 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
631 if ( m_brush
.Ok() && m_brush
.GetStyle() != wxTRANSPARENT
)
633 // Have to add 1 to bottom-right corner of rectangle
634 // to make semi-circles look right (crooked line otherwise).
635 // Unfortunately this is not a reliable method, depends
636 // on the size of shape.
637 // TODO: figure out why this happens!
638 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
642 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
645 CalcBoundingBox(xc
- r
, yc
- r
);
646 CalcBoundingBox(xc
+ r
, yc
+ r
);
649 void wxDC::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
650 wxCoord width
, wxCoord height
)
652 #ifdef __WXMICROWIN__
653 if (!GetHDC()) return;
656 wxCoord x2
= x1
+ width
,
659 #if defined(__WIN32__) && !defined(__SC__) && !defined(__WXMICROWIN__)
666 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
668 // In WIN16, draw a cross
669 HPEN blackPen
= ::CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
670 HPEN whiteBrush
= (HPEN
)::GetStockObject(WHITE_BRUSH
);
671 HPEN hPenOld
= (HPEN
)::SelectObject(GetHdc(), blackPen
);
672 HPEN hBrushOld
= (HPEN
)::SelectObject(GetHdc(), whiteBrush
);
673 ::SetROP2(GetHdc(), R2_COPYPEN
);
674 Rectangle(GetHdc(), x1
, y1
, x2
, y2
);
675 MoveToEx(GetHdc(), x1
, y1
, NULL
);
676 LineTo(GetHdc(), x2
, y2
);
677 MoveToEx(GetHdc(), x2
, y1
, NULL
);
678 LineTo(GetHdc(), x1
, y2
);
679 ::SelectObject(GetHdc(), hPenOld
);
680 ::SelectObject(GetHdc(), hBrushOld
);
681 ::DeleteObject(blackPen
);
684 CalcBoundingBox(x1
, y1
);
685 CalcBoundingBox(x2
, y2
);
688 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
690 #ifdef __WXMICROWIN__
691 if (!GetHDC()) return;
694 COLORREF color
= 0x00ffffff;
697 color
= m_pen
.GetColour().GetPixel();
700 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
702 CalcBoundingBox(x
, y
);
705 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
707 #ifdef __WXMICROWIN__
708 if (!GetHDC()) return;
711 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
713 // Do things less efficiently if we have offsets
714 if (xoffset
!= 0 || yoffset
!= 0)
716 POINT
*cpoints
= new POINT
[n
];
718 for (i
= 0; i
< n
; i
++)
720 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
721 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
723 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
725 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
726 (void)Polygon(GetHdc(), cpoints
, n
);
727 SetPolyFillMode(GetHdc(),prev
);
733 for (i
= 0; i
< n
; i
++)
734 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
736 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
737 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
738 SetPolyFillMode(GetHdc(),prev
);
742 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
744 #ifdef __WXMICROWIN__
745 if (!GetHDC()) return;
748 // Do things less efficiently if we have offsets
749 if (xoffset
!= 0 || yoffset
!= 0)
751 POINT
*cpoints
= new POINT
[n
];
753 for (i
= 0; i
< n
; i
++)
755 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
756 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
758 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
760 (void)Polyline(GetHdc(), cpoints
, n
);
766 for (i
= 0; i
< n
; i
++)
767 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
769 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
773 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
775 #ifdef __WXMICROWIN__
776 if (!GetHDC()) return;
779 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
781 wxCoord x2
= x
+ width
;
782 wxCoord y2
= y
+ height
;
784 if ((m_logicalFunction
== wxCOPY
) && (m_pen
.GetStyle() == wxTRANSPARENT
))
787 rect
.left
= XLOG2DEV(x
);
788 rect
.top
= YLOG2DEV(y
);
789 rect
.right
= XLOG2DEV(x2
);
790 rect
.bottom
= YLOG2DEV(y2
);
791 (void)FillRect(GetHdc(), &rect
, (HBRUSH
)m_brush
.GetResourceHandle() );
795 // Windows draws the filled rectangles without outline (i.e. drawn with a
796 // transparent pen) one pixel smaller in both directions and we want them
797 // to have the same size regardless of which pen is used - adjust
799 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
800 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
806 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
810 CalcBoundingBox(x
, y
);
811 CalcBoundingBox(x2
, y2
);
814 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
816 #ifdef __WXMICROWIN__
817 if (!GetHDC()) return;
820 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
822 // Now, a negative radius value is interpreted to mean
823 // 'the proportion of the smallest X or Y dimension'
827 double smallest
= 0.0;
832 radius
= (- radius
* smallest
);
835 wxCoord x2
= (x
+width
);
836 wxCoord y2
= (y
+height
);
838 // Windows draws the filled rectangles without outline (i.e. drawn with a
839 // transparent pen) one pixel smaller in both directions and we want them
840 // to have the same size regardless of which pen is used - adjust
841 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
847 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
848 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
850 CalcBoundingBox(x
, y
);
851 CalcBoundingBox(x2
, y2
);
854 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
856 #ifdef __WXMICROWIN__
857 if (!GetHDC()) return;
860 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
862 wxCoord x2
= (x
+width
);
863 wxCoord y2
= (y
+height
);
865 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
867 CalcBoundingBox(x
, y
);
868 CalcBoundingBox(x2
, y2
);
871 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
872 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
874 #ifdef __WXMICROWIN__
875 if (!GetHDC()) return;
878 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
883 int rx1
= XLOG2DEV(x
+w
/2);
884 int ry1
= YLOG2DEV(y
+h
/2);
891 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
892 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
893 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
894 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
896 // draw pie with NULL_PEN first and then outline otherwise a line is
897 // drawn from the start and end points to the centre
898 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
901 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
906 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
907 rx1
, ry1
-1, rx2
, ry2
-1);
910 ::SelectObject(GetHdc(), hpenOld
);
912 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
915 CalcBoundingBox(x
, y
);
916 CalcBoundingBox(x2
, y2
);
919 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
921 #ifdef __WXMICROWIN__
922 if (!GetHDC()) return;
925 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
928 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
930 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
933 CalcBoundingBox(x
, y
);
934 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
937 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
939 #ifdef __WXMICROWIN__
940 if (!GetHDC()) return;
943 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
945 int width
= bmp
.GetWidth(),
946 height
= bmp
.GetHeight();
948 HBITMAP hbmpMask
= 0;
952 #endif // wxUSE_PALETTE
954 // do we have AlphaBlend() and company in the headers?
956 if ( bmp
.HasAlpha() )
958 // yes, now try to see if we have it during run-time
960 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
964 // bitmaps can be drawn only from GUI thread so there is no need to
965 // protect this static variable from multiple threads
966 static bool s_triedToLoad
= FALSE
;
967 static AlphaBlend_t pfnAlphaBlend
= NULL
;
968 if ( !s_triedToLoad
)
970 s_triedToLoad
= TRUE
;
972 // don't give errors about the DLL being unavailable, we're
973 // prepared to handle this
976 wxDynamicLibrary
dll(_T("msimg32.dll"));
977 if ( dll
.IsLoaded() )
979 pfnAlphaBlend
= (AlphaBlend_t
)dll
.GetSymbol(_T("AlphaBlend"));
982 // we must keep the DLL loaded if we want to be able to
983 // call AlphaBlend() so just never unload it at all, not a
993 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
996 bf
.BlendOp
= AC_SRC_OVER
;
998 bf
.SourceConstantAlpha
= 0xff;
999 bf
.AlphaFormat
= AC_SRC_ALPHA
;
1001 if ( !pfnAlphaBlend(GetHdc(), x
, y
, width
, height
,
1002 hdcMem
, 0, 0, width
, height
,
1005 wxLogLastError(_T("AlphaBlend"));
1008 else // use our own (probably much slower) implementation
1010 wxAlphaBlend(*this, x
, y
, width
, height
, bmp
);
1015 #endif // defined(AC_SRC_OVER)
1019 wxMask
*mask
= bmp
.GetMask();
1021 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1025 // don't give assert here because this would break existing
1026 // programs - just silently ignore useMask parameter
1033 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1035 // On some systems, MaskBlt succeeds yet is much much slower
1036 // than the wxWindows fall-back implementation. So we need
1037 // to be able to switch this on and off at runtime.
1039 #if wxUSE_SYSTEM_OPTIONS
1040 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1044 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1045 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1047 wxPalette
*pal
= bmp
.GetPalette();
1048 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1050 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1051 ::RealizePalette(hdcMem
);
1053 #endif // wxUSE_PALETTE
1055 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1058 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1062 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1063 #endif // wxUSE_PALETTE
1065 ::SelectObject(hdcMem
, hOldBitmap
);
1072 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1075 memDC
.SelectObject(bmp
);
1077 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1079 memDC
.SelectObject(wxNullBitmap
);
1082 else // no mask, just use BitBlt()
1085 HDC memdc
= ::CreateCompatibleDC( cdc
);
1086 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1088 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1090 COLORREF old_textground
= ::GetTextColor(GetHdc());
1091 COLORREF old_background
= ::GetBkColor(GetHdc());
1092 if (m_textForegroundColour
.Ok())
1094 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1096 if (m_textBackgroundColour
.Ok())
1098 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1102 wxPalette
*pal
= bmp
.GetPalette();
1103 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1105 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1106 ::RealizePalette(memdc
);
1108 #endif // wxUSE_PALETTE
1110 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1111 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1115 ::SelectPalette(memdc
, oldPal
, FALSE
);
1116 #endif // wxUSE_PALETTE
1118 ::SelectObject( memdc
, hOldBitmap
);
1119 ::DeleteDC( memdc
);
1121 ::SetTextColor(GetHdc(), old_textground
);
1122 ::SetBkColor(GetHdc(), old_background
);
1126 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1128 #ifdef __WXMICROWIN__
1129 if (!GetHDC()) return;
1132 DrawAnyText(text
, x
, y
);
1134 // update the bounding box
1135 CalcBoundingBox(x
, y
);
1138 GetTextExtent(text
, &w
, &h
);
1139 CalcBoundingBox(x
+ w
, y
+ h
);
1142 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1144 #ifdef __WXMICROWIN__
1145 if (!GetHDC()) return;
1148 // prepare for drawing the text
1149 if ( m_textForegroundColour
.Ok() )
1150 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
1152 DWORD old_background
= 0;
1153 if ( m_textBackgroundColour
.Ok() )
1155 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1158 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
1161 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
1162 text
.c_str(), text
.length()) == 0 )
1164 wxLogLastError(wxT("TextOut"));
1167 // restore the old parameters (text foreground colour may be left because
1168 // it never is set to anything else, but background should remain
1169 // transparent even if we just drew an opaque string)
1170 if ( m_textBackgroundColour
.Ok() )
1171 (void)SetBkColor(GetHdc(), old_background
);
1173 SetBkMode(GetHdc(), TRANSPARENT
);
1176 void wxDC::DoDrawRotatedText(const wxString
& text
,
1177 wxCoord x
, wxCoord y
,
1180 #ifdef __WXMICROWIN__
1181 if (!GetHDC()) return;
1184 // we test that we have some font because otherwise we should still use the
1185 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1186 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1187 // font for drawing rotated fonts unfortunately)
1188 if ( (angle
== 0.0) && m_font
.Ok() )
1190 DoDrawText(text
, x
, y
);
1192 #ifndef __WXMICROWIN__
1195 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1196 // because it's not TrueType and so can't have non zero
1197 // orientation/escapement under Win9x
1198 wxFont font
= m_font
.Ok() ? m_font
: *wxSWISS_FONT
;
1199 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1201 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1203 wxLogLastError(wxT("GetObject(hfont)"));
1206 // GDI wants the angle in tenth of degree
1207 long angle10
= (long)(angle
* 10);
1208 lf
.lfEscapement
= angle10
;
1209 lf
. lfOrientation
= angle10
;
1211 hfont
= ::CreateFontIndirect(&lf
);
1214 wxLogLastError(wxT("CreateFont"));
1218 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1220 DrawAnyText(text
, x
, y
);
1222 (void)::SelectObject(GetHdc(), hfontOld
);
1223 (void)::DeleteObject(hfont
);
1226 // call the bounding box by adding all four vertices of the rectangle
1227 // containing the text to it (simpler and probably not slower than
1228 // determining which of them is really topmost/leftmost/...)
1230 GetTextExtent(text
, &w
, &h
);
1232 double rad
= DegToRad(angle
);
1234 // "upper left" and "upper right"
1235 CalcBoundingBox(x
, y
);
1236 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(h
*sin(rad
)));
1238 // "bottom left" and "bottom right"
1239 x
+= (wxCoord
)(h
*sin(rad
));
1240 y
+= (wxCoord
)(h
*cos(rad
));
1241 CalcBoundingBox(x
, y
);
1242 CalcBoundingBox(x
+ wxCoord(h
*sin(rad
)), y
+ wxCoord(h
*cos(rad
)));
1247 // ---------------------------------------------------------------------------
1249 // ---------------------------------------------------------------------------
1253 void wxDC::DoSelectPalette(bool realize
)
1255 #ifdef __WXMICROWIN__
1256 if (!GetHDC()) return;
1259 // Set the old object temporarily, in case the assignment deletes an object
1260 // that's not yet selected out.
1263 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1267 if ( m_palette
.Ok() )
1269 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1270 GetHpaletteOf(m_palette
),
1273 m_oldPalette
= (WXHPALETTE
) oldPal
;
1276 ::RealizePalette(GetHdc());
1280 void wxDC::SetPalette(const wxPalette
& palette
)
1284 m_palette
= palette
;
1285 DoSelectPalette(TRUE
);
1289 void wxDC::InitializePalette()
1291 if ( wxDisplayDepth() <= 8 )
1293 // look for any window or parent that has a custom palette. If any has
1294 // one then we need to use it in drawing operations
1295 wxWindow
*win
= m_canvas
->GetAncestorWithCustomPalette();
1297 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1298 if ( m_hasCustomPalette
)
1300 m_palette
= win
->GetPalette();
1302 // turn on MSW translation for this palette
1308 #endif // wxUSE_PALETTE
1310 void wxDC::SetFont(const wxFont
& the_font
)
1312 #ifdef __WXMICROWIN__
1313 if (!GetHDC()) return;
1316 // Set the old object temporarily, in case the assignment deletes an object
1317 // that's not yet selected out.
1320 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1329 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1333 if (m_font
.Ok() && m_font
.GetResourceHandle())
1335 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
1336 if (f
== (HFONT
) NULL
)
1338 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
1341 m_oldFont
= (WXHFONT
) f
;
1345 void wxDC::SetPen(const wxPen
& pen
)
1347 #ifdef __WXMICROWIN__
1348 if (!GetHDC()) return;
1351 // Set the old object temporarily, in case the assignment deletes an object
1352 // that's not yet selected out.
1355 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1364 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1370 if (m_pen
.GetResourceHandle())
1372 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
1374 m_oldPen
= (WXHPEN
) p
;
1379 void wxDC::SetBrush(const wxBrush
& brush
)
1381 #ifdef __WXMICROWIN__
1382 if (!GetHDC()) return;
1385 // Set the old object temporarily, in case the assignment deletes an object
1386 // that's not yet selected out.
1389 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1398 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1404 // to make sure the brush is alligned with the logical coordinates
1405 wxBitmap
*stipple
= m_brush
.GetStipple();
1406 if ( stipple
&& stipple
->Ok() )
1409 ::SetBrushOrgEx(GetHdc(),
1410 m_deviceOriginX
% stipple
->GetWidth(),
1411 m_deviceOriginY
% stipple
->GetHeight(),
1412 NULL
); // don't need previous brush origin
1414 ::SetBrushOrg(GetHdc(),
1415 m_deviceOriginX
% stipple
->GetWidth(),
1416 m_deviceOriginY
% stipple
->GetHeight());
1420 if ( m_brush
.GetResourceHandle() )
1423 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
1425 m_oldBrush
= (WXHBRUSH
) b
;
1430 void wxDC::SetBackground(const wxBrush
& brush
)
1432 #ifdef __WXMICROWIN__
1433 if (!GetHDC()) return;
1436 m_backgroundBrush
= brush
;
1438 if (!m_backgroundBrush
.Ok())
1443 bool customColours
= TRUE
;
1444 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
1445 // change background colours from the control-panel specified colours.
1446 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
1447 customColours
= FALSE
;
1451 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
1453 m_canvas
->SetTransparent(TRUE
);
1457 // New behaviour, 10/2/99: setting the background brush of a DC
1458 // doesn't affect the window background colour. However,
1459 // I'm leaving in the transparency setting because it's needed by
1460 // various controls (e.g. wxStaticText) to determine whether to draw
1461 // transparently or not. TODO: maybe this should be a new function
1462 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1464 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1465 m_canvas
->SetTransparent(FALSE
);
1469 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1471 (void)SetBkColor(GetHdc(), new_color
);
1475 void wxDC::SetBackgroundMode(int mode
)
1477 #ifdef __WXMICROWIN__
1478 if (!GetHDC()) return;
1481 m_backgroundMode
= mode
;
1483 // SetBackgroundColour now only refers to text background
1484 // and m_backgroundMode is used there
1487 void wxDC::SetLogicalFunction(int function
)
1489 #ifdef __WXMICROWIN__
1490 if (!GetHDC()) return;
1493 m_logicalFunction
= function
;
1498 void wxDC::SetRop(WXHDC dc
)
1500 if ( !dc
|| m_logicalFunction
< 0 )
1505 switch (m_logicalFunction
)
1507 case wxCLEAR
: rop
= R2_BLACK
; break;
1508 case wxXOR
: rop
= R2_XORPEN
; break;
1509 case wxINVERT
: rop
= R2_NOT
; break;
1510 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1511 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1512 case wxCOPY
: rop
= R2_COPYPEN
; break;
1513 case wxAND
: rop
= R2_MASKPEN
; break;
1514 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1515 case wxNO_OP
: rop
= R2_NOP
; break;
1516 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1517 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1518 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1519 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1520 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1521 case wxOR
: rop
= R2_MERGEPEN
; break;
1522 case wxSET
: rop
= R2_WHITE
; break;
1525 wxFAIL_MSG( wxT("unsupported logical function") );
1529 SetROP2(GetHdc(), rop
);
1532 bool wxDC::StartDoc(const wxString
& WXUNUSED(message
))
1534 // We might be previewing, so return TRUE to let it continue.
1542 void wxDC::StartPage()
1546 void wxDC::EndPage()
1550 // ---------------------------------------------------------------------------
1552 // ---------------------------------------------------------------------------
1554 wxCoord
wxDC::GetCharHeight() const
1556 #ifdef __WXMICROWIN__
1557 if (!GetHDC()) return 0;
1560 TEXTMETRIC lpTextMetric
;
1562 GetTextMetrics(GetHdc(), &lpTextMetric
);
1564 return lpTextMetric
.tmHeight
;
1567 wxCoord
wxDC::GetCharWidth() const
1569 #ifdef __WXMICROWIN__
1570 if (!GetHDC()) return 0;
1573 TEXTMETRIC lpTextMetric
;
1575 GetTextMetrics(GetHdc(), &lpTextMetric
);
1577 return lpTextMetric
.tmAveCharWidth
;
1580 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1581 wxCoord
*descent
, wxCoord
*externalLeading
,
1584 #ifdef __WXMICROWIN__
1589 if (descent
) *descent
= 0;
1590 if (externalLeading
) *externalLeading
= 0;
1593 #endif // __WXMICROWIN__
1598 wxASSERT_MSG( font
->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1600 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1602 else // don't change the font
1610 GetTextExtentPoint(GetHdc(), string
, string
.length(), &sizeRect
);
1611 GetTextMetrics(GetHdc(), &tm
);
1618 *descent
= tm
.tmDescent
;
1619 if (externalLeading
)
1620 *externalLeading
= tm
.tmExternalLeading
;
1624 ::SelectObject(GetHdc(), hfontOld
);
1628 void wxDC::SetMapMode(int mode
)
1630 #ifdef __WXMICROWIN__
1631 if (!GetHDC()) return;
1634 m_mappingMode
= mode
;
1636 if ( mode
== wxMM_TEXT
)
1639 m_logicalScaleY
= 1.0;
1641 else // need to do some calculations
1643 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1644 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1645 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1646 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1648 if ( (mm_width
== 0) || (mm_height
== 0) )
1650 // we can't calculate mm2pixels[XY] then!
1654 double mm2pixelsX
= pixel_width
/ mm_width
,
1655 mm2pixelsY
= pixel_height
/ mm_height
;
1660 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1661 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1665 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1666 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1670 m_logicalScaleX
= mm2pixelsX
;
1671 m_logicalScaleY
= mm2pixelsY
;
1675 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1676 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1680 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1684 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1685 // cases we could do with MM_TEXT and in the remaining 0.9% with
1686 // MM_ISOTROPIC (TODO!)
1687 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1689 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1690 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1692 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1693 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1695 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1696 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1699 void wxDC::SetUserScale(double x
, double y
)
1701 #ifdef __WXMICROWIN__
1702 if (!GetHDC()) return;
1705 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1711 SetMapMode(m_mappingMode
);
1714 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1716 #ifdef __WXMICROWIN__
1717 if (!GetHDC()) return;
1720 int signX
= xLeftRight
? 1 : -1,
1721 signY
= yBottomUp
? -1 : 1;
1723 if ( signX
!= m_signX
|| signY
!= m_signY
)
1728 SetMapMode(m_mappingMode
);
1732 void wxDC::SetSystemScale(double x
, double y
)
1734 #ifdef __WXMICROWIN__
1735 if (!GetHDC()) return;
1738 if ( x
== m_scaleX
&& y
== m_scaleY
)
1744 SetMapMode(m_mappingMode
);
1747 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1749 #ifdef __WXMICROWIN__
1750 if (!GetHDC()) return;
1753 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1756 m_logicalOriginX
= x
;
1757 m_logicalOriginY
= y
;
1759 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1762 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1764 #ifdef __WXMICROWIN__
1765 if (!GetHDC()) return;
1768 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
1771 m_deviceOriginX
= x
;
1772 m_deviceOriginY
= y
;
1774 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1777 // ---------------------------------------------------------------------------
1778 // coordinates transformations
1779 // ---------------------------------------------------------------------------
1781 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1783 return DeviceToLogicalXRel(x
- m_deviceOriginX
)*m_signX
+ m_logicalOriginX
;
1786 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1788 // axis orientation is not taken into account for conversion of a distance
1789 return (wxCoord
)(x
/ (m_logicalScaleX
*m_userScaleX
*m_scaleX
));
1792 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1794 return DeviceToLogicalYRel(y
- m_deviceOriginY
)*m_signY
+ m_logicalOriginY
;
1797 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1799 // axis orientation is not taken into account for conversion of a distance
1800 return (wxCoord
)( y
/ (m_logicalScaleY
*m_userScaleY
*m_scaleY
));
1803 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1805 return LogicalToDeviceXRel(x
- m_logicalOriginX
)*m_signX
+ m_deviceOriginX
;
1808 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1810 // axis orientation is not taken into account for conversion of a distance
1811 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_scaleX
);
1814 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1816 return LogicalToDeviceYRel(y
- m_logicalOriginY
)*m_signY
+ m_deviceOriginY
;
1819 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1821 // axis orientation is not taken into account for conversion of a distance
1822 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_scaleY
);
1825 // ---------------------------------------------------------------------------
1827 // ---------------------------------------------------------------------------
1829 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1830 wxCoord width
, wxCoord height
,
1831 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1832 int rop
, bool useMask
,
1833 wxCoord xsrcMask
, wxCoord ysrcMask
)
1835 #ifdef __WXMICROWIN__
1836 if (!GetHDC()) return FALSE
;
1839 const wxBitmap
& bmpSrc
= source
->m_selectedBitmap
;
1841 wxMask
*mask
= NULL
;
1844 mask
= bmpSrc
.GetMask();
1846 if ( !(bmpSrc
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1848 // don't give assert here because this would break existing
1849 // programs - just silently ignore useMask parameter
1854 if (xsrcMask
== -1 && ysrcMask
== -1)
1856 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
1859 COLORREF old_textground
= ::GetTextColor(GetHdc());
1860 COLORREF old_background
= ::GetBkColor(GetHdc());
1861 if (m_textForegroundColour
.Ok())
1863 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1865 if (m_textBackgroundColour
.Ok())
1867 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1870 DWORD dwRop
= SRCCOPY
;
1873 case wxXOR
: dwRop
= SRCINVERT
; break;
1874 case wxINVERT
: dwRop
= DSTINVERT
; break;
1875 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1876 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1877 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1878 case wxSET
: dwRop
= WHITENESS
; break;
1879 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1880 case wxAND
: dwRop
= SRCAND
; break;
1881 case wxOR
: dwRop
= SRCPAINT
; break;
1882 case wxEQUIV
: dwRop
= 0x00990066; break;
1883 case wxNAND
: dwRop
= 0x007700E6; break;
1884 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1885 case wxCOPY
: dwRop
= SRCCOPY
; break;
1886 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1887 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1888 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1890 wxFAIL_MSG( wxT("unsupported logical function") );
1894 bool success
= FALSE
;
1899 // we want the part of the image corresponding to the mask to be
1900 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1901 // meaning of fg and bg is inverted which corresponds to wxWin notion
1902 // of the mask which is also contrary to the Windows one)
1904 // On some systems, MaskBlt succeeds yet is much much slower
1905 // than the wxWindows fall-back implementation. So we need
1906 // to be able to switch this on and off at runtime.
1907 #if wxUSE_SYSTEM_OPTIONS
1908 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1914 xdest
, ydest
, width
, height
,
1917 (HBITMAP
)mask
->GetMaskBitmap(),
1919 MAKEROP4(dwRop
, DSTCOPY
)
1926 // Blit bitmap with mask
1929 HBITMAP buffer_bmap
;
1931 #if wxUSE_DC_CACHEING
1932 // create a temp buffer bitmap and DCs to access it and the mask
1933 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, source
->GetHDC());
1934 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
1936 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
1937 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
1939 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
1942 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
1943 #else // !wxUSE_DC_CACHEING
1944 // create a temp buffer bitmap and DCs to access it and the mask
1945 dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1946 dc_buffer
= ::CreateCompatibleDC(GetHdc());
1947 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1948 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1949 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1950 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
1952 // copy dest to buffer
1953 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1954 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1956 wxLogLastError(wxT("BitBlt"));
1959 // copy src to buffer using selected raster op
1960 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1961 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1963 wxLogLastError(wxT("BitBlt"));
1966 // set masked area in buffer to BLACK (pixel value 0)
1967 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1968 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1969 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1970 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1972 wxLogLastError(wxT("BitBlt"));
1975 // set unmasked area in dest to BLACK
1976 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1977 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1978 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1979 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1981 wxLogLastError(wxT("BitBlt"));
1983 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1984 ::SetTextColor(GetHdc(), prevCol
);
1986 // OR buffer to dest
1987 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1988 (int)width
, (int)height
,
1989 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1992 wxLogLastError(wxT("BitBlt"));
1995 // tidy up temporary DCs and bitmap
1996 ::SelectObject(dc_mask
, hOldMaskBitmap
);
1997 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
1999 #if !wxUSE_DC_CACHEING
2001 ::DeleteDC(dc_mask
);
2002 ::DeleteDC(dc_buffer
);
2003 ::DeleteObject(buffer_bmap
);
2008 else // no mask, just BitBlt() it
2010 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2011 // use StretchBlt() if available and finally fall back to BitBlt()
2012 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2013 if ( bmpSrc
.Ok() && (caps
& RC_STRETCHDIB
) )
2018 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2020 &ds
) == sizeof(ds
) )
2022 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2024 if ( ::StretchDIBits(GetHdc(),
2030 (LPBITMAPINFO
)&ds
.dsBmih
,
2033 ) == (int)GDI_ERROR
)
2035 wxLogLastError(wxT("StretchDIBits"));
2044 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2046 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2051 xdest
, ydest
, width
, height
,
2053 xsrc
, ysrc
, width
, height
,
2057 wxLogLastError(_T("StretchBlt"));
2071 (int)width
, (int)height
,
2077 wxLogLastError(_T("BitBlt"));
2086 ::SetTextColor(GetHdc(), old_textground
);
2087 ::SetBkColor(GetHdc(), old_background
);
2092 void wxDC::DoGetSize(int *w
, int *h
) const
2094 #ifdef __WXMICROWIN__
2095 if (!GetHDC()) return;
2098 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2099 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2102 void wxDC::DoGetSizeMM(int *w
, int *h
) const
2104 #ifdef __WXMICROWIN__
2105 if (!GetHDC()) return;
2108 // if we implement it in terms of DoGetSize() instead of directly using the
2109 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2110 // will also work for wxWindowDC and wxClientDC even though their size is
2111 // not the same as the total size of the screen
2112 int wPixels
, hPixels
;
2113 DoGetSize(&wPixels
, &hPixels
);
2117 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2119 wxCHECK_RET( wTotal
, _T("0 width device?") );
2121 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2126 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2128 wxCHECK_RET( hTotal
, _T("0 height device?") );
2130 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2134 wxSize
wxDC::GetPPI() const
2136 #ifdef __WXMICROWIN__
2137 if (!GetHDC()) return wxSize();
2140 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2141 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2143 return wxSize(x
, y
);
2146 // For use by wxWindows only, unless custom units are required.
2147 void wxDC::SetLogicalScale(double x
, double y
)
2149 #ifdef __WXMICROWIN__
2150 if (!GetHDC()) return;
2153 m_logicalScaleX
= x
;
2154 m_logicalScaleY
= y
;
2157 // ----------------------------------------------------------------------------
2159 // ----------------------------------------------------------------------------
2161 #if wxUSE_DC_CACHEING
2164 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2165 * improve it in due course, either using arrays, or simply storing pointers to one
2166 * entry for the bitmap, and two for the DCs. -- JACS
2169 wxList
wxDC::sm_bitmapCache
;
2170 wxList
wxDC::sm_dcCache
;
2172 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2181 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2190 wxDCCacheEntry::~wxDCCacheEntry()
2193 ::DeleteObject((HBITMAP
) m_bitmap
);
2195 ::DeleteDC((HDC
) m_dc
);
2198 wxDCCacheEntry
* wxDC::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2200 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2201 wxNode
* node
= sm_bitmapCache
.GetFirst();
2204 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2206 if (entry
->m_depth
== depth
)
2208 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2210 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2211 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2212 if ( !entry
->m_bitmap
)
2214 wxLogLastError(wxT("CreateCompatibleBitmap"));
2216 entry
->m_width
= w
; entry
->m_height
= h
;
2222 node
= node
->GetNext();
2224 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2227 wxLogLastError(wxT("CreateCompatibleBitmap"));
2229 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2230 AddToBitmapCache(entry
);
2234 wxDCCacheEntry
* wxDC::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2236 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2237 wxNode
* node
= sm_dcCache
.GetFirst();
2240 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2242 // Don't return the same one as we already have
2243 if (!notThis
|| (notThis
!= entry
))
2245 if (entry
->m_depth
== depth
)
2251 node
= node
->GetNext();
2253 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2256 wxLogLastError(wxT("CreateCompatibleDC"));
2258 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2259 AddToDCCache(entry
);
2263 void wxDC::AddToBitmapCache(wxDCCacheEntry
* entry
)
2265 sm_bitmapCache
.Append(entry
);
2268 void wxDC::AddToDCCache(wxDCCacheEntry
* entry
)
2270 sm_dcCache
.Append(entry
);
2273 void wxDC::ClearCache()
2275 sm_dcCache
.DeleteContents(TRUE
);
2277 sm_dcCache
.DeleteContents(FALSE
);
2278 sm_bitmapCache
.DeleteContents(TRUE
);
2279 sm_bitmapCache
.Clear();
2280 sm_bitmapCache
.DeleteContents(FALSE
);
2283 // Clean up cache at app exit
2284 class wxDCModule
: public wxModule
2287 virtual bool OnInit() { return TRUE
; }
2288 virtual void OnExit() { wxDC::ClearCache(); }
2291 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2294 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2296 #endif // wxUSE_DC_CACHEING
2298 // ----------------------------------------------------------------------------
2299 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2300 // ----------------------------------------------------------------------------
2303 wxAlphaBlend(wxDC
& dc
, int xDst
, int yDst
, int w
, int h
, const wxBitmap
& bmpSrc
)
2305 // get the destination DC pixels
2306 wxBitmap
bmpDst(w
, h
, 32 /* force creating RGBA DIB */);
2308 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2310 if ( !::BitBlt(hdcMem
, 0, 0, w
, h
, GetHdcOf(dc
), 0, 0, SRCCOPY
) )
2312 wxLogLastError(_T("BitBlt"));
2315 // combine them with the source bitmap using alpha
2316 wxRawBitmapData
dataDst(bmpDst
),
2319 wxCHECK_RET( dataDst
&& dataSrc
,
2320 _T("failed to get raw data in wxAlphaBlend") );
2322 wxRawBitmapIterator
pDst(dataDst
),
2325 for ( int y
= 0; y
< h
; y
++ )
2327 wxRawBitmapIterator pDstRowStart
= pDst
,
2328 pSrcRowStart
= pSrc
;
2330 for ( int x
= 0; x
< w
; x
++ )
2332 // note that source bitmap uses premultiplied alpha (as required by
2333 // the real AlphaBlend)
2334 const unsigned beta
= 255 - pSrc
.Alpha();
2336 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2337 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2338 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2344 pDst
= pDstRowStart
;
2345 pSrc
= pSrcRowStart
;
2350 // and finally blit them back to the destination DC
2351 if ( !::BitBlt(GetHdcOf(dc
), xDst
, yDst
, w
, h
, hdcMem
, 0, 0, SRCCOPY
) )
2353 wxLogLastError(_T("BitBlt"));