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