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