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"
48 #ifdef wxHAVE_RAW_BITMAP
49 #include "wx/rawbmp.h"
55 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
57 #if wxUSE_COMMON_DIALOGS && !defined(__WXMICROWIN__)
66 #define AC_SRC_ALPHA 1
69 /* Quaternary raster codes */
71 #define MAKEROP4(fore,back) (DWORD)((((back) << 8) & 0xFF000000) | (fore))
74 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxDCBase
)
76 // ---------------------------------------------------------------------------
78 // ---------------------------------------------------------------------------
80 static const int VIEWPORT_EXTENT
= 1000;
82 static const int MM_POINTS
= 9;
83 static const int MM_METRIC
= 10;
85 // usually this is defined in math.h
87 static const double M_PI
= 3.14159265358979323846;
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)
107 #define YLOG2DEV(y) (y)
110 #define XDEV2LOG(x) (x)
111 #define YDEV2LOG(y) (y)
113 // ---------------------------------------------------------------------------
115 // ---------------------------------------------------------------------------
117 // convert degrees to radians
118 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
120 // call AlphaBlend() to blit contents of hdcSrc to hdcDst using alpha
122 // NB: bmpSrc is the bitmap selected in hdcSrc, it is not really needed
123 // to pass it to this function but as we already have it at the point
124 // of call anyhow we do
126 // return true if we could draw the bitmap in one way or the other, false
128 static bool AlphaBlt(HDC hdcDst
,
129 int x
, int y
, int w
, int h
,
131 const wxBitmap
& bmpSrc
);
133 #ifdef wxHAVE_RAW_BITMAP
134 // our (limited) AlphaBlend() replacement
136 wxAlphaBlend(HDC hdcDst
, int x
, int y
, int w
, int h
, const wxBitmap
& bmp
);
139 // ----------------------------------------------------------------------------
141 // ----------------------------------------------------------------------------
143 // instead of duplicating the same code which sets and then restores text
144 // colours in each wxDC method working with wxSTIPPLE_MASK_OPAQUE brushes,
145 // encapsulate this in a small helper class
147 // wxColourChanger: changes the text colours in the ctor if required and
148 // restores them in the dtor
149 class wxColourChanger
152 wxColourChanger(wxDC
& dc
);
158 COLORREF m_colFgOld
, m_colBgOld
;
163 // this class saves the old stretch blit mode during its life time
164 class StretchBltModeChanger
167 StretchBltModeChanger(HDC hdc
, int mode
)
170 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
172 wxLogLastError(_T("SetStretchBltMode"));
175 ~StretchBltModeChanger()
177 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
178 wxLogLastError(_T("SetStretchBltMode"));
187 // ===========================================================================
189 // ===========================================================================
191 // ----------------------------------------------------------------------------
193 // ----------------------------------------------------------------------------
195 wxColourChanger::wxColourChanger(wxDC
& dc
) : m_dc(dc
)
197 const wxBrush
& brush
= dc
.GetBrush();
198 if ( brush
.Ok() && brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
200 HDC hdc
= GetHdcOf(dc
);
201 m_colFgOld
= ::GetTextColor(hdc
);
202 m_colBgOld
= ::GetBkColor(hdc
);
204 // note that Windows convention is opposite to wxWindows one, this is
205 // why text colour becomes the background one and vice versa
206 const wxColour
& colFg
= dc
.GetTextForeground();
209 ::SetBkColor(hdc
, colFg
.GetPixel());
212 const wxColour
& colBg
= dc
.GetTextBackground();
215 ::SetTextColor(hdc
, colBg
.GetPixel());
219 dc
.GetBackgroundMode() == wxTRANSPARENT
? TRANSPARENT
222 // flag which telsl us to undo changes in the dtor
227 // nothing done, nothing to undo
232 wxColourChanger::~wxColourChanger()
236 // restore the colours we changed
237 HDC hdc
= GetHdcOf(m_dc
);
239 ::SetBkMode(hdc
, TRANSPARENT
);
240 ::SetTextColor(hdc
, m_colFgOld
);
241 ::SetBkColor(hdc
, m_colBgOld
);
245 // ---------------------------------------------------------------------------
247 // ---------------------------------------------------------------------------
249 // Default constructor
260 #endif // wxUSE_PALETTE
270 SelectOldObjects(m_hDC
);
272 // if we own the HDC, we delete it, otherwise we just release it
276 ::DeleteDC(GetHdc());
278 else // we don't own our HDC
282 ::ReleaseDC(GetHwndOf(m_canvas
), GetHdc());
286 // Must have been a wxScreenDC
287 ::ReleaseDC((HWND
) NULL
, GetHdc());
293 // This will select current objects out of the DC,
294 // which is what you have to do before deleting the
296 void wxDC::SelectOldObjects(WXHDC dc
)
302 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
304 if (m_selectedBitmap
.Ok())
306 m_selectedBitmap
.SetSelectedInto(NULL
);
313 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
318 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
323 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
330 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
333 #endif // wxUSE_PALETTE
336 m_brush
= wxNullBrush
;
339 m_palette
= wxNullPalette
;
340 #endif // wxUSE_PALETTE
342 m_backgroundBrush
= wxNullBrush
;
343 m_selectedBitmap
= wxNullBitmap
;
346 // ---------------------------------------------------------------------------
348 // ---------------------------------------------------------------------------
350 void wxDC::UpdateClipBox()
352 #ifdef __WXMICROWIN__
353 if (!GetHDC()) return;
357 ::GetClipBox(GetHdc(), &rect
);
359 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
360 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
361 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
362 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
365 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
366 void wxDC::SetClippingHrgn(WXHRGN hrgn
)
368 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
370 #ifdef __WXMICROWIN__
371 if (!GetHdc()) return;
372 #endif // __WXMICROWIN__
374 // note that we combine the new clipping region with the existing one: this
375 // is compatible with what the other ports do and is the documented
376 // behaviour now (starting with 2.3.3)
379 if ( !::GetClipBox(GetHdc(), &rectClip
) )
382 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
383 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
384 rectClip
.right
, rectClip
.bottom
);
386 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
388 ::SelectClipRgn(GetHdc(), hrgnDest
);
391 ::DeleteObject(hrgnClipOld
);
392 ::DeleteObject(hrgnDest
);
394 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
396 wxLogLastError(_T("ExtSelectClipRgn"));
407 void wxDC::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
409 // the region coords are always the device ones, so do the translation
412 // FIXME: possible +/-1 error here, to check!
413 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
415 LogicalToDeviceX(x
+ w
),
416 LogicalToDeviceY(y
+ h
));
419 wxLogLastError(_T("CreateRectRgn"));
423 SetClippingHrgn((WXHRGN
)hrgn
);
425 ::DeleteObject(hrgn
);
429 void wxDC::DoSetClippingRegionAsRegion(const wxRegion
& region
)
431 SetClippingHrgn(region
.GetHRGN());
434 void wxDC::DestroyClippingRegion()
436 #ifdef __WXMICROWIN__
437 if (!GetHDC()) return;
440 if (m_clipping
&& m_hDC
)
442 // TODO: this should restore the previous clipping region,
443 // so that OnPaint processing works correctly, and the update
444 // clipping region doesn't get destroyed after the first
445 // DestroyClippingRegion.
446 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
447 ::SelectClipRgn(GetHdc(), rgn
);
454 // ---------------------------------------------------------------------------
455 // query capabilities
456 // ---------------------------------------------------------------------------
458 bool wxDC::CanDrawBitmap() const
463 bool wxDC::CanGetTextExtent() const
465 #ifdef __WXMICROWIN__
466 // TODO Extend MicroWindows' GetDeviceCaps function
469 // What sort of display is it?
470 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
472 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
476 int wxDC::GetDepth() const
478 #ifdef __WXMICROWIN__
479 if (!GetHDC()) return 16;
482 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
485 // ---------------------------------------------------------------------------
487 // ---------------------------------------------------------------------------
491 #ifdef __WXMICROWIN__
492 if (!GetHDC()) return;
498 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
502 // No, I think we should simply ignore this if printing on e.g.
504 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
505 if (!m_selectedBitmap
.Ok())
508 rect
.left
= 0; rect
.top
= 0;
509 rect
.right
= m_selectedBitmap
.GetWidth();
510 rect
.bottom
= m_selectedBitmap
.GetHeight();
513 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
515 DWORD colour
= ::GetBkColor(GetHdc());
516 HBRUSH brush
= ::CreateSolidBrush(colour
);
517 ::FillRect(GetHdc(), &rect
, brush
);
518 ::DeleteObject(brush
);
520 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
521 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
523 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
524 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
525 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
526 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
527 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
530 bool wxDC::DoFloodFill(wxCoord x
, wxCoord y
, const wxColour
& col
, int style
)
532 #ifdef __WXMICROWIN__
533 if (!GetHDC()) return FALSE
;
536 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
538 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
539 : FLOODFILLBORDER
) ) ;
542 // quoting from the MSDN docs:
544 // Following are some of the reasons this function might fail:
546 // * The filling could not be completed.
547 // * The specified point has the boundary color specified by the
548 // crColor parameter (if FLOODFILLBORDER was requested).
549 // * The specified point does not have the color specified by
550 // crColor (if FLOODFILLSURFACE was requested)
551 // * The point is outside the clipping region that is, it is not
552 // visible on the device.
554 wxLogLastError(wxT("ExtFloodFill"));
557 CalcBoundingBox(x
, y
);
562 bool wxDC::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
564 #ifdef __WXMICROWIN__
565 if (!GetHDC()) return FALSE
;
568 wxCHECK_MSG( col
, FALSE
, _T("NULL colour parameter in wxDC::GetPixel") );
570 // get the color of the pixel
571 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
573 wxRGBToColour(*col
, pixelcolor
);
578 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
580 #ifdef __WXMICROWIN__
581 if (!GetHDC()) return;
584 wxCoord x1
= x
-VIEWPORT_EXTENT
;
585 wxCoord y1
= y
-VIEWPORT_EXTENT
;
586 wxCoord x2
= x
+VIEWPORT_EXTENT
;
587 wxCoord y2
= y
+VIEWPORT_EXTENT
;
589 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), NULL
);
590 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y
));
592 (void)MoveToEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), NULL
);
593 (void)LineTo(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y2
));
595 CalcBoundingBox(x1
, y1
);
596 CalcBoundingBox(x2
, y2
);
599 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
601 #ifdef __WXMICROWIN__
602 if (!GetHDC()) return;
605 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), NULL
);
606 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y2
));
608 CalcBoundingBox(x1
, y1
);
609 CalcBoundingBox(x2
, y2
);
612 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
613 // and ending at (x2, y2)
614 void wxDC::DoDrawArc(wxCoord x1
, wxCoord y1
,
615 wxCoord x2
, wxCoord y2
,
616 wxCoord xc
, wxCoord yc
)
618 #ifdef __WXMICROWIN__
619 if (!GetHDC()) return;
622 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
626 double radius
= (double)sqrt(dx
*dx
+dy
*dy
);
627 wxCoord r
= (wxCoord
)radius
;
629 // treat the special case of full circle separately
630 if ( x1
== x2
&& y1
== y2
)
632 DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
636 wxCoord xx1
= XLOG2DEV(x1
);
637 wxCoord yy1
= YLOG2DEV(y1
);
638 wxCoord xx2
= XLOG2DEV(x2
);
639 wxCoord yy2
= YLOG2DEV(y2
);
640 wxCoord xxc
= XLOG2DEV(xc
);
641 wxCoord yyc
= YLOG2DEV(yc
);
642 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
644 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
645 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
646 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
647 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
649 if ( m_brush
.Ok() && m_brush
.GetStyle() != wxTRANSPARENT
)
651 // Have to add 1 to bottom-right corner of rectangle
652 // to make semi-circles look right (crooked line otherwise).
653 // Unfortunately this is not a reliable method, depends
654 // on the size of shape.
655 // TODO: figure out why this happens!
656 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
660 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
663 CalcBoundingBox(xc
- r
, yc
- r
);
664 CalcBoundingBox(xc
+ r
, yc
+ r
);
667 void wxDC::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
668 wxCoord width
, wxCoord height
)
670 #ifdef __WXMICROWIN__
671 if (!GetHDC()) return;
674 wxCoord x2
= x1
+ width
,
677 #if defined(__WIN32__) && !defined(__SYMANTEC__) && !defined(__WXMICROWIN__)
684 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
686 // In WIN16, draw a cross
687 HPEN blackPen
= ::CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
688 HPEN whiteBrush
= (HPEN
)::GetStockObject(WHITE_BRUSH
);
689 HPEN hPenOld
= (HPEN
)::SelectObject(GetHdc(), blackPen
);
690 HPEN hBrushOld
= (HPEN
)::SelectObject(GetHdc(), whiteBrush
);
691 ::SetROP2(GetHdc(), R2_COPYPEN
);
692 Rectangle(GetHdc(), x1
, y1
, x2
, y2
);
693 MoveToEx(GetHdc(), x1
, y1
, NULL
);
694 LineTo(GetHdc(), x2
, y2
);
695 MoveToEx(GetHdc(), x2
, y1
, NULL
);
696 LineTo(GetHdc(), x1
, y2
);
697 ::SelectObject(GetHdc(), hPenOld
);
698 ::SelectObject(GetHdc(), hBrushOld
);
699 ::DeleteObject(blackPen
);
702 CalcBoundingBox(x1
, y1
);
703 CalcBoundingBox(x2
, y2
);
706 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
708 #ifdef __WXMICROWIN__
709 if (!GetHDC()) return;
712 COLORREF color
= 0x00ffffff;
715 color
= m_pen
.GetColour().GetPixel();
718 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
720 CalcBoundingBox(x
, y
);
723 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
725 #ifdef __WXMICROWIN__
726 if (!GetHDC()) return;
729 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
731 // Do things less efficiently if we have offsets
732 if (xoffset
!= 0 || yoffset
!= 0)
734 POINT
*cpoints
= new POINT
[n
];
736 for (i
= 0; i
< n
; i
++)
738 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
739 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
741 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
743 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
744 (void)Polygon(GetHdc(), cpoints
, n
);
745 SetPolyFillMode(GetHdc(),prev
);
751 for (i
= 0; i
< n
; i
++)
752 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
754 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
755 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
756 SetPolyFillMode(GetHdc(),prev
);
760 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
762 #ifdef __WXMICROWIN__
763 if (!GetHDC()) return;
766 // Do things less efficiently if we have offsets
767 if (xoffset
!= 0 || yoffset
!= 0)
769 POINT
*cpoints
= new POINT
[n
];
771 for (i
= 0; i
< n
; i
++)
773 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
774 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
776 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
778 (void)Polyline(GetHdc(), cpoints
, n
);
784 for (i
= 0; i
< n
; i
++)
785 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
787 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
791 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
793 #ifdef __WXMICROWIN__
794 if (!GetHDC()) return;
797 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
799 wxCoord x2
= x
+ width
;
800 wxCoord y2
= y
+ height
;
802 if ((m_logicalFunction
== wxCOPY
) && (m_pen
.GetStyle() == wxTRANSPARENT
))
805 rect
.left
= XLOG2DEV(x
);
806 rect
.top
= YLOG2DEV(y
);
807 rect
.right
= XLOG2DEV(x2
);
808 rect
.bottom
= YLOG2DEV(y2
);
809 (void)FillRect(GetHdc(), &rect
, (HBRUSH
)m_brush
.GetResourceHandle() );
813 // Windows draws the filled rectangles without outline (i.e. drawn with a
814 // transparent pen) one pixel smaller in both directions and we want them
815 // to have the same size regardless of which pen is used - adjust
817 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
818 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
824 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
828 CalcBoundingBox(x
, y
);
829 CalcBoundingBox(x2
, y2
);
832 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
834 #ifdef __WXMICROWIN__
835 if (!GetHDC()) return;
838 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
840 // Now, a negative radius value is interpreted to mean
841 // 'the proportion of the smallest X or Y dimension'
845 double smallest
= 0.0;
850 radius
= (- radius
* smallest
);
853 wxCoord x2
= (x
+width
);
854 wxCoord y2
= (y
+height
);
856 // Windows draws the filled rectangles without outline (i.e. drawn with a
857 // transparent pen) one pixel smaller in both directions and we want them
858 // to have the same size regardless of which pen is used - adjust
859 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
865 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
866 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
868 CalcBoundingBox(x
, y
);
869 CalcBoundingBox(x2
, y2
);
872 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
874 #ifdef __WXMICROWIN__
875 if (!GetHDC()) return;
878 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
880 wxCoord x2
= (x
+width
);
881 wxCoord y2
= (y
+height
);
883 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
885 CalcBoundingBox(x
, y
);
886 CalcBoundingBox(x2
, y2
);
889 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
890 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
892 #ifdef __WXMICROWIN__
893 if (!GetHDC()) return;
896 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
901 int rx1
= XLOG2DEV(x
+w
/2);
902 int ry1
= YLOG2DEV(y
+h
/2);
909 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
910 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
911 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
912 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
914 // draw pie with NULL_PEN first and then outline otherwise a line is
915 // drawn from the start and end points to the centre
916 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
919 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
924 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
925 rx1
, ry1
-1, rx2
, ry2
-1);
928 ::SelectObject(GetHdc(), hpenOld
);
930 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
933 CalcBoundingBox(x
, y
);
934 CalcBoundingBox(x2
, y2
);
937 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
939 #ifdef __WXMICROWIN__
940 if (!GetHDC()) return;
943 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
946 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
948 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
951 CalcBoundingBox(x
, y
);
952 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
955 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
957 #ifdef __WXMICROWIN__
958 if (!GetHDC()) return;
961 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
963 int width
= bmp
.GetWidth(),
964 height
= bmp
.GetHeight();
966 HBITMAP hbmpMask
= 0;
970 #endif // wxUSE_PALETTE
972 if ( bmp
.HasAlpha() )
975 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
977 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, hdcMem
, bmp
) )
983 wxMask
*mask
= bmp
.GetMask();
985 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
989 // don't give assert here because this would break existing
990 // programs - just silently ignore useMask parameter
997 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
999 // On some systems, MaskBlt succeeds yet is much much slower
1000 // than the wxWindows fall-back implementation. So we need
1001 // to be able to switch this on and off at runtime.
1003 #if wxUSE_SYSTEM_OPTIONS
1004 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1008 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1009 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1011 wxPalette
*pal
= bmp
.GetPalette();
1012 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1014 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1015 ::RealizePalette(hdcMem
);
1017 #endif // wxUSE_PALETTE
1019 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1022 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1026 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1027 #endif // wxUSE_PALETTE
1029 ::SelectObject(hdcMem
, hOldBitmap
);
1036 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1039 memDC
.SelectObject(bmp
);
1041 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1043 memDC
.SelectObject(wxNullBitmap
);
1046 else // no mask, just use BitBlt()
1049 HDC memdc
= ::CreateCompatibleDC( cdc
);
1050 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1052 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1054 COLORREF old_textground
= ::GetTextColor(GetHdc());
1055 COLORREF old_background
= ::GetBkColor(GetHdc());
1056 if (m_textForegroundColour
.Ok())
1058 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1060 if (m_textBackgroundColour
.Ok())
1062 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1066 wxPalette
*pal
= bmp
.GetPalette();
1067 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1069 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1070 ::RealizePalette(memdc
);
1072 #endif // wxUSE_PALETTE
1074 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1075 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1079 ::SelectPalette(memdc
, oldPal
, FALSE
);
1080 #endif // wxUSE_PALETTE
1082 ::SelectObject( memdc
, hOldBitmap
);
1083 ::DeleteDC( memdc
);
1085 ::SetTextColor(GetHdc(), old_textground
);
1086 ::SetBkColor(GetHdc(), old_background
);
1090 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1092 #ifdef __WXMICROWIN__
1093 if (!GetHDC()) return;
1096 DrawAnyText(text
, x
, y
);
1098 // update the bounding box
1099 CalcBoundingBox(x
, y
);
1102 GetTextExtent(text
, &w
, &h
);
1103 CalcBoundingBox(x
+ w
, y
+ h
);
1106 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1108 #ifdef __WXMICROWIN__
1109 if (!GetHDC()) return;
1112 // prepare for drawing the text
1113 if ( m_textForegroundColour
.Ok() )
1114 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
1116 DWORD old_background
= 0;
1117 if ( m_textBackgroundColour
.Ok() )
1119 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1122 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
1125 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
1126 text
.c_str(), text
.length()) == 0 )
1128 wxLogLastError(wxT("TextOut"));
1131 // restore the old parameters (text foreground colour may be left because
1132 // it never is set to anything else, but background should remain
1133 // transparent even if we just drew an opaque string)
1134 if ( m_textBackgroundColour
.Ok() )
1135 (void)SetBkColor(GetHdc(), old_background
);
1137 SetBkMode(GetHdc(), TRANSPARENT
);
1140 void wxDC::DoDrawRotatedText(const wxString
& text
,
1141 wxCoord x
, wxCoord y
,
1144 #ifdef __WXMICROWIN__
1145 if (!GetHDC()) return;
1148 // we test that we have some font because otherwise we should still use the
1149 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1150 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1151 // font for drawing rotated fonts unfortunately)
1152 if ( (angle
== 0.0) && m_font
.Ok() )
1154 DoDrawText(text
, x
, y
);
1156 #ifndef __WXMICROWIN__
1159 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1160 // because it's not TrueType and so can't have non zero
1161 // orientation/escapement under Win9x
1162 wxFont font
= m_font
.Ok() ? m_font
: *wxSWISS_FONT
;
1163 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1165 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1167 wxLogLastError(wxT("GetObject(hfont)"));
1170 // GDI wants the angle in tenth of degree
1171 long angle10
= (long)(angle
* 10);
1172 lf
.lfEscapement
= angle10
;
1173 lf
. lfOrientation
= angle10
;
1175 hfont
= ::CreateFontIndirect(&lf
);
1178 wxLogLastError(wxT("CreateFont"));
1182 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1184 DrawAnyText(text
, x
, y
);
1186 (void)::SelectObject(GetHdc(), hfontOld
);
1187 (void)::DeleteObject(hfont
);
1190 // call the bounding box by adding all four vertices of the rectangle
1191 // containing the text to it (simpler and probably not slower than
1192 // determining which of them is really topmost/leftmost/...)
1194 GetTextExtent(text
, &w
, &h
);
1196 double rad
= DegToRad(angle
);
1198 // "upper left" and "upper right"
1199 CalcBoundingBox(x
, y
);
1200 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(h
*sin(rad
)));
1202 // "bottom left" and "bottom right"
1203 x
+= (wxCoord
)(h
*sin(rad
));
1204 y
+= (wxCoord
)(h
*cos(rad
));
1205 CalcBoundingBox(x
, y
);
1206 CalcBoundingBox(x
+ wxCoord(h
*sin(rad
)), y
+ wxCoord(h
*cos(rad
)));
1211 // ---------------------------------------------------------------------------
1213 // ---------------------------------------------------------------------------
1217 void wxDC::DoSelectPalette(bool realize
)
1219 #ifdef __WXMICROWIN__
1220 if (!GetHDC()) return;
1223 // Set the old object temporarily, in case the assignment deletes an object
1224 // that's not yet selected out.
1227 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1231 if ( m_palette
.Ok() )
1233 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1234 GetHpaletteOf(m_palette
),
1237 m_oldPalette
= (WXHPALETTE
) oldPal
;
1240 ::RealizePalette(GetHdc());
1244 void wxDC::SetPalette(const wxPalette
& palette
)
1248 m_palette
= palette
;
1249 DoSelectPalette(TRUE
);
1253 void wxDC::InitializePalette()
1255 if ( wxDisplayDepth() <= 8 )
1257 // look for any window or parent that has a custom palette. If any has
1258 // one then we need to use it in drawing operations
1259 wxWindow
*win
= m_canvas
->GetAncestorWithCustomPalette();
1261 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1262 if ( m_hasCustomPalette
)
1264 m_palette
= win
->GetPalette();
1266 // turn on MSW translation for this palette
1272 #endif // wxUSE_PALETTE
1274 void wxDC::SetFont(const wxFont
& the_font
)
1276 #ifdef __WXMICROWIN__
1277 if (!GetHDC()) return;
1280 // Set the old object temporarily, in case the assignment deletes an object
1281 // that's not yet selected out.
1284 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1293 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1297 if (m_font
.Ok() && m_font
.GetResourceHandle())
1299 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
1300 if (f
== (HFONT
) NULL
)
1302 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
1305 m_oldFont
= (WXHFONT
) f
;
1309 void wxDC::SetPen(const wxPen
& pen
)
1311 #ifdef __WXMICROWIN__
1312 if (!GetHDC()) return;
1315 // Set the old object temporarily, in case the assignment deletes an object
1316 // that's not yet selected out.
1319 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1328 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1334 if (m_pen
.GetResourceHandle())
1336 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
1338 m_oldPen
= (WXHPEN
) p
;
1343 void wxDC::SetBrush(const wxBrush
& brush
)
1345 #ifdef __WXMICROWIN__
1346 if (!GetHDC()) return;
1349 // Set the old object temporarily, in case the assignment deletes an object
1350 // that's not yet selected out.
1353 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1362 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1368 // to make sure the brush is alligned with the logical coordinates
1369 wxBitmap
*stipple
= m_brush
.GetStipple();
1370 if ( stipple
&& stipple
->Ok() )
1373 ::SetBrushOrgEx(GetHdc(),
1374 m_deviceOriginX
% stipple
->GetWidth(),
1375 m_deviceOriginY
% stipple
->GetHeight(),
1376 NULL
); // don't need previous brush origin
1378 ::SetBrushOrg(GetHdc(),
1379 m_deviceOriginX
% stipple
->GetWidth(),
1380 m_deviceOriginY
% stipple
->GetHeight());
1384 if ( m_brush
.GetResourceHandle() )
1387 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
1389 m_oldBrush
= (WXHBRUSH
) b
;
1394 void wxDC::SetBackground(const wxBrush
& brush
)
1396 #ifdef __WXMICROWIN__
1397 if (!GetHDC()) return;
1400 m_backgroundBrush
= brush
;
1402 if ( m_backgroundBrush
.Ok() )
1404 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1408 void wxDC::SetBackgroundMode(int mode
)
1410 #ifdef __WXMICROWIN__
1411 if (!GetHDC()) return;
1414 m_backgroundMode
= mode
;
1416 // SetBackgroundColour now only refers to text background
1417 // and m_backgroundMode is used there
1420 void wxDC::SetLogicalFunction(int function
)
1422 #ifdef __WXMICROWIN__
1423 if (!GetHDC()) return;
1426 m_logicalFunction
= function
;
1431 void wxDC::SetRop(WXHDC dc
)
1433 if ( !dc
|| m_logicalFunction
< 0 )
1438 switch (m_logicalFunction
)
1440 case wxCLEAR
: rop
= R2_BLACK
; break;
1441 case wxXOR
: rop
= R2_XORPEN
; break;
1442 case wxINVERT
: rop
= R2_NOT
; break;
1443 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1444 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1445 case wxCOPY
: rop
= R2_COPYPEN
; break;
1446 case wxAND
: rop
= R2_MASKPEN
; break;
1447 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1448 case wxNO_OP
: rop
= R2_NOP
; break;
1449 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1450 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1451 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1452 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1453 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1454 case wxOR
: rop
= R2_MERGEPEN
; break;
1455 case wxSET
: rop
= R2_WHITE
; break;
1458 wxFAIL_MSG( wxT("unsupported logical function") );
1462 SetROP2(GetHdc(), rop
);
1465 bool wxDC::StartDoc(const wxString
& WXUNUSED(message
))
1467 // We might be previewing, so return TRUE to let it continue.
1475 void wxDC::StartPage()
1479 void wxDC::EndPage()
1483 // ---------------------------------------------------------------------------
1485 // ---------------------------------------------------------------------------
1487 wxCoord
wxDC::GetCharHeight() const
1489 #ifdef __WXMICROWIN__
1490 if (!GetHDC()) return 0;
1493 TEXTMETRIC lpTextMetric
;
1495 GetTextMetrics(GetHdc(), &lpTextMetric
);
1497 return lpTextMetric
.tmHeight
;
1500 wxCoord
wxDC::GetCharWidth() const
1502 #ifdef __WXMICROWIN__
1503 if (!GetHDC()) return 0;
1506 TEXTMETRIC lpTextMetric
;
1508 GetTextMetrics(GetHdc(), &lpTextMetric
);
1510 return lpTextMetric
.tmAveCharWidth
;
1513 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1514 wxCoord
*descent
, wxCoord
*externalLeading
,
1517 #ifdef __WXMICROWIN__
1522 if (descent
) *descent
= 0;
1523 if (externalLeading
) *externalLeading
= 0;
1526 #endif // __WXMICROWIN__
1531 wxASSERT_MSG( font
->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1533 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1535 else // don't change the font
1543 GetTextExtentPoint(GetHdc(), string
, string
.length(), &sizeRect
);
1544 GetTextMetrics(GetHdc(), &tm
);
1551 *descent
= tm
.tmDescent
;
1552 if (externalLeading
)
1553 *externalLeading
= tm
.tmExternalLeading
;
1557 ::SelectObject(GetHdc(), hfontOld
);
1561 void wxDC::SetMapMode(int mode
)
1563 #ifdef __WXMICROWIN__
1564 if (!GetHDC()) return;
1567 m_mappingMode
= mode
;
1569 if ( mode
== wxMM_TEXT
)
1572 m_logicalScaleY
= 1.0;
1574 else // need to do some calculations
1576 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1577 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1578 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1579 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1581 if ( (mm_width
== 0) || (mm_height
== 0) )
1583 // we can't calculate mm2pixels[XY] then!
1587 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1588 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1593 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1594 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1598 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1599 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1603 m_logicalScaleX
= mm2pixelsX
;
1604 m_logicalScaleY
= mm2pixelsY
;
1608 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1609 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1613 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1617 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1618 // cases we could do with MM_TEXT and in the remaining 0.9% with
1619 // MM_ISOTROPIC (TODO!)
1620 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1622 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1623 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1625 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1626 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1628 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1629 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1632 void wxDC::SetUserScale(double x
, double y
)
1634 #ifdef __WXMICROWIN__
1635 if (!GetHDC()) return;
1638 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1644 SetMapMode(m_mappingMode
);
1647 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1649 #ifdef __WXMICROWIN__
1650 if (!GetHDC()) return;
1653 int signX
= xLeftRight
? 1 : -1,
1654 signY
= yBottomUp
? -1 : 1;
1656 if ( signX
!= m_signX
|| signY
!= m_signY
)
1661 SetMapMode(m_mappingMode
);
1665 void wxDC::SetSystemScale(double x
, double y
)
1667 #ifdef __WXMICROWIN__
1668 if (!GetHDC()) return;
1671 if ( x
== m_scaleX
&& y
== m_scaleY
)
1677 SetMapMode(m_mappingMode
);
1680 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1682 #ifdef __WXMICROWIN__
1683 if (!GetHDC()) return;
1686 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1689 m_logicalOriginX
= x
;
1690 m_logicalOriginY
= y
;
1692 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1695 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1697 #ifdef __WXMICROWIN__
1698 if (!GetHDC()) return;
1701 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
1704 m_deviceOriginX
= x
;
1705 m_deviceOriginY
= y
;
1707 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1710 // ---------------------------------------------------------------------------
1711 // coordinates transformations
1712 // ---------------------------------------------------------------------------
1714 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1716 return DeviceToLogicalXRel(x
- m_deviceOriginX
)*m_signX
+ m_logicalOriginX
;
1719 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1721 // axis orientation is not taken into account for conversion of a distance
1722 return (wxCoord
)(x
/ (m_logicalScaleX
*m_userScaleX
*m_scaleX
));
1725 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1727 return DeviceToLogicalYRel(y
- m_deviceOriginY
)*m_signY
+ m_logicalOriginY
;
1730 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1732 // axis orientation is not taken into account for conversion of a distance
1733 return (wxCoord
)( y
/ (m_logicalScaleY
*m_userScaleY
*m_scaleY
));
1736 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1738 return LogicalToDeviceXRel(x
- m_logicalOriginX
)*m_signX
+ m_deviceOriginX
;
1741 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1743 // axis orientation is not taken into account for conversion of a distance
1744 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_scaleX
);
1747 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1749 return LogicalToDeviceYRel(y
- m_logicalOriginY
)*m_signY
+ m_deviceOriginY
;
1752 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1754 // axis orientation is not taken into account for conversion of a distance
1755 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_scaleY
);
1758 // ---------------------------------------------------------------------------
1760 // ---------------------------------------------------------------------------
1762 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1763 wxCoord width
, wxCoord height
,
1764 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1765 int rop
, bool useMask
,
1766 wxCoord xsrcMask
, wxCoord ysrcMask
)
1768 wxCHECK_MSG( source
, FALSE
, _T("wxDC::Blit(): NULL wxDC pointer") );
1770 #ifdef __WXMICROWIN__
1771 if (!GetHDC()) return FALSE
;
1774 const wxBitmap
& bmpSrc
= source
->m_selectedBitmap
;
1775 if ( bmpSrc
.Ok() && bmpSrc
.HasAlpha() )
1777 if ( AlphaBlt(GetHdc(), xdest
, ydest
, width
, height
,
1778 GetHdcOf(*source
), bmpSrc
) )
1782 wxMask
*mask
= NULL
;
1785 mask
= bmpSrc
.GetMask();
1787 if ( !(bmpSrc
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1789 // don't give assert here because this would break existing
1790 // programs - just silently ignore useMask parameter
1795 if (xsrcMask
== -1 && ysrcMask
== -1)
1797 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
1800 COLORREF old_textground
= ::GetTextColor(GetHdc());
1801 COLORREF old_background
= ::GetBkColor(GetHdc());
1802 if (m_textForegroundColour
.Ok())
1804 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1806 if (m_textBackgroundColour
.Ok())
1808 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1811 DWORD dwRop
= SRCCOPY
;
1814 case wxXOR
: dwRop
= SRCINVERT
; break;
1815 case wxINVERT
: dwRop
= DSTINVERT
; break;
1816 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1817 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1818 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1819 case wxSET
: dwRop
= WHITENESS
; break;
1820 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1821 case wxAND
: dwRop
= SRCAND
; break;
1822 case wxOR
: dwRop
= SRCPAINT
; break;
1823 case wxEQUIV
: dwRop
= 0x00990066; break;
1824 case wxNAND
: dwRop
= 0x007700E6; break;
1825 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1826 case wxCOPY
: dwRop
= SRCCOPY
; break;
1827 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1828 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1829 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1831 wxFAIL_MSG( wxT("unsupported logical function") );
1835 bool success
= FALSE
;
1840 // we want the part of the image corresponding to the mask to be
1841 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1842 // meaning of fg and bg is inverted which corresponds to wxWin notion
1843 // of the mask which is also contrary to the Windows one)
1845 // On some systems, MaskBlt succeeds yet is much much slower
1846 // than the wxWindows fall-back implementation. So we need
1847 // to be able to switch this on and off at runtime.
1848 #if wxUSE_SYSTEM_OPTIONS
1849 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1855 xdest
, ydest
, width
, height
,
1858 (HBITMAP
)mask
->GetMaskBitmap(),
1860 MAKEROP4(dwRop
, DSTCOPY
)
1867 // Blit bitmap with mask
1870 HBITMAP buffer_bmap
;
1872 #if wxUSE_DC_CACHEING
1873 // create a temp buffer bitmap and DCs to access it and the mask
1874 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, source
->GetHDC());
1875 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
1877 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
1878 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
1880 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
1883 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
1884 #else // !wxUSE_DC_CACHEING
1885 // create a temp buffer bitmap and DCs to access it and the mask
1886 dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1887 dc_buffer
= ::CreateCompatibleDC(GetHdc());
1888 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1889 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1890 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1891 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
1893 // copy dest to buffer
1894 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1895 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1897 wxLogLastError(wxT("BitBlt"));
1900 // copy src to buffer using selected raster op
1901 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1902 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1904 wxLogLastError(wxT("BitBlt"));
1907 // set masked area in buffer to BLACK (pixel value 0)
1908 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1909 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1910 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1911 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1913 wxLogLastError(wxT("BitBlt"));
1916 // set unmasked area in dest to BLACK
1917 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1918 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1919 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1920 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1922 wxLogLastError(wxT("BitBlt"));
1924 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1925 ::SetTextColor(GetHdc(), prevCol
);
1927 // OR buffer to dest
1928 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1929 (int)width
, (int)height
,
1930 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1933 wxLogLastError(wxT("BitBlt"));
1936 // tidy up temporary DCs and bitmap
1937 ::SelectObject(dc_mask
, hOldMaskBitmap
);
1938 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
1940 #if !wxUSE_DC_CACHEING
1942 ::DeleteDC(dc_mask
);
1943 ::DeleteDC(dc_buffer
);
1944 ::DeleteObject(buffer_bmap
);
1949 else // no mask, just BitBlt() it
1951 // if we already have a DIB, draw it using StretchDIBits(), otherwise
1952 // use StretchBlt() if available and finally fall back to BitBlt()
1953 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
1954 if ( bmpSrc
.Ok() && (caps
& RC_STRETCHDIB
) )
1959 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
1961 &ds
) == sizeof(ds
) )
1963 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
1965 if ( ::StretchDIBits(GetHdc(),
1971 (LPBITMAPINFO
)&ds
.dsBmih
,
1974 ) == (int)GDI_ERROR
)
1976 wxLogLastError(wxT("StretchDIBits"));
1985 if ( !success
&& (caps
& RC_STRETCHBLT
) )
1987 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
1992 xdest
, ydest
, width
, height
,
1994 xsrc
, ysrc
, width
, height
,
1998 wxLogLastError(_T("StretchBlt"));
2012 (int)width
, (int)height
,
2018 wxLogLastError(_T("BitBlt"));
2027 ::SetTextColor(GetHdc(), old_textground
);
2028 ::SetBkColor(GetHdc(), old_background
);
2033 void wxDC::DoGetSize(int *w
, int *h
) const
2035 #ifdef __WXMICROWIN__
2036 if (!GetHDC()) return;
2039 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2040 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2043 void wxDC::DoGetSizeMM(int *w
, int *h
) const
2045 #ifdef __WXMICROWIN__
2046 if (!GetHDC()) return;
2049 // if we implement it in terms of DoGetSize() instead of directly using the
2050 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2051 // will also work for wxWindowDC and wxClientDC even though their size is
2052 // not the same as the total size of the screen
2053 int wPixels
, hPixels
;
2054 DoGetSize(&wPixels
, &hPixels
);
2058 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2060 wxCHECK_RET( wTotal
, _T("0 width device?") );
2062 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2067 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2069 wxCHECK_RET( hTotal
, _T("0 height device?") );
2071 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2075 wxSize
wxDC::GetPPI() const
2077 #ifdef __WXMICROWIN__
2078 if (!GetHDC()) return wxSize();
2081 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2082 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2084 return wxSize(x
, y
);
2087 // For use by wxWindows only, unless custom units are required.
2088 void wxDC::SetLogicalScale(double x
, double y
)
2090 #ifdef __WXMICROWIN__
2091 if (!GetHDC()) return;
2094 m_logicalScaleX
= x
;
2095 m_logicalScaleY
= y
;
2098 // ----------------------------------------------------------------------------
2100 // ----------------------------------------------------------------------------
2102 #if wxUSE_DC_CACHEING
2105 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2106 * improve it in due course, either using arrays, or simply storing pointers to one
2107 * entry for the bitmap, and two for the DCs. -- JACS
2110 wxList
wxDC::sm_bitmapCache
;
2111 wxList
wxDC::sm_dcCache
;
2113 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2122 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2131 wxDCCacheEntry::~wxDCCacheEntry()
2134 ::DeleteObject((HBITMAP
) m_bitmap
);
2136 ::DeleteDC((HDC
) m_dc
);
2139 wxDCCacheEntry
* wxDC::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2141 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2142 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2145 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2147 if (entry
->m_depth
== depth
)
2149 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2151 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2152 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2153 if ( !entry
->m_bitmap
)
2155 wxLogLastError(wxT("CreateCompatibleBitmap"));
2157 entry
->m_width
= w
; entry
->m_height
= h
;
2163 node
= node
->GetNext();
2165 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2168 wxLogLastError(wxT("CreateCompatibleBitmap"));
2170 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2171 AddToBitmapCache(entry
);
2175 wxDCCacheEntry
* wxDC::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2177 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2178 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2181 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2183 // Don't return the same one as we already have
2184 if (!notThis
|| (notThis
!= entry
))
2186 if (entry
->m_depth
== depth
)
2192 node
= node
->GetNext();
2194 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2197 wxLogLastError(wxT("CreateCompatibleDC"));
2199 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2200 AddToDCCache(entry
);
2204 void wxDC::AddToBitmapCache(wxDCCacheEntry
* entry
)
2206 sm_bitmapCache
.Append(entry
);
2209 void wxDC::AddToDCCache(wxDCCacheEntry
* entry
)
2211 sm_dcCache
.Append(entry
);
2214 void wxDC::ClearCache()
2216 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2217 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2220 // Clean up cache at app exit
2221 class wxDCModule
: public wxModule
2224 virtual bool OnInit() { return TRUE
; }
2225 virtual void OnExit() { wxDC::ClearCache(); }
2228 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2231 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2233 #endif // wxUSE_DC_CACHEING
2235 // ----------------------------------------------------------------------------
2236 // alpha channel support
2237 // ----------------------------------------------------------------------------
2239 static bool AlphaBlt(HDC hdcDst
,
2240 int x
, int y
, int width
, int height
,
2242 const wxBitmap
& bmp
)
2244 wxASSERT_MSG( bmp
.Ok() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2245 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2247 // do we have AlphaBlend() and company in the headers?
2248 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2249 // yes, now try to see if we have it during run-time
2250 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2251 HDC
,int,int,int,int,
2254 // bitmaps can be drawn only from GUI thread so there is no need to
2255 // protect this static variable from multiple threads
2256 static bool s_triedToLoad
= FALSE
;
2257 static AlphaBlend_t pfnAlphaBlend
= NULL
;
2258 if ( !s_triedToLoad
)
2260 s_triedToLoad
= TRUE
;
2262 // don't give errors about the DLL being unavailable, we're
2263 // prepared to handle this
2266 wxDynamicLibrary
dll(_T("msimg32.dll"));
2267 if ( dll
.IsLoaded() )
2269 pfnAlphaBlend
= (AlphaBlend_t
)dll
.GetSymbol(_T("AlphaBlend"));
2270 if ( pfnAlphaBlend
)
2272 // we must keep the DLL loaded if we want to be able to
2273 // call AlphaBlend() so just never unload it at all, not a
2280 if ( pfnAlphaBlend
)
2283 bf
.BlendOp
= AC_SRC_OVER
;
2285 bf
.SourceConstantAlpha
= 0xff;
2286 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2288 if ( pfnAlphaBlend(hdcDst
, x
, y
, width
, height
,
2289 hdcSrc
, 0, 0, width
, height
,
2292 // skip wxAlphaBlend() call below
2296 wxLogLastError(_T("AlphaBlend"));
2298 #endif // defined(AC_SRC_OVER)
2300 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2302 #ifdef wxHAVE_RAW_BITMAP
2303 wxAlphaBlend(hdcDst
, x
, y
, width
, height
, bmp
);
2306 #else // !wxHAVE_RAW_BITMAP
2307 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2308 // alpha but at least something will be shown like this)
2310 #endif // wxHAVE_RAW_BITMAP
2314 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2315 #ifdef wxHAVE_RAW_BITMAP
2318 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
, int w
, int h
, const wxBitmap
& bmpSrc
)
2320 // get the destination DC pixels
2321 wxBitmap
bmpDst(w
, h
, 32 /* force creating RGBA DIB */);
2323 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2325 if ( !::BitBlt(hdcMem
, 0, 0, w
, h
, hdcDst
, 0, 0, SRCCOPY
) )
2327 wxLogLastError(_T("BitBlt"));
2330 // combine them with the source bitmap using alpha
2331 wxAlphaPixelData
dataDst(bmpDst
),
2332 dataSrc((wxBitmap
&)bmpSrc
);
2334 wxCHECK_RET( dataDst
&& dataSrc
,
2335 _T("failed to get raw data in wxAlphaBlend") );
2337 wxAlphaPixelData::Iterator
pDst(dataDst
),
2340 for ( int y
= 0; y
< h
; y
++ )
2342 wxAlphaPixelData::Iterator pDstRowStart
= pDst
,
2343 pSrcRowStart
= pSrc
;
2345 for ( int x
= 0; x
< w
; x
++ )
2347 // note that source bitmap uses premultiplied alpha (as required by
2348 // the real AlphaBlend)
2349 const unsigned beta
= 255 - pSrc
.Alpha();
2351 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2352 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2353 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2359 pDst
= pDstRowStart
;
2360 pSrc
= pSrcRowStart
;
2361 pDst
.OffsetY(dataDst
, 1);
2362 pSrc
.OffsetY(dataSrc
, 1);
2365 // and finally blit them back to the destination DC
2366 if ( !::BitBlt(hdcDst
, xDst
, yDst
, w
, h
, hdcMem
, 0, 0, SRCCOPY
) )
2368 wxLogLastError(_T("BitBlt"));
2372 #endif // #ifdef wxHAVE_RAW_BITMAP