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