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
51 #if wxUSE_NORLANDER_HEADERS
61 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxObject
)
63 // ---------------------------------------------------------------------------
65 // ---------------------------------------------------------------------------
67 static const int VIEWPORT_EXTENT
= 1000;
69 static const int MM_POINTS
= 9;
70 static const int MM_METRIC
= 10;
72 // usually this is defined in math.h
74 static const double M_PI
= 3.14159265358979323846;
77 // ---------------------------------------------------------------------------
79 // ---------------------------------------------------------------------------
81 // convert degrees to radians
82 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
84 // ===========================================================================
86 // ===========================================================================
88 // ---------------------------------------------------------------------------
90 // ---------------------------------------------------------------------------
92 // Default constructor
106 m_windowExtX
= VIEWPORT_EXTENT
;
107 m_windowExtY
= VIEWPORT_EXTENT
;
116 SelectOldObjects(m_hDC
);
118 if ( m_canvas
== NULL
)
119 ::DeleteDC(GetHdc());
121 ::ReleaseDC((HWND
)m_canvas
->GetHWND(), GetHdc());
127 // This will select current objects out of the DC,
128 // which is what you have to do before deleting the
130 void wxDC::SelectOldObjects(WXHDC dc
)
136 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
137 if (m_selectedBitmap
.Ok())
139 m_selectedBitmap
.SetSelectedInto(NULL
);
145 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
150 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
155 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
160 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, TRUE
);
165 m_brush
= wxNullBrush
;
167 m_palette
= wxNullPalette
;
169 m_backgroundBrush
= wxNullBrush
;
170 m_selectedBitmap
= wxNullBitmap
;
173 // ---------------------------------------------------------------------------
175 // ---------------------------------------------------------------------------
177 void wxDC::DoSetClippingRegion(wxCoord cx
, wxCoord cy
, wxCoord cw
, wxCoord ch
)
182 m_clipX2
= (int)(cx
+ cw
);
183 m_clipY2
= (int)(cy
+ ch
);
185 DoClipping((WXHDC
) m_hDC
);
188 void wxDC::DoSetClippingRegionAsRegion(const wxRegion
& region
)
190 wxCHECK_RET( region
.GetHRGN(), wxT("invalid clipping region") );
192 wxRect box
= region
.GetBox();
197 m_clipX2
= box
.x
+ box
.width
;
198 m_clipY2
= box
.y
+ box
.height
;
201 SelectClipRgn(GetHdc(), (HRGN
) region
.GetHRGN());
203 ExtSelectClipRgn(GetHdc(), (HRGN
) region
.GetHRGN(), RGN_AND
);
207 void wxDC::DoClipping(WXHDC dc
)
209 if (m_clipping
&& dc
)
211 IntersectClipRect((HDC
) dc
, XLOG2DEV(m_clipX1
), YLOG2DEV(m_clipY1
),
212 XLOG2DEV(m_clipX2
), YLOG2DEV(m_clipY2
));
216 void wxDC::DestroyClippingRegion()
218 if (m_clipping
&& m_hDC
)
220 // TODO: this should restore the previous clipping region,
221 // so that OnPaint processing works correctly, and the update clipping region
222 // doesn't get destroyed after the first DestroyClippingRegion.
223 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
224 SelectClipRgn(GetHdc(), rgn
);
230 // ---------------------------------------------------------------------------
231 // query capabilities
232 // ---------------------------------------------------------------------------
234 bool wxDC::CanDrawBitmap() const
239 bool wxDC::CanGetTextExtent() const
241 // What sort of display is it?
242 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
244 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
247 int wxDC::GetDepth() const
249 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
252 // ---------------------------------------------------------------------------
254 // ---------------------------------------------------------------------------
261 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
265 // No, I think we should simply ignore this if printing on e.g.
267 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
268 if (!m_selectedBitmap
.Ok())
271 rect
.left
= 0; rect
.top
= 0;
272 rect
.right
= m_selectedBitmap
.GetWidth();
273 rect
.bottom
= m_selectedBitmap
.GetHeight();
276 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
278 DWORD colour
= GetBkColor(GetHdc());
279 HBRUSH brush
= CreateSolidBrush(colour
);
280 FillRect(GetHdc(), &rect
, brush
);
283 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
284 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
285 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
286 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
287 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
290 void wxDC::DoFloodFill(wxCoord x
, wxCoord y
, const wxColour
& col
, int style
)
292 if ( !::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
294 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
297 // quoting from the MSDN docs:
299 // Following are some of the reasons this function might fail:
301 // * The filling could not be completed.
302 // * The specified point has the boundary color specified by the
303 // crColor parameter (if FLOODFILLBORDER was requested).
304 // * The specified point does not have the color specified by
305 // crColor (if FLOODFILLSURFACE was requested)
306 // * The point is outside the clipping region that is, it is not
307 // visible on the device.
309 wxLogLastError("ExtFloodFill");
312 CalcBoundingBox(x
, y
);
315 bool wxDC::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
317 // get the color of the pixel
318 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
320 // get the color of the pen
321 COLORREF pencolor
= 0x00ffffff;
324 pencolor
= m_pen
.GetColour().GetPixel();
327 // return the color of the pixel
330 col
->Set(GetRValue(pixelcolor
),
331 GetGValue(pixelcolor
),
332 GetBValue(pixelcolor
));
335 // check, if color of the pixels is the same as the color of the current
336 // pen and return TRUE if it is, FALSE otherwise
337 return pixelcolor
== pencolor
;
340 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
342 wxCoord x1
= x
-VIEWPORT_EXTENT
;
343 wxCoord y1
= y
-VIEWPORT_EXTENT
;
344 wxCoord x2
= x
+VIEWPORT_EXTENT
;
345 wxCoord y2
= y
+VIEWPORT_EXTENT
;
347 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), NULL
);
348 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y
));
350 (void)MoveToEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), NULL
);
351 (void)LineTo(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y2
));
353 CalcBoundingBox(x1
, y1
);
354 CalcBoundingBox(x2
, y2
);
357 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
359 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), NULL
);
360 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y2
));
362 /* MATTHEW: [6] New normalization */
363 #if WX_STANDARD_GRAPHICS
364 (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::DoDrawPoint(wxCoord x
, wxCoord y
)
415 COLORREF color
= 0x00ffffff;
418 color
= m_pen
.GetColour().GetPixel();
421 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
423 CalcBoundingBox(x
, y
);
426 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
428 COLORREF old_textground
= ::GetTextColor(GetHdc());
429 COLORREF old_background
= ::GetBkColor(GetHdc());
430 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
433 if (m_textForegroundColour
.Ok())
434 { //just the oposite from what is expected see help on pattern brush
435 // 1 in mask becomes bk color
436 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel() );
438 if (m_textBackgroundColour
.Ok())
439 { //just the oposite from what is expected
440 // 0 in mask becomes text color
441 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
444 if (m_backgroundMode
== wxTRANSPARENT
)
445 SetBkMode(GetHdc(), TRANSPARENT
);
447 SetBkMode(GetHdc(), OPAQUE
);
450 // Do things less efficiently if we have offsets
451 if (xoffset
!= 0 || yoffset
!= 0)
453 POINT
*cpoints
= new POINT
[n
];
455 for (i
= 0; i
< n
; i
++)
457 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
458 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
460 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
462 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
463 (void)Polygon(GetHdc(), cpoints
, n
);
464 SetPolyFillMode(GetHdc(),prev
);
470 for (i
= 0; i
< n
; i
++)
471 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
473 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
474 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
475 SetPolyFillMode(GetHdc(),prev
);
478 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
480 ::SetBkMode(GetHdc(), TRANSPARENT
);
481 ::SetTextColor(GetHdc(), old_textground
);
482 ::SetBkColor(GetHdc(), old_background
);
486 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
488 // Do things less efficiently if we have offsets
489 if (xoffset
!= 0 || yoffset
!= 0)
491 POINT
*cpoints
= new POINT
[n
];
493 for (i
= 0; i
< n
; i
++)
495 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
496 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
498 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
500 (void)Polyline(GetHdc(), cpoints
, n
);
506 for (i
= 0; i
< n
; i
++)
507 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
509 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
513 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
515 COLORREF colFgOld
= 0,
518 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
520 colFgOld
= ::GetTextColor(GetHdc());
521 colBgOld
= ::GetBkColor(GetHdc());
523 if ( m_textForegroundColour
.Ok() )
525 // just the oposite from what is expected see help on pattern brush
526 // 1 in mask becomes bk color
527 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel());
530 if ( m_textBackgroundColour
.Ok() )
532 // 0 in mask becomes text color
533 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel());
536 // VZ: IMHO this does strictly nothing here
537 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
541 wxCoord x2
= x
+ width
;
542 wxCoord y2
= y
+ height
;
544 // Windows draws the filled rectangles without outline (i.e. drawn with a
545 // transparent pen) one pixel smaller in both directions and we want them
546 // to have the same size regardless of which pen is used - adjust
547 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
553 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
555 CalcBoundingBox(x
, y
);
556 CalcBoundingBox(x2
, y2
);
558 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
560 // restore the colours we changed
561 ::SetBkMode(GetHdc(), TRANSPARENT
);
562 ::SetTextColor(GetHdc(), colFgOld
);
563 ::SetBkColor(GetHdc(), colBgOld
);
567 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
569 // Now, a negative radius value is interpreted to mean
570 // 'the proportion of the smallest X or Y dimension'
574 double smallest
= 0.0;
579 radius
= (- radius
* smallest
);
582 wxCoord x2
= (x
+width
);
583 wxCoord y2
= (y
+height
);
585 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
586 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
588 CalcBoundingBox(x
, y
);
589 CalcBoundingBox(x2
, y2
);
592 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
594 wxCoord x2
= (x
+width
);
595 wxCoord y2
= (y
+height
);
597 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
599 CalcBoundingBox(x
, y
);
600 CalcBoundingBox(x2
, y2
);
603 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
604 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
609 int rx1
= XLOG2DEV(x
+w
/2);
610 int ry1
= YLOG2DEV(y
+h
/2);
617 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
618 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
619 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
620 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
622 // draw pie with NULL_PEN first and then outline otherwise a line is
623 // drawn from the start and end points to the centre
624 HPEN orig_pen
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
627 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
632 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
633 rx1
, ry1
-1, rx2
, ry2
-1);
635 ::SelectObject(GetHdc(), orig_pen
);
636 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
639 CalcBoundingBox(x
, y
);
640 CalcBoundingBox(x2
, y2
);
643 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
645 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
647 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
649 CalcBoundingBox(x
, y
);
650 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
653 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
655 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
657 int width
= bmp
.GetWidth(),
658 height
= bmp
.GetHeight();
663 HDC memdc
= ::CreateCompatibleDC( cdc
);
664 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
666 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
668 COLORREF old_textground
= ::GetTextColor(GetHdc());
669 COLORREF old_background
= ::GetBkColor(GetHdc());
670 if (m_textForegroundColour
.Ok())
672 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
674 if (m_textBackgroundColour
.Ok())
676 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
679 ::SelectObject( memdc
, hbitmap
);
680 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
683 ::SetTextColor(GetHdc(), old_textground
);
684 ::SetBkColor(GetHdc(), old_background
);
688 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API level
690 memDC
.SelectObject(bmp
);
692 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
694 memDC
.SelectObject(wxNullBitmap
);
698 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
700 DrawAnyText(text
, x
, y
);
702 // update the bounding box
703 CalcBoundingBox(x
, y
);
706 GetTextExtent(text
, &w
, &h
);
707 CalcBoundingBox(x
+ w
, y
+ h
);
710 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
712 // prepare for drawing the text
713 if ( m_textForegroundColour
.Ok() )
714 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
716 DWORD old_background
= 0;
717 if ( m_textBackgroundColour
.Ok() )
719 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
722 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
725 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
726 text
.c_str(), text
.length()) == 0 )
728 wxLogLastError("TextOut");
731 // restore the old parameters (text foreground colour may be left because
732 // it never is set to anything else, but background should remain
733 // transparent even if we just drew an opaque string)
734 if ( m_textBackgroundColour
.Ok() )
735 (void)SetBkColor(GetHdc(), old_background
);
737 SetBkMode(GetHdc(), TRANSPARENT
);
740 void wxDC::DoDrawRotatedText(const wxString
& text
,
741 wxCoord x
, wxCoord y
,
744 // we test that we have some font because otherwise we should still use the
745 // "else" part below to avoid that DrawRotatedText(angle = 180) and
746 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
747 // font for drawing rotated fonts unfortunately)
748 if ( (angle
== 0.0) && m_font
.Ok() )
750 DoDrawText(text
, x
, y
);
754 // NB: don't take DEFAULT_GUI_FONT because it's not TrueType and so
755 // can't have non zero orientation/escapement
756 wxFont font
= m_font
.Ok() ? m_font
: *wxNORMAL_FONT
;
757 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
759 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
761 wxLogLastError("GetObject(hfont)");
764 // GDI wants the angle in tenth of degree
765 long angle10
= (long)(angle
* 10);
766 lf
.lfEscapement
= angle10
;
767 lf
. lfOrientation
= angle10
;
769 hfont
= ::CreateFontIndirect(&lf
);
772 wxLogLastError("CreateFont");
776 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
778 DrawAnyText(text
, x
, y
);
780 (void)::SelectObject(GetHdc(), hfontOld
);
781 (void)::DeleteObject(hfont
);
784 // call the bounding box by adding all four vertices of the rectangle
785 // containing the text to it (simpler and probably not slower than
786 // determining which of them is really topmost/leftmost/...)
788 GetTextExtent(text
, &w
, &h
);
790 double rad
= DegToRad(angle
);
792 // "upper left" and "upper right"
793 CalcBoundingBox(x
, y
);
794 CalcBoundingBox(x
+ w
*cos(rad
), y
- h
*sin(rad
));
796 // "bottom left" and "bottom right"
797 x
+= (wxCoord
)(h
*sin(rad
));
798 y
+= (wxCoord
)(h
*cos(rad
));
799 CalcBoundingBox(x
, y
);
800 CalcBoundingBox(x
+ h
*sin(rad
), y
+ h
*cos(rad
));
804 // ---------------------------------------------------------------------------
806 // ---------------------------------------------------------------------------
808 void wxDC::SetPalette(const wxPalette
& palette
)
810 // Set the old object temporarily, in case the assignment deletes an object
811 // that's not yet selected out.
814 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
822 // Setting a NULL colourmap is a way of restoring
823 // the original colourmap
826 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
833 if (m_palette
.Ok() && m_palette
.GetHPALETTE())
835 HPALETTE oldPal
= ::SelectPalette(GetHdc(), (HPALETTE
) m_palette
.GetHPALETTE(), TRUE
);
837 m_oldPalette
= (WXHPALETTE
) oldPal
;
839 ::RealizePalette(GetHdc());
843 void wxDC::SetFont(const wxFont
& the_font
)
845 // Set the old object temporarily, in case the assignment deletes an object
846 // that's not yet selected out.
849 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
858 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
862 if (m_font
.Ok() && m_font
.GetResourceHandle())
864 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
865 if (f
== (HFONT
) NULL
)
867 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
870 m_oldFont
= (WXHFONT
) f
;
874 void wxDC::SetPen(const wxPen
& pen
)
876 // Set the old object temporarily, in case the assignment deletes an object
877 // that's not yet selected out.
880 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
889 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
895 if (m_pen
.GetResourceHandle())
897 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
899 m_oldPen
= (WXHPEN
) p
;
904 void wxDC::SetBrush(const wxBrush
& brush
)
906 // Set the old object temporarily, in case the assignment deletes an object
907 // that's not yet selected out.
910 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
919 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
925 if (m_brush
.GetResourceHandle())
928 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
930 m_oldBrush
= (WXHBRUSH
) b
;
935 void wxDC::SetBackground(const wxBrush
& brush
)
937 m_backgroundBrush
= brush
;
939 if (!m_backgroundBrush
.Ok())
944 bool customColours
= TRUE
;
945 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
946 // change background colours from the control-panel specified colours.
947 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
948 customColours
= FALSE
;
952 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
954 m_canvas
->SetTransparent(TRUE
);
958 // New behaviour, 10/2/99: setting the background brush of a DC
959 // doesn't affect the window background colour. However,
960 // I'm leaving in the transparency setting because it's needed by
961 // various controls (e.g. wxStaticText) to determine whether to draw
962 // transparently or not. TODO: maybe this should be a new function
963 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
965 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
966 m_canvas
->SetTransparent(FALSE
);
970 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
972 (void)SetBkColor(GetHdc(), new_color
);
976 void wxDC::SetBackgroundMode(int mode
)
978 m_backgroundMode
= mode
;
980 // SetBackgroundColour now only refers to text background
981 // and m_backgroundMode is used there
984 if (m_backgroundMode == wxTRANSPARENT)
985 ::SetBkMode(GetHdc(), TRANSPARENT);
987 ::SetBkMode(GetHdc(), OPAQUE);
991 void wxDC::SetLogicalFunction(int function
)
993 m_logicalFunction
= function
;
998 void wxDC::SetRop(WXHDC dc
)
1000 if ( !dc
|| m_logicalFunction
< 0 )
1005 switch (m_logicalFunction
)
1007 case wxXOR
: rop
= R2_XORPEN
; break;
1008 case wxINVERT
: rop
= R2_NOT
; break;
1009 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1010 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1011 case wxCLEAR
: rop
= R2_WHITE
; break;
1012 case wxSET
: rop
= R2_BLACK
; break;
1013 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1014 case wxAND
: rop
= R2_MASKPEN
; break;
1015 case wxOR
: rop
= R2_MERGEPEN
; break;
1016 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1017 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1018 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1019 case wxCOPY
: rop
= R2_COPYPEN
; break;
1020 case wxNO_OP
: rop
= R2_NOP
; break;
1021 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1022 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1024 wxFAIL_MSG( wxT("unsupported logical function") );
1028 SetROP2(GetHdc(), rop
);
1031 bool wxDC::StartDoc(const wxString
& message
)
1033 // We might be previewing, so return TRUE to let it continue.
1041 void wxDC::StartPage()
1045 void wxDC::EndPage()
1049 // ---------------------------------------------------------------------------
1051 // ---------------------------------------------------------------------------
1053 wxCoord
wxDC::GetCharHeight() const
1055 TEXTMETRIC lpTextMetric
;
1057 GetTextMetrics(GetHdc(), &lpTextMetric
);
1059 return YDEV2LOGREL(lpTextMetric
.tmHeight
);
1062 wxCoord
wxDC::GetCharWidth() const
1064 TEXTMETRIC lpTextMetric
;
1066 GetTextMetrics(GetHdc(), &lpTextMetric
);
1068 return XDEV2LOGREL(lpTextMetric
.tmAveCharWidth
);
1071 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1072 wxCoord
*descent
, wxCoord
*externalLeading
,
1073 wxFont
*theFont
) const
1075 wxFont
*fontToUse
= (wxFont
*) theFont
;
1077 fontToUse
= (wxFont
*) &m_font
;
1082 GetTextExtentPoint(GetHdc(), WXSTRINGCAST string
, wxStrlen(WXSTRINGCAST string
), &sizeRect
);
1083 GetTextMetrics(GetHdc(), &tm
);
1085 if (x
) *x
= XDEV2LOGREL(sizeRect
.cx
);
1086 if (y
) *y
= YDEV2LOGREL(sizeRect
.cy
);
1087 if (descent
) *descent
= tm
.tmDescent
;
1088 if (externalLeading
) *externalLeading
= tm
.tmExternalLeading
;
1091 void wxDC::SetMapMode(int mode
)
1093 m_mappingMode
= mode
;
1095 int pixel_width
= 0;
1096 int pixel_height
= 0;
1100 pixel_width
= GetDeviceCaps(GetHdc(), HORZRES
);
1101 pixel_height
= GetDeviceCaps(GetHdc(), VERTRES
);
1102 mm_width
= GetDeviceCaps(GetHdc(), HORZSIZE
);
1103 mm_height
= GetDeviceCaps(GetHdc(), VERTSIZE
);
1105 if ((pixel_width
== 0) || (pixel_height
== 0) || (mm_width
== 0) || (mm_height
== 0))
1110 double mm2pixelsX
= pixel_width
/mm_width
;
1111 double mm2pixelsY
= pixel_height
/mm_height
;
1117 m_logicalScaleX
= (twips2mm
* mm2pixelsX
);
1118 m_logicalScaleY
= (twips2mm
* mm2pixelsY
);
1123 m_logicalScaleX
= (pt2mm
* mm2pixelsX
);
1124 m_logicalScaleY
= (pt2mm
* mm2pixelsY
);
1129 m_logicalScaleX
= mm2pixelsX
;
1130 m_logicalScaleY
= mm2pixelsY
;
1135 m_logicalScaleX
= (mm2pixelsX
/10.0);
1136 m_logicalScaleY
= (mm2pixelsY
/10.0);
1142 m_logicalScaleX
= 1.0;
1143 m_logicalScaleY
= 1.0;
1148 if (::GetMapMode(GetHdc()) != MM_ANISOTROPIC
)
1149 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1151 SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1152 m_windowExtX
= (int)MS_XDEV2LOGREL(VIEWPORT_EXTENT
);
1153 m_windowExtY
= (int)MS_YDEV2LOGREL(VIEWPORT_EXTENT
);
1154 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
1155 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1156 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1159 void wxDC::SetUserScale(double x
, double y
)
1164 SetMapMode(m_mappingMode
);
1167 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1169 m_signX
= xLeftRight
? 1 : -1;
1170 m_signY
= yBottomUp
? -1 : 1;
1172 SetMapMode(m_mappingMode
);
1175 void wxDC::SetSystemScale(double x
, double y
)
1180 SetMapMode(m_mappingMode
);
1183 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1185 m_logicalOriginX
= x
;
1186 m_logicalOriginY
= y
;
1188 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1191 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1193 m_deviceOriginX
= x
;
1194 m_deviceOriginY
= y
;
1196 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1199 // ---------------------------------------------------------------------------
1200 // coordinates transformations
1201 // ---------------------------------------------------------------------------
1203 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1205 return (wxCoord
) (((x
) - m_deviceOriginX
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
) - m_logicalOriginX
);
1208 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1210 return (wxCoord
) ((x
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
));
1213 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1215 return (wxCoord
) (((y
) - m_deviceOriginY
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
) - m_logicalOriginY
);
1218 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1220 return (wxCoord
) ((y
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
));
1223 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1225 return (wxCoord
) ((x
- m_logicalOriginX
)*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
+ m_deviceOriginX
);
1228 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1230 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
);
1233 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1235 return (wxCoord
) ((y
- m_logicalOriginY
)*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
+ m_deviceOriginY
);
1238 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1240 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
);
1243 // ---------------------------------------------------------------------------
1245 // ---------------------------------------------------------------------------
1247 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1248 wxCoord width
, wxCoord height
,
1249 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1250 int rop
, bool useMask
)
1252 wxMask
*mask
= NULL
;
1255 const wxBitmap
& bmp
= source
->m_selectedBitmap
;
1256 mask
= bmp
.GetMask();
1258 if ( !(bmp
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1260 // don't give assert here because this would break existing
1261 // programs - just silently ignore useMask parameter
1266 COLORREF old_textground
= ::GetTextColor(GetHdc());
1267 COLORREF old_background
= ::GetBkColor(GetHdc());
1268 if (m_textForegroundColour
.Ok())
1270 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1272 if (m_textBackgroundColour
.Ok())
1274 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1277 DWORD dwRop
= SRCCOPY
;
1280 case wxXOR
: dwRop
= SRCINVERT
; break;
1281 case wxINVERT
: dwRop
= DSTINVERT
; break;
1282 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1283 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1284 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1285 case wxSET
: dwRop
= WHITENESS
; break;
1286 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1287 case wxAND
: dwRop
= SRCAND
; break;
1288 case wxOR
: dwRop
= SRCPAINT
; break;
1289 case wxEQUIV
: dwRop
= 0x00990066; break;
1290 case wxNAND
: dwRop
= 0x007700E6; break;
1291 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1292 case wxCOPY
: dwRop
= SRCCOPY
; break;
1293 case wxNO_OP
: dwRop
= 0x00AA0029; break;
1294 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1295 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1297 wxFAIL_MSG( wxT("unsupported logical function") );
1307 HBITMAP hbmpMask
= wxInvertMask((HBITMAP
)mask
->GetMaskBitmap());
1309 // we want the part of the image corresponding to the mask to be
1310 // transparent, i.e. do PATCOPY there and apply dwRop elsewhere
1311 const wxColour
& colBg
= m_backgroundBrush
.GetColour();
1312 HBRUSH hbrBg
= (HBRUSH
)::CreateSolidBrush(wxColourToRGB(colBg
));
1313 HBRUSH hbrOld
= (HBRUSH
)::SelectObject(GetHdc(), hbrBg
);
1315 success
= ::MaskBlt(GetHdc(), xdest
, ydest
, width
, height
,
1316 GetHdcOf(*source
), xsrc
, ysrc
,
1318 MAKEROP4(PATCOPY
, dwRop
)) != 0;
1320 (void)::SelectObject(GetHdc(), hbrOld
);
1321 ::DeleteObject(hbrOld
);
1322 ::DeleteObject(hbmpMask
);
1327 // Blit bitmap with mask
1329 // create a temp buffer bitmap and DCs to access it and the mask
1330 HDC dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1331 HDC dc_buffer
= ::CreateCompatibleDC(GetHdc());
1332 HBITMAP buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1333 ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1334 ::SelectObject(dc_buffer
, buffer_bmap
);
1336 // copy dest to buffer
1337 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1338 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1340 wxLogLastError("BitBlt");
1343 // copy src to buffer using selected raster op
1344 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1345 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1347 wxLogLastError("BitBlt");
1350 // set masked area in buffer to BLACK (pixel value 0)
1351 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1352 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1353 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1354 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1356 wxLogLastError("BitBlt");
1359 // set unmasked area in dest to BLACK
1360 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1361 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1362 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1363 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1365 wxLogLastError("BitBlt");
1367 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1368 ::SetTextColor(GetHdc(), prevCol
);
1370 // OR buffer to dest
1371 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1372 (int)width
, (int)height
,
1373 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1376 wxLogLastError("BitBlt");
1379 // tidy up temporary DCs and bitmap
1380 ::SelectObject(dc_mask
, 0);
1381 ::DeleteDC(dc_mask
);
1382 ::SelectObject(dc_buffer
, 0);
1383 ::DeleteDC(dc_buffer
);
1384 ::DeleteObject(buffer_bmap
);
1387 else // no mask, just BitBlt() it
1389 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1390 (int)width
, (int)height
,
1391 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) != 0;
1394 wxLogLastError("BitBlt");
1397 ::SetTextColor(GetHdc(), old_textground
);
1398 ::SetBkColor(GetHdc(), old_background
);
1403 void wxDC::DoGetSize(int *w
, int *h
) const
1405 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
1406 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
1409 void wxDC::DoGetSizeMM(int *w
, int *h
) const
1411 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZSIZE
);
1412 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1415 wxSize
wxDC::GetPPI() const
1417 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
1418 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
1420 return wxSize(x
, y
);
1423 // For use by wxWindows only, unless custom units are required.
1424 void wxDC::SetLogicalScale(double x
, double y
)
1426 m_logicalScaleX
= x
;
1427 m_logicalScaleY
= y
;
1430 #if WXWIN_COMPATIBILITY
1431 void wxDC::DoGetTextExtent(const wxString
& string
, float *x
, float *y
,
1432 float *descent
, float *externalLeading
,
1433 wxFont
*theFont
, bool use16bit
) const
1435 wxCoord x1
, y1
, descent1
, externalLeading1
;
1436 GetTextExtent(string
, & x1
, & y1
, & descent1
, & externalLeading1
, theFont
, use16bit
);
1439 *descent
= descent1
;
1440 if (externalLeading
)
1441 *externalLeading
= externalLeading1
;
1445 // ---------------------------------------------------------------------------
1446 // spline drawing code
1447 // ---------------------------------------------------------------------------
1451 class wxSpline
: public wxObject
1457 wxSpline(wxList
*list
);
1458 void DeletePoints();
1460 // Doesn't delete points
1464 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
);
1466 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
1467 double a3
, double b3
, double a4
, double b4
);
1468 void wx_clear_stack();
1469 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
1470 double *y3
, double *x4
, double *y4
);
1471 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
1472 double x4
, double y4
);
1473 static bool wx_spline_add_point(double x
, double y
);
1474 static void wx_spline_draw_point_array(wxDC
*dc
);
1475 wxSpline
*wx_make_spline(int x1
, int y1
, int x2
, int y2
, int x3
, int y3
);
1477 void wxDC::DoDrawSpline(wxList
*list
)
1479 wxSpline
spline(list
);
1481 wx_draw_open_spline(this, &spline
);
1484 wxList wx_spline_point_list
;
1486 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
)
1489 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
1490 double x1
, y1
, x2
, y2
;
1492 wxNode
*node
= spline
->points
->First();
1493 p
= (wxPoint
*)node
->Data();
1498 node
= node
->Next();
1499 p
= (wxPoint
*)node
->Data();
1503 cx1
= (double)((x1
+ x2
) / 2);
1504 cy1
= (double)((y1
+ y2
) / 2);
1505 cx2
= (double)((cx1
+ x2
) / 2);
1506 cy2
= (double)((cy1
+ y2
) / 2);
1508 wx_spline_add_point(x1
, y1
);
1510 while ((node
= node
->Next()) != NULL
)
1512 p
= (wxPoint
*)node
->Data();
1517 cx4
= (double)(x1
+ x2
) / 2;
1518 cy4
= (double)(y1
+ y2
) / 2;
1519 cx3
= (double)(x1
+ cx4
) / 2;
1520 cy3
= (double)(y1
+ cy4
) / 2;
1522 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
1526 cx2
= (double)(cx1
+ x2
) / 2;
1527 cy2
= (double)(cy1
+ y2
) / 2;
1530 wx_spline_add_point((double)wx_round(cx1
), (double)wx_round(cy1
));
1531 wx_spline_add_point(x2
, y2
);
1533 wx_spline_draw_point_array(dc
);
1537 /********************* CURVES FOR SPLINES *****************************
1539 The following spline drawing routine is from
1541 "An Algorithm for High-Speed Curve Generation"
1542 by George Merrill Chaikin,
1543 Computer Graphics and Image Processing, 3, Academic Press,
1548 "On Chaikin's Algorithm" by R. F. Riesenfeld,
1549 Computer Graphics and Image Processing, 4, Academic Press,
1552 ***********************************************************************/
1554 #define half(z1, z2) ((z1+z2)/2.0)
1557 /* iterative version */
1559 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
1562 register double xmid
, ymid
;
1563 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1566 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
1568 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
1569 xmid
= (double)half(x2
, x3
);
1570 ymid
= (double)half(y2
, y3
);
1571 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
1572 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
1573 wx_spline_add_point((double)wx_round(x1
), (double)wx_round(y1
));
1574 wx_spline_add_point((double)wx_round(xmid
), (double)wx_round(ymid
));
1576 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
1577 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
1578 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
1579 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
1585 /* utilities used by spline drawing routines */
1588 typedef struct wx_spline_stack_struct
{
1589 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1593 #define SPLINE_STACK_DEPTH 20
1594 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
1595 static Stack
*wx_stack_top
;
1596 static int wx_stack_count
;
1598 void wx_clear_stack()
1600 wx_stack_top
= wx_spline_stack
;
1604 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
1606 wx_stack_top
->x1
= x1
;
1607 wx_stack_top
->y1
= y1
;
1608 wx_stack_top
->x2
= x2
;
1609 wx_stack_top
->y2
= y2
;
1610 wx_stack_top
->x3
= x3
;
1611 wx_stack_top
->y3
= y3
;
1612 wx_stack_top
->x4
= x4
;
1613 wx_stack_top
->y4
= y4
;
1618 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
1619 double *x3
, double *y3
, double *x4
, double *y4
)
1621 if (wx_stack_count
== 0)
1625 *x1
= wx_stack_top
->x1
;
1626 *y1
= wx_stack_top
->y1
;
1627 *x2
= wx_stack_top
->x2
;
1628 *y2
= wx_stack_top
->y2
;
1629 *x3
= wx_stack_top
->x3
;
1630 *y3
= wx_stack_top
->y3
;
1631 *x4
= wx_stack_top
->x4
;
1632 *y4
= wx_stack_top
->y4
;
1636 static bool wx_spline_add_point(double x
, double y
)
1638 wxPoint
*point
= new wxPoint
;
1641 wx_spline_point_list
.Append((wxObject
*)point
);
1645 static void wx_spline_draw_point_array(wxDC
*dc
)
1647 dc
->DrawLines(&wx_spline_point_list
, 0, 0);
1648 wxNode
*node
= wx_spline_point_list
.First();
1651 wxPoint
*point
= (wxPoint
*)node
->Data();
1654 node
= wx_spline_point_list
.First();
1658 wxSpline::wxSpline(wxList
*list
)
1663 wxSpline::~wxSpline()
1667 void wxSpline::DeletePoints()
1669 for(wxNode
*node
= points
->First(); node
; node
= points
->First())
1671 wxPoint
*point
= (wxPoint
*)node
->Data();
1679 #endif // wxUSE_SPLINES