1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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/msw/private.h" // needs to be before #include <commdlg.h>
45 #include "wx/sysopt.h"
46 #include "wx/dcprint.h"
47 #include "wx/module.h"
48 #include "wx/dynload.h"
50 #ifdef wxHAVE_RAW_BITMAP
51 #include "wx/rawbmp.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
;
162 DECLARE_NO_COPY_CLASS(wxColourChanger
)
165 // this class saves the old stretch blit mode during its life time
166 class StretchBltModeChanger
169 StretchBltModeChanger(HDC hdc
, int mode
)
173 m_modeOld
= ::SetStretchBltMode(m_hdc
, mode
);
175 wxLogLastError(_T("SetStretchBltMode"));
179 ~StretchBltModeChanger()
182 if ( !::SetStretchBltMode(m_hdc
, m_modeOld
) )
183 wxLogLastError(_T("SetStretchBltMode"));
192 DECLARE_NO_COPY_CLASS(StretchBltModeChanger
)
195 // ===========================================================================
197 // ===========================================================================
199 // ----------------------------------------------------------------------------
201 // ----------------------------------------------------------------------------
203 wxColourChanger::wxColourChanger(wxDC
& dc
) : m_dc(dc
)
205 const wxBrush
& brush
= dc
.GetBrush();
206 if ( brush
.Ok() && brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
208 HDC hdc
= GetHdcOf(dc
);
209 m_colFgOld
= ::GetTextColor(hdc
);
210 m_colBgOld
= ::GetBkColor(hdc
);
212 // note that Windows convention is opposite to wxWindows one, this is
213 // why text colour becomes the background one and vice versa
214 const wxColour
& colFg
= dc
.GetTextForeground();
217 ::SetBkColor(hdc
, colFg
.GetPixel());
220 const wxColour
& colBg
= dc
.GetTextBackground();
223 ::SetTextColor(hdc
, colBg
.GetPixel());
227 dc
.GetBackgroundMode() == wxTRANSPARENT
? TRANSPARENT
230 // flag which telsl us to undo changes in the dtor
235 // nothing done, nothing to undo
240 wxColourChanger::~wxColourChanger()
244 // restore the colours we changed
245 HDC hdc
= GetHdcOf(m_dc
);
247 ::SetBkMode(hdc
, TRANSPARENT
);
248 ::SetTextColor(hdc
, m_colFgOld
);
249 ::SetBkColor(hdc
, m_colBgOld
);
253 // ---------------------------------------------------------------------------
255 // ---------------------------------------------------------------------------
257 // Default constructor
268 #endif // wxUSE_PALETTE
278 SelectOldObjects(m_hDC
);
280 // if we own the HDC, we delete it, otherwise we just release it
284 ::DeleteDC(GetHdc());
286 else // we don't own our HDC
290 ::ReleaseDC(GetHwndOf(m_canvas
), GetHdc());
294 // Must have been a wxScreenDC
295 ::ReleaseDC((HWND
) NULL
, GetHdc());
301 // This will select current objects out of the DC,
302 // which is what you have to do before deleting the
304 void wxDC::SelectOldObjects(WXHDC dc
)
310 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
312 if (m_selectedBitmap
.Ok())
314 m_selectedBitmap
.SetSelectedInto(NULL
);
321 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
326 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
331 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
338 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, FALSE
);
341 #endif // wxUSE_PALETTE
344 m_brush
= wxNullBrush
;
347 m_palette
= wxNullPalette
;
348 #endif // wxUSE_PALETTE
350 m_backgroundBrush
= wxNullBrush
;
351 m_selectedBitmap
= wxNullBitmap
;
354 // ---------------------------------------------------------------------------
356 // ---------------------------------------------------------------------------
358 void wxDC::UpdateClipBox()
360 #ifdef __WXMICROWIN__
361 if (!GetHDC()) return;
365 ::GetClipBox(GetHdc(), &rect
);
367 m_clipX1
= (wxCoord
) XDEV2LOG(rect
.left
);
368 m_clipY1
= (wxCoord
) YDEV2LOG(rect
.top
);
369 m_clipX2
= (wxCoord
) XDEV2LOG(rect
.right
);
370 m_clipY2
= (wxCoord
) YDEV2LOG(rect
.bottom
);
373 // common part of DoSetClippingRegion() and DoSetClippingRegionAsRegion()
374 void wxDC::SetClippingHrgn(WXHRGN hrgn
)
376 wxCHECK_RET( hrgn
, wxT("invalid clipping region") );
378 #ifdef __WXMICROWIN__
379 if (!GetHdc()) return;
380 #endif // __WXMICROWIN__
382 // note that we combine the new clipping region with the existing one: this
383 // is compatible with what the other ports do and is the documented
384 // behaviour now (starting with 2.3.3)
385 #if defined(__WIN16__) || defined(__WXWINCE__)
387 if ( !::GetClipBox(GetHdc(), &rectClip
) )
390 HRGN hrgnDest
= ::CreateRectRgn(0, 0, 0, 0);
391 HRGN hrgnClipOld
= ::CreateRectRgn(rectClip
.left
, rectClip
.top
,
392 rectClip
.right
, rectClip
.bottom
);
394 if ( ::CombineRgn(hrgnDest
, hrgnClipOld
, (HRGN
)hrgn
, RGN_AND
) != ERROR
)
396 ::SelectClipRgn(GetHdc(), hrgnDest
);
399 ::DeleteObject(hrgnClipOld
);
400 ::DeleteObject(hrgnDest
);
402 if ( ::ExtSelectClipRgn(GetHdc(), (HRGN
)hrgn
, RGN_AND
) == ERROR
)
404 wxLogLastError(_T("ExtSelectClipRgn"));
415 void wxDC::DoSetClippingRegion(wxCoord x
, wxCoord y
, wxCoord w
, wxCoord h
)
417 // the region coords are always the device ones, so do the translation
420 // FIXME: possible +/-1 error here, to check!
421 HRGN hrgn
= ::CreateRectRgn(LogicalToDeviceX(x
),
423 LogicalToDeviceX(x
+ w
),
424 LogicalToDeviceY(y
+ h
));
427 wxLogLastError(_T("CreateRectRgn"));
431 SetClippingHrgn((WXHRGN
)hrgn
);
433 ::DeleteObject(hrgn
);
437 void wxDC::DoSetClippingRegionAsRegion(const wxRegion
& region
)
439 SetClippingHrgn(region
.GetHRGN());
442 void wxDC::DestroyClippingRegion()
444 #ifdef __WXMICROWIN__
445 if (!GetHDC()) return;
448 if (m_clipping
&& m_hDC
)
450 // TODO: this should restore the previous clipping region,
451 // so that OnPaint processing works correctly, and the update
452 // clipping region doesn't get destroyed after the first
453 // DestroyClippingRegion.
454 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
455 ::SelectClipRgn(GetHdc(), rgn
);
462 // ---------------------------------------------------------------------------
463 // query capabilities
464 // ---------------------------------------------------------------------------
466 bool wxDC::CanDrawBitmap() const
471 bool wxDC::CanGetTextExtent() const
473 #ifdef __WXMICROWIN__
474 // TODO Extend MicroWindows' GetDeviceCaps function
477 // What sort of display is it?
478 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
480 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
484 int wxDC::GetDepth() const
486 #ifdef __WXMICROWIN__
487 if (!GetHDC()) return 16;
490 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
493 // ---------------------------------------------------------------------------
495 // ---------------------------------------------------------------------------
499 #ifdef __WXMICROWIN__
500 if (!GetHDC()) return;
506 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
510 // No, I think we should simply ignore this if printing on e.g.
512 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
513 if (!m_selectedBitmap
.Ok())
516 rect
.left
= 0; rect
.top
= 0;
517 rect
.right
= m_selectedBitmap
.GetWidth();
518 rect
.bottom
= m_selectedBitmap
.GetHeight();
522 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
525 DWORD colour
= ::GetBkColor(GetHdc());
526 HBRUSH brush
= ::CreateSolidBrush(colour
);
527 ::FillRect(GetHdc(), &rect
, brush
);
528 ::DeleteObject(brush
);
530 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
531 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
534 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
536 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
537 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
538 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
539 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
543 bool wxDC::DoFloodFill(wxCoord x
, wxCoord y
, const wxColour
& col
, int style
)
549 #ifdef __WXMICROWIN__
550 if (!GetHDC()) return FALSE
;
553 bool success
= (0 != ::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
555 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
556 : FLOODFILLBORDER
) ) ;
559 // quoting from the MSDN docs:
561 // Following are some of the reasons this function might fail:
563 // * The filling could not be completed.
564 // * The specified point has the boundary color specified by the
565 // crColor parameter (if FLOODFILLBORDER was requested).
566 // * The specified point does not have the color specified by
567 // crColor (if FLOODFILLSURFACE was requested)
568 // * The point is outside the clipping region that is, it is not
569 // visible on the device.
571 wxLogLastError(wxT("ExtFloodFill"));
574 CalcBoundingBox(x
, y
);
580 bool wxDC::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
582 #ifdef __WXMICROWIN__
583 if (!GetHDC()) return FALSE
;
586 wxCHECK_MSG( col
, FALSE
, _T("NULL colour parameter in wxDC::GetPixel") );
588 // get the color of the pixel
589 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
591 wxRGBToColour(*col
, pixelcolor
);
596 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
598 #ifdef __WXMICROWIN__
599 if (!GetHDC()) return;
602 wxCoord x1
= x
-VIEWPORT_EXTENT
;
603 wxCoord y1
= y
-VIEWPORT_EXTENT
;
604 wxCoord x2
= x
+VIEWPORT_EXTENT
;
605 wxCoord y2
= y
+VIEWPORT_EXTENT
;
607 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y
));
608 wxDrawLine(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), XLOG2DEV(x
), YLOG2DEV(y2
));
610 CalcBoundingBox(x1
, y1
);
611 CalcBoundingBox(x2
, y2
);
614 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
616 #ifdef __WXMICROWIN__
617 if (!GetHDC()) return;
620 wxDrawLine(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), XLOG2DEV(x2
), YLOG2DEV(y2
));
622 CalcBoundingBox(x1
, y1
);
623 CalcBoundingBox(x2
, y2
);
626 // Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
627 // and ending at (x2, y2)
628 void wxDC::DoDrawArc(wxCoord x1
, wxCoord y1
,
629 wxCoord x2
, wxCoord y2
,
630 wxCoord xc
, wxCoord yc
)
633 // Slower emulation since WinCE doesn't support Pie and Arc
634 double r
= sqrt( (x1
-xc
)*(x1
-xc
) + (y1
-yc
)*(y1
-yc
) );
635 double sa
= acos((x1
-xc
)/r
)/M_PI
*180; // between 0 and 180
636 if( y1
>yc
) sa
= -sa
; // below center
637 double ea
= atan2(yc
-y2
, x2
-xc
)/M_PI
*180;
638 DoDrawEllipticArcRot( xc
-r
, yc
-r
, 2*r
, 2*r
, sa
, ea
);
641 #ifdef __WXMICROWIN__
642 if (!GetHDC()) return;
645 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
649 double radius
= (double)sqrt(dx
*dx
+dy
*dy
);
650 wxCoord r
= (wxCoord
)radius
;
652 // treat the special case of full circle separately
653 if ( x1
== x2
&& y1
== y2
)
655 DrawEllipse(xc
- r
, yc
- r
, 2*r
, 2*r
);
659 wxCoord xx1
= XLOG2DEV(x1
);
660 wxCoord yy1
= YLOG2DEV(y1
);
661 wxCoord xx2
= XLOG2DEV(x2
);
662 wxCoord yy2
= YLOG2DEV(y2
);
663 wxCoord xxc
= XLOG2DEV(xc
);
664 wxCoord yyc
= YLOG2DEV(yc
);
665 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
667 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
668 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
669 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
670 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
672 if ( m_brush
.Ok() && m_brush
.GetStyle() != wxTRANSPARENT
)
674 // Have to add 1 to bottom-right corner of rectangle
675 // to make semi-circles look right (crooked line otherwise).
676 // Unfortunately this is not a reliable method, depends
677 // on the size of shape.
678 // TODO: figure out why this happens!
679 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1, xx1
,yy1
,xx2
,yy2
);
683 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
, xx1
,yy1
,xx2
,yy2
);
686 CalcBoundingBox(xc
- r
, yc
- r
);
687 CalcBoundingBox(xc
+ r
, yc
+ r
);
691 void wxDC::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
692 wxCoord width
, wxCoord height
)
694 #ifdef __WXMICROWIN__
695 if (!GetHDC()) return;
698 wxCoord x2
= x1
+ width
,
701 #if defined(__WIN32__) && !defined(__SYMANTEC__) && !defined(__WXMICROWIN__)
709 DrawFrameControl(GetHdc(), &rect
, DFC_BUTTON
, DFCS_BUTTONCHECK
);
711 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
714 // In WIN16, draw a cross
715 HPEN blackPen
= ::CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
716 HPEN whiteBrush
= (HPEN
)::GetStockObject(WHITE_BRUSH
);
717 HPEN hPenOld
= (HPEN
)::SelectObject(GetHdc(), blackPen
);
718 HPEN hBrushOld
= (HPEN
)::SelectObject(GetHdc(), whiteBrush
);
719 ::SetROP2(GetHdc(), R2_COPYPEN
);
720 Rectangle(GetHdc(), x1
, y1
, x2
, y2
);
721 MoveToEx(GetHdc(), x1
, y1
, NULL
);
722 LineTo(GetHdc(), x2
, y2
);
723 MoveToEx(GetHdc(), x2
, y1
, NULL
);
724 LineTo(GetHdc(), x1
, y2
);
725 ::SelectObject(GetHdc(), hPenOld
);
726 ::SelectObject(GetHdc(), hBrushOld
);
727 ::DeleteObject(blackPen
);
730 CalcBoundingBox(x1
, y1
);
731 CalcBoundingBox(x2
, y2
);
734 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
736 #ifdef __WXMICROWIN__
737 if (!GetHDC()) return;
740 COLORREF color
= 0x00ffffff;
743 color
= m_pen
.GetColour().GetPixel();
746 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
748 CalcBoundingBox(x
, y
);
751 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
753 #ifdef __WXMICROWIN__
754 if (!GetHDC()) return;
757 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
759 // Do things less efficiently if we have offsets
760 if (xoffset
!= 0 || yoffset
!= 0)
762 POINT
*cpoints
= new POINT
[n
];
764 for (i
= 0; i
< n
; i
++)
766 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
767 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
769 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
772 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
774 (void)Polygon(GetHdc(), cpoints
, n
);
776 SetPolyFillMode(GetHdc(),prev
);
783 for (i
= 0; i
< n
; i
++)
784 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
787 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
789 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
791 SetPolyFillMode(GetHdc(),prev
);
796 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
798 #ifdef __WXMICROWIN__
799 if (!GetHDC()) return;
802 // Do things less efficiently if we have offsets
803 if (xoffset
!= 0 || yoffset
!= 0)
805 POINT
*cpoints
= new POINT
[n
];
807 for (i
= 0; i
< n
; i
++)
809 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
810 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
812 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
814 (void)Polyline(GetHdc(), cpoints
, n
);
820 for (i
= 0; i
< n
; i
++)
821 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
823 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
827 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
829 #ifdef __WXMICROWIN__
830 if (!GetHDC()) return;
833 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
835 wxCoord x2
= x
+ width
;
836 wxCoord y2
= y
+ height
;
838 if ((m_logicalFunction
== wxCOPY
) && (m_pen
.GetStyle() == wxTRANSPARENT
))
841 rect
.left
= XLOG2DEV(x
);
842 rect
.top
= YLOG2DEV(y
);
843 rect
.right
= XLOG2DEV(x2
);
844 rect
.bottom
= YLOG2DEV(y2
);
845 (void)FillRect(GetHdc(), &rect
, (HBRUSH
)m_brush
.GetResourceHandle() );
849 // Windows draws the filled rectangles without outline (i.e. drawn with a
850 // transparent pen) one pixel smaller in both directions and we want them
851 // to have the same size regardless of which pen is used - adjust
853 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
854 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
860 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
864 CalcBoundingBox(x
, y
);
865 CalcBoundingBox(x2
, y2
);
868 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
870 #ifdef __WXMICROWIN__
871 if (!GetHDC()) return;
874 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
876 // Now, a negative radius value is interpreted to mean
877 // 'the proportion of the smallest X or Y dimension'
881 double smallest
= (width
< height
) ? width
: height
;
882 radius
= (- radius
* smallest
);
885 wxCoord x2
= (x
+width
);
886 wxCoord y2
= (y
+height
);
888 // Windows draws the filled rectangles without outline (i.e. drawn with a
889 // transparent pen) one pixel smaller in both directions and we want them
890 // to have the same size regardless of which pen is used - adjust
891 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
897 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
898 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
900 CalcBoundingBox(x
, y
);
901 CalcBoundingBox(x2
, y2
);
904 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
906 #ifdef __WXMICROWIN__
907 if (!GetHDC()) return;
910 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
912 wxCoord x2
= (x
+width
);
913 wxCoord y2
= (y
+height
);
915 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
917 CalcBoundingBox(x
, y
);
918 CalcBoundingBox(x2
, y2
);
921 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
922 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
925 DoDrawEllipticArcRot( x
, y
, w
, h
, sa
, ea
);
928 #ifdef __WXMICROWIN__
929 if (!GetHDC()) return;
932 wxColourChanger
cc(*this); // needed for wxSTIPPLE_MASK_OPAQUE handling
937 int rx1
= XLOG2DEV(x
+w
/2);
938 int ry1
= YLOG2DEV(y
+h
/2);
945 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
946 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
947 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
948 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
950 // draw pie with NULL_PEN first and then outline otherwise a line is
951 // drawn from the start and end points to the centre
952 HPEN hpenOld
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
955 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
960 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
961 rx1
, ry1
-1, rx2
, ry2
-1);
964 ::SelectObject(GetHdc(), hpenOld
);
966 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
969 CalcBoundingBox(x
, y
);
970 CalcBoundingBox(x2
, y2
);
974 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
976 #ifdef __WXMICROWIN__
977 if (!GetHDC()) return;
980 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
983 ::DrawIconEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
), icon
.GetWidth(), icon
.GetHeight(), 0, NULL
, DI_NORMAL
);
985 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
988 CalcBoundingBox(x
, y
);
989 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
992 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
994 #ifdef __WXMICROWIN__
995 if (!GetHDC()) return;
998 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
1000 int width
= bmp
.GetWidth(),
1001 height
= bmp
.GetHeight();
1003 HBITMAP hbmpMask
= 0;
1006 HPALETTE oldPal
= 0;
1007 #endif // wxUSE_PALETTE
1009 if ( bmp
.HasAlpha() )
1012 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmp
));
1014 if ( AlphaBlt(GetHdc(), x
, y
, width
, height
, hdcMem
, bmp
) )
1020 wxMask
*mask
= bmp
.GetMask();
1022 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
1026 // don't give assert here because this would break existing
1027 // programs - just silently ignore useMask parameter
1034 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
1036 // On some systems, MaskBlt succeeds yet is much much slower
1037 // than the wxWindows fall-back implementation. So we need
1038 // to be able to switch this on and off at runtime.
1040 #if wxUSE_SYSTEM_OPTIONS
1041 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1045 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
1046 HGDIOBJ hOldBitmap
= ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
1048 wxPalette
*pal
= bmp
.GetPalette();
1049 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1051 oldPal
= ::SelectPalette(hdcMem
, GetHpaletteOf(*pal
), FALSE
);
1052 ::RealizePalette(hdcMem
);
1054 #endif // wxUSE_PALETTE
1056 ok
= ::MaskBlt(cdc
, x
, y
, width
, height
,
1059 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
1063 ::SelectPalette(hdcMem
, oldPal
, FALSE
);
1064 #endif // wxUSE_PALETTE
1066 ::SelectObject(hdcMem
, hOldBitmap
);
1073 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
1076 memDC
.SelectObject(bmp
);
1078 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
1080 memDC
.SelectObject(wxNullBitmap
);
1083 else // no mask, just use BitBlt()
1086 HDC memdc
= ::CreateCompatibleDC( cdc
);
1087 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
1089 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
1091 COLORREF old_textground
= ::GetTextColor(GetHdc());
1092 COLORREF old_background
= ::GetBkColor(GetHdc());
1093 if (m_textForegroundColour
.Ok())
1095 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1097 if (m_textBackgroundColour
.Ok())
1099 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1103 wxPalette
*pal
= bmp
.GetPalette();
1104 if ( pal
&& ::GetDeviceCaps(cdc
,BITSPIXEL
) <= 8 )
1106 oldPal
= ::SelectPalette(memdc
, GetHpaletteOf(*pal
), FALSE
);
1107 ::RealizePalette(memdc
);
1109 #endif // wxUSE_PALETTE
1111 HGDIOBJ hOldBitmap
= ::SelectObject( memdc
, hbitmap
);
1112 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
1116 ::SelectPalette(memdc
, oldPal
, FALSE
);
1117 #endif // wxUSE_PALETTE
1119 ::SelectObject( memdc
, hOldBitmap
);
1120 ::DeleteDC( memdc
);
1122 ::SetTextColor(GetHdc(), old_textground
);
1123 ::SetBkColor(GetHdc(), old_background
);
1127 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
1129 #ifdef __WXMICROWIN__
1130 if (!GetHDC()) return;
1133 DrawAnyText(text
, x
, y
);
1135 // update the bounding box
1136 CalcBoundingBox(x
, y
);
1139 GetTextExtent(text
, &w
, &h
);
1140 CalcBoundingBox(x
+ w
, y
+ h
);
1143 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
1145 #ifdef __WXMICROWIN__
1146 if (!GetHDC()) return;
1149 // prepare for drawing the text
1150 if ( m_textForegroundColour
.Ok() )
1151 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
1153 DWORD old_background
= 0;
1154 if ( m_textBackgroundColour
.Ok() )
1156 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1159 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
1163 if ( ::ExtTextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), 0, NULL
,
1164 text
.c_str(), text
.length(), NULL
) == 0 )
1166 wxLogLastError(wxT("TextOut"));
1169 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
1170 text
.c_str(), text
.length()) == 0 )
1172 wxLogLastError(wxT("TextOut"));
1176 // restore the old parameters (text foreground colour may be left because
1177 // it never is set to anything else, but background should remain
1178 // transparent even if we just drew an opaque string)
1179 if ( m_textBackgroundColour
.Ok() )
1180 (void)SetBkColor(GetHdc(), old_background
);
1182 SetBkMode(GetHdc(), TRANSPARENT
);
1185 void wxDC::DoDrawRotatedText(const wxString
& text
,
1186 wxCoord x
, wxCoord y
,
1189 #ifdef __WXMICROWIN__
1190 if (!GetHDC()) return;
1193 // we test that we have some font because otherwise we should still use the
1194 // "else" part below to avoid that DrawRotatedText(angle = 180) and
1195 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
1196 // font for drawing rotated fonts unfortunately)
1197 if ( (angle
== 0.0) && m_font
.Ok() )
1199 DoDrawText(text
, x
, y
);
1201 #ifndef __WXMICROWIN__
1204 // NB: don't take DEFAULT_GUI_FONT (a.k.a. wxSYS_DEFAULT_GUI_FONT)
1205 // because it's not TrueType and so can't have non zero
1206 // orientation/escapement under Win9x
1207 wxFont font
= m_font
.Ok() ? m_font
: *wxSWISS_FONT
;
1208 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
1210 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
1212 wxLogLastError(wxT("GetObject(hfont)"));
1215 // GDI wants the angle in tenth of degree
1216 long angle10
= (long)(angle
* 10);
1217 lf
.lfEscapement
= angle10
;
1218 lf
. lfOrientation
= angle10
;
1220 hfont
= ::CreateFontIndirect(&lf
);
1223 wxLogLastError(wxT("CreateFont"));
1227 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
1229 DrawAnyText(text
, x
, y
);
1231 (void)::SelectObject(GetHdc(), hfontOld
);
1232 (void)::DeleteObject(hfont
);
1235 // call the bounding box by adding all four vertices of the rectangle
1236 // containing the text to it (simpler and probably not slower than
1237 // determining which of them is really topmost/leftmost/...)
1239 GetTextExtent(text
, &w
, &h
);
1241 double rad
= DegToRad(angle
);
1243 // "upper left" and "upper right"
1244 CalcBoundingBox(x
, y
);
1245 CalcBoundingBox(x
+ wxCoord(w
*cos(rad
)), y
- wxCoord(h
*sin(rad
)));
1247 // "bottom left" and "bottom right"
1248 x
+= (wxCoord
)(h
*sin(rad
));
1249 y
+= (wxCoord
)(h
*cos(rad
));
1250 CalcBoundingBox(x
, y
);
1251 CalcBoundingBox(x
+ wxCoord(h
*sin(rad
)), y
+ wxCoord(h
*cos(rad
)));
1256 // ---------------------------------------------------------------------------
1258 // ---------------------------------------------------------------------------
1262 void wxDC::DoSelectPalette(bool realize
)
1264 #ifdef __WXMICROWIN__
1265 if (!GetHDC()) return;
1268 // Set the old object temporarily, in case the assignment deletes an object
1269 // that's not yet selected out.
1272 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, FALSE
);
1276 if ( m_palette
.Ok() )
1278 HPALETTE oldPal
= ::SelectPalette(GetHdc(),
1279 GetHpaletteOf(m_palette
),
1282 m_oldPalette
= (WXHPALETTE
) oldPal
;
1285 ::RealizePalette(GetHdc());
1289 void wxDC::SetPalette(const wxPalette
& palette
)
1293 m_palette
= palette
;
1294 DoSelectPalette(TRUE
);
1298 void wxDC::InitializePalette()
1300 if ( wxDisplayDepth() <= 8 )
1302 // look for any window or parent that has a custom palette. If any has
1303 // one then we need to use it in drawing operations
1304 wxWindow
*win
= m_canvas
->GetAncestorWithCustomPalette();
1306 m_hasCustomPalette
= win
&& win
->HasCustomPalette();
1307 if ( m_hasCustomPalette
)
1309 m_palette
= win
->GetPalette();
1311 // turn on MSW translation for this palette
1317 #endif // wxUSE_PALETTE
1319 void wxDC::SetFont(const wxFont
& the_font
)
1321 #ifdef __WXMICROWIN__
1322 if (!GetHDC()) return;
1325 // Set the old object temporarily, in case the assignment deletes an object
1326 // that's not yet selected out.
1329 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1338 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
1342 if (m_font
.Ok() && m_font
.GetResourceHandle())
1344 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
1345 if (f
== (HFONT
) NULL
)
1347 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
1350 m_oldFont
= (WXHFONT
) f
;
1354 void wxDC::SetPen(const wxPen
& pen
)
1356 #ifdef __WXMICROWIN__
1357 if (!GetHDC()) return;
1360 // Set the old object temporarily, in case the assignment deletes an object
1361 // that's not yet selected out.
1364 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1373 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
1379 if (m_pen
.GetResourceHandle())
1381 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
1383 m_oldPen
= (WXHPEN
) p
;
1388 void wxDC::SetBrush(const wxBrush
& brush
)
1390 #ifdef __WXMICROWIN__
1391 if (!GetHDC()) return;
1394 // Set the old object temporarily, in case the assignment deletes an object
1395 // that's not yet selected out.
1398 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1407 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1413 // to make sure the brush is alligned with the logical coordinates
1414 wxBitmap
*stipple
= m_brush
.GetStipple();
1415 if ( stipple
&& stipple
->Ok() )
1418 ::SetBrushOrgEx(GetHdc(),
1419 m_deviceOriginX
% stipple
->GetWidth(),
1420 m_deviceOriginY
% stipple
->GetHeight(),
1421 NULL
); // don't need previous brush origin
1423 ::SetBrushOrg(GetHdc(),
1424 m_deviceOriginX
% stipple
->GetWidth(),
1425 m_deviceOriginY
% stipple
->GetHeight());
1429 if ( m_brush
.GetResourceHandle() )
1431 HBRUSH b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
1433 m_oldBrush
= (WXHBRUSH
) b
;
1438 void wxDC::SetBackground(const wxBrush
& brush
)
1440 #ifdef __WXMICROWIN__
1441 if (!GetHDC()) return;
1444 m_backgroundBrush
= brush
;
1446 if ( m_backgroundBrush
.Ok() )
1448 (void)SetBkColor(GetHdc(), m_backgroundBrush
.GetColour().GetPixel());
1452 void wxDC::SetBackgroundMode(int mode
)
1454 #ifdef __WXMICROWIN__
1455 if (!GetHDC()) return;
1458 m_backgroundMode
= mode
;
1460 // SetBackgroundColour now only refers to text background
1461 // and m_backgroundMode is used there
1464 void wxDC::SetLogicalFunction(int function
)
1466 #ifdef __WXMICROWIN__
1467 if (!GetHDC()) return;
1470 m_logicalFunction
= function
;
1475 void wxDC::SetRop(WXHDC dc
)
1477 if ( !dc
|| m_logicalFunction
< 0 )
1482 switch (m_logicalFunction
)
1484 case wxCLEAR
: rop
= R2_BLACK
; break;
1485 case wxXOR
: rop
= R2_XORPEN
; break;
1486 case wxINVERT
: rop
= R2_NOT
; break;
1487 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1488 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1489 case wxCOPY
: rop
= R2_COPYPEN
; break;
1490 case wxAND
: rop
= R2_MASKPEN
; break;
1491 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1492 case wxNO_OP
: rop
= R2_NOP
; break;
1493 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1494 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1495 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1496 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1497 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1498 case wxOR
: rop
= R2_MERGEPEN
; break;
1499 case wxSET
: rop
= R2_WHITE
; break;
1502 wxFAIL_MSG( wxT("unsupported logical function") );
1506 SetROP2(GetHdc(), rop
);
1509 bool wxDC::StartDoc(const wxString
& WXUNUSED(message
))
1511 // We might be previewing, so return TRUE to let it continue.
1519 void wxDC::StartPage()
1523 void wxDC::EndPage()
1527 // ---------------------------------------------------------------------------
1529 // ---------------------------------------------------------------------------
1531 wxCoord
wxDC::GetCharHeight() const
1533 #ifdef __WXMICROWIN__
1534 if (!GetHDC()) return 0;
1537 TEXTMETRIC lpTextMetric
;
1539 GetTextMetrics(GetHdc(), &lpTextMetric
);
1541 return lpTextMetric
.tmHeight
;
1544 wxCoord
wxDC::GetCharWidth() const
1546 #ifdef __WXMICROWIN__
1547 if (!GetHDC()) return 0;
1550 TEXTMETRIC lpTextMetric
;
1552 GetTextMetrics(GetHdc(), &lpTextMetric
);
1554 return lpTextMetric
.tmAveCharWidth
;
1557 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1558 wxCoord
*descent
, wxCoord
*externalLeading
,
1561 #ifdef __WXMICROWIN__
1566 if (descent
) *descent
= 0;
1567 if (externalLeading
) *externalLeading
= 0;
1570 #endif // __WXMICROWIN__
1575 wxASSERT_MSG( font
->Ok(), _T("invalid font in wxDC::GetTextExtent") );
1577 hfontOld
= (HFONT
)::SelectObject(GetHdc(), GetHfontOf(*font
));
1579 else // don't change the font
1587 GetTextExtentPoint(GetHdc(), string
, string
.length(), &sizeRect
);
1588 GetTextMetrics(GetHdc(), &tm
);
1595 *descent
= tm
.tmDescent
;
1596 if (externalLeading
)
1597 *externalLeading
= tm
.tmExternalLeading
;
1601 ::SelectObject(GetHdc(), hfontOld
);
1605 void wxDC::SetMapMode(int mode
)
1607 #ifdef __WXMICROWIN__
1608 if (!GetHDC()) return;
1611 m_mappingMode
= mode
;
1613 if ( mode
== wxMM_TEXT
)
1616 m_logicalScaleY
= 1.0;
1618 else // need to do some calculations
1620 int pixel_width
= ::GetDeviceCaps(GetHdc(), HORZRES
),
1621 pixel_height
= ::GetDeviceCaps(GetHdc(), VERTRES
),
1622 mm_width
= ::GetDeviceCaps(GetHdc(), HORZSIZE
),
1623 mm_height
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1625 if ( (mm_width
== 0) || (mm_height
== 0) )
1627 // we can't calculate mm2pixels[XY] then!
1631 double mm2pixelsX
= (double)pixel_width
/ mm_width
,
1632 mm2pixelsY
= (double)pixel_height
/ mm_height
;
1637 m_logicalScaleX
= twips2mm
* mm2pixelsX
;
1638 m_logicalScaleY
= twips2mm
* mm2pixelsY
;
1642 m_logicalScaleX
= pt2mm
* mm2pixelsX
;
1643 m_logicalScaleY
= pt2mm
* mm2pixelsY
;
1647 m_logicalScaleX
= mm2pixelsX
;
1648 m_logicalScaleY
= mm2pixelsY
;
1652 m_logicalScaleX
= mm2pixelsX
/ 10.0;
1653 m_logicalScaleY
= mm2pixelsY
/ 10.0;
1657 wxFAIL_MSG( _T("unknown mapping mode in SetMapMode") );
1661 // VZ: it seems very wasteful to always use MM_ANISOTROPIC when in 99% of
1662 // cases we could do with MM_TEXT and in the remaining 0.9% with
1663 // MM_ISOTROPIC (TODO!)
1665 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1667 int width
= DeviceToLogicalXRel(VIEWPORT_EXTENT
)*m_signX
,
1668 height
= DeviceToLogicalYRel(VIEWPORT_EXTENT
)*m_signY
;
1670 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1671 ::SetWindowExtEx(GetHdc(), width
, height
, NULL
);
1673 ::SetViewportOrgEx(GetHdc(), m_deviceOriginX
, m_deviceOriginY
, NULL
);
1674 ::SetWindowOrgEx(GetHdc(), m_logicalOriginX
, m_logicalOriginY
, NULL
);
1678 void wxDC::SetUserScale(double x
, double y
)
1680 #ifdef __WXMICROWIN__
1681 if (!GetHDC()) return;
1685 if ( x
== m_userScaleX
&& y
== m_userScaleY
)
1691 SetMapMode(m_mappingMode
);
1695 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1697 #ifdef __WXMICROWIN__
1698 if (!GetHDC()) return;
1702 int signX
= xLeftRight
? 1 : -1,
1703 signY
= yBottomUp
? -1 : 1;
1705 if ( signX
!= m_signX
|| signY
!= m_signY
)
1710 SetMapMode(m_mappingMode
);
1715 void wxDC::SetSystemScale(double x
, double y
)
1717 #ifdef __WXMICROWIN__
1718 if (!GetHDC()) return;
1722 if ( x
== m_scaleX
&& y
== m_scaleY
)
1728 SetMapMode(m_mappingMode
);
1732 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1734 #ifdef __WXMICROWIN__
1735 if (!GetHDC()) return;
1739 if ( x
== m_logicalOriginX
&& y
== m_logicalOriginY
)
1742 m_logicalOriginX
= x
;
1743 m_logicalOriginY
= y
;
1745 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1749 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1751 #ifdef __WXMICROWIN__
1752 if (!GetHDC()) return;
1756 if ( x
== m_deviceOriginX
&& y
== m_deviceOriginY
)
1759 m_deviceOriginX
= x
;
1760 m_deviceOriginY
= y
;
1762 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1766 // ---------------------------------------------------------------------------
1767 // coordinates transformations
1768 // ---------------------------------------------------------------------------
1770 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1772 return DeviceToLogicalXRel(x
- m_deviceOriginX
)*m_signX
+ m_logicalOriginX
;
1775 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1777 // axis orientation is not taken into account for conversion of a distance
1778 return (wxCoord
)(x
/ (m_logicalScaleX
*m_userScaleX
*m_scaleX
));
1781 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1783 return DeviceToLogicalYRel(y
- m_deviceOriginY
)*m_signY
+ m_logicalOriginY
;
1786 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1788 // axis orientation is not taken into account for conversion of a distance
1789 return (wxCoord
)( y
/ (m_logicalScaleY
*m_userScaleY
*m_scaleY
));
1792 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1794 return LogicalToDeviceXRel(x
- m_logicalOriginX
)*m_signX
+ m_deviceOriginX
;
1797 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1799 // axis orientation is not taken into account for conversion of a distance
1800 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_scaleX
);
1803 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1805 return LogicalToDeviceYRel(y
- m_logicalOriginY
)*m_signY
+ m_deviceOriginY
;
1808 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1810 // axis orientation is not taken into account for conversion of a distance
1811 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_scaleY
);
1814 // ---------------------------------------------------------------------------
1816 // ---------------------------------------------------------------------------
1818 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1819 wxCoord width
, wxCoord height
,
1820 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1821 int rop
, bool useMask
,
1822 wxCoord xsrcMask
, wxCoord ysrcMask
)
1824 wxCHECK_MSG( source
, FALSE
, _T("wxDC::Blit(): NULL wxDC pointer") );
1826 #ifdef __WXMICROWIN__
1827 if (!GetHDC()) return FALSE
;
1830 const wxBitmap
& bmpSrc
= source
->m_selectedBitmap
;
1831 if ( bmpSrc
.Ok() && bmpSrc
.HasAlpha() )
1833 if ( AlphaBlt(GetHdc(), xdest
, ydest
, width
, height
,
1834 GetHdcOf(*source
), bmpSrc
) )
1838 wxMask
*mask
= NULL
;
1841 mask
= bmpSrc
.GetMask();
1843 if ( !(bmpSrc
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1845 // don't give assert here because this would break existing
1846 // programs - just silently ignore useMask parameter
1851 if (xsrcMask
== -1 && ysrcMask
== -1)
1853 xsrcMask
= xsrc
; ysrcMask
= ysrc
;
1856 COLORREF old_textground
= ::GetTextColor(GetHdc());
1857 COLORREF old_background
= ::GetBkColor(GetHdc());
1858 if (m_textForegroundColour
.Ok())
1860 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1862 if (m_textBackgroundColour
.Ok())
1864 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1870 case wxXOR
: dwRop
= SRCINVERT
; break;
1871 case wxINVERT
: dwRop
= DSTINVERT
; break;
1872 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1873 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1874 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1875 case wxSET
: dwRop
= WHITENESS
; break;
1876 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1877 case wxAND
: dwRop
= SRCAND
; break;
1878 case wxOR
: dwRop
= SRCPAINT
; break;
1879 case wxEQUIV
: dwRop
= 0x00990066; break;
1880 case wxNAND
: dwRop
= 0x007700E6; break;
1881 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1882 case wxCOPY
: dwRop
= SRCCOPY
; break;
1883 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1884 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1885 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1887 wxFAIL_MSG( wxT("unsupported logical function") );
1891 bool success
= FALSE
;
1896 // we want the part of the image corresponding to the mask to be
1897 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1898 // meaning of fg and bg is inverted which corresponds to wxWin notion
1899 // of the mask which is also contrary to the Windows one)
1901 // On some systems, MaskBlt succeeds yet is much much slower
1902 // than the wxWindows fall-back implementation. So we need
1903 // to be able to switch this on and off at runtime.
1904 #if wxUSE_SYSTEM_OPTIONS
1905 if (wxSystemOptions::GetOptionInt(wxT("no-maskblt")) == 0)
1911 xdest
, ydest
, width
, height
,
1914 (HBITMAP
)mask
->GetMaskBitmap(),
1916 MAKEROP4(dwRop
, DSTCOPY
)
1923 // Blit bitmap with mask
1926 HBITMAP buffer_bmap
;
1928 #if wxUSE_DC_CACHEING
1929 // create a temp buffer bitmap and DCs to access it and the mask
1930 wxDCCacheEntry
* dcCacheEntry1
= FindDCInCache(NULL
, source
->GetHDC());
1931 dc_mask
= (HDC
) dcCacheEntry1
->m_dc
;
1933 wxDCCacheEntry
* dcCacheEntry2
= FindDCInCache(dcCacheEntry1
, GetHDC());
1934 dc_buffer
= (HDC
) dcCacheEntry2
->m_dc
;
1936 wxDCCacheEntry
* bitmapCacheEntry
= FindBitmapInCache(GetHDC(),
1939 buffer_bmap
= (HBITMAP
) bitmapCacheEntry
->m_bitmap
;
1940 #else // !wxUSE_DC_CACHEING
1941 // create a temp buffer bitmap and DCs to access it and the mask
1942 dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1943 dc_buffer
= ::CreateCompatibleDC(GetHdc());
1944 buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1945 #endif // wxUSE_DC_CACHEING/!wxUSE_DC_CACHEING
1946 HGDIOBJ hOldMaskBitmap
= ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1947 HGDIOBJ hOldBufferBitmap
= ::SelectObject(dc_buffer
, buffer_bmap
);
1949 // copy dest to buffer
1950 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1951 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1953 wxLogLastError(wxT("BitBlt"));
1956 // copy src to buffer using selected raster op
1957 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1958 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1960 wxLogLastError(wxT("BitBlt"));
1963 // set masked area in buffer to BLACK (pixel value 0)
1964 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1965 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1966 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1967 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1969 wxLogLastError(wxT("BitBlt"));
1972 // set unmasked area in dest to BLACK
1973 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1974 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1975 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1976 dc_mask
, xsrcMask
, ysrcMask
, SRCAND
) )
1978 wxLogLastError(wxT("BitBlt"));
1980 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1981 ::SetTextColor(GetHdc(), prevCol
);
1983 // OR buffer to dest
1984 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1985 (int)width
, (int)height
,
1986 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1989 wxLogLastError(wxT("BitBlt"));
1992 // tidy up temporary DCs and bitmap
1993 ::SelectObject(dc_mask
, hOldMaskBitmap
);
1994 ::SelectObject(dc_buffer
, hOldBufferBitmap
);
1996 #if !wxUSE_DC_CACHEING
1998 ::DeleteDC(dc_mask
);
1999 ::DeleteDC(dc_buffer
);
2000 ::DeleteObject(buffer_bmap
);
2005 else // no mask, just BitBlt() it
2007 // if we already have a DIB, draw it using StretchDIBits(), otherwise
2008 // use StretchBlt() if available and finally fall back to BitBlt()
2010 // FIXME: use appropriate WinCE functions
2012 const int caps
= ::GetDeviceCaps(GetHdc(), RASTERCAPS
);
2013 if ( bmpSrc
.Ok() && (caps
& RC_STRETCHDIB
) )
2018 if ( ::GetObject(GetHbitmapOf(bmpSrc
),
2020 &ds
) == sizeof(ds
) )
2022 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2024 if ( ::StretchDIBits(GetHdc(),
2030 (LPBITMAPINFO
)&ds
.dsBmih
,
2033 ) == (int)GDI_ERROR
)
2035 wxLogLastError(wxT("StretchDIBits"));
2044 if ( !success
&& (caps
& RC_STRETCHBLT
) )
2049 StretchBltModeChanger
changeMode(GetHdc(), COLORONCOLOR
);
2055 xdest
, ydest
, width
, height
,
2057 xsrc
, ysrc
, width
, height
,
2061 wxLogLastError(_T("StretchBlt"));
2075 (int)width
, (int)height
,
2081 wxLogLastError(_T("BitBlt"));
2090 ::SetTextColor(GetHdc(), old_textground
);
2091 ::SetBkColor(GetHdc(), old_background
);
2096 void wxDC::DoGetSize(int *w
, int *h
) const
2098 #ifdef __WXMICROWIN__
2099 if (!GetHDC()) return;
2102 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2103 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2106 void wxDC::DoGetSizeMM(int *w
, int *h
) const
2108 #ifdef __WXMICROWIN__
2109 if (!GetHDC()) return;
2112 // if we implement it in terms of DoGetSize() instead of directly using the
2113 // results returned by GetDeviceCaps(HORZ/VERTSIZE) as was done before, it
2114 // will also work for wxWindowDC and wxClientDC even though their size is
2115 // not the same as the total size of the screen
2116 int wPixels
, hPixels
;
2117 DoGetSize(&wPixels
, &hPixels
);
2121 int wTotal
= ::GetDeviceCaps(GetHdc(), HORZRES
);
2123 wxCHECK_RET( wTotal
, _T("0 width device?") );
2125 *w
= (wPixels
* ::GetDeviceCaps(GetHdc(), HORZSIZE
)) / wTotal
;
2130 int hTotal
= ::GetDeviceCaps(GetHdc(), VERTRES
);
2132 wxCHECK_RET( hTotal
, _T("0 height device?") );
2134 *h
= (hPixels
* ::GetDeviceCaps(GetHdc(), VERTSIZE
)) / hTotal
;
2138 wxSize
wxDC::GetPPI() const
2140 #ifdef __WXMICROWIN__
2141 if (!GetHDC()) return wxSize();
2144 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
2145 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
2147 return wxSize(x
, y
);
2150 // For use by wxWindows only, unless custom units are required.
2151 void wxDC::SetLogicalScale(double x
, double y
)
2153 #ifdef __WXMICROWIN__
2154 if (!GetHDC()) return;
2157 m_logicalScaleX
= x
;
2158 m_logicalScaleY
= y
;
2161 // ----------------------------------------------------------------------------
2163 // ----------------------------------------------------------------------------
2165 #if wxUSE_DC_CACHEING
2168 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
2169 * improve it in due course, either using arrays, or simply storing pointers to one
2170 * entry for the bitmap, and two for the DCs. -- JACS
2173 wxList
wxDC::sm_bitmapCache
;
2174 wxList
wxDC::sm_dcCache
;
2176 wxDCCacheEntry::wxDCCacheEntry(WXHBITMAP hBitmap
, int w
, int h
, int depth
)
2185 wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC
, int depth
)
2194 wxDCCacheEntry::~wxDCCacheEntry()
2197 ::DeleteObject((HBITMAP
) m_bitmap
);
2199 ::DeleteDC((HDC
) m_dc
);
2202 wxDCCacheEntry
* wxDC::FindBitmapInCache(WXHDC dc
, int w
, int h
)
2204 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2205 wxList::compatibility_iterator node
= sm_bitmapCache
.GetFirst();
2208 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2210 if (entry
->m_depth
== depth
)
2212 if (entry
->m_width
< w
|| entry
->m_height
< h
)
2214 ::DeleteObject((HBITMAP
) entry
->m_bitmap
);
2215 entry
->m_bitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2216 if ( !entry
->m_bitmap
)
2218 wxLogLastError(wxT("CreateCompatibleBitmap"));
2220 entry
->m_width
= w
; entry
->m_height
= h
;
2226 node
= node
->GetNext();
2228 WXHBITMAP hBitmap
= (WXHBITMAP
) ::CreateCompatibleBitmap((HDC
) dc
, w
, h
);
2231 wxLogLastError(wxT("CreateCompatibleBitmap"));
2233 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hBitmap
, w
, h
, depth
);
2234 AddToBitmapCache(entry
);
2238 wxDCCacheEntry
* wxDC::FindDCInCache(wxDCCacheEntry
* notThis
, WXHDC dc
)
2240 int depth
= ::GetDeviceCaps((HDC
) dc
, PLANES
) * ::GetDeviceCaps((HDC
) dc
, BITSPIXEL
);
2241 wxList::compatibility_iterator node
= sm_dcCache
.GetFirst();
2244 wxDCCacheEntry
* entry
= (wxDCCacheEntry
*) node
->GetData();
2246 // Don't return the same one as we already have
2247 if (!notThis
|| (notThis
!= entry
))
2249 if (entry
->m_depth
== depth
)
2255 node
= node
->GetNext();
2257 WXHDC hDC
= (WXHDC
) ::CreateCompatibleDC((HDC
) dc
);
2260 wxLogLastError(wxT("CreateCompatibleDC"));
2262 wxDCCacheEntry
* entry
= new wxDCCacheEntry(hDC
, depth
);
2263 AddToDCCache(entry
);
2267 void wxDC::AddToBitmapCache(wxDCCacheEntry
* entry
)
2269 sm_bitmapCache
.Append(entry
);
2272 void wxDC::AddToDCCache(wxDCCacheEntry
* entry
)
2274 sm_dcCache
.Append(entry
);
2277 void wxDC::ClearCache()
2279 WX_CLEAR_LIST(wxList
, sm_dcCache
);
2280 WX_CLEAR_LIST(wxList
, sm_bitmapCache
);
2283 // Clean up cache at app exit
2284 class wxDCModule
: public wxModule
2287 virtual bool OnInit() { return TRUE
; }
2288 virtual void OnExit() { wxDC::ClearCache(); }
2291 DECLARE_DYNAMIC_CLASS(wxDCModule
)
2294 IMPLEMENT_DYNAMIC_CLASS(wxDCModule
, wxModule
)
2296 #endif // wxUSE_DC_CACHEING
2298 // ----------------------------------------------------------------------------
2299 // alpha channel support
2300 // ----------------------------------------------------------------------------
2302 static bool AlphaBlt(HDC hdcDst
,
2303 int x
, int y
, int width
, int height
,
2305 const wxBitmap
& bmp
)
2307 wxASSERT_MSG( bmp
.Ok() && bmp
.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
2308 wxASSERT_MSG( hdcDst
&& hdcSrc
, _T("AlphaBlt(): invalid HDC") );
2310 // do we have AlphaBlend() and company in the headers?
2311 #if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
2312 // yes, now try to see if we have it during run-time
2313 typedef BOOL (WINAPI
*AlphaBlend_t
)(HDC
,int,int,int,int,
2314 HDC
,int,int,int,int,
2317 // bitmaps can be drawn only from GUI thread so there is no need to
2318 // protect this static variable from multiple threads
2319 static bool s_triedToLoad
= FALSE
;
2320 static AlphaBlend_t pfnAlphaBlend
= NULL
;
2321 if ( !s_triedToLoad
)
2323 s_triedToLoad
= TRUE
;
2325 // don't give errors about the DLL being unavailable, we're
2326 // prepared to handle this
2329 wxDynamicLibrary
dll(_T("msimg32.dll"));
2330 if ( dll
.IsLoaded() )
2332 pfnAlphaBlend
= (AlphaBlend_t
)dll
.GetSymbol(_T("AlphaBlend"));
2333 if ( pfnAlphaBlend
)
2335 // we must keep the DLL loaded if we want to be able to
2336 // call AlphaBlend() so just never unload it at all, not a
2343 if ( pfnAlphaBlend
)
2346 bf
.BlendOp
= AC_SRC_OVER
;
2348 bf
.SourceConstantAlpha
= 0xff;
2349 bf
.AlphaFormat
= AC_SRC_ALPHA
;
2351 if ( pfnAlphaBlend(hdcDst
, x
, y
, width
, height
,
2352 hdcSrc
, 0, 0, width
, height
,
2355 // skip wxAlphaBlend() call below
2359 wxLogLastError(_T("AlphaBlend"));
2361 #endif // defined(AC_SRC_OVER)
2363 // AlphaBlend() unavailable of failed: use our own (probably much slower)
2365 #ifdef wxHAVE_RAW_BITMAP
2366 wxAlphaBlend(hdcDst
, x
, y
, width
, height
, bmp
);
2369 #else // !wxHAVE_RAW_BITMAP
2370 // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
2371 // alpha but at least something will be shown like this)
2373 #endif // wxHAVE_RAW_BITMAP
2377 // wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
2378 #ifdef wxHAVE_RAW_BITMAP
2381 wxAlphaBlend(HDC hdcDst
, int xDst
, int yDst
, int w
, int h
, const wxBitmap
& bmpSrc
)
2383 // get the destination DC pixels
2384 wxBitmap
bmpDst(w
, h
, 32 /* force creating RGBA DIB */);
2386 SelectInHDC
select(hdcMem
, GetHbitmapOf(bmpDst
));
2388 if ( !::BitBlt(hdcMem
, 0, 0, w
, h
, hdcDst
, 0, 0, SRCCOPY
) )
2390 wxLogLastError(_T("BitBlt"));
2393 // combine them with the source bitmap using alpha
2394 wxAlphaPixelData
dataDst(bmpDst
),
2395 dataSrc((wxBitmap
&)bmpSrc
);
2397 wxCHECK_RET( dataDst
&& dataSrc
,
2398 _T("failed to get raw data in wxAlphaBlend") );
2400 wxAlphaPixelData::Iterator
pDst(dataDst
),
2403 for ( int y
= 0; y
< h
; y
++ )
2405 wxAlphaPixelData::Iterator pDstRowStart
= pDst
,
2406 pSrcRowStart
= pSrc
;
2408 for ( int x
= 0; x
< w
; x
++ )
2410 // note that source bitmap uses premultiplied alpha (as required by
2411 // the real AlphaBlend)
2412 const unsigned beta
= 255 - pSrc
.Alpha();
2414 pDst
.Red() = pSrc
.Red() + (beta
* pDst
.Red() + 127) / 255;
2415 pDst
.Blue() = pSrc
.Blue() + (beta
* pDst
.Blue() + 127) / 255;
2416 pDst
.Green() = pSrc
.Green() + (beta
* pDst
.Green() + 127) / 255;
2422 pDst
= pDstRowStart
;
2423 pSrc
= pSrcRowStart
;
2424 pDst
.OffsetY(dataDst
, 1);
2425 pSrc
.OffsetY(dataSrc
, 1);
2428 // and finally blit them back to the destination DC
2429 if ( !::BitBlt(hdcDst
, xDst
, yDst
, w
, h
, hdcMem
, 0, 0, SRCCOPY
) )
2431 wxLogLastError(_T("BitBlt"));
2435 #endif // #ifdef wxHAVE_RAW_BITMAP