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"
20 #include "wx/region.h"
23 #include "wx/mac/uma.h"
28 // in case our functions were defined outside std, we make it known all the same
34 #include "wx/mac/private.h"
36 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
37 typedef float CGFloat
;
44 class WXDLLEXPORT wxMacCoreGraphicsPath
: public wxGraphicsPath
46 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsPath
)
48 wxMacCoreGraphicsPath();
49 ~wxMacCoreGraphicsPath();
51 // begins a new subpath at (x,y)
52 virtual void MoveToPoint( wxDouble x
, wxDouble y
);
54 // adds a straight line from the current point to (x,y)
55 virtual void AddLineToPoint( wxDouble x
, wxDouble y
);
57 // adds a cubic Bezier curve from the current point, using two control points and an end point
58 virtual void AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
);
60 // closes the current sub-path
61 virtual void CloseSubpath();
63 // gets the last point of the current path, (0,0) if not yet set
64 virtual void GetCurrentPoint( wxDouble
& x
, wxDouble
&y
);
66 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
67 virtual void AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
);
70 // These are convenience functions which - if not available natively will be assembled
71 // using the primitives from above
74 // adds a quadratic Bezier curve from the current point, using a control point and an end point
75 virtual void AddQuadCurveToPoint( wxDouble cx
, wxDouble cy
, wxDouble x
, wxDouble y
);
77 // appends a rectangle as a new closed subpath
78 virtual void AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
80 // appends an ellipsis as a new closed subpath fitting the passed rectangle
81 virtual void AddCircle( wxDouble x
, wxDouble y
, wxDouble r
);
83 // 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)
84 virtual void AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
);
86 CGPathRef
GetPath() const;
88 CGMutablePathRef m_path
;
91 wxMacCoreGraphicsPath::wxMacCoreGraphicsPath()
93 m_path
= CGPathCreateMutable();
96 wxMacCoreGraphicsPath::~wxMacCoreGraphicsPath()
98 CGPathRelease( m_path
);
101 // opens (starts) a new subpath
102 void wxMacCoreGraphicsPath::MoveToPoint( wxDouble x1
, wxDouble y1
)
104 CGPathMoveToPoint( m_path
, NULL
, x1
, y1
);
107 void wxMacCoreGraphicsPath::AddLineToPoint( wxDouble x1
, wxDouble y1
)
109 CGPathAddLineToPoint( m_path
, NULL
, x1
, y1
);
112 void wxMacCoreGraphicsPath::AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
)
114 CGPathAddCurveToPoint( m_path
, NULL
, cx1
, cy1
, cx2
, cy2
, x
, y
);
117 void wxMacCoreGraphicsPath::AddQuadCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble x
, wxDouble y
)
119 CGPathAddQuadCurveToPoint( m_path
, NULL
, cx1
, cy1
, x
, y
);
122 void wxMacCoreGraphicsPath::AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
124 CGRect cgRect
= { { x
, y
} , { w
, h
} };
125 CGPathAddRect( m_path
, NULL
, cgRect
);
128 void wxMacCoreGraphicsPath::AddCircle( wxDouble x
, wxDouble y
, wxDouble r
)
130 CGPathAddArc( m_path
, NULL
, x
, y
, r
, 0.0 , 2 * M_PI
, true );
133 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
134 void wxMacCoreGraphicsPath::AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
)
136 // inverse direction as we the 'normal' state is a y axis pointing down, ie mirrored to the standard core graphics setup
137 CGPathAddArc( m_path
, NULL
, x
, y
, r
, startAngle
, endAngle
, !clockwise
);
140 void wxMacCoreGraphicsPath::AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
)
142 CGPathAddArcToPoint( m_path
, NULL
, x1
, y1
, x2
, y2
, r
);
145 // closes the current subpath
146 void wxMacCoreGraphicsPath::CloseSubpath()
148 CGPathCloseSubpath( m_path
);
151 CGPathRef
wxMacCoreGraphicsPath::GetPath() const
156 // gets the last point of the current path, (0,0) if not yet set
157 void wxMacCoreGraphicsPath::GetCurrentPoint( wxDouble
& x
, wxDouble
&y
)
159 CGPoint p
= CGPathGetCurrentPoint( m_path
);
168 class WXDLLEXPORT wxMacCoreGraphicsContext
: public wxGraphicsContext
170 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext
)
173 wxMacCoreGraphicsContext( CGrafPtr port
);
175 wxMacCoreGraphicsContext( CGContextRef cgcontext
);
177 wxMacCoreGraphicsContext();
179 ~wxMacCoreGraphicsContext();
182 // creates a path instance that corresponds to the type of graphics context, ie GDIPlus, cairo, CoreGraphics ...
183 virtual wxGraphicsPath
* CreatePath();
185 // push the current state of the context, ie the transformation matrix on a stack
186 virtual void PushState();
188 // pops a stored state from the stack
189 virtual void PopState();
191 // clips drawings to the region
192 virtual void Clip( const wxRegion
®ion
);
199 virtual void Translate( wxDouble dx
, wxDouble dy
);
202 virtual void Scale( wxDouble xScale
, wxDouble yScale
);
205 virtual void Rotate( wxDouble angle
);
212 virtual void SetPen( const wxPen
&pen
);
214 // sets the brush for filling
215 virtual void SetBrush( const wxBrush
&brush
);
217 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
218 virtual void SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
219 const wxColour
&c1
, const wxColour
&c2
);
221 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
222 // with radius r and color cColor
223 virtual void SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
224 const wxColour
&oColor
, const wxColour
&cColor
);
227 virtual void SetFont( const wxFont
&font
);
229 // sets the text color
230 virtual void SetTextColor( const wxColour
&col
);
232 // strokes along a path with the current pen
233 virtual void StrokePath( const wxGraphicsPath
*path
);
235 // fills a path with the current brush
236 virtual void FillPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
238 // draws a path by first filling and then stroking
239 virtual void DrawPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
245 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
);
247 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
);
249 virtual void GetTextExtent( const wxString
&text
, wxDouble
*width
, wxDouble
*height
,
250 wxDouble
*descent
, wxDouble
*externalLeading
) const;
252 virtual void GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const;
258 virtual void DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
260 virtual void DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
262 CGContextRef
GetNativeContext();
263 void SetNativeContext( CGContextRef cg
);
264 CGPathDrawingMode
GetDrawingMode() const { return m_mode
; }
267 CGContextRef m_cgContext
;
269 CGPathDrawingMode m_mode
;
270 ATSUStyle m_macATSUIStyle
;
273 wxColor m_textForegroundColor
;
276 //-----------------------------------------------------------------------------
278 //-----------------------------------------------------------------------------
280 #if !defined( __DARWIN__ ) || defined(__MWERKS__)
282 const double M_PI
= 3.14159265358979;
286 static const double RAD2DEG
= 180.0 / M_PI
;
288 //-----------------------------------------------------------------------------
289 // device context implementation
291 // more and more of the dc functionality should be implemented by calling
292 // the appropricate wxMacCoreGraphicsContext, but we will have to do that step by step
293 // also coordinate conversions should be moved to native matrix ops
294 //-----------------------------------------------------------------------------
296 // we always stock two context states, one at entry, to be able to preserve the
297 // state we were called with, the other one after changing to HI Graphics orientation
298 // (this one is used for getting back clippings etc)
300 //-----------------------------------------------------------------------------
301 // wxGraphicsPath implementation
302 //-----------------------------------------------------------------------------
304 //-----------------------------------------------------------------------------
305 // wxGraphicsContext implementation
306 //-----------------------------------------------------------------------------
308 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGrafPtr port
)
312 m_mode
= kCGPathFill
;
313 m_macATSUIStyle
= NULL
;
316 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGContextRef cgcontext
)
319 m_cgContext
= cgcontext
;
320 m_mode
= kCGPathFill
;
321 m_macATSUIStyle
= NULL
;
322 CGContextSaveGState( m_cgContext
);
323 CGContextSaveGState( m_cgContext
);
326 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext()
330 m_mode
= kCGPathFill
;
331 m_macATSUIStyle
= NULL
;
334 wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext()
338 CGContextSynchronize( m_cgContext
);
339 CGContextRestoreGState( m_cgContext
);
340 CGContextRestoreGState( m_cgContext
);
344 CGContextRelease( m_cgContext
);
347 void wxMacCoreGraphicsContext::Clip( const wxRegion
®ion
)
349 // ClipCGContextToRegion ( m_cgContext, &bounds , (RgnHandle) dc->m_macCurrentClipRgn );
352 void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath
*p
)
354 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
355 CGContextAddPath( m_cgContext
, path
->GetPath() );
356 CGContextStrokePath( m_cgContext
);
359 void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath
*p
, int fillStyle
)
361 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
362 CGPathDrawingMode mode
= m_mode
;
364 if ( fillStyle
== wxODDEVEN_RULE
)
366 if ( mode
== kCGPathFill
)
367 mode
= kCGPathEOFill
;
368 else if ( mode
== kCGPathFillStroke
)
369 mode
= kCGPathEOFillStroke
;
372 CGContextAddPath( m_cgContext
, path
->GetPath() );
373 CGContextDrawPath( m_cgContext
, mode
);
376 void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath
*p
, int fillStyle
)
378 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
380 CGContextAddPath( m_cgContext
, path
->GetPath() );
381 if ( fillStyle
== wxODDEVEN_RULE
)
382 CGContextEOFillPath( m_cgContext
);
384 CGContextFillPath( m_cgContext
);
387 wxGraphicsPath
* wxMacCoreGraphicsContext::CreatePath()
389 // make sure that we now have a real cgref, before doing
390 // anything with paths
391 CGContextRef cg
= GetNativeContext();
394 return new wxMacCoreGraphicsPath();
397 CGContextRef
wxMacCoreGraphicsContext::GetNativeContext()
402 void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg
)
404 // we allow either setting or clearing but not replacing
405 wxASSERT( m_cgContext
== NULL
|| cg
== NULL
);
408 CGContextSaveGState( cg
);
412 void wxMacCoreGraphicsContext::Translate( wxDouble dx
, wxDouble dy
)
414 CGContextTranslateCTM( m_cgContext
, dx
, dy
);
417 void wxMacCoreGraphicsContext::Scale( wxDouble xScale
, wxDouble yScale
)
419 CGContextScaleCTM( m_cgContext
, xScale
, yScale
);
422 void wxMacCoreGraphicsContext::Rotate( wxDouble angle
)
424 CGContextRotateCTM( m_cgContext
, angle
);
427 void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
429 CGImageRef image
= (CGImageRef
)( bmp
.CGImageCreate() );
430 HIRect r
= CGRectMake( x
, y
, w
, h
);
431 HIViewDrawCGImage( m_cgContext
, &r
, image
);
432 CGImageRelease( image
);
435 void wxMacCoreGraphicsContext::DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
437 CGRect r
= CGRectMake( 00 , 00 , w
, h
);
438 CGContextSaveGState( m_cgContext
);
439 CGContextTranslateCTM( m_cgContext
, x
, y
+ h
);
440 CGContextScaleCTM( m_cgContext
, 1, -1 );
441 PlotIconRefInContext( m_cgContext
, &r
, kAlignNone
, kTransformNone
,
442 NULL
, kPlotIconRefNormalFlags
, MAC_WXHICON( icon
.GetHICON() ) );
443 CGContextRestoreGState( m_cgContext
);
446 void wxMacCoreGraphicsContext::PushState()
448 CGContextSaveGState( m_cgContext
);
451 void wxMacCoreGraphicsContext::PopState()
453 CGContextRestoreGState( m_cgContext
);
456 void wxMacCoreGraphicsContext::SetTextColor( const wxColour
&col
)
458 m_textForegroundColor
= col
;
462 #pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes
464 // CGPattern wrapper class: always allocate on heap, never call destructor
466 class wxMacCoreGraphicsPattern
469 wxMacCoreGraphicsPattern() {}
471 // is guaranteed to be called only with a non-Null CGContextRef
472 virtual void Render( CGContextRef ctxRef
) = 0;
474 operator CGPatternRef() const { return m_patternRef
; }
477 virtual ~wxMacCoreGraphicsPattern()
479 // as this is called only when the m_patternRef is been released;
480 // don't release it again
483 static void _Render( void *info
, CGContextRef ctxRef
)
485 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
486 if ( self
&& ctxRef
)
487 self
->Render( ctxRef
);
490 static void _Dispose( void *info
)
492 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
496 CGPatternRef m_patternRef
;
498 static const CGPatternCallbacks ms_Callbacks
;
501 const CGPatternCallbacks
wxMacCoreGraphicsPattern::ms_Callbacks
= { 0, &wxMacCoreGraphicsPattern::_Render
, &wxMacCoreGraphicsPattern::_Dispose
};
503 class ImagePattern
: public wxMacCoreGraphicsPattern
506 ImagePattern( const wxBitmap
* bmp
, const CGAffineTransform
& transform
)
508 wxASSERT( bmp
&& bmp
->Ok() );
510 Init( (CGImageRef
) bmp
->CGImageCreate() , transform
);
513 // ImagePattern takes ownership of CGImageRef passed in
514 ImagePattern( CGImageRef image
, const CGAffineTransform
& transform
)
519 Init( image
, transform
);
522 virtual void Render( CGContextRef ctxRef
)
525 HIViewDrawCGImage( ctxRef
, &m_imageBounds
, m_image
);
529 void Init( CGImageRef image
, const CGAffineTransform
& transform
)
534 m_imageBounds
= CGRectMake( 0.0, 0.0, (CGFloat
)CGImageGetWidth( m_image
), (CGFloat
)CGImageGetHeight( m_image
) );
535 m_patternRef
= CGPatternCreate(
536 this , m_imageBounds
, transform
,
537 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
538 kCGPatternTilingNoDistortion
, true , &wxMacCoreGraphicsPattern::ms_Callbacks
);
542 virtual ~ImagePattern()
545 CGImageRelease( m_image
);
549 CGRect m_imageBounds
;
552 class HatchPattern
: public wxMacCoreGraphicsPattern
555 HatchPattern( int hatchstyle
, const CGAffineTransform
& transform
)
557 m_hatch
= hatchstyle
;
558 m_imageBounds
= CGRectMake( 0.0, 0.0, 8.0 , 8.0 );
559 m_patternRef
= CGPatternCreate(
560 this , m_imageBounds
, transform
,
561 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
562 kCGPatternTilingNoDistortion
, false , &wxMacCoreGraphicsPattern::ms_Callbacks
);
565 void StrokeLineSegments( CGContextRef ctxRef
, const CGPoint pts
[] , size_t count
)
567 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
568 if ( UMAGetSystemVersion() >= 0x1040 )
570 CGContextStrokeLineSegments( ctxRef
, pts
, count
);
575 CGContextBeginPath( ctxRef
);
576 for (size_t i
= 0; i
< count
; i
+= 2)
578 CGContextMoveToPoint(ctxRef
, pts
[i
].x
, pts
[i
].y
);
579 CGContextAddLineToPoint(ctxRef
, pts
[i
+1].x
, pts
[i
+1].y
);
581 CGContextStrokePath(ctxRef
);
585 virtual void Render( CGContextRef ctxRef
)
589 case wxBDIAGONAL_HATCH
:
593 { 8.0 , 0.0 } , { 0.0 , 8.0 }
595 StrokeLineSegments( ctxRef
, pts
, 2 );
599 case wxCROSSDIAG_HATCH
:
603 { 0.0 , 0.0 } , { 8.0 , 8.0 } ,
604 { 8.0 , 0.0 } , { 0.0 , 8.0 }
606 StrokeLineSegments( ctxRef
, pts
, 4 );
610 case wxFDIAGONAL_HATCH
:
614 { 0.0 , 0.0 } , { 8.0 , 8.0 }
616 StrokeLineSegments( ctxRef
, pts
, 2 );
624 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
625 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
627 StrokeLineSegments( ctxRef
, pts
, 4 );
631 case wxHORIZONTAL_HATCH
:
635 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
637 StrokeLineSegments( ctxRef
, pts
, 2 );
641 case wxVERTICAL_HATCH
:
645 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
647 StrokeLineSegments( ctxRef
, pts
, 2 );
657 virtual ~HatchPattern() {}
659 CGRect m_imageBounds
;
665 void wxMacCoreGraphicsContext::SetPen( const wxPen
&pen
)
668 if ( m_cgContext
== NULL
)
671 bool fill
= m_brush
.GetStyle() != wxTRANSPARENT
;
672 bool stroke
= pen
.GetStyle() != wxTRANSPARENT
;
675 // we can benchmark performance; should go into a setting eventually
676 CGContextSetShouldAntialias( m_cgContext
, false );
679 if ( fill
|| stroke
)
682 m_mode
= kCGPathFill
; // just a default
686 CGContextSetRGBStrokeColor( m_cgContext
, pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
687 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 );
689 // TODO: * m_dc->m_scaleX
690 CGFloat penWidth
= pen
.GetWidth();
693 CGContextSetLineWidth( m_cgContext
, penWidth
);
696 switch ( pen
.GetCap() )
699 cap
= kCGLineCapRound
;
702 case wxCAP_PROJECTING
:
703 cap
= kCGLineCapSquare
;
707 cap
= kCGLineCapButt
;
711 cap
= kCGLineCapButt
;
716 switch ( pen
.GetJoin() )
719 join
= kCGLineJoinBevel
;
723 join
= kCGLineJoinMiter
;
727 join
= kCGLineJoinRound
;
731 join
= kCGLineJoinMiter
;
735 m_mode
= kCGPathStroke
;
738 const CGFloat
*lengths
= NULL
;
739 CGFloat
*userLengths
= NULL
;
741 const CGFloat dashUnit
= penWidth
< 1.0 ? 1.0 : penWidth
;
743 const CGFloat dotted
[] = { dashUnit
, dashUnit
+ 2.0 };
744 const CGFloat short_dashed
[] = { 9.0 , 6.0 };
745 const CGFloat dashed
[] = { 19.0 , 9.0 };
746 const CGFloat dotted_dashed
[] = { 9.0 , 6.0 , 3.0 , 3.0 };
748 switch ( pen
.GetStyle() )
755 count
= WXSIZEOF(dotted
);
760 count
= WXSIZEOF(dashed
);
764 lengths
= short_dashed
;
765 count
= WXSIZEOF(short_dashed
);
769 lengths
= dotted_dashed
;
770 count
= WXSIZEOF(dotted_dashed
);
775 count
= pen
.GetDashes( &dashes
);
776 if ((dashes
!= NULL
) && (count
> 0))
778 userLengths
= new CGFloat
[count
];
779 for ( int i
= 0; i
< count
; ++i
)
781 userLengths
[i
] = dashes
[i
] * dashUnit
;
783 if ( i
% 2 == 1 && userLengths
[i
] < dashUnit
+ 2.0 )
784 userLengths
[i
] = dashUnit
+ 2.0;
785 else if ( i
% 2 == 0 && userLengths
[i
] < dashUnit
)
786 userLengths
[i
] = dashUnit
;
789 lengths
= userLengths
;
794 CGFloat alphaArray
[1] = { 1.0 };
795 wxBitmap
* bmp
= pen
.GetStipple();
796 if ( bmp
&& bmp
->Ok() )
798 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
799 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
800 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
801 CGContextSetStrokePattern( m_cgContext
, pattern
, alphaArray
);
808 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
809 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
810 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( pen
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
812 CGFloat colorArray
[4] = { pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
813 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 };
815 CGContextSetStrokePattern( m_cgContext
, pattern
, colorArray
);
820 if ((lengths
!= NULL
) && (count
> 0))
822 CGContextSetLineDash( m_cgContext
, 0 , lengths
, count
);
823 // force the line cap, otherwise we get artifacts (overlaps) and just solid lines
824 cap
= kCGLineCapButt
;
828 CGContextSetLineDash( m_cgContext
, 0 , NULL
, 0 );
831 CGContextSetLineCap( m_cgContext
, cap
);
832 CGContextSetLineJoin( m_cgContext
, join
);
834 delete[] userLengths
;
837 if ( fill
&& stroke
)
838 m_mode
= kCGPathFillStroke
;
842 void wxMacCoreGraphicsContext::SetBrush( const wxBrush
&brush
)
845 if ( m_cgContext
== NULL
)
848 bool fill
= brush
.GetStyle() != wxTRANSPARENT
;
849 bool stroke
= m_pen
.GetStyle() != wxTRANSPARENT
;
852 // we can benchmark performance, should go into a setting later
853 CGContextSetShouldAntialias( m_cgContext
, false );
856 if ( fill
|| stroke
)
859 m_mode
= kCGPathFill
; // just a default
863 if ( brush
.GetStyle() == wxSOLID
)
865 CGContextSetRGBFillColor( m_cgContext
, brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
866 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 );
868 else if ( brush
.IsHatch() )
870 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
871 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
872 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( brush
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
874 CGFloat colorArray
[4] = { brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
875 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 };
877 CGContextSetFillPattern( m_cgContext
, pattern
, colorArray
);
881 // now brush is a bitmap
882 CGFloat alphaArray
[1] = { 1.0 };
883 wxBitmap
* bmp
= brush
.GetStipple();
884 if ( bmp
&& bmp
->Ok() )
886 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
887 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
888 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
889 CGContextSetFillPattern( m_cgContext
, pattern
, alphaArray
);
893 m_mode
= kCGPathFill
;
896 if ( fill
&& stroke
)
897 m_mode
= kCGPathFillStroke
;
899 m_mode
= kCGPathStroke
;
903 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
904 void wxMacCoreGraphicsContext::SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
905 const wxColour
&c1
, const wxColour
&c2
)
909 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
910 // with radius r and color cColor
911 void wxMacCoreGraphicsContext::SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
912 const wxColour
&oColor
, const wxColour
&cColor
)
917 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
)
919 DrawText(str
, x
, y
, 0.0);
922 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
)
924 OSStatus status
= noErr
;
925 ATSUTextLayout atsuLayout
;
926 UniCharCount chars
= str
.length();
927 UniChar
* ubuf
= NULL
;
929 #if SIZEOF_WCHAR_T == 4
930 wxMBConvUTF16 converter
;
932 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
933 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
934 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
936 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
937 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
938 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
939 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
941 chars
= unicharlen
/ 2;
944 ubuf
= (UniChar
*) str
.wc_str();
946 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
947 chars
= wxWcslen( wchar
.data() );
948 ubuf
= (UniChar
*) wchar
.data();
952 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
953 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
955 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the rotated text") );
957 status
= ::ATSUSetTransientFontMatching( atsuLayout
, true );
958 wxASSERT_MSG( status
== noErr
, wxT("couldn't setup transient font matching") );
960 int iAngle
= int( angle
* RAD2DEG
);
961 if ( abs(iAngle
) > 0 )
963 Fixed atsuAngle
= IntToFixed( iAngle
);
964 ATSUAttributeTag atsuTags
[] =
966 kATSULineRotationTag
,
968 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
972 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
976 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
977 atsuTags
, atsuSizes
, atsuValues
);
981 ATSUAttributeTag atsuTags
[] =
985 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
987 sizeof( CGContextRef
) ,
989 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
993 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
994 atsuTags
, atsuSizes
, atsuValues
);
997 ATSUTextMeasurement textBefore
, textAfter
;
998 ATSUTextMeasurement ascent
, descent
;
1000 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1001 &textBefore
, &textAfter
, &ascent
, &descent
);
1003 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1008 if ( m_backgroundMode == wxSOLID )
1010 wxGraphicsPath* path = m_graphicContext->CreatePath();
1011 path->MoveToPoint( drawX , drawY );
1012 path->AddLineToPoint(
1013 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent)) ,
1014 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent)) );
1015 path->AddLineToPoint(
1016 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent ) + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1017 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent) - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1018 path->AddLineToPoint(
1019 (int) (drawX + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1020 (int) (drawY - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1022 m_graphicContext->FillPath( path , m_textBackgroundColour );
1026 x
+= (int)(sin(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1027 y
+= (int)(cos(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1029 status
= ::ATSUMeasureTextImage( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1030 IntToFixed(x
) , IntToFixed(y
) , &rect
);
1031 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1033 CGContextSaveGState(m_cgContext
);
1034 CGContextTranslateCTM(m_cgContext
, x
, y
);
1035 CGContextScaleCTM(m_cgContext
, 1, -1);
1036 status
= ::ATSUDrawText( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1037 IntToFixed(0) , IntToFixed(0) );
1039 wxASSERT_MSG( status
== noErr
, wxT("couldn't draw the rotated text") );
1041 CGContextRestoreGState(m_cgContext
);
1043 ::ATSUDisposeTextLayout(atsuLayout
);
1045 #if SIZEOF_WCHAR_T == 4
1050 void wxMacCoreGraphicsContext::GetTextExtent( const wxString
&str
, wxDouble
*width
, wxDouble
*height
,
1051 wxDouble
*descent
, wxDouble
*externalLeading
) const
1053 wxCHECK_RET( m_macATSUIStyle
!= NULL
, wxT("wxDC(cg)::DoGetTextExtent - no valid font set") );
1055 OSStatus status
= noErr
;
1057 ATSUTextLayout atsuLayout
;
1058 UniCharCount chars
= str
.length();
1059 UniChar
* ubuf
= NULL
;
1061 #if SIZEOF_WCHAR_T == 4
1062 wxMBConvUTF16 converter
;
1064 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
1065 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1066 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
1068 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1069 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1070 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1071 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1073 chars
= unicharlen
/ 2;
1076 ubuf
= (UniChar
*) str
.wc_str();
1078 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1079 chars
= wxWcslen( wchar
.data() );
1080 ubuf
= (UniChar
*) wchar
.data();
1084 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1085 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1087 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the text") );
1089 ATSUTextMeasurement textBefore
, textAfter
;
1090 ATSUTextMeasurement textAscent
, textDescent
;
1092 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1093 &textBefore
, &textAfter
, &textAscent
, &textDescent
);
1096 *height
= FixedToInt(textAscent
+ textDescent
);
1098 *descent
= FixedToInt(textDescent
);
1099 if ( externalLeading
)
1100 *externalLeading
= 0;
1102 *width
= FixedToInt(textAfter
- textBefore
);
1104 ::ATSUDisposeTextLayout(atsuLayout
);
1107 void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const
1110 widths
.Add(0, text
.length());
1115 ATSUTextLayout atsuLayout
;
1116 UniCharCount chars
= text
.length();
1117 UniChar
* ubuf
= NULL
;
1119 #if SIZEOF_WCHAR_T == 4
1120 wxMBConvUTF16 converter
;
1122 size_t unicharlen
= converter
.WC2MB( NULL
, text
.wc_str() , 0 );
1123 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1124 converter
.WC2MB( (char*) ubuf
, text
.wc_str(), unicharlen
+ 2 );
1126 const wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1127 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1128 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1129 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1131 chars
= unicharlen
/ 2;
1134 ubuf
= (UniChar
*) text
.wc_str();
1136 wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1137 chars
= wxWcslen( wchar
.data() );
1138 ubuf
= (UniChar
*) wchar
.data();
1142 ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1143 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1145 for ( int pos
= 0; pos
< (int)chars
; pos
++ )
1147 unsigned long actualNumberOfBounds
= 0;
1148 ATSTrapezoid glyphBounds
;
1150 // We get a single bound, since the text should only require one. If it requires more, there is an issue
1152 result
= ATSUGetGlyphBounds( atsuLayout
, 0, 0, kATSUFromTextBeginning
, pos
+ 1,
1153 kATSUseDeviceOrigins
, 1, &glyphBounds
, &actualNumberOfBounds
);
1154 if (result
!= noErr
|| actualNumberOfBounds
!= 1 )
1157 widths
[pos
] = FixedToInt( glyphBounds
.upperRight
.x
- glyphBounds
.upperLeft
.x
);
1158 //unsigned char uch = s[i];
1161 ::ATSUDisposeTextLayout(atsuLayout
);
1164 void wxMacCoreGraphicsContext::SetFont( const wxFont
&font
)
1166 if ( m_macATSUIStyle
)
1168 ::ATSUDisposeStyle((ATSUStyle
)m_macATSUIStyle
);
1169 m_macATSUIStyle
= NULL
;
1176 status
= ATSUCreateAndCopyStyle( (ATSUStyle
) font
.MacGetATSUStyle() , (ATSUStyle
*) &m_macATSUIStyle
);
1178 wxASSERT_MSG( status
== noErr
, wxT("couldn't create ATSU style") );
1180 // we need the scale here ...
1182 Fixed atsuSize
= IntToFixed( int( /*m_scaleY*/ 1 * font
.MacGetFontSize()) );
1183 RGBColor atsuColor
= MAC_WXCOLORREF( m_textForegroundColor
.GetPixel() );
1184 ATSUAttributeTag atsuTags
[] =
1189 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1192 sizeof( RGBColor
) ,
1194 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1200 status
= ::ATSUSetAttributes(
1201 (ATSUStyle
)m_macATSUIStyle
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
) ,
1202 atsuTags
, atsuSizes
, atsuValues
);
1204 wxASSERT_MSG( status
== noErr
, wxT("couldn't modify ATSU style") );
1208 wxGraphicsContext
* wxGraphicsContext::Create( const wxWindowDC
&dc
)
1210 return new wxMacCoreGraphicsContext((CGContextRef
)dc
.GetWindow()->MacGetCGContextRef() );
1213 #endif // wxMAC_USE_CORE_GRAPHICS