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 // get the color of the pen
322 COLORREF pencolor
= 0x00ffffff;
325 pencolor
= m_pen
.GetColour().GetPixel();
328 // return the color of the pixel
331 col
->Set(GetRValue(pixelcolor
),
332 GetGValue(pixelcolor
),
333 GetBValue(pixelcolor
));
336 // check, if color of the pixels is the same as the color of the current
337 // pen and return TRUE if it is, FALSE otherwise
338 return pixelcolor
== pencolor
;
341 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
343 wxCoord x1
= x
-VIEWPORT_EXTENT
;
344 wxCoord y1
= y
-VIEWPORT_EXTENT
;
345 wxCoord x2
= x
+VIEWPORT_EXTENT
;
346 wxCoord y2
= y
+VIEWPORT_EXTENT
;
348 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), NULL
);
349 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y
));
351 (void)MoveToEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), NULL
);
352 (void)LineTo(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y2
));
354 CalcBoundingBox(x1
, y1
);
355 CalcBoundingBox(x2
, y2
);
358 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
360 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), NULL
);
361 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y2
));
363 // Normalization: Windows doesn't draw the last point of the line.
364 // But apparently neither does GTK+, so we take it out again.
365 // (void)LineTo(GetHdc(), XLOG2DEV(x2) + 1, YLOG2DEV(y2));
367 CalcBoundingBox(x1
, y1
);
368 CalcBoundingBox(x2
, y2
);
371 void wxDC::DoDrawArc(wxCoord x1
,wxCoord y1
,wxCoord x2
,wxCoord y2
, wxCoord xc
, wxCoord yc
)
375 double radius
= (double)sqrt(dx
*dx
+dy
*dy
) ;;
376 if (x1
==x2
&& x2
==y2
)
378 DrawEllipse(xc
,yc
,(wxCoord
)(radius
*2.0),(wxCoord
)(radius
*2.0));
382 wxCoord xx1
= XLOG2DEV(x1
);
383 wxCoord yy1
= YLOG2DEV(y1
);
384 wxCoord xx2
= XLOG2DEV(x2
);
385 wxCoord yy2
= YLOG2DEV(y2
);
386 wxCoord xxc
= XLOG2DEV(xc
);
387 wxCoord yyc
= YLOG2DEV(yc
);
388 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
390 (void)MoveToEx(GetHdc(), (int) xx1
, (int) yy1
, NULL
);
391 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
392 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
393 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
394 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
395 if (m_brush
.Ok() && m_brush
.GetStyle() !=wxTRANSPARENT
)
397 // Have to add 1 to bottom-right corner of rectangle
398 // to make semi-circles look right (crooked line otherwise).
399 // Unfortunately this is not a reliable method, depends
400 // on the size of shape.
401 // TODO: figure out why this happens!
402 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1,
406 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
,
409 CalcBoundingBox((wxCoord
)(xc
-radius
), (wxCoord
)(yc
-radius
));
410 CalcBoundingBox((wxCoord
)(xc
+radius
), (wxCoord
)(yc
+radius
));
413 void wxDC::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
414 wxCoord width
, wxCoord height
)
416 wxCoord x2
= x1
+ width
,
419 #if defined(__WIN32__) && !defined(__SC__)
426 DrawFrameControl(GetHdc(), &rect
, DFC_MENU
, DFCS_MENUCHECK
);
428 // In WIN16, draw a cross
429 HPEN blackPen
= ::CreatePen(PS_SOLID
, 1, RGB(0, 0, 0));
430 HPEN whiteBrush
= (HPEN
)::GetStockObject(WHITE_BRUSH
);
431 HPEN hPenOld
= (HPEN
)::SelectObject(hdcMem
, blackPen
);
432 HPEN hBrushOld
= (HPEN
)::SelectObject(hdcMem
, whiteBrush
);
433 ::SetROP2(GetHdc(), R2_COPYPEN
);
434 Rectangle(GetHdc(), x1
, y1
, x2
, y2
);
435 MoveTo(GetHdc(), x1
, y1
);
436 LineTo(GetHdc(), x2
, y2
);
437 MoveTo(GetHdc(), x2
, y1
);
438 LineTo(GetHdc(), x1
, y2
);
439 ::SelectObject(GetHdc(), hPenOld
);
440 ::SelectObject(GetHdc(), hBrushOld
);
441 ::DeleteObject(blackPen
);
444 CalcBoundingBox(x1
, y1
);
445 CalcBoundingBox(x2
, y2
);
448 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
450 COLORREF color
= 0x00ffffff;
453 color
= m_pen
.GetColour().GetPixel();
456 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
458 CalcBoundingBox(x
, y
);
461 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
463 COLORREF old_textground
= ::GetTextColor(GetHdc());
464 COLORREF old_background
= ::GetBkColor(GetHdc());
465 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
468 if (m_textForegroundColour
.Ok())
469 { //just the oposite from what is expected see help on pattern brush
470 // 1 in mask becomes bk color
471 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel() );
473 if (m_textBackgroundColour
.Ok())
474 { //just the oposite from what is expected
475 // 0 in mask becomes text color
476 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
479 if (m_backgroundMode
== wxTRANSPARENT
)
480 SetBkMode(GetHdc(), TRANSPARENT
);
482 SetBkMode(GetHdc(), OPAQUE
);
485 // Do things less efficiently if we have offsets
486 if (xoffset
!= 0 || yoffset
!= 0)
488 POINT
*cpoints
= new POINT
[n
];
490 for (i
= 0; i
< n
; i
++)
492 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
493 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
495 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
497 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
498 (void)Polygon(GetHdc(), cpoints
, n
);
499 SetPolyFillMode(GetHdc(),prev
);
505 for (i
= 0; i
< n
; i
++)
506 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
508 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
509 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
510 SetPolyFillMode(GetHdc(),prev
);
513 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
515 ::SetBkMode(GetHdc(), TRANSPARENT
);
516 ::SetTextColor(GetHdc(), old_textground
);
517 ::SetBkColor(GetHdc(), old_background
);
521 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
523 // Do things less efficiently if we have offsets
524 if (xoffset
!= 0 || yoffset
!= 0)
526 POINT
*cpoints
= new POINT
[n
];
528 for (i
= 0; i
< n
; i
++)
530 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
531 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
533 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
535 (void)Polyline(GetHdc(), cpoints
, n
);
541 for (i
= 0; i
< n
; i
++)
542 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
544 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
548 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
550 COLORREF colFgOld
= 0,
553 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
555 colFgOld
= ::GetTextColor(GetHdc());
556 colBgOld
= ::GetBkColor(GetHdc());
558 if ( m_textForegroundColour
.Ok() )
560 // just the oposite from what is expected see help on pattern brush
561 // 1 in mask becomes bk color
562 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel());
565 if ( m_textBackgroundColour
.Ok() )
567 // 0 in mask becomes text color
568 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel());
571 // VZ: IMHO this does strictly nothing here
572 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
576 wxCoord x2
= x
+ width
;
577 wxCoord y2
= y
+ height
;
579 // Windows draws the filled rectangles without outline (i.e. drawn with a
580 // transparent pen) one pixel smaller in both directions and we want them
581 // to have the same size regardless of which pen is used - adjust
582 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
588 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
590 CalcBoundingBox(x
, y
);
591 CalcBoundingBox(x2
, y2
);
593 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
595 // restore the colours we changed
596 ::SetBkMode(GetHdc(), TRANSPARENT
);
597 ::SetTextColor(GetHdc(), colFgOld
);
598 ::SetBkColor(GetHdc(), colBgOld
);
602 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
604 // Now, a negative radius value is interpreted to mean
605 // 'the proportion of the smallest X or Y dimension'
609 double smallest
= 0.0;
614 radius
= (- radius
* smallest
);
617 wxCoord x2
= (x
+width
);
618 wxCoord y2
= (y
+height
);
620 // Windows draws the filled rectangles without outline (i.e. drawn with a
621 // transparent pen) one pixel smaller in both directions and we want them
622 // to have the same size regardless of which pen is used - adjust
623 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
629 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
630 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
632 CalcBoundingBox(x
, y
);
633 CalcBoundingBox(x2
, y2
);
636 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
638 wxCoord x2
= (x
+width
);
639 wxCoord y2
= (y
+height
);
641 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
643 CalcBoundingBox(x
, y
);
644 CalcBoundingBox(x2
, y2
);
647 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
648 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
653 int rx1
= XLOG2DEV(x
+w
/2);
654 int ry1
= YLOG2DEV(y
+h
/2);
661 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
662 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
663 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
664 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
666 // draw pie with NULL_PEN first and then outline otherwise a line is
667 // drawn from the start and end points to the centre
668 HPEN orig_pen
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
671 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
676 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
677 rx1
, ry1
-1, rx2
, ry2
-1);
679 ::SelectObject(GetHdc(), orig_pen
);
680 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
683 CalcBoundingBox(x
, y
);
684 CalcBoundingBox(x2
, y2
);
687 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
689 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
691 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
693 CalcBoundingBox(x
, y
);
694 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
697 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
699 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
701 int width
= bmp
.GetWidth(),
702 height
= bmp
.GetHeight();
704 HBITMAP hbmpMask
= 0;
708 wxMask
*mask
= bmp
.GetMask();
710 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
714 // don't give assert here because this would break existing
715 // programs - just silently ignore useMask parameter
723 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
724 ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
726 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
728 bool ok
= ::MaskBlt(GetHdc(), x
, y
, width
, height
,
731 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
737 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
740 memDC
.SelectObject(bmp
);
742 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
744 memDC
.SelectObject(wxNullBitmap
);
747 else // no mask, just use BitBlt()
750 HDC memdc
= ::CreateCompatibleDC( cdc
);
751 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
753 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
755 COLORREF old_textground
= ::GetTextColor(GetHdc());
756 COLORREF old_background
= ::GetBkColor(GetHdc());
757 if (m_textForegroundColour
.Ok())
759 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
761 if (m_textBackgroundColour
.Ok())
763 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
766 ::SelectObject( memdc
, hbitmap
);
767 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
770 ::SetTextColor(GetHdc(), old_textground
);
771 ::SetBkColor(GetHdc(), old_background
);
775 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
777 DrawAnyText(text
, x
, y
);
779 // update the bounding box
780 CalcBoundingBox(x
, y
);
783 GetTextExtent(text
, &w
, &h
);
784 CalcBoundingBox(x
+ w
, y
+ h
);
787 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
789 // prepare for drawing the text
790 if ( m_textForegroundColour
.Ok() )
791 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
793 DWORD old_background
= 0;
794 if ( m_textBackgroundColour
.Ok() )
796 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
799 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
802 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
803 text
.c_str(), text
.length()) == 0 )
805 wxLogLastError("TextOut");
808 // restore the old parameters (text foreground colour may be left because
809 // it never is set to anything else, but background should remain
810 // transparent even if we just drew an opaque string)
811 if ( m_textBackgroundColour
.Ok() )
812 (void)SetBkColor(GetHdc(), old_background
);
814 SetBkMode(GetHdc(), TRANSPARENT
);
817 void wxDC::DoDrawRotatedText(const wxString
& text
,
818 wxCoord x
, wxCoord y
,
821 // we test that we have some font because otherwise we should still use the
822 // "else" part below to avoid that DrawRotatedText(angle = 180) and
823 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
824 // font for drawing rotated fonts unfortunately)
825 if ( (angle
== 0.0) && m_font
.Ok() )
827 DoDrawText(text
, x
, y
);
831 // NB: don't take DEFAULT_GUI_FONT because it's not TrueType and so
832 // can't have non zero orientation/escapement
833 wxFont font
= m_font
.Ok() ? m_font
: *wxNORMAL_FONT
;
834 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
836 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
838 wxLogLastError("GetObject(hfont)");
841 // GDI wants the angle in tenth of degree
842 long angle10
= (long)(angle
* 10);
843 lf
.lfEscapement
= angle10
;
844 lf
. lfOrientation
= angle10
;
846 hfont
= ::CreateFontIndirect(&lf
);
849 wxLogLastError("CreateFont");
853 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
855 DrawAnyText(text
, x
, y
);
857 (void)::SelectObject(GetHdc(), hfontOld
);
858 (void)::DeleteObject(hfont
);
861 // call the bounding box by adding all four vertices of the rectangle
862 // containing the text to it (simpler and probably not slower than
863 // determining which of them is really topmost/leftmost/...)
865 GetTextExtent(text
, &w
, &h
);
867 double rad
= DegToRad(angle
);
869 // "upper left" and "upper right"
870 CalcBoundingBox(x
, y
);
871 CalcBoundingBox(x
+ w
*cos(rad
), y
- h
*sin(rad
));
873 // "bottom left" and "bottom right"
874 x
+= (wxCoord
)(h
*sin(rad
));
875 y
+= (wxCoord
)(h
*cos(rad
));
876 CalcBoundingBox(x
, y
);
877 CalcBoundingBox(x
+ h
*sin(rad
), y
+ h
*cos(rad
));
881 // ---------------------------------------------------------------------------
883 // ---------------------------------------------------------------------------
885 void wxDC::SetPalette(const wxPalette
& palette
)
887 // Set the old object temporarily, in case the assignment deletes an object
888 // that's not yet selected out.
891 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
899 // Setting a NULL colourmap is a way of restoring
900 // the original colourmap
903 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
910 if (m_palette
.Ok() && m_palette
.GetHPALETTE())
912 HPALETTE oldPal
= ::SelectPalette(GetHdc(), (HPALETTE
) m_palette
.GetHPALETTE(), TRUE
);
914 m_oldPalette
= (WXHPALETTE
) oldPal
;
916 ::RealizePalette(GetHdc());
920 void wxDC::SetFont(const wxFont
& the_font
)
922 // Set the old object temporarily, in case the assignment deletes an object
923 // that's not yet selected out.
926 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
935 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
939 if (m_font
.Ok() && m_font
.GetResourceHandle())
941 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
942 if (f
== (HFONT
) NULL
)
944 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
947 m_oldFont
= (WXHFONT
) f
;
951 void wxDC::SetPen(const wxPen
& pen
)
953 // Set the old object temporarily, in case the assignment deletes an object
954 // that's not yet selected out.
957 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
966 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
972 if (m_pen
.GetResourceHandle())
974 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
976 m_oldPen
= (WXHPEN
) p
;
981 void wxDC::SetBrush(const wxBrush
& brush
)
983 // Set the old object temporarily, in case the assignment deletes an object
984 // that's not yet selected out.
987 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
996 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
1002 if (m_brush
.GetResourceHandle())
1005 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
1007 m_oldBrush
= (WXHBRUSH
) b
;
1012 void wxDC::SetBackground(const wxBrush
& brush
)
1014 m_backgroundBrush
= brush
;
1016 if (!m_backgroundBrush
.Ok())
1021 bool customColours
= TRUE
;
1022 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
1023 // change background colours from the control-panel specified colours.
1024 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
1025 customColours
= FALSE
;
1029 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
1031 m_canvas
->SetTransparent(TRUE
);
1035 // New behaviour, 10/2/99: setting the background brush of a DC
1036 // doesn't affect the window background colour. However,
1037 // I'm leaving in the transparency setting because it's needed by
1038 // various controls (e.g. wxStaticText) to determine whether to draw
1039 // transparently or not. TODO: maybe this should be a new function
1040 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1042 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1043 m_canvas
->SetTransparent(FALSE
);
1047 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1049 (void)SetBkColor(GetHdc(), new_color
);
1053 void wxDC::SetBackgroundMode(int mode
)
1055 m_backgroundMode
= mode
;
1057 // SetBackgroundColour now only refers to text background
1058 // and m_backgroundMode is used there
1061 if (m_backgroundMode == wxTRANSPARENT)
1062 ::SetBkMode(GetHdc(), TRANSPARENT);
1064 ::SetBkMode(GetHdc(), OPAQUE);
1068 void wxDC::SetLogicalFunction(int function
)
1070 m_logicalFunction
= function
;
1075 void wxDC::SetRop(WXHDC dc
)
1077 if ( !dc
|| m_logicalFunction
< 0 )
1082 switch (m_logicalFunction
)
1084 case wxCLEAR
: rop
= R2_BLACK
; break;
1085 case wxXOR
: rop
= R2_XORPEN
; break;
1086 case wxINVERT
: rop
= R2_NOT
; break;
1087 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1088 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1089 case wxCOPY
: rop
= R2_COPYPEN
; break;
1090 case wxAND
: rop
= R2_MASKPEN
; break;
1091 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1092 case wxNO_OP
: rop
= R2_NOP
; break;
1093 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1094 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1095 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1096 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1097 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1098 case wxOR
: rop
= R2_MERGEPEN
; break;
1099 case wxSET
: rop
= R2_WHITE
; break;
1102 wxFAIL_MSG( wxT("unsupported logical function") );
1106 SetROP2(GetHdc(), rop
);
1109 bool wxDC::StartDoc(const wxString
& message
)
1111 // We might be previewing, so return TRUE to let it continue.
1119 void wxDC::StartPage()
1123 void wxDC::EndPage()
1127 // ---------------------------------------------------------------------------
1129 // ---------------------------------------------------------------------------
1131 wxCoord
wxDC::GetCharHeight() const
1133 TEXTMETRIC lpTextMetric
;
1135 GetTextMetrics(GetHdc(), &lpTextMetric
);
1137 return YDEV2LOGREL(lpTextMetric
.tmHeight
);
1140 wxCoord
wxDC::GetCharWidth() const
1142 TEXTMETRIC lpTextMetric
;
1144 GetTextMetrics(GetHdc(), &lpTextMetric
);
1146 return XDEV2LOGREL(lpTextMetric
.tmAveCharWidth
);
1149 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1150 wxCoord
*descent
, wxCoord
*externalLeading
,
1151 wxFont
*theFont
) const
1153 wxFont
*fontToUse
= (wxFont
*) theFont
;
1155 fontToUse
= (wxFont
*) &m_font
;
1160 GetTextExtentPoint(GetHdc(), WXSTRINGCAST string
, wxStrlen(WXSTRINGCAST string
), &sizeRect
);
1161 GetTextMetrics(GetHdc(), &tm
);
1163 if (x
) *x
= XDEV2LOGREL(sizeRect
.cx
);
1164 if (y
) *y
= YDEV2LOGREL(sizeRect
.cy
);
1165 if (descent
) *descent
= tm
.tmDescent
;
1166 if (externalLeading
) *externalLeading
= tm
.tmExternalLeading
;
1169 void wxDC::SetMapMode(int mode
)
1171 m_mappingMode
= mode
;
1173 int pixel_width
= 0;
1174 int pixel_height
= 0;
1178 pixel_width
= GetDeviceCaps(GetHdc(), HORZRES
);
1179 pixel_height
= GetDeviceCaps(GetHdc(), VERTRES
);
1180 mm_width
= GetDeviceCaps(GetHdc(), HORZSIZE
);
1181 mm_height
= GetDeviceCaps(GetHdc(), VERTSIZE
);
1183 if ((pixel_width
== 0) || (pixel_height
== 0) || (mm_width
== 0) || (mm_height
== 0))
1188 double mm2pixelsX
= pixel_width
/mm_width
;
1189 double mm2pixelsY
= pixel_height
/mm_height
;
1195 m_logicalScaleX
= (twips2mm
* mm2pixelsX
);
1196 m_logicalScaleY
= (twips2mm
* mm2pixelsY
);
1201 m_logicalScaleX
= (pt2mm
* mm2pixelsX
);
1202 m_logicalScaleY
= (pt2mm
* mm2pixelsY
);
1207 m_logicalScaleX
= mm2pixelsX
;
1208 m_logicalScaleY
= mm2pixelsY
;
1213 m_logicalScaleX
= (mm2pixelsX
/10.0);
1214 m_logicalScaleY
= (mm2pixelsY
/10.0);
1220 m_logicalScaleX
= 1.0;
1221 m_logicalScaleY
= 1.0;
1226 if (::GetMapMode(GetHdc()) != MM_ANISOTROPIC
)
1227 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1229 SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1230 m_windowExtX
= (int)MS_XDEV2LOGREL(VIEWPORT_EXTENT
);
1231 m_windowExtY
= (int)MS_YDEV2LOGREL(VIEWPORT_EXTENT
);
1232 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
1233 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1234 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1237 void wxDC::SetUserScale(double x
, double y
)
1242 SetMapMode(m_mappingMode
);
1245 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1247 m_signX
= xLeftRight
? 1 : -1;
1248 m_signY
= yBottomUp
? -1 : 1;
1250 SetMapMode(m_mappingMode
);
1253 void wxDC::SetSystemScale(double x
, double y
)
1258 SetMapMode(m_mappingMode
);
1261 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1263 m_logicalOriginX
= x
;
1264 m_logicalOriginY
= y
;
1266 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1269 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1271 m_deviceOriginX
= x
;
1272 m_deviceOriginY
= y
;
1274 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1277 // ---------------------------------------------------------------------------
1278 // coordinates transformations
1279 // ---------------------------------------------------------------------------
1281 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1283 return (wxCoord
) (((x
) - m_deviceOriginX
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
) - m_logicalOriginX
);
1286 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1288 return (wxCoord
) ((x
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
));
1291 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1293 return (wxCoord
) (((y
) - m_deviceOriginY
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
) - m_logicalOriginY
);
1296 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1298 return (wxCoord
) ((y
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
));
1301 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1303 return (wxCoord
) ((x
- m_logicalOriginX
)*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
+ m_deviceOriginX
);
1306 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1308 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
);
1311 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1313 return (wxCoord
) ((y
- m_logicalOriginY
)*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
+ m_deviceOriginY
);
1316 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1318 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
);
1321 // ---------------------------------------------------------------------------
1323 // ---------------------------------------------------------------------------
1325 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1326 wxCoord width
, wxCoord height
,
1327 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1328 int rop
, bool useMask
)
1330 wxMask
*mask
= NULL
;
1333 const wxBitmap
& bmp
= source
->m_selectedBitmap
;
1334 mask
= bmp
.GetMask();
1336 if ( !(bmp
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1338 // don't give assert here because this would break existing
1339 // programs - just silently ignore useMask parameter
1344 COLORREF old_textground
= ::GetTextColor(GetHdc());
1345 COLORREF old_background
= ::GetBkColor(GetHdc());
1346 if (m_textForegroundColour
.Ok())
1348 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1350 if (m_textBackgroundColour
.Ok())
1352 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1355 DWORD dwRop
= SRCCOPY
;
1358 case wxXOR
: dwRop
= SRCINVERT
; break;
1359 case wxINVERT
: dwRop
= DSTINVERT
; break;
1360 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1361 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1362 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1363 case wxSET
: dwRop
= WHITENESS
; break;
1364 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1365 case wxAND
: dwRop
= SRCAND
; break;
1366 case wxOR
: dwRop
= SRCPAINT
; break;
1367 case wxEQUIV
: dwRop
= 0x00990066; break;
1368 case wxNAND
: dwRop
= 0x007700E6; break;
1369 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1370 case wxCOPY
: dwRop
= SRCCOPY
; break;
1371 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1372 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1373 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1375 wxFAIL_MSG( wxT("unsupported logical function") );
1384 // we want the part of the image corresponding to the mask to be
1385 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1386 // meaning of fg and bg is inverted which corresponds to wxWin notion
1387 // of the mask which is also contrary to the Windows one)
1388 success
= ::MaskBlt(GetHdc(), xdest
, ydest
, width
, height
,
1389 GetHdcOf(*source
), xsrc
, ysrc
,
1390 (HBITMAP
)mask
->GetMaskBitmap(), 0, 0,
1391 MAKEROP4(dwRop
, DSTCOPY
)) != 0;
1396 // Blit bitmap with mask
1398 // create a temp buffer bitmap and DCs to access it and the mask
1399 HDC dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1400 HDC dc_buffer
= ::CreateCompatibleDC(GetHdc());
1401 HBITMAP buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1402 ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1403 ::SelectObject(dc_buffer
, buffer_bmap
);
1405 // copy dest to buffer
1406 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1407 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1409 wxLogLastError("BitBlt");
1412 // copy src to buffer using selected raster op
1413 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1414 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1416 wxLogLastError("BitBlt");
1419 // set masked area in buffer to BLACK (pixel value 0)
1420 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1421 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1422 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1423 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1425 wxLogLastError("BitBlt");
1428 // set unmasked area in dest to BLACK
1429 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1430 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1431 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1432 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1434 wxLogLastError("BitBlt");
1436 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1437 ::SetTextColor(GetHdc(), prevCol
);
1439 // OR buffer to dest
1440 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1441 (int)width
, (int)height
,
1442 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1445 wxLogLastError("BitBlt");
1448 // tidy up temporary DCs and bitmap
1449 ::SelectObject(dc_mask
, 0);
1450 ::DeleteDC(dc_mask
);
1451 ::SelectObject(dc_buffer
, 0);
1452 ::DeleteDC(dc_buffer
);
1453 ::DeleteObject(buffer_bmap
);
1456 else // no mask, just BitBlt() it
1458 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1459 (int)width
, (int)height
,
1460 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) != 0;
1463 wxLogLastError("BitBlt");
1466 ::SetTextColor(GetHdc(), old_textground
);
1467 ::SetBkColor(GetHdc(), old_background
);
1472 void wxDC::DoGetSize(int *w
, int *h
) const
1474 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
1475 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
1478 void wxDC::DoGetSizeMM(int *w
, int *h
) const
1480 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZSIZE
);
1481 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1484 wxSize
wxDC::GetPPI() const
1486 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
1487 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
1489 return wxSize(x
, y
);
1492 // For use by wxWindows only, unless custom units are required.
1493 void wxDC::SetLogicalScale(double x
, double y
)
1495 m_logicalScaleX
= x
;
1496 m_logicalScaleY
= y
;
1499 #if WXWIN_COMPATIBILITY
1500 void wxDC::DoGetTextExtent(const wxString
& string
, float *x
, float *y
,
1501 float *descent
, float *externalLeading
,
1502 wxFont
*theFont
, bool use16bit
) const
1504 wxCoord x1
, y1
, descent1
, externalLeading1
;
1505 GetTextExtent(string
, & x1
, & y1
, & descent1
, & externalLeading1
, theFont
, use16bit
);
1508 *descent
= descent1
;
1509 if (externalLeading
)
1510 *externalLeading
= externalLeading1
;
1514 // ---------------------------------------------------------------------------
1515 // spline drawing code
1516 // ---------------------------------------------------------------------------
1520 class wxSpline
: public wxObject
1526 wxSpline(wxList
*list
);
1527 void DeletePoints();
1529 // Doesn't delete points
1533 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
);
1535 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
1536 double a3
, double b3
, double a4
, double b4
);
1537 void wx_clear_stack();
1538 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
1539 double *y3
, double *x4
, double *y4
);
1540 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
1541 double x4
, double y4
);
1542 static bool wx_spline_add_point(double x
, double y
);
1543 static void wx_spline_draw_point_array(wxDC
*dc
);
1544 wxSpline
*wx_make_spline(int x1
, int y1
, int x2
, int y2
, int x3
, int y3
);
1546 void wxDC::DoDrawSpline(wxList
*list
)
1548 wxSpline
spline(list
);
1550 wx_draw_open_spline(this, &spline
);
1553 wxList wx_spline_point_list
;
1555 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
)
1558 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
1559 double x1
, y1
, x2
, y2
;
1561 wxNode
*node
= spline
->points
->First();
1562 p
= (wxPoint
*)node
->Data();
1567 node
= node
->Next();
1568 p
= (wxPoint
*)node
->Data();
1572 cx1
= (double)((x1
+ x2
) / 2);
1573 cy1
= (double)((y1
+ y2
) / 2);
1574 cx2
= (double)((cx1
+ x2
) / 2);
1575 cy2
= (double)((cy1
+ y2
) / 2);
1577 wx_spline_add_point(x1
, y1
);
1579 while ((node
= node
->Next()) != NULL
)
1581 p
= (wxPoint
*)node
->Data();
1586 cx4
= (double)(x1
+ x2
) / 2;
1587 cy4
= (double)(y1
+ y2
) / 2;
1588 cx3
= (double)(x1
+ cx4
) / 2;
1589 cy3
= (double)(y1
+ cy4
) / 2;
1591 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
1595 cx2
= (double)(cx1
+ x2
) / 2;
1596 cy2
= (double)(cy1
+ y2
) / 2;
1599 wx_spline_add_point((double)wx_round(cx1
), (double)wx_round(cy1
));
1600 wx_spline_add_point(x2
, y2
);
1602 wx_spline_draw_point_array(dc
);
1606 /********************* CURVES FOR SPLINES *****************************
1608 The following spline drawing routine is from
1610 "An Algorithm for High-Speed Curve Generation"
1611 by George Merrill Chaikin,
1612 Computer Graphics and Image Processing, 3, Academic Press,
1617 "On Chaikin's Algorithm" by R. F. Riesenfeld,
1618 Computer Graphics and Image Processing, 4, Academic Press,
1621 ***********************************************************************/
1623 #define half(z1, z2) ((z1+z2)/2.0)
1626 /* iterative version */
1628 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
1631 register double xmid
, ymid
;
1632 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1635 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
1637 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
1638 xmid
= (double)half(x2
, x3
);
1639 ymid
= (double)half(y2
, y3
);
1640 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
1641 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
1642 wx_spline_add_point((double)wx_round(x1
), (double)wx_round(y1
));
1643 wx_spline_add_point((double)wx_round(xmid
), (double)wx_round(ymid
));
1645 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
1646 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
1647 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
1648 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
1654 /* utilities used by spline drawing routines */
1657 typedef struct wx_spline_stack_struct
{
1658 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1662 #define SPLINE_STACK_DEPTH 20
1663 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
1664 static Stack
*wx_stack_top
;
1665 static int wx_stack_count
;
1667 void wx_clear_stack()
1669 wx_stack_top
= wx_spline_stack
;
1673 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
1675 wx_stack_top
->x1
= x1
;
1676 wx_stack_top
->y1
= y1
;
1677 wx_stack_top
->x2
= x2
;
1678 wx_stack_top
->y2
= y2
;
1679 wx_stack_top
->x3
= x3
;
1680 wx_stack_top
->y3
= y3
;
1681 wx_stack_top
->x4
= x4
;
1682 wx_stack_top
->y4
= y4
;
1687 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
1688 double *x3
, double *y3
, double *x4
, double *y4
)
1690 if (wx_stack_count
== 0)
1694 *x1
= wx_stack_top
->x1
;
1695 *y1
= wx_stack_top
->y1
;
1696 *x2
= wx_stack_top
->x2
;
1697 *y2
= wx_stack_top
->y2
;
1698 *x3
= wx_stack_top
->x3
;
1699 *y3
= wx_stack_top
->y3
;
1700 *x4
= wx_stack_top
->x4
;
1701 *y4
= wx_stack_top
->y4
;
1705 static bool wx_spline_add_point(double x
, double y
)
1707 wxPoint
*point
= new wxPoint
;
1710 wx_spline_point_list
.Append((wxObject
*)point
);
1714 static void wx_spline_draw_point_array(wxDC
*dc
)
1716 dc
->DrawLines(&wx_spline_point_list
, 0, 0);
1717 wxNode
*node
= wx_spline_point_list
.First();
1720 wxPoint
*point
= (wxPoint
*)node
->Data();
1723 node
= wx_spline_point_list
.First();
1727 wxSpline::wxSpline(wxList
*list
)
1732 wxSpline::~wxSpline()
1736 void wxSpline::DeletePoints()
1738 for(wxNode
*node
= points
->First(); node
; node
= points
->First())
1740 wxPoint
*point
= (wxPoint
*)node
->Data();
1748 #endif // wxUSE_SPLINES