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