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 #include "wx/graphics.h"
16 #if wxUSE_GRAPHICS_CONTEXT && wxMAC_USE_CORE_GRAPHICS
21 #include "wx/dcmemory.h"
22 #include "wx/dcprint.h"
23 #include "wx/region.h"
27 #include "wx/mac/uma.h"
33 // in case our functions were defined outside std, we make it known all the same
39 #include "wx/mac/private.h"
41 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
42 typedef float CGFloat
;
49 class WXDLLEXPORT wxMacCoreGraphicsPath
: public wxGraphicsPath
51 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsPath
)
53 wxMacCoreGraphicsPath();
54 ~wxMacCoreGraphicsPath();
56 // begins a new subpath at (x,y)
57 virtual void MoveToPoint( wxDouble x
, wxDouble y
);
59 // adds a straight line from the current point to (x,y)
60 virtual void AddLineToPoint( wxDouble x
, wxDouble y
);
62 // adds a cubic Bezier curve from the current point, using two control points and an end point
63 virtual void AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
);
65 // closes the current sub-path
66 virtual void CloseSubpath();
68 // gets the last point of the current path, (0,0) if not yet set
69 virtual void GetCurrentPoint( wxDouble
& x
, wxDouble
&y
);
71 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
72 virtual void AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
);
75 // These are convenience functions which - if not available natively will be assembled
76 // using the primitives from above
79 // adds a quadratic Bezier curve from the current point, using a control point and an end point
80 virtual void AddQuadCurveToPoint( wxDouble cx
, wxDouble cy
, wxDouble x
, wxDouble y
);
82 // appends a rectangle as a new closed subpath
83 virtual void AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
85 // appends an ellipsis as a new closed subpath fitting the passed rectangle
86 virtual void AddCircle( wxDouble x
, wxDouble y
, wxDouble r
);
88 // 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)
89 virtual void AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
);
91 CGPathRef
GetPath() const;
93 CGMutablePathRef m_path
;
96 wxMacCoreGraphicsPath::wxMacCoreGraphicsPath()
98 m_path
= CGPathCreateMutable();
101 wxMacCoreGraphicsPath::~wxMacCoreGraphicsPath()
103 CGPathRelease( m_path
);
106 // opens (starts) a new subpath
107 void wxMacCoreGraphicsPath::MoveToPoint( wxDouble x1
, wxDouble y1
)
109 CGPathMoveToPoint( m_path
, NULL
, x1
, y1
);
112 void wxMacCoreGraphicsPath::AddLineToPoint( wxDouble x1
, wxDouble y1
)
114 CGPathAddLineToPoint( m_path
, NULL
, x1
, y1
);
117 void wxMacCoreGraphicsPath::AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
)
119 CGPathAddCurveToPoint( m_path
, NULL
, cx1
, cy1
, cx2
, cy2
, x
, y
);
122 void wxMacCoreGraphicsPath::AddQuadCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble x
, wxDouble y
)
124 CGPathAddQuadCurveToPoint( m_path
, NULL
, cx1
, cy1
, x
, y
);
127 void wxMacCoreGraphicsPath::AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
129 CGRect cgRect
= { { x
, y
} , { w
, h
} };
130 CGPathAddRect( m_path
, NULL
, cgRect
);
133 void wxMacCoreGraphicsPath::AddCircle( wxDouble x
, wxDouble y
, wxDouble r
)
135 CGPathAddArc( m_path
, NULL
, x
, y
, r
, 0.0 , 2 * M_PI
, true );
138 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
139 void wxMacCoreGraphicsPath::AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
)
141 // inverse direction as we the 'normal' state is a y axis pointing down, ie mirrored to the standard core graphics setup
142 CGPathAddArc( m_path
, NULL
, x
, y
, r
, startAngle
, endAngle
, !clockwise
);
145 void wxMacCoreGraphicsPath::AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
)
147 CGPathAddArcToPoint( m_path
, NULL
, x1
, y1
, x2
, y2
, r
);
150 // closes the current subpath
151 void wxMacCoreGraphicsPath::CloseSubpath()
153 CGPathCloseSubpath( m_path
);
156 CGPathRef
wxMacCoreGraphicsPath::GetPath() const
161 // gets the last point of the current path, (0,0) if not yet set
162 void wxMacCoreGraphicsPath::GetCurrentPoint( wxDouble
& x
, wxDouble
&y
)
164 CGPoint p
= CGPathGetCurrentPoint( m_path
);
173 class WXDLLEXPORT wxMacCoreGraphicsContext
: public wxGraphicsContext
175 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext
)
178 wxMacCoreGraphicsContext( CGrafPtr port
);
180 wxMacCoreGraphicsContext( CGContextRef cgcontext
);
182 wxMacCoreGraphicsContext();
184 ~wxMacCoreGraphicsContext();
187 // creates a path instance that corresponds to the type of graphics context, ie GDIPlus, cairo, CoreGraphics ...
188 virtual wxGraphicsPath
* CreatePath();
190 // push the current state of the context, ie the transformation matrix on a stack
191 virtual void PushState();
193 // pops a stored state from the stack
194 virtual void PopState();
196 // clips drawings to the region
197 virtual void Clip( const wxRegion
®ion
);
204 virtual void Translate( wxDouble dx
, wxDouble dy
);
207 virtual void Scale( wxDouble xScale
, wxDouble yScale
);
210 virtual void Rotate( wxDouble angle
);
217 virtual void SetPen( const wxPen
&pen
);
219 // sets the brush for filling
220 virtual void SetBrush( const wxBrush
&brush
);
222 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
223 virtual void SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
224 const wxColour
&c1
, const wxColour
&c2
);
226 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
227 // with radius r and color cColor
228 virtual void SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
229 const wxColour
&oColor
, const wxColour
&cColor
);
232 virtual void SetFont( const wxFont
&font
);
234 // sets the text color
235 virtual void SetTextColor( const wxColour
&col
);
237 // strokes along a path with the current pen
238 virtual void StrokePath( const wxGraphicsPath
*path
);
240 // fills a path with the current brush
241 virtual void FillPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
243 // draws a path by first filling and then stroking
244 virtual void DrawPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
250 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
);
252 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
);
254 virtual void GetTextExtent( const wxString
&text
, wxDouble
*width
, wxDouble
*height
,
255 wxDouble
*descent
, wxDouble
*externalLeading
) const;
257 virtual void GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const;
263 virtual void DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
265 virtual void DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
267 CGContextRef
GetNativeContext();
268 void SetNativeContext( CGContextRef cg
);
269 CGPathDrawingMode
GetDrawingMode() const { return m_mode
; }
272 CGContextRef m_cgContext
;
274 CGPathDrawingMode m_mode
;
275 ATSUStyle m_macATSUIStyle
;
278 wxColor m_textForegroundColor
;
281 //-----------------------------------------------------------------------------
283 //-----------------------------------------------------------------------------
285 #if !defined( __DARWIN__ ) || defined(__MWERKS__)
287 const double M_PI
= 3.14159265358979;
291 const double RAD2DEG
= 180.0 / M_PI
;
292 const short kEmulatedMode
= -1;
293 const short kUnsupportedMode
= -2;
295 extern TECObjectRef s_TECNativeCToUnicode
;
297 //-----------------------------------------------------------------------------
299 //-----------------------------------------------------------------------------
301 static inline double dmin(double a
, double b
) { return a
< b
? a
: b
; }
302 static inline double dmax(double a
, double b
) { return a
> b
? a
: b
; }
303 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
305 //-----------------------------------------------------------------------------
306 // device context implementation
308 // more and more of the dc functionality should be implemented by calling
309 // the appropricate wxMacCoreGraphicsContext, but we will have to do that step by step
310 // also coordinate conversions should be moved to native matrix ops
311 //-----------------------------------------------------------------------------
313 // we always stock two context states, one at entry, to be able to preserve the
314 // state we were called with, the other one after changing to HI Graphics orientation
315 // (this one is used for getting back clippings etc)
317 //-----------------------------------------------------------------------------
318 // wxGraphicsPath implementation
319 //-----------------------------------------------------------------------------
321 //-----------------------------------------------------------------------------
322 // wxGraphicsContext implementation
323 //-----------------------------------------------------------------------------
325 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGrafPtr port
)
329 m_mode
= kCGPathFill
;
330 m_macATSUIStyle
= NULL
;
333 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGContextRef cgcontext
)
336 m_cgContext
= cgcontext
;
337 m_mode
= kCGPathFill
;
338 m_macATSUIStyle
= NULL
;
339 CGContextSaveGState( m_cgContext
);
340 CGContextSaveGState( m_cgContext
);
343 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext()
347 m_mode
= kCGPathFill
;
348 m_macATSUIStyle
= NULL
;
351 wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext()
355 CGContextSynchronize( m_cgContext
);
356 CGContextRestoreGState( m_cgContext
);
357 CGContextRestoreGState( m_cgContext
);
361 CGContextRelease( m_cgContext
);
364 void wxMacCoreGraphicsContext::Clip( const wxRegion
®ion
)
366 // ClipCGContextToRegion ( m_cgContext, &bounds , (RgnHandle) dc->m_macCurrentClipRgn );
369 void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath
*p
)
371 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
372 CGContextAddPath( m_cgContext
, path
->GetPath() );
373 CGContextStrokePath( m_cgContext
);
376 void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath
*p
, int fillStyle
)
378 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
379 CGPathDrawingMode mode
= m_mode
;
381 if ( fillStyle
== wxODDEVEN_RULE
)
383 if ( mode
== kCGPathFill
)
384 mode
= kCGPathEOFill
;
385 else if ( mode
== kCGPathFillStroke
)
386 mode
= kCGPathEOFillStroke
;
389 CGContextAddPath( m_cgContext
, path
->GetPath() );
390 CGContextDrawPath( m_cgContext
, mode
);
393 void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath
*p
, int fillStyle
)
395 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
397 CGContextAddPath( m_cgContext
, path
->GetPath() );
398 if ( fillStyle
== wxODDEVEN_RULE
)
399 CGContextEOFillPath( m_cgContext
);
401 CGContextFillPath( m_cgContext
);
404 wxGraphicsPath
* wxMacCoreGraphicsContext::CreatePath()
406 // make sure that we now have a real cgref, before doing
407 // anything with paths
408 CGContextRef cg
= GetNativeContext();
411 return new wxMacCoreGraphicsPath();
414 CGContextRef
wxMacCoreGraphicsContext::GetNativeContext()
419 void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg
)
421 // we allow either setting or clearing but not replacing
422 wxASSERT( m_cgContext
== NULL
|| cg
== NULL
);
425 CGContextSaveGState( cg
);
429 void wxMacCoreGraphicsContext::Translate( wxDouble dx
, wxDouble dy
)
431 CGContextTranslateCTM( m_cgContext
, dx
, dy
);
434 void wxMacCoreGraphicsContext::Scale( wxDouble xScale
, wxDouble yScale
)
436 CGContextScaleCTM( m_cgContext
, xScale
, yScale
);
439 void wxMacCoreGraphicsContext::Rotate( wxDouble angle
)
441 CGContextRotateCTM( m_cgContext
, angle
);
444 void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
446 CGImageRef image
= (CGImageRef
)( bmp
.CGImageCreate() );
447 HIRect r
= CGRectMake( x
, y
, w
, h
);
448 HIViewDrawCGImage( m_cgContext
, &r
, image
);
449 CGImageRelease( image
);
452 void wxMacCoreGraphicsContext::DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
454 CGRect r
= CGRectMake( 00 , 00 , w
, h
);
455 CGContextSaveGState( m_cgContext
);
456 CGContextTranslateCTM( m_cgContext
, x
, y
+ h
);
457 CGContextScaleCTM( m_cgContext
, 1, -1 );
458 PlotIconRefInContext( m_cgContext
, &r
, kAlignNone
, kTransformNone
,
459 NULL
, kPlotIconRefNormalFlags
, MAC_WXHICON( icon
.GetHICON() ) );
460 CGContextRestoreGState( m_cgContext
);
463 void wxMacCoreGraphicsContext::PushState()
465 CGContextSaveGState( m_cgContext
);
468 void wxMacCoreGraphicsContext::PopState()
470 CGContextRestoreGState( m_cgContext
);
473 void wxMacCoreGraphicsContext::SetTextColor( const wxColour
&col
)
475 m_textForegroundColor
= col
;
479 #pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes
481 // CGPattern wrapper class: always allocate on heap, never call destructor
483 class wxMacCoreGraphicsPattern
486 wxMacCoreGraphicsPattern() {}
488 // is guaranteed to be called only with a non-Null CGContextRef
489 virtual void Render( CGContextRef ctxRef
) = 0;
491 operator CGPatternRef() const { return m_patternRef
; }
494 virtual ~wxMacCoreGraphicsPattern()
496 // as this is called only when the m_patternRef is been released;
497 // don't release it again
500 static void _Render( void *info
, CGContextRef ctxRef
)
502 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
503 if ( self
&& ctxRef
)
504 self
->Render( ctxRef
);
507 static void _Dispose( void *info
)
509 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
513 CGPatternRef m_patternRef
;
515 static const CGPatternCallbacks ms_Callbacks
;
518 const CGPatternCallbacks
wxMacCoreGraphicsPattern::ms_Callbacks
= { 0, &wxMacCoreGraphicsPattern::_Render
, &wxMacCoreGraphicsPattern::_Dispose
};
520 class ImagePattern
: public wxMacCoreGraphicsPattern
523 ImagePattern( const wxBitmap
* bmp
, CGAffineTransform transform
)
525 wxASSERT( bmp
&& bmp
->Ok() );
527 Init( (CGImageRef
) bmp
->CGImageCreate() , transform
);
530 // ImagePattern takes ownership of CGImageRef passed in
531 ImagePattern( CGImageRef image
, CGAffineTransform transform
)
536 Init( image
, transform
);
539 virtual void Render( CGContextRef ctxRef
)
542 HIViewDrawCGImage( ctxRef
, &m_imageBounds
, m_image
);
546 void Init( CGImageRef image
, CGAffineTransform transform
)
551 m_imageBounds
= CGRectMake( 0.0, 0.0, (CGFloat
)CGImageGetWidth( m_image
), (CGFloat
)CGImageGetHeight( m_image
) );
552 m_patternRef
= CGPatternCreate(
553 this , m_imageBounds
, transform
,
554 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
555 kCGPatternTilingNoDistortion
, true , &wxMacCoreGraphicsPattern::ms_Callbacks
);
559 virtual ~ImagePattern()
562 CGImageRelease( m_image
);
566 CGRect m_imageBounds
;
569 class HatchPattern
: public wxMacCoreGraphicsPattern
572 HatchPattern( int hatchstyle
, CGAffineTransform transform
)
574 m_hatch
= hatchstyle
;
575 m_imageBounds
= CGRectMake( 0.0, 0.0, 8.0 , 8.0 );
576 m_patternRef
= CGPatternCreate(
577 this , m_imageBounds
, transform
,
578 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
579 kCGPatternTilingNoDistortion
, false , &wxMacCoreGraphicsPattern::ms_Callbacks
);
582 void StrokeLineSegments( CGContextRef ctxRef
, const CGPoint pts
[] , size_t count
)
584 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
585 if ( UMAGetSystemVersion() >= 0x1040 )
587 CGContextStrokeLineSegments( ctxRef
, pts
, count
);
592 CGContextBeginPath( ctxRef
);
593 for (size_t i
= 0; i
< count
; i
+= 2)
595 CGContextMoveToPoint(ctxRef
, pts
[i
].x
, pts
[i
].y
);
596 CGContextAddLineToPoint(ctxRef
, pts
[i
+1].x
, pts
[i
+1].y
);
598 CGContextStrokePath(ctxRef
);
602 virtual void Render( CGContextRef ctxRef
)
606 case wxBDIAGONAL_HATCH
:
610 { 8.0 , 0.0 } , { 0.0 , 8.0 }
612 StrokeLineSegments( ctxRef
, pts
, 2 );
616 case wxCROSSDIAG_HATCH
:
620 { 0.0 , 0.0 } , { 8.0 , 8.0 } ,
621 { 8.0 , 0.0 } , { 0.0 , 8.0 }
623 StrokeLineSegments( ctxRef
, pts
, 4 );
627 case wxFDIAGONAL_HATCH
:
631 { 0.0 , 0.0 } , { 8.0 , 8.0 }
633 StrokeLineSegments( ctxRef
, pts
, 2 );
641 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
642 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
644 StrokeLineSegments( ctxRef
, pts
, 4 );
648 case wxHORIZONTAL_HATCH
:
652 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
654 StrokeLineSegments( ctxRef
, pts
, 2 );
658 case wxVERTICAL_HATCH
:
662 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
664 StrokeLineSegments( ctxRef
, pts
, 2 );
674 virtual ~HatchPattern() {}
676 CGRect m_imageBounds
;
682 void wxMacCoreGraphicsContext::SetPen( const wxPen
&pen
)
685 if ( m_cgContext
== NULL
)
688 bool fill
= m_brush
.GetStyle() != wxTRANSPARENT
;
689 bool stroke
= pen
.GetStyle() != wxTRANSPARENT
;
692 // we can benchmark performance; should go into a setting eventually
693 CGContextSetShouldAntialias( m_cgContext
, false );
699 m_mode
= kCGPathFill
; // just a default
703 CGContextSetRGBStrokeColor( m_cgContext
, pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
704 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 );
706 // TODO: * m_dc->m_scaleX
707 CGFloat penWidth
= pen
.GetWidth();
710 CGContextSetLineWidth( m_cgContext
, penWidth
);
713 switch ( pen
.GetCap() )
716 cap
= kCGLineCapRound
;
719 case wxCAP_PROJECTING
:
720 cap
= kCGLineCapSquare
;
724 cap
= kCGLineCapButt
;
728 cap
= kCGLineCapButt
;
733 switch ( pen
.GetJoin() )
736 join
= kCGLineJoinBevel
;
740 join
= kCGLineJoinMiter
;
744 join
= kCGLineJoinRound
;
748 join
= kCGLineJoinMiter
;
752 m_mode
= kCGPathStroke
;
755 const CGFloat
*lengths
= NULL
;
756 CGFloat
*userLengths
= NULL
;
758 const CGFloat dashUnit
= penWidth
< 1.0 ? 1.0 : penWidth
;
760 const CGFloat dotted
[] = { dashUnit
, dashUnit
+ 2.0 };
761 const CGFloat short_dashed
[] = { 9.0 , 6.0 };
762 const CGFloat dashed
[] = { 19.0 , 9.0 };
763 const CGFloat dotted_dashed
[] = { 9.0 , 6.0 , 3.0 , 3.0 };
765 switch ( pen
.GetStyle() )
772 count
= WXSIZEOF(dotted
);
777 count
= WXSIZEOF(dashed
);
781 lengths
= short_dashed
;
782 count
= WXSIZEOF(short_dashed
);
786 lengths
= dotted_dashed
;
787 count
= WXSIZEOF(dotted_dashed
);
792 count
= pen
.GetDashes( &dashes
);
793 if ((dashes
!= NULL
) && (count
> 0))
795 userLengths
= new CGFloat
[count
];
796 for ( int i
= 0; i
< count
; ++i
)
798 userLengths
[i
] = dashes
[i
] * dashUnit
;
800 if ( i
% 2 == 1 && userLengths
[i
] < dashUnit
+ 2.0 )
801 userLengths
[i
] = dashUnit
+ 2.0;
802 else if ( i
% 2 == 0 && userLengths
[i
] < dashUnit
)
803 userLengths
[i
] = dashUnit
;
806 lengths
= userLengths
;
811 CGFloat alphaArray
[1] = { 1.0 };
812 wxBitmap
* bmp
= pen
.GetStipple();
813 if ( bmp
&& bmp
->Ok() )
815 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
816 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
817 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
818 CGContextSetStrokePattern( m_cgContext
, pattern
, alphaArray
);
825 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
826 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
827 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( pen
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
829 CGFloat colorArray
[4] = { pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
830 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 };
832 CGContextSetStrokePattern( m_cgContext
, pattern
, colorArray
);
837 if ((lengths
!= NULL
) && (count
> 0))
839 CGContextSetLineDash( m_cgContext
, 0 , lengths
, count
);
840 // force the line cap, otherwise we get artifacts (overlaps) and just solid lines
841 cap
= kCGLineCapButt
;
845 CGContextSetLineDash( m_cgContext
, 0 , NULL
, 0 );
848 CGContextSetLineCap( m_cgContext
, cap
);
849 CGContextSetLineJoin( m_cgContext
, join
);
851 delete[] userLengths
;
854 if ( fill
&& stroke
)
855 m_mode
= kCGPathFillStroke
;
859 void wxMacCoreGraphicsContext::SetBrush( const wxBrush
&brush
)
862 if ( m_cgContext
== NULL
)
865 bool fill
= brush
.GetStyle() != wxTRANSPARENT
;
866 bool stroke
= m_pen
.GetStyle() != wxTRANSPARENT
;
869 // we can benchmark performance, should go into a setting later
870 CGContextSetShouldAntialias( m_cgContext
, false );
876 m_mode
= kCGPathFill
; // just a default
880 if ( brush
.GetStyle() == wxSOLID
)
882 CGContextSetRGBFillColor( m_cgContext
, brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
883 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 );
885 else if ( brush
.IsHatch() )
887 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
888 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
889 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( brush
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
891 CGFloat colorArray
[4] = { brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
892 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 };
894 CGContextSetFillPattern( m_cgContext
, pattern
, colorArray
);
898 // now brush is a bitmap
899 CGFloat alphaArray
[1] = { 1.0 };
900 wxBitmap
* bmp
= brush
.GetStipple();
901 if ( bmp
&& bmp
->Ok() )
903 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
904 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
905 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
906 CGContextSetFillPattern( m_cgContext
, pattern
, alphaArray
);
910 m_mode
= kCGPathFill
;
913 if ( fill
&& stroke
)
914 m_mode
= kCGPathFillStroke
;
916 m_mode
= kCGPathStroke
;
920 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
921 void wxMacCoreGraphicsContext::SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
922 const wxColour
&c1
, const wxColour
&c2
)
926 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
927 // with radius r and color cColor
928 void wxMacCoreGraphicsContext::SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
929 const wxColour
&oColor
, const wxColour
&cColor
)
934 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
)
936 DrawText(str
, x
, y
, 0.0);
939 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
)
941 OSStatus status
= noErr
;
942 ATSUTextLayout atsuLayout
;
943 UniCharCount chars
= str
.length();
944 UniChar
* ubuf
= NULL
;
946 #if SIZEOF_WCHAR_T == 4
947 wxMBConvUTF16 converter
;
949 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
950 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
951 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
953 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
954 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
955 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
956 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
958 chars
= unicharlen
/ 2;
961 ubuf
= (UniChar
*) str
.wc_str();
963 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
964 chars
= wxWcslen( wchar
.data() );
965 ubuf
= (UniChar
*) wchar
.data();
969 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
970 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
972 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the rotated text") );
974 status
= ::ATSUSetTransientFontMatching( atsuLayout
, true );
975 wxASSERT_MSG( status
== noErr
, wxT("couldn't setup transient font matching") );
977 int iAngle
= int( angle
* RAD2DEG
);
978 if ( abs(iAngle
) > 0 )
980 Fixed atsuAngle
= IntToFixed( iAngle
);
981 ATSUAttributeTag atsuTags
[] =
983 kATSULineRotationTag
,
985 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
989 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
993 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
994 atsuTags
, atsuSizes
, atsuValues
);
998 ATSUAttributeTag atsuTags
[] =
1002 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1004 sizeof( CGContextRef
) ,
1006 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1010 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1011 atsuTags
, atsuSizes
, atsuValues
);
1014 ATSUTextMeasurement textBefore
, textAfter
;
1015 ATSUTextMeasurement ascent
, descent
;
1017 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1018 &textBefore
, &textAfter
, &ascent
, &descent
);
1020 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1025 if ( m_backgroundMode == wxSOLID )
1027 wxGraphicsPath* path = m_graphicContext->CreatePath();
1028 path->MoveToPoint( drawX , drawY );
1029 path->AddLineToPoint(
1030 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent)) ,
1031 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent)) );
1032 path->AddLineToPoint(
1033 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent ) + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1034 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent) - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1035 path->AddLineToPoint(
1036 (int) (drawX + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1037 (int) (drawY - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1039 m_graphicContext->FillPath( path , m_textBackgroundColour );
1043 x
+= (int)(sin(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1044 y
+= (int)(cos(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1046 status
= ::ATSUMeasureTextImage( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1047 IntToFixed(x
) , IntToFixed(y
) , &rect
);
1048 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1050 CGContextSaveGState(m_cgContext
);
1051 CGContextTranslateCTM(m_cgContext
, x
, y
);
1052 CGContextScaleCTM(m_cgContext
, 1, -1);
1053 status
= ::ATSUDrawText( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1054 IntToFixed(0) , IntToFixed(0) );
1056 wxASSERT_MSG( status
== noErr
, wxT("couldn't draw the rotated text") );
1058 CGContextRestoreGState(m_cgContext
);
1060 ::ATSUDisposeTextLayout(atsuLayout
);
1062 #if SIZEOF_WCHAR_T == 4
1067 void wxMacCoreGraphicsContext::GetTextExtent( const wxString
&str
, wxDouble
*width
, wxDouble
*height
,
1068 wxDouble
*descent
, wxDouble
*externalLeading
) const
1070 wxCHECK_RET( m_macATSUIStyle
!= NULL
, wxT("wxDC(cg)::DoGetTextExtent - no valid font set") );
1072 OSStatus status
= noErr
;
1074 ATSUTextLayout atsuLayout
;
1075 UniCharCount chars
= str
.length();
1076 UniChar
* ubuf
= NULL
;
1078 #if SIZEOF_WCHAR_T == 4
1079 wxMBConvUTF16 converter
;
1081 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
1082 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1083 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
1085 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1086 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1087 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1088 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1090 chars
= unicharlen
/ 2;
1093 ubuf
= (UniChar
*) str
.wc_str();
1095 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1096 chars
= wxWcslen( wchar
.data() );
1097 ubuf
= (UniChar
*) wchar
.data();
1101 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1102 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1104 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the text") );
1106 ATSUTextMeasurement textBefore
, textAfter
;
1107 ATSUTextMeasurement textAscent
, textDescent
;
1109 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1110 &textBefore
, &textAfter
, &textAscent
, &textDescent
);
1113 *height
= FixedToInt(textAscent
+ textDescent
);
1115 *descent
= FixedToInt(textDescent
);
1116 if ( externalLeading
)
1117 *externalLeading
= 0;
1119 *width
= FixedToInt(textAfter
- textBefore
);
1121 ::ATSUDisposeTextLayout(atsuLayout
);
1124 void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const
1127 widths
.Add(0, text
.length());
1132 ATSUTextLayout atsuLayout
;
1133 UniCharCount chars
= text
.length();
1134 UniChar
* ubuf
= NULL
;
1136 #if SIZEOF_WCHAR_T == 4
1137 wxMBConvUTF16 converter
;
1139 size_t unicharlen
= converter
.WC2MB( NULL
, text
.wc_str() , 0 );
1140 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1141 converter
.WC2MB( (char*) ubuf
, text
.wc_str(), unicharlen
+ 2 );
1143 const wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1144 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1145 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1146 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1148 chars
= unicharlen
/ 2;
1151 ubuf
= (UniChar
*) text
.wc_str();
1153 wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1154 chars
= wxWcslen( wchar
.data() );
1155 ubuf
= (UniChar
*) wchar
.data();
1160 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1161 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1163 for ( int pos
= 0; pos
< (int)chars
; pos
++ )
1165 unsigned long actualNumberOfBounds
= 0;
1166 ATSTrapezoid glyphBounds
;
1168 // We get a single bound, since the text should only require one. If it requires more, there is an issue
1170 result
= ATSUGetGlyphBounds( atsuLayout
, 0, 0, kATSUFromTextBeginning
, pos
+ 1,
1171 kATSUseDeviceOrigins
, 1, &glyphBounds
, &actualNumberOfBounds
);
1172 if (result
!= noErr
|| actualNumberOfBounds
!= 1 )
1175 widths
[pos
] = FixedToInt( glyphBounds
.upperRight
.x
- glyphBounds
.upperLeft
.x
);
1176 //unsigned char uch = s[i];
1179 ::ATSUDisposeTextLayout(atsuLayout
);
1182 void wxMacCoreGraphicsContext::SetFont( const wxFont
&font
)
1184 if ( m_macATSUIStyle
)
1186 ::ATSUDisposeStyle((ATSUStyle
)m_macATSUIStyle
);
1187 m_macATSUIStyle
= NULL
;
1194 status
= ATSUCreateAndCopyStyle( (ATSUStyle
) font
.MacGetATSUStyle() , (ATSUStyle
*) &m_macATSUIStyle
);
1196 wxASSERT_MSG( status
== noErr
, wxT("couldn't create ATSU style") );
1198 // we need the scale here ...
1200 Fixed atsuSize
= IntToFixed( int( /*m_scaleY*/ 1 * font
.MacGetFontSize()) );
1201 RGBColor atsuColor
= MAC_WXCOLORREF( m_textForegroundColor
.GetPixel() );
1202 ATSUAttributeTag atsuTags
[] =
1207 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1210 sizeof( RGBColor
) ,
1212 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1218 status
= ::ATSUSetAttributes(
1219 (ATSUStyle
)m_macATSUIStyle
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
) ,
1220 atsuTags
, atsuSizes
, atsuValues
);
1222 wxASSERT_MSG( status
== noErr
, wxT("couldn't modify ATSU style") );
1226 wxGraphicsContext
* wxGraphicsContext::Create( const wxWindowDC
&dc
)
1228 return new wxMacCoreGraphicsContext((CGContextRef
)dc
.GetWindow()->MacGetCGContextRef() );
1231 #endif // wxMAC_USE_CORE_GRAPHICS