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
;
995 SetRop((WXHDC
) m_hDC
);
998 void wxDC::SetRop(WXHDC dc
)
1000 if (!dc
|| m_logicalFunction
< 0)
1005 switch (m_logicalFunction
)
1007 case wxXOR
: c_rop
= R2_XORPEN
; break;
1008 case wxINVERT
: c_rop
= R2_NOT
; break;
1009 case wxOR_REVERSE
: c_rop
= R2_MERGEPENNOT
; break;
1010 case wxAND_REVERSE
: c_rop
= R2_MASKPENNOT
; break;
1011 case wxCLEAR
: c_rop
= R2_WHITE
; break;
1012 case wxSET
: c_rop
= R2_BLACK
; break;
1013 case wxOR_INVERT
: c_rop
= R2_MERGENOTPEN
; break;
1014 case wxAND
: c_rop
= R2_MASKPEN
; break;
1015 case wxOR
: c_rop
= R2_MERGEPEN
; break;
1016 case wxEQUIV
: c_rop
= R2_NOTXORPEN
; break;
1017 case wxNAND
: c_rop
= R2_NOTMASKPEN
; break;
1018 case wxAND_INVERT
: c_rop
= R2_MASKNOTPEN
; break;
1019 case wxCOPY
: c_rop
= R2_COPYPEN
; break;
1020 case wxNO_OP
: c_rop
= R2_NOP
; break;
1021 case wxSRC_INVERT
: c_rop
= R2_NOTCOPYPEN
; break;
1022 case wxNOR
: c_rop
= R2_NOTMERGEPEN
; break;
1025 wxFAIL_MSG( wxT("unsupported logical function") );
1029 SetROP2((HDC
) dc
, c_rop
);
1032 bool wxDC::StartDoc(const wxString
& message
)
1034 // We might be previewing, so return TRUE to let it continue.
1042 void wxDC::StartPage()
1046 void wxDC::EndPage()
1050 // ---------------------------------------------------------------------------
1052 // ---------------------------------------------------------------------------
1054 wxCoord
wxDC::GetCharHeight() const
1056 TEXTMETRIC lpTextMetric
;
1058 GetTextMetrics(GetHdc(), &lpTextMetric
);
1060 return YDEV2LOGREL(lpTextMetric
.tmHeight
);
1063 wxCoord
wxDC::GetCharWidth() const
1065 TEXTMETRIC lpTextMetric
;
1067 GetTextMetrics(GetHdc(), &lpTextMetric
);
1069 return XDEV2LOGREL(lpTextMetric
.tmAveCharWidth
);
1072 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1073 wxCoord
*descent
, wxCoord
*externalLeading
,
1074 wxFont
*theFont
) const
1076 wxFont
*fontToUse
= (wxFont
*) theFont
;
1078 fontToUse
= (wxFont
*) &m_font
;
1083 GetTextExtentPoint(GetHdc(), WXSTRINGCAST string
, wxStrlen(WXSTRINGCAST string
), &sizeRect
);
1084 GetTextMetrics(GetHdc(), &tm
);
1086 if (x
) *x
= XDEV2LOGREL(sizeRect
.cx
);
1087 if (y
) *y
= YDEV2LOGREL(sizeRect
.cy
);
1088 if (descent
) *descent
= tm
.tmDescent
;
1089 if (externalLeading
) *externalLeading
= tm
.tmExternalLeading
;
1092 void wxDC::SetMapMode(int mode
)
1094 m_mappingMode
= mode
;
1096 int pixel_width
= 0;
1097 int pixel_height
= 0;
1101 pixel_width
= GetDeviceCaps(GetHdc(), HORZRES
);
1102 pixel_height
= GetDeviceCaps(GetHdc(), VERTRES
);
1103 mm_width
= GetDeviceCaps(GetHdc(), HORZSIZE
);
1104 mm_height
= GetDeviceCaps(GetHdc(), VERTSIZE
);
1106 if ((pixel_width
== 0) || (pixel_height
== 0) || (mm_width
== 0) || (mm_height
== 0))
1111 double mm2pixelsX
= pixel_width
/mm_width
;
1112 double mm2pixelsY
= pixel_height
/mm_height
;
1118 m_logicalScaleX
= (twips2mm
* mm2pixelsX
);
1119 m_logicalScaleY
= (twips2mm
* mm2pixelsY
);
1124 m_logicalScaleX
= (pt2mm
* mm2pixelsX
);
1125 m_logicalScaleY
= (pt2mm
* mm2pixelsY
);
1130 m_logicalScaleX
= mm2pixelsX
;
1131 m_logicalScaleY
= mm2pixelsY
;
1136 m_logicalScaleX
= (mm2pixelsX
/10.0);
1137 m_logicalScaleY
= (mm2pixelsY
/10.0);
1143 m_logicalScaleX
= 1.0;
1144 m_logicalScaleY
= 1.0;
1149 if (::GetMapMode(GetHdc()) != MM_ANISOTROPIC
)
1150 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1152 SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1153 m_windowExtX
= (int)MS_XDEV2LOGREL(VIEWPORT_EXTENT
);
1154 m_windowExtY
= (int)MS_YDEV2LOGREL(VIEWPORT_EXTENT
);
1155 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
1156 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1157 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1160 void wxDC::SetUserScale(double x
, double y
)
1165 SetMapMode(m_mappingMode
);
1168 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1170 m_signX
= xLeftRight
? 1 : -1;
1171 m_signY
= yBottomUp
? -1 : 1;
1173 SetMapMode(m_mappingMode
);
1176 void wxDC::SetSystemScale(double x
, double y
)
1181 SetMapMode(m_mappingMode
);
1184 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1186 m_logicalOriginX
= x
;
1187 m_logicalOriginY
= y
;
1189 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1192 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1194 m_deviceOriginX
= x
;
1195 m_deviceOriginY
= y
;
1197 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1200 // ---------------------------------------------------------------------------
1201 // coordinates transformations
1202 // ---------------------------------------------------------------------------
1204 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1206 return (wxCoord
) (((x
) - m_deviceOriginX
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
) - m_logicalOriginX
);
1209 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1211 return (wxCoord
) ((x
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
));
1214 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1216 return (wxCoord
) (((y
) - m_deviceOriginY
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
) - m_logicalOriginY
);
1219 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1221 return (wxCoord
) ((y
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
));
1224 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1226 return (wxCoord
) ((x
- m_logicalOriginX
)*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
+ m_deviceOriginX
);
1229 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1231 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
);
1234 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1236 return (wxCoord
) ((y
- m_logicalOriginY
)*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
+ m_deviceOriginY
);
1239 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1241 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
);
1244 // ---------------------------------------------------------------------------
1246 // ---------------------------------------------------------------------------
1248 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1249 wxCoord width
, wxCoord height
,
1250 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1251 int rop
, bool useMask
)
1253 wxMask
*mask
= NULL
;
1256 const wxBitmap
& bmp
= source
->m_selectedBitmap
;
1257 mask
= bmp
.GetMask();
1259 if ( !(bmp
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1261 // don't give assert here because this would break existing
1262 // programs - just silently ignore useMask parameter
1267 COLORREF old_textground
= ::GetTextColor(GetHdc());
1268 COLORREF old_background
= ::GetBkColor(GetHdc());
1269 if (m_textForegroundColour
.Ok())
1271 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1273 if (m_textBackgroundColour
.Ok())
1275 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1278 DWORD dwRop
= SRCCOPY
;
1281 case wxXOR
: dwRop
= SRCINVERT
; break;
1282 case wxINVERT
: dwRop
= DSTINVERT
; break;
1283 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1284 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1285 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1286 case wxSET
: dwRop
= WHITENESS
; break;
1287 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1288 case wxAND
: dwRop
= SRCAND
; break;
1289 case wxOR
: dwRop
= SRCPAINT
; break;
1290 case wxEQUIV
: dwRop
= 0x00990066; break;
1291 case wxNAND
: dwRop
= 0x007700E6; break;
1292 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1293 case wxCOPY
: dwRop
= SRCCOPY
; break;
1294 case wxNO_OP
: dwRop
= 0x00AA0029; break;
1295 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1296 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1299 wxFAIL_MSG( wxT("unsupported logical function") );
1310 HBITMAP hbmpMask
= wxInvertMask((HBITMAP
)mask
->GetMaskBitmap());
1312 // we want the part of the image corresponding to the mask to be
1313 // transparent, i.e. do PATCOPY there and apply dwRop elsewhere
1314 const wxColour
& colBg
= m_backgroundBrush
.GetColour();
1315 HBRUSH hbrBg
= (HBRUSH
)::CreateSolidBrush(wxColourToRGB(colBg
));
1316 HBRUSH hbrOld
= (HBRUSH
)::SelectObject(GetHdc(), hbrBg
);
1318 success
= ::MaskBlt(GetHdc(), xdest
, ydest
, width
, height
,
1319 GetHdcOf(*source
), xsrc
, ysrc
,
1321 MAKEROP4(PATCOPY
, dwRop
)) != 0;
1323 (void)::SelectObject(GetHdc(), hbrOld
);
1324 ::DeleteObject(hbrOld
);
1325 ::DeleteObject(hbmpMask
);
1330 // Blit bitmap with mask
1332 // create a temp buffer bitmap and DCs to access it and the mask
1333 HDC dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1334 HDC dc_buffer
= ::CreateCompatibleDC(GetHdc());
1335 HBITMAP buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1336 ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1337 ::SelectObject(dc_buffer
, buffer_bmap
);
1339 // copy dest to buffer
1340 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1341 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1343 wxLogLastError("BitBlt");
1346 // copy src to buffer using selected raster op
1347 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1348 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1350 wxLogLastError("BitBlt");
1353 // set masked area in buffer to BLACK (pixel value 0)
1354 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1355 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1356 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1357 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1359 wxLogLastError("BitBlt");
1362 // set unmasked area in dest to BLACK
1363 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1364 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1365 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1366 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1368 wxLogLastError("BitBlt");
1370 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1371 ::SetTextColor(GetHdc(), prevCol
);
1373 // OR buffer to dest
1374 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1375 (int)width
, (int)height
,
1376 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1379 wxLogLastError("BitBlt");
1382 // tidy up temporary DCs and bitmap
1383 ::SelectObject(dc_mask
, 0);
1384 ::DeleteDC(dc_mask
);
1385 ::SelectObject(dc_buffer
, 0);
1386 ::DeleteDC(dc_buffer
);
1387 ::DeleteObject(buffer_bmap
);
1390 else // no mask, just BitBlt() it
1392 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1393 (int)width
, (int)height
,
1394 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) != 0;
1397 wxLogLastError("BitBlt");
1400 ::SetTextColor(GetHdc(), old_textground
);
1401 ::SetBkColor(GetHdc(), old_background
);
1406 void wxDC::DoGetSize(int *w
, int *h
) const
1408 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
1409 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
1412 void wxDC::DoGetSizeMM(int *w
, int *h
) const
1414 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZSIZE
);
1415 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1418 wxSize
wxDC::GetPPI() const
1420 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
1421 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
1423 return wxSize(x
, y
);
1426 // For use by wxWindows only, unless custom units are required.
1427 void wxDC::SetLogicalScale(double x
, double y
)
1429 m_logicalScaleX
= x
;
1430 m_logicalScaleY
= y
;
1433 #if WXWIN_COMPATIBILITY
1434 void wxDC::DoGetTextExtent(const wxString
& string
, float *x
, float *y
,
1435 float *descent
, float *externalLeading
,
1436 wxFont
*theFont
, bool use16bit
) const
1438 wxCoord x1
, y1
, descent1
, externalLeading1
;
1439 GetTextExtent(string
, & x1
, & y1
, & descent1
, & externalLeading1
, theFont
, use16bit
);
1442 *descent
= descent1
;
1443 if (externalLeading
)
1444 *externalLeading
= externalLeading1
;
1448 // ---------------------------------------------------------------------------
1449 // spline drawing code
1450 // ---------------------------------------------------------------------------
1454 class wxSpline
: public wxObject
1460 wxSpline(wxList
*list
);
1461 void DeletePoints();
1463 // Doesn't delete points
1467 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
);
1469 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
1470 double a3
, double b3
, double a4
, double b4
);
1471 void wx_clear_stack();
1472 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
1473 double *y3
, double *x4
, double *y4
);
1474 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
1475 double x4
, double y4
);
1476 static bool wx_spline_add_point(double x
, double y
);
1477 static void wx_spline_draw_point_array(wxDC
*dc
);
1478 wxSpline
*wx_make_spline(int x1
, int y1
, int x2
, int y2
, int x3
, int y3
);
1480 void wxDC::DoDrawSpline(wxList
*list
)
1482 wxSpline
spline(list
);
1484 wx_draw_open_spline(this, &spline
);
1487 wxList wx_spline_point_list
;
1489 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
)
1492 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
1493 double x1
, y1
, x2
, y2
;
1495 wxNode
*node
= spline
->points
->First();
1496 p
= (wxPoint
*)node
->Data();
1501 node
= node
->Next();
1502 p
= (wxPoint
*)node
->Data();
1506 cx1
= (double)((x1
+ x2
) / 2);
1507 cy1
= (double)((y1
+ y2
) / 2);
1508 cx2
= (double)((cx1
+ x2
) / 2);
1509 cy2
= (double)((cy1
+ y2
) / 2);
1511 wx_spline_add_point(x1
, y1
);
1513 while ((node
= node
->Next()) != NULL
)
1515 p
= (wxPoint
*)node
->Data();
1520 cx4
= (double)(x1
+ x2
) / 2;
1521 cy4
= (double)(y1
+ y2
) / 2;
1522 cx3
= (double)(x1
+ cx4
) / 2;
1523 cy3
= (double)(y1
+ cy4
) / 2;
1525 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
1529 cx2
= (double)(cx1
+ x2
) / 2;
1530 cy2
= (double)(cy1
+ y2
) / 2;
1533 wx_spline_add_point((double)wx_round(cx1
), (double)wx_round(cy1
));
1534 wx_spline_add_point(x2
, y2
);
1536 wx_spline_draw_point_array(dc
);
1540 /********************* CURVES FOR SPLINES *****************************
1542 The following spline drawing routine is from
1544 "An Algorithm for High-Speed Curve Generation"
1545 by George Merrill Chaikin,
1546 Computer Graphics and Image Processing, 3, Academic Press,
1551 "On Chaikin's Algorithm" by R. F. Riesenfeld,
1552 Computer Graphics and Image Processing, 4, Academic Press,
1555 ***********************************************************************/
1557 #define half(z1, z2) ((z1+z2)/2.0)
1560 /* iterative version */
1562 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
1565 register double xmid
, ymid
;
1566 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1569 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
1571 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
1572 xmid
= (double)half(x2
, x3
);
1573 ymid
= (double)half(y2
, y3
);
1574 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
1575 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
1576 wx_spline_add_point((double)wx_round(x1
), (double)wx_round(y1
));
1577 wx_spline_add_point((double)wx_round(xmid
), (double)wx_round(ymid
));
1579 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
1580 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
1581 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
1582 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
1588 /* utilities used by spline drawing routines */
1591 typedef struct wx_spline_stack_struct
{
1592 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1596 #define SPLINE_STACK_DEPTH 20
1597 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
1598 static Stack
*wx_stack_top
;
1599 static int wx_stack_count
;
1601 void wx_clear_stack()
1603 wx_stack_top
= wx_spline_stack
;
1607 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
1609 wx_stack_top
->x1
= x1
;
1610 wx_stack_top
->y1
= y1
;
1611 wx_stack_top
->x2
= x2
;
1612 wx_stack_top
->y2
= y2
;
1613 wx_stack_top
->x3
= x3
;
1614 wx_stack_top
->y3
= y3
;
1615 wx_stack_top
->x4
= x4
;
1616 wx_stack_top
->y4
= y4
;
1621 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
1622 double *x3
, double *y3
, double *x4
, double *y4
)
1624 if (wx_stack_count
== 0)
1628 *x1
= wx_stack_top
->x1
;
1629 *y1
= wx_stack_top
->y1
;
1630 *x2
= wx_stack_top
->x2
;
1631 *y2
= wx_stack_top
->y2
;
1632 *x3
= wx_stack_top
->x3
;
1633 *y3
= wx_stack_top
->y3
;
1634 *x4
= wx_stack_top
->x4
;
1635 *y4
= wx_stack_top
->y4
;
1639 static bool wx_spline_add_point(double x
, double y
)
1641 wxPoint
*point
= new wxPoint
;
1644 wx_spline_point_list
.Append((wxObject
*)point
);
1648 static void wx_spline_draw_point_array(wxDC
*dc
)
1650 dc
->DrawLines(&wx_spline_point_list
, 0, 0);
1651 wxNode
*node
= wx_spline_point_list
.First();
1654 wxPoint
*point
= (wxPoint
*)node
->Data();
1657 node
= wx_spline_point_list
.First();
1661 wxSpline::wxSpline(wxList
*list
)
1666 wxSpline::~wxSpline()
1670 void wxSpline::DeletePoints()
1672 for(wxNode
*node
= points
->First(); node
; node
= points
->First())
1674 wxPoint
*point
= (wxPoint
*)node
->Data();
1682 #endif // wxUSE_SPLINES