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
;
45 class WXDLLEXPORT wxMacCoreGraphicsPath
: public wxGraphicsPath
47 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsPath
)
49 wxMacCoreGraphicsPath();
50 ~wxMacCoreGraphicsPath();
52 // begins a new subpath at (x,y)
53 virtual void MoveToPoint( wxDouble x
, wxDouble y
);
55 // adds a straight line from the current point to (x,y)
56 virtual void AddLineToPoint( wxDouble x
, wxDouble y
);
58 // adds a cubic Bezier curve from the current point, using two control points and an end point
59 virtual void AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
);
61 // closes the current sub-path
62 virtual void CloseSubpath();
64 // gets the last point of the current path, (0,0) if not yet set
65 virtual void GetCurrentPoint( wxDouble
& x
, wxDouble
&y
);
67 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
68 virtual void AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
);
71 // These are convenience functions which - if not available natively will be assembled
72 // using the primitives from above
75 // adds a quadratic Bezier curve from the current point, using a control point and an end point
76 virtual void AddQuadCurveToPoint( wxDouble cx
, wxDouble cy
, wxDouble x
, wxDouble y
);
78 // appends a rectangle as a new closed subpath
79 virtual void AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
81 // appends an ellipsis as a new closed subpath fitting the passed rectangle
82 virtual void AddCircle( wxDouble x
, wxDouble y
, wxDouble r
);
84 // 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)
85 virtual void AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
);
87 CGPathRef
GetPath() const;
89 CGMutablePathRef m_path
;
92 wxMacCoreGraphicsPath::wxMacCoreGraphicsPath()
94 m_path
= CGPathCreateMutable();
97 wxMacCoreGraphicsPath::~wxMacCoreGraphicsPath()
99 CGPathRelease( m_path
);
102 // opens (starts) a new subpath
103 void wxMacCoreGraphicsPath::MoveToPoint( wxDouble x1
, wxDouble y1
)
105 CGPathMoveToPoint( m_path
, NULL
, x1
, y1
);
108 void wxMacCoreGraphicsPath::AddLineToPoint( wxDouble x1
, wxDouble y1
)
110 CGPathAddLineToPoint( m_path
, NULL
, x1
, y1
);
113 void wxMacCoreGraphicsPath::AddCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble cx2
, wxDouble cy2
, wxDouble x
, wxDouble y
)
115 CGPathAddCurveToPoint( m_path
, NULL
, cx1
, cy1
, cx2
, cy2
, x
, y
);
118 void wxMacCoreGraphicsPath::AddQuadCurveToPoint( wxDouble cx1
, wxDouble cy1
, wxDouble x
, wxDouble y
)
120 CGPathAddQuadCurveToPoint( m_path
, NULL
, cx1
, cy1
, x
, y
);
123 void wxMacCoreGraphicsPath::AddRectangle( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
125 CGRect cgRect
= { { x
, y
} , { w
, h
} };
126 CGPathAddRect( m_path
, NULL
, cgRect
);
129 void wxMacCoreGraphicsPath::AddCircle( wxDouble x
, wxDouble y
, wxDouble r
)
131 CGPathAddArc( m_path
, NULL
, x
, y
, r
, 0.0 , 2 * M_PI
, true );
134 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
135 void wxMacCoreGraphicsPath::AddArc( wxDouble x
, wxDouble y
, wxDouble r
, wxDouble startAngle
, wxDouble endAngle
, bool clockwise
)
137 // inverse direction as we the 'normal' state is a y axis pointing down, ie mirrored to the standard core graphics setup
138 CGPathAddArc( m_path
, NULL
, x
, y
, r
, startAngle
, endAngle
, !clockwise
);
141 void wxMacCoreGraphicsPath::AddArcToPoint( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
, wxDouble r
)
143 CGPathAddArcToPoint( m_path
, NULL
, x1
, y1
, x2
, y2
, r
);
146 // closes the current subpath
147 void wxMacCoreGraphicsPath::CloseSubpath()
149 CGPathCloseSubpath( m_path
);
152 CGPathRef
wxMacCoreGraphicsPath::GetPath() const
157 // gets the last point of the current path, (0,0) if not yet set
158 void wxMacCoreGraphicsPath::GetCurrentPoint( wxDouble
& x
, wxDouble
&y
)
160 CGPoint p
= CGPathGetCurrentPoint( m_path
);
169 class WXDLLEXPORT wxMacCoreGraphicsContext
: public wxGraphicsContext
171 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext
)
174 wxMacCoreGraphicsContext( CGContextRef cgcontext
);
176 wxMacCoreGraphicsContext( WindowRef window
);
178 wxMacCoreGraphicsContext();
180 ~wxMacCoreGraphicsContext();
184 // creates a path instance that corresponds to the type of graphics context, ie GDIPlus, cairo, CoreGraphics ...
185 virtual wxGraphicsPath
* CreatePath();
187 // push the current state of the context, ie the transformation matrix on a stack
188 virtual void PushState();
190 // pops a stored state from the stack
191 virtual void PopState();
193 // clips drawings to the region
194 virtual void Clip( const wxRegion
®ion
);
196 // clips drawings to the rect
197 virtual void Clip( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
199 // resets the clipping to original extent
200 virtual void ResetClip();
202 virtual void * GetNativeContext();
209 virtual void Translate( wxDouble dx
, wxDouble dy
);
212 virtual void Scale( wxDouble xScale
, wxDouble yScale
);
215 virtual void Rotate( wxDouble angle
);
222 virtual void SetPen( const wxPen
&pen
);
224 // sets the brush for filling
225 virtual void SetBrush( const wxBrush
&brush
);
227 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
228 virtual void SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
229 const wxColour
&c1
, const wxColour
&c2
);
231 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
232 // with radius r and color cColor
233 virtual void SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
234 const wxColour
&oColor
, const wxColour
&cColor
);
237 virtual void SetFont( const wxFont
&font
);
239 // sets the text color
240 virtual void SetTextColor( const wxColour
&col
);
242 // strokes along a path with the current pen
243 virtual void StrokePath( const wxGraphicsPath
*path
);
245 // fills a path with the current brush
246 virtual void FillPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
248 // draws a path by first filling and then stroking
249 virtual void DrawPath( const wxGraphicsPath
*path
, int fillStyle
= wxWINDING_RULE
);
255 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
);
257 virtual void DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
);
259 virtual void GetTextExtent( const wxString
&text
, wxDouble
*width
, wxDouble
*height
,
260 wxDouble
*descent
, wxDouble
*externalLeading
) const;
262 virtual void GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const;
268 virtual void DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
270 virtual void DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
);
272 void SetNativeContext( CGContextRef cg
);
273 CGPathDrawingMode
GetDrawingMode() const { return m_mode
; }
276 CGContextRef m_cgContext
;
277 WindowRef m_windowRef
;
278 bool m_releaseContext
;
279 CGPathDrawingMode m_mode
;
280 ATSUStyle m_macATSUIStyle
;
284 wxColor m_textForegroundColor
;
287 //-----------------------------------------------------------------------------
289 //-----------------------------------------------------------------------------
291 #if !defined( __DARWIN__ ) || defined(__MWERKS__)
293 const double M_PI
= 3.14159265358979;
297 static const double RAD2DEG
= 180.0 / M_PI
;
299 //-----------------------------------------------------------------------------
300 // device context implementation
302 // more and more of the dc functionality should be implemented by calling
303 // the appropricate wxMacCoreGraphicsContext, but we will have to do that step by step
304 // also coordinate conversions should be moved to native matrix ops
305 //-----------------------------------------------------------------------------
307 // we always stock two context states, one at entry, to be able to preserve the
308 // state we were called with, the other one after changing to HI Graphics orientation
309 // (this one is used for getting back clippings etc)
311 //-----------------------------------------------------------------------------
312 // wxGraphicsPath implementation
313 //-----------------------------------------------------------------------------
315 //-----------------------------------------------------------------------------
316 // wxGraphicsContext implementation
317 //-----------------------------------------------------------------------------
319 void wxMacCoreGraphicsContext::Init()
322 m_mode
= kCGPathFill
;
323 m_macATSUIStyle
= NULL
;
324 m_releaseContext
= false;
327 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGContextRef cgcontext
)
330 m_cgContext
= cgcontext
;
331 CGContextSaveGState( m_cgContext
);
332 CGContextSaveGState( m_cgContext
);
335 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( WindowRef window
)
338 m_windowRef
= window
;
339 OSStatus status
= QDBeginCGContext( GetWindowPort( window
) , &m_cgContext
);
340 wxASSERT_MSG( status
== noErr
, wxT("Cannot nest wxDCs on the same window") );
342 GetWindowBounds( window
, kWindowContentRgn
, &bounds
);
343 CGContextSaveGState( m_cgContext
);
344 CGContextTranslateCTM( m_cgContext
, 0 , bounds
.bottom
- bounds
.top
);
345 CGContextScaleCTM( m_cgContext
, 1 , -1 );
346 CGContextSaveGState( m_cgContext
);
347 m_releaseContext
= true;
350 wxMacCoreGraphicsContext::wxMacCoreGraphicsContext()
355 wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext()
359 CGContextSynchronize( m_cgContext
);
360 CGContextRestoreGState( m_cgContext
);
361 CGContextRestoreGState( m_cgContext
);
364 if ( m_releaseContext
)
365 QDEndCGContext( GetWindowPort( m_windowRef
) , &m_cgContext
);
368 void wxMacCoreGraphicsContext::Clip( const wxRegion
®ion
)
370 HIShapeRef shape
= HIShapeCreateWithQDRgn( (RgnHandle
) region
.GetWXHRGN() );
371 HIShapeReplacePathInCGContext( shape
, m_cgContext
);
372 CGContextClip( m_cgContext
);
376 // clips drawings to the rect
377 void wxMacCoreGraphicsContext::Clip( wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
379 HIRect r
= CGRectMake( x
, y
, w
, h
);
380 CGContextClipToRect( m_cgContext
, r
);
383 // resets the clipping to original extent
384 void wxMacCoreGraphicsContext::ResetClip()
386 CGContextRestoreGState( m_cgContext
);
387 CGContextSaveGState( m_cgContext
);
390 void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath
*p
)
392 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
393 int width
= m_pen
.GetWidth();
396 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
399 bool offset
= ( width
% 2 ) == 1 ;
402 CGContextTranslateCTM( m_cgContext
, 0.5, 0.5 );
404 CGContextAddPath( m_cgContext
, path
->GetPath() );
405 CGContextStrokePath( m_cgContext
);
408 CGContextTranslateCTM( m_cgContext
, -0.5, -0.5 );
411 void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath
*p
, int fillStyle
)
413 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
414 CGPathDrawingMode mode
= m_mode
;
416 if ( fillStyle
== wxODDEVEN_RULE
)
418 if ( mode
== kCGPathFill
)
419 mode
= kCGPathEOFill
;
420 else if ( mode
== kCGPathFillStroke
)
421 mode
= kCGPathEOFillStroke
;
424 int width
= m_pen
.GetWidth();
427 if ( m_pen
.GetStyle() == wxTRANSPARENT
)
430 bool offset
= ( width
% 2 ) == 1 ;
433 CGContextTranslateCTM( m_cgContext
, 0.5, 0.5 );
435 CGContextAddPath( m_cgContext
, path
->GetPath() );
436 CGContextDrawPath( m_cgContext
, mode
);
439 CGContextTranslateCTM( m_cgContext
, -0.5, -0.5 );
442 void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath
*p
, int fillStyle
)
444 const wxMacCoreGraphicsPath
* path
= dynamic_cast< const wxMacCoreGraphicsPath
*>( p
);
446 CGContextAddPath( m_cgContext
, path
->GetPath() );
447 if ( fillStyle
== wxODDEVEN_RULE
)
448 CGContextEOFillPath( m_cgContext
);
450 CGContextFillPath( m_cgContext
);
453 wxGraphicsPath
* wxMacCoreGraphicsContext::CreatePath()
455 return new wxMacCoreGraphicsPath();
458 void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg
)
460 // we allow either setting or clearing but not replacing
461 wxASSERT( m_cgContext
== NULL
|| cg
== NULL
);
464 CGContextSaveGState( cg
);
468 void wxMacCoreGraphicsContext::Translate( wxDouble dx
, wxDouble dy
)
470 CGContextTranslateCTM( m_cgContext
, dx
, dy
);
473 void wxMacCoreGraphicsContext::Scale( wxDouble xScale
, wxDouble yScale
)
475 CGContextScaleCTM( m_cgContext
, xScale
, yScale
);
478 void wxMacCoreGraphicsContext::Rotate( wxDouble angle
)
480 CGContextRotateCTM( m_cgContext
, angle
);
483 void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap
&bmp
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
485 CGImageRef image
= (CGImageRef
)( bmp
.CGImageCreate() );
486 HIRect r
= CGRectMake( x
, y
, w
, h
);
487 HIViewDrawCGImage( m_cgContext
, &r
, image
);
488 CGImageRelease( image
);
491 void wxMacCoreGraphicsContext::DrawIcon( const wxIcon
&icon
, wxDouble x
, wxDouble y
, wxDouble w
, wxDouble h
)
493 CGRect r
= CGRectMake( 00 , 00 , w
, h
);
494 CGContextSaveGState( m_cgContext
);
495 CGContextTranslateCTM( m_cgContext
, x
, y
+ h
);
496 CGContextScaleCTM( m_cgContext
, 1, -1 );
497 PlotIconRefInContext( m_cgContext
, &r
, kAlignNone
, kTransformNone
,
498 NULL
, kPlotIconRefNormalFlags
, MAC_WXHICON( icon
.GetHICON() ) );
499 CGContextRestoreGState( m_cgContext
);
502 void wxMacCoreGraphicsContext::PushState()
504 CGContextSaveGState( m_cgContext
);
507 void wxMacCoreGraphicsContext::PopState()
509 CGContextRestoreGState( m_cgContext
);
512 void wxMacCoreGraphicsContext::SetTextColor( const wxColour
&col
)
514 m_textForegroundColor
= col
;
515 // to recreate the native font after color change
520 #pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes
522 // CGPattern wrapper class: always allocate on heap, never call destructor
524 class wxMacCoreGraphicsPattern
527 wxMacCoreGraphicsPattern() {}
529 // is guaranteed to be called only with a non-Null CGContextRef
530 virtual void Render( CGContextRef ctxRef
) = 0;
532 operator CGPatternRef() const { return m_patternRef
; }
535 virtual ~wxMacCoreGraphicsPattern()
537 // as this is called only when the m_patternRef is been released;
538 // don't release it again
541 static void _Render( void *info
, CGContextRef ctxRef
)
543 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
544 if ( self
&& ctxRef
)
545 self
->Render( ctxRef
);
548 static void _Dispose( void *info
)
550 wxMacCoreGraphicsPattern
* self
= (wxMacCoreGraphicsPattern
*) info
;
554 CGPatternRef m_patternRef
;
556 static const CGPatternCallbacks ms_Callbacks
;
559 const CGPatternCallbacks
wxMacCoreGraphicsPattern::ms_Callbacks
= { 0, &wxMacCoreGraphicsPattern::_Render
, &wxMacCoreGraphicsPattern::_Dispose
};
561 class ImagePattern
: public wxMacCoreGraphicsPattern
564 ImagePattern( const wxBitmap
* bmp
, const CGAffineTransform
& transform
)
566 wxASSERT( bmp
&& bmp
->Ok() );
568 Init( (CGImageRef
) bmp
->CGImageCreate() , transform
);
571 // ImagePattern takes ownership of CGImageRef passed in
572 ImagePattern( CGImageRef image
, const CGAffineTransform
& transform
)
577 Init( image
, transform
);
580 virtual void Render( CGContextRef ctxRef
)
583 HIViewDrawCGImage( ctxRef
, &m_imageBounds
, m_image
);
587 void Init( CGImageRef image
, const CGAffineTransform
& transform
)
592 m_imageBounds
= CGRectMake( 0.0, 0.0, (CGFloat
)CGImageGetWidth( m_image
), (CGFloat
)CGImageGetHeight( m_image
) );
593 m_patternRef
= CGPatternCreate(
594 this , m_imageBounds
, transform
,
595 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
596 kCGPatternTilingNoDistortion
, true , &wxMacCoreGraphicsPattern::ms_Callbacks
);
600 virtual ~ImagePattern()
603 CGImageRelease( m_image
);
607 CGRect m_imageBounds
;
610 class HatchPattern
: public wxMacCoreGraphicsPattern
613 HatchPattern( int hatchstyle
, const CGAffineTransform
& transform
)
615 m_hatch
= hatchstyle
;
616 m_imageBounds
= CGRectMake( 0.0, 0.0, 8.0 , 8.0 );
617 m_patternRef
= CGPatternCreate(
618 this , m_imageBounds
, transform
,
619 m_imageBounds
.size
.width
, m_imageBounds
.size
.height
,
620 kCGPatternTilingNoDistortion
, false , &wxMacCoreGraphicsPattern::ms_Callbacks
);
623 void StrokeLineSegments( CGContextRef ctxRef
, const CGPoint pts
[] , size_t count
)
625 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
626 if ( UMAGetSystemVersion() >= 0x1040 )
628 CGContextStrokeLineSegments( ctxRef
, pts
, count
);
633 CGContextBeginPath( ctxRef
);
634 for (size_t i
= 0; i
< count
; i
+= 2)
636 CGContextMoveToPoint(ctxRef
, pts
[i
].x
, pts
[i
].y
);
637 CGContextAddLineToPoint(ctxRef
, pts
[i
+1].x
, pts
[i
+1].y
);
639 CGContextStrokePath(ctxRef
);
643 virtual void Render( CGContextRef ctxRef
)
647 case wxBDIAGONAL_HATCH
:
651 { 8.0 , 0.0 } , { 0.0 , 8.0 }
653 StrokeLineSegments( ctxRef
, pts
, 2 );
657 case wxCROSSDIAG_HATCH
:
661 { 0.0 , 0.0 } , { 8.0 , 8.0 } ,
662 { 8.0 , 0.0 } , { 0.0 , 8.0 }
664 StrokeLineSegments( ctxRef
, pts
, 4 );
668 case wxFDIAGONAL_HATCH
:
672 { 0.0 , 0.0 } , { 8.0 , 8.0 }
674 StrokeLineSegments( ctxRef
, pts
, 2 );
682 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
683 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
685 StrokeLineSegments( ctxRef
, pts
, 4 );
689 case wxHORIZONTAL_HATCH
:
693 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
695 StrokeLineSegments( ctxRef
, pts
, 2 );
699 case wxVERTICAL_HATCH
:
703 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
705 StrokeLineSegments( ctxRef
, pts
, 2 );
715 virtual ~HatchPattern() {}
717 CGRect m_imageBounds
;
723 void wxMacCoreGraphicsContext::SetPen( const wxPen
&pen
)
726 if ( m_cgContext
== NULL
)
729 bool fill
= m_brush
.GetStyle() != wxTRANSPARENT
;
730 bool stroke
= pen
.GetStyle() != wxTRANSPARENT
;
733 // we can benchmark performance; should go into a setting eventually
734 CGContextSetShouldAntialias( m_cgContext
, false );
737 if ( fill
|| stroke
)
740 m_mode
= kCGPathFill
; // just a default
744 CGContextSetRGBStrokeColor( m_cgContext
, pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
745 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 );
747 // TODO: * m_dc->m_scaleX
748 CGFloat penWidth
= pen
.GetWidth();
751 CGContextSetLineWidth( m_cgContext
, penWidth
);
754 switch ( pen
.GetCap() )
757 cap
= kCGLineCapRound
;
760 case wxCAP_PROJECTING
:
761 cap
= kCGLineCapSquare
;
765 cap
= kCGLineCapButt
;
769 cap
= kCGLineCapButt
;
774 switch ( pen
.GetJoin() )
777 join
= kCGLineJoinBevel
;
781 join
= kCGLineJoinMiter
;
785 join
= kCGLineJoinRound
;
789 join
= kCGLineJoinMiter
;
793 m_mode
= kCGPathStroke
;
796 const CGFloat
*lengths
= NULL
;
797 CGFloat
*userLengths
= NULL
;
799 const CGFloat dashUnit
= penWidth
< 1.0 ? 1.0 : penWidth
;
801 const CGFloat dotted
[] = { dashUnit
, dashUnit
+ 2.0 };
802 const CGFloat short_dashed
[] = { 9.0 , 6.0 };
803 const CGFloat dashed
[] = { 19.0 , 9.0 };
804 const CGFloat dotted_dashed
[] = { 9.0 , 6.0 , 3.0 , 3.0 };
806 switch ( pen
.GetStyle() )
813 count
= WXSIZEOF(dotted
);
818 count
= WXSIZEOF(dashed
);
822 lengths
= short_dashed
;
823 count
= WXSIZEOF(short_dashed
);
827 lengths
= dotted_dashed
;
828 count
= WXSIZEOF(dotted_dashed
);
833 count
= pen
.GetDashes( &dashes
);
834 if ((dashes
!= NULL
) && (count
> 0))
836 userLengths
= new CGFloat
[count
];
837 for ( int i
= 0; i
< count
; ++i
)
839 userLengths
[i
] = dashes
[i
] * dashUnit
;
841 if ( i
% 2 == 1 && userLengths
[i
] < dashUnit
+ 2.0 )
842 userLengths
[i
] = dashUnit
+ 2.0;
843 else if ( i
% 2 == 0 && userLengths
[i
] < dashUnit
)
844 userLengths
[i
] = dashUnit
;
847 lengths
= userLengths
;
852 CGFloat alphaArray
[1] = { 1.0 };
853 wxBitmap
* bmp
= pen
.GetStipple();
854 if ( bmp
&& bmp
->Ok() )
856 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
857 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
858 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
859 CGContextSetStrokePattern( m_cgContext
, pattern
, alphaArray
);
866 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
867 CGContextSetStrokeColorSpace( m_cgContext
, patternSpace
);
868 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( pen
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
870 CGFloat colorArray
[4] = { pen
.GetColour().Red() / 255.0 , pen
.GetColour().Green() / 255.0 ,
871 pen
.GetColour().Blue() / 255.0 , pen
.GetColour().Alpha() / 255.0 };
873 CGContextSetStrokePattern( m_cgContext
, pattern
, colorArray
);
878 if ((lengths
!= NULL
) && (count
> 0))
880 CGContextSetLineDash( m_cgContext
, 0 , lengths
, count
);
881 // force the line cap, otherwise we get artifacts (overlaps) and just solid lines
882 cap
= kCGLineCapButt
;
886 CGContextSetLineDash( m_cgContext
, 0 , NULL
, 0 );
889 CGContextSetLineCap( m_cgContext
, cap
);
890 CGContextSetLineJoin( m_cgContext
, join
);
892 delete[] userLengths
;
895 if ( fill
&& stroke
)
896 m_mode
= kCGPathFillStroke
;
900 void wxMacCoreGraphicsContext::SetBrush( const wxBrush
&brush
)
903 if ( m_cgContext
== NULL
)
906 bool fill
= brush
.GetStyle() != wxTRANSPARENT
;
907 bool stroke
= m_pen
.GetStyle() != wxTRANSPARENT
;
910 // we can benchmark performance, should go into a setting later
911 CGContextSetShouldAntialias( m_cgContext
, false );
914 if ( fill
|| stroke
)
917 m_mode
= kCGPathFill
; // just a default
921 if ( brush
.GetStyle() == wxSOLID
)
923 CGContextSetRGBFillColor( m_cgContext
, brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
924 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 );
926 else if ( brush
.IsHatch() )
928 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
929 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
930 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new HatchPattern( brush
.GetStyle() , CGContextGetCTM( m_cgContext
) ) ) );
932 CGFloat colorArray
[4] = { brush
.GetColour().Red() / 255.0 , brush
.GetColour().Green() / 255.0 ,
933 brush
.GetColour().Blue() / 255.0 , brush
.GetColour().Alpha() / 255.0 };
935 CGContextSetFillPattern( m_cgContext
, pattern
, colorArray
);
939 // now brush is a bitmap
940 CGFloat alphaArray
[1] = { 1.0 };
941 wxBitmap
* bmp
= brush
.GetStipple();
942 if ( bmp
&& bmp
->Ok() )
944 wxMacCFRefHolder
<CGColorSpaceRef
> patternSpace( CGColorSpaceCreatePattern( NULL
) );
945 CGContextSetFillColorSpace( m_cgContext
, patternSpace
);
946 wxMacCFRefHolder
<CGPatternRef
> pattern( *( new ImagePattern( bmp
, CGContextGetCTM( m_cgContext
) ) ) );
947 CGContextSetFillPattern( m_cgContext
, pattern
, alphaArray
);
951 m_mode
= kCGPathFill
;
954 if ( fill
&& stroke
)
955 m_mode
= kCGPathFillStroke
;
957 m_mode
= kCGPathStroke
;
961 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
962 void wxMacCoreGraphicsContext::SetLinearGradientBrush( wxDouble x1
, wxDouble y1
, wxDouble x2
, wxDouble y2
,
963 const wxColour
&c1
, const wxColour
&c2
)
967 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
968 // with radius r and color cColor
969 void wxMacCoreGraphicsContext::SetRadialGradientBrush( wxDouble xo
, wxDouble yo
, wxDouble xc
, wxDouble yc
, wxDouble radius
,
970 const wxColour
&oColor
, const wxColour
&cColor
)
975 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
)
977 DrawText(str
, x
, y
, 0.0);
980 void wxMacCoreGraphicsContext::DrawText( const wxString
&str
, wxDouble x
, wxDouble y
, wxDouble angle
)
982 OSStatus status
= noErr
;
983 ATSUTextLayout atsuLayout
;
984 UniCharCount chars
= str
.length();
985 UniChar
* ubuf
= NULL
;
987 #if SIZEOF_WCHAR_T == 4
988 wxMBConvUTF16 converter
;
990 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
991 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
992 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
994 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
995 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
996 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
997 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
999 chars
= unicharlen
/ 2;
1002 ubuf
= (UniChar
*) str
.wc_str();
1004 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1005 chars
= wxWcslen( wchar
.data() );
1006 ubuf
= (UniChar
*) wchar
.data();
1010 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1011 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1013 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the rotated text") );
1015 status
= ::ATSUSetTransientFontMatching( atsuLayout
, true );
1016 wxASSERT_MSG( status
== noErr
, wxT("couldn't setup transient font matching") );
1018 int iAngle
= int( angle
* RAD2DEG
);
1019 if ( abs(iAngle
) > 0 )
1021 Fixed atsuAngle
= IntToFixed( iAngle
);
1022 ATSUAttributeTag atsuTags
[] =
1024 kATSULineRotationTag
,
1026 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1030 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1034 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1035 atsuTags
, atsuSizes
, atsuValues
);
1039 ATSUAttributeTag atsuTags
[] =
1043 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1045 sizeof( CGContextRef
) ,
1047 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1051 status
= ::ATSUSetLayoutControls(atsuLayout
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
),
1052 atsuTags
, atsuSizes
, atsuValues
);
1055 ATSUTextMeasurement textBefore
, textAfter
;
1056 ATSUTextMeasurement ascent
, descent
;
1058 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1059 &textBefore
, &textAfter
, &ascent
, &descent
);
1061 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1066 if ( m_backgroundMode == wxSOLID )
1068 wxGraphicsPath* path = m_graphicContext->CreatePath();
1069 path->MoveToPoint( drawX , drawY );
1070 path->AddLineToPoint(
1071 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent)) ,
1072 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent)) );
1073 path->AddLineToPoint(
1074 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent ) + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1075 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent) - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1076 path->AddLineToPoint(
1077 (int) (drawX + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1078 (int) (drawY - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1080 m_graphicContext->FillPath( path , m_textBackgroundColour );
1084 x
+= (int)(sin(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1085 y
+= (int)(cos(angle
/ RAD2DEG
) * FixedToInt(ascent
));
1087 status
= ::ATSUMeasureTextImage( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1088 IntToFixed(x
) , IntToFixed(y
) , &rect
);
1089 wxASSERT_MSG( status
== noErr
, wxT("couldn't measure the rotated text") );
1091 CGContextSaveGState(m_cgContext
);
1092 CGContextTranslateCTM(m_cgContext
, x
, y
);
1093 CGContextScaleCTM(m_cgContext
, 1, -1);
1094 status
= ::ATSUDrawText( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1095 IntToFixed(0) , IntToFixed(0) );
1097 wxASSERT_MSG( status
== noErr
, wxT("couldn't draw the rotated text") );
1099 CGContextRestoreGState(m_cgContext
);
1101 ::ATSUDisposeTextLayout(atsuLayout
);
1103 #if SIZEOF_WCHAR_T == 4
1108 void wxMacCoreGraphicsContext::GetTextExtent( const wxString
&str
, wxDouble
*width
, wxDouble
*height
,
1109 wxDouble
*descent
, wxDouble
*externalLeading
) const
1111 wxCHECK_RET( m_macATSUIStyle
!= NULL
, wxT("wxDC(cg)::DoGetTextExtent - no valid font set") );
1113 OSStatus status
= noErr
;
1115 ATSUTextLayout atsuLayout
;
1116 UniCharCount chars
= str
.length();
1117 UniChar
* ubuf
= NULL
;
1119 #if SIZEOF_WCHAR_T == 4
1120 wxMBConvUTF16 converter
;
1122 size_t unicharlen
= converter
.WC2MB( NULL
, str
.wc_str() , 0 );
1123 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1124 converter
.WC2MB( (char*) ubuf
, str
.wc_str(), unicharlen
+ 2 );
1126 const wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1127 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1128 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1129 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1131 chars
= unicharlen
/ 2;
1134 ubuf
= (UniChar
*) str
.wc_str();
1136 wxWCharBuffer wchar
= str
.wc_str( wxConvLocal
);
1137 chars
= wxWcslen( wchar
.data() );
1138 ubuf
= (UniChar
*) wchar
.data();
1142 status
= ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1143 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1145 wxASSERT_MSG( status
== noErr
, wxT("couldn't create the layout of the text") );
1147 ATSUTextMeasurement textBefore
, textAfter
;
1148 ATSUTextMeasurement textAscent
, textDescent
;
1150 status
= ::ATSUGetUnjustifiedBounds( atsuLayout
, kATSUFromTextBeginning
, kATSUToTextEnd
,
1151 &textBefore
, &textAfter
, &textAscent
, &textDescent
);
1154 *height
= FixedToInt(textAscent
+ textDescent
);
1156 *descent
= FixedToInt(textDescent
);
1157 if ( externalLeading
)
1158 *externalLeading
= 0;
1160 *width
= FixedToInt(textAfter
- textBefore
);
1162 ::ATSUDisposeTextLayout(atsuLayout
);
1165 void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString
& text
, wxArrayDouble
& widths
) const
1168 widths
.Add(0, text
.length());
1173 ATSUTextLayout atsuLayout
;
1174 UniCharCount chars
= text
.length();
1175 UniChar
* ubuf
= NULL
;
1177 #if SIZEOF_WCHAR_T == 4
1178 wxMBConvUTF16 converter
;
1180 size_t unicharlen
= converter
.WC2MB( NULL
, text
.wc_str() , 0 );
1181 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1182 converter
.WC2MB( (char*) ubuf
, text
.wc_str(), unicharlen
+ 2 );
1184 const wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1185 size_t unicharlen
= converter
.WC2MB( NULL
, wchar
.data() , 0 );
1186 ubuf
= (UniChar
*) malloc( unicharlen
+ 2 );
1187 converter
.WC2MB( (char*) ubuf
, wchar
.data() , unicharlen
+ 2 );
1189 chars
= unicharlen
/ 2;
1192 ubuf
= (UniChar
*) text
.wc_str();
1194 wxWCharBuffer wchar
= text
.wc_str( wxConvLocal
);
1195 chars
= wxWcslen( wchar
.data() );
1196 ubuf
= (UniChar
*) wchar
.data();
1200 ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr
) ubuf
, 0 , chars
, chars
, 1 ,
1201 &chars
, (ATSUStyle
*) &m_macATSUIStyle
, &atsuLayout
);
1203 for ( int pos
= 0; pos
< (int)chars
; pos
++ )
1205 unsigned long actualNumberOfBounds
= 0;
1206 ATSTrapezoid glyphBounds
;
1208 // We get a single bound, since the text should only require one. If it requires more, there is an issue
1210 result
= ATSUGetGlyphBounds( atsuLayout
, 0, 0, kATSUFromTextBeginning
, pos
+ 1,
1211 kATSUseDeviceOrigins
, 1, &glyphBounds
, &actualNumberOfBounds
);
1212 if (result
!= noErr
|| actualNumberOfBounds
!= 1 )
1215 widths
[pos
] = FixedToInt( glyphBounds
.upperRight
.x
- glyphBounds
.upperLeft
.x
);
1216 //unsigned char uch = s[i];
1219 ::ATSUDisposeTextLayout(atsuLayout
);
1222 void wxMacCoreGraphicsContext::SetFont( const wxFont
&font
)
1224 if ( m_macATSUIStyle
)
1226 ::ATSUDisposeStyle((ATSUStyle
)m_macATSUIStyle
);
1227 m_macATSUIStyle
= NULL
;
1235 status
= ATSUCreateAndCopyStyle( (ATSUStyle
) font
.MacGetATSUStyle() , (ATSUStyle
*) &m_macATSUIStyle
);
1237 wxASSERT_MSG( status
== noErr
, wxT("couldn't create ATSU style") );
1239 // we need the scale here ...
1241 Fixed atsuSize
= IntToFixed( int( /*m_scaleY*/ 1 * font
.MacGetFontSize()) );
1242 RGBColor atsuColor
= MAC_WXCOLORREF( m_textForegroundColor
.GetPixel() );
1243 ATSUAttributeTag atsuTags
[] =
1248 ByteCount atsuSizes
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1251 sizeof( RGBColor
) ,
1253 ATSUAttributeValuePtr atsuValues
[sizeof(atsuTags
) / sizeof(ATSUAttributeTag
)] =
1259 status
= ::ATSUSetAttributes(
1260 (ATSUStyle
)m_macATSUIStyle
, sizeof(atsuTags
) / sizeof(ATSUAttributeTag
) ,
1261 atsuTags
, atsuSizes
, atsuValues
);
1263 wxASSERT_MSG( status
== noErr
, wxT("couldn't modify ATSU style") );
1267 void * wxMacCoreGraphicsContext::GetNativeContext()
1272 wxGraphicsContext
* wxGraphicsContext::Create( const wxWindowDC
&dc
)
1274 return new wxMacCoreGraphicsContext((CGContextRef
)dc
.GetWindow()->MacGetCGContextRef() );
1277 wxGraphicsContext
* wxGraphicsContext::Create( wxWindow
* window
)
1279 wxGraphicsContext
* ctx
= new wxMacCoreGraphicsContext( (WindowRef
) window
->MacGetTopLevelWindowRef() );
1280 CGContextRef cg
= (CGContextRef
) ctx
->GetNativeContext() ;
1281 CGContextRestoreGState( cg
);
1284 window
->MacWindowToRootWindow( &x
, &y
);
1285 CGContextTranslateCTM( cg
, x
, y
);
1286 CGContextSaveGState( cg
);
1291 wxGraphicsContext
* wxGraphicsContext::CreateFromNative( void * context
)
1293 return new wxMacCoreGraphicsContext((CGContextRef
)context
);
1296 wxGraphicsContext
* wxGraphicsContext::CreateFromNativeWindow( void * window
)
1298 return new wxMacCoreGraphicsContext((WindowRef
)window
);
1301 #endif // wxMAC_USE_CORE_GRAPHICS