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
WXUNUSED(srcWidth
), wxCoord
WXUNUSED(srcHeight
),
96 // temporary default implementation to avoid breaking platforms that don't
98 return DoBlit(xdest
, ydest
, dstWidth
, dstHeight
, source
,
99 xsrc
, ysrc
, rop
, useMask
, xsrcMask
, ysrcMask
);
102 // ----------------------------------------------------------------------------
104 // ----------------------------------------------------------------------------
106 void wxDCBase::DrawLines(const wxList
*list
, 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 DoDrawLines(n
, points
, xoffset
, yoffset
);
125 void wxDCBase::DrawPolygon(const wxList
*list
,
126 wxCoord xoffset
, wxCoord yoffset
,
129 int n
= list
->GetCount();
130 wxPoint
*points
= new wxPoint
[n
];
133 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
135 wxPoint
*point
= (wxPoint
*)node
->GetData();
136 points
[i
].x
= point
->x
;
137 points
[i
].y
= point
->y
;
140 DoDrawPolygon(n
, points
, xoffset
, yoffset
, fillStyle
);
146 wxDCBase::DoDrawPolyPolygon(int n
,
149 wxCoord xoffset
, wxCoord yoffset
,
154 DoDrawPolygon(count
[0], points
, xoffset
, yoffset
, fillStyle
);
162 for (i
= j
= lastOfs
= 0; i
< n
; i
++)
167 pts
= new wxPoint
[j
+n
-1];
168 for (i
= 0; i
< j
; i
++)
170 for (i
= 2; i
<= n
; i
++)
172 lastOfs
-= count
[n
-i
];
173 pts
[j
++] = pts
[lastOfs
];
177 SetPen(wxPen(*wxBLACK
, 0, wxTRANSPARENT
));
178 DoDrawPolygon(j
, pts
, xoffset
, yoffset
, fillStyle
);
180 for (i
= j
= 0; i
< n
; i
++)
182 DoDrawLines(count
[i
], pts
+j
, xoffset
, yoffset
);
188 // ----------------------------------------------------------------------------
190 // ----------------------------------------------------------------------------
194 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
195 void wxDCBase::DrawSpline(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
, wxCoord x3
, wxCoord y3
)
199 wxPoint
*point1
= new wxPoint
;
200 point1
->x
= x1
; point1
->y
= y1
;
201 point_list
.Append((wxObject
*)point1
);
203 wxPoint
*point2
= new wxPoint
;
204 point2
->x
= x2
; point2
->y
= y2
;
205 point_list
.Append((wxObject
*)point2
);
207 wxPoint
*point3
= new wxPoint
;
208 point3
->x
= x3
; point3
->y
= y3
;
209 point_list
.Append((wxObject
*)point3
);
211 DrawSpline(&point_list
);
213 for( wxList::compatibility_iterator node
= point_list
.GetFirst(); node
; node
= node
->GetNext() )
215 wxPoint
*p
= (wxPoint
*)node
->GetData();
220 void wxDCBase::DrawSpline(int n
, wxPoint points
[])
223 for (int i
=0; i
< n
; i
++)
225 list
.Append((wxObject
*)&points
[i
]);
231 // ----------------------------------- spline code ----------------------------------------
233 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
234 double a3
, double b3
, double a4
, double b4
);
235 void wx_clear_stack();
236 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
237 double *y3
, double *x4
, double *y4
);
238 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
239 double x4
, double y4
);
240 static bool wx_spline_add_point(double x
, double y
);
241 static void wx_spline_draw_point_array(wxDCBase
*dc
);
243 wxList wx_spline_point_list
;
245 #define half(z1, z2) ((z1+z2)/2.0)
248 /* iterative version */
250 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
253 register double xmid
, ymid
;
254 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
257 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
259 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
260 xmid
= (double)half(x2
, x3
);
261 ymid
= (double)half(y2
, y3
);
262 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
263 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
264 wx_spline_add_point( x1
, y1
);
265 wx_spline_add_point( xmid
, ymid
);
267 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
268 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
269 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
270 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
275 /* utilities used by spline drawing routines */
277 typedef struct wx_spline_stack_struct
{
278 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
281 #define SPLINE_STACK_DEPTH 20
282 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
283 static Stack
*wx_stack_top
;
284 static int wx_stack_count
;
286 void wx_clear_stack()
288 wx_stack_top
= wx_spline_stack
;
292 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
294 wx_stack_top
->x1
= x1
;
295 wx_stack_top
->y1
= y1
;
296 wx_stack_top
->x2
= x2
;
297 wx_stack_top
->y2
= y2
;
298 wx_stack_top
->x3
= x3
;
299 wx_stack_top
->y3
= y3
;
300 wx_stack_top
->x4
= x4
;
301 wx_stack_top
->y4
= y4
;
306 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
307 double *x3
, double *y3
, double *x4
, double *y4
)
309 if (wx_stack_count
== 0)
313 *x1
= wx_stack_top
->x1
;
314 *y1
= wx_stack_top
->y1
;
315 *x2
= wx_stack_top
->x2
;
316 *y2
= wx_stack_top
->y2
;
317 *x3
= wx_stack_top
->x3
;
318 *y3
= wx_stack_top
->y3
;
319 *x4
= wx_stack_top
->x4
;
320 *y4
= wx_stack_top
->y4
;
324 static bool wx_spline_add_point(double x
, double y
)
326 wxPoint
*point
= new wxPoint
;
329 wx_spline_point_list
.Append((wxObject
*)point
);
333 static void wx_spline_draw_point_array(wxDCBase
*dc
)
335 dc
->DrawLines(&wx_spline_point_list
, 0, 0 );
336 wxList::compatibility_iterator node
= wx_spline_point_list
.GetFirst();
339 wxPoint
*point
= (wxPoint
*)node
->GetData();
341 wx_spline_point_list
.Erase(node
);
342 node
= wx_spline_point_list
.GetFirst();
346 void wxDCBase::DoDrawSpline( wxList
*points
)
348 wxCHECK_RET( Ok(), wxT("invalid window dc") );
351 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
352 double x1
, y1
, x2
, y2
;
354 wxList::compatibility_iterator node
= points
->GetFirst();
359 p
= (wxPoint
*)node
->GetData();
364 node
= node
->GetNext();
365 p
= (wxPoint
*)node
->GetData();
369 cx1
= (double)((x1
+ x2
) / 2);
370 cy1
= (double)((y1
+ y2
) / 2);
371 cx2
= (double)((cx1
+ x2
) / 2);
372 cy2
= (double)((cy1
+ y2
) / 2);
374 wx_spline_add_point(x1
, y1
);
376 while ((node
= node
->GetNext())
382 p
= (wxPoint
*)node
->GetData();
387 cx4
= (double)(x1
+ x2
) / 2;
388 cy4
= (double)(y1
+ y2
) / 2;
389 cx3
= (double)(x1
+ cx4
) / 2;
390 cy3
= (double)(y1
+ cy4
) / 2;
392 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
396 cx2
= (double)(cx1
+ x2
) / 2;
397 cy2
= (double)(cy1
+ y2
) / 2;
400 wx_spline_add_point( cx1
, cy1
);
401 wx_spline_add_point( x2
, y2
);
403 wx_spline_draw_point_array( this );
406 #endif // wxUSE_SPLINES
408 // ----------------------------------------------------------------------------
409 // Partial Text Extents
410 // ----------------------------------------------------------------------------
413 // Each element of the widths array will be the width of the string up to and
414 // including the corresponding character in text. This is the generic
415 // implementation, the port-specific classes should do this with native APIs
416 // if available and if faster. Note: pango_layout_index_to_pos is much slower
417 // than calling GetTextExtent!!
424 FontWidthCache() : m_scaleX(1), m_widths(NULL
) { }
425 ~FontWidthCache() { delete []m_widths
; }
430 m_widths
= new int[FWC_SIZE
];
432 memset(m_widths
, 0, sizeof(int)*FWC_SIZE
);
440 static FontWidthCache s_fontWidthCache
;
442 bool wxDCBase::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
446 const size_t len
= text
.length();
450 // reset the cache if font or horizontal scale have changed
451 if ( !s_fontWidthCache
.m_widths
||
452 !wxIsSameDouble(s_fontWidthCache
.m_scaleX
, m_scaleX
) ||
453 (s_fontWidthCache
.m_font
!= GetFont()) )
455 s_fontWidthCache
.Reset();
456 s_fontWidthCache
.m_font
= GetFont();
457 s_fontWidthCache
.m_scaleX
= m_scaleX
;
460 // Calculate the position of each character based on the widths of
461 // the previous characters
463 for ( size_t i
= 0; i
< len
; i
++ )
465 const wxChar c
= text
[i
];
466 unsigned int c_int
= (unsigned int)c
;
468 if ((c_int
< FWC_SIZE
) && (s_fontWidthCache
.m_widths
[c_int
] != 0))
470 w
= s_fontWidthCache
.m_widths
[c_int
];
474 GetTextExtent(c
, &w
, &h
);
475 if (c_int
< FWC_SIZE
)
476 s_fontWidthCache
.m_widths
[c_int
] = w
;
480 widths
[i
] = totalWidth
;
487 // ----------------------------------------------------------------------------
488 // enhanced text drawing
489 // ----------------------------------------------------------------------------
491 void wxDCBase::GetMultiLineTextExtent(const wxString
& text
,
497 wxCoord widthTextMax
= 0, widthLine
,
498 heightTextTotal
= 0, heightLineDefault
= 0, heightLine
= 0;
501 for ( const wxChar
*pc
= text
; ; pc
++ )
503 if ( *pc
== _T('\n') || *pc
== _T('\0') )
505 if ( curLine
.empty() )
507 // we can't use GetTextExtent - it will return 0 for both width
508 // and height and an empty line should count in height
511 // assume that this line has the same height as the previous
513 if ( !heightLineDefault
)
514 heightLineDefault
= heightLine
;
516 if ( !heightLineDefault
)
518 // but we don't know it yet - choose something reasonable
519 GetTextExtent(_T("W"), NULL
, &heightLineDefault
,
523 heightTextTotal
+= heightLineDefault
;
527 GetTextExtent(curLine
, &widthLine
, &heightLine
,
529 if ( widthLine
> widthTextMax
)
530 widthTextMax
= widthLine
;
531 heightTextTotal
+= heightLine
;
534 if ( *pc
== _T('\n') )
553 *y
= heightTextTotal
;
558 void wxDCBase::DrawLabel(const wxString
& text
,
559 const wxBitmap
& bitmap
,
563 wxRect
*rectBounding
)
565 // find the text position
566 wxCoord widthText
, heightText
, heightLine
;
567 GetMultiLineTextExtent(text
, &widthText
, &heightText
, &heightLine
);
569 wxCoord width
, height
;
572 width
= widthText
+ bitmap
.GetWidth();
573 height
= bitmap
.GetHeight();
582 if ( alignment
& wxALIGN_RIGHT
)
584 x
= rect
.GetRight() - width
;
586 else if ( alignment
& wxALIGN_CENTRE_HORIZONTAL
)
588 x
= (rect
.GetLeft() + rect
.GetRight() + 1 - width
) / 2;
590 else // alignment & wxALIGN_LEFT
595 if ( alignment
& wxALIGN_BOTTOM
)
597 y
= rect
.GetBottom() - height
;
599 else if ( alignment
& wxALIGN_CENTRE_VERTICAL
)
601 y
= (rect
.GetTop() + rect
.GetBottom() + 1 - height
) / 2;
603 else // alignment & wxALIGN_TOP
608 // draw the bitmap first
614 DrawBitmap(bitmap
, x
, y
, true /* use mask */);
616 wxCoord offset
= bitmap
.GetWidth() + 4;
620 y
+= (height
- heightText
) / 2;
623 // we will draw the underscore under the accel char later
624 wxCoord startUnderscore
= 0,
628 // split the string into lines and draw each of them separately
630 for ( wxString::const_iterator pc
= text
.begin(); ; ++pc
)
632 if ( *pc
== _T('\n') || pc
== text
.end() )
634 int xRealStart
= x
; // init it here to avoid compielr warnings
636 if ( !curLine
.empty() )
638 // NB: can't test for !(alignment & wxALIGN_LEFT) because
640 if ( alignment
& (wxALIGN_RIGHT
| wxALIGN_CENTRE_HORIZONTAL
) )
643 GetTextExtent(curLine
, &widthLine
, NULL
);
645 if ( alignment
& wxALIGN_RIGHT
)
647 xRealStart
+= width
- widthLine
;
649 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
651 xRealStart
+= (width
- widthLine
) / 2;
654 //else: left aligned, nothing to do
656 DrawText(curLine
, xRealStart
, y
);
661 // do we have underscore in this line? we can check yUnderscore
662 // because it is set below to just y + heightLine if we do
663 if ( y
== yUnderscore
)
665 // adjust the horz positions to account for the shift
666 startUnderscore
+= xRealStart
;
667 endUnderscore
+= xRealStart
;
670 if ( pc
== text
.end() )
675 else // not end of line
677 if ( pc
- text
.begin() == (size_t)indexAccel
)
679 // remeber to draw underscore here
680 GetTextExtent(curLine
, &startUnderscore
, NULL
);
682 GetTextExtent(curLine
, &endUnderscore
, NULL
);
684 yUnderscore
= y
+ heightLine
;
693 // draw the underscore if found
694 if ( startUnderscore
!= endUnderscore
)
696 // it should be of the same colour as text
697 SetPen(wxPen(GetTextForeground(), 0, wxSOLID
));
701 DrawLine(startUnderscore
, yUnderscore
, endUnderscore
, yUnderscore
);
704 // return bounding rect if requested
707 *rectBounding
= wxRect(x
, y
- heightText
, widthText
, heightText
);
710 CalcBoundingBox(x0
, y0
);
711 CalcBoundingBox(x0
+ width0
, y0
+ height
);
715 void wxDCBase::DoGradientFillLinear(const wxRect
& rect
,
716 const wxColour
& initialColour
,
717 const wxColour
& destColour
,
718 wxDirection nDirection
)
721 wxPen oldPen
= m_pen
;
722 wxBrush oldBrush
= m_brush
;
724 wxUint8 nR1
= initialColour
.Red();
725 wxUint8 nG1
= initialColour
.Green();
726 wxUint8 nB1
= initialColour
.Blue();
727 wxUint8 nR2
= destColour
.Red();
728 wxUint8 nG2
= destColour
.Green();
729 wxUint8 nB2
= destColour
.Blue();
732 if ( nDirection
== wxEAST
|| nDirection
== wxWEST
)
734 wxInt32 x
= rect
.GetWidth();
735 wxInt32 w
= x
; // width of area to shade
736 wxInt32 xDelta
= w
/256; // height of one shade bend
744 nR
= nR1
- (nR1
-nR2
)*(w
-x
)/w
;
746 nR
= nR1
+ (nR2
-nR1
)*(w
-x
)/w
;
749 nG
= nG1
- (nG1
-nG2
)*(w
-x
)/w
;
751 nG
= nG1
+ (nG2
-nG1
)*(w
-x
)/w
;
754 nB
= nB1
- (nB1
-nB2
)*(w
-x
)/w
;
756 nB
= nB1
+ (nB2
-nB1
)*(w
-x
)/w
;
758 wxColour
colour(nR
,nG
,nB
);
759 SetPen(wxPen(colour
, 1, wxSOLID
));
760 SetBrush(wxBrush(colour
));
761 if(nDirection
== wxEAST
)
762 DrawRectangle(rect
.GetRight()-x
-xDelta
, rect
.GetTop(),
763 xDelta
, rect
.GetHeight());
764 else //nDirection == wxWEST
765 DrawRectangle(rect
.GetLeft()+x
, rect
.GetTop(),
766 xDelta
, rect
.GetHeight());
769 else // nDirection == wxNORTH || nDirection == wxSOUTH
771 wxInt32 y
= rect
.GetHeight();
772 wxInt32 w
= y
; // height of area to shade
773 wxInt32 yDelta
= w
/255; // height of one shade bend
781 nR
= nR1
- (nR1
-nR2
)*(w
-y
)/w
;
783 nR
= nR1
+ (nR2
-nR1
)*(w
-y
)/w
;
786 nG
= nG1
- (nG1
-nG2
)*(w
-y
)/w
;
788 nG
= nG1
+ (nG2
-nG1
)*(w
-y
)/w
;
791 nB
= nB1
- (nB1
-nB2
)*(w
-y
)/w
;
793 nB
= nB1
+ (nB2
-nB1
)*(w
-y
)/w
;
795 wxColour
colour(nR
,nG
,nB
);
796 SetPen(wxPen(colour
, 1, wxSOLID
));
797 SetBrush(wxBrush(colour
));
798 if(nDirection
== wxNORTH
)
799 DrawRectangle(rect
.GetLeft(), rect
.GetTop()+y
,
800 rect
.GetWidth(), yDelta
);
801 else //nDirection == wxSOUTH
802 DrawRectangle(rect
.GetLeft(), rect
.GetBottom()-y
-yDelta
,
803 rect
.GetWidth(), yDelta
);
811 void wxDCBase::DoGradientFillConcentric(const wxRect
& rect
,
812 const wxColour
& initialColour
,
813 const wxColour
& destColour
,
814 const wxPoint
& circleCenter
)
816 //save the old pen color
817 wxColour oldPenColour
= m_pen
.GetColour();
819 wxUint8 nR1
= destColour
.Red();
820 wxUint8 nG1
= destColour
.Green();
821 wxUint8 nB1
= destColour
.Blue();
822 wxUint8 nR2
= initialColour
.Red();
823 wxUint8 nG2
= initialColour
.Green();
824 wxUint8 nB2
= initialColour
.Blue();
829 wxInt32 cx
= rect
.GetWidth() / 2;
830 wxInt32 cy
= rect
.GetHeight() / 2;
838 wxInt32 nCircleOffX
= circleCenter
.x
- (rect
.GetWidth() / 2);
839 wxInt32 nCircleOffY
= circleCenter
.y
- (rect
.GetHeight() / 2);
841 for ( wxInt32 x
= 0; x
< rect
.GetWidth(); x
++ )
843 for ( wxInt32 y
= 0; y
< rect
.GetHeight(); y
++ )
845 //get color difference
846 wxInt32 nGradient
= ((nRadius
-
848 pow((double)(x
- cx
- nCircleOffX
), 2) +
849 pow((double)(y
- cy
- nCircleOffY
), 2)
857 nR
= (wxUint8
)(nR1
+ ((nR2
- nR1
) * nGradient
/ 100));
858 nG
= (wxUint8
)(nG1
+ ((nG2
- nG1
) * nGradient
/ 100));
859 nB
= (wxUint8
)(nB1
+ ((nB2
- nB1
) * nGradient
/ 100));
862 m_pen
.SetColour(wxColour(nR
,nG
,nB
));
863 DrawPoint(wxPoint(x
+ rect
.GetLeft(), y
+ rect
.GetTop()));
866 //return old pen color
867 m_pen
.SetColour(oldPenColour
);
871 Notes for wxWidgets DrawEllipticArcRot(...)
873 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
874 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
877 All methods are generic, so they can be implemented in wxDCBase.
878 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
879 methods like (WinCE) wxDC::DoDrawArc(...).
881 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
882 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
883 parts) or every column (in steep parts) only one pixel is calculated.
884 Trigonometric calculation (sin, cos, tan, atan) is only done if the
885 starting angle is not equal to the ending angle. The calculation of the
886 pixels is done using simple arithmetic only and should perform not too
887 bad even on devices without floating point processor. I didn't test this yet.
889 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
890 For instance: an ellipse rotated 180 degrees is drawn
891 slightly different from the original.
893 The points are then moved to an array and used to draw a polyline and/or polygon
894 (with center added, the pie).
895 The result looks quite similar to the native ellipse, only e few pixels differ.
897 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
898 slower as DrawEllipse(...), which calls the native API.
899 An rotated ellipse outside the clipping region takes nearly the same time,
900 while an native ellipse outside takes nearly no time to draw.
902 If you draw an arc with this new method, you will see the starting and ending angles
903 are calculated properly.
904 If you use DrawEllipticArc(...), you will see they are only correct for circles
905 and not properly calculated for ellipses.
908 p.lenhard@t-online.de
912 void wxDCBase::DoDrawEllipticArcRot( wxCoord x
, wxCoord y
,
913 wxCoord w
, wxCoord h
,
914 double sa
, double ea
, double angle
)
918 CalculateEllipticPoints( &list
, x
, y
, w
, h
, sa
, ea
);
919 Rotate( &list
, angle
, wxPoint( x
+w
/2, y
+h
/2 ) );
921 // Add center (for polygon/pie)
922 list
.Append( (wxObject
*) new wxPoint( x
+w
/2, y
+h
/2 ) );
924 // copy list into array and delete list elements
925 int n
= list
.GetCount();
926 wxPoint
*points
= new wxPoint
[n
];
929 for ( node
= list
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
931 wxPoint
*point
= (wxPoint
*)node
->GetData();
932 points
[i
].x
= point
->x
;
933 points
[i
].y
= point
->y
;
937 // first draw the pie without pen, if necessary
938 if( GetBrush() != *wxTRANSPARENT_BRUSH
)
940 wxPen
tempPen( GetPen() );
941 SetPen( *wxTRANSPARENT_PEN
);
942 DoDrawPolygon( n
, points
, 0, 0 );
946 // then draw the arc without brush, if necessary
947 if( GetPen() != *wxTRANSPARENT_PEN
)
950 DoDrawLines( n
-1, points
, 0, 0 );
955 } // DrawEllipticArcRot
957 void wxDCBase::Rotate( wxList
* points
, double angle
, wxPoint center
)
962 double dSinA
= -sin(angle
*2.0*pi
/360.0);
963 double dCosA
= cos(angle
*2.0*pi
/360.0);
964 for ( wxNode
* node
= points
->GetFirst(); node
; node
= node
->GetNext() )
966 wxPoint
* point
= (wxPoint
*)node
->GetData();
968 // transform coordinates, if necessary
969 if( center
.x
) point
->x
-= center
.x
;
970 if( center
.y
) point
->y
-= center
.y
;
972 // calculate rotation, rounding simply by implicit cast to integer
973 int xTemp
= point
->x
* dCosA
- point
->y
* dSinA
;
974 point
->y
= point
->x
* dSinA
+ point
->y
* dCosA
;
977 // back transform coordinates, if necessary
978 if( center
.x
) point
->x
+= center
.x
;
979 if( center
.y
) point
->y
+= center
.y
;
984 void wxDCBase::CalculateEllipticPoints( wxList
* points
,
985 wxCoord xStart
, wxCoord yStart
,
986 wxCoord w
, wxCoord h
,
987 double sa
, double ea
)
998 bool bUseAngles
= false;
1004 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
1006 if( 2*a
== w
) decrX
= 1;
1008 if( 2*b
== h
) decrY
= 1;
1010 wxCoord xCenter
= xStart
+ a
;
1011 wxCoord yCenter
= yStart
+ b
;
1012 // calculate data for start and end, if necessary
1016 // normalisation of angles
1017 while( sa
<0 ) sa
+= 360;
1018 while( ea
<0 ) ea
+= 360;
1019 while( sa
>=360 ) sa
-= 360;
1020 while( ea
>=360 ) ea
-= 360;
1021 // calculate quadrant numbers
1022 if( sa
> 270 ) sq
= 3;
1023 else if( sa
> 180 ) sq
= 2;
1024 else if( sa
> 90 ) sq
= 1;
1025 if( ea
> 270 ) eq
= 3;
1026 else if( ea
> 180 ) eq
= 2;
1027 else if( ea
> 90 ) eq
= 1;
1028 sar
= sa
* pi
/ 180.0;
1029 ear
= ea
* pi
/ 180.0;
1030 // correct angle circle -> ellipse
1031 sar
= atan( -a
/(double)b
* tan( sar
) );
1032 if ( sq
== 1 || sq
== 2 ) sar
+= pi
;
1033 ear
= atan( -a
/(double)b
* tan( ear
) );
1034 if ( eq
== 1 || eq
== 2 ) ear
+= pi
;
1035 // coordinates of points
1036 xsa
= xCenter
+ a
* cos( sar
);
1037 if( sq
== 0 || sq
== 3 ) xsa
-= decrX
;
1038 ysa
= yCenter
+ b
* sin( sar
);
1039 if( sq
== 2 || sq
== 3 ) ysa
-= decrY
;
1040 xea
= xCenter
+ a
* cos( ear
);
1041 if( eq
== 0 || eq
== 3 ) xea
-= decrX
;
1042 yea
= yCenter
+ b
* sin( ear
);
1043 if( eq
== 2 || eq
== 3 ) yea
-= decrY
;
1045 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1047 double c2
= 2.0 / w
;
1056 // Lists for quadrant 1 to 4
1057 wxList pointsarray
[4];
1058 // Calculate points for first quadrant and set in all quadrants
1059 for( x
= 0; x
<= a
; ++x
)
1064 bool bNewPoint
= false;
1065 while( y2
> c1
- c2
* x2
&& y
> 0 )
1071 // old y now to big: set point with old y, old x
1072 if( bNewPoint
&& x
>1)
1075 // remove points on the same line
1076 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
- y_old
) );
1077 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
- y_old
) );
1078 pointsarray
[2].Insert( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
+ y_old
- decrY
) );
1079 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
+ y_old
- decrY
) );
1081 } // calculate point
1083 // Starting and/or ending points for the quadrants, first quadrant gets both.
1084 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1085 pointsarray
[0].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
- b
) );
1086 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- a
, yCenter
) );
1087 pointsarray
[2].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
+ b
- decrY
) );
1088 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1090 // copy quadrants in original list
1093 // Copy the right part of the points in the lists
1094 // and delete the wxPoints, because they do not leave this method.
1095 points
->Append( (wxObject
*) new wxPoint( xsa
, ysa
) );
1097 bool bStarted
= false;
1098 bool bReady
= false;
1099 bool bForceTurn
= ( sq
== eq
&& sa
> ea
);
1102 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1104 // once: go to starting point in start quadrant
1107 ( (wxPoint
*) node
->GetData() )->x
< xsa
+1 && q
<= 1
1109 ( (wxPoint
*) node
->GetData() )->x
> xsa
-1 && q
>= 2
1116 // copy point, if not at ending point
1119 if( q
!= eq
|| bForceTurn
1121 ( (wxPoint
*) node
->GetData() )->x
> xea
+1 && q
<= 1
1123 ( (wxPoint
*) node
->GetData() )->x
< xea
-1 && q
>= 2
1127 wxPoint
* pPoint
= new wxPoint( *((wxPoint
*) node
->GetData() ) );
1128 points
->Append( (wxObject
*) pPoint
);
1130 else if( q
== eq
&& !bForceTurn
|| ( (wxPoint
*) node
->GetData() )->x
== xea
)
1140 } // while not bReady
1141 points
->Append( (wxObject
*) new wxPoint( xea
, yea
) );
1144 for( q
= 0; q
< 4; ++q
)
1146 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1148 wxPoint
*p
= (wxPoint
*)node
->GetData();
1156 // copy whole ellipse, wxPoints will be deleted outside
1157 for( node
= pointsarray
[0].GetFirst(); node
; node
= node
->GetNext() )
1159 wxObject
*p
= node
->GetData();
1160 points
->Append( p
);
1162 for( node
= pointsarray
[1].GetFirst(); node
; node
= node
->GetNext() )
1164 wxObject
*p
= node
->GetData();
1165 points
->Append( p
);
1167 for( node
= pointsarray
[2].GetFirst(); node
; node
= node
->GetNext() )
1169 wxObject
*p
= node
->GetData();
1170 points
->Append( p
);
1172 for( node
= pointsarray
[3].GetFirst(); node
; node
= node
->GetNext() )
1174 wxObject
*p
= node
->GetData();
1175 points
->Append( p
);
1178 } // CalculateEllipticPoints
1180 #endif // __WXWINCE__