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 /* MATTHEW: [6] New normalization */
364 #if WX_STANDARD_GRAPHICS
365 (void)LineTo(GetHdc(), XLOG2DEV(x2
) + 1, YLOG2DEV(y2
));
368 CalcBoundingBox(x1
, y1
);
369 CalcBoundingBox(x2
, y2
);
372 void wxDC::DoDrawArc(wxCoord x1
,wxCoord y1
,wxCoord x2
,wxCoord y2
, wxCoord xc
, wxCoord yc
)
376 double radius
= (double)sqrt(dx
*dx
+dy
*dy
) ;;
377 if (x1
==x2
&& x2
==y2
)
379 DrawEllipse(xc
,yc
,(wxCoord
)(radius
*2.0),(wxCoord
)(radius
*2.0));
383 wxCoord xx1
= XLOG2DEV(x1
);
384 wxCoord yy1
= YLOG2DEV(y1
);
385 wxCoord xx2
= XLOG2DEV(x2
);
386 wxCoord yy2
= YLOG2DEV(y2
);
387 wxCoord xxc
= XLOG2DEV(xc
);
388 wxCoord yyc
= YLOG2DEV(yc
);
389 wxCoord ray
= (wxCoord
) sqrt(double((xxc
-xx1
)*(xxc
-xx1
)+(yyc
-yy1
)*(yyc
-yy1
)));
391 (void)MoveToEx(GetHdc(), (int) xx1
, (int) yy1
, NULL
);
392 wxCoord xxx1
= (wxCoord
) (xxc
-ray
);
393 wxCoord yyy1
= (wxCoord
) (yyc
-ray
);
394 wxCoord xxx2
= (wxCoord
) (xxc
+ray
);
395 wxCoord yyy2
= (wxCoord
) (yyc
+ray
);
396 if (m_brush
.Ok() && m_brush
.GetStyle() !=wxTRANSPARENT
)
398 // Have to add 1 to bottom-right corner of rectangle
399 // to make semi-circles look right (crooked line otherwise).
400 // Unfortunately this is not a reliable method, depends
401 // on the size of shape.
402 // TODO: figure out why this happens!
403 Pie(GetHdc(),xxx1
,yyy1
,xxx2
+1,yyy2
+1,
407 Arc(GetHdc(),xxx1
,yyy1
,xxx2
,yyy2
,
410 CalcBoundingBox((wxCoord
)(xc
-radius
), (wxCoord
)(yc
-radius
));
411 CalcBoundingBox((wxCoord
)(xc
+radius
), (wxCoord
)(yc
+radius
));
414 void wxDC::DoDrawPoint(wxCoord x
, wxCoord y
)
416 COLORREF color
= 0x00ffffff;
419 color
= m_pen
.GetColour().GetPixel();
422 SetPixel(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), color
);
424 CalcBoundingBox(x
, y
);
427 void wxDC::DoDrawPolygon(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
,int fillStyle
)
429 COLORREF old_textground
= ::GetTextColor(GetHdc());
430 COLORREF old_background
= ::GetBkColor(GetHdc());
431 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
434 if (m_textForegroundColour
.Ok())
435 { //just the oposite from what is expected see help on pattern brush
436 // 1 in mask becomes bk color
437 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel() );
439 if (m_textBackgroundColour
.Ok())
440 { //just the oposite from what is expected
441 // 0 in mask becomes text color
442 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
445 if (m_backgroundMode
== wxTRANSPARENT
)
446 SetBkMode(GetHdc(), TRANSPARENT
);
448 SetBkMode(GetHdc(), OPAQUE
);
451 // Do things less efficiently if we have offsets
452 if (xoffset
!= 0 || yoffset
!= 0)
454 POINT
*cpoints
= new POINT
[n
];
456 for (i
= 0; i
< n
; i
++)
458 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
459 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
461 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
463 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
464 (void)Polygon(GetHdc(), cpoints
, n
);
465 SetPolyFillMode(GetHdc(),prev
);
471 for (i
= 0; i
< n
; i
++)
472 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
474 int prev
= SetPolyFillMode(GetHdc(),fillStyle
==wxODDEVEN_RULE
?ALTERNATE
:WINDING
);
475 (void)Polygon(GetHdc(), (POINT
*) points
, n
);
476 SetPolyFillMode(GetHdc(),prev
);
479 if (m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
481 ::SetBkMode(GetHdc(), TRANSPARENT
);
482 ::SetTextColor(GetHdc(), old_textground
);
483 ::SetBkColor(GetHdc(), old_background
);
487 void wxDC::DoDrawLines(int n
, wxPoint points
[], wxCoord xoffset
, wxCoord yoffset
)
489 // Do things less efficiently if we have offsets
490 if (xoffset
!= 0 || yoffset
!= 0)
492 POINT
*cpoints
= new POINT
[n
];
494 for (i
= 0; i
< n
; i
++)
496 cpoints
[i
].x
= (int)(points
[i
].x
+ xoffset
);
497 cpoints
[i
].y
= (int)(points
[i
].y
+ yoffset
);
499 CalcBoundingBox(cpoints
[i
].x
, cpoints
[i
].y
);
501 (void)Polyline(GetHdc(), cpoints
, n
);
507 for (i
= 0; i
< n
; i
++)
508 CalcBoundingBox(points
[i
].x
, points
[i
].y
);
510 (void)Polyline(GetHdc(), (POINT
*) points
, n
);
514 void wxDC::DoDrawRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
516 COLORREF colFgOld
= 0,
519 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
521 colFgOld
= ::GetTextColor(GetHdc());
522 colBgOld
= ::GetBkColor(GetHdc());
524 if ( m_textForegroundColour
.Ok() )
526 // just the oposite from what is expected see help on pattern brush
527 // 1 in mask becomes bk color
528 ::SetBkColor(GetHdc(), m_textForegroundColour
.GetPixel());
531 if ( m_textBackgroundColour
.Ok() )
533 // 0 in mask becomes text color
534 ::SetTextColor(GetHdc(), m_textBackgroundColour
.GetPixel());
537 // VZ: IMHO this does strictly nothing here
538 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
542 wxCoord x2
= x
+ width
;
543 wxCoord y2
= y
+ height
;
545 // Windows draws the filled rectangles without outline (i.e. drawn with a
546 // transparent pen) one pixel smaller in both directions and we want them
547 // to have the same size regardless of which pen is used - adjust
548 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
554 (void)Rectangle(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
556 CalcBoundingBox(x
, y
);
557 CalcBoundingBox(x2
, y2
);
559 if ( m_brush
.GetStyle() == wxSTIPPLE_MASK_OPAQUE
)
561 // restore the colours we changed
562 ::SetBkMode(GetHdc(), TRANSPARENT
);
563 ::SetTextColor(GetHdc(), colFgOld
);
564 ::SetBkColor(GetHdc(), colBgOld
);
568 void wxDC::DoDrawRoundedRectangle(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
, double radius
)
570 // Now, a negative radius value is interpreted to mean
571 // 'the proportion of the smallest X or Y dimension'
575 double smallest
= 0.0;
580 radius
= (- radius
* smallest
);
583 wxCoord x2
= (x
+width
);
584 wxCoord y2
= (y
+height
);
586 // Windows draws the filled rectangles without outline (i.e. drawn with a
587 // transparent pen) one pixel smaller in both directions and we want them
588 // to have the same size regardless of which pen is used - adjust
589 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
595 (void)RoundRect(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
),
596 YLOG2DEV(y2
), (int) (2*XLOG2DEV(radius
)), (int)( 2*YLOG2DEV(radius
)));
598 CalcBoundingBox(x
, y
);
599 CalcBoundingBox(x2
, y2
);
602 void wxDC::DoDrawEllipse(wxCoord x
, wxCoord y
, wxCoord width
, wxCoord height
)
604 wxCoord x2
= (x
+width
);
605 wxCoord y2
= (y
+height
);
607 (void)Ellipse(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
));
609 CalcBoundingBox(x
, y
);
610 CalcBoundingBox(x2
, y2
);
613 // Chris Breeze 20/5/98: first implementation of DrawEllipticArc on Windows
614 void wxDC::DoDrawEllipticArc(wxCoord x
,wxCoord y
,wxCoord w
,wxCoord h
,double sa
,double ea
)
619 int rx1
= XLOG2DEV(x
+w
/2);
620 int ry1
= YLOG2DEV(y
+h
/2);
627 rx1
+= (int)(100.0 * abs(w
) * cos(sa
));
628 ry1
-= (int)(100.0 * abs(h
) * m_signY
* sin(sa
));
629 rx2
+= (int)(100.0 * abs(w
) * cos(ea
));
630 ry2
-= (int)(100.0 * abs(h
) * m_signY
* sin(ea
));
632 // draw pie with NULL_PEN first and then outline otherwise a line is
633 // drawn from the start and end points to the centre
634 HPEN orig_pen
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
) ::GetStockObject(NULL_PEN
));
637 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
)+1, YLOG2DEV(y2
)+1,
642 (void)Pie(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
)-1, XLOG2DEV(x2
)+1, YLOG2DEV(y2
),
643 rx1
, ry1
-1, rx2
, ry2
-1);
645 ::SelectObject(GetHdc(), orig_pen
);
646 (void)Arc(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), XLOG2DEV(x2
), YLOG2DEV(y2
),
649 CalcBoundingBox(x
, y
);
650 CalcBoundingBox(x2
, y2
);
653 void wxDC::DoDrawIcon(const wxIcon
& icon
, wxCoord x
, wxCoord y
)
655 wxCHECK_RET( icon
.Ok(), wxT("invalid icon in DrawIcon") );
657 ::DrawIcon(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
), GetHiconOf(icon
));
659 CalcBoundingBox(x
, y
);
660 CalcBoundingBox(x
+ icon
.GetWidth(), y
+ icon
.GetHeight());
663 void wxDC::DoDrawBitmap( const wxBitmap
&bmp
, wxCoord x
, wxCoord y
, bool useMask
)
665 wxCHECK_RET( bmp
.Ok(), _T("invalid bitmap in wxDC::DrawBitmap") );
667 int width
= bmp
.GetWidth(),
668 height
= bmp
.GetHeight();
670 HBITMAP hbmpMask
= 0;
674 wxMask
*mask
= bmp
.GetMask();
676 hbmpMask
= (HBITMAP
)mask
->GetMaskBitmap();
680 // don't give assert here because this would break existing
681 // programs - just silently ignore useMask parameter
689 HDC hdcMem
= ::CreateCompatibleDC(GetHdc());
690 ::SelectObject(hdcMem
, GetHbitmapOf(bmp
));
692 // use MaskBlt() with ROP which doesn't do anything to dst in the mask
694 bool ok
= ::MaskBlt(GetHdc(), x
, y
, width
, height
,
697 MAKEROP4(SRCCOPY
, DSTCOPY
)) != 0;
703 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API
706 memDC
.SelectObject(bmp
);
708 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
710 memDC
.SelectObject(wxNullBitmap
);
713 else // no mask, just use BitBlt()
716 HDC memdc
= ::CreateCompatibleDC( cdc
);
717 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
719 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
721 COLORREF old_textground
= ::GetTextColor(GetHdc());
722 COLORREF old_background
= ::GetBkColor(GetHdc());
723 if (m_textForegroundColour
.Ok())
725 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
727 if (m_textBackgroundColour
.Ok())
729 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
732 ::SelectObject( memdc
, hbitmap
);
733 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
736 ::SetTextColor(GetHdc(), old_textground
);
737 ::SetBkColor(GetHdc(), old_background
);
741 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
743 DrawAnyText(text
, x
, y
);
745 // update the bounding box
746 CalcBoundingBox(x
, y
);
749 GetTextExtent(text
, &w
, &h
);
750 CalcBoundingBox(x
+ w
, y
+ h
);
753 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
755 // prepare for drawing the text
756 if ( m_textForegroundColour
.Ok() )
757 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
759 DWORD old_background
= 0;
760 if ( m_textBackgroundColour
.Ok() )
762 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
765 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
768 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
769 text
.c_str(), text
.length()) == 0 )
771 wxLogLastError("TextOut");
774 // restore the old parameters (text foreground colour may be left because
775 // it never is set to anything else, but background should remain
776 // transparent even if we just drew an opaque string)
777 if ( m_textBackgroundColour
.Ok() )
778 (void)SetBkColor(GetHdc(), old_background
);
780 SetBkMode(GetHdc(), TRANSPARENT
);
783 void wxDC::DoDrawRotatedText(const wxString
& text
,
784 wxCoord x
, wxCoord y
,
787 // we test that we have some font because otherwise we should still use the
788 // "else" part below to avoid that DrawRotatedText(angle = 180) and
789 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
790 // font for drawing rotated fonts unfortunately)
791 if ( (angle
== 0.0) && m_font
.Ok() )
793 DoDrawText(text
, x
, y
);
797 // NB: don't take DEFAULT_GUI_FONT because it's not TrueType and so
798 // can't have non zero orientation/escapement
799 wxFont font
= m_font
.Ok() ? m_font
: *wxNORMAL_FONT
;
800 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
802 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
804 wxLogLastError("GetObject(hfont)");
807 // GDI wants the angle in tenth of degree
808 long angle10
= (long)(angle
* 10);
809 lf
.lfEscapement
= angle10
;
810 lf
. lfOrientation
= angle10
;
812 hfont
= ::CreateFontIndirect(&lf
);
815 wxLogLastError("CreateFont");
819 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
821 DrawAnyText(text
, x
, y
);
823 (void)::SelectObject(GetHdc(), hfontOld
);
824 (void)::DeleteObject(hfont
);
827 // call the bounding box by adding all four vertices of the rectangle
828 // containing the text to it (simpler and probably not slower than
829 // determining which of them is really topmost/leftmost/...)
831 GetTextExtent(text
, &w
, &h
);
833 double rad
= DegToRad(angle
);
835 // "upper left" and "upper right"
836 CalcBoundingBox(x
, y
);
837 CalcBoundingBox(x
+ w
*cos(rad
), y
- h
*sin(rad
));
839 // "bottom left" and "bottom right"
840 x
+= (wxCoord
)(h
*sin(rad
));
841 y
+= (wxCoord
)(h
*cos(rad
));
842 CalcBoundingBox(x
, y
);
843 CalcBoundingBox(x
+ h
*sin(rad
), y
+ h
*cos(rad
));
847 // ---------------------------------------------------------------------------
849 // ---------------------------------------------------------------------------
851 void wxDC::SetPalette(const wxPalette
& palette
)
853 // Set the old object temporarily, in case the assignment deletes an object
854 // that's not yet selected out.
857 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
865 // Setting a NULL colourmap is a way of restoring
866 // the original colourmap
869 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
876 if (m_palette
.Ok() && m_palette
.GetHPALETTE())
878 HPALETTE oldPal
= ::SelectPalette(GetHdc(), (HPALETTE
) m_palette
.GetHPALETTE(), TRUE
);
880 m_oldPalette
= (WXHPALETTE
) oldPal
;
882 ::RealizePalette(GetHdc());
886 void wxDC::SetFont(const wxFont
& the_font
)
888 // Set the old object temporarily, in case the assignment deletes an object
889 // that's not yet selected out.
892 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
901 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
905 if (m_font
.Ok() && m_font
.GetResourceHandle())
907 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
908 if (f
== (HFONT
) NULL
)
910 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
913 m_oldFont
= (WXHFONT
) f
;
917 void wxDC::SetPen(const wxPen
& pen
)
919 // Set the old object temporarily, in case the assignment deletes an object
920 // that's not yet selected out.
923 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
932 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
938 if (m_pen
.GetResourceHandle())
940 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
942 m_oldPen
= (WXHPEN
) p
;
947 void wxDC::SetBrush(const wxBrush
& brush
)
949 // Set the old object temporarily, in case the assignment deletes an object
950 // that's not yet selected out.
953 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
962 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
968 if (m_brush
.GetResourceHandle())
971 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
973 m_oldBrush
= (WXHBRUSH
) b
;
978 void wxDC::SetBackground(const wxBrush
& brush
)
980 m_backgroundBrush
= brush
;
982 if (!m_backgroundBrush
.Ok())
987 bool customColours
= TRUE
;
988 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
989 // change background colours from the control-panel specified colours.
990 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
991 customColours
= FALSE
;
995 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
997 m_canvas
->SetTransparent(TRUE
);
1001 // New behaviour, 10/2/99: setting the background brush of a DC
1002 // doesn't affect the window background colour. However,
1003 // I'm leaving in the transparency setting because it's needed by
1004 // various controls (e.g. wxStaticText) to determine whether to draw
1005 // transparently or not. TODO: maybe this should be a new function
1006 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1008 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1009 m_canvas
->SetTransparent(FALSE
);
1013 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1015 (void)SetBkColor(GetHdc(), new_color
);
1019 void wxDC::SetBackgroundMode(int mode
)
1021 m_backgroundMode
= mode
;
1023 // SetBackgroundColour now only refers to text background
1024 // and m_backgroundMode is used there
1027 if (m_backgroundMode == wxTRANSPARENT)
1028 ::SetBkMode(GetHdc(), TRANSPARENT);
1030 ::SetBkMode(GetHdc(), OPAQUE);
1034 void wxDC::SetLogicalFunction(int function
)
1036 m_logicalFunction
= function
;
1041 void wxDC::SetRop(WXHDC dc
)
1043 if ( !dc
|| m_logicalFunction
< 0 )
1048 switch (m_logicalFunction
)
1050 case wxCLEAR
: rop
= R2_BLACK
; break;
1051 case wxXOR
: rop
= R2_XORPEN
; break;
1052 case wxINVERT
: rop
= R2_NOT
; break;
1053 case wxOR_REVERSE
: rop
= R2_MERGEPENNOT
; break;
1054 case wxAND_REVERSE
: rop
= R2_MASKPENNOT
; break;
1055 case wxCOPY
: rop
= R2_COPYPEN
; break;
1056 case wxAND
: rop
= R2_MASKPEN
; break;
1057 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1058 case wxNO_OP
: rop
= R2_NOP
; break;
1059 case wxNOR
: rop
= R2_NOTMERGEPEN
; break;
1060 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1061 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1062 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1063 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1064 case wxOR
: rop
= R2_MERGEPEN
; break;
1065 case wxSET
: rop
= R2_WHITE
; break;
1068 wxFAIL_MSG( wxT("unsupported logical function") );
1072 SetROP2(GetHdc(), rop
);
1075 bool wxDC::StartDoc(const wxString
& message
)
1077 // We might be previewing, so return TRUE to let it continue.
1085 void wxDC::StartPage()
1089 void wxDC::EndPage()
1093 // ---------------------------------------------------------------------------
1095 // ---------------------------------------------------------------------------
1097 wxCoord
wxDC::GetCharHeight() const
1099 TEXTMETRIC lpTextMetric
;
1101 GetTextMetrics(GetHdc(), &lpTextMetric
);
1103 return YDEV2LOGREL(lpTextMetric
.tmHeight
);
1106 wxCoord
wxDC::GetCharWidth() const
1108 TEXTMETRIC lpTextMetric
;
1110 GetTextMetrics(GetHdc(), &lpTextMetric
);
1112 return XDEV2LOGREL(lpTextMetric
.tmAveCharWidth
);
1115 void wxDC::DoGetTextExtent(const wxString
& string
, wxCoord
*x
, wxCoord
*y
,
1116 wxCoord
*descent
, wxCoord
*externalLeading
,
1117 wxFont
*theFont
) const
1119 wxFont
*fontToUse
= (wxFont
*) theFont
;
1121 fontToUse
= (wxFont
*) &m_font
;
1126 GetTextExtentPoint(GetHdc(), WXSTRINGCAST string
, wxStrlen(WXSTRINGCAST string
), &sizeRect
);
1127 GetTextMetrics(GetHdc(), &tm
);
1129 if (x
) *x
= XDEV2LOGREL(sizeRect
.cx
);
1130 if (y
) *y
= YDEV2LOGREL(sizeRect
.cy
);
1131 if (descent
) *descent
= tm
.tmDescent
;
1132 if (externalLeading
) *externalLeading
= tm
.tmExternalLeading
;
1135 void wxDC::SetMapMode(int mode
)
1137 m_mappingMode
= mode
;
1139 int pixel_width
= 0;
1140 int pixel_height
= 0;
1144 pixel_width
= GetDeviceCaps(GetHdc(), HORZRES
);
1145 pixel_height
= GetDeviceCaps(GetHdc(), VERTRES
);
1146 mm_width
= GetDeviceCaps(GetHdc(), HORZSIZE
);
1147 mm_height
= GetDeviceCaps(GetHdc(), VERTSIZE
);
1149 if ((pixel_width
== 0) || (pixel_height
== 0) || (mm_width
== 0) || (mm_height
== 0))
1154 double mm2pixelsX
= pixel_width
/mm_width
;
1155 double mm2pixelsY
= pixel_height
/mm_height
;
1161 m_logicalScaleX
= (twips2mm
* mm2pixelsX
);
1162 m_logicalScaleY
= (twips2mm
* mm2pixelsY
);
1167 m_logicalScaleX
= (pt2mm
* mm2pixelsX
);
1168 m_logicalScaleY
= (pt2mm
* mm2pixelsY
);
1173 m_logicalScaleX
= mm2pixelsX
;
1174 m_logicalScaleY
= mm2pixelsY
;
1179 m_logicalScaleX
= (mm2pixelsX
/10.0);
1180 m_logicalScaleY
= (mm2pixelsY
/10.0);
1186 m_logicalScaleX
= 1.0;
1187 m_logicalScaleY
= 1.0;
1192 if (::GetMapMode(GetHdc()) != MM_ANISOTROPIC
)
1193 ::SetMapMode(GetHdc(), MM_ANISOTROPIC
);
1195 SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT
, VIEWPORT_EXTENT
, NULL
);
1196 m_windowExtX
= (int)MS_XDEV2LOGREL(VIEWPORT_EXTENT
);
1197 m_windowExtY
= (int)MS_YDEV2LOGREL(VIEWPORT_EXTENT
);
1198 ::SetWindowExtEx(GetHdc(), m_windowExtX
, m_windowExtY
, NULL
);
1199 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1200 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1203 void wxDC::SetUserScale(double x
, double y
)
1208 SetMapMode(m_mappingMode
);
1211 void wxDC::SetAxisOrientation(bool xLeftRight
, bool yBottomUp
)
1213 m_signX
= xLeftRight
? 1 : -1;
1214 m_signY
= yBottomUp
? -1 : 1;
1216 SetMapMode(m_mappingMode
);
1219 void wxDC::SetSystemScale(double x
, double y
)
1224 SetMapMode(m_mappingMode
);
1227 void wxDC::SetLogicalOrigin(wxCoord x
, wxCoord y
)
1229 m_logicalOriginX
= x
;
1230 m_logicalOriginY
= y
;
1232 ::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX
, (int)m_logicalOriginY
, NULL
);
1235 void wxDC::SetDeviceOrigin(wxCoord x
, wxCoord y
)
1237 m_deviceOriginX
= x
;
1238 m_deviceOriginY
= y
;
1240 ::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX
, (int)m_deviceOriginY
, NULL
);
1243 // ---------------------------------------------------------------------------
1244 // coordinates transformations
1245 // ---------------------------------------------------------------------------
1247 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
1249 return (wxCoord
) (((x
) - m_deviceOriginX
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
) - m_logicalOriginX
);
1252 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
1254 return (wxCoord
) ((x
)/(m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
));
1257 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
1259 return (wxCoord
) (((y
) - m_deviceOriginY
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
) - m_logicalOriginY
);
1262 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
1264 return (wxCoord
) ((y
)/(m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
));
1267 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
1269 return (wxCoord
) ((x
- m_logicalOriginX
)*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
+ m_deviceOriginX
);
1272 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
1274 return (wxCoord
) (x
*m_logicalScaleX
*m_userScaleX
*m_signX
*m_scaleX
);
1277 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
1279 return (wxCoord
) ((y
- m_logicalOriginY
)*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
+ m_deviceOriginY
);
1282 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
1284 return (wxCoord
) (y
*m_logicalScaleY
*m_userScaleY
*m_signY
*m_scaleY
);
1287 // ---------------------------------------------------------------------------
1289 // ---------------------------------------------------------------------------
1291 bool wxDC::DoBlit(wxCoord xdest
, wxCoord ydest
,
1292 wxCoord width
, wxCoord height
,
1293 wxDC
*source
, wxCoord xsrc
, wxCoord ysrc
,
1294 int rop
, bool useMask
)
1296 wxMask
*mask
= NULL
;
1299 const wxBitmap
& bmp
= source
->m_selectedBitmap
;
1300 mask
= bmp
.GetMask();
1302 if ( !(bmp
.Ok() && mask
&& mask
->GetMaskBitmap()) )
1304 // don't give assert here because this would break existing
1305 // programs - just silently ignore useMask parameter
1310 COLORREF old_textground
= ::GetTextColor(GetHdc());
1311 COLORREF old_background
= ::GetBkColor(GetHdc());
1312 if (m_textForegroundColour
.Ok())
1314 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
1316 if (m_textBackgroundColour
.Ok())
1318 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
1321 DWORD dwRop
= SRCCOPY
;
1324 case wxXOR
: dwRop
= SRCINVERT
; break;
1325 case wxINVERT
: dwRop
= DSTINVERT
; break;
1326 case wxOR_REVERSE
: dwRop
= 0x00DD0228; break;
1327 case wxAND_REVERSE
: dwRop
= SRCERASE
; break;
1328 case wxCLEAR
: dwRop
= BLACKNESS
; break;
1329 case wxSET
: dwRop
= WHITENESS
; break;
1330 case wxOR_INVERT
: dwRop
= MERGEPAINT
; break;
1331 case wxAND
: dwRop
= SRCAND
; break;
1332 case wxOR
: dwRop
= SRCPAINT
; break;
1333 case wxEQUIV
: dwRop
= 0x00990066; break;
1334 case wxNAND
: dwRop
= 0x007700E6; break;
1335 case wxAND_INVERT
: dwRop
= 0x00220326; break;
1336 case wxCOPY
: dwRop
= SRCCOPY
; break;
1337 case wxNO_OP
: dwRop
= DSTCOPY
; break;
1338 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1339 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1341 wxFAIL_MSG( wxT("unsupported logical function") );
1350 // we want the part of the image corresponding to the mask to be
1351 // transparent, so use "DSTCOPY" ROP for the mask points (the usual
1352 // meaning of fg and bg is inverted which corresponds to wxWin notion
1353 // of the mask which is also contrary to the Windows one)
1354 success
= ::MaskBlt(GetHdc(), xdest
, ydest
, width
, height
,
1355 GetHdcOf(*source
), xsrc
, ysrc
,
1356 (HBITMAP
)mask
->GetMaskBitmap(), 0, 0,
1357 MAKEROP4(dwRop
, DSTCOPY
)) != 0;
1362 // Blit bitmap with mask
1364 // create a temp buffer bitmap and DCs to access it and the mask
1365 HDC dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1366 HDC dc_buffer
= ::CreateCompatibleDC(GetHdc());
1367 HBITMAP buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1368 ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1369 ::SelectObject(dc_buffer
, buffer_bmap
);
1371 // copy dest to buffer
1372 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1373 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1375 wxLogLastError("BitBlt");
1378 // copy src to buffer using selected raster op
1379 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1380 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1382 wxLogLastError("BitBlt");
1385 // set masked area in buffer to BLACK (pixel value 0)
1386 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1387 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1388 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1389 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1391 wxLogLastError("BitBlt");
1394 // set unmasked area in dest to BLACK
1395 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1396 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1397 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1398 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1400 wxLogLastError("BitBlt");
1402 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1403 ::SetTextColor(GetHdc(), prevCol
);
1405 // OR buffer to dest
1406 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1407 (int)width
, (int)height
,
1408 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1411 wxLogLastError("BitBlt");
1414 // tidy up temporary DCs and bitmap
1415 ::SelectObject(dc_mask
, 0);
1416 ::DeleteDC(dc_mask
);
1417 ::SelectObject(dc_buffer
, 0);
1418 ::DeleteDC(dc_buffer
);
1419 ::DeleteObject(buffer_bmap
);
1422 else // no mask, just BitBlt() it
1424 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1425 (int)width
, (int)height
,
1426 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) != 0;
1429 wxLogLastError("BitBlt");
1432 ::SetTextColor(GetHdc(), old_textground
);
1433 ::SetBkColor(GetHdc(), old_background
);
1438 void wxDC::DoGetSize(int *w
, int *h
) const
1440 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
1441 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
1444 void wxDC::DoGetSizeMM(int *w
, int *h
) const
1446 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZSIZE
);
1447 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1450 wxSize
wxDC::GetPPI() const
1452 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
1453 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
1455 return wxSize(x
, y
);
1458 // For use by wxWindows only, unless custom units are required.
1459 void wxDC::SetLogicalScale(double x
, double y
)
1461 m_logicalScaleX
= x
;
1462 m_logicalScaleY
= y
;
1465 #if WXWIN_COMPATIBILITY
1466 void wxDC::DoGetTextExtent(const wxString
& string
, float *x
, float *y
,
1467 float *descent
, float *externalLeading
,
1468 wxFont
*theFont
, bool use16bit
) const
1470 wxCoord x1
, y1
, descent1
, externalLeading1
;
1471 GetTextExtent(string
, & x1
, & y1
, & descent1
, & externalLeading1
, theFont
, use16bit
);
1474 *descent
= descent1
;
1475 if (externalLeading
)
1476 *externalLeading
= externalLeading1
;
1480 // ---------------------------------------------------------------------------
1481 // spline drawing code
1482 // ---------------------------------------------------------------------------
1486 class wxSpline
: public wxObject
1492 wxSpline(wxList
*list
);
1493 void DeletePoints();
1495 // Doesn't delete points
1499 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
);
1501 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
1502 double a3
, double b3
, double a4
, double b4
);
1503 void wx_clear_stack();
1504 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
1505 double *y3
, double *x4
, double *y4
);
1506 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
1507 double x4
, double y4
);
1508 static bool wx_spline_add_point(double x
, double y
);
1509 static void wx_spline_draw_point_array(wxDC
*dc
);
1510 wxSpline
*wx_make_spline(int x1
, int y1
, int x2
, int y2
, int x3
, int y3
);
1512 void wxDC::DoDrawSpline(wxList
*list
)
1514 wxSpline
spline(list
);
1516 wx_draw_open_spline(this, &spline
);
1519 wxList wx_spline_point_list
;
1521 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
)
1524 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
1525 double x1
, y1
, x2
, y2
;
1527 wxNode
*node
= spline
->points
->First();
1528 p
= (wxPoint
*)node
->Data();
1533 node
= node
->Next();
1534 p
= (wxPoint
*)node
->Data();
1538 cx1
= (double)((x1
+ x2
) / 2);
1539 cy1
= (double)((y1
+ y2
) / 2);
1540 cx2
= (double)((cx1
+ x2
) / 2);
1541 cy2
= (double)((cy1
+ y2
) / 2);
1543 wx_spline_add_point(x1
, y1
);
1545 while ((node
= node
->Next()) != NULL
)
1547 p
= (wxPoint
*)node
->Data();
1552 cx4
= (double)(x1
+ x2
) / 2;
1553 cy4
= (double)(y1
+ y2
) / 2;
1554 cx3
= (double)(x1
+ cx4
) / 2;
1555 cy3
= (double)(y1
+ cy4
) / 2;
1557 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
1561 cx2
= (double)(cx1
+ x2
) / 2;
1562 cy2
= (double)(cy1
+ y2
) / 2;
1565 wx_spline_add_point((double)wx_round(cx1
), (double)wx_round(cy1
));
1566 wx_spline_add_point(x2
, y2
);
1568 wx_spline_draw_point_array(dc
);
1572 /********************* CURVES FOR SPLINES *****************************
1574 The following spline drawing routine is from
1576 "An Algorithm for High-Speed Curve Generation"
1577 by George Merrill Chaikin,
1578 Computer Graphics and Image Processing, 3, Academic Press,
1583 "On Chaikin's Algorithm" by R. F. Riesenfeld,
1584 Computer Graphics and Image Processing, 4, Academic Press,
1587 ***********************************************************************/
1589 #define half(z1, z2) ((z1+z2)/2.0)
1592 /* iterative version */
1594 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
1597 register double xmid
, ymid
;
1598 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1601 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
1603 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
1604 xmid
= (double)half(x2
, x3
);
1605 ymid
= (double)half(y2
, y3
);
1606 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
1607 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
1608 wx_spline_add_point((double)wx_round(x1
), (double)wx_round(y1
));
1609 wx_spline_add_point((double)wx_round(xmid
), (double)wx_round(ymid
));
1611 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
1612 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
1613 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
1614 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
1620 /* utilities used by spline drawing routines */
1623 typedef struct wx_spline_stack_struct
{
1624 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1628 #define SPLINE_STACK_DEPTH 20
1629 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
1630 static Stack
*wx_stack_top
;
1631 static int wx_stack_count
;
1633 void wx_clear_stack()
1635 wx_stack_top
= wx_spline_stack
;
1639 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
1641 wx_stack_top
->x1
= x1
;
1642 wx_stack_top
->y1
= y1
;
1643 wx_stack_top
->x2
= x2
;
1644 wx_stack_top
->y2
= y2
;
1645 wx_stack_top
->x3
= x3
;
1646 wx_stack_top
->y3
= y3
;
1647 wx_stack_top
->x4
= x4
;
1648 wx_stack_top
->y4
= y4
;
1653 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
1654 double *x3
, double *y3
, double *x4
, double *y4
)
1656 if (wx_stack_count
== 0)
1660 *x1
= wx_stack_top
->x1
;
1661 *y1
= wx_stack_top
->y1
;
1662 *x2
= wx_stack_top
->x2
;
1663 *y2
= wx_stack_top
->y2
;
1664 *x3
= wx_stack_top
->x3
;
1665 *y3
= wx_stack_top
->y3
;
1666 *x4
= wx_stack_top
->x4
;
1667 *y4
= wx_stack_top
->y4
;
1671 static bool wx_spline_add_point(double x
, double y
)
1673 wxPoint
*point
= new wxPoint
;
1676 wx_spline_point_list
.Append((wxObject
*)point
);
1680 static void wx_spline_draw_point_array(wxDC
*dc
)
1682 dc
->DrawLines(&wx_spline_point_list
, 0, 0);
1683 wxNode
*node
= wx_spline_point_list
.First();
1686 wxPoint
*point
= (wxPoint
*)node
->Data();
1689 node
= wx_spline_point_list
.First();
1693 wxSpline::wxSpline(wxList
*list
)
1698 wxSpline::~wxSpline()
1702 void wxSpline::DeletePoints()
1704 for(wxNode
*node
= points
->First(); node
; node
= points
->First())
1706 wxPoint
*point
= (wxPoint
*)node
->Data();
1714 #endif // wxUSE_SPLINES