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
689 // GRG: now this works regardless of what the source bitmap
690 // contains in the area which is to be transparent.
692 bool ok
= ::MaskBlt(GetHdc(), x
, y
, width
, height
,
695 MAKEROP4(SRCCOPY
, 0x00AA0029)) != 0;
701 // VZ: this is incorrect, Blit() doesn't (and can't) draw
702 // transparently, but it's still better than nothing at all
703 // GRG: Blit() *should* draw transparently when there is a mask
705 // Rather than reproduce wxDC::Blit, let's do it at the wxWin API level
707 memDC
.SelectObject(bmp
);
709 Blit(x
, y
, width
, height
, &memDC
, 0, 0, wxCOPY
, useMask
);
711 memDC
.SelectObject(wxNullBitmap
);
714 else // no mask, just use BitBlt()
717 HDC memdc
= ::CreateCompatibleDC( cdc
);
718 HBITMAP hbitmap
= (HBITMAP
) bmp
.GetHBITMAP( );
720 wxASSERT_MSG( hbitmap
, wxT("bitmap is ok but HBITMAP is NULL?") );
722 COLORREF old_textground
= ::GetTextColor(GetHdc());
723 COLORREF old_background
= ::GetBkColor(GetHdc());
724 if (m_textForegroundColour
.Ok())
726 ::SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel() );
728 if (m_textBackgroundColour
.Ok())
730 ::SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
733 ::SelectObject( memdc
, hbitmap
);
734 ::BitBlt( cdc
, x
, y
, width
, height
, memdc
, 0, 0, SRCCOPY
);
737 ::SetTextColor(GetHdc(), old_textground
);
738 ::SetBkColor(GetHdc(), old_background
);
742 void wxDC::DoDrawText(const wxString
& text
, wxCoord x
, wxCoord y
)
744 DrawAnyText(text
, x
, y
);
746 // update the bounding box
747 CalcBoundingBox(x
, y
);
750 GetTextExtent(text
, &w
, &h
);
751 CalcBoundingBox(x
+ w
, y
+ h
);
754 void wxDC::DrawAnyText(const wxString
& text
, wxCoord x
, wxCoord y
)
756 // prepare for drawing the text
757 if ( m_textForegroundColour
.Ok() )
758 SetTextColor(GetHdc(), m_textForegroundColour
.GetPixel());
760 DWORD old_background
= 0;
761 if ( m_textBackgroundColour
.Ok() )
763 old_background
= SetBkColor(GetHdc(), m_textBackgroundColour
.GetPixel() );
766 SetBkMode(GetHdc(), m_backgroundMode
== wxTRANSPARENT
? TRANSPARENT
769 if ( ::TextOut(GetHdc(), XLOG2DEV(x
), YLOG2DEV(y
),
770 text
.c_str(), text
.length()) == 0 )
772 wxLogLastError("TextOut");
775 // restore the old parameters (text foreground colour may be left because
776 // it never is set to anything else, but background should remain
777 // transparent even if we just drew an opaque string)
778 if ( m_textBackgroundColour
.Ok() )
779 (void)SetBkColor(GetHdc(), old_background
);
781 SetBkMode(GetHdc(), TRANSPARENT
);
784 void wxDC::DoDrawRotatedText(const wxString
& text
,
785 wxCoord x
, wxCoord y
,
788 // we test that we have some font because otherwise we should still use the
789 // "else" part below to avoid that DrawRotatedText(angle = 180) and
790 // DrawRotatedText(angle = 0) use different fonts (we can't use the default
791 // font for drawing rotated fonts unfortunately)
792 if ( (angle
== 0.0) && m_font
.Ok() )
794 DoDrawText(text
, x
, y
);
798 // NB: don't take DEFAULT_GUI_FONT because it's not TrueType and so
799 // can't have non zero orientation/escapement
800 wxFont font
= m_font
.Ok() ? m_font
: *wxNORMAL_FONT
;
801 HFONT hfont
= (HFONT
)font
.GetResourceHandle();
803 if ( ::GetObject(hfont
, sizeof(lf
), &lf
) == 0 )
805 wxLogLastError("GetObject(hfont)");
808 // GDI wants the angle in tenth of degree
809 long angle10
= (long)(angle
* 10);
810 lf
.lfEscapement
= angle10
;
811 lf
. lfOrientation
= angle10
;
813 hfont
= ::CreateFontIndirect(&lf
);
816 wxLogLastError("CreateFont");
820 HFONT hfontOld
= (HFONT
)::SelectObject(GetHdc(), hfont
);
822 DrawAnyText(text
, x
, y
);
824 (void)::SelectObject(GetHdc(), hfontOld
);
825 (void)::DeleteObject(hfont
);
828 // call the bounding box by adding all four vertices of the rectangle
829 // containing the text to it (simpler and probably not slower than
830 // determining which of them is really topmost/leftmost/...)
832 GetTextExtent(text
, &w
, &h
);
834 double rad
= DegToRad(angle
);
836 // "upper left" and "upper right"
837 CalcBoundingBox(x
, y
);
838 CalcBoundingBox(x
+ w
*cos(rad
), y
- h
*sin(rad
));
840 // "bottom left" and "bottom right"
841 x
+= (wxCoord
)(h
*sin(rad
));
842 y
+= (wxCoord
)(h
*cos(rad
));
843 CalcBoundingBox(x
, y
);
844 CalcBoundingBox(x
+ h
*sin(rad
), y
+ h
*cos(rad
));
848 // ---------------------------------------------------------------------------
850 // ---------------------------------------------------------------------------
852 void wxDC::SetPalette(const wxPalette
& palette
)
854 // Set the old object temporarily, in case the assignment deletes an object
855 // that's not yet selected out.
858 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
866 // Setting a NULL colourmap is a way of restoring
867 // the original colourmap
870 ::SelectPalette(GetHdc(), (HPALETTE
) m_oldPalette
, TRUE
);
877 if (m_palette
.Ok() && m_palette
.GetHPALETTE())
879 HPALETTE oldPal
= ::SelectPalette(GetHdc(), (HPALETTE
) m_palette
.GetHPALETTE(), TRUE
);
881 m_oldPalette
= (WXHPALETTE
) oldPal
;
883 ::RealizePalette(GetHdc());
887 void wxDC::SetFont(const wxFont
& the_font
)
889 // Set the old object temporarily, in case the assignment deletes an object
890 // that's not yet selected out.
893 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
902 ::SelectObject(GetHdc(), (HFONT
) m_oldFont
);
906 if (m_font
.Ok() && m_font
.GetResourceHandle())
908 HFONT f
= (HFONT
) ::SelectObject(GetHdc(), (HFONT
) m_font
.GetResourceHandle());
909 if (f
== (HFONT
) NULL
)
911 wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
914 m_oldFont
= (WXHFONT
) f
;
918 void wxDC::SetPen(const wxPen
& pen
)
920 // Set the old object temporarily, in case the assignment deletes an object
921 // that's not yet selected out.
924 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
933 ::SelectObject(GetHdc(), (HPEN
) m_oldPen
);
939 if (m_pen
.GetResourceHandle())
941 HPEN p
= (HPEN
) ::SelectObject(GetHdc(), (HPEN
)m_pen
.GetResourceHandle());
943 m_oldPen
= (WXHPEN
) p
;
948 void wxDC::SetBrush(const wxBrush
& brush
)
950 // Set the old object temporarily, in case the assignment deletes an object
951 // that's not yet selected out.
954 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
963 ::SelectObject(GetHdc(), (HBRUSH
) m_oldBrush
);
969 if (m_brush
.GetResourceHandle())
972 b
= (HBRUSH
) ::SelectObject(GetHdc(), (HBRUSH
)m_brush
.GetResourceHandle());
974 m_oldBrush
= (WXHBRUSH
) b
;
979 void wxDC::SetBackground(const wxBrush
& brush
)
981 m_backgroundBrush
= brush
;
983 if (!m_backgroundBrush
.Ok())
988 bool customColours
= TRUE
;
989 // If we haven't specified wxUSER_COLOURS, don't allow the panel/dialog box to
990 // change background colours from the control-panel specified colours.
991 if (m_canvas
->IsKindOf(CLASSINFO(wxWindow
)) && ((m_canvas
->GetWindowStyleFlag() & wxUSER_COLOURS
) != wxUSER_COLOURS
))
992 customColours
= FALSE
;
996 if (m_backgroundBrush
.GetStyle()==wxTRANSPARENT
)
998 m_canvas
->SetTransparent(TRUE
);
1002 // New behaviour, 10/2/99: setting the background brush of a DC
1003 // doesn't affect the window background colour. However,
1004 // I'm leaving in the transparency setting because it's needed by
1005 // various controls (e.g. wxStaticText) to determine whether to draw
1006 // transparently or not. TODO: maybe this should be a new function
1007 // wxWindow::SetTransparency(). Should that apply to the child itself, or the
1009 // m_canvas->SetBackgroundColour(m_backgroundBrush.GetColour());
1010 m_canvas
->SetTransparent(FALSE
);
1014 COLORREF new_color
= m_backgroundBrush
.GetColour().GetPixel();
1016 (void)SetBkColor(GetHdc(), new_color
);
1020 void wxDC::SetBackgroundMode(int mode
)
1022 m_backgroundMode
= mode
;
1024 // SetBackgroundColour now only refers to text background
1025 // and m_backgroundMode is used there
1028 if (m_backgroundMode == wxTRANSPARENT)
1029 ::SetBkMode(GetHdc(), TRANSPARENT);
1031 ::SetBkMode(GetHdc(), OPAQUE);
1035 void wxDC::SetLogicalFunction(int function
)
1037 m_logicalFunction
= function
;
1042 void wxDC::SetRop(WXHDC dc
)
1044 if ( !dc
|| m_logicalFunction
< 0 )
1049 switch (m_logicalFunction
)
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 wxCLEAR
: rop
= R2_WHITE
; break;
1056 case wxSET
: rop
= R2_BLACK
; break;
1057 case wxOR_INVERT
: rop
= R2_MERGENOTPEN
; break;
1058 case wxAND
: rop
= R2_MASKPEN
; break;
1059 case wxOR
: rop
= R2_MERGEPEN
; break;
1060 case wxEQUIV
: rop
= R2_NOTXORPEN
; break;
1061 case wxNAND
: rop
= R2_NOTMASKPEN
; break;
1062 case wxAND_INVERT
: rop
= R2_MASKNOTPEN
; break;
1063 case wxCOPY
: rop
= R2_COPYPEN
; break;
1064 case wxNO_OP
: rop
= R2_NOP
; break;
1065 case wxSRC_INVERT
: rop
= R2_NOTCOPYPEN
; break;
1066 case wxNOR
: rop
= R2_NOTMERGEPEN
; 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
= 0x00AA0029; break;
1338 case wxSRC_INVERT
: dwRop
= NOTSRCCOPY
; break;
1339 case wxNOR
: dwRop
= NOTSRCCOPY
; break;
1341 wxFAIL_MSG( wxT("unsupported logical function") );
1350 // prepare the mask bitmap
1351 HBITMAP hbmpMask
= wxInvertMask((HBITMAP
)mask
->GetMaskBitmap());
1353 // select the correct brush: the current one by default, background one
1358 hbrNew
= (HBRUSH
)m_brush
.GetResourceHandle();
1360 else if ( m_backgroundBrush
.Ok() )
1362 hbrNew
= (HBRUSH
)m_backgroundBrush
.GetResourceHandle();
1369 HGDIOBJ hbrOld
= hbrNew
? ::SelectObject(GetHdc(), hbrNew
) : 0;
1371 // we want the part of the image corresponding to the mask to be
1372 // transparent, i.e. do PATCOPY there and apply dwRop elsewhere
1374 // GRG: PATCOPY is not transparent, as can be seen when blitting
1375 // over a pattern: the 'transparent' area would be filled
1376 // with the selected colour. We should use NOP instead, or
1377 // do MaskBlt + BitBlt.
1379 success
= ::MaskBlt(GetHdc(), xdest
, ydest
, width
, height
,
1380 GetHdcOf(*source
), xsrc
, ysrc
,
1382 MAKEROP4(0x00AA0029, dwRop
)) != 0;
1386 (void)::SelectObject(GetHdc(), hbrOld
);
1389 ::DeleteObject(hbmpMask
);
1394 // Blit bitmap with mask
1396 // create a temp buffer bitmap and DCs to access it and the mask
1397 HDC dc_mask
= ::CreateCompatibleDC(GetHdcOf(*source
));
1398 HDC dc_buffer
= ::CreateCompatibleDC(GetHdc());
1399 HBITMAP buffer_bmap
= ::CreateCompatibleBitmap(GetHdc(), width
, height
);
1400 ::SelectObject(dc_mask
, (HBITMAP
) mask
->GetMaskBitmap());
1401 ::SelectObject(dc_buffer
, buffer_bmap
);
1403 // copy dest to buffer
1404 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1405 GetHdc(), xdest
, ydest
, SRCCOPY
) )
1407 wxLogLastError("BitBlt");
1410 // copy src to buffer using selected raster op
1411 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1412 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) )
1414 wxLogLastError("BitBlt");
1417 // set masked area in buffer to BLACK (pixel value 0)
1418 COLORREF prevBkCol
= ::SetBkColor(GetHdc(), RGB(255, 255, 255));
1419 COLORREF prevCol
= ::SetTextColor(GetHdc(), RGB(0, 0, 0));
1420 if ( !::BitBlt(dc_buffer
, 0, 0, (int)width
, (int)height
,
1421 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1423 wxLogLastError("BitBlt");
1426 // set unmasked area in dest to BLACK
1427 ::SetBkColor(GetHdc(), RGB(0, 0, 0));
1428 ::SetTextColor(GetHdc(), RGB(255, 255, 255));
1429 if ( !::BitBlt(GetHdc(), xdest
, ydest
, (int)width
, (int)height
,
1430 dc_mask
, xsrc
, ysrc
, SRCAND
) )
1432 wxLogLastError("BitBlt");
1434 ::SetBkColor(GetHdc(), prevBkCol
); // restore colours to original values
1435 ::SetTextColor(GetHdc(), prevCol
);
1437 // OR buffer to dest
1438 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1439 (int)width
, (int)height
,
1440 dc_buffer
, 0, 0, SRCPAINT
) != 0;
1443 wxLogLastError("BitBlt");
1446 // tidy up temporary DCs and bitmap
1447 ::SelectObject(dc_mask
, 0);
1448 ::DeleteDC(dc_mask
);
1449 ::SelectObject(dc_buffer
, 0);
1450 ::DeleteDC(dc_buffer
);
1451 ::DeleteObject(buffer_bmap
);
1454 else // no mask, just BitBlt() it
1456 success
= ::BitBlt(GetHdc(), xdest
, ydest
,
1457 (int)width
, (int)height
,
1458 GetHdcOf(*source
), xsrc
, ysrc
, dwRop
) != 0;
1461 wxLogLastError("BitBlt");
1464 ::SetTextColor(GetHdc(), old_textground
);
1465 ::SetBkColor(GetHdc(), old_background
);
1470 void wxDC::DoGetSize(int *w
, int *h
) const
1472 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZRES
);
1473 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTRES
);
1476 void wxDC::DoGetSizeMM(int *w
, int *h
) const
1478 if ( w
) *w
= ::GetDeviceCaps(GetHdc(), HORZSIZE
);
1479 if ( h
) *h
= ::GetDeviceCaps(GetHdc(), VERTSIZE
);
1482 wxSize
wxDC::GetPPI() const
1484 int x
= ::GetDeviceCaps(GetHdc(), LOGPIXELSX
);
1485 int y
= ::GetDeviceCaps(GetHdc(), LOGPIXELSY
);
1487 return wxSize(x
, y
);
1490 // For use by wxWindows only, unless custom units are required.
1491 void wxDC::SetLogicalScale(double x
, double y
)
1493 m_logicalScaleX
= x
;
1494 m_logicalScaleY
= y
;
1497 #if WXWIN_COMPATIBILITY
1498 void wxDC::DoGetTextExtent(const wxString
& string
, float *x
, float *y
,
1499 float *descent
, float *externalLeading
,
1500 wxFont
*theFont
, bool use16bit
) const
1502 wxCoord x1
, y1
, descent1
, externalLeading1
;
1503 GetTextExtent(string
, & x1
, & y1
, & descent1
, & externalLeading1
, theFont
, use16bit
);
1506 *descent
= descent1
;
1507 if (externalLeading
)
1508 *externalLeading
= externalLeading1
;
1512 // ---------------------------------------------------------------------------
1513 // spline drawing code
1514 // ---------------------------------------------------------------------------
1518 class wxSpline
: public wxObject
1524 wxSpline(wxList
*list
);
1525 void DeletePoints();
1527 // Doesn't delete points
1531 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
);
1533 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
1534 double a3
, double b3
, double a4
, double b4
);
1535 void wx_clear_stack();
1536 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
1537 double *y3
, double *x4
, double *y4
);
1538 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
1539 double x4
, double y4
);
1540 static bool wx_spline_add_point(double x
, double y
);
1541 static void wx_spline_draw_point_array(wxDC
*dc
);
1542 wxSpline
*wx_make_spline(int x1
, int y1
, int x2
, int y2
, int x3
, int y3
);
1544 void wxDC::DoDrawSpline(wxList
*list
)
1546 wxSpline
spline(list
);
1548 wx_draw_open_spline(this, &spline
);
1551 wxList wx_spline_point_list
;
1553 void wx_draw_open_spline(wxDC
*dc
, wxSpline
*spline
)
1556 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
1557 double x1
, y1
, x2
, y2
;
1559 wxNode
*node
= spline
->points
->First();
1560 p
= (wxPoint
*)node
->Data();
1565 node
= node
->Next();
1566 p
= (wxPoint
*)node
->Data();
1570 cx1
= (double)((x1
+ x2
) / 2);
1571 cy1
= (double)((y1
+ y2
) / 2);
1572 cx2
= (double)((cx1
+ x2
) / 2);
1573 cy2
= (double)((cy1
+ y2
) / 2);
1575 wx_spline_add_point(x1
, y1
);
1577 while ((node
= node
->Next()) != NULL
)
1579 p
= (wxPoint
*)node
->Data();
1584 cx4
= (double)(x1
+ x2
) / 2;
1585 cy4
= (double)(y1
+ y2
) / 2;
1586 cx3
= (double)(x1
+ cx4
) / 2;
1587 cy3
= (double)(y1
+ cy4
) / 2;
1589 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
1593 cx2
= (double)(cx1
+ x2
) / 2;
1594 cy2
= (double)(cy1
+ y2
) / 2;
1597 wx_spline_add_point((double)wx_round(cx1
), (double)wx_round(cy1
));
1598 wx_spline_add_point(x2
, y2
);
1600 wx_spline_draw_point_array(dc
);
1604 /********************* CURVES FOR SPLINES *****************************
1606 The following spline drawing routine is from
1608 "An Algorithm for High-Speed Curve Generation"
1609 by George Merrill Chaikin,
1610 Computer Graphics and Image Processing, 3, Academic Press,
1615 "On Chaikin's Algorithm" by R. F. Riesenfeld,
1616 Computer Graphics and Image Processing, 4, Academic Press,
1619 ***********************************************************************/
1621 #define half(z1, z2) ((z1+z2)/2.0)
1624 /* iterative version */
1626 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
1629 register double xmid
, ymid
;
1630 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1633 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
1635 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
1636 xmid
= (double)half(x2
, x3
);
1637 ymid
= (double)half(y2
, y3
);
1638 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
1639 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
1640 wx_spline_add_point((double)wx_round(x1
), (double)wx_round(y1
));
1641 wx_spline_add_point((double)wx_round(xmid
), (double)wx_round(ymid
));
1643 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
1644 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
1645 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
1646 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
1652 /* utilities used by spline drawing routines */
1655 typedef struct wx_spline_stack_struct
{
1656 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
1660 #define SPLINE_STACK_DEPTH 20
1661 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
1662 static Stack
*wx_stack_top
;
1663 static int wx_stack_count
;
1665 void wx_clear_stack()
1667 wx_stack_top
= wx_spline_stack
;
1671 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
1673 wx_stack_top
->x1
= x1
;
1674 wx_stack_top
->y1
= y1
;
1675 wx_stack_top
->x2
= x2
;
1676 wx_stack_top
->y2
= y2
;
1677 wx_stack_top
->x3
= x3
;
1678 wx_stack_top
->y3
= y3
;
1679 wx_stack_top
->x4
= x4
;
1680 wx_stack_top
->y4
= y4
;
1685 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
1686 double *x3
, double *y3
, double *x4
, double *y4
)
1688 if (wx_stack_count
== 0)
1692 *x1
= wx_stack_top
->x1
;
1693 *y1
= wx_stack_top
->y1
;
1694 *x2
= wx_stack_top
->x2
;
1695 *y2
= wx_stack_top
->y2
;
1696 *x3
= wx_stack_top
->x3
;
1697 *y3
= wx_stack_top
->y3
;
1698 *x4
= wx_stack_top
->x4
;
1699 *y4
= wx_stack_top
->y4
;
1703 static bool wx_spline_add_point(double x
, double y
)
1705 wxPoint
*point
= new wxPoint
;
1708 wx_spline_point_list
.Append((wxObject
*)point
);
1712 static void wx_spline_draw_point_array(wxDC
*dc
)
1714 dc
->DrawLines(&wx_spline_point_list
, 0, 0);
1715 wxNode
*node
= wx_spline_point_list
.First();
1718 wxPoint
*point
= (wxPoint
*)node
->Data();
1721 node
= wx_spline_point_list
.First();
1725 wxSpline::wxSpline(wxList
*list
)
1730 wxSpline::~wxSpline()
1734 void wxSpline::DeletePoints()
1736 for(wxNode
*node
= points
->First(); node
; node
= points
->First())
1738 wxPoint
*point
= (wxPoint
*)node
->Data();
1746 #endif // wxUSE_SPLINES