1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/dcbase.cpp
3 // Purpose: generic methods of the wxDC Class
4 // Author: Vadim Zeitlin
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.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"
36 #include "wx/module.h"
40 #include "wx/msw/dcclient.h"
41 #include "wx/msw/dcmemory.h"
42 #include "wx/msw/dcscreen.h"
46 #include "wx/gtk/dcclient.h"
47 #include "wx/gtk/dcmemory.h"
48 #include "wx/gtk/dcscreen.h"
49 #elif defined(__WXGTK__)
50 #include "wx/gtk1/dcclient.h"
51 #include "wx/gtk1/dcmemory.h"
52 #include "wx/gtk1/dcscreen.h"
56 #include "wx/mac/dcclient.h"
57 #include "wx/mac/dcmemory.h"
58 #include "wx/mac/dcscreen.h"
62 #include "wx/os2/dcclient.h"
63 #include "wx/os2/dcmemory.h"
64 #include "wx/os2/dcscreen.h"
68 #include "wx/cocoa/dcclient.h"
69 #include "wx/cocoa/dcmemory.h"
70 #include "wx/cocoa/dcscreen.h"
74 #include "wx/motif/dcclient.h"
75 #include "wx/motif/dcmemory.h"
76 #include "wx/motif/dcscreen.h"
80 #include "wx/x11/dcclient.h"
81 #include "wx/x11/dcmemory.h"
82 #include "wx/x11/dcscreen.h"
86 #include "wx/dfb/dcclient.h"
87 #include "wx/dfb/dcmemory.h"
88 #include "wx/dfb/dcscreen.h"
92 #include "wx/palmos/dcclient.h"
93 #include "wx/palmos/dcmemory.h"
94 #include "wx/palmos/dcscreen.h"
97 //----------------------------------------------------------------------------
99 //----------------------------------------------------------------------------
101 wxDCFactory
*wxDCFactory::m_factory
= NULL
;
103 void wxDCFactory::Set(wxDCFactory
*factory
)
110 wxDCFactory
*wxDCFactory::Get()
113 m_factory
= new wxNativeDCFactory
;
118 class wxDCFactoryCleanupModule
: public wxModule
121 virtual bool OnInit() { return true; }
122 virtual void OnExit() { wxDCFactory::Set(NULL
); }
125 DECLARE_DYNAMIC_CLASS(wxDCFactoryCleanupModule
)
128 IMPLEMENT_DYNAMIC_CLASS(wxDCFactoryCleanupModule
, wxModule
)
130 //-----------------------------------------------------------------------------
132 //-----------------------------------------------------------------------------
134 wxDCImpl
* wxNativeDCFactory::CreateWindowDC( wxWindowDC
*owner
)
136 return new wxWindowDCImpl( owner
);
139 wxDCImpl
* wxNativeDCFactory::CreateWindowDC( wxWindowDC
*owner
, wxWindow
*window
)
141 return new wxWindowDCImpl( owner
, window
);
144 wxDCImpl
* wxNativeDCFactory::CreateClientDC( wxClientDC
*owner
)
146 return new wxClientDCImpl( owner
);
149 wxDCImpl
* wxNativeDCFactory::CreateClientDC( wxClientDC
*owner
, wxWindow
*window
)
151 return new wxClientDCImpl( owner
, window
);
154 wxDCImpl
* wxNativeDCFactory::CreatePaintDC( wxPaintDC
*owner
)
156 return new wxPaintDCImpl( owner
);
159 wxDCImpl
* wxNativeDCFactory::CreatePaintDC( wxPaintDC
*owner
, wxWindow
*window
)
161 return new wxPaintDCImpl( owner
, window
);
164 wxDCImpl
* wxNativeDCFactory::CreateMemoryDC( wxMemoryDC
*owner
)
166 return new wxMemoryDCImpl( owner
);
169 wxDCImpl
* wxNativeDCFactory::CreateMemoryDC( wxMemoryDC
*owner
, wxBitmap
&bitmap
)
171 return new wxMemoryDCImpl( owner
, bitmap
);
174 wxDCImpl
* wxNativeDCFactory::CreateMemoryDC( wxMemoryDC
*owner
, wxDC
*dc
)
176 return new wxMemoryDCImpl( owner
, dc
);
179 wxDCImpl
* wxNativeDCFactory::CreateScreenDC( wxScreenDC
*owner
)
181 return new wxScreenDCImpl( owner
);
184 #if wxUSE_PRINTING_ARCHITECTURE
185 wxDCImpl
*wxNativeDCFactory::CreatePrinterDC( wxPrinterDC
*owner
, const wxPrintData
&data
)
187 wxPrintFactory
*factory
= wxPrintFactory::GetFactory();
188 return factory
->CreatePrinterDCImpl( owner
, data
);
192 //-----------------------------------------------------------------------------
194 //-----------------------------------------------------------------------------
196 IMPLEMENT_ABSTRACT_CLASS(wxWindowDC
, wxDC
)
198 wxWindowDC::wxWindowDC(wxWindow
*win
)
199 : wxDC(wxDCFactory::Get()->CreateWindowDC(this, win
))
203 //-----------------------------------------------------------------------------
205 //-----------------------------------------------------------------------------
207 IMPLEMENT_ABSTRACT_CLASS(wxClientDC
, wxWindowDC
)
209 wxClientDC::wxClientDC(wxWindow
*win
)
210 : wxWindowDC(wxDCFactory::Get()->CreateClientDC(this, win
))
214 //-----------------------------------------------------------------------------
216 //-----------------------------------------------------------------------------
218 IMPLEMENT_DYNAMIC_CLASS(wxMemoryDC
, wxDC
)
220 wxMemoryDC::wxMemoryDC()
221 : wxDC(wxDCFactory::Get()->CreateMemoryDC(this))
225 wxMemoryDC::wxMemoryDC(wxBitmap
& bitmap
)
226 : wxDC(wxDCFactory::Get()->CreateMemoryDC(this, bitmap
))
230 wxMemoryDC::wxMemoryDC(wxDC
*dc
)
231 : wxDC(wxDCFactory::Get()->CreateMemoryDC(this, dc
))
235 void wxMemoryDC::SelectObject(wxBitmap
& bmp
)
237 // make sure that the given wxBitmap is not sharing its data with other
238 // wxBitmap instances as its contents will be modified by any drawing
239 // operation done on this DC
243 GetImpl()->DoSelect(bmp
);
246 void wxMemoryDC::SelectObjectAsSource(const wxBitmap
& bmp
)
248 GetImpl()->DoSelect(bmp
);
251 const wxBitmap
& wxMemoryDC::GetSelectedBitmap() const
253 return GetImpl()->GetSelectedBitmap();
256 wxBitmap
& wxMemoryDC::GetSelectedBitmap()
258 return GetImpl()->GetSelectedBitmap();
262 //-----------------------------------------------------------------------------
264 //-----------------------------------------------------------------------------
266 IMPLEMENT_ABSTRACT_CLASS(wxPaintDC
, wxClientDC
)
268 wxPaintDC::wxPaintDC(wxWindow
*win
)
269 : wxClientDC(wxDCFactory::Get()->CreatePaintDC(this, win
))
273 //-----------------------------------------------------------------------------
275 //-----------------------------------------------------------------------------
277 IMPLEMENT_DYNAMIC_CLASS(wxScreenDC
, wxWindowDC
)
279 wxScreenDC::wxScreenDC()
280 : wxDC(wxDCFactory::Get()->CreateScreenDC(this))
284 //-----------------------------------------------------------------------------
286 //-----------------------------------------------------------------------------
288 #if wxUSE_PRINTING_ARCHITECTURE
290 IMPLEMENT_DYNAMIC_CLASS(wxPrinterDC
, wxDC
)
292 wxPrinterDC::wxPrinterDC()
293 : wxDC(wxDCFactory::Get()->CreatePrinterDC(this, wxPrintData()))
297 wxPrinterDC::wxPrinterDC(const wxPrintData
& data
)
298 : wxDC(wxDCFactory::Get()->CreatePrinterDC(this, data
))
302 wxRect
wxPrinterDC::GetPaperRect()
304 return GetImpl()->GetPaperRect();
307 int wxPrinterDC::GetResolution()
309 return GetImpl()->GetResolution();
312 #endif // wxUSE_PRINTING_ARCHITECTURE
314 //-----------------------------------------------------------------------------
316 //-----------------------------------------------------------------------------
318 IMPLEMENT_ABSTRACT_CLASS(wxDCImpl
, wxObject
)
320 wxDCImpl::wxDCImpl( wxDC
*owner
)
322 , m_colour(wxColourDisplay())
326 , m_isBBoxValid(false)
327 , m_logicalOriginX(0), m_logicalOriginY(0)
328 , m_deviceOriginX(0), m_deviceOriginY(0)
329 , m_deviceLocalOriginX(0), m_deviceLocalOriginY(0)
330 , m_logicalScaleX(1.0), m_logicalScaleY(1.0)
331 , m_userScaleX(1.0), m_userScaleY(1.0)
332 , m_scaleX(1.0), m_scaleY(1.0)
333 , m_signX(1), m_signY(1)
334 , m_minX(0), m_minY(0), m_maxX(0), m_maxY(0)
335 , m_clipX1(0), m_clipY1(0), m_clipX2(0), m_clipY2(0)
336 , m_logicalFunction(wxCOPY
)
337 , m_backgroundMode(wxTRANSPARENT
)
338 , m_mappingMode(wxMM_TEXT
)
341 , m_backgroundBrush()
342 , m_textForegroundColour(*wxBLACK
)
343 , m_textBackgroundColour(*wxWHITE
)
347 , m_hasCustomPalette(false)
348 #endif // wxUSE_PALETTE
352 m_mm_to_pix_x
= (double)wxGetDisplaySize().GetWidth() /
353 (double)wxGetDisplaySizeMM().GetWidth();
354 m_mm_to_pix_y
= (double)wxGetDisplaySize().GetHeight() /
355 (double)wxGetDisplaySizeMM().GetHeight();
361 wxDCImpl::~wxDCImpl()
365 // ----------------------------------------------------------------------------
366 // coordinate conversions and transforms
367 // ----------------------------------------------------------------------------
369 wxCoord
wxDCImpl::DeviceToLogicalX(wxCoord x
) const
371 return wxRound((double)(x
- m_deviceOriginX
- m_deviceLocalOriginX
) / m_scaleX
) * m_signX
+ m_logicalOriginX
;
374 wxCoord
wxDCImpl::DeviceToLogicalY(wxCoord y
) const
376 return wxRound((double)(y
- m_deviceOriginY
- m_deviceLocalOriginY
) / m_scaleY
) * m_signY
+ m_logicalOriginY
;
379 wxCoord
wxDCImpl::DeviceToLogicalXRel(wxCoord x
) const
381 return wxRound((double)(x
) / m_scaleX
);
384 wxCoord
wxDCImpl::DeviceToLogicalYRel(wxCoord y
) const
386 return wxRound((double)(y
) / m_scaleY
);
389 wxCoord
wxDCImpl::LogicalToDeviceX(wxCoord x
) const
391 return wxRound((double)(x
- m_logicalOriginX
) * m_scaleX
) * m_signX
+ m_deviceOriginX
* m_signY
+ m_deviceLocalOriginX
;
394 wxCoord
wxDCImpl::LogicalToDeviceY(wxCoord y
) const
396 return wxRound((double)(y
- m_logicalOriginY
) * m_scaleY
) * m_signY
+ m_deviceOriginY
* m_signY
+ m_deviceLocalOriginY
;
399 wxCoord
wxDCImpl::LogicalToDeviceXRel(wxCoord x
) const
401 return wxRound((double)(x
) * m_scaleX
);
404 wxCoord
wxDCImpl::LogicalToDeviceYRel(wxCoord y
) const
406 return wxRound((double)(y
) * m_scaleY
);
409 void wxDCImpl::ComputeScaleAndOrigin()
411 m_scaleX
= m_logicalScaleX
* m_userScaleX
;
412 m_scaleY
= m_logicalScaleY
* m_userScaleY
;
415 void wxDCImpl::SetMapMode( int mode
)
420 SetLogicalScale( twips2mm
*m_mm_to_pix_x
, twips2mm
*m_mm_to_pix_y
);
423 SetLogicalScale( pt2mm
*m_mm_to_pix_x
, pt2mm
*m_mm_to_pix_y
);
426 SetLogicalScale( m_mm_to_pix_x
, m_mm_to_pix_y
);
429 SetLogicalScale( m_mm_to_pix_x
/10.0, m_mm_to_pix_y
/10.0 );
433 SetLogicalScale( 1.0, 1.0 );
436 m_mappingMode
= mode
;
439 void wxDCImpl::SetUserScale( double x
, double y
)
441 // allow negative ? -> no
444 ComputeScaleAndOrigin();
447 void wxDCImpl::SetLogicalScale( double x
, double y
)
452 ComputeScaleAndOrigin();
455 void wxDCImpl::SetLogicalOrigin( wxCoord x
, wxCoord y
)
457 m_logicalOriginX
= x
* m_signX
;
458 m_logicalOriginY
= y
* m_signY
;
459 ComputeScaleAndOrigin();
462 void wxDCImpl::SetDeviceOrigin( wxCoord x
, wxCoord y
)
466 ComputeScaleAndOrigin();
469 void wxDCImpl::SetDeviceLocalOrigin( wxCoord x
, wxCoord y
)
471 m_deviceLocalOriginX
= x
;
472 m_deviceLocalOriginY
= y
;
473 ComputeScaleAndOrigin();
476 void wxDCImpl::SetAxisOrientation( bool xLeftRight
, bool yBottomUp
)
478 // only wxPostScripDC has m_signX = -1, we override SetAxisOrientation there
479 // wxWidgets 2.9: no longer override it
480 m_signX
= (xLeftRight
? 1 : -1);
481 m_signY
= (yBottomUp
? -1 : 1);
482 ComputeScaleAndOrigin();
486 // Each element of the widths array will be the width of the string up to and
487 // including the corresponding character in text. This is the generic
488 // implementation, the port-specific classes should do this with native APIs
489 // if available and if faster. Note: pango_layout_index_to_pos is much slower
490 // than calling GetTextExtent!!
497 FontWidthCache() : m_scaleX(1), m_widths(NULL
) { }
498 ~FontWidthCache() { delete []m_widths
; }
503 m_widths
= new int[FWC_SIZE
];
505 memset(m_widths
, 0, sizeof(int)*FWC_SIZE
);
513 static FontWidthCache s_fontWidthCache
;
515 bool wxDCImpl::DoGetPartialTextExtents(const wxString
& text
, wxArrayInt
& widths
) const
519 const size_t len
= text
.length();
523 // reset the cache if font or horizontal scale have changed
524 if ( !s_fontWidthCache
.m_widths
||
525 !wxIsSameDouble(s_fontWidthCache
.m_scaleX
, m_scaleX
) ||
526 (s_fontWidthCache
.m_font
!= GetFont()) )
528 s_fontWidthCache
.Reset();
529 s_fontWidthCache
.m_font
= GetFont();
530 s_fontWidthCache
.m_scaleX
= m_scaleX
;
533 // Calculate the position of each character based on the widths of
534 // the previous characters
536 for ( size_t i
= 0; i
< len
; i
++ )
538 const wxChar c
= text
[i
];
539 unsigned int c_int
= (unsigned int)c
;
541 if ((c_int
< FWC_SIZE
) && (s_fontWidthCache
.m_widths
[c_int
] != 0))
543 w
= s_fontWidthCache
.m_widths
[c_int
];
547 DoGetTextExtent(c
, &w
, &h
);
548 if (c_int
< FWC_SIZE
)
549 s_fontWidthCache
.m_widths
[c_int
] = w
;
553 widths
[i
] = totalWidth
;
559 void wxDCImpl::GetMultiLineTextExtent(const wxString
& text
,
563 const wxFont
*font
) const
565 wxCoord widthTextMax
= 0, widthLine
,
566 heightTextTotal
= 0, heightLineDefault
= 0, heightLine
= 0;
569 for ( wxString::const_iterator pc
= text
.begin(); ; ++pc
)
571 if ( pc
== text
.end() || *pc
== _T('\n') )
573 if ( curLine
.empty() )
575 // we can't use GetTextExtent - it will return 0 for both width
576 // and height and an empty line should count in height
579 // assume that this line has the same height as the previous
581 if ( !heightLineDefault
)
582 heightLineDefault
= heightLine
;
584 if ( !heightLineDefault
)
586 // but we don't know it yet - choose something reasonable
587 DoGetTextExtent(_T("W"), NULL
, &heightLineDefault
,
591 heightTextTotal
+= heightLineDefault
;
595 DoGetTextExtent(curLine
, &widthLine
, &heightLine
,
597 if ( widthLine
> widthTextMax
)
598 widthTextMax
= widthLine
;
599 heightTextTotal
+= heightLine
;
602 if ( pc
== text
.end() )
620 *y
= heightTextTotal
;
625 void wxDCImpl::DoDrawCheckMark(wxCoord x1
, wxCoord y1
,
626 wxCoord width
, wxCoord height
)
628 wxCHECK_RET( IsOk(), wxT("invalid window dc") );
630 wxCoord x2
= x1
+ width
,
633 // the pen width is calibrated to give 3 for width == height == 10
634 wxDCPenChanger
pen( *m_owner
, wxPen(GetTextForeground(), (width
+ height
+ 1)/7));
636 // we're drawing a scaled version of wx/generic/tick.xpm here
637 wxCoord x3
= x1
+ (4*width
) / 10, // x of the tick bottom
638 y3
= y1
+ height
/ 2; // y of the left tick branch
639 DoDrawLine(x1
, y3
, x3
, y2
);
640 DoDrawLine(x3
, y2
, x2
, y1
);
642 CalcBoundingBox(x1
, y1
);
643 CalcBoundingBox(x2
, y2
);
647 wxDCImpl::DoStretchBlit(wxCoord xdest
, wxCoord ydest
,
648 wxCoord dstWidth
, wxCoord dstHeight
,
650 wxCoord xsrc
, wxCoord ysrc
,
651 wxCoord srcWidth
, wxCoord srcHeight
,
657 wxCHECK_MSG( srcWidth
&& srcHeight
&& dstWidth
&& dstHeight
, false,
658 _T("invalid blit size") );
660 // emulate the stretching by modifying the DC scale
661 double xscale
= (double)srcWidth
/dstWidth
,
662 yscale
= (double)srcHeight
/dstHeight
;
664 double xscaleOld
, yscaleOld
;
665 GetUserScale(&xscaleOld
, &yscaleOld
);
666 SetUserScale(xscaleOld
/xscale
, yscaleOld
/yscale
);
668 bool rc
= DoBlit(wxCoord(xdest
*xscale
), wxCoord(ydest
*yscale
),
669 wxCoord(dstWidth
*xscale
), wxCoord(dstHeight
*yscale
),
671 xsrc
, ysrc
, rop
, useMask
, xsrcMask
, ysrcMask
);
673 SetUserScale(xscaleOld
, yscaleOld
);
678 void wxDCImpl::DrawLines(const wxPointList
*list
, wxCoord xoffset
, wxCoord yoffset
)
680 int n
= list
->GetCount();
681 wxPoint
*points
= new wxPoint
[n
];
684 for ( wxPointList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
686 wxPoint
*point
= node
->GetData();
687 points
[i
].x
= point
->x
;
688 points
[i
].y
= point
->y
;
691 DoDrawLines(n
, points
, xoffset
, yoffset
);
696 void wxDCImpl::DrawPolygon(const wxPointList
*list
,
697 wxCoord xoffset
, wxCoord yoffset
,
700 int n
= list
->GetCount();
701 wxPoint
*points
= new wxPoint
[n
];
704 for ( wxPointList::compatibility_iterator node
= list
->GetFirst(); node
; node
= node
->GetNext(), i
++ )
706 wxPoint
*point
= node
->GetData();
707 points
[i
].x
= point
->x
;
708 points
[i
].y
= point
->y
;
711 DoDrawPolygon(n
, points
, xoffset
, yoffset
, fillStyle
);
717 wxDCImpl::DoDrawPolyPolygon(int n
,
720 wxCoord xoffset
, wxCoord yoffset
,
725 DoDrawPolygon(count
[0], points
, xoffset
, yoffset
, fillStyle
);
733 for (i
= j
= lastOfs
= 0; i
< n
; i
++)
738 pts
= new wxPoint
[j
+n
-1];
739 for (i
= 0; i
< j
; i
++)
741 for (i
= 2; i
<= n
; i
++)
743 lastOfs
-= count
[n
-i
];
744 pts
[j
++] = pts
[lastOfs
];
748 SetPen(wxPen(*wxBLACK
, 0, wxTRANSPARENT
));
749 DoDrawPolygon(j
, pts
, xoffset
, yoffset
, fillStyle
);
751 for (i
= j
= 0; i
< n
; i
++)
753 DoDrawLines(count
[i
], pts
+j
, xoffset
, yoffset
);
761 void wxDCImpl::DoDrawSpline(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
, wxCoord x3
, wxCoord y3
)
763 wxPointList point_list
;
765 wxPoint
*point1
= new wxPoint
;
766 point1
->x
= x1
; point1
->y
= y1
;
767 point_list
.Append( point1
);
769 wxPoint
*point2
= new wxPoint
;
770 point2
->x
= x2
; point2
->y
= y2
;
771 point_list
.Append( point2
);
773 wxPoint
*point3
= new wxPoint
;
774 point3
->x
= x3
; point3
->y
= y3
;
775 point_list
.Append( point3
);
777 DoDrawSpline(&point_list
);
779 for( wxPointList::compatibility_iterator node
= point_list
.GetFirst(); node
; node
= node
->GetNext() )
781 wxPoint
*p
= node
->GetData();
786 void wxDCImpl::DoDrawSpline(int n
, wxPoint points
[])
789 for (int i
=0; i
< n
; i
++)
790 list
.Append( &points
[i
] );
795 // ----------------------------------- spline code ----------------------------------------
797 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
,
798 double a3
, double b3
, double a4
, double b4
);
799 void wx_clear_stack();
800 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
, double *x3
,
801 double *y3
, double *x4
, double *y4
);
802 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
,
803 double x4
, double y4
);
804 static bool wx_spline_add_point(double x
, double y
);
805 static void wx_spline_draw_point_array(wxDC
*dc
);
807 wxPointList wx_spline_point_list
;
809 #define half(z1, z2) ((z1+z2)/2.0)
812 /* iterative version */
814 void wx_quadratic_spline(double a1
, double b1
, double a2
, double b2
, double a3
, double b3
, double a4
,
817 register double xmid
, ymid
;
818 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
821 wx_spline_push(a1
, b1
, a2
, b2
, a3
, b3
, a4
, b4
);
823 while (wx_spline_pop(&x1
, &y1
, &x2
, &y2
, &x3
, &y3
, &x4
, &y4
)) {
824 xmid
= (double)half(x2
, x3
);
825 ymid
= (double)half(y2
, y3
);
826 if (fabs(x1
- xmid
) < THRESHOLD
&& fabs(y1
- ymid
) < THRESHOLD
&&
827 fabs(xmid
- x4
) < THRESHOLD
&& fabs(ymid
- y4
) < THRESHOLD
) {
828 wx_spline_add_point( x1
, y1
);
829 wx_spline_add_point( xmid
, ymid
);
831 wx_spline_push(xmid
, ymid
, (double)half(xmid
, x3
), (double)half(ymid
, y3
),
832 (double)half(x3
, x4
), (double)half(y3
, y4
), x4
, y4
);
833 wx_spline_push(x1
, y1
, (double)half(x1
, x2
), (double)half(y1
, y2
),
834 (double)half(x2
, xmid
), (double)half(y2
, ymid
), xmid
, ymid
);
839 /* utilities used by spline drawing routines */
841 typedef struct wx_spline_stack_struct
{
842 double x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
845 #define SPLINE_STACK_DEPTH 20
846 static Stack wx_spline_stack
[SPLINE_STACK_DEPTH
];
847 static Stack
*wx_stack_top
;
848 static int wx_stack_count
;
850 void wx_clear_stack()
852 wx_stack_top
= wx_spline_stack
;
856 void wx_spline_push(double x1
, double y1
, double x2
, double y2
, double x3
, double y3
, double x4
, double y4
)
858 wx_stack_top
->x1
= x1
;
859 wx_stack_top
->y1
= y1
;
860 wx_stack_top
->x2
= x2
;
861 wx_stack_top
->y2
= y2
;
862 wx_stack_top
->x3
= x3
;
863 wx_stack_top
->y3
= y3
;
864 wx_stack_top
->x4
= x4
;
865 wx_stack_top
->y4
= y4
;
870 int wx_spline_pop(double *x1
, double *y1
, double *x2
, double *y2
,
871 double *x3
, double *y3
, double *x4
, double *y4
)
873 if (wx_stack_count
== 0)
877 *x1
= wx_stack_top
->x1
;
878 *y1
= wx_stack_top
->y1
;
879 *x2
= wx_stack_top
->x2
;
880 *y2
= wx_stack_top
->y2
;
881 *x3
= wx_stack_top
->x3
;
882 *y3
= wx_stack_top
->y3
;
883 *x4
= wx_stack_top
->x4
;
884 *y4
= wx_stack_top
->y4
;
888 static bool wx_spline_add_point(double x
, double y
)
890 wxPoint
*point
= new wxPoint( wxRound(x
), wxRound(y
) );
891 wx_spline_point_list
.Append(point
);
895 static void wx_spline_draw_point_array(wxDC
*dc
)
897 dc
->DrawLines(&wx_spline_point_list
, 0, 0 );
898 wxPointList::compatibility_iterator node
= wx_spline_point_list
.GetFirst();
901 wxPoint
*point
= node
->GetData();
903 wx_spline_point_list
.Erase(node
);
904 node
= wx_spline_point_list
.GetFirst();
908 void wxDCImpl::DoDrawSpline( const wxPointList
*points
)
910 wxCHECK_RET( IsOk(), wxT("invalid window dc") );
913 double cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
;
914 double x1
, y1
, x2
, y2
;
916 wxPointList::compatibility_iterator node
= points
->GetFirst();
921 p
= (wxPoint
*)node
->GetData();
926 node
= node
->GetNext();
931 cx1
= (double)((x1
+ x2
) / 2);
932 cy1
= (double)((y1
+ y2
) / 2);
933 cx2
= (double)((cx1
+ x2
) / 2);
934 cy2
= (double)((cy1
+ y2
) / 2);
936 wx_spline_add_point(x1
, y1
);
938 while ((node
= node
->GetNext())
949 cx4
= (double)(x1
+ x2
) / 2;
950 cy4
= (double)(y1
+ y2
) / 2;
951 cx3
= (double)(x1
+ cx4
) / 2;
952 cy3
= (double)(y1
+ cy4
) / 2;
954 wx_quadratic_spline(cx1
, cy1
, cx2
, cy2
, cx3
, cy3
, cx4
, cy4
);
958 cx2
= (double)(cx1
+ x2
) / 2;
959 cy2
= (double)(cy1
+ y2
) / 2;
962 wx_spline_add_point( cx1
, cy1
);
963 wx_spline_add_point( x2
, y2
);
965 wx_spline_draw_point_array( m_owner
);
968 #endif // wxUSE_SPLINES
972 void wxDCImpl::DoGradientFillLinear(const wxRect
& rect
,
973 const wxColour
& initialColour
,
974 const wxColour
& destColour
,
975 wxDirection nDirection
)
978 wxPen oldPen
= m_pen
;
979 wxBrush oldBrush
= m_brush
;
981 wxUint8 nR1
= initialColour
.Red();
982 wxUint8 nG1
= initialColour
.Green();
983 wxUint8 nB1
= initialColour
.Blue();
984 wxUint8 nR2
= destColour
.Red();
985 wxUint8 nG2
= destColour
.Green();
986 wxUint8 nB2
= destColour
.Blue();
989 if ( nDirection
== wxEAST
|| nDirection
== wxWEST
)
991 wxInt32 x
= rect
.GetWidth();
992 wxInt32 w
= x
; // width of area to shade
993 wxInt32 xDelta
= w
/256; // height of one shade bend
1001 nR
= nR1
- (nR1
-nR2
)*(w
-x
)/w
;
1003 nR
= nR1
+ (nR2
-nR1
)*(w
-x
)/w
;
1006 nG
= nG1
- (nG1
-nG2
)*(w
-x
)/w
;
1008 nG
= nG1
+ (nG2
-nG1
)*(w
-x
)/w
;
1011 nB
= nB1
- (nB1
-nB2
)*(w
-x
)/w
;
1013 nB
= nB1
+ (nB2
-nB1
)*(w
-x
)/w
;
1015 wxColour
colour(nR
,nG
,nB
);
1016 SetPen(wxPen(colour
, 1, wxSOLID
));
1017 SetBrush(wxBrush(colour
));
1018 if(nDirection
== wxEAST
)
1019 DoDrawRectangle(rect
.GetRight()-x
-xDelta
+1, rect
.GetTop(),
1020 xDelta
, rect
.GetHeight());
1021 else //nDirection == wxWEST
1022 DoDrawRectangle(rect
.GetLeft()+x
, rect
.GetTop(),
1023 xDelta
, rect
.GetHeight());
1026 else // nDirection == wxNORTH || nDirection == wxSOUTH
1028 wxInt32 y
= rect
.GetHeight();
1029 wxInt32 w
= y
; // height of area to shade
1030 wxInt32 yDelta
= w
/255; // height of one shade bend
1038 nR
= nR1
- (nR1
-nR2
)*(w
-y
)/w
;
1040 nR
= nR1
+ (nR2
-nR1
)*(w
-y
)/w
;
1043 nG
= nG1
- (nG1
-nG2
)*(w
-y
)/w
;
1045 nG
= nG1
+ (nG2
-nG1
)*(w
-y
)/w
;
1048 nB
= nB1
- (nB1
-nB2
)*(w
-y
)/w
;
1050 nB
= nB1
+ (nB2
-nB1
)*(w
-y
)/w
;
1052 wxColour
colour(nR
,nG
,nB
);
1053 SetPen(wxPen(colour
, 1, wxSOLID
));
1054 SetBrush(wxBrush(colour
));
1055 if(nDirection
== wxNORTH
)
1056 DoDrawRectangle(rect
.GetLeft(), rect
.GetTop()+y
,
1057 rect
.GetWidth(), yDelta
);
1058 else //nDirection == wxSOUTH
1059 DoDrawRectangle(rect
.GetLeft(), rect
.GetBottom()-y
-yDelta
+1,
1060 rect
.GetWidth(), yDelta
);
1068 void wxDCImpl::DoGradientFillConcentric(const wxRect
& rect
,
1069 const wxColour
& initialColour
,
1070 const wxColour
& destColour
,
1071 const wxPoint
& circleCenter
)
1073 //save the old pen color
1074 wxColour oldPenColour
= m_pen
.GetColour();
1076 wxUint8 nR1
= destColour
.Red();
1077 wxUint8 nG1
= destColour
.Green();
1078 wxUint8 nB1
= destColour
.Blue();
1079 wxUint8 nR2
= initialColour
.Red();
1080 wxUint8 nG2
= initialColour
.Green();
1081 wxUint8 nB2
= initialColour
.Blue();
1086 wxInt32 cx
= rect
.GetWidth() / 2;
1087 wxInt32 cy
= rect
.GetHeight() / 2;
1095 wxInt32 nCircleOffX
= circleCenter
.x
- (rect
.GetWidth() / 2);
1096 wxInt32 nCircleOffY
= circleCenter
.y
- (rect
.GetHeight() / 2);
1098 for ( wxInt32 x
= 0; x
< rect
.GetWidth(); x
++ )
1100 for ( wxInt32 y
= 0; y
< rect
.GetHeight(); y
++ )
1102 //get color difference
1103 wxInt32 nGradient
= ((nRadius
-
1105 pow((double)(x
- cx
- nCircleOffX
), 2) +
1106 pow((double)(y
- cy
- nCircleOffY
), 2)
1107 )) * 100) / nRadius
;
1109 //normalize Gradient
1114 nR
= (wxUint8
)(nR1
+ ((nR2
- nR1
) * nGradient
/ 100));
1115 nG
= (wxUint8
)(nG1
+ ((nG2
- nG1
) * nGradient
/ 100));
1116 nB
= (wxUint8
)(nB1
+ ((nB2
- nB1
) * nGradient
/ 100));
1119 m_pen
.SetColour(wxColour(nR
,nG
,nB
));
1120 DoDrawPoint(x
+ rect
.GetLeft(), y
+ rect
.GetTop());
1123 //return old pen color
1124 m_pen
.SetColour(oldPenColour
);
1127 //-----------------------------------------------------------------------------
1129 //-----------------------------------------------------------------------------
1131 IMPLEMENT_ABSTRACT_CLASS(wxDC
, wxObject
)
1133 void wxDC::DrawLabel(const wxString
& text
,
1134 const wxBitmap
& bitmap
,
1138 wxRect
*rectBounding
)
1140 // find the text position
1141 wxCoord widthText
, heightText
, heightLine
;
1142 GetMultiLineTextExtent(text
, &widthText
, &heightText
, &heightLine
);
1144 wxCoord width
, height
;
1147 width
= widthText
+ bitmap
.GetWidth();
1148 height
= bitmap
.GetHeight();
1153 height
= heightText
;
1157 if ( alignment
& wxALIGN_RIGHT
)
1159 x
= rect
.GetRight() - width
;
1161 else if ( alignment
& wxALIGN_CENTRE_HORIZONTAL
)
1163 x
= (rect
.GetLeft() + rect
.GetRight() + 1 - width
) / 2;
1165 else // alignment & wxALIGN_LEFT
1170 if ( alignment
& wxALIGN_BOTTOM
)
1172 y
= rect
.GetBottom() - height
;
1174 else if ( alignment
& wxALIGN_CENTRE_VERTICAL
)
1176 y
= (rect
.GetTop() + rect
.GetBottom() + 1 - height
) / 2;
1178 else // alignment & wxALIGN_TOP
1183 // draw the bitmap first
1189 DrawBitmap(bitmap
, x
, y
, true /* use mask */);
1191 wxCoord offset
= bitmap
.GetWidth() + 4;
1195 y
+= (height
- heightText
) / 2;
1198 // we will draw the underscore under the accel char later
1199 wxCoord startUnderscore
= 0,
1203 // split the string into lines and draw each of them separately
1205 for ( wxString::const_iterator pc
= text
.begin(); ; ++pc
)
1207 if ( *pc
== _T('\n') || pc
== text
.end() )
1209 int xRealStart
= x
; // init it here to avoid compielr warnings
1211 if ( !curLine
.empty() )
1213 // NB: can't test for !(alignment & wxALIGN_LEFT) because
1214 // wxALIGN_LEFT is 0
1215 if ( alignment
& (wxALIGN_RIGHT
| wxALIGN_CENTRE_HORIZONTAL
) )
1218 GetTextExtent(curLine
, &widthLine
, NULL
);
1220 if ( alignment
& wxALIGN_RIGHT
)
1222 xRealStart
+= width
- widthLine
;
1224 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
1226 xRealStart
+= (width
- widthLine
) / 2;
1229 //else: left aligned, nothing to do
1231 DrawText(curLine
, xRealStart
, y
);
1236 // do we have underscore in this line? we can check yUnderscore
1237 // because it is set below to just y + heightLine if we do
1238 if ( y
== yUnderscore
)
1240 // adjust the horz positions to account for the shift
1241 startUnderscore
+= xRealStart
;
1242 endUnderscore
+= xRealStart
;
1245 if ( pc
== text
.end() )
1250 else // not end of line
1252 if ( pc
- text
.begin() == indexAccel
)
1254 // remeber to draw underscore here
1255 GetTextExtent(curLine
, &startUnderscore
, NULL
);
1257 GetTextExtent(curLine
, &endUnderscore
, NULL
);
1259 yUnderscore
= y
+ heightLine
;
1268 // draw the underscore if found
1269 if ( startUnderscore
!= endUnderscore
)
1271 // it should be of the same colour as text
1272 SetPen(wxPen(GetTextForeground(), 0, wxSOLID
));
1276 DrawLine(startUnderscore
, yUnderscore
, endUnderscore
, yUnderscore
);
1279 // return bounding rect if requested
1282 *rectBounding
= wxRect(x
, y
- heightText
, widthText
, heightText
);
1285 CalcBoundingBox(x0
, y0
);
1286 CalcBoundingBox(x0
+ width0
, y0
+ height
);
1289 #if WXWIN_COMPATIBILITY_2_8
1290 // for compatibility with the old code when wxCoord was long everywhere
1291 void wxDC::GetTextExtent(const wxString
& string
,
1294 long *externalLeading
,
1295 const wxFont
*theFont
) const
1297 wxCoord x2
, y2
, descent2
, externalLeading2
;
1298 m_pimpl
->DoGetTextExtent(string
, &x2
, &y2
,
1299 &descent2
, &externalLeading2
,
1306 *descent
= descent2
;
1307 if ( externalLeading
)
1308 *externalLeading
= externalLeading2
;
1311 void wxDC::GetLogicalOrigin(long *x
, long *y
) const
1314 m_pimpl
->DoGetLogicalOrigin(&x2
, &y2
);
1321 void wxDC::GetDeviceOrigin(long *x
, long *y
) const
1324 m_pimpl
->DoGetDeviceOrigin(&x2
, &y2
);
1331 void wxDC::GetClippingBox(long *x
, long *y
, long *w
, long *h
) const
1333 wxCoord xx
,yy
,ww
,hh
;
1334 m_pimpl
->DoGetClippingBox(&xx
, &yy
, &ww
, &hh
);
1341 #endif // WXWIN_COMPATIBILITY_2_8
1344 Notes for wxWidgets DrawEllipticArcRot(...)
1346 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
1347 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
1350 All methods are generic, so they can be implemented in wxDCBase.
1351 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
1352 methods like (WinCE) wxDC::DoDrawArc(...).
1354 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
1355 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
1356 parts) or every column (in steep parts) only one pixel is calculated.
1357 Trigonometric calculation (sin, cos, tan, atan) is only done if the
1358 starting angle is not equal to the ending angle. The calculation of the
1359 pixels is done using simple arithmetic only and should perform not too
1360 bad even on devices without floating point processor. I didn't test this yet.
1362 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
1363 For instance: an ellipse rotated 180 degrees is drawn
1364 slightly different from the original.
1366 The points are then moved to an array and used to draw a polyline and/or polygon
1367 (with center added, the pie).
1368 The result looks quite similar to the native ellipse, only e few pixels differ.
1370 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
1371 slower as DrawEllipse(...), which calls the native API.
1372 An rotated ellipse outside the clipping region takes nearly the same time,
1373 while an native ellipse outside takes nearly no time to draw.
1375 If you draw an arc with this new method, you will see the starting and ending angles
1376 are calculated properly.
1377 If you use DrawEllipticArc(...), you will see they are only correct for circles
1378 and not properly calculated for ellipses.
1381 p.lenhard@t-online.de
1385 void wxDCImpl::DoDrawEllipticArcRot( wxCoord x
, wxCoord y
,
1386 wxCoord w
, wxCoord h
,
1387 double sa
, double ea
, double angle
)
1391 CalculateEllipticPoints( &list
, x
, y
, w
, h
, sa
, ea
);
1392 Rotate( &list
, angle
, wxPoint( x
+w
/2, y
+h
/2 ) );
1394 // Add center (for polygon/pie)
1395 list
.Append( new wxPoint( x
+w
/2, y
+h
/2 ) );
1397 // copy list into array and delete list elements
1398 int n
= list
.GetCount();
1399 wxPoint
*points
= new wxPoint
[n
];
1401 wxPointList::compatibility_iterator node
;
1402 for ( node
= list
.GetFirst(); node
; node
= node
->GetNext(), i
++ )
1404 wxPoint
*point
= node
->GetData();
1405 points
[i
].x
= point
->x
;
1406 points
[i
].y
= point
->y
;
1410 // first draw the pie without pen, if necessary
1411 if( GetBrush() != *wxTRANSPARENT_BRUSH
)
1413 wxPen
tempPen( GetPen() );
1414 SetPen( *wxTRANSPARENT_PEN
);
1415 DoDrawPolygon( n
, points
, 0, 0 );
1419 // then draw the arc without brush, if necessary
1420 if( GetPen() != *wxTRANSPARENT_PEN
)
1423 DoDrawLines( n
-1, points
, 0, 0 );
1428 } // DrawEllipticArcRot
1430 void wxDCImpl::Rotate( wxPointList
* points
, double angle
, wxPoint center
)
1435 double dSinA
= -sin(angle
*2.0*pi
/360.0);
1436 double dCosA
= cos(angle
*2.0*pi
/360.0);
1437 wxPointList::compatibility_iterator node
;
1438 for ( node
= points
->GetFirst(); node
; node
= node
->GetNext() )
1440 wxPoint
* point
= node
->GetData();
1442 // transform coordinates, if necessary
1443 if( center
.x
) point
->x
-= center
.x
;
1444 if( center
.y
) point
->y
-= center
.y
;
1446 // calculate rotation, rounding simply by implicit cast to integer
1447 int xTemp
= point
->x
* dCosA
- point
->y
* dSinA
;
1448 point
->y
= point
->x
* dSinA
+ point
->y
* dCosA
;
1451 // back transform coordinates, if necessary
1452 if( center
.x
) point
->x
+= center
.x
;
1453 if( center
.y
) point
->y
+= center
.y
;
1458 void wxDCImpl::CalculateEllipticPoints( wxPointList
* points
,
1459 wxCoord xStart
, wxCoord yStart
,
1460 wxCoord w
, wxCoord h
,
1461 double sa
, double ea
)
1472 bool bUseAngles
= false;
1478 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
1480 if( 2*a
== w
) decrX
= 1;
1482 if( 2*b
== h
) decrY
= 1;
1484 wxCoord xCenter
= xStart
+ a
;
1485 wxCoord yCenter
= yStart
+ b
;
1486 // calculate data for start and end, if necessary
1490 // normalisation of angles
1491 while( sa
<0 ) sa
+= 360;
1492 while( ea
<0 ) ea
+= 360;
1493 while( sa
>=360 ) sa
-= 360;
1494 while( ea
>=360 ) ea
-= 360;
1495 // calculate quadrant numbers
1496 if( sa
> 270 ) sq
= 3;
1497 else if( sa
> 180 ) sq
= 2;
1498 else if( sa
> 90 ) sq
= 1;
1499 if( ea
> 270 ) eq
= 3;
1500 else if( ea
> 180 ) eq
= 2;
1501 else if( ea
> 90 ) eq
= 1;
1502 sar
= sa
* pi
/ 180.0;
1503 ear
= ea
* pi
/ 180.0;
1504 // correct angle circle -> ellipse
1505 sar
= atan( -a
/(double)b
* tan( sar
) );
1506 if ( sq
== 1 || sq
== 2 ) sar
+= pi
;
1507 ear
= atan( -a
/(double)b
* tan( ear
) );
1508 if ( eq
== 1 || eq
== 2 ) ear
+= pi
;
1509 // coordinates of points
1510 xsa
= xCenter
+ a
* cos( sar
);
1511 if( sq
== 0 || sq
== 3 ) xsa
-= decrX
;
1512 ysa
= yCenter
+ b
* sin( sar
);
1513 if( sq
== 2 || sq
== 3 ) ysa
-= decrY
;
1514 xea
= xCenter
+ a
* cos( ear
);
1515 if( eq
== 0 || eq
== 3 ) xea
-= decrX
;
1516 yea
= yCenter
+ b
* sin( ear
);
1517 if( eq
== 2 || eq
== 3 ) yea
-= decrY
;
1519 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1521 double c2
= 2.0 / w
;
1530 // Lists for quadrant 1 to 4
1531 wxPointList pointsarray
[4];
1532 // Calculate points for first quadrant and set in all quadrants
1533 for( x
= 0; x
<= a
; ++x
)
1538 bool bNewPoint
= false;
1539 while( y2
> c1
- c2
* x2
&& y
> 0 )
1545 // old y now to big: set point with old y, old x
1546 if( bNewPoint
&& x
>1)
1549 // remove points on the same line
1550 pointsarray
[0].Insert( new wxPoint( xCenter
+ x1
- decrX
, yCenter
- y_old
) );
1551 pointsarray
[1].Append( new wxPoint( xCenter
- x1
, yCenter
- y_old
) );
1552 pointsarray
[2].Insert( new wxPoint( xCenter
- x1
, yCenter
+ y_old
- decrY
) );
1553 pointsarray
[3].Append( new wxPoint( xCenter
+ x1
- decrX
, yCenter
+ y_old
- decrY
) );
1555 } // calculate point
1557 // Starting and/or ending points for the quadrants, first quadrant gets both.
1558 pointsarray
[0].Insert( new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1559 pointsarray
[0].Append( new wxPoint( xCenter
, yCenter
- b
) );
1560 pointsarray
[1].Append( new wxPoint( xCenter
- a
, yCenter
) );
1561 pointsarray
[2].Append( new wxPoint( xCenter
, yCenter
+ b
- decrY
) );
1562 pointsarray
[3].Append( new wxPoint( xCenter
+ a
- decrX
, yCenter
) );
1564 // copy quadrants in original list
1567 // Copy the right part of the points in the lists
1568 // and delete the wxPoints, because they do not leave this method.
1569 points
->Append( new wxPoint( xsa
, ysa
) );
1571 bool bStarted
= false;
1572 bool bReady
= false;
1573 bool bForceTurn
= ( sq
== eq
&& sa
> ea
);
1576 wxPointList::compatibility_iterator node
;
1577 for( node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1579 // once: go to starting point in start quadrant
1582 node
->GetData()->x
< xsa
+1 && q
<= 1
1584 node
->GetData()->x
> xsa
-1 && q
>= 2
1591 // copy point, if not at ending point
1594 if( q
!= eq
|| bForceTurn
1596 ( (wxPoint
*) node
->GetData() )->x
> xea
+1 && q
<= 1
1598 ( (wxPoint
*) node
->GetData() )->x
< xea
-1 && q
>= 2
1602 wxPoint
* pPoint
= new wxPoint( *(node
->GetData()) );
1603 points
->Append( pPoint
);
1605 else if( q
== eq
&& !bForceTurn
|| node
->GetData()->x
== xea
)
1615 } // while not bReady
1616 points
->Append( new wxPoint( xea
, yea
) );
1619 for( q
= 0; q
< 4; ++q
)
1621 wxPointList::compatibility_iterator node
;
1622 for( node
= pointsarray
[q
].GetFirst(); node
; node
= node
->GetNext() )
1624 wxPoint
*p
= node
->GetData();
1631 wxPointList::compatibility_iterator node
;
1632 // copy whole ellipse, wxPoints will be deleted outside
1633 for( node
= pointsarray
[0].GetFirst(); node
; node
= node
->GetNext() )
1635 wxPoint
*p
= node
->GetData();
1636 points
->Append( p
);
1638 for( node
= pointsarray
[1].GetFirst(); node
; node
= node
->GetNext() )
1640 wxPoint
*p
= node
->GetData();
1641 points
->Append( p
);
1643 for( node
= pointsarray
[2].GetFirst(); node
; node
= node
->GetNext() )
1645 wxPoint
*p
= node
->GetData();
1646 points
->Append( p
);
1648 for( node
= pointsarray
[3].GetFirst(); node
; node
= node
->GetNext() )
1650 wxPoint
*p
= node
->GetData();
1651 points
->Append( p
);
1654 } // CalculateEllipticPoints
1656 #endif // __WXWINCE__