Include wx/math.h according to precompiled headers of wx/wx.h (with other minor clean...
[wxWidgets.git] / src / common / dcbase.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/dcbase.cpp
3 // Purpose: generic methods of the wxDC Class
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 05/25/99
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/dc.h"
28
29 #ifndef WX_PRECOMP
30 #include "wx/math.h"
31 #endif
32
33 // bool wxDCBase::sm_cacheing = false;
34
35 IMPLEMENT_ABSTRACT_CLASS(wxDCBase, wxObject)
36
37 // ============================================================================
38 // implementation
39 // ============================================================================
40
41 #if WXWIN_COMPATIBILITY_2_6
42 void wxDCBase::BeginDrawing()
43 {
44 }
45
46 void wxDCBase::EndDrawing()
47 {
48 }
49 #endif // WXWIN_COMPATIBILITY_2_6
50
51 // ----------------------------------------------------------------------------
52 // special symbols
53 // ----------------------------------------------------------------------------
54
55 void wxDCBase::DoDrawCheckMark(wxCoord x1, wxCoord y1,
56 wxCoord width, wxCoord height)
57 {
58 wxCHECK_RET( Ok(), wxT("invalid window dc") );
59
60 wxCoord x2 = x1 + width,
61 y2 = y1 + height;
62
63 // this is to yield width of 3 for width == height == 10
64 SetPen(wxPen(GetTextForeground(), (width + height + 1) / 7, wxSOLID));
65
66 // we're drawing a scaled version of wx/generic/tick.xpm here
67 wxCoord x3 = x1 + (4*width) / 10, // x of the tick bottom
68 y3 = y1 + height / 2; // y of the left tick branch
69 DoDrawLine(x1, y3, x3, y2);
70 DoDrawLine(x3, y2, x2, y1);
71
72 CalcBoundingBox(x1, y1);
73 CalcBoundingBox(x2, y2);
74 }
75
76 // ----------------------------------------------------------------------------
77 // line/polygons
78 // ----------------------------------------------------------------------------
79
80 void wxDCBase::DrawLines(const wxList *list, wxCoord xoffset, wxCoord yoffset)
81 {
82 int n = list->GetCount();
83 wxPoint *points = new wxPoint[n];
84
85 int i = 0;
86 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
87 {
88 wxPoint *point = (wxPoint *)node->GetData();
89 points[i].x = point->x;
90 points[i].y = point->y;
91 }
92
93 DoDrawLines(n, points, xoffset, yoffset);
94
95 delete [] points;
96 }
97
98
99 void wxDCBase::DrawPolygon(const wxList *list,
100 wxCoord xoffset, wxCoord yoffset,
101 int fillStyle)
102 {
103 int n = list->GetCount();
104 wxPoint *points = new wxPoint[n];
105
106 int i = 0;
107 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
108 {
109 wxPoint *point = (wxPoint *)node->GetData();
110 points[i].x = point->x;
111 points[i].y = point->y;
112 }
113
114 DoDrawPolygon(n, points, xoffset, yoffset, fillStyle);
115
116 delete [] points;
117 }
118
119 void
120 wxDCBase::DoDrawPolyPolygon(int n,
121 int count[],
122 wxPoint points[],
123 wxCoord xoffset, wxCoord yoffset,
124 int fillStyle)
125 {
126 if ( n == 1 )
127 {
128 DoDrawPolygon(count[0], points, xoffset, yoffset, fillStyle);
129 return;
130 }
131
132 int i, j, lastOfs;
133 wxPoint* pts;
134 wxPen pen;
135
136 for (i = j = lastOfs = 0; i < n; i++)
137 {
138 lastOfs = j;
139 j += count[i];
140 }
141 pts = new wxPoint[j+n-1];
142 for (i = 0; i < j; i++)
143 pts[i] = points[i];
144 for (i = 2; i <= n; i++)
145 {
146 lastOfs -= count[n-i];
147 pts[j++] = pts[lastOfs];
148 }
149
150 pen = GetPen();
151 SetPen(wxPen(*wxBLACK, 0, wxTRANSPARENT));
152 DoDrawPolygon(j, pts, xoffset, yoffset, fillStyle);
153 SetPen(pen);
154 for (i = j = 0; i < n; i++)
155 {
156 DoDrawLines(count[i], pts+j, xoffset, yoffset);
157 j += count[i];
158 }
159 delete[] pts;
160 }
161
162 // ----------------------------------------------------------------------------
163 // splines
164 // ----------------------------------------------------------------------------
165
166 #if wxUSE_SPLINES
167
168 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
169 void wxDCBase::DrawSpline(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord x3, wxCoord y3)
170 {
171 wxList point_list;
172
173 wxPoint *point1 = new wxPoint;
174 point1->x = x1; point1->y = y1;
175 point_list.Append((wxObject*)point1);
176
177 wxPoint *point2 = new wxPoint;
178 point2->x = x2; point2->y = y2;
179 point_list.Append((wxObject*)point2);
180
181 wxPoint *point3 = new wxPoint;
182 point3->x = x3; point3->y = y3;
183 point_list.Append((wxObject*)point3);
184
185 DrawSpline(&point_list);
186
187 for( wxList::compatibility_iterator node = point_list.GetFirst(); node; node = node->GetNext() )
188 {
189 wxPoint *p = (wxPoint *)node->GetData();
190 delete p;
191 }
192 }
193
194 void wxDCBase::DrawSpline(int n, wxPoint points[])
195 {
196 wxList list;
197 for (int i =0; i < n; i++)
198 {
199 list.Append((wxObject*)&points[i]);
200 }
201
202 DrawSpline(&list);
203 }
204
205 // ----------------------------------- spline code ----------------------------------------
206
207 void wx_quadratic_spline(double a1, double b1, double a2, double b2,
208 double a3, double b3, double a4, double b4);
209 void wx_clear_stack();
210 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
211 double *y3, double *x4, double *y4);
212 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
213 double x4, double y4);
214 static bool wx_spline_add_point(double x, double y);
215 static void wx_spline_draw_point_array(wxDCBase *dc);
216
217 wxList wx_spline_point_list;
218
219 #define half(z1, z2) ((z1+z2)/2.0)
220 #define THRESHOLD 5
221
222 /* iterative version */
223
224 void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4,
225 double b4)
226 {
227 register double xmid, ymid;
228 double x1, y1, x2, y2, x3, y3, x4, y4;
229
230 wx_clear_stack();
231 wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
232
233 while (wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
234 xmid = (double)half(x2, x3);
235 ymid = (double)half(y2, y3);
236 if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
237 fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
238 wx_spline_add_point( x1, y1 );
239 wx_spline_add_point( xmid, ymid );
240 } else {
241 wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3),
242 (double)half(x3, x4), (double)half(y3, y4), x4, y4);
243 wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
244 (double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid);
245 }
246 }
247 }
248
249 /* utilities used by spline drawing routines */
250
251 typedef struct wx_spline_stack_struct {
252 double x1, y1, x2, y2, x3, y3, x4, y4;
253 } Stack;
254
255 #define SPLINE_STACK_DEPTH 20
256 static Stack wx_spline_stack[SPLINE_STACK_DEPTH];
257 static Stack *wx_stack_top;
258 static int wx_stack_count;
259
260 void wx_clear_stack()
261 {
262 wx_stack_top = wx_spline_stack;
263 wx_stack_count = 0;
264 }
265
266 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
267 {
268 wx_stack_top->x1 = x1;
269 wx_stack_top->y1 = y1;
270 wx_stack_top->x2 = x2;
271 wx_stack_top->y2 = y2;
272 wx_stack_top->x3 = x3;
273 wx_stack_top->y3 = y3;
274 wx_stack_top->x4 = x4;
275 wx_stack_top->y4 = y4;
276 wx_stack_top++;
277 wx_stack_count++;
278 }
279
280 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
281 double *x3, double *y3, double *x4, double *y4)
282 {
283 if (wx_stack_count == 0)
284 return (0);
285 wx_stack_top--;
286 wx_stack_count--;
287 *x1 = wx_stack_top->x1;
288 *y1 = wx_stack_top->y1;
289 *x2 = wx_stack_top->x2;
290 *y2 = wx_stack_top->y2;
291 *x3 = wx_stack_top->x3;
292 *y3 = wx_stack_top->y3;
293 *x4 = wx_stack_top->x4;
294 *y4 = wx_stack_top->y4;
295 return (1);
296 }
297
298 static bool wx_spline_add_point(double x, double y)
299 {
300 wxPoint *point = new wxPoint ;
301 point->x = (int) x;
302 point->y = (int) y;
303 wx_spline_point_list.Append((wxObject*)point);
304 return true;
305 }
306
307 static void wx_spline_draw_point_array(wxDCBase *dc)
308 {
309 dc->DrawLines(&wx_spline_point_list, 0, 0 );
310 wxList::compatibility_iterator node = wx_spline_point_list.GetFirst();
311 while (node)
312 {
313 wxPoint *point = (wxPoint *)node->GetData();
314 delete point;
315 wx_spline_point_list.Erase(node);
316 node = wx_spline_point_list.GetFirst();
317 }
318 }
319
320 void wxDCBase::DoDrawSpline( wxList *points )
321 {
322 wxCHECK_RET( Ok(), wxT("invalid window dc") );
323
324 wxPoint *p;
325 double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
326 double x1, y1, x2, y2;
327
328 wxList::compatibility_iterator node = points->GetFirst();
329 if (node == wxList::compatibility_iterator())
330 // empty list
331 return;
332
333 p = (wxPoint *)node->GetData();
334
335 x1 = p->x;
336 y1 = p->y;
337
338 node = node->GetNext();
339 p = (wxPoint *)node->GetData();
340
341 x2 = p->x;
342 y2 = p->y;
343 cx1 = (double)((x1 + x2) / 2);
344 cy1 = (double)((y1 + y2) / 2);
345 cx2 = (double)((cx1 + x2) / 2);
346 cy2 = (double)((cy1 + y2) / 2);
347
348 wx_spline_add_point(x1, y1);
349
350 while ((node = node->GetNext())
351 #if !wxUSE_STL
352 != NULL
353 #endif // !wxUSE_STL
354 )
355 {
356 p = (wxPoint *)node->GetData();
357 x1 = x2;
358 y1 = y2;
359 x2 = p->x;
360 y2 = p->y;
361 cx4 = (double)(x1 + x2) / 2;
362 cy4 = (double)(y1 + y2) / 2;
363 cx3 = (double)(x1 + cx4) / 2;
364 cy3 = (double)(y1 + cy4) / 2;
365
366 wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
367
368 cx1 = cx4;
369 cy1 = cy4;
370 cx2 = (double)(cx1 + x2) / 2;
371 cy2 = (double)(cy1 + y2) / 2;
372 }
373
374 wx_spline_add_point( cx1, cy1 );
375 wx_spline_add_point( x2, y2 );
376
377 wx_spline_draw_point_array( this );
378 }
379
380 #endif // wxUSE_SPLINES
381
382 // ----------------------------------------------------------------------------
383 // Partial Text Extents
384 // ----------------------------------------------------------------------------
385
386
387 // Each element of the widths array will be the width of the string up to and
388 // including the corresponding character in text. This is the generic
389 // implementation, the port-specific classes should do this with native APIs
390 // if available and if faster. Note: pango_layout_index_to_pos is much slower
391 // than calling GetTextExtent!!
392
393 #define FWC_SIZE 256
394
395 class FontWidthCache
396 {
397 public:
398 FontWidthCache() : m_scaleX(1), m_widths(NULL) { }
399 ~FontWidthCache() { delete []m_widths; }
400
401 void Reset()
402 {
403 if (!m_widths)
404 m_widths = new int[FWC_SIZE];
405
406 memset(m_widths, 0, sizeof(int)*FWC_SIZE);
407 }
408
409 wxFont m_font;
410 double m_scaleX;
411 int *m_widths;
412 };
413
414 static FontWidthCache s_fontWidthCache;
415
416 bool wxDCBase::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
417 {
418 int totalWidth = 0;
419
420 const size_t len = text.length();
421 widths.Empty();
422 widths.Add(0, len);
423
424 // reset the cache if font or horizontal scale have changed
425 if ( !s_fontWidthCache.m_widths ||
426 !wxIsSameDouble(s_fontWidthCache.m_scaleX, m_scaleX) ||
427 (s_fontWidthCache.m_font != GetFont()) )
428 {
429 s_fontWidthCache.Reset();
430 s_fontWidthCache.m_font = GetFont();
431 s_fontWidthCache.m_scaleX = m_scaleX;
432 }
433
434 // Calculate the position of each character based on the widths of
435 // the previous characters
436 int w, h;
437 for ( size_t i = 0; i < len; i++ )
438 {
439 const wxChar c = text[i];
440 unsigned int c_int = (unsigned int)c;
441
442 if ((c_int < FWC_SIZE) && (s_fontWidthCache.m_widths[c_int] != 0))
443 {
444 w = s_fontWidthCache.m_widths[c_int];
445 }
446 else
447 {
448 GetTextExtent(c, &w, &h);
449 if (c_int < FWC_SIZE)
450 s_fontWidthCache.m_widths[c_int] = w;
451 }
452
453 totalWidth += w;
454 widths[i] = totalWidth;
455 }
456
457 return true;
458 }
459
460
461 // ----------------------------------------------------------------------------
462 // enhanced text drawing
463 // ----------------------------------------------------------------------------
464
465 void wxDCBase::GetMultiLineTextExtent(const wxString& text,
466 wxCoord *x,
467 wxCoord *y,
468 wxCoord *h,
469 wxFont *font)
470 {
471 wxCoord widthTextMax = 0, widthLine,
472 heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
473
474 wxString curLine;
475 for ( const wxChar *pc = text; ; pc++ )
476 {
477 if ( *pc == _T('\n') || *pc == _T('\0') )
478 {
479 if ( curLine.empty() )
480 {
481 // we can't use GetTextExtent - it will return 0 for both width
482 // and height and an empty line should count in height
483 // calculation
484
485 // assume that this line has the same height as the previous
486 // one
487 if ( !heightLineDefault )
488 heightLineDefault = heightLine;
489
490 if ( !heightLineDefault )
491 {
492 // but we don't know it yet - choose something reasonable
493 GetTextExtent(_T("W"), NULL, &heightLineDefault,
494 NULL, NULL, font);
495 }
496
497 heightTextTotal += heightLineDefault;
498 }
499 else
500 {
501 GetTextExtent(curLine, &widthLine, &heightLine,
502 NULL, NULL, font);
503 if ( widthLine > widthTextMax )
504 widthTextMax = widthLine;
505 heightTextTotal += heightLine;
506 }
507
508 if ( *pc == _T('\n') )
509 {
510 curLine.clear();
511 }
512 else
513 {
514 // the end of string
515 break;
516 }
517 }
518 else
519 {
520 curLine += *pc;
521 }
522 }
523
524 if ( x )
525 *x = widthTextMax;
526 if ( y )
527 *y = heightTextTotal;
528 if ( h )
529 *h = heightLine;
530 }
531
532 void wxDCBase::DrawLabel(const wxString& text,
533 const wxBitmap& bitmap,
534 const wxRect& rect,
535 int alignment,
536 int indexAccel,
537 wxRect *rectBounding)
538 {
539 // find the text position
540 wxCoord widthText, heightText, heightLine;
541 GetMultiLineTextExtent(text, &widthText, &heightText, &heightLine);
542
543 wxCoord width, height;
544 if ( bitmap.Ok() )
545 {
546 width = widthText + bitmap.GetWidth();
547 height = bitmap.GetHeight();
548 }
549 else // no bitmap
550 {
551 width = widthText;
552 height = heightText;
553 }
554
555 wxCoord x, y;
556 if ( alignment & wxALIGN_RIGHT )
557 {
558 x = rect.GetRight() - width;
559 }
560 else if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
561 {
562 x = (rect.GetLeft() + rect.GetRight() + 1 - width) / 2;
563 }
564 else // alignment & wxALIGN_LEFT
565 {
566 x = rect.GetLeft();
567 }
568
569 if ( alignment & wxALIGN_BOTTOM )
570 {
571 y = rect.GetBottom() - height;
572 }
573 else if ( alignment & wxALIGN_CENTRE_VERTICAL )
574 {
575 y = (rect.GetTop() + rect.GetBottom() + 1 - height) / 2;
576 }
577 else // alignment & wxALIGN_TOP
578 {
579 y = rect.GetTop();
580 }
581
582 // draw the bitmap first
583 wxCoord x0 = x,
584 y0 = y,
585 width0 = width;
586 if ( bitmap.Ok() )
587 {
588 DrawBitmap(bitmap, x, y, true /* use mask */);
589
590 wxCoord offset = bitmap.GetWidth() + 4;
591 x += offset;
592 width -= offset;
593
594 y += (height - heightText) / 2;
595 }
596
597 // we will draw the underscore under the accel char later
598 wxCoord startUnderscore = 0,
599 endUnderscore = 0,
600 yUnderscore = 0;
601
602 // split the string into lines and draw each of them separately
603 wxString curLine;
604 for ( const wxChar *pc = text; ; pc++ )
605 {
606 if ( *pc == _T('\n') || *pc == _T('\0') )
607 {
608 int xRealStart = x; // init it here to avoid compielr warnings
609
610 if ( !curLine.empty() )
611 {
612 // NB: can't test for !(alignment & wxALIGN_LEFT) because
613 // wxALIGN_LEFT is 0
614 if ( alignment & (wxALIGN_RIGHT | wxALIGN_CENTRE_HORIZONTAL) )
615 {
616 wxCoord widthLine;
617 GetTextExtent(curLine, &widthLine, NULL);
618
619 if ( alignment & wxALIGN_RIGHT )
620 {
621 xRealStart += width - widthLine;
622 }
623 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
624 {
625 xRealStart += (width - widthLine) / 2;
626 }
627 }
628 //else: left aligned, nothing to do
629
630 DrawText(curLine, xRealStart, y);
631 }
632
633 y += heightLine;
634
635 // do we have underscore in this line? we can check yUnderscore
636 // because it is set below to just y + heightLine if we do
637 if ( y == yUnderscore )
638 {
639 // adjust the horz positions to account for the shift
640 startUnderscore += xRealStart;
641 endUnderscore += xRealStart;
642 }
643
644 if ( *pc == _T('\0') )
645 break;
646
647 curLine.clear();
648 }
649 else // not end of line
650 {
651 if ( pc - text.c_str() == indexAccel )
652 {
653 // remeber to draw underscore here
654 GetTextExtent(curLine, &startUnderscore, NULL);
655 curLine += *pc;
656 GetTextExtent(curLine, &endUnderscore, NULL);
657
658 yUnderscore = y + heightLine;
659 }
660 else
661 {
662 curLine += *pc;
663 }
664 }
665 }
666
667 // draw the underscore if found
668 if ( startUnderscore != endUnderscore )
669 {
670 // it should be of the same colour as text
671 SetPen(wxPen(GetTextForeground(), 0, wxSOLID));
672
673 yUnderscore--;
674
675 DrawLine(startUnderscore, yUnderscore, endUnderscore, yUnderscore);
676 }
677
678 // return bounding rect if requested
679 if ( rectBounding )
680 {
681 *rectBounding = wxRect(x, y - heightText, widthText, heightText);
682 }
683
684 CalcBoundingBox(x0, y0);
685 CalcBoundingBox(x0 + width0, y0 + height);
686 }
687
688
689 void wxDCBase::DoGradientFillLinear(const wxRect& rect,
690 const wxColour& initialColour,
691 const wxColour& destColour,
692 wxDirection nDirection)
693 {
694 // save old pen
695 wxPen oldPen = m_pen;
696
697 wxUint8 nR1 = destColour.Red();
698 wxUint8 nG1 = destColour.Green();
699 wxUint8 nB1 = destColour.Blue();
700 wxUint8 nR2 = initialColour.Red();
701 wxUint8 nG2 = initialColour.Green();
702 wxUint8 nB2 = initialColour.Blue();
703 wxUint8 nR, nG, nB;
704
705 if ( nDirection == wxEAST || nDirection == wxWEST )
706 {
707 wxInt32 x = rect.GetWidth();
708 wxInt32 w = x; // width of area to shade
709 wxInt32 xDelta = w/256; // height of one shade bend
710 if (xDelta < 1)
711 xDelta = 1;
712
713 while (x >= xDelta)
714 {
715 x -= xDelta;
716 if (nR1 > nR2)
717 nR = nR1 - (nR1-nR2)*(w-x)/w;
718 else
719 nR = nR1 + (nR2-nR1)*(w-x)/w;
720
721 if (nG1 > nG2)
722 nG = nG1 - (nG1-nG2)*(w-x)/w;
723 else
724 nG = nG1 + (nG2-nG1)*(w-x)/w;
725
726 if (nB1 > nB2)
727 nB = nB1 - (nB1-nB2)*(w-x)/w;
728 else
729 nB = nB1 + (nB2-nB1)*(w-x)/w;
730
731 SetPen(wxPen(wxColour(nR, nG, nB), 1, wxSOLID));
732 if(nDirection == wxEAST)
733 DrawRectangle(rect.GetLeft()+x, rect.GetTop(),
734 xDelta, rect.GetHeight());
735 else //nDirection == wxWEST
736 DrawRectangle(rect.GetRight()-x-xDelta, rect.GetTop(),
737 xDelta, rect.GetHeight());
738 }
739 }
740 else // nDirection == wxNORTH || nDirection == wxSOUTH
741 {
742 wxInt32 y = rect.GetHeight();
743 wxInt32 w = y; // height of area to shade
744 wxInt32 yDelta = w/255; // height of one shade bend
745 if (yDelta < 1)
746 yDelta = 1;
747
748 while (y > 0)
749 {
750 y -= yDelta;
751 if (nR1 > nR2)
752 nR = nR1 - (nR1-nR2)*(w-y)/w;
753 else
754 nR = nR1 + (nR2-nR1)*(w-y)/w;
755
756 if (nG1 > nG2)
757 nG = nG1 - (nG1-nG2)*(w-y)/w;
758 else
759 nG = nG1 + (nG2-nG1)*(w-y)/w;
760
761 if (nB1 > nB2)
762 nB = nB1 - (nB1-nB2)*(w-y)/w;
763 else
764 nB = nB1 + (nB2-nB1)*(w-y)/w;
765
766 SetPen(wxPen(wxColour(nR, nG, nB), 1, wxSOLID));
767 if(nDirection == wxNORTH)
768 DrawRectangle(rect.GetLeft(), rect.GetTop()+y,
769 rect.GetWidth(), yDelta);
770 else //nDirection == wxSOUTH
771 DrawRectangle(rect.GetLeft(), rect.GetBottom()-y-yDelta,
772 rect.GetWidth(), yDelta);
773 }
774 }
775
776 SetPen(oldPen);
777 }
778
779 void wxDCBase::GradientFillConcentric(const wxRect& rect,
780 const wxColour& initialColour,
781 const wxColour& destColour,
782 const wxPoint& circleCenter)
783 {
784 //save the old pen color
785 wxColour oldPenColour = m_pen.GetColour();
786
787 wxUint8 nR1 = destColour.Red();
788 wxUint8 nG1 = destColour.Green();
789 wxUint8 nB1 = destColour.Blue();
790 wxUint8 nR2 = initialColour.Red();
791 wxUint8 nG2 = initialColour.Green();
792 wxUint8 nB2 = initialColour.Blue();
793 wxUint8 nR, nG, nB;
794
795
796 //Radius
797 wxInt32 cx = rect.GetWidth() / 2;
798 wxInt32 cy = rect.GetHeight() / 2;
799 wxInt32 nRadius;
800 if (cx < cy)
801 nRadius = cx;
802 else
803 nRadius = cy;
804
805 //Offset of circle
806 wxInt32 nCircleOffX = circleCenter.x - (rect.GetWidth() / 2);
807 wxInt32 nCircleOffY = circleCenter.y - (rect.GetHeight() / 2);
808
809 for ( wxInt32 x = 0; x < rect.GetWidth(); x++ )
810 {
811 for ( wxInt32 y = 0; y < rect.GetHeight(); y++ )
812 {
813 //get color difference
814 wxInt32 nGradient = ((nRadius -
815 (wxInt32)sqrt(
816 pow((double)(x - cx - nCircleOffX), 2) +
817 pow((double)(y - cy - nCircleOffY), 2)
818 )) * 100) / nRadius;
819
820 //normalize Gradient
821 if (nGradient < 0 )
822 nGradient = 0;
823
824 //get dest colors
825 nR = (wxUint8)(nR1 + ((nR2 - nR1) * nGradient / 100));
826 nG = (wxUint8)(nG1 + ((nG2 - nG1) * nGradient / 100));
827 nB = (wxUint8)(nB1 + ((nB2 - nB1) * nGradient / 100));
828
829 //set the pixel
830 m_pen.SetColour(wxColour(nR,nG,nB));
831 DrawPoint(wxPoint(x + rect.GetLeft(), y + rect.GetTop()));
832 }
833 }
834 //return old pen color
835 m_pen.SetColour(oldPenColour);
836 }
837
838 /*
839 Notes for wxWidgets DrawEllipticArcRot(...)
840
841 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
842 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
843 which are also new.
844
845 All methods are generic, so they can be implemented in wxDCBase.
846 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
847 methods like (WinCE) wxDC::DoDrawArc(...).
848
849 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
850 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
851 parts) or every column (in steep parts) only one pixel is calculated.
852 Trigonometric calculation (sin, cos, tan, atan) is only done if the
853 starting angle is not equal to the ending angle. The calculation of the
854 pixels is done using simple arithmetic only and should perform not too
855 bad even on devices without floating point processor. I didn't test this yet.
856
857 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
858 For instance: an ellipse rotated 180 degrees is drawn
859 slightly different from the original.
860
861 The points are then moved to an array and used to draw a polyline and/or polygon
862 (with center added, the pie).
863 The result looks quite similar to the native ellipse, only e few pixels differ.
864
865 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
866 slower as DrawEllipse(...), which calls the native API.
867 An rotated ellipse outside the clipping region takes nearly the same time,
868 while an native ellipse outside takes nearly no time to draw.
869
870 If you draw an arc with this new method, you will see the starting and ending angles
871 are calculated properly.
872 If you use DrawEllipticArc(...), you will see they are only correct for circles
873 and not properly calculated for ellipses.
874
875 Peter Lenhard
876 p.lenhard@t-online.de
877 */
878
879 #ifdef __WXWINCE__
880 void wxDCBase::DoDrawEllipticArcRot( wxCoord x, wxCoord y,
881 wxCoord w, wxCoord h,
882 double sa, double ea, double angle )
883 {
884 wxList list;
885
886 CalculateEllipticPoints( &list, x, y, w, h, sa, ea );
887 Rotate( &list, angle, wxPoint( x+w/2, y+h/2 ) );
888
889 // Add center (for polygon/pie)
890 list.Append( (wxObject*) new wxPoint( x+w/2, y+h/2 ) );
891
892 // copy list into array and delete list elements
893 int n = list.GetCount();
894 wxPoint *points = new wxPoint[n];
895 int i = 0;
896 wxNode* node = 0;
897 for ( node = list.GetFirst(); node; node = node->GetNext(), i++ )
898 {
899 wxPoint *point = (wxPoint *)node->GetData();
900 points[i].x = point->x;
901 points[i].y = point->y;
902 delete point;
903 }
904
905 // first draw the pie without pen, if necessary
906 if( GetBrush() != *wxTRANSPARENT_BRUSH )
907 {
908 wxPen tempPen( GetPen() );
909 SetPen( *wxTRANSPARENT_PEN );
910 DoDrawPolygon( n, points, 0, 0 );
911 SetPen( tempPen );
912 }
913
914 // then draw the arc without brush, if necessary
915 if( GetPen() != *wxTRANSPARENT_PEN )
916 {
917 // without center
918 DoDrawLines( n-1, points, 0, 0 );
919 }
920
921 delete [] points;
922
923 } // DrawEllipticArcRot
924
925 void wxDCBase::Rotate( wxList* points, double angle, wxPoint center )
926 {
927 if( angle != 0.0 )
928 {
929 double pi(M_PI);
930 double dSinA = -sin(angle*2.0*pi/360.0);
931 double dCosA = cos(angle*2.0*pi/360.0);
932 for ( wxNode* node = points->GetFirst(); node; node = node->GetNext() )
933 {
934 wxPoint* point = (wxPoint*)node->GetData();
935
936 // transform coordinates, if necessary
937 if( center.x ) point->x -= center.x;
938 if( center.y ) point->y -= center.y;
939
940 // calculate rotation, rounding simply by implicit cast to integer
941 int xTemp = point->x * dCosA - point->y * dSinA;
942 point->y = point->x * dSinA + point->y * dCosA;
943 point->x = xTemp;
944
945 // back transform coordinates, if necessary
946 if( center.x ) point->x += center.x;
947 if( center.y ) point->y += center.y;
948 }
949 }
950 }
951
952 void wxDCBase::CalculateEllipticPoints( wxList* points,
953 wxCoord xStart, wxCoord yStart,
954 wxCoord w, wxCoord h,
955 double sa, double ea )
956 {
957 double pi = M_PI;
958 double sar = 0;
959 double ear = 0;
960 int xsa = 0;
961 int ysa = 0;
962 int xea = 0;
963 int yea = 0;
964 int sq = 0;
965 int eq = 0;
966 bool bUseAngles = false;
967 if( w<0 ) w = -w;
968 if( h<0 ) h = -h;
969 // half-axes
970 wxCoord a = w/2;
971 wxCoord b = h/2;
972 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
973 int decrX = 0;
974 if( 2*a == w ) decrX = 1;
975 int decrY = 0;
976 if( 2*b == h ) decrY = 1;
977 // center
978 wxCoord xCenter = xStart + a;
979 wxCoord yCenter = yStart + b;
980 // calculate data for start and end, if necessary
981 if( sa != ea )
982 {
983 bUseAngles = true;
984 // normalisation of angles
985 while( sa<0 ) sa += 360;
986 while( ea<0 ) ea += 360;
987 while( sa>=360 ) sa -= 360;
988 while( ea>=360 ) ea -= 360;
989 // calculate quadrant numbers
990 if( sa > 270 ) sq = 3;
991 else if( sa > 180 ) sq = 2;
992 else if( sa > 90 ) sq = 1;
993 if( ea > 270 ) eq = 3;
994 else if( ea > 180 ) eq = 2;
995 else if( ea > 90 ) eq = 1;
996 sar = sa * pi / 180.0;
997 ear = ea * pi / 180.0;
998 // correct angle circle -> ellipse
999 sar = atan( -a/(double)b * tan( sar ) );
1000 if ( sq == 1 || sq == 2 ) sar += pi;
1001 ear = atan( -a/(double)b * tan( ear ) );
1002 if ( eq == 1 || eq == 2 ) ear += pi;
1003 // coordinates of points
1004 xsa = xCenter + a * cos( sar );
1005 if( sq == 0 || sq == 3 ) xsa -= decrX;
1006 ysa = yCenter + b * sin( sar );
1007 if( sq == 2 || sq == 3 ) ysa -= decrY;
1008 xea = xCenter + a * cos( ear );
1009 if( eq == 0 || eq == 3 ) xea -= decrX;
1010 yea = yCenter + b * sin( ear );
1011 if( eq == 2 || eq == 3 ) yea -= decrY;
1012 } // if iUseAngles
1013 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1014 double c1 = b * b;
1015 double c2 = 2.0 / w;
1016 c2 *= c2;
1017 c2 *= c1;
1018 wxCoord x = 0;
1019 wxCoord y = b;
1020 long x2 = 1;
1021 long y2 = y*y;
1022 long y2_old = 0;
1023 long y_old = 0;
1024 // Lists for quadrant 1 to 4
1025 wxList pointsarray[4];
1026 // Calculate points for first quadrant and set in all quadrants
1027 for( x = 0; x <= a; ++x )
1028 {
1029 x2 = x2+x+x-1;
1030 y2_old = y2;
1031 y_old = y;
1032 bool bNewPoint = false;
1033 while( y2 > c1 - c2 * x2 && y > 0 )
1034 {
1035 bNewPoint = true;
1036 y2 = y2-y-y+1;
1037 --y;
1038 }
1039 // old y now to big: set point with old y, old x
1040 if( bNewPoint && x>1)
1041 {
1042 int x1 = x - 1;
1043 // remove points on the same line
1044 pointsarray[0].Insert( (wxObject*) new wxPoint( xCenter + x1 - decrX, yCenter - y_old ) );
1045 pointsarray[1].Append( (wxObject*) new wxPoint( xCenter - x1, yCenter - y_old ) );
1046 pointsarray[2].Insert( (wxObject*) new wxPoint( xCenter - x1, yCenter + y_old - decrY ) );
1047 pointsarray[3].Append( (wxObject*) new wxPoint( xCenter + x1 - decrX, yCenter + y_old - decrY ) );
1048 } // set point
1049 } // calculate point
1050
1051 // Starting and/or ending points for the quadrants, first quadrant gets both.
1052 pointsarray[0].Insert( (wxObject*) new wxPoint( xCenter + a - decrX, yCenter ) );
1053 pointsarray[0].Append( (wxObject*) new wxPoint( xCenter, yCenter - b ) );
1054 pointsarray[1].Append( (wxObject*) new wxPoint( xCenter - a, yCenter ) );
1055 pointsarray[2].Append( (wxObject*) new wxPoint( xCenter, yCenter + b - decrY ) );
1056 pointsarray[3].Append( (wxObject*) new wxPoint( xCenter + a - decrX, yCenter ) );
1057
1058 // copy quadrants in original list
1059 if( bUseAngles )
1060 {
1061 // Copy the right part of the points in the lists
1062 // and delete the wxPoints, because they do not leave this method.
1063 points->Append( (wxObject*) new wxPoint( xsa, ysa ) );
1064 int q = sq;
1065 bool bStarted = false;
1066 bool bReady = false;
1067 bool bForceTurn = ( sq == eq && sa > ea );
1068 while( !bReady )
1069 {
1070 for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
1071 {
1072 // once: go to starting point in start quadrant
1073 if( !bStarted &&
1074 (
1075 ( (wxPoint*) node->GetData() )->x < xsa+1 && q <= 1
1076 ||
1077 ( (wxPoint*) node->GetData() )->x > xsa-1 && q >= 2
1078 )
1079 )
1080 {
1081 bStarted = true;
1082 }
1083
1084 // copy point, if not at ending point
1085 if( bStarted )
1086 {
1087 if( q != eq || bForceTurn
1088 ||
1089 ( (wxPoint*) node->GetData() )->x > xea+1 && q <= 1
1090 ||
1091 ( (wxPoint*) node->GetData() )->x < xea-1 && q >= 2
1092 )
1093 {
1094 // copy point
1095 wxPoint* pPoint = new wxPoint( *((wxPoint*) node->GetData() ) );
1096 points->Append( (wxObject*) pPoint );
1097 }
1098 else if( q == eq && !bForceTurn || ( (wxPoint*) node->GetData() )->x == xea)
1099 {
1100 bReady = true;
1101 }
1102 }
1103 } // for node
1104 ++q;
1105 if( q > 3 ) q = 0;
1106 bForceTurn = false;
1107 bStarted = true;
1108 } // while not bReady
1109 points->Append( (wxObject*) new wxPoint( xea, yea ) );
1110
1111 // delete points
1112 for( q = 0; q < 4; ++q )
1113 {
1114 for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
1115 {
1116 wxPoint *p = (wxPoint *)node->GetData();
1117 delete p;
1118 }
1119 }
1120 }
1121 else
1122 {
1123 wxNode* node;
1124 // copy whole ellipse, wxPoints will be deleted outside
1125 for( node = pointsarray[0].GetFirst(); node; node = node->GetNext() )
1126 {
1127 wxObject *p = node->GetData();
1128 points->Append( p );
1129 }
1130 for( node = pointsarray[1].GetFirst(); node; node = node->GetNext() )
1131 {
1132 wxObject *p = node->GetData();
1133 points->Append( p );
1134 }
1135 for( node = pointsarray[2].GetFirst(); node; node = node->GetNext() )
1136 {
1137 wxObject *p = node->GetData();
1138 points->Append( p );
1139 }
1140 for( node = pointsarray[3].GetFirst(); node; node = node->GetNext() )
1141 {
1142 wxObject *p = node->GetData();
1143 points->Append( p );
1144 }
1145 } // not iUseAngles
1146 } // CalculateEllipticPoints
1147
1148 #endif