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();
660 HBITMAP hbmpMask
= 0;
664 wxMask
*mask
= bmp
.GetMask();
666 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
670 // don't give assert here because this would break existing
671 // programs - just silently ignore useMask parameter
679 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
680 ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
682 // this will only work if the transparent part of our bitmap is black
683 // because it is combined with the destination rectangle using OR, so
684 // it won't be really transparent otherwise - I don't know what to do
685 // about it, may be use MAKEROP4(SRCCOPY, DSTINVERT) twice? Or create a
686 // copy of the bitmap with the transparent part replaced with black
688 bool ok
= ::MaskBlt(GetHdc(), x
, y
, width
, height
,
691 MAKEROP4(SRCCOPY
, SRCPAINT
)) != 0;
697 // VZ: this is incorrect, Blit() doesn't (and can't) draw
698 // transparently, but it's still better than nothing at all
700 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API level
702 memDC
.SelectObject(bmp
);
704 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
706 memDC
.SelectObject(wxNullBitmap
);
709 else // no mask, just use BitBlt()
712 HDC memdc
= ::CreateCompatibleDC( cdc
);
713 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
715 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
717 COLORREF old_textground
= ::GetTextColor(GetHdc());
718 COLORREF old_background
= ::GetBkColor(GetHdc());
719 if (m_textForegroundColour
.Ok())
721 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
723 if (m_textBackgroundColour
.Ok())
725 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
728 ::SelectObject( memdc
, hbitmap
);
729 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
732 ::SetTextColor(GetHdc(), old_textground
);
733 ::SetBkColor(GetHdc(), old_background
);
737 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
739 DrawAnyText(text
, x
, y
);
741 // update the bounding box
742 CalcBoundingBox(x
, y
);
745 GetTextExtent(text
, &w
, &h
);
746 CalcBoundingBox(x
+ w
, y
+ h
);
749 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
751 // prepare for drawing the text
752 if ( m_textForegroundColour
.Ok() )
753 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
755 DWORD old_background
= 0;
756 if ( m_textBackgroundColour
.Ok() )
758 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
761 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
764 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
765 text
.c_str(), text
.length()) == 0 )
767 wxLogLastError("TextOut");
770 // restore the old parameters (text foreground colour may be left because
771 // it never is set to anything else, but background should remain
772 // transparent even if we just drew an opaque string)
773 if ( m_textBackgroundColour
.Ok() )
774 (void)SetBkColor(GetHdc(), old_background
);
776 SetBkMode(GetHdc(), TRANSPARENT
);
779 void wxDC::DoDrawRotatedText(const wxString
& text
,
780 wxCoord x
, wxCoord y
,
783 // we test that we have some font because otherwise we should still use the
784 // "else" part below to avoid that DrawRotatedText(angle = 180) and
785 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
786 // font for drawing rotated fonts unfortunately)
787 if ( (angle
== 0.0) && m_font
.Ok() )
789 DoDrawText(text
, x
, y
);
793 // NB: don't take DEFAULT_GUI_FONT because it's not TrueType and so
794 // can't have non zero orientation/escapement
795 wxFont font
= m_font
.Ok() ? m_font
: *wxNORMAL_FONT
;
796 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
798 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
800 wxLogLastError("GetObject(hfont)");
803 // GDI wants the angle in tenth of degree
804 long angle10
= (long)(angle
* 10);
805 lf
.lfEscapement
= angle10
;
806 lf
. lfOrientation
= angle10
;
808 hfont
= ::CreateFontIndirect(&lf
);
811 wxLogLastError("CreateFont");
815 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
817 DrawAnyText(text
, x
, y
);
819 (void)::SelectObject(GetHdc(), hfontOld
);
820 (void)::DeleteObject(hfont
);
823 // call the bounding box by adding all four vertices of the rectangle
824 // containing the text to it (simpler and probably not slower than
825 // determining which of them is really topmost/leftmost/...)
827 GetTextExtent(text
, &w
, &h
);
829 double rad
= DegToRad(angle
);
831 // "upper left" and "upper right"
832 CalcBoundingBox(x
, y
);
833 CalcBoundingBox(x
+ w
*cos(rad
), y
- h
*sin(rad
));
835 // "bottom left" and "bottom right"
836 x
+= (wxCoord
)(h
*sin(rad
));
837 y
+= (wxCoord
)(h
*cos(rad
));
838 CalcBoundingBox(x
, y
);
839 CalcBoundingBox(x
+ h
*sin(rad
), y
+ h
*cos(rad
));
843 // ---------------------------------------------------------------------------
845 // ---------------------------------------------------------------------------
847 void wxDC::SetPalette(const wxPalette
& palette
)
849 // Set the old object temporarily, in case the assignment deletes an object
850 // that's not yet selected out.
853 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
861 // Setting a NULL colourmap is a way of restoring
862 // the original colourmap
865 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
872 if (m_palette
.Ok() && m_palette
.GetHPALETTE())
874 HPALETTE oldPal
= ::SelectPalette(GetHdc(), (HPALETTE
) m_palette
.GetHPALETTE(), TRUE
);
876 m_oldPalette
= (WXHPALETTE
) oldPal
;
878 ::RealizePalette(GetHdc());
882 void wxDC::SetFont(const wxFont
& the_font
)
884 // Set the old object temporarily, in case the assignment deletes an object
885 // that's not yet selected out.
888 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
897 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
901 if (m_font
.Ok() && m_font
.GetResourceHandle())
903 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
904 if (f
== (HFONT
) NULL
)
906 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
909 m_oldFont
= (WXHFONT
) f
;
913 void wxDC::SetPen(const wxPen
& pen
)
915 // Set the old object temporarily, in case the assignment deletes an object
916 // that's not yet selected out.
919 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
928 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
934 if (m_pen
.GetResourceHandle())
936 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
938 m_oldPen
= (WXHPEN
) p
;
943 void wxDC::SetBrush(const wxBrush
& brush
)
945 // Set the old object temporarily, in case the assignment deletes an object
946 // that's not yet selected out.
949 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
958 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
964 if (m_brush
.GetResourceHandle())
967 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
969 m_oldBrush
= (WXHBRUSH
) b
;
974 void wxDC::SetBackground(const wxBrush
& brush
)
976 m_backgroundBrush
= brush
;
978 if (!m_backgroundBrush
.Ok())
983 bool customColours
= TRUE
;
984 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
985 // change background colours from the control-panel specified colours.
986 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
987 customColours
= FALSE
;
991 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
993 m_canvas
->SetTransparent(TRUE
);
997 // New behaviour, 10/2/99: setting the background brush of a DC
998 // doesn't affect the window background colour. However,
999 // I'm leaving in the transparency setting because it's needed by
1000 // various controls (e.g. wxStaticText) to determine whether to draw
1001 // transparently or not. TODO: maybe this should be a new function
1002 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1004 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1005 m_canvas
->SetTransparent(FALSE
);
1009 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1011 (void)SetBkColor(GetHdc(), new_color
);
1015 void wxDC::SetBackgroundMode(int mode
)
1017 m_backgroundMode
= mode
;
1019 // SetBackgroundColour now only refers to text background
1020 // and m_backgroundMode is used there
1023 if (m_backgroundMode == wxTRANSPARENT)
1024 ::SetBkMode(GetHdc(), TRANSPARENT);
1026 ::SetBkMode(GetHdc(), OPAQUE);
1030 void wxDC::SetLogicalFunction(int function
)
1032 m_logicalFunction
= function
;
1037 void wxDC::SetRop(WXHDC dc
)
1039 if ( !dc
|| m_logicalFunction
< 0 )
1044 switch (m_logicalFunction
)
1046 case wxXOR
: rop
= R2_XORPEN
; break;
1047 case wxINVERT
: rop
= R2_NOT
; break;
1048 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1049 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1050 case wxCLEAR
: rop
= R2_WHITE
; break;
1051 case wxSET
: rop
= R2_BLACK
; break;
1052 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1053 case wxAND
: rop
= R2_MASKPEN
; break;
1054 case wxOR
: rop
= R2_MERGEPEN
; break;
1055 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1056 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1057 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1058 case wxCOPY
: rop
= R2_COPYPEN
; break;
1059 case wxNO_OP
: rop
= R2_NOP
; break;
1060 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1061 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1063 wxFAIL_MSG( wxT("unsupported logical function") );
1067 SetROP2(GetHdc(), rop
);
1070 bool wxDC::StartDoc(const wxString
& message
)
1072 // We might be previewing, so return TRUE to let it continue.
1080 void wxDC::StartPage()
1084 void wxDC::EndPage()
1088 // ---------------------------------------------------------------------------
1090 // ---------------------------------------------------------------------------
1092 wxCoord
wxDC::GetCharHeight() const
1094 TEXTMETRIC lpTextMetric
;
1096 GetTextMetrics(GetHdc(), &lpTextMetric
);
1098 return YDEV2LOGREL(lpTextMetric
.tmHeight
);
1101 wxCoord
wxDC::GetCharWidth() const
1103 TEXTMETRIC lpTextMetric
;
1105 GetTextMetrics(GetHdc(), &lpTextMetric
);
1107 return XDEV2LOGREL(lpTextMetric
.tmAveCharWidth
);
1110 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1111 wxCoord
*descent
, wxCoord
*externalLeading
,
1112 wxFont
*theFont
) const
1114 wxFont
*fontToUse
= (wxFont
*) theFont
;
1116 fontToUse
= (wxFont
*) &m_font
;
1121 GetTextExtentPoint(GetHdc(), WXSTRINGCAST string
, wxStrlen(WXSTRINGCAST string
), &sizeRect
);
1122 GetTextMetrics(GetHdc(), &tm
);
1124 if (x
) *x
= XDEV2LOGREL(sizeRect
.cx
);
1125 if (y
) *y
= YDEV2LOGREL(sizeRect
.cy
);
1126 if (descent
) *descent
= tm
.tmDescent
;
1127 if (externalLeading
) *externalLeading
= tm
.tmExternalLeading
;
1130 void wxDC::SetMapMode(int mode
)
1132 m_mappingMode
= mode
;
1134 int pixel_width
= 0;
1135 int pixel_height
= 0;
1139 pixel_width
= GetDeviceCaps(GetHdc(), HORZRES
);
1140 pixel_height
= GetDeviceCaps(GetHdc(), VERTRES
);
1141 mm_width
= GetDeviceCaps(GetHdc(), HORZSIZE
);
1142 mm_height
= GetDeviceCaps(GetHdc(), VERTSIZE
);
1144 if ((pixel_width
== 0) || (pixel_height
== 0) || (mm_width
== 0) || (mm_height
== 0))
1149 double mm2pixelsX
= pixel_width
/mm_width
;
1150 double mm2pixelsY
= pixel_height
/mm_height
;
1156 m_logicalScaleX
= (twips2mm
* mm2pixelsX
);
1157 m_logicalScaleY
= (twips2mm
* mm2pixelsY
);
1162 m_logicalScaleX
= (pt2mm
* mm2pixelsX
);
1163 m_logicalScaleY
= (pt2mm
* mm2pixelsY
);
1168 m_logicalScaleX
= mm2pixelsX
;
1169 m_logicalScaleY
= mm2pixelsY
;
1174 m_logicalScaleX
= (mm2pixelsX
/10.0);
1175 m_logicalScaleY
= (mm2pixelsY
/10.0);
1181 m_logicalScaleX
= 1.0;
1182 m_logicalScaleY
= 1.0;
1187 if (::GetMapMode(GetHdc()) != MM_ANISOTROPIC
)
1188 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1190 SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1191 m_windowExtX
= (int)MS_XDEV2LOGREL(VIEWPORT_EXTENT
);
1192 m_windowExtY
= (int)MS_YDEV2LOGREL(VIEWPORT_EXTENT
);
1193 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
1194 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1195 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1198 void wxDC::SetUserScale(double x
, double y
)
1203 SetMapMode(m_mappingMode
);
1206 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1208 m_signX
= xLeftRight
? 1 : -1;
1209 m_signY
= yBottomUp
? -1 : 1;
1211 SetMapMode(m_mappingMode
);
1214 void wxDC::SetSystemScale(double x
, double y
)
1219 SetMapMode(m_mappingMode
);
1222 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1224 m_logicalOriginX
= x
;
1225 m_logicalOriginY
= y
;
1227 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1230 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1232 m_deviceOriginX
= x
;
1233 m_deviceOriginY
= y
;
1235 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1238 // ---------------------------------------------------------------------------
1239 // coordinates transformations
1240 // ---------------------------------------------------------------------------
1242 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1244 return (wxCoord
) (((x
) - m_deviceOriginX
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
) - m_logicalOriginX
);
1247 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1249 return (wxCoord
) ((x
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
));
1252 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1254 return (wxCoord
) (((y
) - m_deviceOriginY
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
) - m_logicalOriginY
);
1257 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1259 return (wxCoord
) ((y
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
));
1262 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1264 return (wxCoord
) ((x
- m_logicalOriginX
)*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
+ m_deviceOriginX
);
1267 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1269 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
);
1272 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1274 return (wxCoord
) ((y
- m_logicalOriginY
)*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
+ m_deviceOriginY
);
1277 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1279 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
);
1282 // ---------------------------------------------------------------------------
1284 // ---------------------------------------------------------------------------
1286 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1287 wxCoord width
, wxCoord height
,
1288 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1289 int rop
, bool useMask
)
1291 wxMask
*mask
= NULL
;
1294 const wxBitmap
& bmp
= source
->m_selectedBitmap
;
1295 mask
= bmp
.GetMask();
1297 if ( !(bmp
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1299 // don't give assert here because this would break existing
1300 // programs - just silently ignore useMask parameter
1305 COLORREF old_textground
= ::GetTextColor(GetHdc());
1306 COLORREF old_background
= ::GetBkColor(GetHdc());
1307 if (m_textForegroundColour
.Ok())
1309 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1311 if (m_textBackgroundColour
.Ok())
1313 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1316 DWORD dwRop
= SRCCOPY
;
1319 case wxXOR
: dwRop
= SRCINVERT
; break;
1320 case wxINVERT
: dwRop
= DSTINVERT
; break;
1321 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1322 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1323 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1324 case wxSET
: dwRop
= WHITENESS
; break;
1325 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1326 case wxAND
: dwRop
= SRCAND
; break;
1327 case wxOR
: dwRop
= SRCPAINT
; break;
1328 case wxEQUIV
: dwRop
= 0x00990066; break;
1329 case wxNAND
: dwRop
= 0x007700E6; break;
1330 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1331 case wxCOPY
: dwRop
= SRCCOPY
; break;
1332 case wxNO_OP
: dwRop
= 0x00AA0029; break;
1333 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1334 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1336 wxFAIL_MSG( wxT("unsupported logical function") );
1346 // prepare the mask bitmap
1347 HBITMAP hbmpMask
= wxInvertMask((HBITMAP
)mask
->GetMaskBitmap());
1349 // select the correct brush: the current one by default, background one
1354 hbrNew
= (HBRUSH
)m_brush
.GetResourceHandle();
1356 else if ( m_backgroundBrush
.Ok() )
1358 hbrNew
= (HBRUSH
)m_backgroundBrush
.GetResourceHandle();
1365 HGDIOBJ hbrOld
= hbrNew
? ::SelectObject(GetHdc(), hbrNew
) : 0;
1367 // we want the part of the image corresponding to the mask to be
1368 // transparent, i.e. do PATCOPY there and apply dwRop elsewhere
1370 success
= ::MaskBlt(GetHdc(), xdest
, ydest
, width
, height
,
1371 GetHdcOf(*source
), xsrc
, ysrc
,
1373 MAKEROP4(PATCOPY
, dwRop
)) != 0;
1377 (void)::SelectObject(GetHdc(), hbrOld
);
1380 ::DeleteObject(hbmpMask
);
1385 // Blit bitmap with mask
1387 // create a temp buffer bitmap and DCs to access it and the mask
1388 HDC dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1389 HDC dc_buffer
= ::CreateCompatibleDC(GetHdc());
1390 HBITMAP buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1391 ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1392 ::SelectObject(dc_buffer
, buffer_bmap
);
1394 // copy dest to buffer
1395 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1396 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1398 wxLogLastError("BitBlt");
1401 // copy src to buffer using selected raster op
1402 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1403 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1405 wxLogLastError("BitBlt");
1408 // set masked area in buffer to BLACK (pixel value 0)
1409 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1410 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1411 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1412 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1414 wxLogLastError("BitBlt");
1417 // set unmasked area in dest to BLACK
1418 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1419 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1420 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1421 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1423 wxLogLastError("BitBlt");
1425 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1426 ::SetTextColor(GetHdc(), prevCol
);
1428 // OR buffer to dest
1429 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1430 (int)width
, (int)height
,
1431 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1434 wxLogLastError("BitBlt");
1437 // tidy up temporary DCs and bitmap
1438 ::SelectObject(dc_mask
, 0);
1439 ::DeleteDC(dc_mask
);
1440 ::SelectObject(dc_buffer
, 0);
1441 ::DeleteDC(dc_buffer
);
1442 ::DeleteObject(buffer_bmap
);
1445 else // no mask, just BitBlt() it
1447 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1448 (int)width
, (int)height
,
1449 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) != 0;
1452 wxLogLastError("BitBlt");
1455 ::SetTextColor(GetHdc(), old_textground
);
1456 ::SetBkColor(GetHdc(), old_background
);
1461 void wxDC::DoGetSize(int *w
, int *h
) const
1463 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
1464 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
1467 void wxDC::DoGetSizeMM(int *w
, int *h
) const
1469 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZSIZE
);
1470 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1473 wxSize
wxDC::GetPPI() const
1475 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
1476 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
1478 return wxSize(x
, y
);
1481 // For use by wxWindows only, unless custom units are required.
1482 void wxDC::SetLogicalScale(double x
, double y
)
1484 m_logicalScaleX
= x
;
1485 m_logicalScaleY
= y
;
1488 #if WXWIN_COMPATIBILITY
1489 void wxDC::DoGetTextExtent(const wxString
& string
, float *x
, float *y
,
1490 float *descent
, float *externalLeading
,
1491 wxFont
*theFont
, bool use16bit
) const
1493 wxCoord x1
, y1
, descent1
, externalLeading1
;
1494 GetTextExtent(string
, & x1
, & y1
, & descent1
, & externalLeading1
, theFont
, use16bit
);
1497 *descent
= descent1
;
1498 if (externalLeading
)
1499 *externalLeading
= externalLeading1
;
1503 // ---------------------------------------------------------------------------
1504 // spline drawing code
1505 // ---------------------------------------------------------------------------
1509 class wxSpline
: public wxObject
1515 wxSpline(wxList
*list
);
1516 void DeletePoints();
1518 // Doesn't delete points
1522 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
);
1524 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
1525 double a3
, double b3
, double a4
, double b4
);
1526 void wx_clear_stack();
1527 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
1528 double *y3
, double *x4
, double *y4
);
1529 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
1530 double x4
, double y4
);
1531 static bool wx_spline_add_point(double x
, double y
);
1532 static void wx_spline_draw_point_array(wxDC
*dc
);
1533 wxSpline
*wx_make_spline(int x1
, int y1
, int x2
, int y2
, int x3
, int y3
);
1535 void wxDC::DoDrawSpline(wxList
*list
)
1537 wxSpline
spline(list
);
1539 wx_draw_open_spline(this, &spline
);
1542 wxList wx_spline_point_list
;
1544 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
)
1547 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
1548 double x1
, y1
, x2
, y2
;
1550 wxNode
*node
= spline
->points
->First();
1551 p
= (wxPoint
*)node
->Data();
1556 node
= node
->Next();
1557 p
= (wxPoint
*)node
->Data();
1561 cx1
= (double)((x1
+ x2
) / 2);
1562 cy1
= (double)((y1
+ y2
) / 2);
1563 cx2
= (double)((cx1
+ x2
) / 2);
1564 cy2
= (double)((cy1
+ y2
) / 2);
1566 wx_spline_add_point(x1
, y1
);
1568 while ((node
= node
->Next()) != NULL
)
1570 p
= (wxPoint
*)node
->Data();
1575 cx4
= (double)(x1
+ x2
) / 2;
1576 cy4
= (double)(y1
+ y2
) / 2;
1577 cx3
= (double)(x1
+ cx4
) / 2;
1578 cy3
= (double)(y1
+ cy4
) / 2;
1580 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
1584 cx2
= (double)(cx1
+ x2
) / 2;
1585 cy2
= (double)(cy1
+ y2
) / 2;
1588 wx_spline_add_point((double)wx_round(cx1
), (double)wx_round(cy1
));
1589 wx_spline_add_point(x2
, y2
);
1591 wx_spline_draw_point_array(dc
);
1595 /********************* CURVES FOR SPLINES *****************************
1597 The following spline drawing routine is from
1599 "An Algorithm for High-Speed Curve Generation"
1600 by George Merrill Chaikin,
1601 Computer Graphics and Image Processing, 3, Academic Press,
1606 "On Chaikin's Algorithm" by R. F. Riesenfeld,
1607 Computer Graphics and Image Processing, 4, Academic Press,
1610 ***********************************************************************/
1612 #define half(z1, z2) ((z1+z2)/2.0)
1615 /* iterative version */
1617 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
1620 register double xmid
, ymid
;
1621 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1624 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
1626 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
1627 xmid
= (double)half(x2
, x3
);
1628 ymid
= (double)half(y2
, y3
);
1629 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
1630 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
1631 wx_spline_add_point((double)wx_round(x1
), (double)wx_round(y1
));
1632 wx_spline_add_point((double)wx_round(xmid
), (double)wx_round(ymid
));
1634 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
1635 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
1636 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
1637 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
1643 /* utilities used by spline drawing routines */
1646 typedef struct wx_spline_stack_struct
{
1647 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1651 #define SPLINE_STACK_DEPTH 20
1652 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
1653 static Stack
*wx_stack_top
;
1654 static int wx_stack_count
;
1656 void wx_clear_stack()
1658 wx_stack_top
= wx_spline_stack
;
1662 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
1664 wx_stack_top
->x1
= x1
;
1665 wx_stack_top
->y1
= y1
;
1666 wx_stack_top
->x2
= x2
;
1667 wx_stack_top
->y2
= y2
;
1668 wx_stack_top
->x3
= x3
;
1669 wx_stack_top
->y3
= y3
;
1670 wx_stack_top
->x4
= x4
;
1671 wx_stack_top
->y4
= y4
;
1676 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
1677 double *x3
, double *y3
, double *x4
, double *y4
)
1679 if (wx_stack_count
== 0)
1683 *x1
= wx_stack_top
->x1
;
1684 *y1
= wx_stack_top
->y1
;
1685 *x2
= wx_stack_top
->x2
;
1686 *y2
= wx_stack_top
->y2
;
1687 *x3
= wx_stack_top
->x3
;
1688 *y3
= wx_stack_top
->y3
;
1689 *x4
= wx_stack_top
->x4
;
1690 *y4
= wx_stack_top
->y4
;
1694 static bool wx_spline_add_point(double x
, double y
)
1696 wxPoint
*point
= new wxPoint
;
1699 wx_spline_point_list
.Append((wxObject
*)point
);
1703 static void wx_spline_draw_point_array(wxDC
*dc
)
1705 dc
->DrawLines(&wx_spline_point_list
, 0, 0);
1706 wxNode
*node
= wx_spline_point_list
.First();
1709 wxPoint
*point
= (wxPoint
*)node
->Data();
1712 node
= wx_spline_point_list
.First();
1716 wxSpline::wxSpline(wxList
*list
)
1721 wxSpline::~wxSpline()
1725 void wxSpline::DeletePoints()
1727 for(wxNode
*node
= points
->First(); node
; node
= points
->First())
1729 wxPoint
*point
= (wxPoint
*)node
->Data();
1737 #endif // wxUSE_SPLINES