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
)
46 : m_colour(wxColourDisplay())
50 , m_isBBoxValid(false)
51 , m_logicalOriginX(0), m_logicalOriginY(0)
52 , m_deviceOriginX(0), m_deviceOriginY(0)
53 , m_deviceLocalOriginX(0), m_deviceLocalOriginY(0)
54 , m_logicalScaleX(1.0), m_logicalScaleY(1.0)
55 , m_userScaleX(1.0), m_userScaleY(1.0)
56 , m_scaleX(1.0), m_scaleY(1.0)
57 , m_signX(1), m_signY(1)
58 , m_minX(0), m_minY(0), m_maxX(0), m_maxY(0)
59 , m_clipX1(0), m_clipY1(0), m_clipX2(0), m_clipY2(0)
60 , m_logicalFunction(wxCOPY
)
61 , m_backgroundMode(wxTRANSPARENT
)
62 , m_mappingMode(wxMM_TEXT
)
65 , m_backgroundBrush(*wxTRANSPARENT_BRUSH
)
66 , m_textForegroundColour(*wxBLACK
)
67 , m_textBackgroundColour(*wxWHITE
)
71 , m_hasCustomPalette(false)
72 #endif // wxUSE_PALETTE
74 m_mm_to_pix_x
= (double)wxGetDisplaySize().GetWidth() /
75 (double)wxGetDisplaySizeMM().GetWidth();
76 m_mm_to_pix_y
= (double)wxGetDisplaySize().GetHeight() /
77 (double)wxGetDisplaySizeMM().GetHeight();
87 #if WXWIN_COMPATIBILITY_2_6
88 void wxDCBase::BeginDrawing()
92 void wxDCBase::EndDrawing()
95 #endif // WXWIN_COMPATIBILITY_2_6
97 // ----------------------------------------------------------------------------
98 // coordinate conversions and transforms
99 // ----------------------------------------------------------------------------
101 wxCoord
wxDCBase::DeviceToLogicalX(wxCoord x
) const
103 return wxRound((double)(x
- m_deviceOriginX
- m_deviceLocalOriginX
) / m_scaleX
) * m_signX
+ m_logicalOriginX
;
106 wxCoord
wxDCBase::DeviceToLogicalY(wxCoord y
) const
108 return wxRound((double)(y
- m_deviceOriginY
- m_deviceLocalOriginY
) / m_scaleY
) * m_signY
+ m_logicalOriginY
;
111 wxCoord
wxDCBase::DeviceToLogicalXRel(wxCoord x
) const
113 return wxRound((double)(x
) / m_scaleX
);
116 wxCoord
wxDCBase::DeviceToLogicalYRel(wxCoord y
) const
118 return wxRound((double)(y
) / m_scaleY
);
121 wxCoord
wxDCBase::LogicalToDeviceX(wxCoord x
) const
123 return wxRound((double)(x
- m_logicalOriginX
) * m_scaleX
) * m_signX
+ m_deviceOriginX
+ m_deviceLocalOriginX
;
126 wxCoord
wxDCBase::LogicalToDeviceY(wxCoord y
) const
128 return wxRound((double)(y
- m_logicalOriginY
) * m_scaleY
) * m_signY
+ m_deviceOriginY
+ m_deviceLocalOriginY
;
131 wxCoord
wxDCBase::LogicalToDeviceXRel(wxCoord x
) const
133 return wxRound((double)(x
) * m_scaleX
);
136 wxCoord
wxDCBase::LogicalToDeviceYRel(wxCoord y
) const
138 return wxRound((double)(y
) * m_scaleY
);
141 void wxDCBase::ComputeScaleAndOrigin()
143 m_scaleX
= m_logicalScaleX
* m_userScaleX
;
144 m_scaleY
= m_logicalScaleY
* m_userScaleY
;
147 void wxDCBase::SetMapMode( int mode
)
152 SetLogicalScale( twips2mm
*m_mm_to_pix_x
, twips2mm
*m_mm_to_pix_y
);
155 SetLogicalScale( pt2mm
*m_mm_to_pix_x
, pt2mm
*m_mm_to_pix_y
);
158 SetLogicalScale( m_mm_to_pix_x
, m_mm_to_pix_y
);
161 SetLogicalScale( m_mm_to_pix_x
/10.0, m_mm_to_pix_y
/10.0 );
165 SetLogicalScale( 1.0, 1.0 );
168 m_mappingMode
= mode
;
171 void wxDCBase::SetUserScale( double x
, double y
)
173 // allow negative ? -> no
176 ComputeScaleAndOrigin();
179 void wxDCBase::SetLogicalScale( double x
, double y
)
184 ComputeScaleAndOrigin();
187 void wxDCBase::SetLogicalOrigin( wxCoord x
, wxCoord y
)
189 m_logicalOriginX
= x
* m_signX
;
190 m_logicalOriginY
= y
* m_signY
;
191 ComputeScaleAndOrigin();
194 void wxDCBase::SetDeviceOrigin( wxCoord x
, wxCoord y
)
198 ComputeScaleAndOrigin();
201 void wxDCBase::SetDeviceLocalOrigin( wxCoord x
, wxCoord y
)
203 m_deviceLocalOriginX
= x
;
204 m_deviceLocalOriginY
= y
;
205 ComputeScaleAndOrigin();
208 void wxDCBase::SetAxisOrientation( bool xLeftRight
, bool yBottomUp
)
210 // only wxPostScripDC has m_signX = -1, we override SetAxisOrientation there
211 m_signX
= (xLeftRight
? 1 : -1);
212 m_signY
= (yBottomUp
? -1 : 1);
213 ComputeScaleAndOrigin();
216 // ----------------------------------------------------------------------------
218 // ----------------------------------------------------------------------------
220 void wxDCBase::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
221 wxCoord width
, wxCoord height
)
223 wxCHECK_RET( Ok(), wxT("invalid window dc") );
225 wxCoord x2
= x1
+ width
,
228 // the pen width is calibrated to give 3 for width == height == 10
229 wxDCPenChanger
pen((wxDC
&)*this,
230 wxPen(GetTextForeground(), (width
+ height
+ 1)/7));
232 // we're drawing a scaled version of wx/generic/tick.xpm here
233 wxCoord x3
= x1
+ (4*width
) / 10, // x of the tick bottom
234 y3
= y1
+ height
/ 2; // y of the left tick branch
235 DoDrawLine(x1
, y3
, x3
, y2
);
236 DoDrawLine(x3
, y2
, x2
, y1
);
238 CalcBoundingBox(x1
, y1
);
239 CalcBoundingBox(x2
, y2
);
242 // ----------------------------------------------------------------------------
243 // stubs for functions not implemented in all ports
244 // ----------------------------------------------------------------------------
247 wxDCBase::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
248 wxCoord dstWidth
, wxCoord dstHeight
,
250 wxCoord xsrc
, wxCoord ysrc
,
251 wxCoord srcWidth
, wxCoord srcHeight
,
257 wxCHECK_MSG( srcWidth
&& srcHeight
&& dstWidth
&& dstHeight
, false,
258 _T("invalid blit size") );
260 // emulate the stretching by modifying the DC scale
261 double xscale
= (double)srcWidth
/dstWidth
,
262 yscale
= (double)srcHeight
/dstHeight
;
264 double xscaleOld
, yscaleOld
;
265 GetUserScale(&xscaleOld
, &yscaleOld
);
266 SetUserScale(xscaleOld
/xscale
, yscaleOld
/yscale
);
268 bool rc
= DoBlit(wxCoord(xdest
*xscale
), wxCoord(ydest
*yscale
),
269 wxCoord(dstWidth
*xscale
), wxCoord(dstHeight
*yscale
),
271 xsrc
, ysrc
, rop
, useMask
, xsrcMask
, ysrcMask
);
273 SetUserScale(xscaleOld
, yscaleOld
);
278 // ----------------------------------------------------------------------------
280 // ----------------------------------------------------------------------------
282 void wxDCBase::DrawLines(const wxList
*list
, wxCoord xoffset
, wxCoord yoffset
)
284 int n
= list
->GetCount();
285 wxPoint
*points
= new wxPoint
[n
];
288 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
290 wxPoint
*point
= (wxPoint
*)node
->GetData();
291 points
[i
].x
= point
->x
;
292 points
[i
].y
= point
->y
;
295 DoDrawLines(n
, points
, xoffset
, yoffset
);
301 void wxDCBase::DrawPolygon(const wxList
*list
,
302 wxCoord xoffset
, wxCoord yoffset
,
305 int n
= list
->GetCount();
306 wxPoint
*points
= new wxPoint
[n
];
309 for ( wxList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
311 wxPoint
*point
= (wxPoint
*)node
->GetData();
312 points
[i
].x
= point
->x
;
313 points
[i
].y
= point
->y
;
316 DoDrawPolygon(n
, points
, xoffset
, yoffset
, fillStyle
);
322 wxDCBase::DoDrawPolyPolygon(int n
,
325 wxCoord xoffset
, wxCoord yoffset
,
330 DoDrawPolygon(count
[0], points
, xoffset
, yoffset
, fillStyle
);
338 for (i
= j
= lastOfs
= 0; i
< n
; i
++)
343 pts
= new wxPoint
[j
+n
-1];
344 for (i
= 0; i
< j
; i
++)
346 for (i
= 2; i
<= n
; i
++)
348 lastOfs
-= count
[n
-i
];
349 pts
[j
++] = pts
[lastOfs
];
353 SetPen(wxPen(*wxBLACK
, 0, wxTRANSPARENT
));
354 DoDrawPolygon(j
, pts
, xoffset
, yoffset
, fillStyle
);
356 for (i
= j
= 0; i
< n
; i
++)
358 DoDrawLines(count
[i
], pts
+j
, xoffset
, yoffset
);
364 // ----------------------------------------------------------------------------
366 // ----------------------------------------------------------------------------
370 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
371 void wxDCBase::DrawSpline(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
, wxCoord x3
, wxCoord y3
)
375 wxPoint
*point1
= new wxPoint
;
376 point1
->x
= x1
; point1
->y
= y1
;
377 point_list
.Append((wxObject
*)point1
);
379 wxPoint
*point2
= new wxPoint
;
380 point2
->x
= x2
; point2
->y
= y2
;
381 point_list
.Append((wxObject
*)point2
);
383 wxPoint
*point3
= new wxPoint
;
384 point3
->x
= x3
; point3
->y
= y3
;
385 point_list
.Append((wxObject
*)point3
);
387 DrawSpline(&point_list
);
389 for( wxList::compatibility_iterator node
= point_list
.GetFirst(); node
; node
= node
->GetNext() )
391 wxPoint
*p
= (wxPoint
*)node
->GetData();
396 void wxDCBase::DrawSpline(int n
, wxPoint points
[])
399 for (int i
=0; i
< n
; i
++)
401 list
.Append((wxObject
*)&points
[i
]);
407 // ----------------------------------- spline code ----------------------------------------
409 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
410 double a3
, double b3
, double a4
, double b4
);
411 void wx_clear_stack();
412 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
413 double *y3
, double *x4
, double *y4
);
414 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
415 double x4
, double y4
);
416 static bool wx_spline_add_point(double x
, double y
);
417 static void wx_spline_draw_point_array(wxDCBase
*dc
);
419 wxList wx_spline_point_list
;
421 #define half(z1, z2) ((z1+z2)/2.0)
424 /* iterative version */
426 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
429 register double xmid
, ymid
;
430 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
433 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
435 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
436 xmid
= (double)half(x2
, x3
);
437 ymid
= (double)half(y2
, y3
);
438 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
439 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
440 wx_spline_add_point( x1
, y1
);
441 wx_spline_add_point( xmid
, ymid
);
443 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
444 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
445 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
446 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
451 /* utilities used by spline drawing routines */
453 typedef struct wx_spline_stack_struct
{
454 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
457 #define SPLINE_STACK_DEPTH 20
458 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
459 static Stack
*wx_stack_top
;
460 static int wx_stack_count
;
462 void wx_clear_stack()
464 wx_stack_top
= wx_spline_stack
;
468 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
470 wx_stack_top
->x1
= x1
;
471 wx_stack_top
->y1
= y1
;
472 wx_stack_top
->x2
= x2
;
473 wx_stack_top
->y2
= y2
;
474 wx_stack_top
->x3
= x3
;
475 wx_stack_top
->y3
= y3
;
476 wx_stack_top
->x4
= x4
;
477 wx_stack_top
->y4
= y4
;
482 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
483 double *x3
, double *y3
, double *x4
, double *y4
)
485 if (wx_stack_count
== 0)
489 *x1
= wx_stack_top
->x1
;
490 *y1
= wx_stack_top
->y1
;
491 *x2
= wx_stack_top
->x2
;
492 *y2
= wx_stack_top
->y2
;
493 *x3
= wx_stack_top
->x3
;
494 *y3
= wx_stack_top
->y3
;
495 *x4
= wx_stack_top
->x4
;
496 *y4
= wx_stack_top
->y4
;
500 static bool wx_spline_add_point(double x
, double y
)
502 wxPoint
*point
= new wxPoint
;
505 wx_spline_point_list
.Append((wxObject
*)point
);
509 static void wx_spline_draw_point_array(wxDCBase
*dc
)
511 dc
->DrawLines(&wx_spline_point_list
, 0, 0 );
512 wxList::compatibility_iterator node
= wx_spline_point_list
.GetFirst();
515 wxPoint
*point
= (wxPoint
*)node
->GetData();
517 wx_spline_point_list
.Erase(node
);
518 node
= wx_spline_point_list
.GetFirst();
522 void wxDCBase::DoDrawSpline( wxList
*points
)
524 wxCHECK_RET( Ok(), wxT("invalid window dc") );
527 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
528 double x1
, y1
, x2
, y2
;
530 wxList::compatibility_iterator node
= points
->GetFirst();
535 p
= (wxPoint
*)node
->GetData();
540 node
= node
->GetNext();
541 p
= (wxPoint
*)node
->GetData();
545 cx1
= (double)((x1
+ x2
) / 2);
546 cy1
= (double)((y1
+ y2
) / 2);
547 cx2
= (double)((cx1
+ x2
) / 2);
548 cy2
= (double)((cy1
+ y2
) / 2);
550 wx_spline_add_point(x1
, y1
);
552 while ((node
= node
->GetNext())
558 p
= (wxPoint
*)node
->GetData();
563 cx4
= (double)(x1
+ x2
) / 2;
564 cy4
= (double)(y1
+ y2
) / 2;
565 cx3
= (double)(x1
+ cx4
) / 2;
566 cy3
= (double)(y1
+ cy4
) / 2;
568 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
572 cx2
= (double)(cx1
+ x2
) / 2;
573 cy2
= (double)(cy1
+ y2
) / 2;
576 wx_spline_add_point( cx1
, cy1
);
577 wx_spline_add_point( x2
, y2
);
579 wx_spline_draw_point_array( this );
582 #endif // wxUSE_SPLINES
584 // ----------------------------------------------------------------------------
585 // Partial Text Extents
586 // ----------------------------------------------------------------------------
589 // Each element of the widths array will be the width of the string up to and
590 // including the corresponding character in text. This is the generic
591 // implementation, the port-specific classes should do this with native APIs
592 // if available and if faster. Note: pango_layout_index_to_pos is much slower
593 // than calling GetTextExtent!!
600 FontWidthCache() : m_scaleX(1), m_widths(NULL
) { }
601 ~FontWidthCache() { delete []m_widths
; }
606 m_widths
= new int[FWC_SIZE
];
608 memset(m_widths
, 0, sizeof(int)*FWC_SIZE
);
616 static FontWidthCache s_fontWidthCache
;
618 bool wxDCBase::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
622 const size_t len
= text
.length();
626 // reset the cache if font or horizontal scale have changed
627 if ( !s_fontWidthCache
.m_widths
||
628 !wxIsSameDouble(s_fontWidthCache
.m_scaleX
, m_scaleX
) ||
629 (s_fontWidthCache
.m_font
!= GetFont()) )
631 s_fontWidthCache
.Reset();
632 s_fontWidthCache
.m_font
= GetFont();
633 s_fontWidthCache
.m_scaleX
= m_scaleX
;
636 // Calculate the position of each character based on the widths of
637 // the previous characters
639 for ( size_t i
= 0; i
< len
; i
++ )
641 const wxChar c
= text
[i
];
642 unsigned int c_int
= (unsigned int)c
;
644 if ((c_int
< FWC_SIZE
) && (s_fontWidthCache
.m_widths
[c_int
] != 0))
646 w
= s_fontWidthCache
.m_widths
[c_int
];
650 GetTextExtent(c
, &w
, &h
);
651 if (c_int
< FWC_SIZE
)
652 s_fontWidthCache
.m_widths
[c_int
] = w
;
656 widths
[i
] = totalWidth
;
663 // ----------------------------------------------------------------------------
664 // enhanced text drawing
665 // ----------------------------------------------------------------------------
667 void wxDCBase::GetMultiLineTextExtent(const wxString
& text
,
671 const wxFont
*font
) const
673 wxCoord widthTextMax
= 0, widthLine
,
674 heightTextTotal
= 0, heightLineDefault
= 0, heightLine
= 0;
677 for ( const wxChar
*pc
= text
; ; pc
++ )
679 if ( *pc
== _T('\n') || *pc
== _T('\0') )
681 if ( curLine
.empty() )
683 // we can't use GetTextExtent - it will return 0 for both width
684 // and height and an empty line should count in height
687 // assume that this line has the same height as the previous
689 if ( !heightLineDefault
)
690 heightLineDefault
= heightLine
;
692 if ( !heightLineDefault
)
694 // but we don't know it yet - choose something reasonable
695 GetTextExtent(_T("W"), NULL
, &heightLineDefault
,
699 heightTextTotal
+= heightLineDefault
;
703 GetTextExtent(curLine
, &widthLine
, &heightLine
,
705 if ( widthLine
> widthTextMax
)
706 widthTextMax
= widthLine
;
707 heightTextTotal
+= heightLine
;
710 if ( *pc
== _T('\n') )
729 *y
= heightTextTotal
;
734 void wxDCBase::DrawLabel(const wxString
& text
,
735 const wxBitmap
& bitmap
,
739 wxRect
*rectBounding
)
741 // find the text position
742 wxCoord widthText
, heightText
, heightLine
;
743 GetMultiLineTextExtent(text
, &widthText
, &heightText
, &heightLine
);
745 wxCoord width
, height
;
748 width
= widthText
+ bitmap
.GetWidth();
749 height
= bitmap
.GetHeight();
758 if ( alignment
& wxALIGN_RIGHT
)
760 x
= rect
.GetRight() - width
;
762 else if ( alignment
& wxALIGN_CENTRE_HORIZONTAL
)
764 x
= (rect
.GetLeft() + rect
.GetRight() + 1 - width
) / 2;
766 else // alignment & wxALIGN_LEFT
771 if ( alignment
& wxALIGN_BOTTOM
)
773 y
= rect
.GetBottom() - height
;
775 else if ( alignment
& wxALIGN_CENTRE_VERTICAL
)
777 y
= (rect
.GetTop() + rect
.GetBottom() + 1 - height
) / 2;
779 else // alignment & wxALIGN_TOP
784 // draw the bitmap first
790 DrawBitmap(bitmap
, x
, y
, true /* use mask */);
792 wxCoord offset
= bitmap
.GetWidth() + 4;
796 y
+= (height
- heightText
) / 2;
799 // we will draw the underscore under the accel char later
800 wxCoord startUnderscore
= 0,
804 // split the string into lines and draw each of them separately
806 for ( wxString::const_iterator pc
= text
.begin(); ; ++pc
)
808 if ( *pc
== _T('\n') || pc
== text
.end() )
810 int xRealStart
= x
; // init it here to avoid compielr warnings
812 if ( !curLine
.empty() )
814 // NB: can't test for !(alignment & wxALIGN_LEFT) because
816 if ( alignment
& (wxALIGN_RIGHT
| wxALIGN_CENTRE_HORIZONTAL
) )
819 GetTextExtent(curLine
, &widthLine
, NULL
);
821 if ( alignment
& wxALIGN_RIGHT
)
823 xRealStart
+= width
- widthLine
;
825 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
827 xRealStart
+= (width
- widthLine
) / 2;
830 //else: left aligned, nothing to do
832 DrawText(curLine
, xRealStart
, y
);
837 // do we have underscore in this line? we can check yUnderscore
838 // because it is set below to just y + heightLine if we do
839 if ( y
== yUnderscore
)
841 // adjust the horz positions to account for the shift
842 startUnderscore
+= xRealStart
;
843 endUnderscore
+= xRealStart
;
846 if ( pc
== text
.end() )
851 else // not end of line
853 if ( pc
- text
.begin() == indexAccel
)
855 // remeber to draw underscore here
856 GetTextExtent(curLine
, &startUnderscore
, NULL
);
858 GetTextExtent(curLine
, &endUnderscore
, NULL
);
860 yUnderscore
= y
+ heightLine
;
869 // draw the underscore if found
870 if ( startUnderscore
!= endUnderscore
)
872 // it should be of the same colour as text
873 SetPen(wxPen(GetTextForeground(), 0, wxSOLID
));
877 DrawLine(startUnderscore
, yUnderscore
, endUnderscore
, yUnderscore
);
880 // return bounding rect if requested
883 *rectBounding
= wxRect(x
, y
- heightText
, widthText
, heightText
);
886 CalcBoundingBox(x0
, y0
);
887 CalcBoundingBox(x0
+ width0
, y0
+ height
);
891 void wxDCBase::DoGradientFillLinear(const wxRect
& rect
,
892 const wxColour
& initialColour
,
893 const wxColour
& destColour
,
894 wxDirection nDirection
)
897 wxPen oldPen
= m_pen
;
898 wxBrush oldBrush
= m_brush
;
900 wxUint8 nR1
= initialColour
.Red();
901 wxUint8 nG1
= initialColour
.Green();
902 wxUint8 nB1
= initialColour
.Blue();
903 wxUint8 nR2
= destColour
.Red();
904 wxUint8 nG2
= destColour
.Green();
905 wxUint8 nB2
= destColour
.Blue();
908 if ( nDirection
== wxEAST
|| nDirection
== wxWEST
)
910 wxInt32 x
= rect
.GetWidth();
911 wxInt32 w
= x
; // width of area to shade
912 wxInt32 xDelta
= w
/256; // height of one shade bend
920 nR
= nR1
- (nR1
-nR2
)*(w
-x
)/w
;
922 nR
= nR1
+ (nR2
-nR1
)*(w
-x
)/w
;
925 nG
= nG1
- (nG1
-nG2
)*(w
-x
)/w
;
927 nG
= nG1
+ (nG2
-nG1
)*(w
-x
)/w
;
930 nB
= nB1
- (nB1
-nB2
)*(w
-x
)/w
;
932 nB
= nB1
+ (nB2
-nB1
)*(w
-x
)/w
;
934 wxColour
colour(nR
,nG
,nB
);
935 SetPen(wxPen(colour
, 1, wxSOLID
));
936 SetBrush(wxBrush(colour
));
937 if(nDirection
== wxEAST
)
938 DrawRectangle(rect
.GetRight()-x
-xDelta
, rect
.GetTop(),
939 xDelta
, rect
.GetHeight());
940 else //nDirection == wxWEST
941 DrawRectangle(rect
.GetLeft()+x
, rect
.GetTop(),
942 xDelta
, rect
.GetHeight());
945 else // nDirection == wxNORTH || nDirection == wxSOUTH
947 wxInt32 y
= rect
.GetHeight();
948 wxInt32 w
= y
; // height of area to shade
949 wxInt32 yDelta
= w
/255; // height of one shade bend
957 nR
= nR1
- (nR1
-nR2
)*(w
-y
)/w
;
959 nR
= nR1
+ (nR2
-nR1
)*(w
-y
)/w
;
962 nG
= nG1
- (nG1
-nG2
)*(w
-y
)/w
;
964 nG
= nG1
+ (nG2
-nG1
)*(w
-y
)/w
;
967 nB
= nB1
- (nB1
-nB2
)*(w
-y
)/w
;
969 nB
= nB1
+ (nB2
-nB1
)*(w
-y
)/w
;
971 wxColour
colour(nR
,nG
,nB
);
972 SetPen(wxPen(colour
, 1, wxSOLID
));
973 SetBrush(wxBrush(colour
));
974 if(nDirection
== wxNORTH
)
975 DrawRectangle(rect
.GetLeft(), rect
.GetTop()+y
,
976 rect
.GetWidth(), yDelta
);
977 else //nDirection == wxSOUTH
978 DrawRectangle(rect
.GetLeft(), rect
.GetBottom()-y
-yDelta
,
979 rect
.GetWidth(), yDelta
);
987 void wxDCBase::DoGradientFillConcentric(const wxRect
& rect
,
988 const wxColour
& initialColour
,
989 const wxColour
& destColour
,
990 const wxPoint
& circleCenter
)
992 //save the old pen color
993 wxColour oldPenColour
= m_pen
.GetColour();
995 wxUint8 nR1
= destColour
.Red();
996 wxUint8 nG1
= destColour
.Green();
997 wxUint8 nB1
= destColour
.Blue();
998 wxUint8 nR2
= initialColour
.Red();
999 wxUint8 nG2
= initialColour
.Green();
1000 wxUint8 nB2
= initialColour
.Blue();
1005 wxInt32 cx
= rect
.GetWidth() / 2;
1006 wxInt32 cy
= rect
.GetHeight() / 2;
1014 wxInt32 nCircleOffX
= circleCenter
.x
- (rect
.GetWidth() / 2);
1015 wxInt32 nCircleOffY
= circleCenter
.y
- (rect
.GetHeight() / 2);
1017 for ( wxInt32 x
= 0; x
< rect
.GetWidth(); x
++ )
1019 for ( wxInt32 y
= 0; y
< rect
.GetHeight(); y
++ )
1021 //get color difference
1022 wxInt32 nGradient
= ((nRadius
-
1024 pow((double)(x
- cx
- nCircleOffX
), 2) +
1025 pow((double)(y
- cy
- nCircleOffY
), 2)
1026 )) * 100) / nRadius
;
1028 //normalize Gradient
1033 nR
= (wxUint8
)(nR1
+ ((nR2
- nR1
) * nGradient
/ 100));
1034 nG
= (wxUint8
)(nG1
+ ((nG2
- nG1
) * nGradient
/ 100));
1035 nB
= (wxUint8
)(nB1
+ ((nB2
- nB1
) * nGradient
/ 100));
1038 m_pen
.SetColour(wxColour(nR
,nG
,nB
));
1039 DrawPoint(wxPoint(x
+ rect
.GetLeft(), y
+ rect
.GetTop()));
1042 //return old pen color
1043 m_pen
.SetColour(oldPenColour
);
1047 Notes for wxWidgets DrawEllipticArcRot(...)
1049 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
1050 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
1053 All methods are generic, so they can be implemented in wxDCBase.
1054 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
1055 methods like (WinCE) wxDC::DoDrawArc(...).
1057 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
1058 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
1059 parts) or every column (in steep parts) only one pixel is calculated.
1060 Trigonometric calculation (sin, cos, tan, atan) is only done if the
1061 starting angle is not equal to the ending angle. The calculation of the
1062 pixels is done using simple arithmetic only and should perform not too
1063 bad even on devices without floating point processor. I didn't test this yet.
1065 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
1066 For instance: an ellipse rotated 180 degrees is drawn
1067 slightly different from the original.
1069 The points are then moved to an array and used to draw a polyline and/or polygon
1070 (with center added, the pie).
1071 The result looks quite similar to the native ellipse, only e few pixels differ.
1073 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
1074 slower as DrawEllipse(...), which calls the native API.
1075 An rotated ellipse outside the clipping region takes nearly the same time,
1076 while an native ellipse outside takes nearly no time to draw.
1078 If you draw an arc with this new method, you will see the starting and ending angles
1079 are calculated properly.
1080 If you use DrawEllipticArc(...), you will see they are only correct for circles
1081 and not properly calculated for ellipses.
1084 p.lenhard@t-online.de
1088 void wxDCBase::DoDrawEllipticArcRot( wxCoord x
, wxCoord y
,
1089 wxCoord w
, wxCoord h
,
1090 double sa
, double ea
, double angle
)
1094 CalculateEllipticPoints( &list
, x
, y
, w
, h
, sa
, ea
);
1095 Rotate( &list
, angle
, wxPoint( x
+w
/2, y
+h
/2 ) );
1097 // Add center (for polygon/pie)
1098 list
.Append( (wxObject
*) new wxPoint( x
+w
/2, y
+h
/2 ) );
1100 // copy list into array and delete list elements
1101 int n
= list
.GetCount();
1102 wxPoint
*points
= new wxPoint
[n
];
1105 for ( node
= list
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
1107 wxPoint
*point
= (wxPoint
*)node
->GetData();
1108 points
[i
].x
= point
->x
;
1109 points
[i
].y
= point
->y
;
1113 // first draw the pie without pen, if necessary
1114 if( GetBrush() != *wxTRANSPARENT_BRUSH
)
1116 wxPen
tempPen( GetPen() );
1117 SetPen( *wxTRANSPARENT_PEN
);
1118 DoDrawPolygon( n
, points
, 0, 0 );
1122 // then draw the arc without brush, if necessary
1123 if( GetPen() != *wxTRANSPARENT_PEN
)
1126 DoDrawLines( n
-1, points
, 0, 0 );
1131 } // DrawEllipticArcRot
1133 void wxDCBase::Rotate( wxList
* points
, double angle
, wxPoint center
)
1138 double dSinA
= -sin(angle
*2.0*pi
/360.0);
1139 double dCosA
= cos(angle
*2.0*pi
/360.0);
1140 for ( wxNode
* node
= points
->GetFirst(); node
; node
= node
->GetNext() )
1142 wxPoint
* point
= (wxPoint
*)node
->GetData();
1144 // transform coordinates, if necessary
1145 if( center
.x
) point
->x
-= center
.x
;
1146 if( center
.y
) point
->y
-= center
.y
;
1148 // calculate rotation, rounding simply by implicit cast to integer
1149 int xTemp
= point
->x
* dCosA
- point
->y
* dSinA
;
1150 point
->y
= point
->x
* dSinA
+ point
->y
* dCosA
;
1153 // back transform coordinates, if necessary
1154 if( center
.x
) point
->x
+= center
.x
;
1155 if( center
.y
) point
->y
+= center
.y
;
1160 void wxDCBase::CalculateEllipticPoints( wxList
* points
,
1161 wxCoord xStart
, wxCoord yStart
,
1162 wxCoord w
, wxCoord h
,
1163 double sa
, double ea
)
1174 bool bUseAngles
= false;
1180 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
1182 if( 2*a
== w
) decrX
= 1;
1184 if( 2*b
== h
) decrY
= 1;
1186 wxCoord xCenter
= xStart
+ a
;
1187 wxCoord yCenter
= yStart
+ b
;
1188 // calculate data for start and end, if necessary
1192 // normalisation of angles
1193 while( sa
<0 ) sa
+= 360;
1194 while( ea
<0 ) ea
+= 360;
1195 while( sa
>=360 ) sa
-= 360;
1196 while( ea
>=360 ) ea
-= 360;
1197 // calculate quadrant numbers
1198 if( sa
> 270 ) sq
= 3;
1199 else if( sa
> 180 ) sq
= 2;
1200 else if( sa
> 90 ) sq
= 1;
1201 if( ea
> 270 ) eq
= 3;
1202 else if( ea
> 180 ) eq
= 2;
1203 else if( ea
> 90 ) eq
= 1;
1204 sar
= sa
* pi
/ 180.0;
1205 ear
= ea
* pi
/ 180.0;
1206 // correct angle circle -> ellipse
1207 sar
= atan( -a
/(double)b
* tan( sar
) );
1208 if ( sq
== 1 || sq
== 2 ) sar
+= pi
;
1209 ear
= atan( -a
/(double)b
* tan( ear
) );
1210 if ( eq
== 1 || eq
== 2 ) ear
+= pi
;
1211 // coordinates of points
1212 xsa
= xCenter
+ a
* cos( sar
);
1213 if( sq
== 0 || sq
== 3 ) xsa
-= decrX
;
1214 ysa
= yCenter
+ b
* sin( sar
);
1215 if( sq
== 2 || sq
== 3 ) ysa
-= decrY
;
1216 xea
= xCenter
+ a
* cos( ear
);
1217 if( eq
== 0 || eq
== 3 ) xea
-= decrX
;
1218 yea
= yCenter
+ b
* sin( ear
);
1219 if( eq
== 2 || eq
== 3 ) yea
-= decrY
;
1221 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1223 double c2
= 2.0 / w
;
1232 // Lists for quadrant 1 to 4
1233 wxList pointsarray
[4];
1234 // Calculate points for first quadrant and set in all quadrants
1235 for( x
= 0; x
<= a
; ++x
)
1240 bool bNewPoint
= false;
1241 while( y2
> c1
- c2
* x2
&& y
> 0 )
1247 // old y now to big: set point with old y, old x
1248 if( bNewPoint
&& x
>1)
1251 // remove points on the same line
1252 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
- y_old
) );
1253 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
- y_old
) );
1254 pointsarray
[2].Insert( (wxObject
*) new wxPoint( xCenter
- x1
, yCenter
+ y_old
- decrY
) );
1255 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ x1
- decrX
, yCenter
+ y_old
- decrY
) );
1257 } // calculate point
1259 // Starting and/or ending points for the quadrants, first quadrant gets both.
1260 pointsarray
[0].Insert( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1261 pointsarray
[0].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
- b
) );
1262 pointsarray
[1].Append( (wxObject
*) new wxPoint( xCenter
- a
, yCenter
) );
1263 pointsarray
[2].Append( (wxObject
*) new wxPoint( xCenter
, yCenter
+ b
- decrY
) );
1264 pointsarray
[3].Append( (wxObject
*) new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1266 // copy quadrants in original list
1269 // Copy the right part of the points in the lists
1270 // and delete the wxPoints, because they do not leave this method.
1271 points
->Append( (wxObject
*) new wxPoint( xsa
, ysa
) );
1273 bool bStarted
= false;
1274 bool bReady
= false;
1275 bool bForceTurn
= ( sq
== eq
&& sa
> ea
);
1278 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1280 // once: go to starting point in start quadrant
1283 ( (wxPoint
*) node
->GetData() )->x
< xsa
+1 && q
<= 1
1285 ( (wxPoint
*) node
->GetData() )->x
> xsa
-1 && q
>= 2
1292 // copy point, if not at ending point
1295 if( q
!= eq
|| bForceTurn
1297 ( (wxPoint
*) node
->GetData() )->x
> xea
+1 && q
<= 1
1299 ( (wxPoint
*) node
->GetData() )->x
< xea
-1 && q
>= 2
1303 wxPoint
* pPoint
= new wxPoint( *((wxPoint
*) node
->GetData() ) );
1304 points
->Append( (wxObject
*) pPoint
);
1306 else if( q
== eq
&& !bForceTurn
|| ( (wxPoint
*) node
->GetData() )->x
== xea
)
1316 } // while not bReady
1317 points
->Append( (wxObject
*) new wxPoint( xea
, yea
) );
1320 for( q
= 0; q
< 4; ++q
)
1322 for( wxNode
*node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1324 wxPoint
*p
= (wxPoint
*)node
->GetData();
1332 // copy whole ellipse, wxPoints will be deleted outside
1333 for( node
= pointsarray
[0].GetFirst(); node
; node
= node
->GetNext() )
1335 wxObject
*p
= node
->GetData();
1336 points
->Append( p
);
1338 for( node
= pointsarray
[1].GetFirst(); node
; node
= node
->GetNext() )
1340 wxObject
*p
= node
->GetData();
1341 points
->Append( p
);
1343 for( node
= pointsarray
[2].GetFirst(); node
; node
= node
->GetNext() )
1345 wxObject
*p
= node
->GetData();
1346 points
->Append( p
);
1348 for( node
= pointsarray
[3].GetFirst(); node
; node
= node
->GetNext() )
1350 wxObject
*p
= node
->GetData();
1351 points
->Append( p
);
1354 } // CalculateEllipticPoints
1356 #endif // __WXWINCE__