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