]> git.saurik.com Git - wxWidgets.git/blob - src/common/dcbase.cpp
don't draw the endpoint in DrawLine at least in the common cases of vertical and...
[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 // this is to yield width of 3 for width == height == 10
68 SetPen(wxPen(GetTextForeground(), (width + height + 1) / 7, wxSOLID));
69
70 // we're drawing a scaled version of wx/generic/tick.xpm here
71 wxCoord x3 = x1 + (4*width) / 10, // x of the tick bottom
72 y3 = y1 + height / 2; // y of the left tick branch
73 DoDrawLine(x1, y3, x3, y2);
74 DoDrawLine(x3, y2, x2, y1);
75
76 CalcBoundingBox(x1, y1);
77 CalcBoundingBox(x2, y2);
78 }
79
80 // ----------------------------------------------------------------------------
81 // line/polygons
82 // ----------------------------------------------------------------------------
83
84 void wxDCBase::DrawLines(const wxList *list, wxCoord xoffset, wxCoord yoffset)
85 {
86 int n = list->GetCount();
87 wxPoint *points = new wxPoint[n];
88
89 int i = 0;
90 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
91 {
92 wxPoint *point = (wxPoint *)node->GetData();
93 points[i].x = point->x;
94 points[i].y = point->y;
95 }
96
97 DoDrawLines(n, points, xoffset, yoffset);
98
99 delete [] points;
100 }
101
102
103 void wxDCBase::DrawPolygon(const wxList *list,
104 wxCoord xoffset, wxCoord yoffset,
105 int fillStyle)
106 {
107 int n = list->GetCount();
108 wxPoint *points = new wxPoint[n];
109
110 int i = 0;
111 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
112 {
113 wxPoint *point = (wxPoint *)node->GetData();
114 points[i].x = point->x;
115 points[i].y = point->y;
116 }
117
118 DoDrawPolygon(n, points, xoffset, yoffset, fillStyle);
119
120 delete [] points;
121 }
122
123 void
124 wxDCBase::DoDrawPolyPolygon(int n,
125 int count[],
126 wxPoint points[],
127 wxCoord xoffset, wxCoord yoffset,
128 int fillStyle)
129 {
130 if ( n == 1 )
131 {
132 DoDrawPolygon(count[0], points, xoffset, yoffset, fillStyle);
133 return;
134 }
135
136 int i, j, lastOfs;
137 wxPoint* pts;
138 wxPen pen;
139
140 for (i = j = lastOfs = 0; i < n; i++)
141 {
142 lastOfs = j;
143 j += count[i];
144 }
145 pts = new wxPoint[j+n-1];
146 for (i = 0; i < j; i++)
147 pts[i] = points[i];
148 for (i = 2; i <= n; i++)
149 {
150 lastOfs -= count[n-i];
151 pts[j++] = pts[lastOfs];
152 }
153
154 pen = GetPen();
155 SetPen(wxPen(*wxBLACK, 0, wxTRANSPARENT));
156 DoDrawPolygon(j, pts, xoffset, yoffset, fillStyle);
157 SetPen(pen);
158 for (i = j = 0; i < n; i++)
159 {
160 DoDrawLines(count[i], pts+j, xoffset, yoffset);
161 j += count[i];
162 }
163 delete[] pts;
164 }
165
166 // ----------------------------------------------------------------------------
167 // splines
168 // ----------------------------------------------------------------------------
169
170 #if wxUSE_SPLINES
171
172 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
173 void wxDCBase::DrawSpline(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord x3, wxCoord y3)
174 {
175 wxList point_list;
176
177 wxPoint *point1 = new wxPoint;
178 point1->x = x1; point1->y = y1;
179 point_list.Append((wxObject*)point1);
180
181 wxPoint *point2 = new wxPoint;
182 point2->x = x2; point2->y = y2;
183 point_list.Append((wxObject*)point2);
184
185 wxPoint *point3 = new wxPoint;
186 point3->x = x3; point3->y = y3;
187 point_list.Append((wxObject*)point3);
188
189 DrawSpline(&point_list);
190
191 for( wxList::compatibility_iterator node = point_list.GetFirst(); node; node = node->GetNext() )
192 {
193 wxPoint *p = (wxPoint *)node->GetData();
194 delete p;
195 }
196 }
197
198 void wxDCBase::DrawSpline(int n, wxPoint points[])
199 {
200 wxList list;
201 for (int i =0; i < n; i++)
202 {
203 list.Append((wxObject*)&points[i]);
204 }
205
206 DrawSpline(&list);
207 }
208
209 // ----------------------------------- spline code ----------------------------------------
210
211 void wx_quadratic_spline(double a1, double b1, double a2, double b2,
212 double a3, double b3, double a4, double b4);
213 void wx_clear_stack();
214 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
215 double *y3, double *x4, double *y4);
216 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
217 double x4, double y4);
218 static bool wx_spline_add_point(double x, double y);
219 static void wx_spline_draw_point_array(wxDCBase *dc);
220
221 wxList wx_spline_point_list;
222
223 #define half(z1, z2) ((z1+z2)/2.0)
224 #define THRESHOLD 5
225
226 /* iterative version */
227
228 void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4,
229 double b4)
230 {
231 register double xmid, ymid;
232 double x1, y1, x2, y2, x3, y3, x4, y4;
233
234 wx_clear_stack();
235 wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
236
237 while (wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
238 xmid = (double)half(x2, x3);
239 ymid = (double)half(y2, y3);
240 if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
241 fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
242 wx_spline_add_point( x1, y1 );
243 wx_spline_add_point( xmid, ymid );
244 } else {
245 wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3),
246 (double)half(x3, x4), (double)half(y3, y4), x4, y4);
247 wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
248 (double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid);
249 }
250 }
251 }
252
253 /* utilities used by spline drawing routines */
254
255 typedef struct wx_spline_stack_struct {
256 double x1, y1, x2, y2, x3, y3, x4, y4;
257 } Stack;
258
259 #define SPLINE_STACK_DEPTH 20
260 static Stack wx_spline_stack[SPLINE_STACK_DEPTH];
261 static Stack *wx_stack_top;
262 static int wx_stack_count;
263
264 void wx_clear_stack()
265 {
266 wx_stack_top = wx_spline_stack;
267 wx_stack_count = 0;
268 }
269
270 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
271 {
272 wx_stack_top->x1 = x1;
273 wx_stack_top->y1 = y1;
274 wx_stack_top->x2 = x2;
275 wx_stack_top->y2 = y2;
276 wx_stack_top->x3 = x3;
277 wx_stack_top->y3 = y3;
278 wx_stack_top->x4 = x4;
279 wx_stack_top->y4 = y4;
280 wx_stack_top++;
281 wx_stack_count++;
282 }
283
284 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
285 double *x3, double *y3, double *x4, double *y4)
286 {
287 if (wx_stack_count == 0)
288 return (0);
289 wx_stack_top--;
290 wx_stack_count--;
291 *x1 = wx_stack_top->x1;
292 *y1 = wx_stack_top->y1;
293 *x2 = wx_stack_top->x2;
294 *y2 = wx_stack_top->y2;
295 *x3 = wx_stack_top->x3;
296 *y3 = wx_stack_top->y3;
297 *x4 = wx_stack_top->x4;
298 *y4 = wx_stack_top->y4;
299 return (1);
300 }
301
302 static bool wx_spline_add_point(double x, double y)
303 {
304 wxPoint *point = new wxPoint ;
305 point->x = (int) x;
306 point->y = (int) y;
307 wx_spline_point_list.Append((wxObject*)point);
308 return true;
309 }
310
311 static void wx_spline_draw_point_array(wxDCBase *dc)
312 {
313 dc->DrawLines(&wx_spline_point_list, 0, 0 );
314 wxList::compatibility_iterator node = wx_spline_point_list.GetFirst();
315 while (node)
316 {
317 wxPoint *point = (wxPoint *)node->GetData();
318 delete point;
319 wx_spline_point_list.Erase(node);
320 node = wx_spline_point_list.GetFirst();
321 }
322 }
323
324 void wxDCBase::DoDrawSpline( wxList *points )
325 {
326 wxCHECK_RET( Ok(), wxT("invalid window dc") );
327
328 wxPoint *p;
329 double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
330 double x1, y1, x2, y2;
331
332 wxList::compatibility_iterator node = points->GetFirst();
333 if (!node)
334 // empty list
335 return;
336
337 p = (wxPoint *)node->GetData();
338
339 x1 = p->x;
340 y1 = p->y;
341
342 node = node->GetNext();
343 p = (wxPoint *)node->GetData();
344
345 x2 = p->x;
346 y2 = p->y;
347 cx1 = (double)((x1 + x2) / 2);
348 cy1 = (double)((y1 + y2) / 2);
349 cx2 = (double)((cx1 + x2) / 2);
350 cy2 = (double)((cy1 + y2) / 2);
351
352 wx_spline_add_point(x1, y1);
353
354 while ((node = node->GetNext())
355 #if !wxUSE_STL
356 != NULL
357 #endif // !wxUSE_STL
358 )
359 {
360 p = (wxPoint *)node->GetData();
361 x1 = x2;
362 y1 = y2;
363 x2 = p->x;
364 y2 = p->y;
365 cx4 = (double)(x1 + x2) / 2;
366 cy4 = (double)(y1 + y2) / 2;
367 cx3 = (double)(x1 + cx4) / 2;
368 cy3 = (double)(y1 + cy4) / 2;
369
370 wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
371
372 cx1 = cx4;
373 cy1 = cy4;
374 cx2 = (double)(cx1 + x2) / 2;
375 cy2 = (double)(cy1 + y2) / 2;
376 }
377
378 wx_spline_add_point( cx1, cy1 );
379 wx_spline_add_point( x2, y2 );
380
381 wx_spline_draw_point_array( this );
382 }
383
384 #endif // wxUSE_SPLINES
385
386 // ----------------------------------------------------------------------------
387 // Partial Text Extents
388 // ----------------------------------------------------------------------------
389
390
391 // Each element of the widths array will be the width of the string up to and
392 // including the corresponding character in text. This is the generic
393 // implementation, the port-specific classes should do this with native APIs
394 // if available and if faster. Note: pango_layout_index_to_pos is much slower
395 // than calling GetTextExtent!!
396
397 #define FWC_SIZE 256
398
399 class FontWidthCache
400 {
401 public:
402 FontWidthCache() : m_scaleX(1), m_widths(NULL) { }
403 ~FontWidthCache() { delete []m_widths; }
404
405 void Reset()
406 {
407 if (!m_widths)
408 m_widths = new int[FWC_SIZE];
409
410 memset(m_widths, 0, sizeof(int)*FWC_SIZE);
411 }
412
413 wxFont m_font;
414 double m_scaleX;
415 int *m_widths;
416 };
417
418 static FontWidthCache s_fontWidthCache;
419
420 bool wxDCBase::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
421 {
422 int totalWidth = 0;
423
424 const size_t len = text.length();
425 widths.Empty();
426 widths.Add(0, len);
427
428 // reset the cache if font or horizontal scale have changed
429 if ( !s_fontWidthCache.m_widths ||
430 !wxIsSameDouble(s_fontWidthCache.m_scaleX, m_scaleX) ||
431 (s_fontWidthCache.m_font != GetFont()) )
432 {
433 s_fontWidthCache.Reset();
434 s_fontWidthCache.m_font = GetFont();
435 s_fontWidthCache.m_scaleX = m_scaleX;
436 }
437
438 // Calculate the position of each character based on the widths of
439 // the previous characters
440 int w, h;
441 for ( size_t i = 0; i < len; i++ )
442 {
443 const wxChar c = text[i];
444 unsigned int c_int = (unsigned int)c;
445
446 if ((c_int < FWC_SIZE) && (s_fontWidthCache.m_widths[c_int] != 0))
447 {
448 w = s_fontWidthCache.m_widths[c_int];
449 }
450 else
451 {
452 GetTextExtent(c, &w, &h);
453 if (c_int < FWC_SIZE)
454 s_fontWidthCache.m_widths[c_int] = w;
455 }
456
457 totalWidth += w;
458 widths[i] = totalWidth;
459 }
460
461 return true;
462 }
463
464
465 // ----------------------------------------------------------------------------
466 // enhanced text drawing
467 // ----------------------------------------------------------------------------
468
469 void wxDCBase::GetMultiLineTextExtent(const wxString& text,
470 wxCoord *x,
471 wxCoord *y,
472 wxCoord *h,
473 wxFont *font)
474 {
475 wxCoord widthTextMax = 0, widthLine,
476 heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
477
478 wxString curLine;
479 for ( const wxChar *pc = text; ; pc++ )
480 {
481 if ( *pc == _T('\n') || *pc == _T('\0') )
482 {
483 if ( curLine.empty() )
484 {
485 // we can't use GetTextExtent - it will return 0 for both width
486 // and height and an empty line should count in height
487 // calculation
488
489 // assume that this line has the same height as the previous
490 // one
491 if ( !heightLineDefault )
492 heightLineDefault = heightLine;
493
494 if ( !heightLineDefault )
495 {
496 // but we don't know it yet - choose something reasonable
497 GetTextExtent(_T("W"), NULL, &heightLineDefault,
498 NULL, NULL, font);
499 }
500
501 heightTextTotal += heightLineDefault;
502 }
503 else
504 {
505 GetTextExtent(curLine, &widthLine, &heightLine,
506 NULL, NULL, font);
507 if ( widthLine > widthTextMax )
508 widthTextMax = widthLine;
509 heightTextTotal += heightLine;
510 }
511
512 if ( *pc == _T('\n') )
513 {
514 curLine.clear();
515 }
516 else
517 {
518 // the end of string
519 break;
520 }
521 }
522 else
523 {
524 curLine += *pc;
525 }
526 }
527
528 if ( x )
529 *x = widthTextMax;
530 if ( y )
531 *y = heightTextTotal;
532 if ( h )
533 *h = heightLine;
534 }
535
536 void wxDCBase::DrawLabel(const wxString& text,
537 const wxBitmap& bitmap,
538 const wxRect& rect,
539 int alignment,
540 int indexAccel,
541 wxRect *rectBounding)
542 {
543 // find the text position
544 wxCoord widthText, heightText, heightLine;
545 GetMultiLineTextExtent(text, &widthText, &heightText, &heightLine);
546
547 wxCoord width, height;
548 if ( bitmap.Ok() )
549 {
550 width = widthText + bitmap.GetWidth();
551 height = bitmap.GetHeight();
552 }
553 else // no bitmap
554 {
555 width = widthText;
556 height = heightText;
557 }
558
559 wxCoord x, y;
560 if ( alignment & wxALIGN_RIGHT )
561 {
562 x = rect.GetRight() - width;
563 }
564 else if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
565 {
566 x = (rect.GetLeft() + rect.GetRight() + 1 - width) / 2;
567 }
568 else // alignment & wxALIGN_LEFT
569 {
570 x = rect.GetLeft();
571 }
572
573 if ( alignment & wxALIGN_BOTTOM )
574 {
575 y = rect.GetBottom() - height;
576 }
577 else if ( alignment & wxALIGN_CENTRE_VERTICAL )
578 {
579 y = (rect.GetTop() + rect.GetBottom() + 1 - height) / 2;
580 }
581 else // alignment & wxALIGN_TOP
582 {
583 y = rect.GetTop();
584 }
585
586 // draw the bitmap first
587 wxCoord x0 = x,
588 y0 = y,
589 width0 = width;
590 if ( bitmap.Ok() )
591 {
592 DrawBitmap(bitmap, x, y, true /* use mask */);
593
594 wxCoord offset = bitmap.GetWidth() + 4;
595 x += offset;
596 width -= offset;
597
598 y += (height - heightText) / 2;
599 }
600
601 // we will draw the underscore under the accel char later
602 wxCoord startUnderscore = 0,
603 endUnderscore = 0,
604 yUnderscore = 0;
605
606 // split the string into lines and draw each of them separately
607 wxString curLine;
608 for ( const wxChar *pc = text; ; pc++ )
609 {
610 if ( *pc == _T('\n') || *pc == _T('\0') )
611 {
612 int xRealStart = x; // init it here to avoid compielr warnings
613
614 if ( !curLine.empty() )
615 {
616 // NB: can't test for !(alignment & wxALIGN_LEFT) because
617 // wxALIGN_LEFT is 0
618 if ( alignment & (wxALIGN_RIGHT | wxALIGN_CENTRE_HORIZONTAL) )
619 {
620 wxCoord widthLine;
621 GetTextExtent(curLine, &widthLine, NULL);
622
623 if ( alignment & wxALIGN_RIGHT )
624 {
625 xRealStart += width - widthLine;
626 }
627 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
628 {
629 xRealStart += (width - widthLine) / 2;
630 }
631 }
632 //else: left aligned, nothing to do
633
634 DrawText(curLine, xRealStart, y);
635 }
636
637 y += heightLine;
638
639 // do we have underscore in this line? we can check yUnderscore
640 // because it is set below to just y + heightLine if we do
641 if ( y == yUnderscore )
642 {
643 // adjust the horz positions to account for the shift
644 startUnderscore += xRealStart;
645 endUnderscore += xRealStart;
646 }
647
648 if ( *pc == _T('\0') )
649 break;
650
651 curLine.clear();
652 }
653 else // not end of line
654 {
655 if ( pc - text.c_str() == indexAccel )
656 {
657 // remeber to draw underscore here
658 GetTextExtent(curLine, &startUnderscore, NULL);
659 curLine += *pc;
660 GetTextExtent(curLine, &endUnderscore, NULL);
661
662 yUnderscore = y + heightLine;
663 }
664 else
665 {
666 curLine += *pc;
667 }
668 }
669 }
670
671 // draw the underscore if found
672 if ( startUnderscore != endUnderscore )
673 {
674 // it should be of the same colour as text
675 SetPen(wxPen(GetTextForeground(), 0, wxSOLID));
676
677 yUnderscore--;
678
679 DrawLine(startUnderscore, yUnderscore, endUnderscore, yUnderscore);
680 }
681
682 // return bounding rect if requested
683 if ( rectBounding )
684 {
685 *rectBounding = wxRect(x, y - heightText, widthText, heightText);
686 }
687
688 CalcBoundingBox(x0, y0);
689 CalcBoundingBox(x0 + width0, y0 + height);
690 }
691
692
693 void wxDCBase::DoGradientFillLinear(const wxRect& rect,
694 const wxColour& initialColour,
695 const wxColour& destColour,
696 wxDirection nDirection)
697 {
698 // save old pen
699 wxPen oldPen = m_pen;
700
701 wxUint8 nR1 = destColour.Red();
702 wxUint8 nG1 = destColour.Green();
703 wxUint8 nB1 = destColour.Blue();
704 wxUint8 nR2 = initialColour.Red();
705 wxUint8 nG2 = initialColour.Green();
706 wxUint8 nB2 = initialColour.Blue();
707 wxUint8 nR, nG, nB;
708
709 if ( nDirection == wxEAST || nDirection == wxWEST )
710 {
711 wxInt32 x = rect.GetWidth();
712 wxInt32 w = x; // width of area to shade
713 wxInt32 xDelta = w/256; // height of one shade bend
714 if (xDelta < 1)
715 xDelta = 1;
716
717 while (x >= xDelta)
718 {
719 x -= xDelta;
720 if (nR1 > nR2)
721 nR = nR1 - (nR1-nR2)*(w-x)/w;
722 else
723 nR = nR1 + (nR2-nR1)*(w-x)/w;
724
725 if (nG1 > nG2)
726 nG = nG1 - (nG1-nG2)*(w-x)/w;
727 else
728 nG = nG1 + (nG2-nG1)*(w-x)/w;
729
730 if (nB1 > nB2)
731 nB = nB1 - (nB1-nB2)*(w-x)/w;
732 else
733 nB = nB1 + (nB2-nB1)*(w-x)/w;
734
735 SetPen(wxPen(wxColour(nR, nG, nB), 1, wxSOLID));
736 if(nDirection == wxEAST)
737 DrawRectangle(rect.GetLeft()+x, rect.GetTop(),
738 xDelta, rect.GetHeight());
739 else //nDirection == wxWEST
740 DrawRectangle(rect.GetRight()-x-xDelta, rect.GetTop(),
741 xDelta, rect.GetHeight());
742 }
743 }
744 else // nDirection == wxNORTH || nDirection == wxSOUTH
745 {
746 wxInt32 y = rect.GetHeight();
747 wxInt32 w = y; // height of area to shade
748 wxInt32 yDelta = w/255; // height of one shade bend
749 if (yDelta < 1)
750 yDelta = 1;
751
752 while (y > 0)
753 {
754 y -= yDelta;
755 if (nR1 > nR2)
756 nR = nR1 - (nR1-nR2)*(w-y)/w;
757 else
758 nR = nR1 + (nR2-nR1)*(w-y)/w;
759
760 if (nG1 > nG2)
761 nG = nG1 - (nG1-nG2)*(w-y)/w;
762 else
763 nG = nG1 + (nG2-nG1)*(w-y)/w;
764
765 if (nB1 > nB2)
766 nB = nB1 - (nB1-nB2)*(w-y)/w;
767 else
768 nB = nB1 + (nB2-nB1)*(w-y)/w;
769
770 SetPen(wxPen(wxColour(nR, nG, nB), 1, wxSOLID));
771 if(nDirection == wxNORTH)
772 DrawRectangle(rect.GetLeft(), rect.GetTop()+y,
773 rect.GetWidth(), yDelta);
774 else //nDirection == wxSOUTH
775 DrawRectangle(rect.GetLeft(), rect.GetBottom()-y-yDelta,
776 rect.GetWidth(), yDelta);
777 }
778 }
779
780 SetPen(oldPen);
781 }
782
783 void wxDCBase::DoGradientFillConcentric(const wxRect& rect,
784 const wxColour& initialColour,
785 const wxColour& destColour,
786 const wxPoint& circleCenter)
787 {
788 //save the old pen color
789 wxColour oldPenColour = m_pen.GetColour();
790
791 wxUint8 nR1 = destColour.Red();
792 wxUint8 nG1 = destColour.Green();
793 wxUint8 nB1 = destColour.Blue();
794 wxUint8 nR2 = initialColour.Red();
795 wxUint8 nG2 = initialColour.Green();
796 wxUint8 nB2 = initialColour.Blue();
797 wxUint8 nR, nG, nB;
798
799
800 //Radius
801 wxInt32 cx = rect.GetWidth() / 2;
802 wxInt32 cy = rect.GetHeight() / 2;
803 wxInt32 nRadius;
804 if (cx < cy)
805 nRadius = cx;
806 else
807 nRadius = cy;
808
809 //Offset of circle
810 wxInt32 nCircleOffX = circleCenter.x - (rect.GetWidth() / 2);
811 wxInt32 nCircleOffY = circleCenter.y - (rect.GetHeight() / 2);
812
813 for ( wxInt32 x = 0; x < rect.GetWidth(); x++ )
814 {
815 for ( wxInt32 y = 0; y < rect.GetHeight(); y++ )
816 {
817 //get color difference
818 wxInt32 nGradient = ((nRadius -
819 (wxInt32)sqrt(
820 pow((double)(x - cx - nCircleOffX), 2) +
821 pow((double)(y - cy - nCircleOffY), 2)
822 )) * 100) / nRadius;
823
824 //normalize Gradient
825 if (nGradient < 0 )
826 nGradient = 0;
827
828 //get dest colors
829 nR = (wxUint8)(nR1 + ((nR2 - nR1) * nGradient / 100));
830 nG = (wxUint8)(nG1 + ((nG2 - nG1) * nGradient / 100));
831 nB = (wxUint8)(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.GetCount();
898 wxPoint *points = new wxPoint[n];
899 int i = 0;
900 wxNode* node = 0;
901 for ( node = list.GetFirst(); node; node = node->GetNext(), i++ )
902 {
903 wxPoint *point = (wxPoint *)node->GetData();
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->GetFirst(); node; node = node->GetNext() )
937 {
938 wxPoint* point = (wxPoint*)node->GetData();
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].GetFirst(); node; node = node->GetNext() )
1075 {
1076 // once: go to starting point in start quadrant
1077 if( !bStarted &&
1078 (
1079 ( (wxPoint*) node->GetData() )->x < xsa+1 && q <= 1
1080 ||
1081 ( (wxPoint*) node->GetData() )->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->GetData() )->x > xea+1 && q <= 1
1094 ||
1095 ( (wxPoint*) node->GetData() )->x < xea-1 && q >= 2
1096 )
1097 {
1098 // copy point
1099 wxPoint* pPoint = new wxPoint( *((wxPoint*) node->GetData() ) );
1100 points->Append( (wxObject*) pPoint );
1101 }
1102 else if( q == eq && !bForceTurn || ( (wxPoint*) node->GetData() )->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].GetFirst(); node; node = node->GetNext() )
1119 {
1120 wxPoint *p = (wxPoint *)node->GetData();
1121 delete p;
1122 }
1123 }
1124 }
1125 else
1126 {
1127 wxNode* node;
1128 // copy whole ellipse, wxPoints will be deleted outside
1129 for( node = pointsarray[0].GetFirst(); node; node = node->GetNext() )
1130 {
1131 wxObject *p = node->GetData();
1132 points->Append( p );
1133 }
1134 for( node = pointsarray[1].GetFirst(); node; node = node->GetNext() )
1135 {
1136 wxObject *p = node->GetData();
1137 points->Append( p );
1138 }
1139 for( node = pointsarray[2].GetFirst(); node; node = node->GetNext() )
1140 {
1141 wxObject *p = node->GetData();
1142 points->Append( p );
1143 }
1144 for( node = pointsarray[3].GetFirst(); node; node = node->GetNext() )
1145 {
1146 wxObject *p = node->GetData();
1147 points->Append( p );
1148 }
1149 } // not iUseAngles
1150 } // CalculateEllipticPoints
1151
1152 #endif