1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/carbon/dccg.cpp
4 // Author: Stefan Csomor
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
14 #if wxUSE_GRAPHICS_CONTEXT && wxMAC_USE_CORE_GRAPHICS
16 #include "wx/graphics.h"
19 #include "wx/dcclient.h"
21 #include "wx/region.h"
24 #include "wx/mac/uma.h"
29 // in case our functions were defined outside std, we make it known all the same
35 #include "wx/mac/private.h"
37 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
38 typedef float CGFloat
;
41 //-----------------------------------------------------------------------------
43 //-----------------------------------------------------------------------------
45 #if !defined( __DARWIN__ ) || defined(__MWERKS__)
47 const double M_PI
= 3.14159265358979;
51 static const double RAD2DEG
= 180.0 / M_PI
;
57 class WXDLLEXPORT wxMacCoreGraphicsPath
: public wxGraphicsPath
60 wxMacCoreGraphicsPath();
61 ~wxMacCoreGraphicsPath();
63 // begins a new subpath at (x,y)
64 virtual void MoveToPoint( wxDouble x
, wxDouble y
);
66 // adds a straight line from the current point to (x,y)
67 virtual void AddLineToPoint( wxDouble x
, wxDouble y
);
69 // adds a cubic Bezier curve from the current point, using two control points and an end point
70 virtual void AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
);
72 // closes the current sub-path
73 virtual void CloseSubpath();
75 // gets the last point of the current path, (0,0) if not yet set
76 virtual void GetCurrentPoint( wxDouble
& x
, wxDouble
&y
);
78 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
79 virtual void AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
);
82 // These are convenience functions which - if not available natively will be assembled
83 // using the primitives from above
86 // adds a quadratic Bezier curve from the current point, using a control point and an end point
87 virtual void AddQuadCurveToPoint( wxDouble cx
, wxDouble cy
, wxDouble x
, wxDouble y
);
89 // appends a rectangle as a new closed subpath
90 virtual void AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
92 // appends an ellipsis as a new closed subpath fitting the passed rectangle
93 virtual void AddCircle( wxDouble x
, wxDouble y
, wxDouble r
);
95 // draws a an arc to two tangents connecting (current) to (x1,y1) and (x1,y1) to (x2,y2), also a straight line from (current) to (x1,y1)
96 virtual void AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
);
98 // returns the native path
99 virtual void * GetNativePath() const { return m_path
; }
101 // give the native path returned by GetNativePath() back (there might be some deallocations necessary)
102 virtual void UnGetNativePath(void *p
) {}
104 DECLARE_DYNAMIC_CLASS(wxMacCoreGraphicsPath
)
105 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsPath
)
107 CGMutablePathRef m_path
;
110 //-----------------------------------------------------------------------------
111 // wxGraphicsPath implementation
112 //-----------------------------------------------------------------------------
114 IMPLEMENT_DYNAMIC_CLASS(wxMacCoreGraphicsPath
, wxGraphicsPath
)
116 wxMacCoreGraphicsPath::wxMacCoreGraphicsPath()
118 m_path
= CGPathCreateMutable();
121 wxMacCoreGraphicsPath::~wxMacCoreGraphicsPath()
123 CGPathRelease( m_path
);
126 // opens (starts) a new subpath
127 void wxMacCoreGraphicsPath::MoveToPoint( wxDouble x1
, wxDouble y1
)
129 CGPathMoveToPoint( m_path
, NULL
, x1
, y1
);
132 void wxMacCoreGraphicsPath::AddLineToPoint( wxDouble x1
, wxDouble y1
)
134 CGPathAddLineToPoint( m_path
, NULL
, x1
, y1
);
137 void wxMacCoreGraphicsPath::AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
)
139 CGPathAddCurveToPoint( m_path
, NULL
, cx1
, cy1
, cx2
, cy2
, x
, y
);
142 void wxMacCoreGraphicsPath::AddQuadCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble x
, wxDouble y
)
144 CGPathAddQuadCurveToPoint( m_path
, NULL
, cx1
, cy1
, x
, y
);
147 void wxMacCoreGraphicsPath::AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
149 CGRect cgRect
= { { x
, y
} , { w
, h
} };
150 CGPathAddRect( m_path
, NULL
, cgRect
);
153 void wxMacCoreGraphicsPath::AddCircle( wxDouble x
, wxDouble y
, wxDouble r
)
155 CGPathAddArc( m_path
, NULL
, x
, y
, r
, 0.0 , 2 * M_PI
, true );
158 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
159 void wxMacCoreGraphicsPath::AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
)
161 // inverse direction as we the 'normal' state is a y axis pointing down, ie mirrored to the standard core graphics setup
162 CGPathAddArc( m_path
, NULL
, x
, y
, r
, startAngle
, endAngle
, !clockwise
);
165 void wxMacCoreGraphicsPath::AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
)
167 CGPathAddArcToPoint( m_path
, NULL
, x1
, y1
, x2
, y2
, r
);
170 // closes the current subpath
171 void wxMacCoreGraphicsPath::CloseSubpath()
173 CGPathCloseSubpath( m_path
);
176 // gets the last point of the current path, (0,0) if not yet set
177 void wxMacCoreGraphicsPath::GetCurrentPoint( wxDouble
& x
, wxDouble
&y
)
179 CGPoint p
= CGPathGetCurrentPoint( m_path
);
188 class WXDLLEXPORT wxMacCoreGraphicsContext
: public wxGraphicsContext
191 wxMacCoreGraphicsContext( CGContextRef cgcontext
);
193 wxMacCoreGraphicsContext( WindowRef window
);
195 wxMacCoreGraphicsContext( wxWindow
* window
);
197 wxMacCoreGraphicsContext();
199 ~wxMacCoreGraphicsContext();
203 // creates a path instance that corresponds to the type of graphics context, ie GDIPlus, cairo, CoreGraphics ...
204 virtual wxGraphicsPath
* CreatePath();
206 // push the current state of the context, ie the transformation matrix on a stack
207 virtual void PushState();
209 // pops a stored state from the stack
210 virtual void PopState();
212 // clips drawings to the region
213 virtual void Clip( const wxRegion
®ion
);
215 // clips drawings to the rect
216 virtual void Clip( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
218 // resets the clipping to original extent
219 virtual void ResetClip();
221 virtual void * GetNativeContext();
228 virtual void Translate( wxDouble dx
, wxDouble dy
);
231 virtual void Scale( wxDouble xScale
, wxDouble yScale
);
234 virtual void Rotate( wxDouble angle
);
241 virtual void SetPen( const wxPen
&pen
);
243 // sets the brush for filling
244 virtual void SetBrush( const wxBrush
&brush
);
246 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
247 virtual void SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
248 const wxColour
&c1
, const wxColour
&c2
);
250 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
251 // with radius r and color cColor
252 virtual void SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
253 const wxColour
&oColor
, const wxColour
&cColor
);
256 virtual void SetFont( const wxFont
&font
);
258 // sets the text color
259 virtual void SetTextColor( const wxColour
&col
);
261 // strokes along a path with the current pen
262 virtual void StrokePath( const wxGraphicsPath
*path
);
264 // fills a path with the current brush
265 virtual void FillPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
267 // draws a path by first filling and then stroking
268 virtual void DrawPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
274 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
);
276 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
);
278 virtual void GetTextExtent( const wxString
&text
, wxDouble
*width
, wxDouble
*height
,
279 wxDouble
*descent
, wxDouble
*externalLeading
) const;
281 virtual void GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const;
287 virtual void DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
289 virtual void DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
291 void SetNativeContext( CGContextRef cg
);
292 CGPathDrawingMode
GetDrawingMode() const { return m_mode
; }
294 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext
)
295 DECLARE_DYNAMIC_CLASS(wxMacCoreGraphicsContext
)
298 void EnsureIsValid();
300 CGContextRef m_cgContext
;
301 WindowRef m_windowRef
;
304 wxMacCFRefHolder
<HIShapeRef
> m_clipRgn
;
305 bool m_releaseContext
;
306 CGPathDrawingMode m_mode
;
307 ATSUStyle m_macATSUIStyle
;
311 wxColor m_textForegroundColor
;
314 //-----------------------------------------------------------------------------
315 // device context implementation
317 // more and more of the dc functionality should be implemented by calling
318 // the appropricate wxMacCoreGraphicsContext, but we will have to do that step by step
319 // also coordinate conversions should be moved to native matrix ops
320 //-----------------------------------------------------------------------------
322 // we always stock two context states, one at entry, to be able to preserve the
323 // state we were called with, the other one after changing to HI Graphics orientation
324 // (this one is used for getting back clippings etc)
326 //-----------------------------------------------------------------------------
327 // wxGraphicsContext implementation
328 //-----------------------------------------------------------------------------
330 IMPLEMENT_DYNAMIC_CLASS(wxMacCoreGraphicsContext
, wxGraphicsContext
)
332 void wxMacCoreGraphicsContext::Init()
335 m_mode
= kCGPathFill
;
336 m_macATSUIStyle
= NULL
;
337 m_releaseContext
= false;
338 m_clipRgn
.Set(HIShapeCreateEmpty());
341 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGContextRef cgcontext
)
344 m_cgContext
= cgcontext
;
345 CGContextSaveGState( m_cgContext
);
346 CGContextSaveGState( m_cgContext
);
349 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( WindowRef window
)
352 m_windowRef
= window
;
355 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( wxWindow
* window
)
358 m_windowRef
= (WindowRef
) window
->MacGetTopLevelWindowRef();
359 m_originX
= m_originY
= 0;
360 window
->MacWindowToRootWindow( &m_originX
, &m_originY
);
363 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext()
368 wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext()
372 // TODO : when is this necessary - should we add a Flush() method ? CGContextSynchronize( m_cgContext );
373 CGContextRestoreGState( m_cgContext
);
374 CGContextRestoreGState( m_cgContext
);
377 if ( m_releaseContext
)
378 QDEndCGContext( GetWindowPort( m_windowRef
) , &m_cgContext
);
381 void wxMacCoreGraphicsContext::EnsureIsValid()
385 OSStatus status
= QDBeginCGContext( GetWindowPort( m_windowRef
) , &m_cgContext
);
386 wxASSERT_MSG( status
== noErr
, wxT("Cannot nest wxDCs on the same window") );
388 GetWindowBounds( m_windowRef
, kWindowContentRgn
, &bounds
);
389 CGContextSaveGState( m_cgContext
);
390 CGContextTranslateCTM( m_cgContext
, 0 , bounds
.bottom
- bounds
.top
);
391 CGContextScaleCTM( m_cgContext
, 1 , -1 );
392 CGContextTranslateCTM( m_cgContext
, m_originX
, m_originY
);
393 CGContextSaveGState( m_cgContext
);
394 m_releaseContext
= true;
395 if ( !HIShapeIsEmpty(m_clipRgn
) )
397 HIShapeReplacePathInCGContext( m_clipRgn
, m_cgContext
);
398 CGContextClip( m_cgContext
);
404 void wxMacCoreGraphicsContext::Clip( const wxRegion
®ion
)
408 HIShapeRef shape
= HIShapeCreateWithQDRgn( (RgnHandle
) region
.GetWXHRGN() );
409 HIShapeReplacePathInCGContext( shape
, m_cgContext
);
410 CGContextClip( m_cgContext
);
415 m_clipRgn
.Set(HIShapeCreateWithQDRgn( (RgnHandle
) region
.GetWXHRGN() ));
419 // clips drawings to the rect
420 void wxMacCoreGraphicsContext::Clip( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
422 HIRect r
= CGRectMake( x
, y
, w
, h
);
425 CGContextClipToRect( m_cgContext
, r
);
429 m_clipRgn
.Set(HIShapeCreateWithRect(&r
));
433 // resets the clipping to original extent
434 void wxMacCoreGraphicsContext::ResetClip()
438 CGContextRestoreGState( m_cgContext
);
439 CGContextSaveGState( m_cgContext
);
443 m_clipRgn
.Set(HIShapeCreateEmpty());
447 void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath
*path
)
451 int width
= m_pen
.GetWidth();
454 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
457 bool offset
= ( width
% 2 ) == 1 ;
460 CGContextTranslateCTM( m_cgContext
, 0.5, 0.5 );
462 CGContextAddPath( m_cgContext
, (CGPathRef
) path
->GetNativePath() );
463 CGContextStrokePath( m_cgContext
);
466 CGContextTranslateCTM( m_cgContext
, -0.5, -0.5 );
469 void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath
*path
, int fillStyle
)
473 CGPathDrawingMode mode
= m_mode
;
475 if ( fillStyle
== wxODDEVEN_RULE
)
477 if ( mode
== kCGPathFill
)
478 mode
= kCGPathEOFill
;
479 else if ( mode
== kCGPathFillStroke
)
480 mode
= kCGPathEOFillStroke
;
483 int width
= m_pen
.GetWidth();
486 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
489 bool offset
= ( width
% 2 ) == 1 ;
492 CGContextTranslateCTM( m_cgContext
, 0.5, 0.5 );
494 CGContextAddPath( m_cgContext
, (CGPathRef
) path
->GetNativePath() );
495 CGContextDrawPath( m_cgContext
, mode
);
498 CGContextTranslateCTM( m_cgContext
, -0.5, -0.5 );
501 void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath
*path
, int fillStyle
)
505 CGContextAddPath( m_cgContext
, (CGPathRef
) path
->GetNativePath() );
506 if ( fillStyle
== wxODDEVEN_RULE
)
507 CGContextEOFillPath( m_cgContext
);
509 CGContextFillPath( m_cgContext
);
512 wxGraphicsPath
* wxMacCoreGraphicsContext::CreatePath()
514 return new wxMacCoreGraphicsPath();
517 void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg
)
519 // we allow either setting or clearing but not replacing
520 wxASSERT( m_cgContext
== NULL
|| cg
== NULL
);
523 CGContextSaveGState( cg
);
527 void wxMacCoreGraphicsContext::Translate( wxDouble dx
, wxDouble dy
)
531 CGContextTranslateCTM( m_cgContext
, dx
, dy
);
534 void wxMacCoreGraphicsContext::Scale( wxDouble xScale
, wxDouble yScale
)
538 CGContextScaleCTM( m_cgContext
, xScale
, yScale
);
541 void wxMacCoreGraphicsContext::Rotate( wxDouble angle
)
545 CGContextRotateCTM( m_cgContext
, angle
);
548 void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
552 CGImageRef image
= (CGImageRef
)( bmp
.CGImageCreate() );
553 HIRect r
= CGRectMake( x
, y
, w
, h
);
554 HIViewDrawCGImage( m_cgContext
, &r
, image
);
555 CGImageRelease( image
);
558 void wxMacCoreGraphicsContext::DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
562 CGRect r
= CGRectMake( 00 , 00 , w
, h
);
563 CGContextSaveGState( m_cgContext
);
564 CGContextTranslateCTM( m_cgContext
, x
, y
+ h
);
565 CGContextScaleCTM( m_cgContext
, 1, -1 );
566 PlotIconRefInContext( m_cgContext
, &r
, kAlignNone
, kTransformNone
,
567 NULL
, kPlotIconRefNormalFlags
, MAC_WXHICON( icon
.GetHICON() ) );
568 CGContextRestoreGState( m_cgContext
);
571 void wxMacCoreGraphicsContext::PushState()
575 CGContextSaveGState( m_cgContext
);
578 void wxMacCoreGraphicsContext::PopState()
582 CGContextRestoreGState( m_cgContext
);
585 void wxMacCoreGraphicsContext::SetTextColor( const wxColour
&col
)
587 m_textForegroundColor
= col
;
588 // to recreate the native font after color change
593 #pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes
595 // CGPattern wrapper class: always allocate on heap, never call destructor
597 class wxMacCoreGraphicsPattern
600 wxMacCoreGraphicsPattern() {}
602 // is guaranteed to be called only with a non-Null CGContextRef
603 virtual void Render( CGContextRef ctxRef
) = 0;
605 operator CGPatternRef() const { return m_patternRef
; }
608 virtual ~wxMacCoreGraphicsPattern()
610 // as this is called only when the m_patternRef is been released;
611 // don't release it again
614 static void _Render( void *info
, CGContextRef ctxRef
)
616 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
617 if ( self
&& ctxRef
)
618 self
->Render( ctxRef
);
621 static void _Dispose( void *info
)
623 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
627 CGPatternRef m_patternRef
;
629 static const CGPatternCallbacks ms_Callbacks
;
632 const CGPatternCallbacks
wxMacCoreGraphicsPattern::ms_Callbacks
= { 0, &wxMacCoreGraphicsPattern::_Render
, &wxMacCoreGraphicsPattern::_Dispose
};
634 class ImagePattern
: public wxMacCoreGraphicsPattern
637 ImagePattern( const wxBitmap
* bmp
, const CGAffineTransform
& transform
)
639 wxASSERT( bmp
&& bmp
->Ok() );
641 Init( (CGImageRef
) bmp
->CGImageCreate() , transform
);
644 // ImagePattern takes ownership of CGImageRef passed in
645 ImagePattern( CGImageRef image
, const CGAffineTransform
& transform
)
650 Init( image
, transform
);
653 virtual void Render( CGContextRef ctxRef
)
656 HIViewDrawCGImage( ctxRef
, &m_imageBounds
, m_image
);
660 void Init( CGImageRef image
, const CGAffineTransform
& transform
)
665 m_imageBounds
= CGRectMake( 0.0, 0.0, (CGFloat
)CGImageGetWidth( m_image
), (CGFloat
)CGImageGetHeight( m_image
) );
666 m_patternRef
= CGPatternCreate(
667 this , m_imageBounds
, transform
,
668 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
669 kCGPatternTilingNoDistortion
, true , &wxMacCoreGraphicsPattern::ms_Callbacks
);
673 virtual ~ImagePattern()
676 CGImageRelease( m_image
);
680 CGRect m_imageBounds
;
683 class HatchPattern
: public wxMacCoreGraphicsPattern
686 HatchPattern( int hatchstyle
, const CGAffineTransform
& transform
)
688 m_hatch
= hatchstyle
;
689 m_imageBounds
= CGRectMake( 0.0, 0.0, 8.0 , 8.0 );
690 m_patternRef
= CGPatternCreate(
691 this , m_imageBounds
, transform
,
692 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
693 kCGPatternTilingNoDistortion
, false , &wxMacCoreGraphicsPattern::ms_Callbacks
);
696 void StrokeLineSegments( CGContextRef ctxRef
, const CGPoint pts
[] , size_t count
)
698 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
699 if ( UMAGetSystemVersion() >= 0x1040 )
701 CGContextStrokeLineSegments( ctxRef
, pts
, count
);
706 CGContextBeginPath( ctxRef
);
707 for (size_t i
= 0; i
< count
; i
+= 2)
709 CGContextMoveToPoint(ctxRef
, pts
[i
].x
, pts
[i
].y
);
710 CGContextAddLineToPoint(ctxRef
, pts
[i
+1].x
, pts
[i
+1].y
);
712 CGContextStrokePath(ctxRef
);
716 virtual void Render( CGContextRef ctxRef
)
720 case wxBDIAGONAL_HATCH
:
724 { 8.0 , 0.0 } , { 0.0 , 8.0 }
726 StrokeLineSegments( ctxRef
, pts
, 2 );
730 case wxCROSSDIAG_HATCH
:
734 { 0.0 , 0.0 } , { 8.0 , 8.0 } ,
735 { 8.0 , 0.0 } , { 0.0 , 8.0 }
737 StrokeLineSegments( ctxRef
, pts
, 4 );
741 case wxFDIAGONAL_HATCH
:
745 { 0.0 , 0.0 } , { 8.0 , 8.0 }
747 StrokeLineSegments( ctxRef
, pts
, 2 );
755 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
756 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
758 StrokeLineSegments( ctxRef
, pts
, 4 );
762 case wxHORIZONTAL_HATCH
:
766 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
768 StrokeLineSegments( ctxRef
, pts
, 2 );
772 case wxVERTICAL_HATCH
:
776 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
778 StrokeLineSegments( ctxRef
, pts
, 2 );
788 virtual ~HatchPattern() {}
790 CGRect m_imageBounds
;
796 void wxMacCoreGraphicsContext::SetPen( const wxPen
&pen
)
799 if ( m_cgContext
== NULL
)
802 bool fill
= m_brush
.GetStyle() != wxTRANSPARENT
;
803 bool stroke
= pen
.GetStyle() != wxTRANSPARENT
;
806 // we can benchmark performance; should go into a setting eventually
807 CGContextSetShouldAntialias( m_cgContext
, false );
810 if ( fill
|| stroke
)
813 m_mode
= kCGPathFill
; // just a default
817 CGContextSetRGBStrokeColor( m_cgContext
, pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
818 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 );
820 // TODO: * m_dc->m_scaleX
821 CGFloat penWidth
= pen
.GetWidth();
824 CGContextSetLineWidth( m_cgContext
, penWidth
);
827 switch ( pen
.GetCap() )
830 cap
= kCGLineCapRound
;
833 case wxCAP_PROJECTING
:
834 cap
= kCGLineCapSquare
;
838 cap
= kCGLineCapButt
;
842 cap
= kCGLineCapButt
;
847 switch ( pen
.GetJoin() )
850 join
= kCGLineJoinBevel
;
854 join
= kCGLineJoinMiter
;
858 join
= kCGLineJoinRound
;
862 join
= kCGLineJoinMiter
;
866 m_mode
= kCGPathStroke
;
869 const CGFloat
*lengths
= NULL
;
870 CGFloat
*userLengths
= NULL
;
872 const CGFloat dashUnit
= penWidth
< 1.0 ? 1.0 : penWidth
;
874 const CGFloat dotted
[] = { dashUnit
, dashUnit
+ 2.0 };
875 const CGFloat short_dashed
[] = { 9.0 , 6.0 };
876 const CGFloat dashed
[] = { 19.0 , 9.0 };
877 const CGFloat dotted_dashed
[] = { 9.0 , 6.0 , 3.0 , 3.0 };
879 switch ( pen
.GetStyle() )
886 count
= WXSIZEOF(dotted
);
891 count
= WXSIZEOF(dashed
);
895 lengths
= short_dashed
;
896 count
= WXSIZEOF(short_dashed
);
900 lengths
= dotted_dashed
;
901 count
= WXSIZEOF(dotted_dashed
);
906 count
= pen
.GetDashes( &dashes
);
907 if ((dashes
!= NULL
) && (count
> 0))
909 userLengths
= new CGFloat
[count
];
910 for ( int i
= 0; i
< count
; ++i
)
912 userLengths
[i
] = dashes
[i
] * dashUnit
;
914 if ( i
% 2 == 1 && userLengths
[i
] < dashUnit
+ 2.0 )
915 userLengths
[i
] = dashUnit
+ 2.0;
916 else if ( i
% 2 == 0 && userLengths
[i
] < dashUnit
)
917 userLengths
[i
] = dashUnit
;
920 lengths
= userLengths
;
925 CGFloat alphaArray
[1] = { 1.0 };
926 wxBitmap
* bmp
= pen
.GetStipple();
927 if ( bmp
&& bmp
->Ok() )
929 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
930 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
931 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
932 CGContextSetStrokePattern( m_cgContext
, pattern
, alphaArray
);
939 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
940 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
941 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( pen
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
943 CGFloat colorArray
[4] = { pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
944 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 };
946 CGContextSetStrokePattern( m_cgContext
, pattern
, colorArray
);
951 if ((lengths
!= NULL
) && (count
> 0))
953 CGContextSetLineDash( m_cgContext
, 0 , lengths
, count
);
954 // force the line cap, otherwise we get artifacts (overlaps) and just solid lines
955 cap
= kCGLineCapButt
;
959 CGContextSetLineDash( m_cgContext
, 0 , NULL
, 0 );
962 CGContextSetLineCap( m_cgContext
, cap
);
963 CGContextSetLineJoin( m_cgContext
, join
);
965 delete[] userLengths
;
968 if ( fill
&& stroke
)
969 m_mode
= kCGPathFillStroke
;
973 void wxMacCoreGraphicsContext::SetBrush( const wxBrush
&brush
)
976 if ( m_cgContext
== NULL
)
979 bool fill
= brush
.GetStyle() != wxTRANSPARENT
;
980 bool stroke
= m_pen
.GetStyle() != wxTRANSPARENT
;
983 // we can benchmark performance, should go into a setting later
984 CGContextSetShouldAntialias( m_cgContext
, false );
987 if ( fill
|| stroke
)
990 m_mode
= kCGPathFill
; // just a default
994 if ( brush
.GetStyle() == wxSOLID
)
996 CGContextSetRGBFillColor( m_cgContext
, brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
997 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 );
999 else if ( brush
.IsHatch() )
1001 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
1002 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
1003 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( brush
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
1005 CGFloat colorArray
[4] = { brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
1006 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 };
1008 CGContextSetFillPattern( m_cgContext
, pattern
, colorArray
);
1012 // now brush is a bitmap
1013 CGFloat alphaArray
[1] = { 1.0 };
1014 wxBitmap
* bmp
= brush
.GetStipple();
1015 if ( bmp
&& bmp
->Ok() )
1017 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
1018 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
1019 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
1020 CGContextSetFillPattern( m_cgContext
, pattern
, alphaArray
);
1024 m_mode
= kCGPathFill
;
1027 if ( fill
&& stroke
)
1028 m_mode
= kCGPathFillStroke
;
1030 m_mode
= kCGPathStroke
;
1034 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
1035 void wxMacCoreGraphicsContext::SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
1036 const wxColour
&c1
, const wxColour
&c2
)
1040 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
1041 // with radius r and color cColor
1042 void wxMacCoreGraphicsContext::SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
1043 const wxColour
&oColor
, const wxColour
&cColor
)
1048 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
)
1050 DrawText(str
, x
, y
, 0.0);
1053 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
)
1057 OSStatus status
= noErr
;
1058 ATSUTextLayout atsuLayout
;
1059 UniCharCount chars
= str
.length();
1060 UniChar
* ubuf
= NULL
;
1062 #if SIZEOF_WCHAR_T == 4
1063 wxMBConvUTF16 converter
;
1065 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
1066 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1067 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
1069 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1070 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1071 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1072 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1074 chars
= unicharlen
/ 2;
1077 ubuf
= (UniChar
*) str
.wc_str();
1079 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1080 chars
= wxWcslen( wchar
.data() );
1081 ubuf
= (UniChar
*) wchar
.data();
1085 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1086 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1088 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the rotated text") );
1090 status
= ::ATSUSetTransientFontMatching( atsuLayout
, true );
1091 wxASSERT_MSG( status
== noErr
, wxT("couldn't setup transient font matching") );
1093 int iAngle
= int( angle
* RAD2DEG
);
1094 if ( abs(iAngle
) > 0 )
1096 Fixed atsuAngle
= IntToFixed( iAngle
);
1097 ATSUAttributeTag atsuTags
[] =
1099 kATSULineRotationTag
,
1101 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1105 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1109 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1110 atsuTags
, atsuSizes
, atsuValues
);
1114 ATSUAttributeTag atsuTags
[] =
1118 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1120 sizeof( CGContextRef
) ,
1122 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1126 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1127 atsuTags
, atsuSizes
, atsuValues
);
1130 ATSUTextMeasurement textBefore
, textAfter
;
1131 ATSUTextMeasurement ascent
, descent
;
1133 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1134 &textBefore
, &textAfter
, &ascent
, &descent
);
1136 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1141 if ( m_backgroundMode == wxSOLID )
1143 wxGraphicsPath* path = m_graphicContext->CreatePath();
1144 path->MoveToPoint( drawX , drawY );
1145 path->AddLineToPoint(
1146 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent)) ,
1147 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent)) );
1148 path->AddLineToPoint(
1149 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent ) + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1150 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent) - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1151 path->AddLineToPoint(
1152 (int) (drawX + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1153 (int) (drawY - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1155 m_graphicContext->FillPath( path , m_textBackgroundColour );
1159 x
+= (int)(sin(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1160 y
+= (int)(cos(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1162 status
= ::ATSUMeasureTextImage( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1163 IntToFixed(x
) , IntToFixed(y
) , &rect
);
1164 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1166 CGContextSaveGState(m_cgContext
);
1167 CGContextTranslateCTM(m_cgContext
, x
, y
);
1168 CGContextScaleCTM(m_cgContext
, 1, -1);
1169 status
= ::ATSUDrawText( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1170 IntToFixed(0) , IntToFixed(0) );
1172 wxASSERT_MSG( status
== noErr
, wxT("couldn't draw the rotated text") );
1174 CGContextRestoreGState(m_cgContext
);
1176 ::ATSUDisposeTextLayout(atsuLayout
);
1178 #if SIZEOF_WCHAR_T == 4
1183 void wxMacCoreGraphicsContext::GetTextExtent( const wxString
&str
, wxDouble
*width
, wxDouble
*height
,
1184 wxDouble
*descent
, wxDouble
*externalLeading
) const
1186 wxCHECK_RET( m_macATSUIStyle
!= NULL
, wxT("wxDC(cg)::DoGetTextExtent - no valid font set") );
1188 OSStatus status
= noErr
;
1190 ATSUTextLayout atsuLayout
;
1191 UniCharCount chars
= str
.length();
1192 UniChar
* ubuf
= NULL
;
1194 #if SIZEOF_WCHAR_T == 4
1195 wxMBConvUTF16 converter
;
1197 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
1198 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1199 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
1201 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1202 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1203 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1204 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1206 chars
= unicharlen
/ 2;
1209 ubuf
= (UniChar
*) str
.wc_str();
1211 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1212 chars
= wxWcslen( wchar
.data() );
1213 ubuf
= (UniChar
*) wchar
.data();
1217 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1218 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1220 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the text") );
1222 ATSUTextMeasurement textBefore
, textAfter
;
1223 ATSUTextMeasurement textAscent
, textDescent
;
1225 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1226 &textBefore
, &textAfter
, &textAscent
, &textDescent
);
1229 *height
= FixedToInt(textAscent
+ textDescent
);
1231 *descent
= FixedToInt(textDescent
);
1232 if ( externalLeading
)
1233 *externalLeading
= 0;
1235 *width
= FixedToInt(textAfter
- textBefore
);
1237 ::ATSUDisposeTextLayout(atsuLayout
);
1240 void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const
1243 widths
.Add(0, text
.length());
1248 ATSUTextLayout atsuLayout
;
1249 UniCharCount chars
= text
.length();
1250 UniChar
* ubuf
= NULL
;
1252 #if SIZEOF_WCHAR_T == 4
1253 wxMBConvUTF16 converter
;
1255 size_t unicharlen
= converter
.WC2MB( NULL
, text
.wc_str() , 0 );
1256 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1257 converter
.WC2MB( (char*) ubuf
, text
.wc_str(), unicharlen
+ 2 );
1259 const wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1260 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1261 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1262 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1264 chars
= unicharlen
/ 2;
1267 ubuf
= (UniChar
*) text
.wc_str();
1269 wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1270 chars
= wxWcslen( wchar
.data() );
1271 ubuf
= (UniChar
*) wchar
.data();
1275 ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1276 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1278 for ( int pos
= 0; pos
< (int)chars
; pos
++ )
1280 unsigned long actualNumberOfBounds
= 0;
1281 ATSTrapezoid glyphBounds
;
1283 // We get a single bound, since the text should only require one. If it requires more, there is an issue
1285 result
= ATSUGetGlyphBounds( atsuLayout
, 0, 0, kATSUFromTextBeginning
, pos
+ 1,
1286 kATSUseDeviceOrigins
, 1, &glyphBounds
, &actualNumberOfBounds
);
1287 if (result
!= noErr
|| actualNumberOfBounds
!= 1 )
1290 widths
[pos
] = FixedToInt( glyphBounds
.upperRight
.x
- glyphBounds
.upperLeft
.x
);
1291 //unsigned char uch = s[i];
1294 ::ATSUDisposeTextLayout(atsuLayout
);
1297 void wxMacCoreGraphicsContext::SetFont( const wxFont
&font
)
1299 if ( m_macATSUIStyle
)
1301 ::ATSUDisposeStyle((ATSUStyle
)m_macATSUIStyle
);
1302 m_macATSUIStyle
= NULL
;
1310 status
= ATSUCreateAndCopyStyle( (ATSUStyle
) font
.MacGetATSUStyle() , (ATSUStyle
*) &m_macATSUIStyle
);
1312 wxASSERT_MSG( status
== noErr
, wxT("couldn't create ATSU style") );
1314 // we need the scale here ...
1316 Fixed atsuSize
= IntToFixed( int( /*m_scaleY*/ 1 * font
.MacGetFontSize()) );
1317 RGBColor atsuColor
= MAC_WXCOLORREF( m_textForegroundColor
.GetPixel() );
1318 ATSUAttributeTag atsuTags
[] =
1323 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1326 sizeof( RGBColor
) ,
1328 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1334 status
= ::ATSUSetAttributes(
1335 (ATSUStyle
)m_macATSUIStyle
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
) ,
1336 atsuTags
, atsuSizes
, atsuValues
);
1338 wxASSERT_MSG( status
== noErr
, wxT("couldn't modify ATSU style") );
1342 void * wxMacCoreGraphicsContext::GetNativeContext()
1347 wxGraphicsContext
* wxGraphicsContext::Create( const wxWindowDC
&dc
)
1349 return new wxMacCoreGraphicsContext((CGContextRef
)dc
.GetWindow()->MacGetCGContextRef() );
1352 wxGraphicsContext
* wxGraphicsContext::Create( wxWindow
* window
)
1354 return new wxMacCoreGraphicsContext( window
);
1357 wxGraphicsContext
* wxGraphicsContext::CreateFromNative( void * context
)
1359 return new wxMacCoreGraphicsContext((CGContextRef
)context
);
1362 wxGraphicsContext
* wxGraphicsContext::CreateFromNativeWindow( void * window
)
1364 return new wxMacCoreGraphicsContext((WindowRef
)window
);
1367 #endif // wxMAC_USE_CORE_GRAPHICS