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 // ----------------------------------------------------------------------------
82 // stubs for functions not implemented in all ports
83 // ----------------------------------------------------------------------------
86 wxDCBase::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
87 wxCoord dstWidth
, wxCoord dstHeight
,
89 wxCoord xsrc
, wxCoord ysrc
,
90 wxCoord srcWidth
, wxCoord srcHeight
,
96 wxCHECK_MSG( srcWidth
&& srcHeight
&& dstWidth
&& dstHeight
, false,
97 _T("invalid blit size") );
99 // emulate the stretching by modifying the DC scale
100 double xscale
= (double)srcWidth
/dstWidth
,
101 yscale
= (double)srcHeight
/dstHeight
;
103 double xscaleOld
, yscaleOld
;
104 GetUserScale(&xscaleOld
, &yscaleOld
);
105 SetUserScale(xscaleOld
/xscale
, yscaleOld
/yscale
);
107 bool rc
= DoBlit(wxCoord(xdest
*xscale
), wxCoord(ydest
*yscale
),
108 wxCoord(dstWidth
*xscale
), wxCoord(dstHeight
*yscale
),
110 xsrc
, ysrc
, rop
, useMask
, xsrcMask
, ysrcMask
);
112 SetUserScale(xscaleOld
, yscaleOld
);
117 // ----------------------------------------------------------------------------
119 // ----------------------------------------------------------------------------
121 void wxDCBase::DrawLines(const wxList
*list
, wxCoord xoffset
, wxCoord yoffset
)
123 int n
= list
->GetCount();
124 wxPoint
*points
= new wxPoint
[n
];
127 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
129 wxPoint
*point
= (wxPoint
*)node
->GetData();
130 points
[i
].x
= point
->x
;
131 points
[i
].y
= point
->y
;
134 DoDrawLines(n
, points
, xoffset
, yoffset
);
140 void wxDCBase::DrawPolygon(const wxList
*list
,
141 wxCoord xoffset
, wxCoord yoffset
,
144 int n
= list
->GetCount();
145 wxPoint
*points
= new wxPoint
[n
];
148 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
150 wxPoint
*point
= (wxPoint
*)node
->GetData();
151 points
[i
].x
= point
->x
;
152 points
[i
].y
= point
->y
;
155 DoDrawPolygon(n
, points
, xoffset
, yoffset
, fillStyle
);
161 wxDCBase::DoDrawPolyPolygon(int n
,
164 wxCoord xoffset
, wxCoord yoffset
,
169 DoDrawPolygon(count
[0], points
, xoffset
, yoffset
, fillStyle
);
177 for (i
= j
= lastOfs
= 0; i
< n
; i
++)
182 pts
= new wxPoint
[j
+n
-1];
183 for (i
= 0; i
< j
; i
++)
185 for (i
= 2; i
<= n
; i
++)
187 lastOfs
-= count
[n
-i
];
188 pts
[j
++] = pts
[lastOfs
];
192 SetPen(wxPen(*wxBLACK
, 0, wxTRANSPARENT
));
193 DoDrawPolygon(j
, pts
, xoffset
, yoffset
, fillStyle
);
195 for (i
= j
= 0; i
< n
; i
++)
197 DoDrawLines(count
[i
], pts
+j
, xoffset
, yoffset
);
203 // ----------------------------------------------------------------------------
205 // ----------------------------------------------------------------------------
209 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
210 void wxDCBase::DrawSpline(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
, wxCoord x3
, wxCoord y3
)
214 wxPoint
*point1
= new wxPoint
;
215 point1
->x
= x1
; point1
->y
= y1
;
216 point_list
.Append((wxObject
*)point1
);
218 wxPoint
*point2
= new wxPoint
;
219 point2
->x
= x2
; point2
->y
= y2
;
220 point_list
.Append((wxObject
*)point2
);
222 wxPoint
*point3
= new wxPoint
;
223 point3
->x
= x3
; point3
->y
= y3
;
224 point_list
.Append((wxObject
*)point3
);
226 DrawSpline(&point_list
);
228 for( wxList::compatibility_iterator node
= point_list
.GetFirst(); node
; node
= node
->GetNext() )
230 wxPoint
*p
= (wxPoint
*)node
->GetData();
235 void wxDCBase::DrawSpline(int n
, wxPoint points
[])
238 for (int i
=0; i
< n
; i
++)
240 list
.Append((wxObject
*)&points
[i
]);
246 // ----------------------------------- spline code ----------------------------------------
248 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
249 double a3
, double b3
, double a4
, double b4
);
250 void wx_clear_stack();
251 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
252 double *y3
, double *x4
, double *y4
);
253 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
254 double x4
, double y4
);
255 static bool wx_spline_add_point(double x
, double y
);
256 static void wx_spline_draw_point_array(wxDCBase
*dc
);
258 wxList wx_spline_point_list
;
260 #define half(z1, z2) ((z1+z2)/2.0)
263 /* iterative version */
265 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
268 register double xmid
, ymid
;
269 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
272 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
274 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
275 xmid
= (double)half(x2
, x3
);
276 ymid
= (double)half(y2
, y3
);
277 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
278 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
279 wx_spline_add_point( x1
, y1
);
280 wx_spline_add_point( xmid
, ymid
);
282 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
283 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
284 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
285 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
290 /* utilities used by spline drawing routines */
292 typedef struct wx_spline_stack_struct
{
293 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
296 #define SPLINE_STACK_DEPTH 20
297 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
298 static Stack
*wx_stack_top
;
299 static int wx_stack_count
;
301 void wx_clear_stack()
303 wx_stack_top
= wx_spline_stack
;
307 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
309 wx_stack_top
->x1
= x1
;
310 wx_stack_top
->y1
= y1
;
311 wx_stack_top
->x2
= x2
;
312 wx_stack_top
->y2
= y2
;
313 wx_stack_top
->x3
= x3
;
314 wx_stack_top
->y3
= y3
;
315 wx_stack_top
->x4
= x4
;
316 wx_stack_top
->y4
= y4
;
321 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
322 double *x3
, double *y3
, double *x4
, double *y4
)
324 if (wx_stack_count
== 0)
328 *x1
= wx_stack_top
->x1
;
329 *y1
= wx_stack_top
->y1
;
330 *x2
= wx_stack_top
->x2
;
331 *y2
= wx_stack_top
->y2
;
332 *x3
= wx_stack_top
->x3
;
333 *y3
= wx_stack_top
->y3
;
334 *x4
= wx_stack_top
->x4
;
335 *y4
= wx_stack_top
->y4
;
339 static bool wx_spline_add_point(double x
, double y
)
341 wxPoint
*point
= new wxPoint
;
344 wx_spline_point_list
.Append((wxObject
*)point
);
348 static void wx_spline_draw_point_array(wxDCBase
*dc
)
350 dc
->DrawLines(&wx_spline_point_list
, 0, 0 );
351 wxList::compatibility_iterator node
= wx_spline_point_list
.GetFirst();
354 wxPoint
*point
= (wxPoint
*)node
->GetData();
356 wx_spline_point_list
.Erase(node
);
357 node
= wx_spline_point_list
.GetFirst();
361 void wxDCBase::DoDrawSpline( wxList
*points
)
363 wxCHECK_RET( Ok(), wxT("invalid window dc") );
366 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
367 double x1
, y1
, x2
, y2
;
369 wxList::compatibility_iterator node
= points
->GetFirst();
374 p
= (wxPoint
*)node
->GetData();
379 node
= node
->GetNext();
380 p
= (wxPoint
*)node
->GetData();
384 cx1
= (double)((x1
+ x2
) / 2);
385 cy1
= (double)((y1
+ y2
) / 2);
386 cx2
= (double)((cx1
+ x2
) / 2);
387 cy2
= (double)((cy1
+ y2
) / 2);
389 wx_spline_add_point(x1
, y1
);
391 while ((node
= node
->GetNext())
397 p
= (wxPoint
*)node
->GetData();
402 cx4
= (double)(x1
+ x2
) / 2;
403 cy4
= (double)(y1
+ y2
) / 2;
404 cx3
= (double)(x1
+ cx4
) / 2;
405 cy3
= (double)(y1
+ cy4
) / 2;
407 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
411 cx2
= (double)(cx1
+ x2
) / 2;
412 cy2
= (double)(cy1
+ y2
) / 2;
415 wx_spline_add_point( cx1
, cy1
);
416 wx_spline_add_point( x2
, y2
);
418 wx_spline_draw_point_array( this );
421 #endif // wxUSE_SPLINES
423 // ----------------------------------------------------------------------------
424 // Partial Text Extents
425 // ----------------------------------------------------------------------------
428 // Each element of the widths array will be the width of the string up to and
429 // including the corresponding character in text. This is the generic
430 // implementation, the port-specific classes should do this with native APIs
431 // if available and if faster. Note: pango_layout_index_to_pos is much slower
432 // than calling GetTextExtent!!
439 FontWidthCache() : m_scaleX(1), m_widths(NULL
) { }
440 ~FontWidthCache() { delete []m_widths
; }
445 m_widths
= new int[FWC_SIZE
];
447 memset(m_widths
, 0, sizeof(int)*FWC_SIZE
);
455 static FontWidthCache s_fontWidthCache
;
457 bool wxDCBase::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
461 const size_t len
= text
.length();
465 // reset the cache if font or horizontal scale have changed
466 if ( !s_fontWidthCache
.m_widths
||
467 !wxIsSameDouble(s_fontWidthCache
.m_scaleX
, m_scaleX
) ||
468 (s_fontWidthCache
.m_font
!= GetFont()) )
470 s_fontWidthCache
.Reset();
471 s_fontWidthCache
.m_font
= GetFont();
472 s_fontWidthCache
.m_scaleX
= m_scaleX
;
475 // Calculate the position of each character based on the widths of
476 // the previous characters
478 for ( size_t i
= 0; i
< len
; i
++ )
480 const wxChar c
= text
[i
];
481 unsigned int c_int
= (unsigned int)c
;
483 if ((c_int
< FWC_SIZE
) && (s_fontWidthCache
.m_widths
[c_int
] != 0))
485 w
= s_fontWidthCache
.m_widths
[c_int
];
489 GetTextExtent(c
, &w
, &h
);
490 if (c_int
< FWC_SIZE
)
491 s_fontWidthCache
.m_widths
[c_int
] = w
;
495 widths
[i
] = totalWidth
;
502 // ----------------------------------------------------------------------------
503 // enhanced text drawing
504 // ----------------------------------------------------------------------------
506 void wxDCBase::GetMultiLineTextExtent(const wxString
& text
,
510 const wxFont
*font
) const
512 wxCoord widthTextMax
= 0, widthLine
,
513 heightTextTotal
= 0, heightLineDefault
= 0, heightLine
= 0;
516 for ( const wxChar
*pc
= text
; ; pc
++ )
518 if ( *pc
== _T('\n') || *pc
== _T('\0') )
520 if ( curLine
.empty() )
522 // we can't use GetTextExtent - it will return 0 for both width
523 // and height and an empty line should count in height
526 // assume that this line has the same height as the previous
528 if ( !heightLineDefault
)
529 heightLineDefault
= heightLine
;
531 if ( !heightLineDefault
)
533 // but we don't know it yet - choose something reasonable
534 GetTextExtent(_T("W"), NULL
, &heightLineDefault
,
538 heightTextTotal
+= heightLineDefault
;
542 GetTextExtent(curLine
, &widthLine
, &heightLine
,
544 if ( widthLine
> widthTextMax
)
545 widthTextMax
= widthLine
;
546 heightTextTotal
+= heightLine
;
549 if ( *pc
== _T('\n') )
568 *y
= heightTextTotal
;
573 void wxDCBase::DrawLabel(const wxString
& text
,
574 const wxBitmap
& bitmap
,
578 wxRect
*rectBounding
)
580 // find the text position
581 wxCoord widthText
, heightText
, heightLine
;
582 GetMultiLineTextExtent(text
, &widthText
, &heightText
, &heightLine
);
584 wxCoord width
, height
;
587 width
= widthText
+ bitmap
.GetWidth();
588 height
= bitmap
.GetHeight();
597 if ( alignment
& wxALIGN_RIGHT
)
599 x
= rect
.GetRight() - width
;
601 else if ( alignment
& wxALIGN_CENTRE_HORIZONTAL
)
603 x
= (rect
.GetLeft() + rect
.GetRight() + 1 - width
) / 2;
605 else // alignment & wxALIGN_LEFT
610 if ( alignment
& wxALIGN_BOTTOM
)
612 y
= rect
.GetBottom() - height
;
614 else if ( alignment
& wxALIGN_CENTRE_VERTICAL
)
616 y
= (rect
.GetTop() + rect
.GetBottom() + 1 - height
) / 2;
618 else // alignment & wxALIGN_TOP
623 // draw the bitmap first
629 DrawBitmap(bitmap
, x
, y
, true /* use mask */);
631 wxCoord offset
= bitmap
.GetWidth() + 4;
635 y
+= (height
- heightText
) / 2;
638 // we will draw the underscore under the accel char later
639 wxCoord startUnderscore
= 0,
643 // split the string into lines and draw each of them separately
645 for ( wxString::const_iterator pc
= text
.begin(); ; ++pc
)
647 if ( *pc
== _T('\n') || pc
== text
.end() )
649 int xRealStart
= x
; // init it here to avoid compielr warnings
651 if ( !curLine
.empty() )
653 // NB: can't test for !(alignment & wxALIGN_LEFT) because
655 if ( alignment
& (wxALIGN_RIGHT
| wxALIGN_CENTRE_HORIZONTAL
) )
658 GetTextExtent(curLine
, &widthLine
, NULL
);
660 if ( alignment
& wxALIGN_RIGHT
)
662 xRealStart
+= width
- widthLine
;
664 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
666 xRealStart
+= (width
- widthLine
) / 2;
669 //else: left aligned, nothing to do
671 DrawText(curLine
, xRealStart
, y
);
676 // do we have underscore in this line? we can check yUnderscore
677 // because it is set below to just y + heightLine if we do
678 if ( y
== yUnderscore
)
680 // adjust the horz positions to account for the shift
681 startUnderscore
+= xRealStart
;
682 endUnderscore
+= xRealStart
;
685 if ( pc
== text
.end() )
690 else // not end of line
692 if ( pc
- text
.begin() == indexAccel
)
694 // remeber to draw underscore here
695 GetTextExtent(curLine
, &startUnderscore
, NULL
);
697 GetTextExtent(curLine
, &endUnderscore
, NULL
);
699 yUnderscore
= y
+ heightLine
;
708 // draw the underscore if found
709 if ( startUnderscore
!= endUnderscore
)
711 // it should be of the same colour as text
712 SetPen(wxPen(GetTextForeground(), 0, wxSOLID
));
716 DrawLine(startUnderscore
, yUnderscore
, endUnderscore
, yUnderscore
);
719 // return bounding rect if requested
722 *rectBounding
= wxRect(x
, y
- heightText
, widthText
, heightText
);
725 CalcBoundingBox(x0
, y0
);
726 CalcBoundingBox(x0
+ width0
, y0
+ height
);
730 void wxDCBase::DoGradientFillLinear(const wxRect
& rect
,
731 const wxColour
& initialColour
,
732 const wxColour
& destColour
,
733 wxDirection nDirection
)
736 wxPen oldPen
= m_pen
;
737 wxBrush oldBrush
= m_brush
;
739 wxUint8 nR1
= initialColour
.Red();
740 wxUint8 nG1
= initialColour
.Green();
741 wxUint8 nB1
= initialColour
.Blue();
742 wxUint8 nR2
= destColour
.Red();
743 wxUint8 nG2
= destColour
.Green();
744 wxUint8 nB2
= destColour
.Blue();
747 if ( nDirection
== wxEAST
|| nDirection
== wxWEST
)
749 wxInt32 x
= rect
.GetWidth();
750 wxInt32 w
= x
; // width of area to shade
751 wxInt32 xDelta
= w
/256; // height of one shade bend
759 nR
= nR1
- (nR1
-nR2
)*(w
-x
)/w
;
761 nR
= nR1
+ (nR2
-nR1
)*(w
-x
)/w
;
764 nG
= nG1
- (nG1
-nG2
)*(w
-x
)/w
;
766 nG
= nG1
+ (nG2
-nG1
)*(w
-x
)/w
;
769 nB
= nB1
- (nB1
-nB2
)*(w
-x
)/w
;
771 nB
= nB1
+ (nB2
-nB1
)*(w
-x
)/w
;
773 wxColour
colour(nR
,nG
,nB
);
774 SetPen(wxPen(colour
, 1, wxSOLID
));
775 SetBrush(wxBrush(colour
));
776 if(nDirection
== wxEAST
)
777 DrawRectangle(rect
.GetRight()-x
-xDelta
, rect
.GetTop(),
778 xDelta
, rect
.GetHeight());
779 else //nDirection == wxWEST
780 DrawRectangle(rect
.GetLeft()+x
, rect
.GetTop(),
781 xDelta
, rect
.GetHeight());
784 else // nDirection == wxNORTH || nDirection == wxSOUTH
786 wxInt32 y
= rect
.GetHeight();
787 wxInt32 w
= y
; // height of area to shade
788 wxInt32 yDelta
= w
/255; // height of one shade bend
796 nR
= nR1
- (nR1
-nR2
)*(w
-y
)/w
;
798 nR
= nR1
+ (nR2
-nR1
)*(w
-y
)/w
;
801 nG
= nG1
- (nG1
-nG2
)*(w
-y
)/w
;
803 nG
= nG1
+ (nG2
-nG1
)*(w
-y
)/w
;
806 nB
= nB1
- (nB1
-nB2
)*(w
-y
)/w
;
808 nB
= nB1
+ (nB2
-nB1
)*(w
-y
)/w
;
810 wxColour
colour(nR
,nG
,nB
);
811 SetPen(wxPen(colour
, 1, wxSOLID
));
812 SetBrush(wxBrush(colour
));
813 if(nDirection
== wxNORTH
)
814 DrawRectangle(rect
.GetLeft(), rect
.GetTop()+y
,
815 rect
.GetWidth(), yDelta
);
816 else //nDirection == wxSOUTH
817 DrawRectangle(rect
.GetLeft(), rect
.GetBottom()-y
-yDelta
,
818 rect
.GetWidth(), yDelta
);
826 void wxDCBase::DoGradientFillConcentric(const wxRect
& rect
,
827 const wxColour
& initialColour
,
828 const wxColour
& destColour
,
829 const wxPoint
& circleCenter
)
831 //save the old pen color
832 wxColour oldPenColour
= m_pen
.GetColour();
834 wxUint8 nR1
= destColour
.Red();
835 wxUint8 nG1
= destColour
.Green();
836 wxUint8 nB1
= destColour
.Blue();
837 wxUint8 nR2
= initialColour
.Red();
838 wxUint8 nG2
= initialColour
.Green();
839 wxUint8 nB2
= initialColour
.Blue();
844 wxInt32 cx
= rect
.GetWidth() / 2;
845 wxInt32 cy
= rect
.GetHeight() / 2;
853 wxInt32 nCircleOffX
= circleCenter
.x
- (rect
.GetWidth() / 2);
854 wxInt32 nCircleOffY
= circleCenter
.y
- (rect
.GetHeight() / 2);
856 for ( wxInt32 x
= 0; x
< rect
.GetWidth(); x
++ )
858 for ( wxInt32 y
= 0; y
< rect
.GetHeight(); y
++ )
860 //get color difference
861 wxInt32 nGradient
= ((nRadius
-
863 pow((double)(x
- cx
- nCircleOffX
), 2) +
864 pow((double)(y
- cy
- nCircleOffY
), 2)
872 nR
= (wxUint8
)(nR1
+ ((nR2
- nR1
) * nGradient
/ 100));
873 nG
= (wxUint8
)(nG1
+ ((nG2
- nG1
) * nGradient
/ 100));
874 nB
= (wxUint8
)(nB1
+ ((nB2
- nB1
) * nGradient
/ 100));
877 m_pen
.SetColour(wxColour(nR
,nG
,nB
));
878 DrawPoint(wxPoint(x
+ rect
.GetLeft(), y
+ rect
.GetTop()));
881 //return old pen color
882 m_pen
.SetColour(oldPenColour
);
886 Notes for wxWidgets DrawEllipticArcRot(...)
888 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
889 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
892 All methods are generic, so they can be implemented in wxDCBase.
893 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
894 methods like (WinCE) wxDC::DoDrawArc(...).
896 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
897 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
898 parts) or every column (in steep parts) only one pixel is calculated.
899 Trigonometric calculation (sin, cos, tan, atan) is only done if the
900 starting angle is not equal to the ending angle. The calculation of the
901 pixels is done using simple arithmetic only and should perform not too
902 bad even on devices without floating point processor. I didn't test this yet.
904 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
905 For instance: an ellipse rotated 180 degrees is drawn
906 slightly different from the original.
908 The points are then moved to an array and used to draw a polyline and/or polygon
909 (with center added, the pie).
910 The result looks quite similar to the native ellipse, only e few pixels differ.
912 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
913 slower as DrawEllipse(...), which calls the native API.
914 An rotated ellipse outside the clipping region takes nearly the same time,
915 while an native ellipse outside takes nearly no time to draw.
917 If you draw an arc with this new method, you will see the starting and ending angles
918 are calculated properly.
919 If you use DrawEllipticArc(...), you will see they are only correct for circles
920 and not properly calculated for ellipses.
923 p.lenhard@t-online.de
927 void wxDCBase::DoDrawEllipticArcRot( wxCoord x
, wxCoord y
,
928 wxCoord w
, wxCoord h
,
929 double sa
, double ea
, double angle
)
933 CalculateEllipticPoints( &list
, x
, y
, w
, h
, sa
, ea
);
934 Rotate( &list
, angle
, wxPoint( x
+w
/2, y
+h
/2 ) );
936 // Add center (for polygon/pie)
937 list
.Append( (wxObject
*) new wxPoint( x
+w
/2, y
+h
/2 ) );
939 // copy list into array and delete list elements
940 int n
= list
.GetCount();
941 wxPoint
*points
= new wxPoint
[n
];
944 for ( node
= list
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
946 wxPoint
*point
= (wxPoint
*)node
->GetData();
947 points
[i
].x
= point
->x
;
948 points
[i
].y
= point
->y
;
952 // first draw the pie without pen, if necessary
953 if( GetBrush() != *wxTRANSPARENT_BRUSH
)
955 wxPen
tempPen( GetPen() );
956 SetPen( *wxTRANSPARENT_PEN
);
957 DoDrawPolygon( n
, points
, 0, 0 );
961 // then draw the arc without brush, if necessary
962 if( GetPen() != *wxTRANSPARENT_PEN
)
965 DoDrawLines( n
-1, points
, 0, 0 );
970 } // DrawEllipticArcRot
972 void wxDCBase::Rotate( wxList
* points
, double angle
, wxPoint center
)
977 double dSinA
= -sin(angle
*2.0*pi
/360.0);
978 double dCosA
= cos(angle
*2.0*pi
/360.0);
979 for ( wxNode
* node
= points
->GetFirst(); node
; node
= node
->GetNext() )
981 wxPoint
* point
= (wxPoint
*)node
->GetData();
983 // transform coordinates, if necessary
984 if( center
.x
) point
->x
-= center
.x
;
985 if( center
.y
) point
->y
-= center
.y
;
987 // calculate rotation, rounding simply by implicit cast to integer
988 int xTemp
= point
->x
* dCosA
- point
->y
* dSinA
;
989 point
->y
= point
->x
* dSinA
+ point
->y
* dCosA
;
992 // back transform coordinates, if necessary
993 if( center
.x
) point
->x
+= center
.x
;
994 if( center
.y
) point
->y
+= center
.y
;
999 void wxDCBase::CalculateEllipticPoints( wxList
* points
,
1000 wxCoord xStart
, wxCoord yStart
,
1001 wxCoord w
, wxCoord h
,
1002 double sa
, double ea
)
1013 bool bUseAngles
= false;
1019 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
1021 if( 2*a
== w
) decrX
= 1;
1023 if( 2*b
== h
) decrY
= 1;
1025 wxCoord xCenter
= xStart
+ a
;
1026 wxCoord yCenter
= yStart
+ b
;
1027 // calculate data for start and end, if necessary
1031 // normalisation of angles
1032 while( sa
<0 ) sa
+= 360;
1033 while( ea
<0 ) ea
+= 360;
1034 while( sa
>=360 ) sa
-= 360;
1035 while( ea
>=360 ) ea
-= 360;
1036 // calculate quadrant numbers
1037 if( sa
> 270 ) sq
= 3;
1038 else if( sa
> 180 ) sq
= 2;
1039 else if( sa
> 90 ) sq
= 1;
1040 if( ea
> 270 ) eq
= 3;
1041 else if( ea
> 180 ) eq
= 2;
1042 else if( ea
> 90 ) eq
= 1;
1043 sar
= sa
* pi
/ 180.0;
1044 ear
= ea
* pi
/ 180.0;
1045 // correct angle circle -> ellipse
1046 sar
= atan( -a
/(double)b
* tan( sar
) );
1047 if ( sq
== 1 || sq
== 2 ) sar
+= pi
;
1048 ear
= atan( -a
/(double)b
* tan( ear
) );
1049 if ( eq
== 1 || eq
== 2 ) ear
+= pi
;
1050 // coordinates of points
1051 xsa
= xCenter
+ a
* cos( sar
);
1052 if( sq
== 0 || sq
== 3 ) xsa
-= decrX
;
1053 ysa
= yCenter
+ b
* sin( sar
);
1054 if( sq
== 2 || sq
== 3 ) ysa
-= decrY
;
1055 xea
= xCenter
+ a
* cos( ear
);
1056 if( eq
== 0 || eq
== 3 ) xea
-= decrX
;
1057 yea
= yCenter
+ b
* sin( ear
);
1058 if( eq
== 2 || eq
== 3 ) yea
-= decrY
;
1060 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1062 double c2
= 2.0 / w
;
1071 // Lists for quadrant 1 to 4
1072 wxList pointsarray
[4];
1073 // Calculate points for first quadrant and set in all quadrants
1074 for( x
= 0; x
<= a
; ++x
)
1079 bool bNewPoint
= false;
1080 while( y2
> c1
- c2
* x2
&& y
> 0 )
1086 // old y now to big: set point with old y, old x
1087 if( bNewPoint
&& x
>1)
1090 // remove points on the same line
1091 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
- y_old
) );
1092 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
- y_old
) );
1093 pointsarray
[2].Insert( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
+ y_old
- decrY
) );
1094 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
+ y_old
- decrY
) );
1096 } // calculate point
1098 // Starting and/or ending points for the quadrants, first quadrant gets both.
1099 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1100 pointsarray
[0].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
- b
) );
1101 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- a
, yCenter
) );
1102 pointsarray
[2].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
+ b
- decrY
) );
1103 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1105 // copy quadrants in original list
1108 // Copy the right part of the points in the lists
1109 // and delete the wxPoints, because they do not leave this method.
1110 points
->Append( (wxObject
*) new wxPoint( xsa
, ysa
) );
1112 bool bStarted
= false;
1113 bool bReady
= false;
1114 bool bForceTurn
= ( sq
== eq
&& sa
> ea
);
1117 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1119 // once: go to starting point in start quadrant
1122 ( (wxPoint
*) node
->GetData() )->x
< xsa
+1 && q
<= 1
1124 ( (wxPoint
*) node
->GetData() )->x
> xsa
-1 && q
>= 2
1131 // copy point, if not at ending point
1134 if( q
!= eq
|| bForceTurn
1136 ( (wxPoint
*) node
->GetData() )->x
> xea
+1 && q
<= 1
1138 ( (wxPoint
*) node
->GetData() )->x
< xea
-1 && q
>= 2
1142 wxPoint
* pPoint
= new wxPoint( *((wxPoint
*) node
->GetData() ) );
1143 points
->Append( (wxObject
*) pPoint
);
1145 else if( q
== eq
&& !bForceTurn
|| ( (wxPoint
*) node
->GetData() )->x
== xea
)
1155 } // while not bReady
1156 points
->Append( (wxObject
*) new wxPoint( xea
, yea
) );
1159 for( q
= 0; q
< 4; ++q
)
1161 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1163 wxPoint
*p
= (wxPoint
*)node
->GetData();
1171 // copy whole ellipse, wxPoints will be deleted outside
1172 for( node
= pointsarray
[0].GetFirst(); node
; node
= node
->GetNext() )
1174 wxObject
*p
= node
->GetData();
1175 points
->Append( p
);
1177 for( node
= pointsarray
[1].GetFirst(); node
; node
= node
->GetNext() )
1179 wxObject
*p
= node
->GetData();
1180 points
->Append( p
);
1182 for( node
= pointsarray
[2].GetFirst(); node
; node
= node
->GetNext() )
1184 wxObject
*p
= node
->GetData();
1185 points
->Append( p
);
1187 for( node
= pointsarray
[3].GetFirst(); node
; node
= node
->GetNext() )
1189 wxObject
*p
= node
->GetData();
1190 points
->Append( p
);
1193 } // CalculateEllipticPoints
1195 #endif // __WXWINCE__