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())
1407 bool customColours
= TRUE
;
1408 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
1409 // change background colours from the control-panel specified colours.
1410 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
1411 customColours
= FALSE
;
1415 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
1417 m_canvas
->SetTransparent(TRUE
);
1421 // New behaviour, 10/2/99: setting the background brush of a DC
1422 // doesn't affect the window background colour. However,
1423 // I'm leaving in the transparency setting because it's needed by
1424 // various controls (e.g. wxStaticText) to determine whether to draw
1425 // transparently or not. TODO: maybe this should be a new function
1426 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1428 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1429 m_canvas
->SetTransparent(FALSE
);
1433 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1435 (void)SetBkColor(GetHdc(), new_color
);
1439 void wxDC::SetBackgroundMode(int mode
)
1441 #ifdef __WXMICROWIN__
1442 if (!GetHDC()) return;
1445 m_backgroundMode
= mode
;
1447 // SetBackgroundColour now only refers to text background
1448 // and m_backgroundMode is used there
1451 void wxDC::SetLogicalFunction(int function
)
1453 #ifdef __WXMICROWIN__
1454 if (!GetHDC()) return;
1457 m_logicalFunction
= function
;
1462 void wxDC::SetRop(WXHDC dc
)
1464 if ( !dc
|| m_logicalFunction
< 0 )
1469 switch (m_logicalFunction
)
1471 case wxCLEAR
: rop
= R2_BLACK
; break;
1472 case wxXOR
: rop
= R2_XORPEN
; break;
1473 case wxINVERT
: rop
= R2_NOT
; break;
1474 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1475 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1476 case wxCOPY
: rop
= R2_COPYPEN
; break;
1477 case wxAND
: rop
= R2_MASKPEN
; break;
1478 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1479 case wxNO_OP
: rop
= R2_NOP
; break;
1480 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1481 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1482 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1483 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1484 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1485 case wxOR
: rop
= R2_MERGEPEN
; break;
1486 case wxSET
: rop
= R2_WHITE
; break;
1489 wxFAIL_MSG( wxT("unsupported logical function") );
1493 SetROP2(GetHdc(), rop
);
1496 bool wxDC::StartDoc(const wxString
& WXUNUSED(message
))
1498 // We might be previewing, so return TRUE to let it continue.
1506 void wxDC::StartPage()
1510 void wxDC::EndPage()
1514 // ---------------------------------------------------------------------------
1516 // ---------------------------------------------------------------------------
1518 wxCoord
wxDC::GetCharHeight() const
1520 #ifdef __WXMICROWIN__
1521 if (!GetHDC()) return 0;
1524 TEXTMETRIC lpTextMetric
;
1526 GetTextMetrics(GetHdc(), &lpTextMetric
);
1528 return lpTextMetric
.tmHeight
;
1531 wxCoord
wxDC::GetCharWidth() const
1533 #ifdef __WXMICROWIN__
1534 if (!GetHDC()) return 0;
1537 TEXTMETRIC lpTextMetric
;
1539 GetTextMetrics(GetHdc(), &lpTextMetric
);
1541 return lpTextMetric
.tmAveCharWidth
;
1544 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1545 wxCoord
*descent
, wxCoord
*externalLeading
,
1548 #ifdef __WXMICROWIN__
1553 if (descent
) *descent
= 0;
1554 if (externalLeading
) *externalLeading
= 0;
1557 #endif // __WXMICROWIN__
1562 wxASSERT_MSG( font
->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1564 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1566 else // don't change the font
1574 GetTextExtentPoint(GetHdc(), string
, string
.length(), &sizeRect
);
1575 GetTextMetrics(GetHdc(), &tm
);
1582 *descent
= tm
.tmDescent
;
1583 if (externalLeading
)
1584 *externalLeading
= tm
.tmExternalLeading
;
1588 ::SelectObject(GetHdc(), hfontOld
);
1592 void wxDC::SetMapMode(int mode
)
1594 #ifdef __WXMICROWIN__
1595 if (!GetHDC()) return;
1598 m_mappingMode
= mode
;
1600 if ( mode
== wxMM_TEXT
)
1603 m_logicalScaleY
= 1.0;
1605 else // need to do some calculations
1607 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1608 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1609 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1610 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1612 if ( (mm_width
== 0) || (mm_height
== 0) )
1614 // we can't calculate mm2pixels[XY] then!
1618 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1619 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1624 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1625 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1629 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1630 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1634 m_logicalScaleX
= mm2pixelsX
;
1635 m_logicalScaleY
= mm2pixelsY
;
1639 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1640 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1644 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1648 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1649 // cases we could do with MM_TEXT and in the remaining 0.9% with
1650 // MM_ISOTROPIC (TODO!)
1651 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1653 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1654 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1656 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1657 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1659 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1660 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1663 void wxDC::SetUserScale(double x
, double y
)
1665 #ifdef __WXMICROWIN__
1666 if (!GetHDC()) return;
1669 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1675 SetMapMode(m_mappingMode
);
1678 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1680 #ifdef __WXMICROWIN__
1681 if (!GetHDC()) return;
1684 int signX
= xLeftRight
? 1 : -1,
1685 signY
= yBottomUp
? -1 : 1;
1687 if ( signX
!= m_signX
|| signY
!= m_signY
)
1692 SetMapMode(m_mappingMode
);
1696 void wxDC::SetSystemScale(double x
, double y
)
1698 #ifdef __WXMICROWIN__
1699 if (!GetHDC()) return;
1702 if ( x
== m_scaleX
&& y
== m_scaleY
)
1708 SetMapMode(m_mappingMode
);
1711 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1713 #ifdef __WXMICROWIN__
1714 if (!GetHDC()) return;
1717 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1720 m_logicalOriginX
= x
;
1721 m_logicalOriginY
= y
;
1723 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1726 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1728 #ifdef __WXMICROWIN__
1729 if (!GetHDC()) return;
1732 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
1735 m_deviceOriginX
= x
;
1736 m_deviceOriginY
= y
;
1738 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1741 // ---------------------------------------------------------------------------
1742 // coordinates transformations
1743 // ---------------------------------------------------------------------------
1745 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1747 return DeviceToLogicalXRel(x
- m_deviceOriginX
)*m_signX
+ m_logicalOriginX
;
1750 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1752 // axis orientation is not taken into account for conversion of a distance
1753 return (wxCoord
)(x
/ (m_logicalScaleX
*m_userScaleX
*m_scaleX
));
1756 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1758 return DeviceToLogicalYRel(y
- m_deviceOriginY
)*m_signY
+ m_logicalOriginY
;
1761 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1763 // axis orientation is not taken into account for conversion of a distance
1764 return (wxCoord
)( y
/ (m_logicalScaleY
*m_userScaleY
*m_scaleY
));
1767 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1769 return LogicalToDeviceXRel(x
- m_logicalOriginX
)*m_signX
+ m_deviceOriginX
;
1772 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1774 // axis orientation is not taken into account for conversion of a distance
1775 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_scaleX
);
1778 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1780 return LogicalToDeviceYRel(y
- m_logicalOriginY
)*m_signY
+ m_deviceOriginY
;
1783 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1785 // axis orientation is not taken into account for conversion of a distance
1786 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_scaleY
);
1789 // ---------------------------------------------------------------------------
1791 // ---------------------------------------------------------------------------
1793 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1794 wxCoord width
, wxCoord height
,
1795 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1796 int rop
, bool useMask
,
1797 wxCoord xsrcMask
, wxCoord ysrcMask
)
1799 wxCHECK_MSG( source
, FALSE
, _T("wxDC::Blit(): NULL wxDC pointer") );
1801 #ifdef __WXMICROWIN__
1802 if (!GetHDC()) return FALSE
;
1805 const wxBitmap
& bmpSrc
= source
->m_selectedBitmap
;
1806 if ( bmpSrc
.Ok() && bmpSrc
.HasAlpha() )
1808 if ( AlphaBlt(GetHdc(), xdest
, ydest
, width
, height
,
1809 GetHdcOf(*source
), bmpSrc
) )
1813 wxMask
*mask
= NULL
;
1816 mask
= bmpSrc
.GetMask();
1818 if ( !(bmpSrc
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1820 // don't give assert here because this would break existing
1821 // programs - just silently ignore useMask parameter
1826 if (xsrcMask
== -1 && ysrcMask
== -1)
1828 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
1831 COLORREF old_textground
= ::GetTextColor(GetHdc());
1832 COLORREF old_background
= ::GetBkColor(GetHdc());
1833 if (m_textForegroundColour
.Ok())
1835 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1837 if (m_textBackgroundColour
.Ok())
1839 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1842 DWORD dwRop
= SRCCOPY
;
1845 case wxXOR
: dwRop
= SRCINVERT
; break;
1846 case wxINVERT
: dwRop
= DSTINVERT
; break;
1847 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1848 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1849 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1850 case wxSET
: dwRop
= WHITENESS
; break;
1851 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1852 case wxAND
: dwRop
= SRCAND
; break;
1853 case wxOR
: dwRop
= SRCPAINT
; break;
1854 case wxEQUIV
: dwRop
= 0x00990066; break;
1855 case wxNAND
: dwRop
= 0x007700E6; break;
1856 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1857 case wxCOPY
: dwRop
= SRCCOPY
; break;
1858 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1859 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1860 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1862 wxFAIL_MSG( wxT("unsupported logical function") );
1866 bool success
= FALSE
;
1871 // we want the part of the image corresponding to the mask to be
1872 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1873 // meaning of fg and bg is inverted which corresponds to wxWin notion
1874 // of the mask which is also contrary to the Windows one)
1876 // On some systems, MaskBlt succeeds yet is much much slower
1877 // than the wxWindows fall-back implementation. So we need
1878 // to be able to switch this on and off at runtime.
1879 #if wxUSE_SYSTEM_OPTIONS
1880 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1886 xdest
, ydest
, width
, height
,
1889 (HBITMAP
)mask
->GetMaskBitmap(),
1891 MAKEROP4(dwRop
, DSTCOPY
)
1898 // Blit bitmap with mask
1901 HBITMAP buffer_bmap
;
1903 #if wxUSE_DC_CACHEING
1904 // create a temp buffer bitmap and DCs to access it and the mask
1905 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, source
->GetHDC());
1906 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
1908 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
1909 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
1911 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
1914 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
1915 #else // !wxUSE_DC_CACHEING
1916 // create a temp buffer bitmap and DCs to access it and the mask
1917 dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1918 dc_buffer
= ::CreateCompatibleDC(GetHdc());
1919 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1920 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1921 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1922 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
1924 // copy dest to buffer
1925 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1926 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1928 wxLogLastError(wxT("BitBlt"));
1931 // copy src to buffer using selected raster op
1932 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1933 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1935 wxLogLastError(wxT("BitBlt"));
1938 // set masked area in buffer to BLACK (pixel value 0)
1939 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1940 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1941 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1942 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1944 wxLogLastError(wxT("BitBlt"));
1947 // set unmasked area in dest to BLACK
1948 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1949 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1950 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1951 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1953 wxLogLastError(wxT("BitBlt"));
1955 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1956 ::SetTextColor(GetHdc(), prevCol
);
1958 // OR buffer to dest
1959 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1960 (int)width
, (int)height
,
1961 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1964 wxLogLastError(wxT("BitBlt"));
1967 // tidy up temporary DCs and bitmap
1968 ::SelectObject(dc_mask
, hOldMaskBitmap
);
1969 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
1971 #if !wxUSE_DC_CACHEING
1973 ::DeleteDC(dc_mask
);
1974 ::DeleteDC(dc_buffer
);
1975 ::DeleteObject(buffer_bmap
);
1980 else // no mask, just BitBlt() it
1982 // if we already have a DIB, draw it using StretchDIBits(), otherwise
1983 // use StretchBlt() if available and finally fall back to BitBlt()
1984 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
1985 if ( bmpSrc
.Ok() && (caps
& RC_STRETCHDIB
) )
1990 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
1992 &ds
) == sizeof(ds
) )
1994 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
1996 if ( ::StretchDIBits(GetHdc(),
2002 (LPBITMAPINFO
)&ds
.dsBmih
,
2005 ) == (int)GDI_ERROR
)
2007 wxLogLastError(wxT("StretchDIBits"));
2016 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2018 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2023 xdest
, ydest
, width
, height
,
2025 xsrc
, ysrc
, width
, height
,
2029 wxLogLastError(_T("StretchBlt"));
2043 (int)width
, (int)height
,
2049 wxLogLastError(_T("BitBlt"));
2058 ::SetTextColor(GetHdc(), old_textground
);
2059 ::SetBkColor(GetHdc(), old_background
);
2064 void wxDC::DoGetSize(int *w
, int *h
) const
2066 #ifdef __WXMICROWIN__
2067 if (!GetHDC()) return;
2070 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2071 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2074 void wxDC::DoGetSizeMM(int *w
, int *h
) const
2076 #ifdef __WXMICROWIN__
2077 if (!GetHDC()) return;
2080 // if we implement it in terms of DoGetSize() instead of directly using the
2081 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2082 // will also work for wxWindowDC and wxClientDC even though their size is
2083 // not the same as the total size of the screen
2084 int wPixels
, hPixels
;
2085 DoGetSize(&wPixels
, &hPixels
);
2089 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2091 wxCHECK_RET( wTotal
, _T("0 width device?") );
2093 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2098 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2100 wxCHECK_RET( hTotal
, _T("0 height device?") );
2102 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2106 wxSize
wxDC::GetPPI() const
2108 #ifdef __WXMICROWIN__
2109 if (!GetHDC()) return wxSize();
2112 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2113 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2115 return wxSize(x
, y
);
2118 // For use by wxWindows only, unless custom units are required.
2119 void wxDC::SetLogicalScale(double x
, double y
)
2121 #ifdef __WXMICROWIN__
2122 if (!GetHDC()) return;
2125 m_logicalScaleX
= x
;
2126 m_logicalScaleY
= y
;
2129 // ----------------------------------------------------------------------------
2131 // ----------------------------------------------------------------------------
2133 #if wxUSE_DC_CACHEING
2136 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2137 * improve it in due course, either using arrays, or simply storing pointers to one
2138 * entry for the bitmap, and two for the DCs. -- JACS
2141 wxList
wxDC::sm_bitmapCache
;
2142 wxList
wxDC::sm_dcCache
;
2144 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2153 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2162 wxDCCacheEntry::~wxDCCacheEntry()
2165 ::DeleteObject((HBITMAP
) m_bitmap
);
2167 ::DeleteDC((HDC
) m_dc
);
2170 wxDCCacheEntry
* wxDC::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2172 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2173 wxNode
* node
= sm_bitmapCache
.GetFirst();
2176 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2178 if (entry
->m_depth
== depth
)
2180 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2182 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2183 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2184 if ( !entry
->m_bitmap
)
2186 wxLogLastError(wxT("CreateCompatibleBitmap"));
2188 entry
->m_width
= w
; entry
->m_height
= h
;
2194 node
= node
->GetNext();
2196 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2199 wxLogLastError(wxT("CreateCompatibleBitmap"));
2201 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2202 AddToBitmapCache(entry
);
2206 wxDCCacheEntry
* wxDC::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2208 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2209 wxNode
* node
= sm_dcCache
.GetFirst();
2212 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2214 // Don't return the same one as we already have
2215 if (!notThis
|| (notThis
!= entry
))
2217 if (entry
->m_depth
== depth
)
2223 node
= node
->GetNext();
2225 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2228 wxLogLastError(wxT("CreateCompatibleDC"));
2230 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2231 AddToDCCache(entry
);
2235 void wxDC::AddToBitmapCache(wxDCCacheEntry
* entry
)
2237 sm_bitmapCache
.Append(entry
);
2240 void wxDC::AddToDCCache(wxDCCacheEntry
* entry
)
2242 sm_dcCache
.Append(entry
);
2245 void wxDC::ClearCache()
2247 sm_dcCache
.DeleteContents(TRUE
);
2249 sm_dcCache
.DeleteContents(FALSE
);
2250 sm_bitmapCache
.DeleteContents(TRUE
);
2251 sm_bitmapCache
.Clear();
2252 sm_bitmapCache
.DeleteContents(FALSE
);
2255 // Clean up cache at app exit
2256 class wxDCModule
: public wxModule
2259 virtual bool OnInit() { return TRUE
; }
2260 virtual void OnExit() { wxDC::ClearCache(); }
2263 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2266 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2268 #endif // wxUSE_DC_CACHEING
2270 // ----------------------------------------------------------------------------
2271 // alpha channel support
2272 // ----------------------------------------------------------------------------
2274 static bool AlphaBlt(HDC hdcDst
,
2275 int x
, int y
, int width
, int height
,
2277 const wxBitmap
& bmp
)
2279 wxASSERT_MSG( bmp
.Ok() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2280 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2282 // do we have AlphaBlend() and company in the headers?
2283 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2284 // yes, now try to see if we have it during run-time
2285 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2286 HDC
,int,int,int,int,
2289 // bitmaps can be drawn only from GUI thread so there is no need to
2290 // protect this static variable from multiple threads
2291 static bool s_triedToLoad
= FALSE
;
2292 static AlphaBlend_t pfnAlphaBlend
= NULL
;
2293 if ( !s_triedToLoad
)
2295 s_triedToLoad
= TRUE
;
2297 // don't give errors about the DLL being unavailable, we're
2298 // prepared to handle this
2301 wxDynamicLibrary
dll(_T("msimg32.dll"));
2302 if ( dll
.IsLoaded() )
2304 pfnAlphaBlend
= (AlphaBlend_t
)dll
.GetSymbol(_T("AlphaBlend"));
2305 if ( pfnAlphaBlend
)
2307 // we must keep the DLL loaded if we want to be able to
2308 // call AlphaBlend() so just never unload it at all, not a
2315 if ( pfnAlphaBlend
)
2318 bf
.BlendOp
= AC_SRC_OVER
;
2320 bf
.SourceConstantAlpha
= 0xff;
2321 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2323 if ( pfnAlphaBlend(hdcDst
, x
, y
, width
, height
,
2324 hdcSrc
, 0, 0, width
, height
,
2327 // skip wxAlphaBlend() call below
2331 wxLogLastError(_T("AlphaBlend"));
2333 #endif // defined(AC_SRC_OVER)
2335 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2337 #ifdef wxHAVE_RAW_BITMAP
2338 wxAlphaBlend(hdcDst
, x
, y
, width
, height
, bmp
);
2341 #else // !wxHAVE_RAW_BITMAP
2342 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2343 // alpha but at least something will be shown like this)
2345 #endif // wxHAVE_RAW_BITMAP
2349 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2350 #ifdef wxHAVE_RAW_BITMAP
2353 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
, int w
, int h
, const wxBitmap
& bmpSrc
)
2355 // get the destination DC pixels
2356 wxBitmap
bmpDst(w
, h
, 32 /* force creating RGBA DIB */);
2358 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2360 if ( !::BitBlt(hdcMem
, 0, 0, w
, h
, hdcDst
, 0, 0, SRCCOPY
) )
2362 wxLogLastError(_T("BitBlt"));
2365 // combine them with the source bitmap using alpha
2366 wxAlphaPixelData
dataDst(bmpDst
),
2367 dataSrc((wxBitmap
&)bmpSrc
);
2369 wxCHECK_RET( dataDst
&& dataSrc
,
2370 _T("failed to get raw data in wxAlphaBlend") );
2372 wxAlphaPixelData::Iterator
pDst(dataDst
),
2375 for ( int y
= 0; y
< h
; y
++ )
2377 wxAlphaPixelData::Iterator pDstRowStart
= pDst
,
2378 pSrcRowStart
= pSrc
;
2380 for ( int x
= 0; x
< w
; x
++ )
2382 // note that source bitmap uses premultiplied alpha (as required by
2383 // the real AlphaBlend)
2384 const unsigned beta
= 255 - pSrc
.Alpha();
2386 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2387 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2388 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2394 pDst
= pDstRowStart
;
2395 pSrc
= pSrcRowStart
;
2396 pDst
.OffsetY(dataDst
, 1);
2397 pSrc
.OffsetY(dataSrc
, 1);
2400 // and finally blit them back to the destination DC
2401 if ( !::BitBlt(hdcDst
, xDst
, yDst
, w
, h
, hdcMem
, 0, 0, SRCCOPY
) )
2403 wxLogLastError(_T("BitBlt"));
2407 #endif // #ifdef wxHAVE_RAW_BITMAP