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