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 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 CGPathAddArc( m_path
, NULL
, x
, y
, r
, startAngle
, endAngle
, clockwise
);
144 void wxMacCoreGraphicsPath::AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
)
146 CGPathAddArcToPoint( m_path
, NULL
, x1
, y1
, x2
, y2
, r
);
149 // closes the current subpath
150 void wxMacCoreGraphicsPath::CloseSubpath()
152 CGPathCloseSubpath( m_path
);
155 CGPathRef
wxMacCoreGraphicsPath::GetPath() const
160 // gets the last point of the current path, (0,0) if not yet set
161 void wxMacCoreGraphicsPath::GetCurrentPoint( wxDouble
& x
, wxDouble
&y
)
163 CGPoint p
= CGPathGetCurrentPoint( m_path
);
172 class WXDLLEXPORT wxMacCoreGraphicsContext
: public wxGraphicsContext
174 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext
)
177 wxMacCoreGraphicsContext( CGrafPtr port
);
179 wxMacCoreGraphicsContext( CGContextRef cgcontext
);
181 wxMacCoreGraphicsContext();
183 ~wxMacCoreGraphicsContext();
186 // creates a path instance that corresponds to the type of graphics context, ie GDIPlus, cairo, CoreGraphics ...
187 virtual wxGraphicsPath
* CreatePath();
189 // push the current state of the context, ie the transformation matrix on a stack
190 virtual void PushState();
192 // pops a stored state from the stack
193 virtual void PopState();
195 // clips drawings to the region
196 virtual void Clip( const wxRegion
®ion
);
203 virtual void Translate( wxDouble dx
, wxDouble dy
);
206 virtual void Scale( wxDouble xScale
, wxDouble yScale
);
209 virtual void Rotate( wxDouble angle
);
216 virtual void SetPen( const wxPen
&pen
);
218 // sets the brush for filling
219 virtual void SetBrush( const wxBrush
&brush
);
221 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
222 virtual void SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
223 const wxColour
&c1
, const wxColour
&c2
);
225 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
226 // with radius r and color cColor
227 virtual void SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
228 const wxColour
&oColor
, const wxColour
&cColor
);
231 virtual void SetFont( const wxFont
&font
);
233 // sets the text color
234 virtual void SetTextColor( const wxColour
&col
);
236 // strokes along a path with the current pen
237 virtual void StrokePath( const wxGraphicsPath
*path
);
239 // fills a path with the current brush
240 virtual void FillPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
242 // draws a path by first filling and then stroking
243 virtual void DrawPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
249 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
);
251 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
);
253 virtual void GetTextExtent( const wxString
&text
, wxDouble
*width
, wxDouble
*height
,
254 wxDouble
*descent
, wxDouble
*externalLeading
) const;
256 virtual void GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const;
262 virtual void DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
264 virtual void DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
266 CGContextRef
GetNativeContext();
267 void SetNativeContext( CGContextRef cg
);
268 CGPathDrawingMode
GetDrawingMode() const { return m_mode
; }
271 CGContextRef m_cgContext
;
273 CGPathDrawingMode m_mode
;
274 ATSUStyle m_macATSUIStyle
;
277 wxColor m_textForegroundColor
;
280 //-----------------------------------------------------------------------------
282 //-----------------------------------------------------------------------------
284 #if !defined( __DARWIN__ ) || defined(__MWERKS__)
286 const double M_PI
= 3.14159265358979;
290 const double RAD2DEG
= 180.0 / M_PI
;
291 const short kEmulatedMode
= -1;
292 const short kUnsupportedMode
= -2;
294 extern TECObjectRef s_TECNativeCToUnicode
;
296 //-----------------------------------------------------------------------------
298 //-----------------------------------------------------------------------------
300 static inline double dmin(double a
, double b
) { return a
< b
? a
: b
; }
301 static inline double dmax(double a
, double b
) { return a
> b
? a
: b
; }
302 static inline double DegToRad(double deg
) { return (deg
* M_PI
) / 180.0; }
304 //-----------------------------------------------------------------------------
305 // device context implementation
307 // more and more of the dc functionality should be implemented by calling
308 // the appropricate wxMacCoreGraphicsContext, but we will have to do that step by step
309 // also coordinate conversions should be moved to native matrix ops
310 //-----------------------------------------------------------------------------
312 // we always stock two context states, one at entry, to be able to preserve the
313 // state we were called with, the other one after changing to HI Graphics orientation
314 // (this one is used for getting back clippings etc)
316 //-----------------------------------------------------------------------------
317 // wxGraphicsPath implementation
318 //-----------------------------------------------------------------------------
320 //-----------------------------------------------------------------------------
321 // wxGraphicsContext implementation
322 //-----------------------------------------------------------------------------
324 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGrafPtr port
)
328 m_mode
= kCGPathFill
;
329 m_macATSUIStyle
= NULL
;
332 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGContextRef cgcontext
)
335 m_cgContext
= cgcontext
;
336 m_mode
= kCGPathFill
;
337 m_macATSUIStyle
= NULL
;
338 CGContextSaveGState( m_cgContext
);
339 CGContextSaveGState( m_cgContext
);
342 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext()
346 m_mode
= kCGPathFill
;
347 m_macATSUIStyle
= NULL
;
350 wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext()
354 CGContextSynchronize( m_cgContext
);
355 CGContextRestoreGState( m_cgContext
);
356 CGContextRestoreGState( m_cgContext
);
360 CGContextRelease( m_cgContext
);
363 void wxMacCoreGraphicsContext::Clip( const wxRegion
®ion
)
365 // ClipCGContextToRegion ( m_cgContext, &bounds , (RgnHandle) dc->m_macCurrentClipRgn );
368 void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath
*p
)
370 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
371 CGContextAddPath( m_cgContext
, path
->GetPath() );
372 CGContextStrokePath( m_cgContext
);
375 void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath
*p
, int fillStyle
)
377 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
378 CGPathDrawingMode mode
= m_mode
;
380 if ( fillStyle
== wxODDEVEN_RULE
)
382 if ( mode
== kCGPathFill
)
383 mode
= kCGPathEOFill
;
384 else if ( mode
== kCGPathFillStroke
)
385 mode
= kCGPathEOFillStroke
;
388 CGContextAddPath( m_cgContext
, path
->GetPath() );
389 CGContextDrawPath( m_cgContext
, mode
);
392 void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath
*p
, int fillStyle
)
394 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
396 CGContextAddPath( m_cgContext
, path
->GetPath() );
397 if ( fillStyle
== wxODDEVEN_RULE
)
398 CGContextEOFillPath( m_cgContext
);
400 CGContextFillPath( m_cgContext
);
403 wxGraphicsPath
* wxMacCoreGraphicsContext::CreatePath()
405 // make sure that we now have a real cgref, before doing
406 // anything with paths
407 CGContextRef cg
= GetNativeContext();
410 return new wxMacCoreGraphicsPath();
413 CGContextRef
wxMacCoreGraphicsContext::GetNativeContext()
418 void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg
)
420 // we allow either setting or clearing but not replacing
421 wxASSERT( m_cgContext
== NULL
|| cg
== NULL
);
424 CGContextSaveGState( cg
);
428 void wxMacCoreGraphicsContext::Translate( wxDouble dx
, wxDouble dy
)
430 CGContextTranslateCTM( m_cgContext
, dx
, dy
);
433 void wxMacCoreGraphicsContext::Scale( wxDouble xScale
, wxDouble yScale
)
435 CGContextScaleCTM( m_cgContext
, xScale
, yScale
);
438 void wxMacCoreGraphicsContext::Rotate( wxDouble angle
)
440 CGContextRotateCTM( m_cgContext
, angle
);
443 void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
445 CGImageRef image
= (CGImageRef
)( bmp
.CGImageCreate() );
446 HIRect r
= CGRectMake( x
, y
, w
, h
);
447 HIViewDrawCGImage( m_cgContext
, &r
, image
);
448 CGImageRelease( image
);
451 void wxMacCoreGraphicsContext::DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
453 CGRect r
= CGRectMake( 00 , 00 , w
, h
);
454 CGContextSaveGState( m_cgContext
);
455 CGContextTranslateCTM( m_cgContext
, x
, y
+ h
);
456 CGContextScaleCTM( m_cgContext
, 1, -1 );
457 PlotIconRefInContext( m_cgContext
, &r
, kAlignNone
, kTransformNone
,
458 NULL
, kPlotIconRefNormalFlags
, MAC_WXHICON( icon
.GetHICON() ) );
459 CGContextRestoreGState( m_cgContext
);
462 void wxMacCoreGraphicsContext::PushState()
464 CGContextSaveGState( m_cgContext
);
467 void wxMacCoreGraphicsContext::PopState()
469 CGContextRestoreGState( m_cgContext
);
472 void wxMacCoreGraphicsContext::SetTextColor( const wxColour
&col
)
474 m_textForegroundColor
= col
;
478 #pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes
480 // CGPattern wrapper class: always allocate on heap, never call destructor
482 class wxMacCoreGraphicsPattern
485 wxMacCoreGraphicsPattern() {}
487 // is guaranteed to be called only with a non-Null CGContextRef
488 virtual void Render( CGContextRef ctxRef
) = 0;
490 operator CGPatternRef() const { return m_patternRef
; }
493 virtual ~wxMacCoreGraphicsPattern()
495 // as this is called only when the m_patternRef is been released;
496 // don't release it again
499 static void _Render( void *info
, CGContextRef ctxRef
)
501 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
502 if ( self
&& ctxRef
)
503 self
->Render( ctxRef
);
506 static void _Dispose( void *info
)
508 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
512 CGPatternRef m_patternRef
;
514 static const CGPatternCallbacks ms_Callbacks
;
517 const CGPatternCallbacks
wxMacCoreGraphicsPattern::ms_Callbacks
= { 0, &wxMacCoreGraphicsPattern::_Render
, &wxMacCoreGraphicsPattern::_Dispose
};
519 class ImagePattern
: public wxMacCoreGraphicsPattern
522 ImagePattern( const wxBitmap
* bmp
, CGAffineTransform transform
)
524 wxASSERT( bmp
&& bmp
->Ok() );
526 Init( (CGImageRef
) bmp
->CGImageCreate() , transform
);
529 // ImagePattern takes ownership of CGImageRef passed in
530 ImagePattern( CGImageRef image
, CGAffineTransform transform
)
535 Init( image
, transform
);
538 virtual void Render( CGContextRef ctxRef
)
541 HIViewDrawCGImage( ctxRef
, &m_imageBounds
, m_image
);
545 void Init( CGImageRef image
, CGAffineTransform transform
)
550 m_imageBounds
= CGRectMake( 0.0, 0.0, (CGFloat
)CGImageGetWidth( m_image
), (CGFloat
)CGImageGetHeight( m_image
) );
551 m_patternRef
= CGPatternCreate(
552 this , m_imageBounds
, transform
,
553 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
554 kCGPatternTilingNoDistortion
, true , &wxMacCoreGraphicsPattern::ms_Callbacks
);
558 virtual ~ImagePattern()
561 CGImageRelease( m_image
);
565 CGRect m_imageBounds
;
568 class HatchPattern
: public wxMacCoreGraphicsPattern
571 HatchPattern( int hatchstyle
, CGAffineTransform transform
)
573 m_hatch
= hatchstyle
;
574 m_imageBounds
= CGRectMake( 0.0, 0.0, 8.0 , 8.0 );
575 m_patternRef
= CGPatternCreate(
576 this , m_imageBounds
, transform
,
577 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
578 kCGPatternTilingNoDistortion
, false , &wxMacCoreGraphicsPattern::ms_Callbacks
);
581 void StrokeLineSegments( CGContextRef ctxRef
, const CGPoint pts
[] , size_t count
)
583 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
584 if ( UMAGetSystemVersion() >= 0x1040 )
586 CGContextStrokeLineSegments( ctxRef
, pts
, count
);
591 CGContextBeginPath( ctxRef
);
592 for (size_t i
= 0; i
< count
; i
+= 2)
594 CGContextMoveToPoint(ctxRef
, pts
[i
].x
, pts
[i
].y
);
595 CGContextAddLineToPoint(ctxRef
, pts
[i
+1].x
, pts
[i
+1].y
);
597 CGContextStrokePath(ctxRef
);
601 virtual void Render( CGContextRef ctxRef
)
605 case wxBDIAGONAL_HATCH
:
609 { 8.0 , 0.0 } , { 0.0 , 8.0 }
611 StrokeLineSegments( ctxRef
, pts
, 2 );
615 case wxCROSSDIAG_HATCH
:
619 { 0.0 , 0.0 } , { 8.0 , 8.0 } ,
620 { 8.0 , 0.0 } , { 0.0 , 8.0 }
622 StrokeLineSegments( ctxRef
, pts
, 4 );
626 case wxFDIAGONAL_HATCH
:
630 { 0.0 , 0.0 } , { 8.0 , 8.0 }
632 StrokeLineSegments( ctxRef
, pts
, 2 );
640 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
641 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
643 StrokeLineSegments( ctxRef
, pts
, 4 );
647 case wxHORIZONTAL_HATCH
:
651 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
653 StrokeLineSegments( ctxRef
, pts
, 2 );
657 case wxVERTICAL_HATCH
:
661 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
663 StrokeLineSegments( ctxRef
, pts
, 2 );
673 virtual ~HatchPattern() {}
675 CGRect m_imageBounds
;
681 void wxMacCoreGraphicsContext::SetPen( const wxPen
&pen
)
684 if ( m_cgContext
== NULL
)
687 bool fill
= m_brush
.GetStyle() != wxTRANSPARENT
;
688 bool stroke
= pen
.GetStyle() != wxTRANSPARENT
;
691 // we can benchmark performance; should go into a setting eventually
692 CGContextSetShouldAntialias( m_cgContext
, false );
698 m_mode
= kCGPathFill
; // just a default
702 CGContextSetRGBStrokeColor( m_cgContext
, pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
703 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 );
705 // TODO: * m_dc->m_scaleX
706 CGFloat penWidth
= pen
.GetWidth();
709 CGContextSetLineWidth( m_cgContext
, penWidth
);
712 switch ( pen
.GetCap() )
715 cap
= kCGLineCapRound
;
718 case wxCAP_PROJECTING
:
719 cap
= kCGLineCapSquare
;
723 cap
= kCGLineCapButt
;
727 cap
= kCGLineCapButt
;
732 switch ( pen
.GetJoin() )
735 join
= kCGLineJoinBevel
;
739 join
= kCGLineJoinMiter
;
743 join
= kCGLineJoinRound
;
747 join
= kCGLineJoinMiter
;
751 m_mode
= kCGPathStroke
;
754 const CGFloat
*lengths
= NULL
;
755 CGFloat
*userLengths
= NULL
;
757 const CGFloat dashUnit
= penWidth
< 1.0 ? 1.0 : penWidth
;
759 const CGFloat dotted
[] = { dashUnit
, dashUnit
+ 2.0 };
760 const CGFloat short_dashed
[] = { 9.0 , 6.0 };
761 const CGFloat dashed
[] = { 19.0 , 9.0 };
762 const CGFloat dotted_dashed
[] = { 9.0 , 6.0 , 3.0 , 3.0 };
764 switch ( pen
.GetStyle() )
771 count
= WXSIZEOF(dotted
);
776 count
= WXSIZEOF(dashed
);
780 lengths
= short_dashed
;
781 count
= WXSIZEOF(short_dashed
);
785 lengths
= dotted_dashed
;
786 count
= WXSIZEOF(dotted_dashed
);
791 count
= pen
.GetDashes( &dashes
);
792 if ((dashes
!= NULL
) && (count
> 0))
794 userLengths
= new CGFloat
[count
];
795 for ( int i
= 0; i
< count
; ++i
)
797 userLengths
[i
] = dashes
[i
] * dashUnit
;
799 if ( i
% 2 == 1 && userLengths
[i
] < dashUnit
+ 2.0 )
800 userLengths
[i
] = dashUnit
+ 2.0;
801 else if ( i
% 2 == 0 && userLengths
[i
] < dashUnit
)
802 userLengths
[i
] = dashUnit
;
805 lengths
= userLengths
;
810 CGFloat alphaArray
[1] = { 1.0 };
811 wxBitmap
* bmp
= pen
.GetStipple();
812 if ( bmp
&& bmp
->Ok() )
814 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
815 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
816 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
817 CGContextSetStrokePattern( m_cgContext
, pattern
, alphaArray
);
824 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
825 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
826 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( pen
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
828 CGFloat colorArray
[4] = { pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
829 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 };
831 CGContextSetStrokePattern( m_cgContext
, pattern
, colorArray
);
836 if ((lengths
!= NULL
) && (count
> 0))
838 CGContextSetLineDash( m_cgContext
, 0 , lengths
, count
);
839 // force the line cap, otherwise we get artifacts (overlaps) and just solid lines
840 cap
= kCGLineCapButt
;
844 CGContextSetLineDash( m_cgContext
, 0 , NULL
, 0 );
847 CGContextSetLineCap( m_cgContext
, cap
);
848 CGContextSetLineJoin( m_cgContext
, join
);
850 delete[] userLengths
;
853 if ( fill
&& stroke
)
854 m_mode
= kCGPathFillStroke
;
858 void wxMacCoreGraphicsContext::SetBrush( const wxBrush
&brush
)
861 if ( m_cgContext
== NULL
)
864 bool fill
= brush
.GetStyle() != wxTRANSPARENT
;
865 bool stroke
= m_pen
.GetStyle() != wxTRANSPARENT
;
868 // we can benchmark performance, should go into a setting later
869 CGContextSetShouldAntialias( m_cgContext
, false );
875 m_mode
= kCGPathFill
; // just a default
879 if ( brush
.GetStyle() == wxSOLID
)
881 CGContextSetRGBFillColor( m_cgContext
, brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
882 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 );
884 else if ( brush
.IsHatch() )
886 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
887 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
888 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( brush
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
890 CGFloat colorArray
[4] = { brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
891 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 };
893 CGContextSetFillPattern( m_cgContext
, pattern
, colorArray
);
897 // now brush is a bitmap
898 CGFloat alphaArray
[1] = { 1.0 };
899 wxBitmap
* bmp
= brush
.GetStipple();
900 if ( bmp
&& bmp
->Ok() )
902 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
903 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
904 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
905 CGContextSetFillPattern( m_cgContext
, pattern
, alphaArray
);
909 m_mode
= kCGPathFill
;
912 if ( fill
&& stroke
)
913 m_mode
= kCGPathFillStroke
;
915 m_mode
= kCGPathStroke
;
919 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
920 void wxMacCoreGraphicsContext::SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
921 const wxColour
&c1
, const wxColour
&c2
)
925 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
926 // with radius r and color cColor
927 void wxMacCoreGraphicsContext::SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
928 const wxColour
&oColor
, const wxColour
&cColor
)
933 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
)
935 DrawText(str
, x
, y
, 0.0);
938 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, double angle
)
940 OSStatus status
= noErr
;
941 ATSUTextLayout atsuLayout
;
942 UniCharCount chars
= str
.length();
943 UniChar
* ubuf
= NULL
;
945 #if SIZEOF_WCHAR_T == 4
946 wxMBConvUTF16 converter
;
948 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
949 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
950 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
952 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
953 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
954 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
955 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
957 chars
= unicharlen
/ 2;
960 ubuf
= (UniChar
*) str
.wc_str();
962 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
963 chars
= wxWcslen( wchar
.data() );
964 ubuf
= (UniChar
*) wchar
.data();
968 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
969 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
971 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the rotated text") );
973 status
= ::ATSUSetTransientFontMatching( atsuLayout
, true );
974 wxASSERT_MSG( status
== noErr
, wxT("couldn't setup transient font matching") );
976 int iAngle
= int( angle
* RAD2DEG
);
977 if ( abs(iAngle
) > 0 )
979 Fixed atsuAngle
= IntToFixed( iAngle
);
980 ATSUAttributeTag atsuTags
[] =
982 kATSULineRotationTag
,
984 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
988 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
992 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
993 atsuTags
, atsuSizes
, atsuValues
);
997 ATSUAttributeTag atsuTags
[] =
1001 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1003 sizeof( CGContextRef
) ,
1005 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1009 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1010 atsuTags
, atsuSizes
, atsuValues
);
1013 ATSUTextMeasurement textBefore
, textAfter
;
1014 ATSUTextMeasurement ascent
, descent
;
1016 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1017 &textBefore
, &textAfter
, &ascent
, &descent
);
1019 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1024 if ( m_backgroundMode == wxSOLID )
1026 wxGraphicsPath* path = m_graphicContext->CreatePath();
1027 path->MoveToPoint( drawX , drawY );
1028 path->AddLineToPoint(
1029 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent)) ,
1030 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent)) );
1031 path->AddLineToPoint(
1032 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent ) + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1033 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent) - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1034 path->AddLineToPoint(
1035 (int) (drawX + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1036 (int) (drawY - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1038 m_graphicContext->FillPath( path , m_textBackgroundColour );
1042 x
+= (int)(sin(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1043 y
+= (int)(cos(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1045 status
= ::ATSUMeasureTextImage( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1046 IntToFixed(x
) , IntToFixed(y
) , &rect
);
1047 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1049 CGContextSaveGState(m_cgContext
);
1050 CGContextTranslateCTM(m_cgContext
, x
, y
);
1051 CGContextScaleCTM(m_cgContext
, 1, -1);
1052 status
= ::ATSUDrawText( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1053 IntToFixed(0) , IntToFixed(0) );
1055 wxASSERT_MSG( status
== noErr
, wxT("couldn't draw the rotated text") );
1057 CGContextRestoreGState(m_cgContext
);
1059 ::ATSUDisposeTextLayout(atsuLayout
);
1061 #if SIZEOF_WCHAR_T == 4
1066 void wxMacCoreGraphicsContext::GetTextExtent( const wxString
&str
, wxDouble
*width
, wxDouble
*height
,
1067 wxDouble
*descent
, wxDouble
*externalLeading
) const
1069 wxCHECK_RET( m_macATSUIStyle
!= NULL
, wxT("wxDC(cg)::DoGetTextExtent - no valid font set") );
1071 OSStatus status
= noErr
;
1073 ATSUTextLayout atsuLayout
;
1074 UniCharCount chars
= str
.length();
1075 UniChar
* ubuf
= NULL
;
1077 #if SIZEOF_WCHAR_T == 4
1078 wxMBConvUTF16 converter
;
1080 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
1081 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1082 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
1084 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1085 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1086 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1087 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1089 chars
= unicharlen
/ 2;
1092 ubuf
= (UniChar
*) str
.wc_str();
1094 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1095 chars
= wxWcslen( wchar
.data() );
1096 ubuf
= (UniChar
*) wchar
.data();
1100 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1101 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1103 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the text") );
1105 ATSUTextMeasurement textBefore
, textAfter
;
1106 ATSUTextMeasurement textAscent
, textDescent
;
1108 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1109 &textBefore
, &textAfter
, &textAscent
, &textDescent
);
1112 *height
= FixedToInt(textAscent
+ textDescent
);
1114 *descent
= FixedToInt(textDescent
);
1115 if ( externalLeading
)
1116 *externalLeading
= 0;
1118 *width
= FixedToInt(textAfter
- textBefore
);
1120 ::ATSUDisposeTextLayout(atsuLayout
);
1123 void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const
1126 widths
.Add(0, text
.length());
1131 ATSUTextLayout atsuLayout
;
1132 UniCharCount chars
= text
.length();
1133 UniChar
* ubuf
= NULL
;
1135 #if SIZEOF_WCHAR_T == 4
1136 wxMBConvUTF16 converter
;
1138 size_t unicharlen
= converter
.WC2MB( NULL
, text
.wc_str() , 0 );
1139 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1140 converter
.WC2MB( (char*) ubuf
, text
.wc_str(), unicharlen
+ 2 );
1142 const wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1143 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1144 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1145 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1147 chars
= unicharlen
/ 2;
1150 ubuf
= (UniChar
*) text
.wc_str();
1152 wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1153 chars
= wxWcslen( wchar
.data() );
1154 ubuf
= (UniChar
*) wchar
.data();
1159 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1160 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1162 for ( int pos
= 0; pos
< (int)chars
; pos
++ )
1164 unsigned long actualNumberOfBounds
= 0;
1165 ATSTrapezoid glyphBounds
;
1167 // We get a single bound, since the text should only require one. If it requires more, there is an issue
1169 result
= ATSUGetGlyphBounds( atsuLayout
, 0, 0, kATSUFromTextBeginning
, pos
+ 1,
1170 kATSUseDeviceOrigins
, 1, &glyphBounds
, &actualNumberOfBounds
);
1171 if (result
!= noErr
|| actualNumberOfBounds
!= 1 )
1174 widths
[pos
] = FixedToInt( glyphBounds
.upperRight
.x
- glyphBounds
.upperLeft
.x
);
1175 //unsigned char uch = s[i];
1178 ::ATSUDisposeTextLayout(atsuLayout
);
1181 void wxMacCoreGraphicsContext::SetFont( const wxFont
&font
)
1183 if ( m_macATSUIStyle
)
1185 ::ATSUDisposeStyle((ATSUStyle
)m_macATSUIStyle
);
1186 m_macATSUIStyle
= NULL
;
1193 status
= ATSUCreateAndCopyStyle( (ATSUStyle
) font
.MacGetATSUStyle() , (ATSUStyle
*) &m_macATSUIStyle
);
1195 wxASSERT_MSG( status
== noErr
, wxT("couldn't create ATSU style") );
1197 // we need the scale here ...
1199 Fixed atsuSize
= IntToFixed( int( /*m_scaleY*/ 1 * font
.MacGetFontSize()) );
1200 RGBColor atsuColor
= MAC_WXCOLORREF( m_textForegroundColor
.GetPixel() );
1201 ATSUAttributeTag atsuTags
[] =
1206 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1209 sizeof( RGBColor
) ,
1211 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1217 status
= ::ATSUSetAttributes(
1218 (ATSUStyle
)m_macATSUIStyle
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
) ,
1219 atsuTags
, atsuSizes
, atsuValues
);
1221 wxASSERT_MSG( status
== noErr
, wxT("couldn't modify ATSU style") );
1225 wxGraphicsContext
* wxGraphicsContext::Create( const wxWindowDC
&dc
)
1227 return new wxMacCoreGraphicsContext((CGContextRef
)dc
.GetWindow()->MacGetCGContextRef() );
1230 #endif // wxMAC_USE_CORE_GRAPHICS