]> git.saurik.com Git - wxWidgets.git/blame - src/common/dcbase.cpp
avoid assert in DoGetBestSize() for an empty tree
[wxWidgets.git] / src / common / dcbase.cpp
CommitLineData
dbe94982 1/////////////////////////////////////////////////////////////////////////////
06052f3f 2// Name: src/common/dcbase.cpp
1e6feb95
VZ
3// Purpose: generic methods of the wxDC Class
4// Author: Vadim Zeitlin
dbe94982
BM
5// Modified by:
6// Created: 05/25/99
7// RCS-ID: $Id$
77ffb593 8// Copyright: (c) wxWidgets team
65571936 9// Licence: wxWindows licence
dbe94982
BM
10/////////////////////////////////////////////////////////////////////////////
11
1e6feb95
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
1e6feb95
VZ
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
dbe94982
BM
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
bd1e9c12 24 #pragma hdrstop
dbe94982
BM
25#endif
26
dbe94982 27#include "wx/dc.h"
653752be 28#include "wx/dcbuffer.h" // for IMPLEMENT_DYNAMIC_CLASS
18680f86
WS
29
30#ifndef WX_PRECOMP
31 #include "wx/math.h"
32#endif
dbe94982 33
68379eaf 34// bool wxDCBase::sm_cacheing = false;
0cbff120 35
e7445ff8
PC
36IMPLEMENT_ABSTRACT_CLASS(wxDCBase, wxObject)
37
1e6feb95
VZ
38// ============================================================================
39// implementation
40// ============================================================================
41
653752be
MB
42IMPLEMENT_DYNAMIC_CLASS(wxBufferedDC, wxMemoryDC)
43IMPLEMENT_ABSTRACT_CLASS(wxBufferedPaintDC, wxBufferedDC)
44
68df211f
JG
45#if WXWIN_COMPATIBILITY_2_6
46void wxDCBase::BeginDrawing()
47{
48}
49
50void wxDCBase::EndDrawing()
51{
52}
53#endif // WXWIN_COMPATIBILITY_2_6
54
1e6feb95
VZ
55// ----------------------------------------------------------------------------
56// special symbols
57// ----------------------------------------------------------------------------
58
cd9da200
VZ
59void 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
860a4018
VZ
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));
cd9da200
VZ
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
1e6feb95
VZ
81// ----------------------------------------------------------------------------
82// line/polygons
83// ----------------------------------------------------------------------------
84
72cdf4c9 85void wxDCBase::DrawLines(const wxList *list, wxCoord xoffset, wxCoord yoffset)
dbe94982 86{
b1d4dd7a 87 int n = list->GetCount();
dbe94982
BM
88 wxPoint *points = new wxPoint[n];
89
90 int i = 0;
222ed1d6 91 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
dbe94982 92 {
b1d4dd7a 93 wxPoint *point = (wxPoint *)node->GetData();
dbe94982
BM
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
104void wxDCBase::DrawPolygon(const wxList *list,
72cdf4c9 105 wxCoord xoffset, wxCoord yoffset,
dbe94982
BM
106 int fillStyle)
107{
b1d4dd7a 108 int n = list->GetCount();
dbe94982
BM
109 wxPoint *points = new wxPoint[n];
110
111 int i = 0;
222ed1d6 112 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
dbe94982 113 {
b1d4dd7a 114 wxPoint *point = (wxPoint *)node->GetData();
dbe94982
BM
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
63b9e659
VZ
124void
125wxDCBase::DoDrawPolyPolygon(int n,
793db755 126 int count[],
63b9e659
VZ
127 wxPoint points[],
128 wxCoord xoffset, wxCoord yoffset,
129 int fillStyle)
130{
131 if ( n == 1 )
132 {
793db755 133 DoDrawPolygon(count[0], points, xoffset, yoffset, fillStyle);
63b9e659
VZ
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;
793db755 144 j += count[i];
63b9e659
VZ
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 {
793db755 151 lastOfs -= count[n-i];
63b9e659
VZ
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 {
793db755
VZ
161 DoDrawLines(count[i], pts+j, xoffset, yoffset);
162 j += count[i];
63b9e659 163 }
6db1f43b 164 delete[] pts;
63b9e659
VZ
165}
166
1e6feb95
VZ
167// ----------------------------------------------------------------------------
168// splines
169// ----------------------------------------------------------------------------
dbe94982 170
88ac883a 171#if wxUSE_SPLINES
dbe94982
BM
172
173// TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
72cdf4c9 174void wxDCBase::DrawSpline(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord x3, wxCoord y3)
dbe94982
BM
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
222ed1d6 192 for( wxList::compatibility_iterator node = point_list.GetFirst(); node; node = node->GetNext() )
dbe94982 193 {
b1d4dd7a 194 wxPoint *p = (wxPoint *)node->GetData();
dbe94982
BM
195 delete p;
196 }
197}
198
199void 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
fe2e4366
VS
210// ----------------------------------- spline code ----------------------------------------
211
212void wx_quadratic_spline(double a1, double b1, double a2, double b2,
213 double a3, double b3, double a4, double b4);
214void wx_clear_stack();
215int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
216 double *y3, double *x4, double *y4);
217void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
218 double x4, double y4);
219static bool wx_spline_add_point(double x, double y);
220static void wx_spline_draw_point_array(wxDCBase *dc);
221
222wxList wx_spline_point_list;
223
224#define half(z1, z2) ((z1+z2)/2.0)
225#define THRESHOLD 5
226
227/* iterative version */
228
229void 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
256typedef struct wx_spline_stack_struct {
257 double x1, y1, x2, y2, x3, y3, x4, y4;
258} Stack;
259
260#define SPLINE_STACK_DEPTH 20
261static Stack wx_spline_stack[SPLINE_STACK_DEPTH];
262static Stack *wx_stack_top;
263static int wx_stack_count;
264
265void wx_clear_stack()
266{
267 wx_stack_top = wx_spline_stack;
268 wx_stack_count = 0;
269}
270
271void 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
285int 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
303static 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);
68379eaf 309 return true;
fe2e4366
VS
310}
311
312static void wx_spline_draw_point_array(wxDCBase *dc)
313{
314 dc->DrawLines(&wx_spline_point_list, 0, 0 );
222ed1d6 315 wxList::compatibility_iterator node = wx_spline_point_list.GetFirst();
fe2e4366
VS
316 while (node)
317 {
b1d4dd7a 318 wxPoint *point = (wxPoint *)node->GetData();
fe2e4366 319 delete point;
222ed1d6 320 wx_spline_point_list.Erase(node);
b1d4dd7a 321 node = wx_spline_point_list.GetFirst();
fe2e4366
VS
322 }
323}
324
325void 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
222ed1d6 333 wxList::compatibility_iterator node = points->GetFirst();
ef419e3b 334 if (!node)
410bb8c1
RD
335 // empty list
336 return;
e0d31471 337
b1d4dd7a 338 p = (wxPoint *)node->GetData();
fe2e4366
VS
339
340 x1 = p->x;
341 y1 = p->y;
342
b1d4dd7a
RL
343 node = node->GetNext();
344 p = (wxPoint *)node->GetData();
fe2e4366
VS
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
28b4db7f
VZ
355 while ((node = node->GetNext())
356#if !wxUSE_STL
357 != NULL
358#endif // !wxUSE_STL
359 )
fe2e4366 360 {
b1d4dd7a 361 p = (wxPoint *)node->GetData();
fe2e4366
VS
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
88ac883a 385#endif // wxUSE_SPLINES
1e6feb95 386
0919e93e
RD
387// ----------------------------------------------------------------------------
388// Partial Text Extents
389// ----------------------------------------------------------------------------
390
391
174ee1b3 392// Each element of the widths array will be the width of the string up to and
3103e8a9 393// including the corresponding character in text. This is the generic
0919e93e 394// implementation, the port-specific classes should do this with native APIs
174ee1b3
RD
395// if available and if faster. Note: pango_layout_index_to_pos is much slower
396// than calling GetTextExtent!!
397
06fe86b7 398#define FWC_SIZE 256
174ee1b3
RD
399
400class FontWidthCache
401{
402public:
403 FontWidthCache() : m_scaleX(1), m_widths(NULL) { }
404 ~FontWidthCache() { delete []m_widths; }
2aaa050c
VZ
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
174ee1b3
RD
414 wxFont m_font;
415 double m_scaleX;
416 int *m_widths;
417};
418
419static FontWidthCache s_fontWidthCache;
0919e93e
RD
420
421bool wxDCBase::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
422{
423 int totalWidth = 0;
0919e93e 424
18680f86 425 const size_t len = text.length();
0919e93e 426 widths.Empty();
174ee1b3 427 widths.Add(0, len);
2aaa050c 428
174ee1b3 429 // reset the cache if font or horizontal scale have changed
c77a6796
VZ
430 if ( !s_fontWidthCache.m_widths ||
431 !wxIsSameDouble(s_fontWidthCache.m_scaleX, m_scaleX) ||
432 (s_fontWidthCache.m_font != GetFont()) )
174ee1b3
RD
433 {
434 s_fontWidthCache.Reset();
435 s_fontWidthCache.m_font = GetFont();
436 s_fontWidthCache.m_scaleX = m_scaleX;
437 }
438
0919e93e
RD
439 // Calculate the position of each character based on the widths of
440 // the previous characters
c77a6796
VZ
441 int w, h;
442 for ( size_t i = 0; i < len; i++ )
174ee1b3
RD
443 {
444 const wxChar c = text[i];
445 unsigned int c_int = (unsigned int)c;
446
2aaa050c 447 if ((c_int < FWC_SIZE) && (s_fontWidthCache.m_widths[c_int] != 0))
174ee1b3
RD
448 {
449 w = s_fontWidthCache.m_widths[c_int];
450 }
2aaa050c 451 else
174ee1b3
RD
452 {
453 GetTextExtent(c, &w, &h);
454 if (c_int < FWC_SIZE)
455 s_fontWidthCache.m_widths[c_int] = w;
456 }
457
0919e93e
RD
458 totalWidth += w;
459 widths[i] = totalWidth;
460 }
2aaa050c 461
0919e93e
RD
462 return true;
463}
464
465
1e6feb95
VZ
466// ----------------------------------------------------------------------------
467// enhanced text drawing
468// ----------------------------------------------------------------------------
469
470void wxDCBase::GetMultiLineTextExtent(const wxString& text,
471 wxCoord *x,
472 wxCoord *y,
473 wxCoord *h,
169147c8 474 wxFont *font) const
1e6feb95 475{
c7aaa64f
VS
476 wxCoord widthTextMax = 0, widthLine,
477 heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
1e6feb95
VZ
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
537void 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 {
68379eaf 593 DrawBitmap(bitmap, x, y, true /* use mask */);
1e6feb95
VZ
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 {
328bfc22 656 if ( pc - text.c_str() == indexAccel )
1e6feb95
VZ
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}
12bdd77c 692
213ad8e7
VZ
693
694void 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;
a983a3cf 701 wxBrush oldBrush = m_brush;
213ad8e7 702
6dff0e29
BW
703 wxUint8 nR1 = initialColour.Red();
704 wxUint8 nG1 = initialColour.Green();
705 wxUint8 nB1 = initialColour.Blue();
706 wxUint8 nR2 = destColour.Red();
707 wxUint8 nG2 = destColour.Green();
708 wxUint8 nB2 = destColour.Blue();
213ad8e7
VZ
709 wxUint8 nR, nG, nB;
710
711 if ( nDirection == wxEAST || nDirection == wxWEST )
712 {
713 wxInt32 x = rect.GetWidth();
714 wxInt32 w = x; // width of area to shade
715 wxInt32 xDelta = w/256; // height of one shade bend
716 if (xDelta < 1)
717 xDelta = 1;
718
719 while (x >= xDelta)
720 {
721 x -= xDelta;
722 if (nR1 > nR2)
723 nR = nR1 - (nR1-nR2)*(w-x)/w;
724 else
725 nR = nR1 + (nR2-nR1)*(w-x)/w;
726
727 if (nG1 > nG2)
728 nG = nG1 - (nG1-nG2)*(w-x)/w;
729 else
730 nG = nG1 + (nG2-nG1)*(w-x)/w;
731
732 if (nB1 > nB2)
733 nB = nB1 - (nB1-nB2)*(w-x)/w;
734 else
735 nB = nB1 + (nB2-nB1)*(w-x)/w;
736
1dab6da9
BW
737 wxColour colour(nR,nG,nB);
738 SetPen(wxPen(colour, 1, wxSOLID));
739 SetBrush(wxBrush(colour));
213ad8e7 740 if(nDirection == wxEAST)
1dab6da9 741 DrawRectangle(rect.GetRight()-x-xDelta, rect.GetTop(),
213ad8e7
VZ
742 xDelta, rect.GetHeight());
743 else //nDirection == wxWEST
1dab6da9 744 DrawRectangle(rect.GetLeft()+x, rect.GetTop(),
213ad8e7
VZ
745 xDelta, rect.GetHeight());
746 }
747 }
748 else // nDirection == wxNORTH || nDirection == wxSOUTH
749 {
750 wxInt32 y = rect.GetHeight();
751 wxInt32 w = y; // height of area to shade
752 wxInt32 yDelta = w/255; // height of one shade bend
753 if (yDelta < 1)
754 yDelta = 1;
755
756 while (y > 0)
757 {
758 y -= yDelta;
759 if (nR1 > nR2)
760 nR = nR1 - (nR1-nR2)*(w-y)/w;
761 else
762 nR = nR1 + (nR2-nR1)*(w-y)/w;
763
764 if (nG1 > nG2)
765 nG = nG1 - (nG1-nG2)*(w-y)/w;
766 else
767 nG = nG1 + (nG2-nG1)*(w-y)/w;
768
769 if (nB1 > nB2)
770 nB = nB1 - (nB1-nB2)*(w-y)/w;
771 else
772 nB = nB1 + (nB2-nB1)*(w-y)/w;
773
1dab6da9
BW
774 wxColour colour(nR,nG,nB);
775 SetPen(wxPen(colour, 1, wxSOLID));
776 SetBrush(wxBrush(colour));
213ad8e7
VZ
777 if(nDirection == wxNORTH)
778 DrawRectangle(rect.GetLeft(), rect.GetTop()+y,
779 rect.GetWidth(), yDelta);
780 else //nDirection == wxSOUTH
781 DrawRectangle(rect.GetLeft(), rect.GetBottom()-y-yDelta,
782 rect.GetWidth(), yDelta);
783 }
784 }
785
786 SetPen(oldPen);
a983a3cf 787 SetBrush(oldBrush);
213ad8e7
VZ
788}
789
fb63a242 790void wxDCBase::DoGradientFillConcentric(const wxRect& rect,
213ad8e7
VZ
791 const wxColour& initialColour,
792 const wxColour& destColour,
793 const wxPoint& circleCenter)
794{
795 //save the old pen color
796 wxColour oldPenColour = m_pen.GetColour();
797
798 wxUint8 nR1 = destColour.Red();
799 wxUint8 nG1 = destColour.Green();
800 wxUint8 nB1 = destColour.Blue();
801 wxUint8 nR2 = initialColour.Red();
802 wxUint8 nG2 = initialColour.Green();
803 wxUint8 nB2 = initialColour.Blue();
804 wxUint8 nR, nG, nB;
805
806
213ad8e7
VZ
807 //Radius
808 wxInt32 cx = rect.GetWidth() / 2;
809 wxInt32 cy = rect.GetHeight() / 2;
810 wxInt32 nRadius;
811 if (cx < cy)
812 nRadius = cx;
813 else
814 nRadius = cy;
815
816 //Offset of circle
817 wxInt32 nCircleOffX = circleCenter.x - (rect.GetWidth() / 2);
818 wxInt32 nCircleOffY = circleCenter.y - (rect.GetHeight() / 2);
819
e35d07b9 820 for ( wxInt32 x = 0; x < rect.GetWidth(); x++ )
213ad8e7 821 {
e35d07b9 822 for ( wxInt32 y = 0; y < rect.GetHeight(); y++ )
213ad8e7
VZ
823 {
824 //get color difference
e35d07b9
VZ
825 wxInt32 nGradient = ((nRadius -
826 (wxInt32)sqrt(
827 pow((double)(x - cx - nCircleOffX), 2) +
828 pow((double)(y - cy - nCircleOffY), 2)
829 )) * 100) / nRadius;
213ad8e7
VZ
830
831 //normalize Gradient
832 if (nGradient < 0 )
833 nGradient = 0;
834
835 //get dest colors
06052f3f
WS
836 nR = (wxUint8)(nR1 + ((nR2 - nR1) * nGradient / 100));
837 nG = (wxUint8)(nG1 + ((nG2 - nG1) * nGradient / 100));
838 nB = (wxUint8)(nB1 + ((nB2 - nB1) * nGradient / 100));
213ad8e7
VZ
839
840 //set the pixel
841 m_pen.SetColour(wxColour(nR,nG,nB));
842 DrawPoint(wxPoint(x + rect.GetLeft(), y + rect.GetTop()));
843 }
844 }
845 //return old pen color
846 m_pen.SetColour(oldPenColour);
847}
848
12bdd77c 849/*
77ffb593 850Notes for wxWidgets DrawEllipticArcRot(...)
12bdd77c
JS
851
852wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
853It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
854which are also new.
855
856All methods are generic, so they can be implemented in wxDCBase.
857DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
858methods like (WinCE) wxDC::DoDrawArc(...).
859
860CalculateEllipticPoints(...) fills a given list of wxPoints with some points
2aaa050c 861of an elliptic arc. The algorithm is pixel-based: In every row (in flat
12bdd77c
JS
862parts) or every column (in steep parts) only one pixel is calculated.
863Trigonometric calculation (sin, cos, tan, atan) is only done if the
2aaa050c 864starting angle is not equal to the ending angle. The calculation of the
12bdd77c
JS
865pixels is done using simple arithmetic only and should perform not too
866bad even on devices without floating point processor. I didn't test this yet.
867
868Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
2aaa050c 869For instance: an ellipse rotated 180 degrees is drawn
12bdd77c
JS
870slightly different from the original.
871
2aaa050c
VZ
872The points are then moved to an array and used to draw a polyline and/or polygon
873(with center added, the pie).
12bdd77c
JS
874The result looks quite similar to the native ellipse, only e few pixels differ.
875
876The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
877slower as DrawEllipse(...), which calls the native API.
878An rotated ellipse outside the clipping region takes nearly the same time,
879while an native ellipse outside takes nearly no time to draw.
880
2aaa050c 881If you draw an arc with this new method, you will see the starting and ending angles
12bdd77c
JS
882are calculated properly.
883If you use DrawEllipticArc(...), you will see they are only correct for circles
884and not properly calculated for ellipses.
885
886Peter Lenhard
887p.lenhard@t-online.de
888*/
889
890#ifdef __WXWINCE__
2aaa050c
VZ
891void wxDCBase::DoDrawEllipticArcRot( wxCoord x, wxCoord y,
892 wxCoord w, wxCoord h,
12bdd77c
JS
893 double sa, double ea, double angle )
894{
895 wxList list;
896
897 CalculateEllipticPoints( &list, x, y, w, h, sa, ea );
898 Rotate( &list, angle, wxPoint( x+w/2, y+h/2 ) );
899
900 // Add center (for polygon/pie)
901 list.Append( (wxObject*) new wxPoint( x+w/2, y+h/2 ) );
902
903 // copy list into array and delete list elements
2c3ebf8b 904 int n = list.GetCount();
12bdd77c
JS
905 wxPoint *points = new wxPoint[n];
906 int i = 0;
2aaa050c 907 wxNode* node = 0;
2c3ebf8b 908 for ( node = list.GetFirst(); node; node = node->GetNext(), i++ )
12bdd77c 909 {
2c3ebf8b 910 wxPoint *point = (wxPoint *)node->GetData();
12bdd77c
JS
911 points[i].x = point->x;
912 points[i].y = point->y;
913 delete point;
914 }
915
916 // first draw the pie without pen, if necessary
63b9e659 917 if( GetBrush() != *wxTRANSPARENT_BRUSH )
12bdd77c
JS
918 {
919 wxPen tempPen( GetPen() );
920 SetPen( *wxTRANSPARENT_PEN );
921 DoDrawPolygon( n, points, 0, 0 );
922 SetPen( tempPen );
923 }
924
925 // then draw the arc without brush, if necessary
63b9e659 926 if( GetPen() != *wxTRANSPARENT_PEN )
12bdd77c
JS
927 {
928 // without center
929 DoDrawLines( n-1, points, 0, 0 );
930 }
931
932 delete [] points;
933
934} // DrawEllipticArcRot
935
936void wxDCBase::Rotate( wxList* points, double angle, wxPoint center )
937{
938 if( angle != 0.0 )
939 {
e0d31471 940 double pi(M_PI);
12bdd77c
JS
941 double dSinA = -sin(angle*2.0*pi/360.0);
942 double dCosA = cos(angle*2.0*pi/360.0);
2c3ebf8b 943 for ( wxNode* node = points->GetFirst(); node; node = node->GetNext() )
12bdd77c 944 {
2c3ebf8b 945 wxPoint* point = (wxPoint*)node->GetData();
2aaa050c 946
12bdd77c
JS
947 // transform coordinates, if necessary
948 if( center.x ) point->x -= center.x;
949 if( center.y ) point->y -= center.y;
950
951 // calculate rotation, rounding simply by implicit cast to integer
952 int xTemp = point->x * dCosA - point->y * dSinA;
953 point->y = point->x * dSinA + point->y * dCosA;
954 point->x = xTemp;
955
956 // back transform coordinates, if necessary
957 if( center.x ) point->x += center.x;
958 if( center.y ) point->y += center.y;
959 }
960 }
961}
962
2aaa050c
VZ
963void wxDCBase::CalculateEllipticPoints( wxList* points,
964 wxCoord xStart, wxCoord yStart,
965 wxCoord w, wxCoord h,
12bdd77c
JS
966 double sa, double ea )
967{
e0d31471 968 double pi = M_PI;
12bdd77c
JS
969 double sar = 0;
970 double ear = 0;
971 int xsa = 0;
972 int ysa = 0;
973 int xea = 0;
974 int yea = 0;
975 int sq = 0;
976 int eq = 0;
977 bool bUseAngles = false;
978 if( w<0 ) w = -w;
979 if( h<0 ) h = -h;
980 // half-axes
981 wxCoord a = w/2;
982 wxCoord b = h/2;
983 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
984 int decrX = 0;
2aaa050c 985 if( 2*a == w ) decrX = 1;
12bdd77c 986 int decrY = 0;
2aaa050c 987 if( 2*b == h ) decrY = 1;
12bdd77c
JS
988 // center
989 wxCoord xCenter = xStart + a;
990 wxCoord yCenter = yStart + b;
991 // calculate data for start and end, if necessary
992 if( sa != ea )
993 {
994 bUseAngles = true;
995 // normalisation of angles
996 while( sa<0 ) sa += 360;
997 while( ea<0 ) ea += 360;
998 while( sa>=360 ) sa -= 360;
999 while( ea>=360 ) ea -= 360;
1000 // calculate quadrant numbers
1001 if( sa > 270 ) sq = 3;
1002 else if( sa > 180 ) sq = 2;
1003 else if( sa > 90 ) sq = 1;
1004 if( ea > 270 ) eq = 3;
1005 else if( ea > 180 ) eq = 2;
1006 else if( ea > 90 ) eq = 1;
1007 sar = sa * pi / 180.0;
1008 ear = ea * pi / 180.0;
1009 // correct angle circle -> ellipse
1010 sar = atan( -a/(double)b * tan( sar ) );
2aaa050c 1011 if ( sq == 1 || sq == 2 ) sar += pi;
12bdd77c
JS
1012 ear = atan( -a/(double)b * tan( ear ) );
1013 if ( eq == 1 || eq == 2 ) ear += pi;
1014 // coordinates of points
1015 xsa = xCenter + a * cos( sar );
1016 if( sq == 0 || sq == 3 ) xsa -= decrX;
1017 ysa = yCenter + b * sin( sar );
1018 if( sq == 2 || sq == 3 ) ysa -= decrY;
1019 xea = xCenter + a * cos( ear );
1020 if( eq == 0 || eq == 3 ) xea -= decrX;
1021 yea = yCenter + b * sin( ear );
1022 if( eq == 2 || eq == 3 ) yea -= decrY;
1023 } // if iUseAngles
1024 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1025 double c1 = b * b;
1026 double c2 = 2.0 / w;
1027 c2 *= c2;
1028 c2 *= c1;
1029 wxCoord x = 0;
1030 wxCoord y = b;
1031 long x2 = 1;
1032 long y2 = y*y;
1033 long y2_old = 0;
1034 long y_old = 0;
1035 // Lists for quadrant 1 to 4
1036 wxList pointsarray[4];
1037 // Calculate points for first quadrant and set in all quadrants
1038 for( x = 0; x <= a; ++x )
1039 {
1040 x2 = x2+x+x-1;
1041 y2_old = y2;
1042 y_old = y;
1043 bool bNewPoint = false;
1044 while( y2 > c1 - c2 * x2 && y > 0 )
1045 {
1046 bNewPoint = true;
1047 y2 = y2-y-y+1;
1048 --y;
1049 }
2aaa050c 1050 // old y now to big: set point with old y, old x
12bdd77c
JS
1051 if( bNewPoint && x>1)
1052 {
1053 int x1 = x - 1;
1054 // remove points on the same line
1055 pointsarray[0].Insert( (wxObject*) new wxPoint( xCenter + x1 - decrX, yCenter - y_old ) );
1056 pointsarray[1].Append( (wxObject*) new wxPoint( xCenter - x1, yCenter - y_old ) );
1057 pointsarray[2].Insert( (wxObject*) new wxPoint( xCenter - x1, yCenter + y_old - decrY ) );
1058 pointsarray[3].Append( (wxObject*) new wxPoint( xCenter + x1 - decrX, yCenter + y_old - decrY ) );
1059 } // set point
1060 } // calculate point
2aaa050c 1061
12bdd77c
JS
1062 // Starting and/or ending points for the quadrants, first quadrant gets both.
1063 pointsarray[0].Insert( (wxObject*) new wxPoint( xCenter + a - decrX, yCenter ) );
1064 pointsarray[0].Append( (wxObject*) new wxPoint( xCenter, yCenter - b ) );
1065 pointsarray[1].Append( (wxObject*) new wxPoint( xCenter - a, yCenter ) );
1066 pointsarray[2].Append( (wxObject*) new wxPoint( xCenter, yCenter + b - decrY ) );
1067 pointsarray[3].Append( (wxObject*) new wxPoint( xCenter + a - decrX, yCenter ) );
1068
1069 // copy quadrants in original list
1070 if( bUseAngles )
1071 {
1072 // Copy the right part of the points in the lists
1073 // and delete the wxPoints, because they do not leave this method.
1074 points->Append( (wxObject*) new wxPoint( xsa, ysa ) );
1075 int q = sq;
1076 bool bStarted = false;
1077 bool bReady = false;
1078 bool bForceTurn = ( sq == eq && sa > ea );
1079 while( !bReady )
1080 {
2c3ebf8b 1081 for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
12bdd77c
JS
1082 {
1083 // once: go to starting point in start quadrant
1084 if( !bStarted &&
2aaa050c 1085 (
2c3ebf8b 1086 ( (wxPoint*) node->GetData() )->x < xsa+1 && q <= 1
2aaa050c 1087 ||
2c3ebf8b 1088 ( (wxPoint*) node->GetData() )->x > xsa-1 && q >= 2
12bdd77c 1089 )
2aaa050c 1090 )
12bdd77c
JS
1091 {
1092 bStarted = true;
1093 }
1094
1095 // copy point, if not at ending point
1096 if( bStarted )
1097 {
1098 if( q != eq || bForceTurn
1099 ||
2c3ebf8b 1100 ( (wxPoint*) node->GetData() )->x > xea+1 && q <= 1
2aaa050c 1101 ||
2c3ebf8b 1102 ( (wxPoint*) node->GetData() )->x < xea-1 && q >= 2
12bdd77c
JS
1103 )
1104 {
1105 // copy point
2c3ebf8b 1106 wxPoint* pPoint = new wxPoint( *((wxPoint*) node->GetData() ) );
12bdd77c
JS
1107 points->Append( (wxObject*) pPoint );
1108 }
2c3ebf8b 1109 else if( q == eq && !bForceTurn || ( (wxPoint*) node->GetData() )->x == xea)
12bdd77c 1110 {
2aaa050c 1111 bReady = true;
12bdd77c
JS
1112 }
1113 }
1114 } // for node
1115 ++q;
1116 if( q > 3 ) q = 0;
1117 bForceTurn = false;
1118 bStarted = true;
1119 } // while not bReady
1120 points->Append( (wxObject*) new wxPoint( xea, yea ) );
1121
1122 // delete points
1123 for( q = 0; q < 4; ++q )
1124 {
2c3ebf8b 1125 for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
12bdd77c 1126 {
2c3ebf8b 1127 wxPoint *p = (wxPoint *)node->GetData();
12bdd77c
JS
1128 delete p;
1129 }
2aaa050c 1130 }
12bdd77c
JS
1131 }
1132 else
1133 {
b1c6a00e 1134 wxNode* node;
12bdd77c 1135 // copy whole ellipse, wxPoints will be deleted outside
2c3ebf8b 1136 for( node = pointsarray[0].GetFirst(); node; node = node->GetNext() )
12bdd77c 1137 {
2c3ebf8b 1138 wxObject *p = node->GetData();
12bdd77c
JS
1139 points->Append( p );
1140 }
2c3ebf8b 1141 for( node = pointsarray[1].GetFirst(); node; node = node->GetNext() )
12bdd77c 1142 {
2c3ebf8b 1143 wxObject *p = node->GetData();
12bdd77c
JS
1144 points->Append( p );
1145 }
2c3ebf8b 1146 for( node = pointsarray[2].GetFirst(); node; node = node->GetNext() )
12bdd77c 1147 {
2c3ebf8b 1148 wxObject *p = node->GetData();
12bdd77c
JS
1149 points->Append( p );
1150 }
2c3ebf8b 1151 for( node = pointsarray[3].GetFirst(); node; node = node->GetNext() )
12bdd77c 1152 {
2c3ebf8b 1153 wxObject *p = node->GetData();
12bdd77c
JS
1154 points->Append( p );
1155 }
1156 } // not iUseAngles
1157} // CalculateEllipticPoints
1158
abeb53aa 1159#endif