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( CGContextRef cgcontext
);
175 wxMacCoreGraphicsContext( WindowRef window
);
177 wxMacCoreGraphicsContext();
179 ~wxMacCoreGraphicsContext();
183 // creates a path instance that corresponds to the type of graphics context, ie GDIPlus, cairo, CoreGraphics ...
184 virtual wxGraphicsPath
* CreatePath();
186 // push the current state of the context, ie the transformation matrix on a stack
187 virtual void PushState();
189 // pops a stored state from the stack
190 virtual void PopState();
192 // clips drawings to the region
193 virtual void Clip( const wxRegion
®ion
);
195 // clips drawings to the rect
196 virtual void Clip( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
198 // resets the clipping to original extent
199 virtual void ResetClip();
201 virtual void * GetNativeContext();
208 virtual void Translate( wxDouble dx
, wxDouble dy
);
211 virtual void Scale( wxDouble xScale
, wxDouble yScale
);
214 virtual void Rotate( wxDouble angle
);
221 virtual void SetPen( const wxPen
&pen
);
223 // sets the brush for filling
224 virtual void SetBrush( const wxBrush
&brush
);
226 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
227 virtual void SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
228 const wxColour
&c1
, const wxColour
&c2
);
230 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
231 // with radius r and color cColor
232 virtual void SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
233 const wxColour
&oColor
, const wxColour
&cColor
);
236 virtual void SetFont( const wxFont
&font
);
238 // sets the text color
239 virtual void SetTextColor( const wxColour
&col
);
241 // strokes along a path with the current pen
242 virtual void StrokePath( const wxGraphicsPath
*path
);
244 // fills a path with the current brush
245 virtual void FillPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
247 // draws a path by first filling and then stroking
248 virtual void DrawPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
254 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
);
256 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
);
258 virtual void GetTextExtent( const wxString
&text
, wxDouble
*width
, wxDouble
*height
,
259 wxDouble
*descent
, wxDouble
*externalLeading
) const;
261 virtual void GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const;
267 virtual void DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
269 virtual void DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
271 void SetNativeContext( CGContextRef cg
);
272 CGPathDrawingMode
GetDrawingMode() const { return m_mode
; }
275 CGContextRef m_cgContext
;
276 WindowRef m_windowRef
;
277 bool m_releaseContext
;
278 CGPathDrawingMode m_mode
;
279 ATSUStyle m_macATSUIStyle
;
283 wxColor m_textForegroundColor
;
286 //-----------------------------------------------------------------------------
288 //-----------------------------------------------------------------------------
290 #if !defined( __DARWIN__ ) || defined(__MWERKS__)
292 const double M_PI
= 3.14159265358979;
296 static const double RAD2DEG
= 180.0 / M_PI
;
298 //-----------------------------------------------------------------------------
299 // device context implementation
301 // more and more of the dc functionality should be implemented by calling
302 // the appropricate wxMacCoreGraphicsContext, but we will have to do that step by step
303 // also coordinate conversions should be moved to native matrix ops
304 //-----------------------------------------------------------------------------
306 // we always stock two context states, one at entry, to be able to preserve the
307 // state we were called with, the other one after changing to HI Graphics orientation
308 // (this one is used for getting back clippings etc)
310 //-----------------------------------------------------------------------------
311 // wxGraphicsPath implementation
312 //-----------------------------------------------------------------------------
314 //-----------------------------------------------------------------------------
315 // wxGraphicsContext implementation
316 //-----------------------------------------------------------------------------
318 void wxMacCoreGraphicsContext::Init()
321 m_mode
= kCGPathFill
;
322 m_macATSUIStyle
= NULL
;
323 m_releaseContext
= false;
326 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGContextRef cgcontext
)
329 m_cgContext
= cgcontext
;
330 CGContextSaveGState( m_cgContext
);
331 CGContextSaveGState( m_cgContext
);
334 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( WindowRef window
)
337 m_windowRef
= window
;
338 OSStatus status
= QDBeginCGContext( GetWindowPort( window
) , &m_cgContext
);
339 wxASSERT_MSG( status
== noErr
, wxT("Cannot nest wxDCs on the same window") );
341 GetWindowBounds( window
, kWindowContentRgn
, &bounds
);
342 CGContextSaveGState( m_cgContext
);
343 CGContextTranslateCTM( m_cgContext
, 0 , bounds
.bottom
- bounds
.top
);
344 CGContextScaleCTM( m_cgContext
, 1 , -1 );
345 CGContextSaveGState( m_cgContext
);
346 m_releaseContext
= true;
349 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext()
354 wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext()
358 CGContextSynchronize( m_cgContext
);
359 CGContextRestoreGState( m_cgContext
);
360 CGContextRestoreGState( m_cgContext
);
363 if ( m_releaseContext
)
364 QDEndCGContext( GetWindowPort( m_windowRef
) , &m_cgContext
);
367 void wxMacCoreGraphicsContext::Clip( const wxRegion
®ion
)
369 HIShapeRef shape
= HIShapeCreateWithQDRgn( (RgnHandle
) region
.GetWXHRGN() );
370 HIShapeReplacePathInCGContext( shape
, m_cgContext
);
371 CGContextClip( m_cgContext
);
375 // clips drawings to the rect
376 void wxMacCoreGraphicsContext::Clip( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
378 HIRect r
= CGRectMake( x
, y
, w
, h
);
379 CGContextClipToRect( m_cgContext
, r
);
382 // resets the clipping to original extent
383 void wxMacCoreGraphicsContext::ResetClip()
385 CGContextRestoreGState( m_cgContext
);
386 CGContextSaveGState( m_cgContext
);
389 void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath
*p
)
391 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
392 int width
= m_pen
.GetWidth();
395 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
398 bool offset
= ( width
% 2 ) == 1 ;
401 CGContextTranslateCTM( m_cgContext
, 0.5, 0.5 );
403 CGContextAddPath( m_cgContext
, path
->GetPath() );
404 CGContextStrokePath( m_cgContext
);
407 CGContextTranslateCTM( m_cgContext
, -0.5, -0.5 );
410 void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath
*p
, int fillStyle
)
412 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
413 CGPathDrawingMode mode
= m_mode
;
415 if ( fillStyle
== wxODDEVEN_RULE
)
417 if ( mode
== kCGPathFill
)
418 mode
= kCGPathEOFill
;
419 else if ( mode
== kCGPathFillStroke
)
420 mode
= kCGPathEOFillStroke
;
423 int width
= m_pen
.GetWidth();
426 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
429 bool offset
= ( width
% 2 ) == 1 ;
432 CGContextTranslateCTM( m_cgContext
, 0.5, 0.5 );
434 CGContextAddPath( m_cgContext
, path
->GetPath() );
435 CGContextDrawPath( m_cgContext
, mode
);
438 CGContextTranslateCTM( m_cgContext
, -0.5, -0.5 );
441 void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath
*p
, int fillStyle
)
443 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
445 CGContextAddPath( m_cgContext
, path
->GetPath() );
446 if ( fillStyle
== wxODDEVEN_RULE
)
447 CGContextEOFillPath( m_cgContext
);
449 CGContextFillPath( m_cgContext
);
452 wxGraphicsPath
* wxMacCoreGraphicsContext::CreatePath()
454 return new wxMacCoreGraphicsPath();
457 void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg
)
459 // we allow either setting or clearing but not replacing
460 wxASSERT( m_cgContext
== NULL
|| cg
== NULL
);
463 CGContextSaveGState( cg
);
467 void wxMacCoreGraphicsContext::Translate( wxDouble dx
, wxDouble dy
)
469 CGContextTranslateCTM( m_cgContext
, dx
, dy
);
472 void wxMacCoreGraphicsContext::Scale( wxDouble xScale
, wxDouble yScale
)
474 CGContextScaleCTM( m_cgContext
, xScale
, yScale
);
477 void wxMacCoreGraphicsContext::Rotate( wxDouble angle
)
479 CGContextRotateCTM( m_cgContext
, angle
);
482 void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
484 CGImageRef image
= (CGImageRef
)( bmp
.CGImageCreate() );
485 HIRect r
= CGRectMake( x
, y
, w
, h
);
486 HIViewDrawCGImage( m_cgContext
, &r
, image
);
487 CGImageRelease( image
);
490 void wxMacCoreGraphicsContext::DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
492 CGRect r
= CGRectMake( 00 , 00 , w
, h
);
493 CGContextSaveGState( m_cgContext
);
494 CGContextTranslateCTM( m_cgContext
, x
, y
+ h
);
495 CGContextScaleCTM( m_cgContext
, 1, -1 );
496 PlotIconRefInContext( m_cgContext
, &r
, kAlignNone
, kTransformNone
,
497 NULL
, kPlotIconRefNormalFlags
, MAC_WXHICON( icon
.GetHICON() ) );
498 CGContextRestoreGState( m_cgContext
);
501 void wxMacCoreGraphicsContext::PushState()
503 CGContextSaveGState( m_cgContext
);
506 void wxMacCoreGraphicsContext::PopState()
508 CGContextRestoreGState( m_cgContext
);
511 void wxMacCoreGraphicsContext::SetTextColor( const wxColour
&col
)
513 m_textForegroundColor
= col
;
514 // to recreate the native font after color change
519 #pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes
521 // CGPattern wrapper class: always allocate on heap, never call destructor
523 class wxMacCoreGraphicsPattern
526 wxMacCoreGraphicsPattern() {}
528 // is guaranteed to be called only with a non-Null CGContextRef
529 virtual void Render( CGContextRef ctxRef
) = 0;
531 operator CGPatternRef() const { return m_patternRef
; }
534 virtual ~wxMacCoreGraphicsPattern()
536 // as this is called only when the m_patternRef is been released;
537 // don't release it again
540 static void _Render( void *info
, CGContextRef ctxRef
)
542 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
543 if ( self
&& ctxRef
)
544 self
->Render( ctxRef
);
547 static void _Dispose( void *info
)
549 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
553 CGPatternRef m_patternRef
;
555 static const CGPatternCallbacks ms_Callbacks
;
558 const CGPatternCallbacks
wxMacCoreGraphicsPattern::ms_Callbacks
= { 0, &wxMacCoreGraphicsPattern::_Render
, &wxMacCoreGraphicsPattern::_Dispose
};
560 class ImagePattern
: public wxMacCoreGraphicsPattern
563 ImagePattern( const wxBitmap
* bmp
, const CGAffineTransform
& transform
)
565 wxASSERT( bmp
&& bmp
->Ok() );
567 Init( (CGImageRef
) bmp
->CGImageCreate() , transform
);
570 // ImagePattern takes ownership of CGImageRef passed in
571 ImagePattern( CGImageRef image
, const CGAffineTransform
& transform
)
576 Init( image
, transform
);
579 virtual void Render( CGContextRef ctxRef
)
582 HIViewDrawCGImage( ctxRef
, &m_imageBounds
, m_image
);
586 void Init( CGImageRef image
, const CGAffineTransform
& transform
)
591 m_imageBounds
= CGRectMake( 0.0, 0.0, (CGFloat
)CGImageGetWidth( m_image
), (CGFloat
)CGImageGetHeight( m_image
) );
592 m_patternRef
= CGPatternCreate(
593 this , m_imageBounds
, transform
,
594 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
595 kCGPatternTilingNoDistortion
, true , &wxMacCoreGraphicsPattern::ms_Callbacks
);
599 virtual ~ImagePattern()
602 CGImageRelease( m_image
);
606 CGRect m_imageBounds
;
609 class HatchPattern
: public wxMacCoreGraphicsPattern
612 HatchPattern( int hatchstyle
, const CGAffineTransform
& transform
)
614 m_hatch
= hatchstyle
;
615 m_imageBounds
= CGRectMake( 0.0, 0.0, 8.0 , 8.0 );
616 m_patternRef
= CGPatternCreate(
617 this , m_imageBounds
, transform
,
618 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
619 kCGPatternTilingNoDistortion
, false , &wxMacCoreGraphicsPattern::ms_Callbacks
);
622 void StrokeLineSegments( CGContextRef ctxRef
, const CGPoint pts
[] , size_t count
)
624 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
625 if ( UMAGetSystemVersion() >= 0x1040 )
627 CGContextStrokeLineSegments( ctxRef
, pts
, count
);
632 CGContextBeginPath( ctxRef
);
633 for (size_t i
= 0; i
< count
; i
+= 2)
635 CGContextMoveToPoint(ctxRef
, pts
[i
].x
, pts
[i
].y
);
636 CGContextAddLineToPoint(ctxRef
, pts
[i
+1].x
, pts
[i
+1].y
);
638 CGContextStrokePath(ctxRef
);
642 virtual void Render( CGContextRef ctxRef
)
646 case wxBDIAGONAL_HATCH
:
650 { 8.0 , 0.0 } , { 0.0 , 8.0 }
652 StrokeLineSegments( ctxRef
, pts
, 2 );
656 case wxCROSSDIAG_HATCH
:
660 { 0.0 , 0.0 } , { 8.0 , 8.0 } ,
661 { 8.0 , 0.0 } , { 0.0 , 8.0 }
663 StrokeLineSegments( ctxRef
, pts
, 4 );
667 case wxFDIAGONAL_HATCH
:
671 { 0.0 , 0.0 } , { 8.0 , 8.0 }
673 StrokeLineSegments( ctxRef
, pts
, 2 );
681 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
682 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
684 StrokeLineSegments( ctxRef
, pts
, 4 );
688 case wxHORIZONTAL_HATCH
:
692 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
694 StrokeLineSegments( ctxRef
, pts
, 2 );
698 case wxVERTICAL_HATCH
:
702 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
704 StrokeLineSegments( ctxRef
, pts
, 2 );
714 virtual ~HatchPattern() {}
716 CGRect m_imageBounds
;
722 void wxMacCoreGraphicsContext::SetPen( const wxPen
&pen
)
725 if ( m_cgContext
== NULL
)
728 bool fill
= m_brush
.GetStyle() != wxTRANSPARENT
;
729 bool stroke
= pen
.GetStyle() != wxTRANSPARENT
;
732 // we can benchmark performance; should go into a setting eventually
733 CGContextSetShouldAntialias( m_cgContext
, false );
736 if ( fill
|| stroke
)
739 m_mode
= kCGPathFill
; // just a default
743 CGContextSetRGBStrokeColor( m_cgContext
, pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
744 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 );
746 // TODO: * m_dc->m_scaleX
747 CGFloat penWidth
= pen
.GetWidth();
750 CGContextSetLineWidth( m_cgContext
, penWidth
);
753 switch ( pen
.GetCap() )
756 cap
= kCGLineCapRound
;
759 case wxCAP_PROJECTING
:
760 cap
= kCGLineCapSquare
;
764 cap
= kCGLineCapButt
;
768 cap
= kCGLineCapButt
;
773 switch ( pen
.GetJoin() )
776 join
= kCGLineJoinBevel
;
780 join
= kCGLineJoinMiter
;
784 join
= kCGLineJoinRound
;
788 join
= kCGLineJoinMiter
;
792 m_mode
= kCGPathStroke
;
795 const CGFloat
*lengths
= NULL
;
796 CGFloat
*userLengths
= NULL
;
798 const CGFloat dashUnit
= penWidth
< 1.0 ? 1.0 : penWidth
;
800 const CGFloat dotted
[] = { dashUnit
, dashUnit
+ 2.0 };
801 const CGFloat short_dashed
[] = { 9.0 , 6.0 };
802 const CGFloat dashed
[] = { 19.0 , 9.0 };
803 const CGFloat dotted_dashed
[] = { 9.0 , 6.0 , 3.0 , 3.0 };
805 switch ( pen
.GetStyle() )
812 count
= WXSIZEOF(dotted
);
817 count
= WXSIZEOF(dashed
);
821 lengths
= short_dashed
;
822 count
= WXSIZEOF(short_dashed
);
826 lengths
= dotted_dashed
;
827 count
= WXSIZEOF(dotted_dashed
);
832 count
= pen
.GetDashes( &dashes
);
833 if ((dashes
!= NULL
) && (count
> 0))
835 userLengths
= new CGFloat
[count
];
836 for ( int i
= 0; i
< count
; ++i
)
838 userLengths
[i
] = dashes
[i
] * dashUnit
;
840 if ( i
% 2 == 1 && userLengths
[i
] < dashUnit
+ 2.0 )
841 userLengths
[i
] = dashUnit
+ 2.0;
842 else if ( i
% 2 == 0 && userLengths
[i
] < dashUnit
)
843 userLengths
[i
] = dashUnit
;
846 lengths
= userLengths
;
851 CGFloat alphaArray
[1] = { 1.0 };
852 wxBitmap
* bmp
= pen
.GetStipple();
853 if ( bmp
&& bmp
->Ok() )
855 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
856 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
857 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
858 CGContextSetStrokePattern( m_cgContext
, pattern
, alphaArray
);
865 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
866 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
867 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( pen
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
869 CGFloat colorArray
[4] = { pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
870 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 };
872 CGContextSetStrokePattern( m_cgContext
, pattern
, colorArray
);
877 if ((lengths
!= NULL
) && (count
> 0))
879 CGContextSetLineDash( m_cgContext
, 0 , lengths
, count
);
880 // force the line cap, otherwise we get artifacts (overlaps) and just solid lines
881 cap
= kCGLineCapButt
;
885 CGContextSetLineDash( m_cgContext
, 0 , NULL
, 0 );
888 CGContextSetLineCap( m_cgContext
, cap
);
889 CGContextSetLineJoin( m_cgContext
, join
);
891 delete[] userLengths
;
894 if ( fill
&& stroke
)
895 m_mode
= kCGPathFillStroke
;
899 void wxMacCoreGraphicsContext::SetBrush( const wxBrush
&brush
)
902 if ( m_cgContext
== NULL
)
905 bool fill
= brush
.GetStyle() != wxTRANSPARENT
;
906 bool stroke
= m_pen
.GetStyle() != wxTRANSPARENT
;
909 // we can benchmark performance, should go into a setting later
910 CGContextSetShouldAntialias( m_cgContext
, false );
913 if ( fill
|| stroke
)
916 m_mode
= kCGPathFill
; // just a default
920 if ( brush
.GetStyle() == wxSOLID
)
922 CGContextSetRGBFillColor( m_cgContext
, brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
923 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 );
925 else if ( brush
.IsHatch() )
927 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
928 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
929 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( brush
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
931 CGFloat colorArray
[4] = { brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
932 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 };
934 CGContextSetFillPattern( m_cgContext
, pattern
, colorArray
);
938 // now brush is a bitmap
939 CGFloat alphaArray
[1] = { 1.0 };
940 wxBitmap
* bmp
= brush
.GetStipple();
941 if ( bmp
&& bmp
->Ok() )
943 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
944 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
945 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
946 CGContextSetFillPattern( m_cgContext
, pattern
, alphaArray
);
950 m_mode
= kCGPathFill
;
953 if ( fill
&& stroke
)
954 m_mode
= kCGPathFillStroke
;
956 m_mode
= kCGPathStroke
;
960 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
961 void wxMacCoreGraphicsContext::SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
962 const wxColour
&c1
, const wxColour
&c2
)
966 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
967 // with radius r and color cColor
968 void wxMacCoreGraphicsContext::SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
969 const wxColour
&oColor
, const wxColour
&cColor
)
974 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
)
976 DrawText(str
, x
, y
, 0.0);
979 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
)
981 OSStatus status
= noErr
;
982 ATSUTextLayout atsuLayout
;
983 UniCharCount chars
= str
.length();
984 UniChar
* ubuf
= NULL
;
986 #if SIZEOF_WCHAR_T == 4
987 wxMBConvUTF16 converter
;
989 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
990 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
991 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
993 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
994 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
995 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
996 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
998 chars
= unicharlen
/ 2;
1001 ubuf
= (UniChar
*) str
.wc_str();
1003 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1004 chars
= wxWcslen( wchar
.data() );
1005 ubuf
= (UniChar
*) wchar
.data();
1009 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1010 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1012 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the rotated text") );
1014 status
= ::ATSUSetTransientFontMatching( atsuLayout
, true );
1015 wxASSERT_MSG( status
== noErr
, wxT("couldn't setup transient font matching") );
1017 int iAngle
= int( angle
* RAD2DEG
);
1018 if ( abs(iAngle
) > 0 )
1020 Fixed atsuAngle
= IntToFixed( iAngle
);
1021 ATSUAttributeTag atsuTags
[] =
1023 kATSULineRotationTag
,
1025 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1029 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1033 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1034 atsuTags
, atsuSizes
, atsuValues
);
1038 ATSUAttributeTag atsuTags
[] =
1042 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1044 sizeof( CGContextRef
) ,
1046 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1050 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1051 atsuTags
, atsuSizes
, atsuValues
);
1054 ATSUTextMeasurement textBefore
, textAfter
;
1055 ATSUTextMeasurement ascent
, descent
;
1057 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1058 &textBefore
, &textAfter
, &ascent
, &descent
);
1060 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1065 if ( m_backgroundMode == wxSOLID )
1067 wxGraphicsPath* path = m_graphicContext->CreatePath();
1068 path->MoveToPoint( drawX , drawY );
1069 path->AddLineToPoint(
1070 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent)) ,
1071 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent)) );
1072 path->AddLineToPoint(
1073 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent ) + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1074 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent) - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1075 path->AddLineToPoint(
1076 (int) (drawX + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1077 (int) (drawY - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1079 m_graphicContext->FillPath( path , m_textBackgroundColour );
1083 x
+= (int)(sin(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1084 y
+= (int)(cos(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1086 status
= ::ATSUMeasureTextImage( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1087 IntToFixed(x
) , IntToFixed(y
) , &rect
);
1088 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1090 CGContextSaveGState(m_cgContext
);
1091 CGContextTranslateCTM(m_cgContext
, x
, y
);
1092 CGContextScaleCTM(m_cgContext
, 1, -1);
1093 status
= ::ATSUDrawText( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1094 IntToFixed(0) , IntToFixed(0) );
1096 wxASSERT_MSG( status
== noErr
, wxT("couldn't draw the rotated text") );
1098 CGContextRestoreGState(m_cgContext
);
1100 ::ATSUDisposeTextLayout(atsuLayout
);
1102 #if SIZEOF_WCHAR_T == 4
1107 void wxMacCoreGraphicsContext::GetTextExtent( const wxString
&str
, wxDouble
*width
, wxDouble
*height
,
1108 wxDouble
*descent
, wxDouble
*externalLeading
) const
1110 wxCHECK_RET( m_macATSUIStyle
!= NULL
, wxT("wxDC(cg)::DoGetTextExtent - no valid font set") );
1112 OSStatus status
= noErr
;
1114 ATSUTextLayout atsuLayout
;
1115 UniCharCount chars
= str
.length();
1116 UniChar
* ubuf
= NULL
;
1118 #if SIZEOF_WCHAR_T == 4
1119 wxMBConvUTF16 converter
;
1121 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
1122 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1123 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
1125 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1126 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1127 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1128 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1130 chars
= unicharlen
/ 2;
1133 ubuf
= (UniChar
*) str
.wc_str();
1135 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1136 chars
= wxWcslen( wchar
.data() );
1137 ubuf
= (UniChar
*) wchar
.data();
1141 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1142 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1144 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the text") );
1146 ATSUTextMeasurement textBefore
, textAfter
;
1147 ATSUTextMeasurement textAscent
, textDescent
;
1149 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1150 &textBefore
, &textAfter
, &textAscent
, &textDescent
);
1153 *height
= FixedToInt(textAscent
+ textDescent
);
1155 *descent
= FixedToInt(textDescent
);
1156 if ( externalLeading
)
1157 *externalLeading
= 0;
1159 *width
= FixedToInt(textAfter
- textBefore
);
1161 ::ATSUDisposeTextLayout(atsuLayout
);
1164 void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const
1167 widths
.Add(0, text
.length());
1172 ATSUTextLayout atsuLayout
;
1173 UniCharCount chars
= text
.length();
1174 UniChar
* ubuf
= NULL
;
1176 #if SIZEOF_WCHAR_T == 4
1177 wxMBConvUTF16 converter
;
1179 size_t unicharlen
= converter
.WC2MB( NULL
, text
.wc_str() , 0 );
1180 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1181 converter
.WC2MB( (char*) ubuf
, text
.wc_str(), unicharlen
+ 2 );
1183 const wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1184 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1185 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1186 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1188 chars
= unicharlen
/ 2;
1191 ubuf
= (UniChar
*) text
.wc_str();
1193 wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1194 chars
= wxWcslen( wchar
.data() );
1195 ubuf
= (UniChar
*) wchar
.data();
1199 ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1200 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1202 for ( int pos
= 0; pos
< (int)chars
; pos
++ )
1204 unsigned long actualNumberOfBounds
= 0;
1205 ATSTrapezoid glyphBounds
;
1207 // We get a single bound, since the text should only require one. If it requires more, there is an issue
1209 result
= ATSUGetGlyphBounds( atsuLayout
, 0, 0, kATSUFromTextBeginning
, pos
+ 1,
1210 kATSUseDeviceOrigins
, 1, &glyphBounds
, &actualNumberOfBounds
);
1211 if (result
!= noErr
|| actualNumberOfBounds
!= 1 )
1214 widths
[pos
] = FixedToInt( glyphBounds
.upperRight
.x
- glyphBounds
.upperLeft
.x
);
1215 //unsigned char uch = s[i];
1218 ::ATSUDisposeTextLayout(atsuLayout
);
1221 void wxMacCoreGraphicsContext::SetFont( const wxFont
&font
)
1223 if ( m_macATSUIStyle
)
1225 ::ATSUDisposeStyle((ATSUStyle
)m_macATSUIStyle
);
1226 m_macATSUIStyle
= NULL
;
1234 status
= ATSUCreateAndCopyStyle( (ATSUStyle
) font
.MacGetATSUStyle() , (ATSUStyle
*) &m_macATSUIStyle
);
1236 wxASSERT_MSG( status
== noErr
, wxT("couldn't create ATSU style") );
1238 // we need the scale here ...
1240 Fixed atsuSize
= IntToFixed( int( /*m_scaleY*/ 1 * font
.MacGetFontSize()) );
1241 RGBColor atsuColor
= MAC_WXCOLORREF( m_textForegroundColor
.GetPixel() );
1242 ATSUAttributeTag atsuTags
[] =
1247 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1250 sizeof( RGBColor
) ,
1252 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1258 status
= ::ATSUSetAttributes(
1259 (ATSUStyle
)m_macATSUIStyle
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
) ,
1260 atsuTags
, atsuSizes
, atsuValues
);
1262 wxASSERT_MSG( status
== noErr
, wxT("couldn't modify ATSU style") );
1266 void * wxMacCoreGraphicsContext::GetNativeContext()
1271 wxGraphicsContext
* wxGraphicsContext::Create( const wxWindowDC
&dc
)
1273 return new wxMacCoreGraphicsContext((CGContextRef
)dc
.GetWindow()->MacGetCGContextRef() );
1276 wxGraphicsContext
* wxGraphicsContext::Create( wxWindow
* window
)
1278 wxGraphicsContext
* ctx
= new wxMacCoreGraphicsContext( (WindowRef
) window
->MacGetTopLevelWindowRef() );
1279 CGContextRef cg
= (CGContextRef
) ctx
->GetNativeContext() ;
1280 CGContextRestoreGState( cg
);
1283 window
->MacWindowToRootWindow( &x
, &y
);
1284 CGContextTranslateCTM( cg
, x
, y
);
1285 CGContextSaveGState( cg
);
1290 wxGraphicsContext
* wxGraphicsContext::CreateFromNative( void * context
)
1292 return new wxMacCoreGraphicsContext((CGContextRef
)context
);
1295 wxGraphicsContext
* wxGraphicsContext::CreateFromNativeWindow( void * window
)
1297 return new wxMacCoreGraphicsContext((WindowRef
)window
);
1300 #endif // wxMAC_USE_CORE_GRAPHICS