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