support for GTK3
[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/dcclient.h"
29 #include "wx/dcmemory.h"
30 #include "wx/dcscreen.h"
31 #include "wx/dcprint.h"
32 #include "wx/prntbase.h"
33 #include "wx/scopeguard.h"
34
35 #ifndef WX_PRECOMP
36 #include "wx/math.h"
37 #include "wx/module.h"
38 #include "wx/window.h"
39 #endif
40
41 #ifdef __WXMSW__
42 #include "wx/msw/dcclient.h"
43 #include "wx/msw/dcmemory.h"
44 #include "wx/msw/dcscreen.h"
45 #endif
46
47 #ifdef __WXGTK3__
48 #include "wx/gtk/dc.h"
49 #elif defined __WXGTK20__
50 #include "wx/gtk/dcclient.h"
51 #include "wx/gtk/dcmemory.h"
52 #include "wx/gtk/dcscreen.h"
53 #elif defined(__WXGTK__)
54 #include "wx/gtk1/dcclient.h"
55 #include "wx/gtk1/dcmemory.h"
56 #include "wx/gtk1/dcscreen.h"
57 #endif
58
59 #ifdef __WXMAC__
60 #include "wx/osx/dcclient.h"
61 #include "wx/osx/dcmemory.h"
62 #include "wx/osx/dcscreen.h"
63 #endif
64
65 #ifdef __WXPM__
66 #include "wx/os2/dcclient.h"
67 #include "wx/os2/dcmemory.h"
68 #include "wx/os2/dcscreen.h"
69 #endif
70
71 #ifdef __WXCOCOA__
72 #include "wx/cocoa/dcclient.h"
73 #include "wx/cocoa/dcmemory.h"
74 #include "wx/cocoa/dcscreen.h"
75 #endif
76
77 #ifdef __WXMOTIF__
78 #include "wx/motif/dcclient.h"
79 #include "wx/motif/dcmemory.h"
80 #include "wx/motif/dcscreen.h"
81 #endif
82
83 #ifdef __WXX11__
84 #include "wx/x11/dcclient.h"
85 #include "wx/x11/dcmemory.h"
86 #include "wx/x11/dcscreen.h"
87 #endif
88
89 #ifdef __WXDFB__
90 #include "wx/dfb/dcclient.h"
91 #include "wx/dfb/dcmemory.h"
92 #include "wx/dfb/dcscreen.h"
93 #endif
94
95 //----------------------------------------------------------------------------
96 // wxDCFactory
97 //----------------------------------------------------------------------------
98
99 wxDCFactory *wxDCFactory::m_factory = NULL;
100
101 void wxDCFactory::Set(wxDCFactory *factory)
102 {
103 delete m_factory;
104
105 m_factory = factory;
106 }
107
108 wxDCFactory *wxDCFactory::Get()
109 {
110 if ( !m_factory )
111 m_factory = new wxNativeDCFactory;
112
113 return m_factory;
114 }
115
116 class wxDCFactoryCleanupModule : public wxModule
117 {
118 public:
119 virtual bool OnInit() { return true; }
120 virtual void OnExit() { wxDCFactory::Set(NULL); }
121
122 private:
123 DECLARE_DYNAMIC_CLASS(wxDCFactoryCleanupModule)
124 };
125
126 IMPLEMENT_DYNAMIC_CLASS(wxDCFactoryCleanupModule, wxModule)
127
128 //-----------------------------------------------------------------------------
129 // wxNativeDCFactory
130 //-----------------------------------------------------------------------------
131
132 wxDCImpl* wxNativeDCFactory::CreateWindowDC( wxWindowDC *owner, wxWindow *window )
133 {
134 wxDCImpl * const impl = new wxWindowDCImpl( owner, window );
135 impl->InheritAttributes(window);
136 return impl;
137 }
138
139 wxDCImpl* wxNativeDCFactory::CreateClientDC( wxClientDC *owner, wxWindow *window )
140 {
141 wxDCImpl * const impl = new wxClientDCImpl( owner, window );
142 impl->InheritAttributes(window);
143 return impl;
144 }
145
146 wxDCImpl* wxNativeDCFactory::CreatePaintDC( wxPaintDC *owner, wxWindow *window )
147 {
148 wxDCImpl * const impl = new wxPaintDCImpl( owner, window );
149 impl->InheritAttributes(window);
150 return impl;
151 }
152
153 wxDCImpl* wxNativeDCFactory::CreateMemoryDC( wxMemoryDC *owner )
154 {
155 return new wxMemoryDCImpl( owner );
156 }
157
158 wxDCImpl* wxNativeDCFactory::CreateMemoryDC(wxMemoryDC *owner, wxBitmap& bitmap)
159 {
160 // the bitmap may be modified when it's selected into a memory DC so make
161 // sure changing this bitmap doesn't affect any other shallow copies of it
162 // (see wxMemoryDC::SelectObject())
163 //
164 // notice that we don't provide any ctor equivalent to SelectObjectAsSource
165 // method because this should be rarely needed and easy to work around by
166 // using the default ctor and calling SelectObjectAsSource itself
167 if ( bitmap.IsOk() )
168 bitmap.UnShare();
169
170 return new wxMemoryDCImpl(owner, bitmap);
171 }
172
173 wxDCImpl* wxNativeDCFactory::CreateMemoryDC( wxMemoryDC *owner, wxDC *dc )
174 {
175 return new wxMemoryDCImpl( owner, dc );
176 }
177
178 wxDCImpl* wxNativeDCFactory::CreateScreenDC( wxScreenDC *owner )
179 {
180 return new wxScreenDCImpl( owner );
181 }
182
183 #if wxUSE_PRINTING_ARCHITECTURE
184 wxDCImpl *wxNativeDCFactory::CreatePrinterDC( wxPrinterDC *owner, const wxPrintData &data )
185 {
186 wxPrintFactory *factory = wxPrintFactory::GetFactory();
187 return factory->CreatePrinterDCImpl( owner, data );
188 }
189 #endif
190
191 //-----------------------------------------------------------------------------
192 // wxWindowDC
193 //-----------------------------------------------------------------------------
194
195 IMPLEMENT_ABSTRACT_CLASS(wxWindowDC, wxDC)
196
197 wxWindowDC::wxWindowDC(wxWindow *win)
198 : wxDC(wxDCFactory::Get()->CreateWindowDC(this, win))
199 {
200 }
201
202 //-----------------------------------------------------------------------------
203 // wxClientDC
204 //-----------------------------------------------------------------------------
205
206 IMPLEMENT_ABSTRACT_CLASS(wxClientDC, wxWindowDC)
207
208 wxClientDC::wxClientDC(wxWindow *win)
209 : wxWindowDC(wxDCFactory::Get()->CreateClientDC(this, win))
210 {
211 }
212
213 //-----------------------------------------------------------------------------
214 // wxMemoryDC
215 //-----------------------------------------------------------------------------
216
217 IMPLEMENT_DYNAMIC_CLASS(wxMemoryDC, wxDC)
218
219 wxMemoryDC::wxMemoryDC()
220 : wxDC(wxDCFactory::Get()->CreateMemoryDC(this))
221 {
222 }
223
224 wxMemoryDC::wxMemoryDC(wxBitmap& bitmap)
225 : wxDC(wxDCFactory::Get()->CreateMemoryDC(this, bitmap))
226 {
227 }
228
229 wxMemoryDC::wxMemoryDC(wxDC *dc)
230 : wxDC(wxDCFactory::Get()->CreateMemoryDC(this, dc))
231 {
232 }
233
234 void wxMemoryDC::SelectObject(wxBitmap& bmp)
235 {
236 if ( bmp.IsSameAs(GetSelectedBitmap()) )
237 {
238 // Nothing to do, this bitmap is already selected.
239 return;
240 }
241
242 // make sure that the given wxBitmap is not sharing its data with other
243 // wxBitmap instances as its contents will be modified by any drawing
244 // operation done on this DC
245 if (bmp.IsOk())
246 bmp.UnShare();
247
248 GetImpl()->DoSelect(bmp);
249 }
250
251 void wxMemoryDC::SelectObjectAsSource(const wxBitmap& bmp)
252 {
253 GetImpl()->DoSelect(bmp);
254 }
255
256 const wxBitmap& wxMemoryDC::GetSelectedBitmap() const
257 {
258 return GetImpl()->GetSelectedBitmap();
259 }
260
261 wxBitmap& wxMemoryDC::GetSelectedBitmap()
262 {
263 return GetImpl()->GetSelectedBitmap();
264 }
265
266
267 //-----------------------------------------------------------------------------
268 // wxPaintDC
269 //-----------------------------------------------------------------------------
270
271 IMPLEMENT_ABSTRACT_CLASS(wxPaintDC, wxClientDC)
272
273 wxPaintDC::wxPaintDC(wxWindow *win)
274 : wxClientDC(wxDCFactory::Get()->CreatePaintDC(this, win))
275 {
276 }
277
278 //-----------------------------------------------------------------------------
279 // wxScreenDC
280 //-----------------------------------------------------------------------------
281
282 IMPLEMENT_DYNAMIC_CLASS(wxScreenDC, wxWindowDC)
283
284 wxScreenDC::wxScreenDC()
285 : wxDC(wxDCFactory::Get()->CreateScreenDC(this))
286 {
287 }
288
289 //-----------------------------------------------------------------------------
290 // wxPrinterDC
291 //-----------------------------------------------------------------------------
292
293 #if wxUSE_PRINTING_ARCHITECTURE
294
295 IMPLEMENT_DYNAMIC_CLASS(wxPrinterDC, wxDC)
296
297 wxPrinterDC::wxPrinterDC()
298 : wxDC(wxDCFactory::Get()->CreatePrinterDC(this, wxPrintData()))
299 {
300 }
301
302 wxPrinterDC::wxPrinterDC(const wxPrintData& data)
303 : wxDC(wxDCFactory::Get()->CreatePrinterDC(this, data))
304 {
305 }
306
307 wxRect wxPrinterDC::GetPaperRect() const
308 {
309 return GetImpl()->GetPaperRect();
310 }
311
312 int wxPrinterDC::GetResolution() const
313 {
314 return GetImpl()->GetResolution();
315 }
316
317 #endif // wxUSE_PRINTING_ARCHITECTURE
318
319 //-----------------------------------------------------------------------------
320 // wxDCImpl
321 //-----------------------------------------------------------------------------
322
323 IMPLEMENT_ABSTRACT_CLASS(wxDCImpl, wxObject)
324
325 wxDCImpl::wxDCImpl( wxDC *owner )
326 : m_window(NULL)
327 , m_colour(wxColourDisplay())
328 , m_ok(true)
329 , m_clipping(false)
330 , m_isInteractive(0)
331 , m_isBBoxValid(false)
332 , m_logicalOriginX(0), m_logicalOriginY(0)
333 , m_deviceOriginX(0), m_deviceOriginY(0)
334 , m_deviceLocalOriginX(0), m_deviceLocalOriginY(0)
335 , m_logicalScaleX(1.0), m_logicalScaleY(1.0)
336 , m_userScaleX(1.0), m_userScaleY(1.0)
337 , m_scaleX(1.0), m_scaleY(1.0)
338 , m_signX(1), m_signY(1)
339 , m_minX(0), m_minY(0), m_maxX(0), m_maxY(0)
340 , m_clipX1(0), m_clipY1(0), m_clipX2(0), m_clipY2(0)
341 , m_logicalFunction(wxCOPY)
342 , m_backgroundMode(wxBRUSHSTYLE_TRANSPARENT)
343 , m_mappingMode(wxMM_TEXT)
344 , m_pen()
345 , m_brush()
346 , m_backgroundBrush()
347 , m_textForegroundColour(*wxBLACK)
348 , m_textBackgroundColour(*wxWHITE)
349 , m_font()
350 #if wxUSE_PALETTE
351 , m_palette()
352 , m_hasCustomPalette(false)
353 #endif // wxUSE_PALETTE
354 {
355 m_owner = owner;
356
357 m_mm_to_pix_x = (double)wxGetDisplaySize().GetWidth() /
358 (double)wxGetDisplaySizeMM().GetWidth();
359 m_mm_to_pix_y = (double)wxGetDisplaySize().GetHeight() /
360 (double)wxGetDisplaySizeMM().GetHeight();
361
362 ResetBoundingBox();
363 ResetClipping();
364 }
365
366 wxDCImpl::~wxDCImpl()
367 {
368 }
369
370 // ----------------------------------------------------------------------------
371 // coordinate conversions and transforms
372 // ----------------------------------------------------------------------------
373
374 wxCoord wxDCImpl::DeviceToLogicalX(wxCoord x) const
375 {
376 return wxRound( (double)((x - m_deviceOriginX - m_deviceLocalOriginX) * m_signX) / m_scaleX ) + m_logicalOriginX ;
377 }
378
379 wxCoord wxDCImpl::DeviceToLogicalY(wxCoord y) const
380 {
381 return wxRound( (double)((y - m_deviceOriginY - m_deviceLocalOriginY) * m_signY) / m_scaleY ) + m_logicalOriginY ;
382 }
383
384 wxCoord wxDCImpl::DeviceToLogicalXRel(wxCoord x) const
385 {
386 return wxRound((double)(x) / m_scaleX);
387 }
388
389 wxCoord wxDCImpl::DeviceToLogicalYRel(wxCoord y) const
390 {
391 return wxRound((double)(y) / m_scaleY);
392 }
393
394 wxCoord wxDCImpl::LogicalToDeviceX(wxCoord x) const
395 {
396 return wxRound( (double)((x - m_logicalOriginX) * m_signX) * m_scaleX) + m_deviceOriginX + m_deviceLocalOriginX;
397 }
398
399 wxCoord wxDCImpl::LogicalToDeviceY(wxCoord y) const
400 {
401 return wxRound( (double)((y - m_logicalOriginY) * m_signY) * m_scaleY) + m_deviceOriginY + m_deviceLocalOriginY;
402 }
403
404 wxCoord wxDCImpl::LogicalToDeviceXRel(wxCoord x) const
405 {
406 return wxRound((double)(x) * m_scaleX);
407 }
408
409 wxCoord wxDCImpl::LogicalToDeviceYRel(wxCoord y) const
410 {
411 return wxRound((double)(y) * m_scaleY);
412 }
413
414 void wxDCImpl::ComputeScaleAndOrigin()
415 {
416 m_scaleX = m_logicalScaleX * m_userScaleX;
417 m_scaleY = m_logicalScaleY * m_userScaleY;
418 }
419
420 void wxDCImpl::SetMapMode( wxMappingMode mode )
421 {
422 switch (mode)
423 {
424 case wxMM_TWIPS:
425 SetLogicalScale( twips2mm*m_mm_to_pix_x, twips2mm*m_mm_to_pix_y );
426 break;
427 case wxMM_POINTS:
428 SetLogicalScale( pt2mm*m_mm_to_pix_x, pt2mm*m_mm_to_pix_y );
429 break;
430 case wxMM_METRIC:
431 SetLogicalScale( m_mm_to_pix_x, m_mm_to_pix_y );
432 break;
433 case wxMM_LOMETRIC:
434 SetLogicalScale( m_mm_to_pix_x/10.0, m_mm_to_pix_y/10.0 );
435 break;
436 default:
437 case wxMM_TEXT:
438 SetLogicalScale( 1.0, 1.0 );
439 break;
440 }
441 m_mappingMode = mode;
442 }
443
444 void wxDCImpl::SetUserScale( double x, double y )
445 {
446 // allow negative ? -> no
447 m_userScaleX = x;
448 m_userScaleY = y;
449 ComputeScaleAndOrigin();
450 }
451
452 void wxDCImpl::SetLogicalScale( double x, double y )
453 {
454 // allow negative ?
455 m_logicalScaleX = x;
456 m_logicalScaleY = y;
457 ComputeScaleAndOrigin();
458 }
459
460 void wxDCImpl::SetLogicalOrigin( wxCoord x, wxCoord y )
461 {
462 m_logicalOriginX = x * m_signX;
463 m_logicalOriginY = y * m_signY;
464 ComputeScaleAndOrigin();
465 }
466
467 void wxDCImpl::SetDeviceOrigin( wxCoord x, wxCoord y )
468 {
469 m_deviceOriginX = x;
470 m_deviceOriginY = y;
471 ComputeScaleAndOrigin();
472 }
473
474 void wxDCImpl::SetDeviceLocalOrigin( wxCoord x, wxCoord y )
475 {
476 m_deviceLocalOriginX = x;
477 m_deviceLocalOriginY = y;
478 ComputeScaleAndOrigin();
479 }
480
481 void wxDCImpl::SetAxisOrientation( bool xLeftRight, bool yBottomUp )
482 {
483 // only wxPostScripDC has m_signX = -1, we override SetAxisOrientation there
484 // wxWidgets 2.9: no longer override it
485 m_signX = (xLeftRight ? 1 : -1);
486 m_signY = (yBottomUp ? -1 : 1);
487 ComputeScaleAndOrigin();
488 }
489
490
491 // Each element of the widths array will be the width of the string up to and
492 // including the corresponding character in text. This is the generic
493 // implementation, the port-specific classes should do this with native APIs
494 // if available and if faster. Note: pango_layout_index_to_pos is much slower
495 // than calling GetTextExtent!!
496
497 #define FWC_SIZE 256
498
499 class FontWidthCache
500 {
501 public:
502 FontWidthCache() : m_scaleX(1), m_widths(NULL) { }
503 ~FontWidthCache() { delete []m_widths; }
504
505 void Reset()
506 {
507 if (!m_widths)
508 m_widths = new int[FWC_SIZE];
509
510 memset(m_widths, 0, sizeof(int)*FWC_SIZE);
511 }
512
513 wxFont m_font;
514 double m_scaleX;
515 int *m_widths;
516 };
517
518 static FontWidthCache s_fontWidthCache;
519
520 bool wxDCImpl::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
521 {
522 int totalWidth = 0;
523
524 const size_t len = text.length();
525 widths.Empty();
526 widths.Add(0, len);
527
528 // reset the cache if font or horizontal scale have changed
529 if ( !s_fontWidthCache.m_widths ||
530 !wxIsSameDouble(s_fontWidthCache.m_scaleX, m_scaleX) ||
531 (s_fontWidthCache.m_font != GetFont()) )
532 {
533 s_fontWidthCache.Reset();
534 s_fontWidthCache.m_font = GetFont();
535 s_fontWidthCache.m_scaleX = m_scaleX;
536 }
537
538 // Calculate the position of each character based on the widths of
539 // the previous characters
540 int w, h;
541 for ( size_t i = 0; i < len; i++ )
542 {
543 const wxChar c = text[i];
544 unsigned int c_int = (unsigned int)c;
545
546 if ((c_int < FWC_SIZE) && (s_fontWidthCache.m_widths[c_int] != 0))
547 {
548 w = s_fontWidthCache.m_widths[c_int];
549 }
550 else
551 {
552 DoGetTextExtent(c, &w, &h);
553 if (c_int < FWC_SIZE)
554 s_fontWidthCache.m_widths[c_int] = w;
555 }
556
557 totalWidth += w;
558 widths[i] = totalWidth;
559 }
560
561 return true;
562 }
563
564 void wxDCImpl::GetMultiLineTextExtent(const wxString& text,
565 wxCoord *x,
566 wxCoord *y,
567 wxCoord *h,
568 const wxFont *font) const
569 {
570 wxCoord widthTextMax = 0, widthLine,
571 heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
572
573 wxString curLine;
574 for ( wxString::const_iterator pc = text.begin(); ; ++pc )
575 {
576 if ( pc == text.end() || *pc == wxT('\n') )
577 {
578 if ( curLine.empty() )
579 {
580 // we can't use GetTextExtent - it will return 0 for both width
581 // and height and an empty line should count in height
582 // calculation
583
584 // assume that this line has the same height as the previous
585 // one
586 if ( !heightLineDefault )
587 heightLineDefault = heightLine;
588
589 if ( !heightLineDefault )
590 {
591 // but we don't know it yet - choose something reasonable
592 DoGetTextExtent(wxT("W"), NULL, &heightLineDefault,
593 NULL, NULL, font);
594 }
595
596 heightTextTotal += heightLineDefault;
597 }
598 else
599 {
600 DoGetTextExtent(curLine, &widthLine, &heightLine,
601 NULL, NULL, font);
602 if ( widthLine > widthTextMax )
603 widthTextMax = widthLine;
604 heightTextTotal += heightLine;
605 }
606
607 if ( pc == text.end() )
608 {
609 break;
610 }
611 else // '\n'
612 {
613 curLine.clear();
614 }
615 }
616 else
617 {
618 curLine += *pc;
619 }
620 }
621
622 if ( x )
623 *x = widthTextMax;
624 if ( y )
625 *y = heightTextTotal;
626 if ( h )
627 *h = heightLine;
628 }
629
630 void wxDCImpl::DoDrawCheckMark(wxCoord x1, wxCoord y1,
631 wxCoord width, wxCoord height)
632 {
633 wxCHECK_RET( IsOk(), wxT("invalid window dc") );
634
635 wxCoord x2 = x1 + width,
636 y2 = y1 + height;
637
638 // the pen width is calibrated to give 3 for width == height == 10
639 wxDCPenChanger pen( *m_owner, wxPen(GetTextForeground(), (width + height + 1)/7));
640
641 // we're drawing a scaled version of wx/generic/tick.xpm here
642 wxCoord x3 = x1 + (4*width) / 10, // x of the tick bottom
643 y3 = y1 + height / 2; // y of the left tick branch
644 DoDrawLine(x1, y3, x3, y2);
645 DoDrawLine(x3, y2, x2, y1);
646
647 CalcBoundingBox(x1, y1);
648 CalcBoundingBox(x2, y2);
649 }
650
651 bool
652 wxDCImpl::DoStretchBlit(wxCoord xdest, wxCoord ydest,
653 wxCoord dstWidth, wxCoord dstHeight,
654 wxDC *source,
655 wxCoord xsrc, wxCoord ysrc,
656 wxCoord srcWidth, wxCoord srcHeight,
657 wxRasterOperationMode rop,
658 bool useMask,
659 wxCoord xsrcMask,
660 wxCoord ysrcMask)
661 {
662 wxCHECK_MSG( srcWidth && srcHeight && dstWidth && dstHeight, false,
663 wxT("invalid blit size") );
664
665 // emulate the stretching by modifying the DC scale
666 double xscale = (double)srcWidth/dstWidth,
667 yscale = (double)srcHeight/dstHeight;
668
669 double xscaleOld, yscaleOld;
670 GetUserScale(&xscaleOld, &yscaleOld);
671 SetUserScale(xscaleOld/xscale, yscaleOld/yscale);
672
673 bool rc = DoBlit(wxCoord(xdest*xscale), wxCoord(ydest*yscale),
674 wxCoord(dstWidth*xscale), wxCoord(dstHeight*yscale),
675 source,
676 xsrc, ysrc, rop, useMask, xsrcMask, ysrcMask);
677
678 SetUserScale(xscaleOld, yscaleOld);
679
680 return rc;
681 }
682
683 void wxDCImpl::DrawLines(const wxPointList *list, wxCoord xoffset, wxCoord yoffset)
684 {
685 int n = list->GetCount();
686 wxPoint *points = new wxPoint[n];
687
688 int i = 0;
689 for ( wxPointList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
690 {
691 wxPoint *point = node->GetData();
692 points[i].x = point->x;
693 points[i].y = point->y;
694 }
695
696 DoDrawLines(n, points, xoffset, yoffset);
697
698 delete [] points;
699 }
700
701 void wxDCImpl::DrawPolygon(const wxPointList *list,
702 wxCoord xoffset, wxCoord yoffset,
703 wxPolygonFillMode fillStyle)
704 {
705 int n = list->GetCount();
706 wxPoint *points = new wxPoint[n];
707
708 int i = 0;
709 for ( wxPointList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
710 {
711 wxPoint *point = node->GetData();
712 points[i].x = point->x;
713 points[i].y = point->y;
714 }
715
716 DoDrawPolygon(n, points, xoffset, yoffset, fillStyle);
717
718 delete [] points;
719 }
720
721 void
722 wxDCImpl::DoDrawPolyPolygon(int n,
723 int count[],
724 wxPoint points[],
725 wxCoord xoffset, wxCoord yoffset,
726 wxPolygonFillMode fillStyle)
727 {
728 if ( n == 1 )
729 {
730 DoDrawPolygon(count[0], points, xoffset, yoffset, fillStyle);
731 return;
732 }
733
734 int i, j, lastOfs;
735 wxPoint* pts;
736
737 for (i = j = lastOfs = 0; i < n; i++)
738 {
739 lastOfs = j;
740 j += count[i];
741 }
742 pts = new wxPoint[j+n-1];
743 for (i = 0; i < j; i++)
744 pts[i] = points[i];
745 for (i = 2; i <= n; i++)
746 {
747 lastOfs -= count[n-i];
748 pts[j++] = pts[lastOfs];
749 }
750
751 {
752 wxDCPenChanger setTransp(*m_owner, *wxTRANSPARENT_PEN);
753 DoDrawPolygon(j, pts, xoffset, yoffset, fillStyle);
754 }
755
756 for (i = j = 0; i < n; i++)
757 {
758 DoDrawLines(count[i], pts+j, xoffset, yoffset);
759 j += count[i];
760 }
761 delete[] pts;
762 }
763
764 #if wxUSE_SPLINES
765
766 void wxDCImpl::DrawSpline(wxCoord x1, wxCoord y1,
767 wxCoord x2, wxCoord y2,
768 wxCoord x3, wxCoord y3)
769 {
770 wxPoint points[] = { wxPoint(x1, y1), wxPoint(x2, y2), wxPoint(x3, y3) };
771 DrawSpline(WXSIZEOF(points), points);
772 }
773
774 void wxDCImpl::DrawSpline(int n, wxPoint points[])
775 {
776 wxPointList list;
777 for ( int i = 0; i < n; i++ )
778 list.Append(&points[i]);
779
780 DrawSpline(&list);
781 }
782
783 // ----------------------------------- spline code ----------------------------------------
784
785 void wx_quadratic_spline(double a1, double b1, double a2, double b2,
786 double a3, double b3, double a4, double b4);
787 void wx_clear_stack();
788 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
789 double *y3, double *x4, double *y4);
790 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
791 double x4, double y4);
792 static bool wx_spline_add_point(double x, double y);
793 static void wx_spline_draw_point_array(wxDC *dc);
794
795 static wxPointList wx_spline_point_list;
796
797 #define half(z1, z2) ((z1+z2)/2.0)
798 #define THRESHOLD 5
799
800 /* iterative version */
801
802 void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4,
803 double b4)
804 {
805 register double xmid, ymid;
806 double x1, y1, x2, y2, x3, y3, x4, y4;
807
808 wx_clear_stack();
809 wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
810
811 while (wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
812 xmid = (double)half(x2, x3);
813 ymid = (double)half(y2, y3);
814 if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
815 fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
816 wx_spline_add_point( x1, y1 );
817 wx_spline_add_point( xmid, ymid );
818 } else {
819 wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3),
820 (double)half(x3, x4), (double)half(y3, y4), x4, y4);
821 wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
822 (double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid);
823 }
824 }
825 }
826
827 /* utilities used by spline drawing routines */
828
829 typedef struct wx_spline_stack_struct {
830 double x1, y1, x2, y2, x3, y3, x4, y4;
831 } Stack;
832
833 #define SPLINE_STACK_DEPTH 20
834 static Stack wx_spline_stack[SPLINE_STACK_DEPTH];
835 static Stack *wx_stack_top;
836 static int wx_stack_count;
837
838 void wx_clear_stack()
839 {
840 wx_stack_top = wx_spline_stack;
841 wx_stack_count = 0;
842 }
843
844 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
845 {
846 wx_stack_top->x1 = x1;
847 wx_stack_top->y1 = y1;
848 wx_stack_top->x2 = x2;
849 wx_stack_top->y2 = y2;
850 wx_stack_top->x3 = x3;
851 wx_stack_top->y3 = y3;
852 wx_stack_top->x4 = x4;
853 wx_stack_top->y4 = y4;
854 wx_stack_top++;
855 wx_stack_count++;
856 }
857
858 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
859 double *x3, double *y3, double *x4, double *y4)
860 {
861 if (wx_stack_count == 0)
862 return (0);
863 wx_stack_top--;
864 wx_stack_count--;
865 *x1 = wx_stack_top->x1;
866 *y1 = wx_stack_top->y1;
867 *x2 = wx_stack_top->x2;
868 *y2 = wx_stack_top->y2;
869 *x3 = wx_stack_top->x3;
870 *y3 = wx_stack_top->y3;
871 *x4 = wx_stack_top->x4;
872 *y4 = wx_stack_top->y4;
873 return (1);
874 }
875
876 static bool wx_spline_add_point(double x, double y)
877 {
878 wxPoint *point = new wxPoint( wxRound(x), wxRound(y) );
879 wx_spline_point_list.Append(point );
880 return true;
881 }
882
883 static void wx_spline_draw_point_array(wxDC *dc)
884 {
885 dc->DrawLines(&wx_spline_point_list, 0, 0 );
886 wxPointList::compatibility_iterator node = wx_spline_point_list.GetFirst();
887 while (node)
888 {
889 wxPoint *point = node->GetData();
890 delete point;
891 wx_spline_point_list.Erase(node);
892 node = wx_spline_point_list.GetFirst();
893 }
894 }
895
896 void wxDCImpl::DoDrawSpline( const wxPointList *points )
897 {
898 wxCHECK_RET( IsOk(), wxT("invalid window dc") );
899
900 wxPoint *p;
901 double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
902 double x1, y1, x2, y2;
903
904 wxPointList::compatibility_iterator node = points->GetFirst();
905 if (!node)
906 // empty list
907 return;
908
909 p = (wxPoint *)node->GetData();
910
911 x1 = p->x;
912 y1 = p->y;
913
914 node = node->GetNext();
915 p = node->GetData();
916
917 x2 = p->x;
918 y2 = p->y;
919 cx1 = (double)((x1 + x2) / 2);
920 cy1 = (double)((y1 + y2) / 2);
921 cx2 = (double)((cx1 + x2) / 2);
922 cy2 = (double)((cy1 + y2) / 2);
923
924 wx_spline_add_point(x1, y1);
925
926 while ((node = node->GetNext())
927 #if !wxUSE_STD_CONTAINERS
928 != NULL
929 #endif // !wxUSE_STD_CONTAINERS
930 )
931 {
932 p = node->GetData();
933 x1 = x2;
934 y1 = y2;
935 x2 = p->x;
936 y2 = p->y;
937 cx4 = (double)(x1 + x2) / 2;
938 cy4 = (double)(y1 + y2) / 2;
939 cx3 = (double)(x1 + cx4) / 2;
940 cy3 = (double)(y1 + cy4) / 2;
941
942 wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
943
944 cx1 = cx4;
945 cy1 = cy4;
946 cx2 = (double)(cx1 + x2) / 2;
947 cy2 = (double)(cy1 + y2) / 2;
948 }
949
950 wx_spline_add_point( cx1, cy1 );
951 wx_spline_add_point( x2, y2 );
952
953 wx_spline_draw_point_array( m_owner );
954 }
955
956 #endif // wxUSE_SPLINES
957
958
959
960 void wxDCImpl::DoGradientFillLinear(const wxRect& rect,
961 const wxColour& initialColour,
962 const wxColour& destColour,
963 wxDirection nDirection)
964 {
965 // save old pen
966 wxPen oldPen = m_pen;
967 wxBrush oldBrush = m_brush;
968
969 wxUint8 nR1 = initialColour.Red();
970 wxUint8 nG1 = initialColour.Green();
971 wxUint8 nB1 = initialColour.Blue();
972 wxUint8 nR2 = destColour.Red();
973 wxUint8 nG2 = destColour.Green();
974 wxUint8 nB2 = destColour.Blue();
975 wxUint8 nR, nG, nB;
976
977 if ( nDirection == wxEAST || nDirection == wxWEST )
978 {
979 wxInt32 x = rect.GetWidth();
980 wxInt32 w = x; // width of area to shade
981 wxInt32 xDelta = w/256; // height of one shade bend
982 if (xDelta < 1)
983 xDelta = 1;
984
985 while (x >= xDelta)
986 {
987 x -= xDelta;
988 if (nR1 > nR2)
989 nR = nR1 - (nR1-nR2)*(w-x)/w;
990 else
991 nR = nR1 + (nR2-nR1)*(w-x)/w;
992
993 if (nG1 > nG2)
994 nG = nG1 - (nG1-nG2)*(w-x)/w;
995 else
996 nG = nG1 + (nG2-nG1)*(w-x)/w;
997
998 if (nB1 > nB2)
999 nB = nB1 - (nB1-nB2)*(w-x)/w;
1000 else
1001 nB = nB1 + (nB2-nB1)*(w-x)/w;
1002
1003 wxColour colour(nR,nG,nB);
1004 SetPen(wxPen(colour, 1, wxPENSTYLE_SOLID));
1005 SetBrush(wxBrush(colour));
1006 if(nDirection == wxEAST)
1007 DoDrawRectangle(rect.GetRight()-x-xDelta+1, rect.GetTop(),
1008 xDelta, rect.GetHeight());
1009 else //nDirection == wxWEST
1010 DoDrawRectangle(rect.GetLeft()+x, rect.GetTop(),
1011 xDelta, rect.GetHeight());
1012 }
1013 }
1014 else // nDirection == wxNORTH || nDirection == wxSOUTH
1015 {
1016 wxInt32 y = rect.GetHeight();
1017 wxInt32 w = y; // height of area to shade
1018 wxInt32 yDelta = w/255; // height of one shade bend
1019 if (yDelta < 1)
1020 yDelta = 1;
1021
1022 while (y > 0)
1023 {
1024 y -= yDelta;
1025 if (nR1 > nR2)
1026 nR = nR1 - (nR1-nR2)*(w-y)/w;
1027 else
1028 nR = nR1 + (nR2-nR1)*(w-y)/w;
1029
1030 if (nG1 > nG2)
1031 nG = nG1 - (nG1-nG2)*(w-y)/w;
1032 else
1033 nG = nG1 + (nG2-nG1)*(w-y)/w;
1034
1035 if (nB1 > nB2)
1036 nB = nB1 - (nB1-nB2)*(w-y)/w;
1037 else
1038 nB = nB1 + (nB2-nB1)*(w-y)/w;
1039
1040 wxColour colour(nR,nG,nB);
1041 SetPen(wxPen(colour, 1, wxPENSTYLE_SOLID));
1042 SetBrush(wxBrush(colour));
1043 if(nDirection == wxNORTH)
1044 DoDrawRectangle(rect.GetLeft(), rect.GetTop()+y,
1045 rect.GetWidth(), yDelta);
1046 else //nDirection == wxSOUTH
1047 DoDrawRectangle(rect.GetLeft(), rect.GetBottom()-y-yDelta+1,
1048 rect.GetWidth(), yDelta);
1049 }
1050 }
1051
1052 SetPen(oldPen);
1053 SetBrush(oldBrush);
1054 }
1055
1056 void wxDCImpl::DoGradientFillConcentric(const wxRect& rect,
1057 const wxColour& initialColour,
1058 const wxColour& destColour,
1059 const wxPoint& circleCenter)
1060 {
1061 // save the old pen and ensure it is restored on exit
1062 const wxPen penOrig = m_pen;
1063 wxON_BLOCK_EXIT_SET(m_pen, penOrig);
1064
1065 wxUint8 nR1 = destColour.Red();
1066 wxUint8 nG1 = destColour.Green();
1067 wxUint8 nB1 = destColour.Blue();
1068 wxUint8 nR2 = initialColour.Red();
1069 wxUint8 nG2 = initialColour.Green();
1070 wxUint8 nB2 = initialColour.Blue();
1071 wxUint8 nR, nG, nB;
1072
1073
1074 //Radius
1075 double cx = rect.GetWidth() / 2;
1076 double cy = rect.GetHeight() / 2;
1077 double dRadius;
1078 if (cx < cy)
1079 dRadius = cx;
1080 else
1081 dRadius = cy;
1082
1083 //Offset of circle
1084 double ptX, ptY;
1085 ptX = circleCenter.x;
1086 ptY = circleCenter.y;
1087 double nCircleOffX = ptX - cx;
1088 double nCircleOffY = ptY - cy;
1089
1090 double dGradient;
1091 double dx, dy;
1092
1093 for ( wxInt32 x = 0; x < rect.GetWidth(); x++ )
1094 {
1095 for ( wxInt32 y = 0; y < rect.GetHeight(); y++ )
1096 {
1097 //get color difference
1098 dx = x;
1099 dy = y;
1100
1101 dGradient = ((dRadius - sqrt( (dx - cx - nCircleOffX) * (dx - cx - nCircleOffX)
1102 +(dy - cy - nCircleOffY) * (dy - cy - nCircleOffY)
1103 )
1104 ) * 100
1105 ) / dRadius;
1106
1107 //normalize Gradient
1108 if (dGradient < 0)
1109 dGradient = 0.0;
1110
1111 //get dest colors
1112 nR = (wxUint8)(nR1 + ((nR2 - nR1) * dGradient / 100));
1113 nG = (wxUint8)(nG1 + ((nG2 - nG1) * dGradient / 100));
1114 nB = (wxUint8)(nB1 + ((nB2 - nB1) * dGradient / 100));
1115
1116 //set the pixel
1117 SetPen(wxColour(nR,nG,nB));
1118 DoDrawPoint(x + rect.GetLeft(), y + rect.GetTop());
1119 }
1120 }
1121 }
1122
1123 void wxDCImpl::InheritAttributes(wxWindow *win)
1124 {
1125 wxCHECK_RET( win, "window can't be NULL" );
1126
1127 SetFont(win->GetFont());
1128 SetTextForeground(win->GetForegroundColour());
1129 SetTextBackground(win->GetBackgroundColour());
1130 SetBackground(win->GetBackgroundColour());
1131 SetLayoutDirection(win->GetLayoutDirection());
1132 }
1133
1134 void wxDCImpl::DoGetFontMetrics(int *height,
1135 int *ascent,
1136 int *descent,
1137 int *internalLeading,
1138 int *externalLeading,
1139 int *averageWidth) const
1140 {
1141 // Average width is typically the same as width of 'x'.
1142 wxCoord h, d;
1143 DoGetTextExtent("x", averageWidth, &h, &d, externalLeading);
1144
1145 if ( height )
1146 *height = h;
1147 if ( ascent )
1148 *ascent = h - d;
1149 if ( descent )
1150 *descent = d;
1151 if ( internalLeading )
1152 *internalLeading = 0;
1153 }
1154
1155 //-----------------------------------------------------------------------------
1156 // wxDC
1157 //-----------------------------------------------------------------------------
1158
1159 IMPLEMENT_ABSTRACT_CLASS(wxDC, wxObject)
1160
1161 void wxDC::CopyAttributes(const wxDC& dc)
1162 {
1163 SetFont(dc.GetFont());
1164 SetTextForeground(dc.GetTextForeground());
1165 SetTextBackground(dc.GetTextBackground());
1166 SetBackground(dc.GetBackground());
1167 SetLayoutDirection(dc.GetLayoutDirection());
1168 }
1169
1170 void wxDC::DrawLabel(const wxString& text,
1171 const wxBitmap& bitmap,
1172 const wxRect& rect,
1173 int alignment,
1174 int indexAccel,
1175 wxRect *rectBounding)
1176 {
1177 // find the text position
1178 wxCoord widthText, heightText, heightLine;
1179 GetMultiLineTextExtent(text, &widthText, &heightText, &heightLine);
1180
1181 wxCoord width, height;
1182 if ( bitmap.IsOk() )
1183 {
1184 width = widthText + bitmap.GetWidth();
1185 height = bitmap.GetHeight();
1186 }
1187 else // no bitmap
1188 {
1189 width = widthText;
1190 height = heightText;
1191 }
1192
1193 wxCoord x, y;
1194 if ( alignment & wxALIGN_RIGHT )
1195 {
1196 x = rect.GetRight() - width;
1197 }
1198 else if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
1199 {
1200 x = (rect.GetLeft() + rect.GetRight() + 1 - width) / 2;
1201 }
1202 else // alignment & wxALIGN_LEFT
1203 {
1204 x = rect.GetLeft();
1205 }
1206
1207 if ( alignment & wxALIGN_BOTTOM )
1208 {
1209 y = rect.GetBottom() - height;
1210 }
1211 else if ( alignment & wxALIGN_CENTRE_VERTICAL )
1212 {
1213 y = (rect.GetTop() + rect.GetBottom() + 1 - height) / 2;
1214 }
1215 else // alignment & wxALIGN_TOP
1216 {
1217 y = rect.GetTop();
1218 }
1219
1220 // draw the bitmap first
1221 wxCoord x0 = x,
1222 y0 = y,
1223 width0 = width;
1224 if ( bitmap.IsOk() )
1225 {
1226 DrawBitmap(bitmap, x, y, true /* use mask */);
1227
1228 wxCoord offset = bitmap.GetWidth() + 4;
1229 x += offset;
1230 width -= offset;
1231
1232 y += (height - heightText) / 2;
1233 }
1234
1235 // we will draw the underscore under the accel char later
1236 wxCoord startUnderscore = 0,
1237 endUnderscore = 0,
1238 yUnderscore = 0;
1239
1240 // split the string into lines and draw each of them separately
1241 //
1242 // NB: while wxDC::DrawText() on some platforms supports drawing multi-line
1243 // strings natively, this is not the case for all of them, notably not
1244 // wxMSW which uses this function for multi-line texts, so we may only
1245 // call DrawText() for single-line strings from here to avoid infinite
1246 // recursion.
1247 wxString curLine;
1248 for ( wxString::const_iterator pc = text.begin(); ; ++pc )
1249 {
1250 if ( pc == text.end() || *pc == '\n' )
1251 {
1252 int xRealStart = x; // init it here to avoid compielr warnings
1253
1254 if ( !curLine.empty() )
1255 {
1256 // NB: can't test for !(alignment & wxALIGN_LEFT) because
1257 // wxALIGN_LEFT is 0
1258 if ( alignment & (wxALIGN_RIGHT | wxALIGN_CENTRE_HORIZONTAL) )
1259 {
1260 wxCoord widthLine;
1261 GetTextExtent(curLine, &widthLine, NULL);
1262
1263 if ( alignment & wxALIGN_RIGHT )
1264 {
1265 xRealStart += width - widthLine;
1266 }
1267 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
1268 {
1269 xRealStart += (width - widthLine) / 2;
1270 }
1271 }
1272 //else: left aligned, nothing to do
1273
1274 DrawText(curLine, xRealStart, y);
1275 }
1276
1277 y += heightLine;
1278
1279 // do we have underscore in this line? we can check yUnderscore
1280 // because it is set below to just y + heightLine if we do
1281 if ( y == yUnderscore )
1282 {
1283 // adjust the horz positions to account for the shift
1284 startUnderscore += xRealStart;
1285 endUnderscore += xRealStart;
1286 }
1287
1288 if ( pc == text.end() )
1289 break;
1290
1291 curLine.clear();
1292 }
1293 else // not end of line
1294 {
1295 if ( pc - text.begin() == indexAccel )
1296 {
1297 // remember to draw underscore here
1298 GetTextExtent(curLine, &startUnderscore, NULL);
1299 curLine += *pc;
1300 GetTextExtent(curLine, &endUnderscore, NULL);
1301
1302 yUnderscore = y + heightLine;
1303 }
1304 else
1305 {
1306 curLine += *pc;
1307 }
1308 }
1309 }
1310
1311 // draw the underscore if found
1312 if ( startUnderscore != endUnderscore )
1313 {
1314 // it should be of the same colour as text
1315 SetPen(wxPen(GetTextForeground(), 0, wxPENSTYLE_SOLID));
1316
1317 // This adjustment is relatively arbitrary: we need to draw the
1318 // underline slightly higher to avoid overflowing the character cell
1319 // but whether we should do it 1, 2 or 3 pixels higher is not clear.
1320 //
1321 // The currently used value seems to be compatible with native MSW
1322 // behaviour, i.e. it results in the same appearance of the owner-drawn
1323 // and normal labels.
1324 yUnderscore -= 2;
1325
1326 DrawLine(startUnderscore, yUnderscore, endUnderscore, yUnderscore);
1327 }
1328
1329 // return bounding rect if requested
1330 if ( rectBounding )
1331 {
1332 *rectBounding = wxRect(x, y - heightText, widthText, heightText);
1333 }
1334
1335 CalcBoundingBox(x0, y0);
1336 CalcBoundingBox(x0 + width0, y0 + height);
1337 }
1338
1339 #if WXWIN_COMPATIBILITY_2_8
1340 // for compatibility with the old code when wxCoord was long everywhere
1341 void wxDC::GetTextExtent(const wxString& string,
1342 long *x, long *y,
1343 long *descent,
1344 long *externalLeading,
1345 const wxFont *theFont) const
1346 {
1347 wxCoord x2, y2, descent2, externalLeading2;
1348 m_pimpl->DoGetTextExtent(string, &x2, &y2,
1349 &descent2, &externalLeading2,
1350 theFont);
1351 if ( x )
1352 *x = x2;
1353 if ( y )
1354 *y = y2;
1355 if ( descent )
1356 *descent = descent2;
1357 if ( externalLeading )
1358 *externalLeading = externalLeading2;
1359 }
1360
1361 void wxDC::GetLogicalOrigin(long *x, long *y) const
1362 {
1363 wxCoord x2, y2;
1364 m_pimpl->DoGetLogicalOrigin(&x2, &y2);
1365 if ( x )
1366 *x = x2;
1367 if ( y )
1368 *y = y2;
1369 }
1370
1371 void wxDC::GetDeviceOrigin(long *x, long *y) const
1372 {
1373 wxCoord x2, y2;
1374 m_pimpl->DoGetDeviceOrigin(&x2, &y2);
1375 if ( x )
1376 *x = x2;
1377 if ( y )
1378 *y = y2;
1379 }
1380
1381 void wxDC::GetClippingBox(long *x, long *y, long *w, long *h) const
1382 {
1383 wxCoord xx,yy,ww,hh;
1384 m_pimpl->DoGetClippingBox(&xx, &yy, &ww, &hh);
1385 if (x) *x = xx;
1386 if (y) *y = yy;
1387 if (w) *w = ww;
1388 if (h) *h = hh;
1389 }
1390
1391 void wxDC::DrawObject(wxDrawObject* drawobject)
1392 {
1393 drawobject->Draw(*this);
1394 CalcBoundingBox(drawobject->MinX(),drawobject->MinY());
1395 CalcBoundingBox(drawobject->MaxX(),drawobject->MaxY());
1396 }
1397
1398 #endif // WXWIN_COMPATIBILITY_2_8
1399
1400 /*
1401 Notes for wxWidgets DrawEllipticArcRot(...)
1402
1403 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
1404 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
1405 which are also new.
1406
1407 All methods are generic, so they can be implemented in wxDCBase.
1408 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
1409 methods like (WinCE) wxDC::DoDrawArc(...).
1410
1411 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
1412 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
1413 parts) or every column (in steep parts) only one pixel is calculated.
1414 Trigonometric calculation (sin, cos, tan, atan) is only done if the
1415 starting angle is not equal to the ending angle. The calculation of the
1416 pixels is done using simple arithmetic only and should perform not too
1417 bad even on devices without floating point processor. I didn't test this yet.
1418
1419 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
1420 For instance: an ellipse rotated 180 degrees is drawn
1421 slightly different from the original.
1422
1423 The points are then moved to an array and used to draw a polyline and/or polygon
1424 (with center added, the pie).
1425 The result looks quite similar to the native ellipse, only e few pixels differ.
1426
1427 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
1428 slower as DrawEllipse(...), which calls the native API.
1429 An rotated ellipse outside the clipping region takes nearly the same time,
1430 while an native ellipse outside takes nearly no time to draw.
1431
1432 If you draw an arc with this new method, you will see the starting and ending angles
1433 are calculated properly.
1434 If you use DrawEllipticArc(...), you will see they are only correct for circles
1435 and not properly calculated for ellipses.
1436
1437 Peter Lenhard
1438 p.lenhard@t-online.de
1439 */
1440
1441 #ifdef __WXWINCE__
1442 void wxDCImpl::DoDrawEllipticArcRot( wxCoord x, wxCoord y,
1443 wxCoord w, wxCoord h,
1444 double sa, double ea, double angle )
1445 {
1446 wxPointList list;
1447
1448 CalculateEllipticPoints( &list, x, y, w, h, sa, ea );
1449 Rotate( &list, angle, wxPoint( x+w/2, y+h/2 ) );
1450
1451 // Add center (for polygon/pie)
1452 list.Append( new wxPoint( x+w/2, y+h/2 ) );
1453
1454 // copy list into array and delete list elements
1455 int n = list.GetCount();
1456 wxPoint *points = new wxPoint[n];
1457 int i = 0;
1458 wxPointList::compatibility_iterator node;
1459 for ( node = list.GetFirst(); node; node = node->GetNext(), i++ )
1460 {
1461 wxPoint *point = node->GetData();
1462 points[i].x = point->x;
1463 points[i].y = point->y;
1464 delete point;
1465 }
1466
1467 // first draw the pie without pen, if necessary
1468 if( GetBrush() != *wxTRANSPARENT_BRUSH )
1469 {
1470 wxPen tempPen( GetPen() );
1471 SetPen( *wxTRANSPARENT_PEN );
1472 DoDrawPolygon( n, points, 0, 0 );
1473 SetPen( tempPen );
1474 }
1475
1476 // then draw the arc without brush, if necessary
1477 if( GetPen() != *wxTRANSPARENT_PEN )
1478 {
1479 // without center
1480 DoDrawLines( n-1, points, 0, 0 );
1481 }
1482
1483 delete [] points;
1484
1485 } // DrawEllipticArcRot
1486
1487 void wxDCImpl::Rotate( wxPointList* points, double angle, wxPoint center )
1488 {
1489 if( angle != 0.0 )
1490 {
1491 double pi(M_PI);
1492 double dSinA = -sin(angle*2.0*pi/360.0);
1493 double dCosA = cos(angle*2.0*pi/360.0);
1494 wxPointList::compatibility_iterator node;
1495 for ( node = points->GetFirst(); node; node = node->GetNext() )
1496 {
1497 wxPoint* point = node->GetData();
1498
1499 // transform coordinates, if necessary
1500 if( center.x ) point->x -= center.x;
1501 if( center.y ) point->y -= center.y;
1502
1503 // calculate rotation, rounding simply by implicit cast to integer
1504 int xTemp = point->x * dCosA - point->y * dSinA;
1505 point->y = point->x * dSinA + point->y * dCosA;
1506 point->x = xTemp;
1507
1508 // back transform coordinates, if necessary
1509 if( center.x ) point->x += center.x;
1510 if( center.y ) point->y += center.y;
1511 }
1512 }
1513 }
1514
1515 void wxDCImpl::CalculateEllipticPoints( wxPointList* points,
1516 wxCoord xStart, wxCoord yStart,
1517 wxCoord w, wxCoord h,
1518 double sa, double ea )
1519 {
1520 double pi = M_PI;
1521 double sar = 0;
1522 double ear = 0;
1523 int xsa = 0;
1524 int ysa = 0;
1525 int xea = 0;
1526 int yea = 0;
1527 int sq = 0;
1528 int eq = 0;
1529 bool bUseAngles = false;
1530 if( w<0 ) w = -w;
1531 if( h<0 ) h = -h;
1532 // half-axes
1533 wxCoord a = w/2;
1534 wxCoord b = h/2;
1535 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
1536 int decrX = 0;
1537 if( 2*a == w ) decrX = 1;
1538 int decrY = 0;
1539 if( 2*b == h ) decrY = 1;
1540 // center
1541 wxCoord xCenter = xStart + a;
1542 wxCoord yCenter = yStart + b;
1543 // calculate data for start and end, if necessary
1544 if( sa != ea )
1545 {
1546 bUseAngles = true;
1547 // normalisation of angles
1548 while( sa<0 ) sa += 360;
1549 while( ea<0 ) ea += 360;
1550 while( sa>=360 ) sa -= 360;
1551 while( ea>=360 ) ea -= 360;
1552 // calculate quadrant numbers
1553 if( sa > 270 ) sq = 3;
1554 else if( sa > 180 ) sq = 2;
1555 else if( sa > 90 ) sq = 1;
1556 if( ea > 270 ) eq = 3;
1557 else if( ea > 180 ) eq = 2;
1558 else if( ea > 90 ) eq = 1;
1559 sar = sa * pi / 180.0;
1560 ear = ea * pi / 180.0;
1561 // correct angle circle -> ellipse
1562 sar = atan( -a/(double)b * tan( sar ) );
1563 if ( sq == 1 || sq == 2 ) sar += pi;
1564 ear = atan( -a/(double)b * tan( ear ) );
1565 if ( eq == 1 || eq == 2 ) ear += pi;
1566 // coordinates of points
1567 xsa = xCenter + a * cos( sar );
1568 if( sq == 0 || sq == 3 ) xsa -= decrX;
1569 ysa = yCenter + b * sin( sar );
1570 if( sq == 2 || sq == 3 ) ysa -= decrY;
1571 xea = xCenter + a * cos( ear );
1572 if( eq == 0 || eq == 3 ) xea -= decrX;
1573 yea = yCenter + b * sin( ear );
1574 if( eq == 2 || eq == 3 ) yea -= decrY;
1575 } // if iUseAngles
1576 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1577 double c1 = b * b;
1578 double c2 = 2.0 / w;
1579 c2 *= c2;
1580 c2 *= c1;
1581 wxCoord x = 0;
1582 wxCoord y = b;
1583 long x2 = 1;
1584 long y2 = y*y;
1585 long y2_old = 0;
1586 long y_old = 0;
1587 // Lists for quadrant 1 to 4
1588 wxPointList pointsarray[4];
1589 // Calculate points for first quadrant and set in all quadrants
1590 for( x = 0; x <= a; ++x )
1591 {
1592 x2 = x2+x+x-1;
1593 y2_old = y2;
1594 y_old = y;
1595 bool bNewPoint = false;
1596 while( y2 > c1 - c2 * x2 && y > 0 )
1597 {
1598 bNewPoint = true;
1599 y2 = y2-y-y+1;
1600 --y;
1601 }
1602 // old y now too big: set point with old y, old x
1603 if( bNewPoint && x>1)
1604 {
1605 int x1 = x - 1;
1606 // remove points on the same line
1607 pointsarray[0].Insert( new wxPoint( xCenter + x1 - decrX, yCenter - y_old ) );
1608 pointsarray[1].Append( new wxPoint( xCenter - x1, yCenter - y_old ) );
1609 pointsarray[2].Insert( new wxPoint( xCenter - x1, yCenter + y_old - decrY ) );
1610 pointsarray[3].Append( new wxPoint( xCenter + x1 - decrX, yCenter + y_old - decrY ) );
1611 } // set point
1612 } // calculate point
1613
1614 // Starting and/or ending points for the quadrants, first quadrant gets both.
1615 pointsarray[0].Insert( new wxPoint( xCenter + a - decrX, yCenter ) );
1616 pointsarray[0].Append( new wxPoint( xCenter, yCenter - b ) );
1617 pointsarray[1].Append( new wxPoint( xCenter - a, yCenter ) );
1618 pointsarray[2].Append( new wxPoint( xCenter, yCenter + b - decrY ) );
1619 pointsarray[3].Append( new wxPoint( xCenter + a - decrX, yCenter ) );
1620
1621 // copy quadrants in original list
1622 if( bUseAngles )
1623 {
1624 // Copy the right part of the points in the lists
1625 // and delete the wxPoints, because they do not leave this method.
1626 points->Append( new wxPoint( xsa, ysa ) );
1627 int q = sq;
1628 bool bStarted = false;
1629 bool bReady = false;
1630 bool bForceTurn = ( sq == eq && sa > ea );
1631 while( !bReady )
1632 {
1633 wxPointList::compatibility_iterator node;
1634 for( node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
1635 {
1636 // once: go to starting point in start quadrant
1637 if( !bStarted &&
1638 (
1639 node->GetData()->x < xsa+1 && q <= 1
1640 ||
1641 node->GetData()->x > xsa-1 && q >= 2
1642 )
1643 )
1644 {
1645 bStarted = true;
1646 }
1647
1648 // copy point, if not at ending point
1649 if( bStarted )
1650 {
1651 if( q != eq || bForceTurn
1652 ||
1653 ( (wxPoint*) node->GetData() )->x > xea+1 && q <= 1
1654 ||
1655 ( (wxPoint*) node->GetData() )->x < xea-1 && q >= 2
1656 )
1657 {
1658 // copy point
1659 wxPoint* pPoint = new wxPoint( *(node->GetData()) );
1660 points->Append( pPoint );
1661 }
1662 else if( q == eq && !bForceTurn || node->GetData()->x == xea)
1663 {
1664 bReady = true;
1665 }
1666 }
1667 } // for node
1668 ++q;
1669 if( q > 3 ) q = 0;
1670 bForceTurn = false;
1671 bStarted = true;
1672 } // while not bReady
1673 points->Append( new wxPoint( xea, yea ) );
1674
1675 // delete points
1676 for( q = 0; q < 4; ++q )
1677 {
1678 wxPointList::compatibility_iterator node;
1679 for( node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
1680 {
1681 wxPoint *p = node->GetData();
1682 delete p;
1683 }
1684 }
1685 }
1686 else
1687 {
1688 wxPointList::compatibility_iterator node;
1689 // copy whole ellipse, wxPoints will be deleted outside
1690 for( node = pointsarray[0].GetFirst(); node; node = node->GetNext() )
1691 {
1692 wxPoint *p = node->GetData();
1693 points->Append( p );
1694 }
1695 for( node = pointsarray[1].GetFirst(); node; node = node->GetNext() )
1696 {
1697 wxPoint *p = node->GetData();
1698 points->Append( p );
1699 }
1700 for( node = pointsarray[2].GetFirst(); node; node = node->GetNext() )
1701 {
1702 wxPoint *p = node->GetData();
1703 points->Append( p );
1704 }
1705 for( node = pointsarray[3].GetFirst(); node; node = node->GetNext() )
1706 {
1707 wxPoint *p = node->GetData();
1708 points->Append( p );
1709 }
1710 } // not iUseAngles
1711 } // CalculateEllipticPoints
1712
1713 #endif // __WXWINCE__
1714
1715 float wxDCImpl::GetFontPointSizeAdjustment(float dpi)
1716 {
1717 // wxMSW has long-standing bug where wxFont point size is interpreted as
1718 // "pixel size corresponding to given point size *on screen*". In other
1719 // words, on a typical 600dpi printer and a typical 96dpi screen, fonts
1720 // are ~6 times smaller when printing. Unfortunately, this bug is so severe
1721 // that *all* printing code has to account for it and consequently, other
1722 // ports need to emulate this bug too:
1723 const wxSize screenPPI = wxGetDisplayPPI();
1724 return float(screenPPI.y) / dpi;
1725 }