1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart and Markus Holzem
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/dcprint.h"
48 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
50 #if wxUSE_COMMON_DIALOGS
58 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxObject
)
60 // ---------------------------------------------------------------------------
62 // ---------------------------------------------------------------------------
64 static const int VIEWPORT_EXTENT
= 1000;
66 static const int MM_POINTS
= 9;
67 static const int MM_METRIC
= 10;
69 // usually this is defined in math.h
71 static const double M_PI
= 3.14159265358979323846;
74 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
75 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
76 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
78 // ---------------------------------------------------------------------------
80 // ---------------------------------------------------------------------------
82 // convert degrees to radians
83 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
85 // ===========================================================================
87 // ===========================================================================
89 // ---------------------------------------------------------------------------
91 // ---------------------------------------------------------------------------
93 // Default constructor
107 m_windowExtX
= VIEWPORT_EXTENT
;
108 m_windowExtY
= VIEWPORT_EXTENT
;
117 SelectOldObjects(m_hDC
);
119 if ( m_canvas
== NULL
)
120 ::DeleteDC(GetHdc());
122 ::ReleaseDC((HWND
)m_canvas
->GetHWND(), GetHdc());
128 // This will select current objects out of the DC,
129 // which is what you have to do before deleting the
131 void wxDC::SelectOldObjects(WXHDC dc
)
137 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
138 if (m_selectedBitmap
.Ok())
140 m_selectedBitmap
.SetSelectedInto(NULL
);
146 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
151 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
156 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
161 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, TRUE
);
166 m_brush
= wxNullBrush
;
168 m_palette
= wxNullPalette
;
170 m_backgroundBrush
= wxNullBrush
;
171 m_selectedBitmap
= wxNullBitmap
;
174 // ---------------------------------------------------------------------------
176 // ---------------------------------------------------------------------------
178 void wxDC::DoSetClippingRegion(wxCoord cx
, wxCoord cy
, wxCoord cw
, wxCoord ch
)
183 m_clipX2
= (int)(cx
+ cw
);
184 m_clipY2
= (int)(cy
+ ch
);
186 DoClipping((WXHDC
) m_hDC
);
189 void wxDC::DoSetClippingRegionAsRegion(const wxRegion
& region
)
191 wxCHECK_RET( region
.GetHRGN(), wxT("invalid clipping region") );
193 wxRect box
= region
.GetBox();
198 m_clipX2
= box
.x
+ box
.width
;
199 m_clipY2
= box
.y
+ box
.height
;
202 SelectClipRgn(GetHdc(), (HRGN
) region
.GetHRGN());
204 ExtSelectClipRgn(GetHdc(), (HRGN
) region
.GetHRGN(), RGN_AND
);
208 void wxDC::DoClipping(WXHDC dc
)
210 if (m_clipping
&& dc
)
212 IntersectClipRect((HDC
) dc
, XLOG2DEV(m_clipX1
), YLOG2DEV(m_clipY1
),
213 XLOG2DEV(m_clipX2
), YLOG2DEV(m_clipY2
));
217 void wxDC::DestroyClippingRegion()
219 if (m_clipping
&& m_hDC
)
221 // TODO: this should restore the previous clipping region,
222 // so that OnPaint processing works correctly, and the update clipping region
223 // doesn't get destroyed after the first DestroyClippingRegion.
224 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
225 SelectClipRgn(GetHdc(), rgn
);
231 // ---------------------------------------------------------------------------
232 // query capabilities
233 // ---------------------------------------------------------------------------
235 bool wxDC::CanDrawBitmap() const
240 bool wxDC::CanGetTextExtent() const
242 // What sort of display is it?
243 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
245 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
248 int wxDC::GetDepth() const
250 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
253 // ---------------------------------------------------------------------------
255 // ---------------------------------------------------------------------------
262 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
266 // No, I think we should simply ignore this if printing on e.g.
268 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
269 if (!m_selectedBitmap
.Ok())
272 rect
.left
= 0; rect
.top
= 0;
273 rect
.right
= m_selectedBitmap
.GetWidth();
274 rect
.bottom
= m_selectedBitmap
.GetHeight();
277 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
279 DWORD colour
= GetBkColor(GetHdc());
280 HBRUSH brush
= CreateSolidBrush(colour
);
281 FillRect(GetHdc(), &rect
, brush
);
284 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
285 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
286 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
287 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
288 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
291 void wxDC::DoFloodFill(wxCoord x
, wxCoord y
, const wxColour
& col
, int style
)
293 if ( !::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
295 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
298 // quoting from the MSDN docs:
300 // Following are some of the reasons this function might fail:
302 // * The filling could not be completed.
303 // * The specified point has the boundary color specified by the
304 // crColor parameter (if FLOODFILLBORDER was requested).
305 // * The specified point does not have the color specified by
306 // crColor (if FLOODFILLSURFACE was requested)
307 // * The point is outside the clipping region that is, it is not
308 // visible on the device.
310 wxLogLastError("ExtFloodFill");
313 CalcBoundingBox(x
, y
);
316 bool wxDC::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
318 // get the color of the pixel
319 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
321 // JACS: what was this for?
323 // get the color of the pen
324 COLORREF pencolor
= 0x00ffffff;
327 pencolor
= m_pen
.GetColour().GetPixel();
331 // return the color of the pixel
334 col
->Set(GetRValue(pixelcolor
),
335 GetGValue(pixelcolor
),
336 GetBValue(pixelcolor
));
339 // check, if color of the pixels is the same as the color of the current
340 // pen and return TRUE if it is, FALSE otherwise
341 // JACS, 24/02/2000: can't understand the reason for this, so returning TRUE instead.
342 // return pixelcolor == pencolor;
347 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
349 wxCoord x1
= x
-VIEWPORT_EXTENT
;
350 wxCoord y1
= y
-VIEWPORT_EXTENT
;
351 wxCoord x2
= x
+VIEWPORT_EXTENT
;
352 wxCoord y2
= y
+VIEWPORT_EXTENT
;
354 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), NULL
);
355 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y
));
357 (void)MoveToEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), NULL
);
358 (void)LineTo(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y2
));
360 CalcBoundingBox(x1
, y1
);
361 CalcBoundingBox(x2
, y2
);
364 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
366 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), NULL
);
367 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y2
));
369 // Normalization: Windows doesn't draw the last point of the line.
370 // But apparently neither does GTK+, so we take it out again.
371 // (void)LineTo(GetHdc(), XLOG2DEV(x2) + 1, YLOG2DEV(y2));
373 CalcBoundingBox(x1
, y1
);
374 CalcBoundingBox(x2
, y2
);
377 void wxDC::DoDrawArc(wxCoord x1
,wxCoord y1
,wxCoord x2
,wxCoord y2
, wxCoord xc
, wxCoord yc
)
381 double radius
= (double)sqrt(dx
*dx
+dy
*dy
) ;;
382 if (x1
==x2
&& x2
==y2
)
384 DrawEllipse(xc
,yc
,(wxCoord
)(radius
*2.0),(wxCoord
)(radius
*2.0));
388 wxCoord xx1
= XLOG2DEV(x1
);
389 wxCoord yy1
= YLOG2DEV(y1
);
390 wxCoord xx2
= XLOG2DEV(x2
);
391 wxCoord yy2
= YLOG2DEV(y2
);
392 wxCoord xxc
= XLOG2DEV(xc
);
393 wxCoord yyc
= YLOG2DEV(yc
);
394 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
396 (void)MoveToEx(GetHdc(), (int) xx1
, (int) yy1
, NULL
);
397 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
398 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
399 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
400 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
401 if (m_brush
.Ok() && m_brush
.GetStyle() !=wxTRANSPARENT
)
403 // Have to add 1 to bottom-right corner of rectangle
404 // to make semi-circles look right (crooked line otherwise).
405 // Unfortunately this is not a reliable method, depends
406 // on the size of shape.
407 // TODO: figure out why this happens!
408 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1,
412 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
,
415 CalcBoundingBox((wxCoord
)(xc
-radius
), (wxCoord
)(yc
-radius
));
416 CalcBoundingBox((wxCoord
)(xc
+radius
), (wxCoord
)(yc
+radius
));
419 void wxDC::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
420 wxCoord width
, wxCoord height
)
422 wxCoord x2
= x1
+ width
,
425 #if defined(__WIN32__) && !defined(__SC__)
432 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
434 // In WIN16, draw a cross
435 HPEN blackPen
= ::CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
436 HPEN whiteBrush
= (HPEN
)::GetStockObject(WHITE_BRUSH
);
437 HPEN hPenOld
= (HPEN
)::SelectObject(GetHdc(), blackPen
);
438 HPEN hBrushOld
= (HPEN
)::SelectObject(GetHdc(), whiteBrush
);
439 ::SetROP2(GetHdc(), R2_COPYPEN
);
440 Rectangle(GetHdc(), x1
, y1
, x2
, y2
);
441 MoveTo(GetHdc(), x1
, y1
);
442 LineTo(GetHdc(), x2
, y2
);
443 MoveTo(GetHdc(), x2
, y1
);
444 LineTo(GetHdc(), x1
, y2
);
445 ::SelectObject(GetHdc(), hPenOld
);
446 ::SelectObject(GetHdc(), hBrushOld
);
447 ::DeleteObject(blackPen
);
450 CalcBoundingBox(x1
, y1
);
451 CalcBoundingBox(x2
, y2
);
454 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
456 COLORREF color
= 0x00ffffff;
459 color
= m_pen
.GetColour().GetPixel();
462 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
464 CalcBoundingBox(x
, y
);
467 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
469 COLORREF old_textground
= ::GetTextColor(GetHdc());
470 COLORREF old_background
= ::GetBkColor(GetHdc());
471 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
474 if (m_textForegroundColour
.Ok())
475 { //just the oposite from what is expected see help on pattern brush
476 // 1 in mask becomes bk color
477 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel() );
479 if (m_textBackgroundColour
.Ok())
480 { //just the oposite from what is expected
481 // 0 in mask becomes text color
482 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
485 if (m_backgroundMode
== wxTRANSPARENT
)
486 SetBkMode(GetHdc(), TRANSPARENT
);
488 SetBkMode(GetHdc(), OPAQUE
);
491 // Do things less efficiently if we have offsets
492 if (xoffset
!= 0 || yoffset
!= 0)
494 POINT
*cpoints
= new POINT
[n
];
496 for (i
= 0; i
< n
; i
++)
498 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
499 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
501 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
503 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
504 (void)Polygon(GetHdc(), cpoints
, n
);
505 SetPolyFillMode(GetHdc(),prev
);
511 for (i
= 0; i
< n
; i
++)
512 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
514 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
515 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
516 SetPolyFillMode(GetHdc(),prev
);
519 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
521 ::SetBkMode(GetHdc(), TRANSPARENT
);
522 ::SetTextColor(GetHdc(), old_textground
);
523 ::SetBkColor(GetHdc(), old_background
);
527 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
529 // Do things less efficiently if we have offsets
530 if (xoffset
!= 0 || yoffset
!= 0)
532 POINT
*cpoints
= new POINT
[n
];
534 for (i
= 0; i
< n
; i
++)
536 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
537 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
539 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
541 (void)Polyline(GetHdc(), cpoints
, n
);
547 for (i
= 0; i
< n
; i
++)
548 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
550 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
554 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
556 COLORREF colFgOld
= 0,
559 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
561 colFgOld
= ::GetTextColor(GetHdc());
562 colBgOld
= ::GetBkColor(GetHdc());
564 if ( m_textForegroundColour
.Ok() )
566 // just the oposite from what is expected see help on pattern brush
567 // 1 in mask becomes bk color
568 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel());
571 if ( m_textBackgroundColour
.Ok() )
573 // 0 in mask becomes text color
574 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel());
577 // VZ: IMHO this does strictly nothing here
578 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
582 wxCoord x2
= x
+ width
;
583 wxCoord y2
= y
+ height
;
585 if ((m_logicalFunction
== wxCOPY
) && (m_pen
.GetStyle() == wxTRANSPARENT
))
588 rect
.left
= XLOG2DEV(x
);
589 rect
.top
= YLOG2DEV(y
);
590 rect
.right
= XLOG2DEV(x2
);
591 rect
.bottom
= YLOG2DEV(y2
);
592 (void)FillRect(GetHdc(), &rect
, (HBRUSH
)m_brush
.GetResourceHandle() );
596 // Windows draws the filled rectangles without outline (i.e. drawn with a
597 // transparent pen) one pixel smaller in both directions and we want them
598 // to have the same size regardless of which pen is used - adjust
600 // I wonder if this shouldn´t be done after the LOG2DEV() conversions. RR.
601 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
607 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
611 CalcBoundingBox(x
, y
);
612 CalcBoundingBox(x2
, y2
);
614 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
616 // restore the colours we changed
617 ::SetBkMode(GetHdc(), TRANSPARENT
);
618 ::SetTextColor(GetHdc(), colFgOld
);
619 ::SetBkColor(GetHdc(), colBgOld
);
623 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
625 // Now, a negative radius value is interpreted to mean
626 // 'the proportion of the smallest X or Y dimension'
630 double smallest
= 0.0;
635 radius
= (- radius
* smallest
);
638 wxCoord x2
= (x
+width
);
639 wxCoord y2
= (y
+height
);
641 // Windows draws the filled rectangles without outline (i.e. drawn with a
642 // transparent pen) one pixel smaller in both directions and we want them
643 // to have the same size regardless of which pen is used - adjust
644 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
650 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
651 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
653 CalcBoundingBox(x
, y
);
654 CalcBoundingBox(x2
, y2
);
657 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
659 wxCoord x2
= (x
+width
);
660 wxCoord y2
= (y
+height
);
662 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
664 CalcBoundingBox(x
, y
);
665 CalcBoundingBox(x2
, y2
);
668 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
669 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
674 int rx1
= XLOG2DEV(x
+w
/2);
675 int ry1
= YLOG2DEV(y
+h
/2);
682 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
683 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
684 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
685 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
687 // draw pie with NULL_PEN first and then outline otherwise a line is
688 // drawn from the start and end points to the centre
689 HPEN orig_pen
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
692 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
697 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
698 rx1
, ry1
-1, rx2
, ry2
-1);
700 ::SelectObject(GetHdc(), orig_pen
);
701 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
704 CalcBoundingBox(x
, y
);
705 CalcBoundingBox(x2
, y2
);
708 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
710 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
712 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
714 CalcBoundingBox(x
, y
);
715 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
718 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
720 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
722 int width
= bmp
.GetWidth(),
723 height
= bmp
.GetHeight();
725 HBITMAP hbmpMask
= 0;
729 wxMask
*mask
= bmp
.GetMask();
731 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
735 // don't give assert here because this would break existing
736 // programs - just silently ignore useMask parameter
744 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
745 ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
747 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
749 bool ok
= ::MaskBlt(GetHdc(), x
, y
, width
, height
,
752 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
758 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
761 memDC
.SelectObject(bmp
);
763 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
765 memDC
.SelectObject(wxNullBitmap
);
768 else // no mask, just use BitBlt()
771 HDC memdc
= ::CreateCompatibleDC( cdc
);
772 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
774 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
776 COLORREF old_textground
= ::GetTextColor(GetHdc());
777 COLORREF old_background
= ::GetBkColor(GetHdc());
778 if (m_textForegroundColour
.Ok())
780 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
782 if (m_textBackgroundColour
.Ok())
784 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
787 ::SelectObject( memdc
, hbitmap
);
788 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
791 ::SetTextColor(GetHdc(), old_textground
);
792 ::SetBkColor(GetHdc(), old_background
);
796 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
798 DrawAnyText(text
, x
, y
);
800 // update the bounding box
801 CalcBoundingBox(x
, y
);
804 GetTextExtent(text
, &w
, &h
);
805 CalcBoundingBox(x
+ w
, y
+ h
);
808 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
810 // prepare for drawing the text
811 if ( m_textForegroundColour
.Ok() )
812 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
814 DWORD old_background
= 0;
815 if ( m_textBackgroundColour
.Ok() )
817 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
820 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
823 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
824 text
.c_str(), text
.length()) == 0 )
826 wxLogLastError("TextOut");
829 // restore the old parameters (text foreground colour may be left because
830 // it never is set to anything else, but background should remain
831 // transparent even if we just drew an opaque string)
832 if ( m_textBackgroundColour
.Ok() )
833 (void)SetBkColor(GetHdc(), old_background
);
835 SetBkMode(GetHdc(), TRANSPARENT
);
838 void wxDC::DoDrawRotatedText(const wxString
& text
,
839 wxCoord x
, wxCoord y
,
842 // we test that we have some font because otherwise we should still use the
843 // "else" part below to avoid that DrawRotatedText(angle = 180) and
844 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
845 // font for drawing rotated fonts unfortunately)
846 if ( (angle
== 0.0) && m_font
.Ok() )
848 DoDrawText(text
, x
, y
);
852 // NB: don't take DEFAULT_GUI_FONT because it's not TrueType and so
853 // can't have non zero orientation/escapement
854 wxFont font
= m_font
.Ok() ? m_font
: *wxNORMAL_FONT
;
855 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
857 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
859 wxLogLastError("GetObject(hfont)");
862 // GDI wants the angle in tenth of degree
863 long angle10
= (long)(angle
* 10);
864 lf
.lfEscapement
= angle10
;
865 lf
. lfOrientation
= angle10
;
867 hfont
= ::CreateFontIndirect(&lf
);
870 wxLogLastError("CreateFont");
874 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
876 DrawAnyText(text
, x
, y
);
878 (void)::SelectObject(GetHdc(), hfontOld
);
879 (void)::DeleteObject(hfont
);
882 // call the bounding box by adding all four vertices of the rectangle
883 // containing the text to it (simpler and probably not slower than
884 // determining which of them is really topmost/leftmost/...)
886 GetTextExtent(text
, &w
, &h
);
888 double rad
= DegToRad(angle
);
890 // "upper left" and "upper right"
891 CalcBoundingBox(x
, y
);
892 CalcBoundingBox(x
+ w
*cos(rad
), y
- h
*sin(rad
));
894 // "bottom left" and "bottom right"
895 x
+= (wxCoord
)(h
*sin(rad
));
896 y
+= (wxCoord
)(h
*cos(rad
));
897 CalcBoundingBox(x
, y
);
898 CalcBoundingBox(x
+ h
*sin(rad
), y
+ h
*cos(rad
));
902 // ---------------------------------------------------------------------------
904 // ---------------------------------------------------------------------------
906 void wxDC::SetPalette(const wxPalette
& palette
)
908 // Set the old object temporarily, in case the assignment deletes an object
909 // that's not yet selected out.
912 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
920 // Setting a NULL colourmap is a way of restoring
921 // the original colourmap
924 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
931 if (m_palette
.Ok() && m_palette
.GetHPALETTE())
933 HPALETTE oldPal
= ::SelectPalette(GetHdc(), (HPALETTE
) m_palette
.GetHPALETTE(), TRUE
);
935 m_oldPalette
= (WXHPALETTE
) oldPal
;
937 ::RealizePalette(GetHdc());
941 void wxDC::SetFont(const wxFont
& the_font
)
943 // Set the old object temporarily, in case the assignment deletes an object
944 // that's not yet selected out.
947 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
956 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
960 if (m_font
.Ok() && m_font
.GetResourceHandle())
962 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
963 if (f
== (HFONT
) NULL
)
965 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
968 m_oldFont
= (WXHFONT
) f
;
972 void wxDC::SetPen(const wxPen
& pen
)
974 // Set the old object temporarily, in case the assignment deletes an object
975 // that's not yet selected out.
978 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
987 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
993 if (m_pen
.GetResourceHandle())
995 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
997 m_oldPen
= (WXHPEN
) p
;
1002 void wxDC::SetBrush(const wxBrush
& brush
)
1004 // Set the old object temporarily, in case the assignment deletes an object
1005 // that's not yet selected out.
1008 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1017 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1023 if (m_brush
.GetResourceHandle())
1026 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
1028 m_oldBrush
= (WXHBRUSH
) b
;
1033 void wxDC::SetBackground(const wxBrush
& brush
)
1035 m_backgroundBrush
= brush
;
1037 if (!m_backgroundBrush
.Ok())
1042 bool customColours
= TRUE
;
1043 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
1044 // change background colours from the control-panel specified colours.
1045 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
1046 customColours
= FALSE
;
1050 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
1052 m_canvas
->SetTransparent(TRUE
);
1056 // New behaviour, 10/2/99: setting the background brush of a DC
1057 // doesn't affect the window background colour. However,
1058 // I'm leaving in the transparency setting because it's needed by
1059 // various controls (e.g. wxStaticText) to determine whether to draw
1060 // transparently or not. TODO: maybe this should be a new function
1061 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1063 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1064 m_canvas
->SetTransparent(FALSE
);
1068 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1070 (void)SetBkColor(GetHdc(), new_color
);
1074 void wxDC::SetBackgroundMode(int mode
)
1076 m_backgroundMode
= mode
;
1078 // SetBackgroundColour now only refers to text background
1079 // and m_backgroundMode is used there
1082 if (m_backgroundMode == wxTRANSPARENT)
1083 ::SetBkMode(GetHdc(), TRANSPARENT);
1085 ::SetBkMode(GetHdc(), OPAQUE);
1089 void wxDC::SetLogicalFunction(int function
)
1091 m_logicalFunction
= function
;
1096 void wxDC::SetRop(WXHDC dc
)
1098 if ( !dc
|| m_logicalFunction
< 0 )
1103 switch (m_logicalFunction
)
1105 case wxCLEAR
: rop
= R2_BLACK
; break;
1106 case wxXOR
: rop
= R2_XORPEN
; break;
1107 case wxINVERT
: rop
= R2_NOT
; break;
1108 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1109 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1110 case wxCOPY
: rop
= R2_COPYPEN
; break;
1111 case wxAND
: rop
= R2_MASKPEN
; break;
1112 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1113 case wxNO_OP
: rop
= R2_NOP
; break;
1114 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1115 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1116 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1117 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1118 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1119 case wxOR
: rop
= R2_MERGEPEN
; break;
1120 case wxSET
: rop
= R2_WHITE
; break;
1123 wxFAIL_MSG( wxT("unsupported logical function") );
1127 SetROP2(GetHdc(), rop
);
1130 bool wxDC::StartDoc(const wxString
& message
)
1132 // We might be previewing, so return TRUE to let it continue.
1140 void wxDC::StartPage()
1144 void wxDC::EndPage()
1148 // ---------------------------------------------------------------------------
1150 // ---------------------------------------------------------------------------
1152 wxCoord
wxDC::GetCharHeight() const
1154 TEXTMETRIC lpTextMetric
;
1156 GetTextMetrics(GetHdc(), &lpTextMetric
);
1158 return YDEV2LOGREL(lpTextMetric
.tmHeight
);
1161 wxCoord
wxDC::GetCharWidth() const
1163 TEXTMETRIC lpTextMetric
;
1165 GetTextMetrics(GetHdc(), &lpTextMetric
);
1167 return XDEV2LOGREL(lpTextMetric
.tmAveCharWidth
);
1170 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1171 wxCoord
*descent
, wxCoord
*externalLeading
,
1172 wxFont
*theFont
) const
1174 wxFont
*fontToUse
= (wxFont
*) theFont
;
1176 fontToUse
= (wxFont
*) &m_font
;
1181 GetTextExtentPoint(GetHdc(), WXSTRINGCAST string
, wxStrlen(WXSTRINGCAST string
), &sizeRect
);
1182 GetTextMetrics(GetHdc(), &tm
);
1184 if (x
) *x
= XDEV2LOGREL(sizeRect
.cx
);
1185 if (y
) *y
= YDEV2LOGREL(sizeRect
.cy
);
1186 if (descent
) *descent
= tm
.tmDescent
;
1187 if (externalLeading
) *externalLeading
= tm
.tmExternalLeading
;
1190 void wxDC::SetMapMode(int mode
)
1192 m_mappingMode
= mode
;
1194 int pixel_width
= 0;
1195 int pixel_height
= 0;
1199 pixel_width
= GetDeviceCaps(GetHdc(), HORZRES
);
1200 pixel_height
= GetDeviceCaps(GetHdc(), VERTRES
);
1201 mm_width
= GetDeviceCaps(GetHdc(), HORZSIZE
);
1202 mm_height
= GetDeviceCaps(GetHdc(), VERTSIZE
);
1204 if ((pixel_width
== 0) || (pixel_height
== 0) || (mm_width
== 0) || (mm_height
== 0))
1209 double mm2pixelsX
= pixel_width
/mm_width
;
1210 double mm2pixelsY
= pixel_height
/mm_height
;
1216 m_logicalScaleX
= (twips2mm
* mm2pixelsX
);
1217 m_logicalScaleY
= (twips2mm
* mm2pixelsY
);
1222 m_logicalScaleX
= (pt2mm
* mm2pixelsX
);
1223 m_logicalScaleY
= (pt2mm
* mm2pixelsY
);
1228 m_logicalScaleX
= mm2pixelsX
;
1229 m_logicalScaleY
= mm2pixelsY
;
1234 m_logicalScaleX
= (mm2pixelsX
/10.0);
1235 m_logicalScaleY
= (mm2pixelsY
/10.0);
1241 m_logicalScaleX
= 1.0;
1242 m_logicalScaleY
= 1.0;
1247 if (::GetMapMode(GetHdc()) != MM_ANISOTROPIC
)
1248 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1250 SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1251 m_windowExtX
= (int)MS_XDEV2LOGREL(VIEWPORT_EXTENT
);
1252 m_windowExtY
= (int)MS_YDEV2LOGREL(VIEWPORT_EXTENT
);
1253 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
1254 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1255 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1258 void wxDC::SetUserScale(double x
, double y
)
1263 SetMapMode(m_mappingMode
);
1266 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1268 m_signX
= xLeftRight
? 1 : -1;
1269 m_signY
= yBottomUp
? -1 : 1;
1271 SetMapMode(m_mappingMode
);
1274 void wxDC::SetSystemScale(double x
, double y
)
1279 SetMapMode(m_mappingMode
);
1282 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1284 m_logicalOriginX
= x
;
1285 m_logicalOriginY
= y
;
1287 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1290 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1292 m_deviceOriginX
= x
;
1293 m_deviceOriginY
= y
;
1295 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1298 // ---------------------------------------------------------------------------
1299 // coordinates transformations
1300 // ---------------------------------------------------------------------------
1302 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1304 return (wxCoord
) (((x
) - m_deviceOriginX
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
) - m_logicalOriginX
);
1307 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1309 return (wxCoord
) ((x
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
));
1312 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1314 return (wxCoord
) (((y
) - m_deviceOriginY
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
) - m_logicalOriginY
);
1317 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1319 return (wxCoord
) ((y
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
));
1322 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1324 return (wxCoord
) ((x
- m_logicalOriginX
)*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
+ m_deviceOriginX
);
1327 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1329 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
);
1332 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1334 return (wxCoord
) ((y
- m_logicalOriginY
)*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
+ m_deviceOriginY
);
1337 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1339 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
);
1342 // ---------------------------------------------------------------------------
1344 // ---------------------------------------------------------------------------
1346 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1347 wxCoord width
, wxCoord height
,
1348 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1349 int rop
, bool useMask
)
1351 wxMask
*mask
= NULL
;
1354 const wxBitmap
& bmp
= source
->m_selectedBitmap
;
1355 mask
= bmp
.GetMask();
1357 if ( !(bmp
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1359 // don't give assert here because this would break existing
1360 // programs - just silently ignore useMask parameter
1365 COLORREF old_textground
= ::GetTextColor(GetHdc());
1366 COLORREF old_background
= ::GetBkColor(GetHdc());
1367 if (m_textForegroundColour
.Ok())
1369 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1371 if (m_textBackgroundColour
.Ok())
1373 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1376 DWORD dwRop
= SRCCOPY
;
1379 case wxXOR
: dwRop
= SRCINVERT
; break;
1380 case wxINVERT
: dwRop
= DSTINVERT
; break;
1381 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1382 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1383 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1384 case wxSET
: dwRop
= WHITENESS
; break;
1385 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1386 case wxAND
: dwRop
= SRCAND
; break;
1387 case wxOR
: dwRop
= SRCPAINT
; break;
1388 case wxEQUIV
: dwRop
= 0x00990066; break;
1389 case wxNAND
: dwRop
= 0x007700E6; break;
1390 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1391 case wxCOPY
: dwRop
= SRCCOPY
; break;
1392 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1393 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1394 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1396 wxFAIL_MSG( wxT("unsupported logical function") );
1405 // we want the part of the image corresponding to the mask to be
1406 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1407 // meaning of fg and bg is inverted which corresponds to wxWin notion
1408 // of the mask which is also contrary to the Windows one)
1409 success
= ::MaskBlt(GetHdc(), xdest
, ydest
, width
, height
,
1410 GetHdcOf(*source
), xsrc
, ysrc
,
1411 (HBITMAP
)mask
->GetMaskBitmap(), 0, 0,
1412 MAKEROP4(dwRop
, DSTCOPY
)) != 0;
1417 // Blit bitmap with mask
1419 // create a temp buffer bitmap and DCs to access it and the mask
1420 HDC dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1421 HDC dc_buffer
= ::CreateCompatibleDC(GetHdc());
1422 HBITMAP buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1423 ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1424 ::SelectObject(dc_buffer
, buffer_bmap
);
1426 // copy dest to buffer
1427 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1428 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1430 wxLogLastError("BitBlt");
1433 // copy src to buffer using selected raster op
1434 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1435 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1437 wxLogLastError("BitBlt");
1440 // set masked area in buffer to BLACK (pixel value 0)
1441 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1442 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1443 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1444 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1446 wxLogLastError("BitBlt");
1449 // set unmasked area in dest to BLACK
1450 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1451 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1452 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1453 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1455 wxLogLastError("BitBlt");
1457 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1458 ::SetTextColor(GetHdc(), prevCol
);
1460 // OR buffer to dest
1461 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1462 (int)width
, (int)height
,
1463 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1466 wxLogLastError("BitBlt");
1469 // tidy up temporary DCs and bitmap
1470 ::SelectObject(dc_mask
, 0);
1471 ::DeleteDC(dc_mask
);
1472 ::SelectObject(dc_buffer
, 0);
1473 ::DeleteDC(dc_buffer
);
1474 ::DeleteObject(buffer_bmap
);
1477 else // no mask, just BitBlt() it
1479 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1480 (int)width
, (int)height
,
1481 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) != 0;
1484 wxLogLastError("BitBlt");
1487 ::SetTextColor(GetHdc(), old_textground
);
1488 ::SetBkColor(GetHdc(), old_background
);
1493 void wxDC::DoGetSize(int *w
, int *h
) const
1495 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
1496 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
1499 void wxDC::DoGetSizeMM(int *w
, int *h
) const
1501 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZSIZE
);
1502 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1505 wxSize
wxDC::GetPPI() const
1507 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
1508 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
1510 return wxSize(x
, y
);
1513 // For use by wxWindows only, unless custom units are required.
1514 void wxDC::SetLogicalScale(double x
, double y
)
1516 m_logicalScaleX
= x
;
1517 m_logicalScaleY
= y
;
1520 #if WXWIN_COMPATIBILITY
1521 void wxDC::DoGetTextExtent(const wxString
& string
, float *x
, float *y
,
1522 float *descent
, float *externalLeading
,
1523 wxFont
*theFont
, bool use16bit
) const
1525 wxCoord x1
, y1
, descent1
, externalLeading1
;
1526 GetTextExtent(string
, & x1
, & y1
, & descent1
, & externalLeading1
, theFont
, use16bit
);
1529 *descent
= descent1
;
1530 if (externalLeading
)
1531 *externalLeading
= externalLeading1
;
1535 // ---------------------------------------------------------------------------
1536 // spline drawing code
1537 // ---------------------------------------------------------------------------
1541 class wxSpline
: public wxObject
1547 wxSpline(wxList
*list
);
1548 void DeletePoints();
1550 // Doesn't delete points
1554 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
);
1556 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
1557 double a3
, double b3
, double a4
, double b4
);
1558 void wx_clear_stack();
1559 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
1560 double *y3
, double *x4
, double *y4
);
1561 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
1562 double x4
, double y4
);
1563 static bool wx_spline_add_point(double x
, double y
);
1564 static void wx_spline_draw_point_array(wxDC
*dc
);
1565 wxSpline
*wx_make_spline(int x1
, int y1
, int x2
, int y2
, int x3
, int y3
);
1567 void wxDC::DoDrawSpline(wxList
*list
)
1569 wxSpline
spline(list
);
1571 wx_draw_open_spline(this, &spline
);
1574 wxList wx_spline_point_list
;
1576 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
)
1579 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
1580 double x1
, y1
, x2
, y2
;
1582 wxNode
*node
= spline
->points
->First();
1583 p
= (wxPoint
*)node
->Data();
1588 node
= node
->Next();
1589 p
= (wxPoint
*)node
->Data();
1593 cx1
= (double)((x1
+ x2
) / 2);
1594 cy1
= (double)((y1
+ y2
) / 2);
1595 cx2
= (double)((cx1
+ x2
) / 2);
1596 cy2
= (double)((cy1
+ y2
) / 2);
1598 wx_spline_add_point(x1
, y1
);
1600 while ((node
= node
->Next()) != NULL
)
1602 p
= (wxPoint
*)node
->Data();
1607 cx4
= (double)(x1
+ x2
) / 2;
1608 cy4
= (double)(y1
+ y2
) / 2;
1609 cx3
= (double)(x1
+ cx4
) / 2;
1610 cy3
= (double)(y1
+ cy4
) / 2;
1612 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
1616 cx2
= (double)(cx1
+ x2
) / 2;
1617 cy2
= (double)(cy1
+ y2
) / 2;
1620 wx_spline_add_point((double)wx_round(cx1
), (double)wx_round(cy1
));
1621 wx_spline_add_point(x2
, y2
);
1623 wx_spline_draw_point_array(dc
);
1627 /********************* CURVES FOR SPLINES *****************************
1629 The following spline drawing routine is from
1631 "An Algorithm for High-Speed Curve Generation"
1632 by George Merrill Chaikin,
1633 Computer Graphics and Image Processing, 3, Academic Press,
1638 "On Chaikin's Algorithm" by R. F. Riesenfeld,
1639 Computer Graphics and Image Processing, 4, Academic Press,
1642 ***********************************************************************/
1644 #define half(z1, z2) ((z1+z2)/2.0)
1647 /* iterative version */
1649 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
1652 register double xmid
, ymid
;
1653 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1656 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
1658 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
1659 xmid
= (double)half(x2
, x3
);
1660 ymid
= (double)half(y2
, y3
);
1661 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
1662 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
1663 wx_spline_add_point((double)wx_round(x1
), (double)wx_round(y1
));
1664 wx_spline_add_point((double)wx_round(xmid
), (double)wx_round(ymid
));
1666 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
1667 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
1668 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
1669 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
1675 /* utilities used by spline drawing routines */
1678 typedef struct wx_spline_stack_struct
{
1679 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1683 #define SPLINE_STACK_DEPTH 20
1684 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
1685 static Stack
*wx_stack_top
;
1686 static int wx_stack_count
;
1688 void wx_clear_stack()
1690 wx_stack_top
= wx_spline_stack
;
1694 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
1696 wx_stack_top
->x1
= x1
;
1697 wx_stack_top
->y1
= y1
;
1698 wx_stack_top
->x2
= x2
;
1699 wx_stack_top
->y2
= y2
;
1700 wx_stack_top
->x3
= x3
;
1701 wx_stack_top
->y3
= y3
;
1702 wx_stack_top
->x4
= x4
;
1703 wx_stack_top
->y4
= y4
;
1708 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
1709 double *x3
, double *y3
, double *x4
, double *y4
)
1711 if (wx_stack_count
== 0)
1715 *x1
= wx_stack_top
->x1
;
1716 *y1
= wx_stack_top
->y1
;
1717 *x2
= wx_stack_top
->x2
;
1718 *y2
= wx_stack_top
->y2
;
1719 *x3
= wx_stack_top
->x3
;
1720 *y3
= wx_stack_top
->y3
;
1721 *x4
= wx_stack_top
->x4
;
1722 *y4
= wx_stack_top
->y4
;
1726 static bool wx_spline_add_point(double x
, double y
)
1728 wxPoint
*point
= new wxPoint
;
1731 wx_spline_point_list
.Append((wxObject
*)point
);
1735 static void wx_spline_draw_point_array(wxDC
*dc
)
1737 dc
->DrawLines(&wx_spline_point_list
, 0, 0);
1738 wxNode
*node
= wx_spline_point_list
.First();
1741 wxPoint
*point
= (wxPoint
*)node
->Data();
1744 node
= wx_spline_point_list
.First();
1748 wxSpline::wxSpline(wxList
*list
)
1753 wxSpline::~wxSpline()
1757 void wxSpline::DeletePoints()
1759 for(wxNode
*node
= points
->First(); node
; node
= points
->First())
1761 wxPoint
*point
= (wxPoint
*)node
->Data();
1769 #endif // wxUSE_SPLINES