1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
21 #pragma implementation "dc.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
32 #include "wx/window.h"
35 #include "wx/dialog.h"
37 #include "wx/bitmap.h"
38 #include "wx/dcmemory.h"
43 #include "wx/dcprint.h"
48 #include "wx/msw/private.h" // needs to be before #include <commdlg.h>
50 #if wxUSE_COMMON_DIALOGS
58 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxObject
)
60 // ---------------------------------------------------------------------------
62 // ---------------------------------------------------------------------------
64 static const int VIEWPORT_EXTENT
= 1000;
66 static const int MM_POINTS
= 9;
67 static const int MM_METRIC
= 10;
69 // usually this is defined in math.h
71 static const double M_PI
= 3.14159265358979323846;
74 // ROPs which don't have standard names (see "Ternary Raster Operations" in the
75 // MSDN docs for how this and other numbers in wxDC::Blit() are obtained)
76 #define DSTCOPY 0x00AA0029 // a.k.a. NOP operation
78 // ---------------------------------------------------------------------------
80 // ---------------------------------------------------------------------------
82 // convert degrees to radians
83 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
85 // ===========================================================================
87 // ===========================================================================
89 // ---------------------------------------------------------------------------
91 // ---------------------------------------------------------------------------
93 // Default constructor
107 m_windowExtX
= VIEWPORT_EXTENT
;
108 m_windowExtY
= VIEWPORT_EXTENT
;
117 SelectOldObjects(m_hDC
);
119 if ( m_canvas
== NULL
)
120 ::DeleteDC(GetHdc());
122 ::ReleaseDC((HWND
)m_canvas
->GetHWND(), GetHdc());
128 // This will select current objects out of the DC,
129 // which is what you have to do before deleting the
131 void wxDC::SelectOldObjects(WXHDC dc
)
137 ::SelectObject((HDC
) dc
, (HBITMAP
) m_oldBitmap
);
138 if (m_selectedBitmap
.Ok())
140 m_selectedBitmap
.SetSelectedInto(NULL
);
146 ::SelectObject((HDC
) dc
, (HPEN
) m_oldPen
);
151 ::SelectObject((HDC
) dc
, (HBRUSH
) m_oldBrush
);
156 ::SelectObject((HDC
) dc
, (HFONT
) m_oldFont
);
161 ::SelectPalette((HDC
) dc
, (HPALETTE
) m_oldPalette
, TRUE
);
166 m_brush
= wxNullBrush
;
168 m_palette
= wxNullPalette
;
170 m_backgroundBrush
= wxNullBrush
;
171 m_selectedBitmap
= wxNullBitmap
;
174 // ---------------------------------------------------------------------------
176 // ---------------------------------------------------------------------------
178 void wxDC::DoSetClippingRegion(wxCoord cx
, wxCoord cy
, wxCoord cw
, wxCoord ch
)
183 m_clipX2
= (int)(cx
+ cw
);
184 m_clipY2
= (int)(cy
+ ch
);
186 DoClipping((WXHDC
) m_hDC
);
189 void wxDC::DoSetClippingRegionAsRegion(const wxRegion
& region
)
191 wxCHECK_RET( region
.GetHRGN(), wxT("invalid clipping region") );
193 wxRect box
= region
.GetBox();
198 m_clipX2
= box
.x
+ box
.width
;
199 m_clipY2
= box
.y
+ box
.height
;
202 SelectClipRgn(GetHdc(), (HRGN
) region
.GetHRGN());
204 ExtSelectClipRgn(GetHdc(), (HRGN
) region
.GetHRGN(), RGN_AND
);
208 void wxDC::DoClipping(WXHDC dc
)
210 if (m_clipping
&& dc
)
212 IntersectClipRect((HDC
) dc
, XLOG2DEV(m_clipX1
), YLOG2DEV(m_clipY1
),
213 XLOG2DEV(m_clipX2
), YLOG2DEV(m_clipY2
));
217 void wxDC::DestroyClippingRegion()
219 if (m_clipping
&& m_hDC
)
221 // TODO: this should restore the previous clipping region,
222 // so that OnPaint processing works correctly, and the update clipping region
223 // doesn't get destroyed after the first DestroyClippingRegion.
224 HRGN rgn
= CreateRectRgn(0, 0, 32000, 32000);
225 SelectClipRgn(GetHdc(), rgn
);
231 // ---------------------------------------------------------------------------
232 // query capabilities
233 // ---------------------------------------------------------------------------
235 bool wxDC::CanDrawBitmap() const
240 bool wxDC::CanGetTextExtent() const
242 // What sort of display is it?
243 int technology
= ::GetDeviceCaps(GetHdc(), TECHNOLOGY
);
245 return (technology
== DT_RASDISPLAY
) || (technology
== DT_RASPRINTER
);
248 int wxDC::GetDepth() const
250 return (int)::GetDeviceCaps(GetHdc(), BITSPIXEL
);
253 // ---------------------------------------------------------------------------
255 // ---------------------------------------------------------------------------
262 GetClientRect((HWND
) m_canvas
->GetHWND(), &rect
);
266 // No, I think we should simply ignore this if printing on e.g.
268 // wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
269 if (!m_selectedBitmap
.Ok())
272 rect
.left
= 0; rect
.top
= 0;
273 rect
.right
= m_selectedBitmap
.GetWidth();
274 rect
.bottom
= m_selectedBitmap
.GetHeight();
277 (void) ::SetMapMode(GetHdc(), MM_TEXT
);
279 DWORD colour
= GetBkColor(GetHdc());
280 HBRUSH brush
= CreateSolidBrush(colour
);
281 FillRect(GetHdc(), &rect
, brush
);
284 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
285 ::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
286 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
287 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
288 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
291 void wxDC::DoFloodFill(wxCoord x
, wxCoord y
, const wxColour
& col
, int style
)
293 if ( !::ExtFloodFill(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
295 style
== wxFLOOD_SURFACE
? FLOODFILLSURFACE
298 // quoting from the MSDN docs:
300 // Following are some of the reasons this function might fail:
302 // * The filling could not be completed.
303 // * The specified point has the boundary color specified by the
304 // crColor parameter (if FLOODFILLBORDER was requested).
305 // * The specified point does not have the color specified by
306 // crColor (if FLOODFILLSURFACE was requested)
307 // * The point is outside the clipping region that is, it is not
308 // visible on the device.
310 wxLogLastError("ExtFloodFill");
313 CalcBoundingBox(x
, y
);
316 bool wxDC::DoGetPixel(wxCoord x
, wxCoord y
, wxColour
*col
) const
318 // get the color of the pixel
319 COLORREF pixelcolor
= ::GetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
));
321 // get the color of the pen
322 COLORREF pencolor
= 0x00ffffff;
325 pencolor
= m_pen
.GetColour().GetPixel();
328 // return the color of the pixel
331 col
->Set(GetRValue(pixelcolor
),
332 GetGValue(pixelcolor
),
333 GetBValue(pixelcolor
));
336 // check, if color of the pixels is the same as the color of the current
337 // pen and return TRUE if it is, FALSE otherwise
338 return pixelcolor
== pencolor
;
341 void wxDC::DoCrossHair(wxCoord x
, wxCoord y
)
343 wxCoord x1
= x
-VIEWPORT_EXTENT
;
344 wxCoord y1
= y
-VIEWPORT_EXTENT
;
345 wxCoord x2
= x
+VIEWPORT_EXTENT
;
346 wxCoord y2
= y
+VIEWPORT_EXTENT
;
348 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y
), NULL
);
349 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y
));
351 (void)MoveToEx(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y1
), NULL
);
352 (void)LineTo(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y2
));
354 CalcBoundingBox(x1
, y1
);
355 CalcBoundingBox(x2
, y2
);
358 void wxDC::DoDrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
360 (void)MoveToEx(GetHdc(), XLOG2DEV(x1
), YLOG2DEV(y1
), NULL
);
361 (void)LineTo(GetHdc(), XLOG2DEV(x2
), YLOG2DEV(y2
));
363 // Normalization: Windows doesn't draw the last point of the line
364 (void)LineTo(GetHdc(), XLOG2DEV(x2
) + 1, YLOG2DEV(y2
));
366 CalcBoundingBox(x1
, y1
);
367 CalcBoundingBox(x2
, y2
);
370 void wxDC::DoDrawArc(wxCoord x1
,wxCoord y1
,wxCoord x2
,wxCoord y2
, wxCoord xc
, wxCoord yc
)
374 double radius
= (double)sqrt(dx
*dx
+dy
*dy
) ;;
375 if (x1
==x2
&& x2
==y2
)
377 DrawEllipse(xc
,yc
,(wxCoord
)(radius
*2.0),(wxCoord
)(radius
*2.0));
381 wxCoord xx1
= XLOG2DEV(x1
);
382 wxCoord yy1
= YLOG2DEV(y1
);
383 wxCoord xx2
= XLOG2DEV(x2
);
384 wxCoord yy2
= YLOG2DEV(y2
);
385 wxCoord xxc
= XLOG2DEV(xc
);
386 wxCoord yyc
= YLOG2DEV(yc
);
387 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
389 (void)MoveToEx(GetHdc(), (int) xx1
, (int) yy1
, NULL
);
390 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
391 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
392 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
393 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
394 if (m_brush
.Ok() && m_brush
.GetStyle() !=wxTRANSPARENT
)
396 // Have to add 1 to bottom-right corner of rectangle
397 // to make semi-circles look right (crooked line otherwise).
398 // Unfortunately this is not a reliable method, depends
399 // on the size of shape.
400 // TODO: figure out why this happens!
401 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1,
405 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
,
408 CalcBoundingBox((wxCoord
)(xc
-radius
), (wxCoord
)(yc
-radius
));
409 CalcBoundingBox((wxCoord
)(xc
+radius
), (wxCoord
)(yc
+radius
));
412 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
414 COLORREF color
= 0x00ffffff;
417 color
= m_pen
.GetColour().GetPixel();
420 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
422 CalcBoundingBox(x
, y
);
425 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
427 COLORREF old_textground
= ::GetTextColor(GetHdc());
428 COLORREF old_background
= ::GetBkColor(GetHdc());
429 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
432 if (m_textForegroundColour
.Ok())
433 { //just the oposite from what is expected see help on pattern brush
434 // 1 in mask becomes bk color
435 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel() );
437 if (m_textBackgroundColour
.Ok())
438 { //just the oposite from what is expected
439 // 0 in mask becomes text color
440 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
443 if (m_backgroundMode
== wxTRANSPARENT
)
444 SetBkMode(GetHdc(), TRANSPARENT
);
446 SetBkMode(GetHdc(), OPAQUE
);
449 // Do things less efficiently if we have offsets
450 if (xoffset
!= 0 || yoffset
!= 0)
452 POINT
*cpoints
= new POINT
[n
];
454 for (i
= 0; i
< n
; i
++)
456 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
457 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
459 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
461 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
462 (void)Polygon(GetHdc(), cpoints
, n
);
463 SetPolyFillMode(GetHdc(),prev
);
469 for (i
= 0; i
< n
; i
++)
470 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
472 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
473 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
474 SetPolyFillMode(GetHdc(),prev
);
477 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
479 ::SetBkMode(GetHdc(), TRANSPARENT
);
480 ::SetTextColor(GetHdc(), old_textground
);
481 ::SetBkColor(GetHdc(), old_background
);
485 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
487 // Do things less efficiently if we have offsets
488 if (xoffset
!= 0 || yoffset
!= 0)
490 POINT
*cpoints
= new POINT
[n
];
492 for (i
= 0; i
< n
; i
++)
494 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
495 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
497 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
499 (void)Polyline(GetHdc(), cpoints
, n
);
505 for (i
= 0; i
< n
; i
++)
506 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
508 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
512 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
514 COLORREF colFgOld
= 0,
517 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
519 colFgOld
= ::GetTextColor(GetHdc());
520 colBgOld
= ::GetBkColor(GetHdc());
522 if ( m_textForegroundColour
.Ok() )
524 // just the oposite from what is expected see help on pattern brush
525 // 1 in mask becomes bk color
526 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel());
529 if ( m_textBackgroundColour
.Ok() )
531 // 0 in mask becomes text color
532 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel());
535 // VZ: IMHO this does strictly nothing here
536 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
540 wxCoord x2
= x
+ width
;
541 wxCoord y2
= y
+ height
;
543 // Windows draws the filled rectangles without outline (i.e. drawn with a
544 // transparent pen) one pixel smaller in both directions and we want them
545 // to have the same size regardless of which pen is used - adjust
546 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
552 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
554 CalcBoundingBox(x
, y
);
555 CalcBoundingBox(x2
, y2
);
557 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
559 // restore the colours we changed
560 ::SetBkMode(GetHdc(), TRANSPARENT
);
561 ::SetTextColor(GetHdc(), colFgOld
);
562 ::SetBkColor(GetHdc(), colBgOld
);
566 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
568 // Now, a negative radius value is interpreted to mean
569 // 'the proportion of the smallest X or Y dimension'
573 double smallest
= 0.0;
578 radius
= (- radius
* smallest
);
581 wxCoord x2
= (x
+width
);
582 wxCoord y2
= (y
+height
);
584 // Windows draws the filled rectangles without outline (i.e. drawn with a
585 // transparent pen) one pixel smaller in both directions and we want them
586 // to have the same size regardless of which pen is used - adjust
587 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
593 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
594 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
596 CalcBoundingBox(x
, y
);
597 CalcBoundingBox(x2
, y2
);
600 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
602 wxCoord x2
= (x
+width
);
603 wxCoord y2
= (y
+height
);
605 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
607 CalcBoundingBox(x
, y
);
608 CalcBoundingBox(x2
, y2
);
611 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
612 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
617 int rx1
= XLOG2DEV(x
+w
/2);
618 int ry1
= YLOG2DEV(y
+h
/2);
625 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
626 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
627 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
628 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
630 // draw pie with NULL_PEN first and then outline otherwise a line is
631 // drawn from the start and end points to the centre
632 HPEN orig_pen
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
635 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
640 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
641 rx1
, ry1
-1, rx2
, ry2
-1);
643 ::SelectObject(GetHdc(), orig_pen
);
644 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
647 CalcBoundingBox(x
, y
);
648 CalcBoundingBox(x2
, y2
);
651 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
653 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
655 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
657 CalcBoundingBox(x
, y
);
658 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
661 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
663 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
665 int width
= bmp
.GetWidth(),
666 height
= bmp
.GetHeight();
668 HBITMAP hbmpMask
= 0;
672 wxMask
*mask
= bmp
.GetMask();
674 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
678 // don't give assert here because this would break existing
679 // programs - just silently ignore useMask parameter
687 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
688 ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
690 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
692 bool ok
= ::MaskBlt(GetHdc(), x
, y
, width
, height
,
695 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
701 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
704 memDC
.SelectObject(bmp
);
706 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
708 memDC
.SelectObject(wxNullBitmap
);
711 else // no mask, just use BitBlt()
714 HDC memdc
= ::CreateCompatibleDC( cdc
);
715 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
717 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
719 COLORREF old_textground
= ::GetTextColor(GetHdc());
720 COLORREF old_background
= ::GetBkColor(GetHdc());
721 if (m_textForegroundColour
.Ok())
723 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
725 if (m_textBackgroundColour
.Ok())
727 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
730 ::SelectObject( memdc
, hbitmap
);
731 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
734 ::SetTextColor(GetHdc(), old_textground
);
735 ::SetBkColor(GetHdc(), old_background
);
739 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
741 DrawAnyText(text
, x
, y
);
743 // update the bounding box
744 CalcBoundingBox(x
, y
);
747 GetTextExtent(text
, &w
, &h
);
748 CalcBoundingBox(x
+ w
, y
+ h
);
751 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
753 // prepare for drawing the text
754 if ( m_textForegroundColour
.Ok() )
755 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
757 DWORD old_background
= 0;
758 if ( m_textBackgroundColour
.Ok() )
760 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
763 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
766 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
767 text
.c_str(), text
.length()) == 0 )
769 wxLogLastError("TextOut");
772 // restore the old parameters (text foreground colour may be left because
773 // it never is set to anything else, but background should remain
774 // transparent even if we just drew an opaque string)
775 if ( m_textBackgroundColour
.Ok() )
776 (void)SetBkColor(GetHdc(), old_background
);
778 SetBkMode(GetHdc(), TRANSPARENT
);
781 void wxDC::DoDrawRotatedText(const wxString
& text
,
782 wxCoord x
, wxCoord y
,
785 // we test that we have some font because otherwise we should still use the
786 // "else" part below to avoid that DrawRotatedText(angle = 180) and
787 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
788 // font for drawing rotated fonts unfortunately)
789 if ( (angle
== 0.0) && m_font
.Ok() )
791 DoDrawText(text
, x
, y
);
795 // NB: don't take DEFAULT_GUI_FONT because it's not TrueType and so
796 // can't have non zero orientation/escapement
797 wxFont font
= m_font
.Ok() ? m_font
: *wxNORMAL_FONT
;
798 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
800 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
802 wxLogLastError("GetObject(hfont)");
805 // GDI wants the angle in tenth of degree
806 long angle10
= (long)(angle
* 10);
807 lf
.lfEscapement
= angle10
;
808 lf
. lfOrientation
= angle10
;
810 hfont
= ::CreateFontIndirect(&lf
);
813 wxLogLastError("CreateFont");
817 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
819 DrawAnyText(text
, x
, y
);
821 (void)::SelectObject(GetHdc(), hfontOld
);
822 (void)::DeleteObject(hfont
);
825 // call the bounding box by adding all four vertices of the rectangle
826 // containing the text to it (simpler and probably not slower than
827 // determining which of them is really topmost/leftmost/...)
829 GetTextExtent(text
, &w
, &h
);
831 double rad
= DegToRad(angle
);
833 // "upper left" and "upper right"
834 CalcBoundingBox(x
, y
);
835 CalcBoundingBox(x
+ w
*cos(rad
), y
- h
*sin(rad
));
837 // "bottom left" and "bottom right"
838 x
+= (wxCoord
)(h
*sin(rad
));
839 y
+= (wxCoord
)(h
*cos(rad
));
840 CalcBoundingBox(x
, y
);
841 CalcBoundingBox(x
+ h
*sin(rad
), y
+ h
*cos(rad
));
845 // ---------------------------------------------------------------------------
847 // ---------------------------------------------------------------------------
849 void wxDC::SetPalette(const wxPalette
& palette
)
851 // Set the old object temporarily, in case the assignment deletes an object
852 // that's not yet selected out.
855 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
863 // Setting a NULL colourmap is a way of restoring
864 // the original colourmap
867 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
874 if (m_palette
.Ok() && m_palette
.GetHPALETTE())
876 HPALETTE oldPal
= ::SelectPalette(GetHdc(), (HPALETTE
) m_palette
.GetHPALETTE(), TRUE
);
878 m_oldPalette
= (WXHPALETTE
) oldPal
;
880 ::RealizePalette(GetHdc());
884 void wxDC::SetFont(const wxFont
& the_font
)
886 // Set the old object temporarily, in case the assignment deletes an object
887 // that's not yet selected out.
890 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
899 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
903 if (m_font
.Ok() && m_font
.GetResourceHandle())
905 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
906 if (f
== (HFONT
) NULL
)
908 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
911 m_oldFont
= (WXHFONT
) f
;
915 void wxDC::SetPen(const wxPen
& pen
)
917 // Set the old object temporarily, in case the assignment deletes an object
918 // that's not yet selected out.
921 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
930 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
936 if (m_pen
.GetResourceHandle())
938 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
940 m_oldPen
= (WXHPEN
) p
;
945 void wxDC::SetBrush(const wxBrush
& brush
)
947 // Set the old object temporarily, in case the assignment deletes an object
948 // that's not yet selected out.
951 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
960 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
966 if (m_brush
.GetResourceHandle())
969 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
971 m_oldBrush
= (WXHBRUSH
) b
;
976 void wxDC::SetBackground(const wxBrush
& brush
)
978 m_backgroundBrush
= brush
;
980 if (!m_backgroundBrush
.Ok())
985 bool customColours
= TRUE
;
986 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
987 // change background colours from the control-panel specified colours.
988 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
989 customColours
= FALSE
;
993 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
995 m_canvas
->SetTransparent(TRUE
);
999 // New behaviour, 10/2/99: setting the background brush of a DC
1000 // doesn't affect the window background colour. However,
1001 // I'm leaving in the transparency setting because it's needed by
1002 // various controls (e.g. wxStaticText) to determine whether to draw
1003 // transparently or not. TODO: maybe this should be a new function
1004 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1006 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1007 m_canvas
->SetTransparent(FALSE
);
1011 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1013 (void)SetBkColor(GetHdc(), new_color
);
1017 void wxDC::SetBackgroundMode(int mode
)
1019 m_backgroundMode
= mode
;
1021 // SetBackgroundColour now only refers to text background
1022 // and m_backgroundMode is used there
1025 if (m_backgroundMode == wxTRANSPARENT)
1026 ::SetBkMode(GetHdc(), TRANSPARENT);
1028 ::SetBkMode(GetHdc(), OPAQUE);
1032 void wxDC::SetLogicalFunction(int function
)
1034 m_logicalFunction
= function
;
1039 void wxDC::SetRop(WXHDC dc
)
1041 if ( !dc
|| m_logicalFunction
< 0 )
1046 switch (m_logicalFunction
)
1048 case wxCLEAR
: rop
= R2_BLACK
; break;
1049 case wxXOR
: rop
= R2_XORPEN
; break;
1050 case wxINVERT
: rop
= R2_NOT
; break;
1051 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1052 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1053 case wxCOPY
: rop
= R2_COPYPEN
; break;
1054 case wxAND
: rop
= R2_MASKPEN
; break;
1055 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1056 case wxNO_OP
: rop
= R2_NOP
; break;
1057 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1058 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1059 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1060 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1061 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1062 case wxOR
: rop
= R2_MERGEPEN
; break;
1063 case wxSET
: rop
= R2_WHITE
; break;
1066 wxFAIL_MSG( wxT("unsupported logical function") );
1070 SetROP2(GetHdc(), rop
);
1073 bool wxDC::StartDoc(const wxString
& message
)
1075 // We might be previewing, so return TRUE to let it continue.
1083 void wxDC::StartPage()
1087 void wxDC::EndPage()
1091 // ---------------------------------------------------------------------------
1093 // ---------------------------------------------------------------------------
1095 wxCoord
wxDC::GetCharHeight() const
1097 TEXTMETRIC lpTextMetric
;
1099 GetTextMetrics(GetHdc(), &lpTextMetric
);
1101 return YDEV2LOGREL(lpTextMetric
.tmHeight
);
1104 wxCoord
wxDC::GetCharWidth() const
1106 TEXTMETRIC lpTextMetric
;
1108 GetTextMetrics(GetHdc(), &lpTextMetric
);
1110 return XDEV2LOGREL(lpTextMetric
.tmAveCharWidth
);
1113 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1114 wxCoord
*descent
, wxCoord
*externalLeading
,
1115 wxFont
*theFont
) const
1117 wxFont
*fontToUse
= (wxFont
*) theFont
;
1119 fontToUse
= (wxFont
*) &m_font
;
1124 GetTextExtentPoint(GetHdc(), WXSTRINGCAST string
, wxStrlen(WXSTRINGCAST string
), &sizeRect
);
1125 GetTextMetrics(GetHdc(), &tm
);
1127 if (x
) *x
= XDEV2LOGREL(sizeRect
.cx
);
1128 if (y
) *y
= YDEV2LOGREL(sizeRect
.cy
);
1129 if (descent
) *descent
= tm
.tmDescent
;
1130 if (externalLeading
) *externalLeading
= tm
.tmExternalLeading
;
1133 void wxDC::SetMapMode(int mode
)
1135 m_mappingMode
= mode
;
1137 int pixel_width
= 0;
1138 int pixel_height
= 0;
1142 pixel_width
= GetDeviceCaps(GetHdc(), HORZRES
);
1143 pixel_height
= GetDeviceCaps(GetHdc(), VERTRES
);
1144 mm_width
= GetDeviceCaps(GetHdc(), HORZSIZE
);
1145 mm_height
= GetDeviceCaps(GetHdc(), VERTSIZE
);
1147 if ((pixel_width
== 0) || (pixel_height
== 0) || (mm_width
== 0) || (mm_height
== 0))
1152 double mm2pixelsX
= pixel_width
/mm_width
;
1153 double mm2pixelsY
= pixel_height
/mm_height
;
1159 m_logicalScaleX
= (twips2mm
* mm2pixelsX
);
1160 m_logicalScaleY
= (twips2mm
* mm2pixelsY
);
1165 m_logicalScaleX
= (pt2mm
* mm2pixelsX
);
1166 m_logicalScaleY
= (pt2mm
* mm2pixelsY
);
1171 m_logicalScaleX
= mm2pixelsX
;
1172 m_logicalScaleY
= mm2pixelsY
;
1177 m_logicalScaleX
= (mm2pixelsX
/10.0);
1178 m_logicalScaleY
= (mm2pixelsY
/10.0);
1184 m_logicalScaleX
= 1.0;
1185 m_logicalScaleY
= 1.0;
1190 if (::GetMapMode(GetHdc()) != MM_ANISOTROPIC
)
1191 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1193 SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1194 m_windowExtX
= (int)MS_XDEV2LOGREL(VIEWPORT_EXTENT
);
1195 m_windowExtY
= (int)MS_YDEV2LOGREL(VIEWPORT_EXTENT
);
1196 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
1197 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1198 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1201 void wxDC::SetUserScale(double x
, double y
)
1206 SetMapMode(m_mappingMode
);
1209 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1211 m_signX
= xLeftRight
? 1 : -1;
1212 m_signY
= yBottomUp
? -1 : 1;
1214 SetMapMode(m_mappingMode
);
1217 void wxDC::SetSystemScale(double x
, double y
)
1222 SetMapMode(m_mappingMode
);
1225 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1227 m_logicalOriginX
= x
;
1228 m_logicalOriginY
= y
;
1230 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1233 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1235 m_deviceOriginX
= x
;
1236 m_deviceOriginY
= y
;
1238 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1241 // ---------------------------------------------------------------------------
1242 // coordinates transformations
1243 // ---------------------------------------------------------------------------
1245 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1247 return (wxCoord
) (((x
) - m_deviceOriginX
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
) - m_logicalOriginX
);
1250 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1252 return (wxCoord
) ((x
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
));
1255 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1257 return (wxCoord
) (((y
) - m_deviceOriginY
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
) - m_logicalOriginY
);
1260 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1262 return (wxCoord
) ((y
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
));
1265 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1267 return (wxCoord
) ((x
- m_logicalOriginX
)*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
+ m_deviceOriginX
);
1270 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1272 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
);
1275 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1277 return (wxCoord
) ((y
- m_logicalOriginY
)*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
+ m_deviceOriginY
);
1280 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1282 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
);
1285 // ---------------------------------------------------------------------------
1287 // ---------------------------------------------------------------------------
1289 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1290 wxCoord width
, wxCoord height
,
1291 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1292 int rop
, bool useMask
)
1294 wxMask
*mask
= NULL
;
1297 const wxBitmap
& bmp
= source
->m_selectedBitmap
;
1298 mask
= bmp
.GetMask();
1300 if ( !(bmp
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1302 // don't give assert here because this would break existing
1303 // programs - just silently ignore useMask parameter
1308 COLORREF old_textground
= ::GetTextColor(GetHdc());
1309 COLORREF old_background
= ::GetBkColor(GetHdc());
1310 if (m_textForegroundColour
.Ok())
1312 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1314 if (m_textBackgroundColour
.Ok())
1316 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1319 DWORD dwRop
= SRCCOPY
;
1322 case wxXOR
: dwRop
= SRCINVERT
; break;
1323 case wxINVERT
: dwRop
= DSTINVERT
; break;
1324 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1325 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1326 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1327 case wxSET
: dwRop
= WHITENESS
; break;
1328 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1329 case wxAND
: dwRop
= SRCAND
; break;
1330 case wxOR
: dwRop
= SRCPAINT
; break;
1331 case wxEQUIV
: dwRop
= 0x00990066; break;
1332 case wxNAND
: dwRop
= 0x007700E6; break;
1333 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1334 case wxCOPY
: dwRop
= SRCCOPY
; break;
1335 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1336 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1337 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1339 wxFAIL_MSG( wxT("unsupported logical function") );
1348 // we want the part of the image corresponding to the mask to be
1349 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1350 // meaning of fg and bg is inverted which corresponds to wxWin notion
1351 // of the mask which is also contrary to the Windows one)
1352 success
= ::MaskBlt(GetHdc(), xdest
, ydest
, width
, height
,
1353 GetHdcOf(*source
), xsrc
, ysrc
,
1354 (HBITMAP
)mask
->GetMaskBitmap(), 0, 0,
1355 MAKEROP4(dwRop
, DSTCOPY
)) != 0;
1360 // Blit bitmap with mask
1362 // create a temp buffer bitmap and DCs to access it and the mask
1363 HDC dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1364 HDC dc_buffer
= ::CreateCompatibleDC(GetHdc());
1365 HBITMAP buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1366 ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1367 ::SelectObject(dc_buffer
, buffer_bmap
);
1369 // copy dest to buffer
1370 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1371 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1373 wxLogLastError("BitBlt");
1376 // copy src to buffer using selected raster op
1377 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1378 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1380 wxLogLastError("BitBlt");
1383 // set masked area in buffer to BLACK (pixel value 0)
1384 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1385 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1386 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1387 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1389 wxLogLastError("BitBlt");
1392 // set unmasked area in dest to BLACK
1393 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1394 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1395 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1396 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1398 wxLogLastError("BitBlt");
1400 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1401 ::SetTextColor(GetHdc(), prevCol
);
1403 // OR buffer to dest
1404 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1405 (int)width
, (int)height
,
1406 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1409 wxLogLastError("BitBlt");
1412 // tidy up temporary DCs and bitmap
1413 ::SelectObject(dc_mask
, 0);
1414 ::DeleteDC(dc_mask
);
1415 ::SelectObject(dc_buffer
, 0);
1416 ::DeleteDC(dc_buffer
);
1417 ::DeleteObject(buffer_bmap
);
1420 else // no mask, just BitBlt() it
1422 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1423 (int)width
, (int)height
,
1424 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) != 0;
1427 wxLogLastError("BitBlt");
1430 ::SetTextColor(GetHdc(), old_textground
);
1431 ::SetBkColor(GetHdc(), old_background
);
1436 void wxDC::DoGetSize(int *w
, int *h
) const
1438 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
1439 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
1442 void wxDC::DoGetSizeMM(int *w
, int *h
) const
1444 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZSIZE
);
1445 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1448 wxSize
wxDC::GetPPI() const
1450 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
1451 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
1453 return wxSize(x
, y
);
1456 // For use by wxWindows only, unless custom units are required.
1457 void wxDC::SetLogicalScale(double x
, double y
)
1459 m_logicalScaleX
= x
;
1460 m_logicalScaleY
= y
;
1463 #if WXWIN_COMPATIBILITY
1464 void wxDC::DoGetTextExtent(const wxString
& string
, float *x
, float *y
,
1465 float *descent
, float *externalLeading
,
1466 wxFont
*theFont
, bool use16bit
) const
1468 wxCoord x1
, y1
, descent1
, externalLeading1
;
1469 GetTextExtent(string
, & x1
, & y1
, & descent1
, & externalLeading1
, theFont
, use16bit
);
1472 *descent
= descent1
;
1473 if (externalLeading
)
1474 *externalLeading
= externalLeading1
;
1478 // ---------------------------------------------------------------------------
1479 // spline drawing code
1480 // ---------------------------------------------------------------------------
1484 class wxSpline
: public wxObject
1490 wxSpline(wxList
*list
);
1491 void DeletePoints();
1493 // Doesn't delete points
1497 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
);
1499 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
1500 double a3
, double b3
, double a4
, double b4
);
1501 void wx_clear_stack();
1502 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
1503 double *y3
, double *x4
, double *y4
);
1504 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
1505 double x4
, double y4
);
1506 static bool wx_spline_add_point(double x
, double y
);
1507 static void wx_spline_draw_point_array(wxDC
*dc
);
1508 wxSpline
*wx_make_spline(int x1
, int y1
, int x2
, int y2
, int x3
, int y3
);
1510 void wxDC::DoDrawSpline(wxList
*list
)
1512 wxSpline
spline(list
);
1514 wx_draw_open_spline(this, &spline
);
1517 wxList wx_spline_point_list
;
1519 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
)
1522 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
1523 double x1
, y1
, x2
, y2
;
1525 wxNode
*node
= spline
->points
->First();
1526 p
= (wxPoint
*)node
->Data();
1531 node
= node
->Next();
1532 p
= (wxPoint
*)node
->Data();
1536 cx1
= (double)((x1
+ x2
) / 2);
1537 cy1
= (double)((y1
+ y2
) / 2);
1538 cx2
= (double)((cx1
+ x2
) / 2);
1539 cy2
= (double)((cy1
+ y2
) / 2);
1541 wx_spline_add_point(x1
, y1
);
1543 while ((node
= node
->Next()) != NULL
)
1545 p
= (wxPoint
*)node
->Data();
1550 cx4
= (double)(x1
+ x2
) / 2;
1551 cy4
= (double)(y1
+ y2
) / 2;
1552 cx3
= (double)(x1
+ cx4
) / 2;
1553 cy3
= (double)(y1
+ cy4
) / 2;
1555 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
1559 cx2
= (double)(cx1
+ x2
) / 2;
1560 cy2
= (double)(cy1
+ y2
) / 2;
1563 wx_spline_add_point((double)wx_round(cx1
), (double)wx_round(cy1
));
1564 wx_spline_add_point(x2
, y2
);
1566 wx_spline_draw_point_array(dc
);
1570 /********************* CURVES FOR SPLINES *****************************
1572 The following spline drawing routine is from
1574 "An Algorithm for High-Speed Curve Generation"
1575 by George Merrill Chaikin,
1576 Computer Graphics and Image Processing, 3, Academic Press,
1581 "On Chaikin's Algorithm" by R. F. Riesenfeld,
1582 Computer Graphics and Image Processing, 4, Academic Press,
1585 ***********************************************************************/
1587 #define half(z1, z2) ((z1+z2)/2.0)
1590 /* iterative version */
1592 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
1595 register double xmid
, ymid
;
1596 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1599 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
1601 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
1602 xmid
= (double)half(x2
, x3
);
1603 ymid
= (double)half(y2
, y3
);
1604 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
1605 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
1606 wx_spline_add_point((double)wx_round(x1
), (double)wx_round(y1
));
1607 wx_spline_add_point((double)wx_round(xmid
), (double)wx_round(ymid
));
1609 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
1610 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
1611 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
1612 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
1618 /* utilities used by spline drawing routines */
1621 typedef struct wx_spline_stack_struct
{
1622 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1626 #define SPLINE_STACK_DEPTH 20
1627 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
1628 static Stack
*wx_stack_top
;
1629 static int wx_stack_count
;
1631 void wx_clear_stack()
1633 wx_stack_top
= wx_spline_stack
;
1637 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
1639 wx_stack_top
->x1
= x1
;
1640 wx_stack_top
->y1
= y1
;
1641 wx_stack_top
->x2
= x2
;
1642 wx_stack_top
->y2
= y2
;
1643 wx_stack_top
->x3
= x3
;
1644 wx_stack_top
->y3
= y3
;
1645 wx_stack_top
->x4
= x4
;
1646 wx_stack_top
->y4
= y4
;
1651 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
1652 double *x3
, double *y3
, double *x4
, double *y4
)
1654 if (wx_stack_count
== 0)
1658 *x1
= wx_stack_top
->x1
;
1659 *y1
= wx_stack_top
->y1
;
1660 *x2
= wx_stack_top
->x2
;
1661 *y2
= wx_stack_top
->y2
;
1662 *x3
= wx_stack_top
->x3
;
1663 *y3
= wx_stack_top
->y3
;
1664 *x4
= wx_stack_top
->x4
;
1665 *y4
= wx_stack_top
->y4
;
1669 static bool wx_spline_add_point(double x
, double y
)
1671 wxPoint
*point
= new wxPoint
;
1674 wx_spline_point_list
.Append((wxObject
*)point
);
1678 static void wx_spline_draw_point_array(wxDC
*dc
)
1680 dc
->DrawLines(&wx_spline_point_list
, 0, 0);
1681 wxNode
*node
= wx_spline_point_list
.First();
1684 wxPoint
*point
= (wxPoint
*)node
->Data();
1687 node
= wx_spline_point_list
.First();
1691 wxSpline::wxSpline(wxList
*list
)
1696 wxSpline::~wxSpline()
1700 void wxSpline::DeletePoints()
1702 for(wxNode
*node
= points
->First(); node
; node
= points
->First())
1704 wxPoint
*point
= (wxPoint
*)node
->Data();
1712 #endif // wxUSE_SPLINES