1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/carbon/dccg.cpp
4 // Author: Stefan Csomor
8 // Copyright: (c) Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
14 #if wxUSE_GRAPHICS_CONTEXT && wxMAC_USE_CORE_GRAPHICS
16 #include "wx/graphics.h"
19 #include "wx/dcclient.h"
21 #include "wx/region.h"
24 #include "wx/mac/uma.h"
29 // in case our functions were defined outside std, we make it known all the same
35 #include "wx/mac/private.h"
37 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
38 typedef float CGFloat
;
41 //-----------------------------------------------------------------------------
43 //-----------------------------------------------------------------------------
45 #if !defined( __DARWIN__ ) || defined(__MWERKS__)
47 const double M_PI
= 3.14159265358979;
51 static const double RAD2DEG
= 180.0 / M_PI
;
57 class WXDLLEXPORT wxMacCoreGraphicsPath
: public wxGraphicsPath
60 wxMacCoreGraphicsPath();
61 ~wxMacCoreGraphicsPath();
63 // begins a new subpath at (x,y)
64 virtual void MoveToPoint( wxDouble x
, wxDouble y
);
66 // adds a straight line from the current point to (x,y)
67 virtual void AddLineToPoint( wxDouble x
, wxDouble y
);
69 // adds a cubic Bezier curve from the current point, using two control points and an end point
70 virtual void AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
);
72 // closes the current sub-path
73 virtual void CloseSubpath();
75 // gets the last point of the current path, (0,0) if not yet set
76 virtual void GetCurrentPoint( wxDouble
& x
, wxDouble
&y
);
78 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
79 virtual void AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
);
82 // These are convenience functions which - if not available natively will be assembled
83 // using the primitives from above
86 // adds a quadratic Bezier curve from the current point, using a control point and an end point
87 virtual void AddQuadCurveToPoint( wxDouble cx
, wxDouble cy
, wxDouble x
, wxDouble y
);
89 // appends a rectangle as a new closed subpath
90 virtual void AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
92 // appends an ellipsis as a new closed subpath fitting the passed rectangle
93 virtual void AddCircle( wxDouble x
, wxDouble y
, wxDouble r
);
95 // draws a an arc to two tangents connecting (current) to (x1,y1) and (x1,y1) to (x2,y2), also a straight line from (current) to (x1,y1)
96 virtual void AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
);
98 // returns the native path
99 virtual void * GetNativePath() const { return m_path
; }
101 // give the native path returned by GetNativePath() back (there might be some deallocations necessary)
102 virtual void UnGetNativePath(void *p
) {}
104 DECLARE_DYNAMIC_CLASS(wxMacCoreGraphicsPath
)
105 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsPath
)
107 CGMutablePathRef m_path
;
110 //-----------------------------------------------------------------------------
111 // wxGraphicsPath implementation
112 //-----------------------------------------------------------------------------
114 IMPLEMENT_DYNAMIC_CLASS(wxMacCoreGraphicsPath
, wxGraphicsPath
)
116 wxMacCoreGraphicsPath::wxMacCoreGraphicsPath()
118 m_path
= CGPathCreateMutable();
121 wxMacCoreGraphicsPath::~wxMacCoreGraphicsPath()
123 CGPathRelease( m_path
);
126 // opens (starts) a new subpath
127 void wxMacCoreGraphicsPath::MoveToPoint( wxDouble x1
, wxDouble y1
)
129 CGPathMoveToPoint( m_path
, NULL
, x1
, y1
);
132 void wxMacCoreGraphicsPath::AddLineToPoint( wxDouble x1
, wxDouble y1
)
134 CGPathAddLineToPoint( m_path
, NULL
, x1
, y1
);
137 void wxMacCoreGraphicsPath::AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
)
139 CGPathAddCurveToPoint( m_path
, NULL
, cx1
, cy1
, cx2
, cy2
, x
, y
);
142 void wxMacCoreGraphicsPath::AddQuadCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble x
, wxDouble y
)
144 CGPathAddQuadCurveToPoint( m_path
, NULL
, cx1
, cy1
, x
, y
);
147 void wxMacCoreGraphicsPath::AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
149 CGRect cgRect
= { { x
, y
} , { w
, h
} };
150 CGPathAddRect( m_path
, NULL
, cgRect
);
153 void wxMacCoreGraphicsPath::AddCircle( wxDouble x
, wxDouble y
, wxDouble r
)
155 CGPathAddArc( m_path
, NULL
, x
, y
, r
, 0.0 , 2 * M_PI
, true );
158 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
159 void wxMacCoreGraphicsPath::AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
)
161 // inverse direction as we the 'normal' state is a y axis pointing down, ie mirrored to the standard core graphics setup
162 CGPathAddArc( m_path
, NULL
, x
, y
, r
, startAngle
, endAngle
, !clockwise
);
165 void wxMacCoreGraphicsPath::AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
)
167 CGPathAddArcToPoint( m_path
, NULL
, x1
, y1
, x2
, y2
, r
);
170 // closes the current subpath
171 void wxMacCoreGraphicsPath::CloseSubpath()
173 CGPathCloseSubpath( m_path
);
176 // gets the last point of the current path, (0,0) if not yet set
177 void wxMacCoreGraphicsPath::GetCurrentPoint( wxDouble
& x
, wxDouble
&y
)
179 CGPoint p
= CGPathGetCurrentPoint( m_path
);
188 class WXDLLEXPORT wxMacCoreGraphicsContext
: public wxGraphicsContext
191 wxMacCoreGraphicsContext( CGContextRef cgcontext
);
193 wxMacCoreGraphicsContext( WindowRef window
);
195 wxMacCoreGraphicsContext( wxWindow
* window
);
197 wxMacCoreGraphicsContext();
199 ~wxMacCoreGraphicsContext();
203 // creates a path instance that corresponds to the type of graphics context, ie GDIPlus, cairo, CoreGraphics ...
204 virtual wxGraphicsPath
* CreatePath();
206 // push the current state of the context, ie the transformation matrix on a stack
207 virtual void PushState();
209 // pops a stored state from the stack
210 virtual void PopState();
212 // clips drawings to the region
213 virtual void Clip( const wxRegion
®ion
);
215 // clips drawings to the rect
216 virtual void Clip( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
218 // resets the clipping to original extent
219 virtual void ResetClip();
221 virtual void * GetNativeContext();
228 virtual void Translate( wxDouble dx
, wxDouble dy
);
231 virtual void Scale( wxDouble xScale
, wxDouble yScale
);
234 virtual void Rotate( wxDouble angle
);
241 virtual void SetPen( const wxPen
&pen
);
243 // sets the brush for filling
244 virtual void SetBrush( const wxBrush
&brush
);
246 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
247 virtual void SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
248 const wxColour
&c1
, const wxColour
&c2
);
250 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
251 // with radius r and color cColor
252 virtual void SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
253 const wxColour
&oColor
, const wxColour
&cColor
);
256 virtual void SetFont( const wxFont
&font
);
258 // sets the text color
259 virtual void SetTextColour( const wxColour
&col
);
261 // strokes along a path with the current pen
262 virtual void StrokePath( const wxGraphicsPath
*path
);
264 // fills a path with the current brush
265 virtual void FillPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
267 // draws a path by first filling and then stroking
268 virtual void DrawPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
274 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
);
276 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
);
278 virtual void GetTextExtent( const wxString
&text
, wxDouble
*width
, wxDouble
*height
,
279 wxDouble
*descent
, wxDouble
*externalLeading
) const;
281 virtual void GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const;
287 virtual void DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
289 virtual void DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
291 void SetNativeContext( CGContextRef cg
);
292 CGPathDrawingMode
GetDrawingMode() const { return m_mode
; }
295 virtual bool ShouldOffset() const
297 int penwidth
= m_pen
.GetWidth();
300 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
302 return ( penwidth
% 2 ) == 1;
306 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext
)
307 DECLARE_DYNAMIC_CLASS(wxMacCoreGraphicsContext
)
310 void EnsureIsValid();
312 CGContextRef m_cgContext
;
313 WindowRef m_windowRef
;
316 wxMacCFRefHolder
<HIShapeRef
> m_clipRgn
;
317 bool m_releaseContext
;
318 CGPathDrawingMode m_mode
;
319 ATSUStyle m_macATSUIStyle
;
323 wxColor m_textForegroundColor
;
326 //-----------------------------------------------------------------------------
327 // device context implementation
329 // more and more of the dc functionality should be implemented by calling
330 // the appropricate wxMacCoreGraphicsContext, but we will have to do that step by step
331 // also coordinate conversions should be moved to native matrix ops
332 //-----------------------------------------------------------------------------
334 // we always stock two context states, one at entry, to be able to preserve the
335 // state we were called with, the other one after changing to HI Graphics orientation
336 // (this one is used for getting back clippings etc)
338 //-----------------------------------------------------------------------------
339 // wxGraphicsContext implementation
340 //-----------------------------------------------------------------------------
342 IMPLEMENT_DYNAMIC_CLASS(wxMacCoreGraphicsContext
, wxGraphicsContext
)
344 void wxMacCoreGraphicsContext::Init()
347 m_mode
= kCGPathFill
;
348 m_macATSUIStyle
= NULL
;
349 m_releaseContext
= false;
350 HIRect r
= CGRectMake(0,0,0,0);
351 m_clipRgn
.Set(HIShapeCreateWithRect(&r
));
354 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGContextRef cgcontext
)
357 m_cgContext
= cgcontext
;
358 // FIXME: This check is needed because currently we need to use a DC/GraphicsContext
359 // in order to get font properties, like wxFont::GetPixelSize, but since we don't have
360 // a native window attached to use, I create a wxGraphicsContext with a NULL CGContextRef
361 // for this one operation.
363 // When wxFont::GetPixelSize on Mac no longer needs a graphics context, this check
367 CGContextSaveGState( m_cgContext
);
368 CGContextSaveGState( m_cgContext
);
372 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( WindowRef window
)
375 m_windowRef
= window
;
378 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( wxWindow
* window
)
381 m_windowRef
= (WindowRef
) window
->MacGetTopLevelWindowRef();
382 m_originX
= m_originY
= 0;
383 window
->MacWindowToRootWindow( &m_originX
, &m_originY
);
386 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext()
391 wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext()
395 // TODO : when is this necessary - should we add a Flush() method ? CGContextSynchronize( m_cgContext );
396 CGContextRestoreGState( m_cgContext
);
397 CGContextRestoreGState( m_cgContext
);
400 if ( m_releaseContext
)
401 QDEndCGContext( GetWindowPort( m_windowRef
) , &m_cgContext
);
404 void wxMacCoreGraphicsContext::EnsureIsValid()
408 OSStatus status
= QDBeginCGContext( GetWindowPort( m_windowRef
) , &m_cgContext
);
409 wxASSERT_MSG( status
== noErr
, wxT("Cannot nest wxDCs on the same window") );
411 GetWindowBounds( m_windowRef
, kWindowContentRgn
, &bounds
);
412 CGContextSaveGState( m_cgContext
);
413 CGContextTranslateCTM( m_cgContext
, 0 , bounds
.bottom
- bounds
.top
);
414 CGContextScaleCTM( m_cgContext
, 1 , -1 );
415 CGContextTranslateCTM( m_cgContext
, m_originX
, m_originY
);
416 CGContextSaveGState( m_cgContext
);
417 m_releaseContext
= true;
418 if ( !HIShapeIsEmpty(m_clipRgn
) )
420 HIShapeReplacePathInCGContext( m_clipRgn
, m_cgContext
);
421 CGContextClip( m_cgContext
);
427 void wxMacCoreGraphicsContext::Clip( const wxRegion
®ion
)
431 HIShapeRef shape
= HIShapeCreateWithQDRgn( (RgnHandle
) region
.GetWXHRGN() );
432 HIShapeReplacePathInCGContext( shape
, m_cgContext
);
433 CGContextClip( m_cgContext
);
438 m_clipRgn
.Set(HIShapeCreateWithQDRgn( (RgnHandle
) region
.GetWXHRGN() ));
442 // clips drawings to the rect
443 void wxMacCoreGraphicsContext::Clip( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
445 HIRect r
= CGRectMake( x
, y
, w
, h
);
448 CGContextClipToRect( m_cgContext
, r
);
452 m_clipRgn
.Set(HIShapeCreateWithRect(&r
));
456 // resets the clipping to original extent
457 void wxMacCoreGraphicsContext::ResetClip()
461 CGContextRestoreGState( m_cgContext
);
462 CGContextSaveGState( m_cgContext
);
466 HIRect r
= CGRectMake(0,0,0,0);
467 m_clipRgn
.Set(HIShapeCreateWithRect(&r
));
471 void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath
*path
)
475 bool offset
= ShouldOffset();
478 CGContextTranslateCTM( m_cgContext
, 0.5, 0.5 );
480 CGContextAddPath( m_cgContext
, (CGPathRef
) path
->GetNativePath() );
481 CGContextStrokePath( m_cgContext
);
484 CGContextTranslateCTM( m_cgContext
, -0.5, -0.5 );
487 void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath
*path
, int fillStyle
)
491 CGPathDrawingMode mode
= m_mode
;
493 if ( fillStyle
== wxODDEVEN_RULE
)
495 if ( mode
== kCGPathFill
)
496 mode
= kCGPathEOFill
;
497 else if ( mode
== kCGPathFillStroke
)
498 mode
= kCGPathEOFillStroke
;
501 bool offset
= ShouldOffset();
504 CGContextTranslateCTM( m_cgContext
, 0.5, 0.5 );
506 CGContextAddPath( m_cgContext
, (CGPathRef
) path
->GetNativePath() );
507 CGContextDrawPath( m_cgContext
, mode
);
510 CGContextTranslateCTM( m_cgContext
, -0.5, -0.5 );
513 void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath
*path
, int fillStyle
)
517 CGContextAddPath( m_cgContext
, (CGPathRef
) path
->GetNativePath() );
518 if ( fillStyle
== wxODDEVEN_RULE
)
519 CGContextEOFillPath( m_cgContext
);
521 CGContextFillPath( m_cgContext
);
524 wxGraphicsPath
* wxMacCoreGraphicsContext::CreatePath()
526 return new wxMacCoreGraphicsPath();
529 void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg
)
531 // we allow either setting or clearing but not replacing
532 wxASSERT( m_cgContext
== NULL
|| cg
== NULL
);
535 CGContextSaveGState( cg
);
539 void wxMacCoreGraphicsContext::Translate( wxDouble dx
, wxDouble dy
)
543 CGContextTranslateCTM( m_cgContext
, dx
, dy
);
546 void wxMacCoreGraphicsContext::Scale( wxDouble xScale
, wxDouble yScale
)
550 CGContextScaleCTM( m_cgContext
, xScale
, yScale
);
553 void wxMacCoreGraphicsContext::Rotate( wxDouble angle
)
557 CGContextRotateCTM( m_cgContext
, angle
);
560 void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
564 CGImageRef image
= (CGImageRef
)( bmp
.CGImageCreate() );
565 HIRect r
= CGRectMake( x
, y
, w
, h
);
566 HIViewDrawCGImage( m_cgContext
, &r
, image
);
567 CGImageRelease( image
);
570 void wxMacCoreGraphicsContext::DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
574 CGRect r
= CGRectMake( 00 , 00 , w
, h
);
575 CGContextSaveGState( m_cgContext
);
576 CGContextTranslateCTM( m_cgContext
, x
, y
+ h
);
577 CGContextScaleCTM( m_cgContext
, 1, -1 );
578 PlotIconRefInContext( m_cgContext
, &r
, kAlignNone
, kTransformNone
,
579 NULL
, kPlotIconRefNormalFlags
, MAC_WXHICON( icon
.GetHICON() ) );
580 CGContextRestoreGState( m_cgContext
);
583 void wxMacCoreGraphicsContext::PushState()
587 CGContextSaveGState( m_cgContext
);
590 void wxMacCoreGraphicsContext::PopState()
594 CGContextRestoreGState( m_cgContext
);
597 void wxMacCoreGraphicsContext::SetTextColour( const wxColour
&col
)
599 m_textForegroundColor
= col
;
600 // to recreate the native font after color change
605 #pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes
607 // CGPattern wrapper class: always allocate on heap, never call destructor
609 class wxMacCoreGraphicsPattern
612 wxMacCoreGraphicsPattern() {}
614 // is guaranteed to be called only with a non-Null CGContextRef
615 virtual void Render( CGContextRef ctxRef
) = 0;
617 operator CGPatternRef() const { return m_patternRef
; }
620 virtual ~wxMacCoreGraphicsPattern()
622 // as this is called only when the m_patternRef is been released;
623 // don't release it again
626 static void _Render( void *info
, CGContextRef ctxRef
)
628 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
629 if ( self
&& ctxRef
)
630 self
->Render( ctxRef
);
633 static void _Dispose( void *info
)
635 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
639 CGPatternRef m_patternRef
;
641 static const CGPatternCallbacks ms_Callbacks
;
644 const CGPatternCallbacks
wxMacCoreGraphicsPattern::ms_Callbacks
= { 0, &wxMacCoreGraphicsPattern::_Render
, &wxMacCoreGraphicsPattern::_Dispose
};
646 class ImagePattern
: public wxMacCoreGraphicsPattern
649 ImagePattern( const wxBitmap
* bmp
, const CGAffineTransform
& transform
)
651 wxASSERT( bmp
&& bmp
->Ok() );
653 Init( (CGImageRef
) bmp
->CGImageCreate() , transform
);
656 // ImagePattern takes ownership of CGImageRef passed in
657 ImagePattern( CGImageRef image
, const CGAffineTransform
& transform
)
662 Init( image
, transform
);
665 virtual void Render( CGContextRef ctxRef
)
668 HIViewDrawCGImage( ctxRef
, &m_imageBounds
, m_image
);
672 void Init( CGImageRef image
, const CGAffineTransform
& transform
)
677 m_imageBounds
= CGRectMake( 0.0, 0.0, (CGFloat
)CGImageGetWidth( m_image
), (CGFloat
)CGImageGetHeight( m_image
) );
678 m_patternRef
= CGPatternCreate(
679 this , m_imageBounds
, transform
,
680 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
681 kCGPatternTilingNoDistortion
, true , &wxMacCoreGraphicsPattern::ms_Callbacks
);
685 virtual ~ImagePattern()
688 CGImageRelease( m_image
);
692 CGRect m_imageBounds
;
695 class HatchPattern
: public wxMacCoreGraphicsPattern
698 HatchPattern( int hatchstyle
, const CGAffineTransform
& transform
)
700 m_hatch
= hatchstyle
;
701 m_imageBounds
= CGRectMake( 0.0, 0.0, 8.0 , 8.0 );
702 m_patternRef
= CGPatternCreate(
703 this , m_imageBounds
, transform
,
704 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
705 kCGPatternTilingNoDistortion
, false , &wxMacCoreGraphicsPattern::ms_Callbacks
);
708 void StrokeLineSegments( CGContextRef ctxRef
, const CGPoint pts
[] , size_t count
)
710 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
711 if ( UMAGetSystemVersion() >= 0x1040 )
713 CGContextStrokeLineSegments( ctxRef
, pts
, count
);
718 CGContextBeginPath( ctxRef
);
719 for (size_t i
= 0; i
< count
; i
+= 2)
721 CGContextMoveToPoint(ctxRef
, pts
[i
].x
, pts
[i
].y
);
722 CGContextAddLineToPoint(ctxRef
, pts
[i
+1].x
, pts
[i
+1].y
);
724 CGContextStrokePath(ctxRef
);
728 virtual void Render( CGContextRef ctxRef
)
732 case wxBDIAGONAL_HATCH
:
736 { 8.0 , 0.0 } , { 0.0 , 8.0 }
738 StrokeLineSegments( ctxRef
, pts
, 2 );
742 case wxCROSSDIAG_HATCH
:
746 { 0.0 , 0.0 } , { 8.0 , 8.0 } ,
747 { 8.0 , 0.0 } , { 0.0 , 8.0 }
749 StrokeLineSegments( ctxRef
, pts
, 4 );
753 case wxFDIAGONAL_HATCH
:
757 { 0.0 , 0.0 } , { 8.0 , 8.0 }
759 StrokeLineSegments( ctxRef
, pts
, 2 );
767 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
768 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
770 StrokeLineSegments( ctxRef
, pts
, 4 );
774 case wxHORIZONTAL_HATCH
:
778 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
780 StrokeLineSegments( ctxRef
, pts
, 2 );
784 case wxVERTICAL_HATCH
:
788 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
790 StrokeLineSegments( ctxRef
, pts
, 2 );
800 virtual ~HatchPattern() {}
802 CGRect m_imageBounds
;
808 void wxMacCoreGraphicsContext::SetPen( const wxPen
&pen
)
811 if ( m_cgContext
== NULL
)
814 bool fill
= m_brush
.GetStyle() != wxTRANSPARENT
;
815 bool stroke
= pen
.GetStyle() != wxTRANSPARENT
;
818 // we can benchmark performance; should go into a setting eventually
819 CGContextSetShouldAntialias( m_cgContext
, false );
822 if ( fill
|| stroke
)
825 m_mode
= kCGPathFill
; // just a default
829 CGContextSetRGBStrokeColor( m_cgContext
, pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
830 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 );
832 // TODO: * m_dc->m_scaleX
833 CGFloat penWidth
= pen
.GetWidth();
836 CGContextSetLineWidth( m_cgContext
, penWidth
);
839 switch ( pen
.GetCap() )
842 cap
= kCGLineCapRound
;
845 case wxCAP_PROJECTING
:
846 cap
= kCGLineCapSquare
;
850 cap
= kCGLineCapButt
;
854 cap
= kCGLineCapButt
;
859 switch ( pen
.GetJoin() )
862 join
= kCGLineJoinBevel
;
866 join
= kCGLineJoinMiter
;
870 join
= kCGLineJoinRound
;
874 join
= kCGLineJoinMiter
;
878 m_mode
= kCGPathStroke
;
881 const CGFloat
*lengths
= NULL
;
882 CGFloat
*userLengths
= NULL
;
884 const CGFloat dashUnit
= penWidth
< 1.0 ? 1.0 : penWidth
;
886 const CGFloat dotted
[] = { dashUnit
, dashUnit
+ 2.0 };
887 const CGFloat short_dashed
[] = { 9.0 , 6.0 };
888 const CGFloat dashed
[] = { 19.0 , 9.0 };
889 const CGFloat dotted_dashed
[] = { 9.0 , 6.0 , 3.0 , 3.0 };
891 switch ( pen
.GetStyle() )
898 count
= WXSIZEOF(dotted
);
903 count
= WXSIZEOF(dashed
);
907 lengths
= short_dashed
;
908 count
= WXSIZEOF(short_dashed
);
912 lengths
= dotted_dashed
;
913 count
= WXSIZEOF(dotted_dashed
);
918 count
= pen
.GetDashes( &dashes
);
919 if ((dashes
!= NULL
) && (count
> 0))
921 userLengths
= new CGFloat
[count
];
922 for ( int i
= 0; i
< count
; ++i
)
924 userLengths
[i
] = dashes
[i
] * dashUnit
;
926 if ( i
% 2 == 1 && userLengths
[i
] < dashUnit
+ 2.0 )
927 userLengths
[i
] = dashUnit
+ 2.0;
928 else if ( i
% 2 == 0 && userLengths
[i
] < dashUnit
)
929 userLengths
[i
] = dashUnit
;
932 lengths
= userLengths
;
937 CGFloat alphaArray
[1] = { 1.0 };
938 wxBitmap
* bmp
= pen
.GetStipple();
939 if ( bmp
&& bmp
->Ok() )
941 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
942 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
943 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
944 CGContextSetStrokePattern( m_cgContext
, pattern
, alphaArray
);
951 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
952 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
953 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( pen
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
955 CGFloat colorArray
[4] = { pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
956 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 };
958 CGContextSetStrokePattern( m_cgContext
, pattern
, colorArray
);
963 if ((lengths
!= NULL
) && (count
> 0))
965 CGContextSetLineDash( m_cgContext
, 0 , lengths
, count
);
966 // force the line cap, otherwise we get artifacts (overlaps) and just solid lines
967 cap
= kCGLineCapButt
;
971 CGContextSetLineDash( m_cgContext
, 0 , NULL
, 0 );
974 CGContextSetLineCap( m_cgContext
, cap
);
975 CGContextSetLineJoin( m_cgContext
, join
);
977 delete[] userLengths
;
980 if ( fill
&& stroke
)
981 m_mode
= kCGPathFillStroke
;
985 void wxMacCoreGraphicsContext::SetBrush( const wxBrush
&brush
)
988 if ( m_cgContext
== NULL
)
991 bool fill
= brush
.GetStyle() != wxTRANSPARENT
;
992 bool stroke
= m_pen
.GetStyle() != wxTRANSPARENT
;
995 // we can benchmark performance, should go into a setting later
996 CGContextSetShouldAntialias( m_cgContext
, false );
999 if ( fill
|| stroke
)
1002 m_mode
= kCGPathFill
; // just a default
1006 if ( brush
.GetStyle() == wxSOLID
)
1008 CGContextSetRGBFillColor( m_cgContext
, brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
1009 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 );
1011 else if ( brush
.IsHatch() )
1013 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
1014 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
1015 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( brush
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
1017 CGFloat colorArray
[4] = { brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
1018 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 };
1020 CGContextSetFillPattern( m_cgContext
, pattern
, colorArray
);
1024 // now brush is a bitmap
1025 CGFloat alphaArray
[1] = { 1.0 };
1026 wxBitmap
* bmp
= brush
.GetStipple();
1027 if ( bmp
&& bmp
->Ok() )
1029 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
1030 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
1031 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
1032 CGContextSetFillPattern( m_cgContext
, pattern
, alphaArray
);
1036 m_mode
= kCGPathFill
;
1039 if ( fill
&& stroke
)
1040 m_mode
= kCGPathFillStroke
;
1042 m_mode
= kCGPathStroke
;
1046 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
1047 void wxMacCoreGraphicsContext::SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
1048 const wxColour
&c1
, const wxColour
&c2
)
1052 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
1053 // with radius r and color cColor
1054 void wxMacCoreGraphicsContext::SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
1055 const wxColour
&oColor
, const wxColour
&cColor
)
1060 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
)
1062 DrawText(str
, x
, y
, 0.0);
1065 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
)
1069 OSStatus status
= noErr
;
1070 ATSUTextLayout atsuLayout
;
1071 UniCharCount chars
= str
.length();
1072 UniChar
* ubuf
= NULL
;
1074 #if SIZEOF_WCHAR_T == 4
1075 wxMBConvUTF16 converter
;
1077 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
1078 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1079 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
1081 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1082 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1083 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1084 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1086 chars
= unicharlen
/ 2;
1089 ubuf
= (UniChar
*) str
.wc_str();
1091 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1092 chars
= wxWcslen( wchar
.data() );
1093 ubuf
= (UniChar
*) wchar
.data();
1097 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1098 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1100 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the rotated text") );
1102 status
= ::ATSUSetTransientFontMatching( atsuLayout
, true );
1103 wxASSERT_MSG( status
== noErr
, wxT("couldn't setup transient font matching") );
1105 int iAngle
= int( angle
* RAD2DEG
);
1106 if ( abs(iAngle
) > 0 )
1108 Fixed atsuAngle
= IntToFixed( iAngle
);
1109 ATSUAttributeTag atsuTags
[] =
1111 kATSULineRotationTag
,
1113 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1117 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1121 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1122 atsuTags
, atsuSizes
, atsuValues
);
1126 ATSUAttributeTag atsuTags
[] =
1130 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1132 sizeof( CGContextRef
) ,
1134 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1138 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1139 atsuTags
, atsuSizes
, atsuValues
);
1142 ATSUTextMeasurement textBefore
, textAfter
;
1143 ATSUTextMeasurement ascent
, descent
;
1145 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1146 &textBefore
, &textAfter
, &ascent
, &descent
);
1148 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1153 if ( m_backgroundMode == wxSOLID )
1155 wxGraphicsPath* path = m_graphicContext->CreatePath();
1156 path->MoveToPoint( drawX , drawY );
1157 path->AddLineToPoint(
1158 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent)) ,
1159 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent)) );
1160 path->AddLineToPoint(
1161 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent ) + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1162 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent) - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1163 path->AddLineToPoint(
1164 (int) (drawX + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1165 (int) (drawY - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1167 m_graphicContext->FillPath( path , m_textBackgroundColour );
1171 x
+= (int)(sin(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1172 y
+= (int)(cos(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1174 status
= ::ATSUMeasureTextImage( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1175 IntToFixed(x
) , IntToFixed(y
) , &rect
);
1176 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1178 CGContextSaveGState(m_cgContext
);
1179 CGContextTranslateCTM(m_cgContext
, x
, y
);
1180 CGContextScaleCTM(m_cgContext
, 1, -1);
1181 status
= ::ATSUDrawText( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1182 IntToFixed(0) , IntToFixed(0) );
1184 wxASSERT_MSG( status
== noErr
, wxT("couldn't draw the rotated text") );
1186 CGContextRestoreGState(m_cgContext
);
1188 ::ATSUDisposeTextLayout(atsuLayout
);
1190 #if SIZEOF_WCHAR_T == 4
1195 void wxMacCoreGraphicsContext::GetTextExtent( const wxString
&str
, wxDouble
*width
, wxDouble
*height
,
1196 wxDouble
*descent
, wxDouble
*externalLeading
) const
1198 wxCHECK_RET( m_macATSUIStyle
!= NULL
, wxT("wxDC(cg)::DoGetTextExtent - no valid font set") );
1200 OSStatus status
= noErr
;
1202 ATSUTextLayout atsuLayout
;
1203 UniCharCount chars
= str
.length();
1204 UniChar
* ubuf
= NULL
;
1206 #if SIZEOF_WCHAR_T == 4
1207 wxMBConvUTF16 converter
;
1209 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
1210 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1211 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
1213 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1214 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1215 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1216 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1218 chars
= unicharlen
/ 2;
1221 ubuf
= (UniChar
*) str
.wc_str();
1223 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1224 chars
= wxWcslen( wchar
.data() );
1225 ubuf
= (UniChar
*) wchar
.data();
1229 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1230 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1232 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the text") );
1234 ATSUTextMeasurement textBefore
, textAfter
;
1235 ATSUTextMeasurement textAscent
, textDescent
;
1237 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1238 &textBefore
, &textAfter
, &textAscent
, &textDescent
);
1241 *height
= FixedToInt(textAscent
+ textDescent
);
1243 *descent
= FixedToInt(textDescent
);
1244 if ( externalLeading
)
1245 *externalLeading
= 0;
1247 *width
= FixedToInt(textAfter
- textBefore
);
1249 ::ATSUDisposeTextLayout(atsuLayout
);
1252 void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const
1255 widths
.Add(0, text
.length());
1260 ATSUTextLayout atsuLayout
;
1261 UniCharCount chars
= text
.length();
1262 UniChar
* ubuf
= NULL
;
1264 #if SIZEOF_WCHAR_T == 4
1265 wxMBConvUTF16 converter
;
1267 size_t unicharlen
= converter
.WC2MB( NULL
, text
.wc_str() , 0 );
1268 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1269 converter
.WC2MB( (char*) ubuf
, text
.wc_str(), unicharlen
+ 2 );
1271 const wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1272 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1273 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1274 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1276 chars
= unicharlen
/ 2;
1279 ubuf
= (UniChar
*) text
.wc_str();
1281 wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1282 chars
= wxWcslen( wchar
.data() );
1283 ubuf
= (UniChar
*) wchar
.data();
1287 ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1288 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1290 for ( int pos
= 0; pos
< (int)chars
; pos
++ )
1292 unsigned long actualNumberOfBounds
= 0;
1293 ATSTrapezoid glyphBounds
;
1295 // We get a single bound, since the text should only require one. If it requires more, there is an issue
1297 result
= ATSUGetGlyphBounds( atsuLayout
, 0, 0, kATSUFromTextBeginning
, pos
+ 1,
1298 kATSUseDeviceOrigins
, 1, &glyphBounds
, &actualNumberOfBounds
);
1299 if (result
!= noErr
|| actualNumberOfBounds
!= 1 )
1302 widths
[pos
] = FixedToInt( glyphBounds
.upperRight
.x
- glyphBounds
.upperLeft
.x
);
1303 //unsigned char uch = s[i];
1306 ::ATSUDisposeTextLayout(atsuLayout
);
1309 void wxMacCoreGraphicsContext::SetFont( const wxFont
&font
)
1311 if ( m_macATSUIStyle
)
1313 ::ATSUDisposeStyle((ATSUStyle
)m_macATSUIStyle
);
1314 m_macATSUIStyle
= NULL
;
1322 status
= ATSUCreateAndCopyStyle( (ATSUStyle
) font
.MacGetATSUStyle() , (ATSUStyle
*) &m_macATSUIStyle
);
1324 wxASSERT_MSG( status
== noErr
, wxT("couldn't create ATSU style") );
1326 // we need the scale here ...
1328 Fixed atsuSize
= IntToFixed( int( /*m_scaleY*/ 1 * font
.MacGetFontSize()) );
1329 RGBColor atsuColor
= MAC_WXCOLORREF( m_textForegroundColor
.GetPixel() );
1330 ATSUAttributeTag atsuTags
[] =
1335 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1338 sizeof( RGBColor
) ,
1340 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1346 status
= ::ATSUSetAttributes(
1347 (ATSUStyle
)m_macATSUIStyle
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
) ,
1348 atsuTags
, atsuSizes
, atsuValues
);
1350 wxASSERT_MSG( status
== noErr
, wxT("couldn't modify ATSU style") );
1354 void * wxMacCoreGraphicsContext::GetNativeContext()
1359 wxGraphicsContext
* wxGraphicsContext::Create( const wxWindowDC
&dc
)
1361 return new wxMacCoreGraphicsContext((CGContextRef
)dc
.GetWindow()->MacGetCGContextRef() );
1364 wxGraphicsContext
* wxGraphicsContext::Create( wxWindow
* window
)
1366 return new wxMacCoreGraphicsContext( window
);
1369 wxGraphicsContext
* wxGraphicsContext::CreateFromNative( void * context
)
1371 return new wxMacCoreGraphicsContext((CGContextRef
)context
);
1374 wxGraphicsContext
* wxGraphicsContext::CreateFromNativeWindow( void * window
)
1376 return new wxMacCoreGraphicsContext((WindowRef
)window
);
1379 #endif // wxMAC_USE_CORE_GRAPHICS