1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/dcbase.cpp
3 // Purpose: generic methods of the wxDC Class
4 // Author: Vadim Zeitlin
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
28 #include "wx/dcbuffer.h" // for IMPLEMENT_DYNAMIC_CLASS
34 // bool wxDCBase::sm_cacheing = false;
36 IMPLEMENT_ABSTRACT_CLASS(wxDCBase
, wxObject
)
38 // ============================================================================
40 // ============================================================================
42 IMPLEMENT_DYNAMIC_CLASS(wxBufferedDC
, wxMemoryDC
)
43 IMPLEMENT_ABSTRACT_CLASS(wxBufferedPaintDC
, wxBufferedDC
)
45 #if WXWIN_COMPATIBILITY_2_6
46 void wxDCBase::BeginDrawing()
50 void wxDCBase::EndDrawing()
53 #endif // WXWIN_COMPATIBILITY_2_6
55 // ----------------------------------------------------------------------------
57 // ----------------------------------------------------------------------------
59 void wxDCBase::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
60 wxCoord width
, wxCoord height
)
62 wxCHECK_RET( Ok(), wxT("invalid window dc") );
64 wxCoord x2
= x1
+ width
,
67 // the pen width is calibrated to give 3 for width == height == 10
68 wxDCPenChanger
pen((wxDC
&)*this,
69 wxPen(GetTextForeground(), (width
+ height
+ 1)/7));
71 // we're drawing a scaled version of wx/generic/tick.xpm here
72 wxCoord x3
= x1
+ (4*width
) / 10, // x of the tick bottom
73 y3
= y1
+ height
/ 2; // y of the left tick branch
74 DoDrawLine(x1
, y3
, x3
, y2
);
75 DoDrawLine(x3
, y2
, x2
, y1
);
77 CalcBoundingBox(x1
, y1
);
78 CalcBoundingBox(x2
, y2
);
81 // ----------------------------------------------------------------------------
83 // ----------------------------------------------------------------------------
85 void wxDCBase::DrawLines(const wxList
*list
, wxCoord xoffset
, wxCoord yoffset
)
87 int n
= list
->GetCount();
88 wxPoint
*points
= new wxPoint
[n
];
91 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
93 wxPoint
*point
= (wxPoint
*)node
->GetData();
94 points
[i
].x
= point
->x
;
95 points
[i
].y
= point
->y
;
98 DoDrawLines(n
, points
, xoffset
, yoffset
);
104 void wxDCBase::DrawPolygon(const wxList
*list
,
105 wxCoord xoffset
, wxCoord yoffset
,
108 int n
= list
->GetCount();
109 wxPoint
*points
= new wxPoint
[n
];
112 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
114 wxPoint
*point
= (wxPoint
*)node
->GetData();
115 points
[i
].x
= point
->x
;
116 points
[i
].y
= point
->y
;
119 DoDrawPolygon(n
, points
, xoffset
, yoffset
, fillStyle
);
125 wxDCBase::DoDrawPolyPolygon(int n
,
128 wxCoord xoffset
, wxCoord yoffset
,
133 DoDrawPolygon(count
[0], points
, xoffset
, yoffset
, fillStyle
);
141 for (i
= j
= lastOfs
= 0; i
< n
; i
++)
146 pts
= new wxPoint
[j
+n
-1];
147 for (i
= 0; i
< j
; i
++)
149 for (i
= 2; i
<= n
; i
++)
151 lastOfs
-= count
[n
-i
];
152 pts
[j
++] = pts
[lastOfs
];
156 SetPen(wxPen(*wxBLACK
, 0, wxTRANSPARENT
));
157 DoDrawPolygon(j
, pts
, xoffset
, yoffset
, fillStyle
);
159 for (i
= j
= 0; i
< n
; i
++)
161 DoDrawLines(count
[i
], pts
+j
, xoffset
, yoffset
);
167 // ----------------------------------------------------------------------------
169 // ----------------------------------------------------------------------------
173 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
174 void wxDCBase::DrawSpline(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
, wxCoord x3
, wxCoord y3
)
178 wxPoint
*point1
= new wxPoint
;
179 point1
->x
= x1
; point1
->y
= y1
;
180 point_list
.Append((wxObject
*)point1
);
182 wxPoint
*point2
= new wxPoint
;
183 point2
->x
= x2
; point2
->y
= y2
;
184 point_list
.Append((wxObject
*)point2
);
186 wxPoint
*point3
= new wxPoint
;
187 point3
->x
= x3
; point3
->y
= y3
;
188 point_list
.Append((wxObject
*)point3
);
190 DrawSpline(&point_list
);
192 for( wxList::compatibility_iterator node
= point_list
.GetFirst(); node
; node
= node
->GetNext() )
194 wxPoint
*p
= (wxPoint
*)node
->GetData();
199 void wxDCBase::DrawSpline(int n
, wxPoint points
[])
202 for (int i
=0; i
< n
; i
++)
204 list
.Append((wxObject
*)&points
[i
]);
210 // ----------------------------------- spline code ----------------------------------------
212 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
213 double a3
, double b3
, double a4
, double b4
);
214 void wx_clear_stack();
215 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
216 double *y3
, double *x4
, double *y4
);
217 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
218 double x4
, double y4
);
219 static bool wx_spline_add_point(double x
, double y
);
220 static void wx_spline_draw_point_array(wxDCBase
*dc
);
222 wxList wx_spline_point_list
;
224 #define half(z1, z2) ((z1+z2)/2.0)
227 /* iterative version */
229 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
232 register double xmid
, ymid
;
233 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
236 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
238 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
239 xmid
= (double)half(x2
, x3
);
240 ymid
= (double)half(y2
, y3
);
241 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
242 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
243 wx_spline_add_point( x1
, y1
);
244 wx_spline_add_point( xmid
, ymid
);
246 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
247 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
248 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
249 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
254 /* utilities used by spline drawing routines */
256 typedef struct wx_spline_stack_struct
{
257 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
260 #define SPLINE_STACK_DEPTH 20
261 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
262 static Stack
*wx_stack_top
;
263 static int wx_stack_count
;
265 void wx_clear_stack()
267 wx_stack_top
= wx_spline_stack
;
271 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
273 wx_stack_top
->x1
= x1
;
274 wx_stack_top
->y1
= y1
;
275 wx_stack_top
->x2
= x2
;
276 wx_stack_top
->y2
= y2
;
277 wx_stack_top
->x3
= x3
;
278 wx_stack_top
->y3
= y3
;
279 wx_stack_top
->x4
= x4
;
280 wx_stack_top
->y4
= y4
;
285 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
286 double *x3
, double *y3
, double *x4
, double *y4
)
288 if (wx_stack_count
== 0)
292 *x1
= wx_stack_top
->x1
;
293 *y1
= wx_stack_top
->y1
;
294 *x2
= wx_stack_top
->x2
;
295 *y2
= wx_stack_top
->y2
;
296 *x3
= wx_stack_top
->x3
;
297 *y3
= wx_stack_top
->y3
;
298 *x4
= wx_stack_top
->x4
;
299 *y4
= wx_stack_top
->y4
;
303 static bool wx_spline_add_point(double x
, double y
)
305 wxPoint
*point
= new wxPoint
;
308 wx_spline_point_list
.Append((wxObject
*)point
);
312 static void wx_spline_draw_point_array(wxDCBase
*dc
)
314 dc
->DrawLines(&wx_spline_point_list
, 0, 0 );
315 wxList::compatibility_iterator node
= wx_spline_point_list
.GetFirst();
318 wxPoint
*point
= (wxPoint
*)node
->GetData();
320 wx_spline_point_list
.Erase(node
);
321 node
= wx_spline_point_list
.GetFirst();
325 void wxDCBase::DoDrawSpline( wxList
*points
)
327 wxCHECK_RET( Ok(), wxT("invalid window dc") );
330 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
331 double x1
, y1
, x2
, y2
;
333 wxList::compatibility_iterator node
= points
->GetFirst();
338 p
= (wxPoint
*)node
->GetData();
343 node
= node
->GetNext();
344 p
= (wxPoint
*)node
->GetData();
348 cx1
= (double)((x1
+ x2
) / 2);
349 cy1
= (double)((y1
+ y2
) / 2);
350 cx2
= (double)((cx1
+ x2
) / 2);
351 cy2
= (double)((cy1
+ y2
) / 2);
353 wx_spline_add_point(x1
, y1
);
355 while ((node
= node
->GetNext())
361 p
= (wxPoint
*)node
->GetData();
366 cx4
= (double)(x1
+ x2
) / 2;
367 cy4
= (double)(y1
+ y2
) / 2;
368 cx3
= (double)(x1
+ cx4
) / 2;
369 cy3
= (double)(y1
+ cy4
) / 2;
371 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
375 cx2
= (double)(cx1
+ x2
) / 2;
376 cy2
= (double)(cy1
+ y2
) / 2;
379 wx_spline_add_point( cx1
, cy1
);
380 wx_spline_add_point( x2
, y2
);
382 wx_spline_draw_point_array( this );
385 #endif // wxUSE_SPLINES
387 // ----------------------------------------------------------------------------
388 // Partial Text Extents
389 // ----------------------------------------------------------------------------
392 // Each element of the widths array will be the width of the string up to and
393 // including the corresponding character in text. This is the generic
394 // implementation, the port-specific classes should do this with native APIs
395 // if available and if faster. Note: pango_layout_index_to_pos is much slower
396 // than calling GetTextExtent!!
403 FontWidthCache() : m_scaleX(1), m_widths(NULL
) { }
404 ~FontWidthCache() { delete []m_widths
; }
409 m_widths
= new int[FWC_SIZE
];
411 memset(m_widths
, 0, sizeof(int)*FWC_SIZE
);
419 static FontWidthCache s_fontWidthCache
;
421 bool wxDCBase::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
425 const size_t len
= text
.length();
429 // reset the cache if font or horizontal scale have changed
430 if ( !s_fontWidthCache
.m_widths
||
431 !wxIsSameDouble(s_fontWidthCache
.m_scaleX
, m_scaleX
) ||
432 (s_fontWidthCache
.m_font
!= GetFont()) )
434 s_fontWidthCache
.Reset();
435 s_fontWidthCache
.m_font
= GetFont();
436 s_fontWidthCache
.m_scaleX
= m_scaleX
;
439 // Calculate the position of each character based on the widths of
440 // the previous characters
442 for ( size_t i
= 0; i
< len
; i
++ )
444 const wxChar c
= text
[i
];
445 unsigned int c_int
= (unsigned int)c
;
447 if ((c_int
< FWC_SIZE
) && (s_fontWidthCache
.m_widths
[c_int
] != 0))
449 w
= s_fontWidthCache
.m_widths
[c_int
];
453 GetTextExtent(c
, &w
, &h
);
454 if (c_int
< FWC_SIZE
)
455 s_fontWidthCache
.m_widths
[c_int
] = w
;
459 widths
[i
] = totalWidth
;
466 // ----------------------------------------------------------------------------
467 // enhanced text drawing
468 // ----------------------------------------------------------------------------
470 void wxDCBase::GetMultiLineTextExtent(const wxString
& text
,
476 wxCoord widthTextMax
= 0, widthLine
,
477 heightTextTotal
= 0, heightLineDefault
= 0, heightLine
= 0;
480 for ( const wxChar
*pc
= text
; ; pc
++ )
482 if ( *pc
== _T('\n') || *pc
== _T('\0') )
484 if ( curLine
.empty() )
486 // we can't use GetTextExtent - it will return 0 for both width
487 // and height and an empty line should count in height
490 // assume that this line has the same height as the previous
492 if ( !heightLineDefault
)
493 heightLineDefault
= heightLine
;
495 if ( !heightLineDefault
)
497 // but we don't know it yet - choose something reasonable
498 GetTextExtent(_T("W"), NULL
, &heightLineDefault
,
502 heightTextTotal
+= heightLineDefault
;
506 GetTextExtent(curLine
, &widthLine
, &heightLine
,
508 if ( widthLine
> widthTextMax
)
509 widthTextMax
= widthLine
;
510 heightTextTotal
+= heightLine
;
513 if ( *pc
== _T('\n') )
532 *y
= heightTextTotal
;
537 void wxDCBase::DrawLabel(const wxString
& text
,
538 const wxBitmap
& bitmap
,
542 wxRect
*rectBounding
)
544 // find the text position
545 wxCoord widthText
, heightText
, heightLine
;
546 GetMultiLineTextExtent(text
, &widthText
, &heightText
, &heightLine
);
548 wxCoord width
, height
;
551 width
= widthText
+ bitmap
.GetWidth();
552 height
= bitmap
.GetHeight();
561 if ( alignment
& wxALIGN_RIGHT
)
563 x
= rect
.GetRight() - width
;
565 else if ( alignment
& wxALIGN_CENTRE_HORIZONTAL
)
567 x
= (rect
.GetLeft() + rect
.GetRight() + 1 - width
) / 2;
569 else // alignment & wxALIGN_LEFT
574 if ( alignment
& wxALIGN_BOTTOM
)
576 y
= rect
.GetBottom() - height
;
578 else if ( alignment
& wxALIGN_CENTRE_VERTICAL
)
580 y
= (rect
.GetTop() + rect
.GetBottom() + 1 - height
) / 2;
582 else // alignment & wxALIGN_TOP
587 // draw the bitmap first
593 DrawBitmap(bitmap
, x
, y
, true /* use mask */);
595 wxCoord offset
= bitmap
.GetWidth() + 4;
599 y
+= (height
- heightText
) / 2;
602 // we will draw the underscore under the accel char later
603 wxCoord startUnderscore
= 0,
607 // split the string into lines and draw each of them separately
609 for ( const wxChar
*pc
= text
; ; pc
++ )
611 if ( *pc
== _T('\n') || *pc
== _T('\0') )
613 int xRealStart
= x
; // init it here to avoid compielr warnings
615 if ( !curLine
.empty() )
617 // NB: can't test for !(alignment & wxALIGN_LEFT) because
619 if ( alignment
& (wxALIGN_RIGHT
| wxALIGN_CENTRE_HORIZONTAL
) )
622 GetTextExtent(curLine
, &widthLine
, NULL
);
624 if ( alignment
& wxALIGN_RIGHT
)
626 xRealStart
+= width
- widthLine
;
628 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
630 xRealStart
+= (width
- widthLine
) / 2;
633 //else: left aligned, nothing to do
635 DrawText(curLine
, xRealStart
, y
);
640 // do we have underscore in this line? we can check yUnderscore
641 // because it is set below to just y + heightLine if we do
642 if ( y
== yUnderscore
)
644 // adjust the horz positions to account for the shift
645 startUnderscore
+= xRealStart
;
646 endUnderscore
+= xRealStart
;
649 if ( *pc
== _T('\0') )
654 else // not end of line
656 if ( pc
- text
.c_str() == indexAccel
)
658 // remeber to draw underscore here
659 GetTextExtent(curLine
, &startUnderscore
, NULL
);
661 GetTextExtent(curLine
, &endUnderscore
, NULL
);
663 yUnderscore
= y
+ heightLine
;
672 // draw the underscore if found
673 if ( startUnderscore
!= endUnderscore
)
675 // it should be of the same colour as text
676 SetPen(wxPen(GetTextForeground(), 0, wxSOLID
));
680 DrawLine(startUnderscore
, yUnderscore
, endUnderscore
, yUnderscore
);
683 // return bounding rect if requested
686 *rectBounding
= wxRect(x
, y
- heightText
, widthText
, heightText
);
689 CalcBoundingBox(x0
, y0
);
690 CalcBoundingBox(x0
+ width0
, y0
+ height
);
694 void wxDCBase::DoGradientFillLinear(const wxRect
& rect
,
695 const wxColour
& initialColour
,
696 const wxColour
& destColour
,
697 wxDirection nDirection
)
700 wxPen oldPen
= m_pen
;
701 wxBrush oldBrush
= m_brush
;
703 wxUint8 nR1
= initialColour
.Red();
704 wxUint8 nG1
= initialColour
.Green();
705 wxUint8 nB1
= initialColour
.Blue();
706 wxUint8 nR2
= destColour
.Red();
707 wxUint8 nG2
= destColour
.Green();
708 wxUint8 nB2
= destColour
.Blue();
711 if ( nDirection
== wxEAST
|| nDirection
== wxWEST
)
713 wxInt32 x
= rect
.GetWidth();
714 wxInt32 w
= x
; // width of area to shade
715 wxInt32 xDelta
= w
/256; // height of one shade bend
723 nR
= nR1
- (nR1
-nR2
)*(w
-x
)/w
;
725 nR
= nR1
+ (nR2
-nR1
)*(w
-x
)/w
;
728 nG
= nG1
- (nG1
-nG2
)*(w
-x
)/w
;
730 nG
= nG1
+ (nG2
-nG1
)*(w
-x
)/w
;
733 nB
= nB1
- (nB1
-nB2
)*(w
-x
)/w
;
735 nB
= nB1
+ (nB2
-nB1
)*(w
-x
)/w
;
737 wxColour
colour(nR
,nG
,nB
);
738 SetPen(wxPen(colour
, 1, wxSOLID
));
739 SetBrush(wxBrush(colour
));
740 if(nDirection
== wxEAST
)
741 DrawRectangle(rect
.GetRight()-x
-xDelta
, rect
.GetTop(),
742 xDelta
, rect
.GetHeight());
743 else //nDirection == wxWEST
744 DrawRectangle(rect
.GetLeft()+x
, rect
.GetTop(),
745 xDelta
, rect
.GetHeight());
748 else // nDirection == wxNORTH || nDirection == wxSOUTH
750 wxInt32 y
= rect
.GetHeight();
751 wxInt32 w
= y
; // height of area to shade
752 wxInt32 yDelta
= w
/255; // height of one shade bend
760 nR
= nR1
- (nR1
-nR2
)*(w
-y
)/w
;
762 nR
= nR1
+ (nR2
-nR1
)*(w
-y
)/w
;
765 nG
= nG1
- (nG1
-nG2
)*(w
-y
)/w
;
767 nG
= nG1
+ (nG2
-nG1
)*(w
-y
)/w
;
770 nB
= nB1
- (nB1
-nB2
)*(w
-y
)/w
;
772 nB
= nB1
+ (nB2
-nB1
)*(w
-y
)/w
;
774 wxColour
colour(nR
,nG
,nB
);
775 SetPen(wxPen(colour
, 1, wxSOLID
));
776 SetBrush(wxBrush(colour
));
777 if(nDirection
== wxNORTH
)
778 DrawRectangle(rect
.GetLeft(), rect
.GetTop()+y
,
779 rect
.GetWidth(), yDelta
);
780 else //nDirection == wxSOUTH
781 DrawRectangle(rect
.GetLeft(), rect
.GetBottom()-y
-yDelta
,
782 rect
.GetWidth(), yDelta
);
790 void wxDCBase::DoGradientFillConcentric(const wxRect
& rect
,
791 const wxColour
& initialColour
,
792 const wxColour
& destColour
,
793 const wxPoint
& circleCenter
)
795 //save the old pen color
796 wxColour oldPenColour
= m_pen
.GetColour();
798 wxUint8 nR1
= destColour
.Red();
799 wxUint8 nG1
= destColour
.Green();
800 wxUint8 nB1
= destColour
.Blue();
801 wxUint8 nR2
= initialColour
.Red();
802 wxUint8 nG2
= initialColour
.Green();
803 wxUint8 nB2
= initialColour
.Blue();
808 wxInt32 cx
= rect
.GetWidth() / 2;
809 wxInt32 cy
= rect
.GetHeight() / 2;
817 wxInt32 nCircleOffX
= circleCenter
.x
- (rect
.GetWidth() / 2);
818 wxInt32 nCircleOffY
= circleCenter
.y
- (rect
.GetHeight() / 2);
820 for ( wxInt32 x
= 0; x
< rect
.GetWidth(); x
++ )
822 for ( wxInt32 y
= 0; y
< rect
.GetHeight(); y
++ )
824 //get color difference
825 wxInt32 nGradient
= ((nRadius
-
827 pow((double)(x
- cx
- nCircleOffX
), 2) +
828 pow((double)(y
- cy
- nCircleOffY
), 2)
836 nR
= (wxUint8
)(nR1
+ ((nR2
- nR1
) * nGradient
/ 100));
837 nG
= (wxUint8
)(nG1
+ ((nG2
- nG1
) * nGradient
/ 100));
838 nB
= (wxUint8
)(nB1
+ ((nB2
- nB1
) * nGradient
/ 100));
841 m_pen
.SetColour(wxColour(nR
,nG
,nB
));
842 DrawPoint(wxPoint(x
+ rect
.GetLeft(), y
+ rect
.GetTop()));
845 //return old pen color
846 m_pen
.SetColour(oldPenColour
);
850 Notes for wxWidgets DrawEllipticArcRot(...)
852 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
853 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
856 All methods are generic, so they can be implemented in wxDCBase.
857 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
858 methods like (WinCE) wxDC::DoDrawArc(...).
860 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
861 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
862 parts) or every column (in steep parts) only one pixel is calculated.
863 Trigonometric calculation (sin, cos, tan, atan) is only done if the
864 starting angle is not equal to the ending angle. The calculation of the
865 pixels is done using simple arithmetic only and should perform not too
866 bad even on devices without floating point processor. I didn't test this yet.
868 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
869 For instance: an ellipse rotated 180 degrees is drawn
870 slightly different from the original.
872 The points are then moved to an array and used to draw a polyline and/or polygon
873 (with center added, the pie).
874 The result looks quite similar to the native ellipse, only e few pixels differ.
876 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
877 slower as DrawEllipse(...), which calls the native API.
878 An rotated ellipse outside the clipping region takes nearly the same time,
879 while an native ellipse outside takes nearly no time to draw.
881 If you draw an arc with this new method, you will see the starting and ending angles
882 are calculated properly.
883 If you use DrawEllipticArc(...), you will see they are only correct for circles
884 and not properly calculated for ellipses.
887 p.lenhard@t-online.de
891 void wxDCBase::DoDrawEllipticArcRot( wxCoord x
, wxCoord y
,
892 wxCoord w
, wxCoord h
,
893 double sa
, double ea
, double angle
)
897 CalculateEllipticPoints( &list
, x
, y
, w
, h
, sa
, ea
);
898 Rotate( &list
, angle
, wxPoint( x
+w
/2, y
+h
/2 ) );
900 // Add center (for polygon/pie)
901 list
.Append( (wxObject
*) new wxPoint( x
+w
/2, y
+h
/2 ) );
903 // copy list into array and delete list elements
904 int n
= list
.GetCount();
905 wxPoint
*points
= new wxPoint
[n
];
908 for ( node
= list
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
910 wxPoint
*point
= (wxPoint
*)node
->GetData();
911 points
[i
].x
= point
->x
;
912 points
[i
].y
= point
->y
;
916 // first draw the pie without pen, if necessary
917 if( GetBrush() != *wxTRANSPARENT_BRUSH
)
919 wxPen
tempPen( GetPen() );
920 SetPen( *wxTRANSPARENT_PEN
);
921 DoDrawPolygon( n
, points
, 0, 0 );
925 // then draw the arc without brush, if necessary
926 if( GetPen() != *wxTRANSPARENT_PEN
)
929 DoDrawLines( n
-1, points
, 0, 0 );
934 } // DrawEllipticArcRot
936 void wxDCBase::Rotate( wxList
* points
, double angle
, wxPoint center
)
941 double dSinA
= -sin(angle
*2.0*pi
/360.0);
942 double dCosA
= cos(angle
*2.0*pi
/360.0);
943 for ( wxNode
* node
= points
->GetFirst(); node
; node
= node
->GetNext() )
945 wxPoint
* point
= (wxPoint
*)node
->GetData();
947 // transform coordinates, if necessary
948 if( center
.x
) point
->x
-= center
.x
;
949 if( center
.y
) point
->y
-= center
.y
;
951 // calculate rotation, rounding simply by implicit cast to integer
952 int xTemp
= point
->x
* dCosA
- point
->y
* dSinA
;
953 point
->y
= point
->x
* dSinA
+ point
->y
* dCosA
;
956 // back transform coordinates, if necessary
957 if( center
.x
) point
->x
+= center
.x
;
958 if( center
.y
) point
->y
+= center
.y
;
963 void wxDCBase::CalculateEllipticPoints( wxList
* points
,
964 wxCoord xStart
, wxCoord yStart
,
965 wxCoord w
, wxCoord h
,
966 double sa
, double ea
)
977 bool bUseAngles
= false;
983 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
985 if( 2*a
== w
) decrX
= 1;
987 if( 2*b
== h
) decrY
= 1;
989 wxCoord xCenter
= xStart
+ a
;
990 wxCoord yCenter
= yStart
+ b
;
991 // calculate data for start and end, if necessary
995 // normalisation of angles
996 while( sa
<0 ) sa
+= 360;
997 while( ea
<0 ) ea
+= 360;
998 while( sa
>=360 ) sa
-= 360;
999 while( ea
>=360 ) ea
-= 360;
1000 // calculate quadrant numbers
1001 if( sa
> 270 ) sq
= 3;
1002 else if( sa
> 180 ) sq
= 2;
1003 else if( sa
> 90 ) sq
= 1;
1004 if( ea
> 270 ) eq
= 3;
1005 else if( ea
> 180 ) eq
= 2;
1006 else if( ea
> 90 ) eq
= 1;
1007 sar
= sa
* pi
/ 180.0;
1008 ear
= ea
* pi
/ 180.0;
1009 // correct angle circle -> ellipse
1010 sar
= atan( -a
/(double)b
* tan( sar
) );
1011 if ( sq
== 1 || sq
== 2 ) sar
+= pi
;
1012 ear
= atan( -a
/(double)b
* tan( ear
) );
1013 if ( eq
== 1 || eq
== 2 ) ear
+= pi
;
1014 // coordinates of points
1015 xsa
= xCenter
+ a
* cos( sar
);
1016 if( sq
== 0 || sq
== 3 ) xsa
-= decrX
;
1017 ysa
= yCenter
+ b
* sin( sar
);
1018 if( sq
== 2 || sq
== 3 ) ysa
-= decrY
;
1019 xea
= xCenter
+ a
* cos( ear
);
1020 if( eq
== 0 || eq
== 3 ) xea
-= decrX
;
1021 yea
= yCenter
+ b
* sin( ear
);
1022 if( eq
== 2 || eq
== 3 ) yea
-= decrY
;
1024 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1026 double c2
= 2.0 / w
;
1035 // Lists for quadrant 1 to 4
1036 wxList pointsarray
[4];
1037 // Calculate points for first quadrant and set in all quadrants
1038 for( x
= 0; x
<= a
; ++x
)
1043 bool bNewPoint
= false;
1044 while( y2
> c1
- c2
* x2
&& y
> 0 )
1050 // old y now to big: set point with old y, old x
1051 if( bNewPoint
&& x
>1)
1054 // remove points on the same line
1055 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
- y_old
) );
1056 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
- y_old
) );
1057 pointsarray
[2].Insert( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
+ y_old
- decrY
) );
1058 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
+ y_old
- decrY
) );
1060 } // calculate point
1062 // Starting and/or ending points for the quadrants, first quadrant gets both.
1063 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1064 pointsarray
[0].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
- b
) );
1065 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- a
, yCenter
) );
1066 pointsarray
[2].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
+ b
- decrY
) );
1067 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1069 // copy quadrants in original list
1072 // Copy the right part of the points in the lists
1073 // and delete the wxPoints, because they do not leave this method.
1074 points
->Append( (wxObject
*) new wxPoint( xsa
, ysa
) );
1076 bool bStarted
= false;
1077 bool bReady
= false;
1078 bool bForceTurn
= ( sq
== eq
&& sa
> ea
);
1081 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1083 // once: go to starting point in start quadrant
1086 ( (wxPoint
*) node
->GetData() )->x
< xsa
+1 && q
<= 1
1088 ( (wxPoint
*) node
->GetData() )->x
> xsa
-1 && q
>= 2
1095 // copy point, if not at ending point
1098 if( q
!= eq
|| bForceTurn
1100 ( (wxPoint
*) node
->GetData() )->x
> xea
+1 && q
<= 1
1102 ( (wxPoint
*) node
->GetData() )->x
< xea
-1 && q
>= 2
1106 wxPoint
* pPoint
= new wxPoint( *((wxPoint
*) node
->GetData() ) );
1107 points
->Append( (wxObject
*) pPoint
);
1109 else if( q
== eq
&& !bForceTurn
|| ( (wxPoint
*) node
->GetData() )->x
== xea
)
1119 } // while not bReady
1120 points
->Append( (wxObject
*) new wxPoint( xea
, yea
) );
1123 for( q
= 0; q
< 4; ++q
)
1125 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1127 wxPoint
*p
= (wxPoint
*)node
->GetData();
1135 // copy whole ellipse, wxPoints will be deleted outside
1136 for( node
= pointsarray
[0].GetFirst(); node
; node
= node
->GetNext() )
1138 wxObject
*p
= node
->GetData();
1139 points
->Append( p
);
1141 for( node
= pointsarray
[1].GetFirst(); node
; node
= node
->GetNext() )
1143 wxObject
*p
= node
->GetData();
1144 points
->Append( p
);
1146 for( node
= pointsarray
[2].GetFirst(); node
; node
= node
->GetNext() )
1148 wxObject
*p
= node
->GetData();
1149 points
->Append( p
);
1151 for( node
= pointsarray
[3].GetFirst(); node
; node
= node
->GetNext() )
1153 wxObject
*p
= node
->GetData();
1154 points
->Append( p
);
1157 } // CalculateEllipticPoints