]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/graphics.cpp
graphics context implementation
[wxWidgets.git] / src / mac / carbon / graphics.cpp
CommitLineData
50581042
SC
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/mac/carbon/dccg.cpp
3// Purpose: wxDC class
4// Author: Stefan Csomor
5// Modified by:
6// Created: 01/02/97
7// RCS-ID: $Id$
8// Copyright: (c) Stefan Csomor
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#include "wx/wxprec.h"
13
14#include "wx/graphics.h"
15
16#if wxMAC_USE_CORE_GRAPHICS
17
18#ifndef WX_PRECOMP
19 #include "wx/log.h"
20 #include "wx/app.h"
21 #include "wx/dcmemory.h"
22 #include "wx/dcprint.h"
23 #include "wx/region.h"
24 #include "wx/image.h"
25#endif
26
27#include "wx/mac/uma.h"
28
29
30#ifdef __MSL__
31 #if __MSL__ >= 0x6000
32 #include "math.h"
33 // in case our functions were defined outside std, we make it known all the same
34 namespace std { }
35 using namespace std;
36 #endif
37#endif
38
39#include "wx/mac/private.h"
40
41#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
42typedef float CGFloat;
43#endif
44
45//
46// Graphics Path
47//
48
49class WXDLLEXPORT wxMacCoreGraphicsPath : public wxGraphicsPath
50{
51 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsPath)
52public :
53 wxMacCoreGraphicsPath();
54 ~wxMacCoreGraphicsPath();
55
56 // begins a new subpath at (x,y)
57 virtual void MoveToPoint( wxDouble x, wxDouble y );
58
59 // adds a straight line from the current point to (x,y)
60 virtual void AddLineToPoint( wxDouble x, wxDouble y );
61
62 // adds a cubic Bezier curve from the current point, using two control points and an end point
63 virtual void AddCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y );
64
65 // closes the current sub-path
66 virtual void CloseSubpath();
67
68 // gets the last point of the current path, (0,0) if not yet set
69 virtual void GetCurrentPoint( wxDouble& x, wxDouble&y);
70
71 // adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
72 virtual void AddArc( wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise );
73
74 //
75 // These are convenience functions which - if not available natively will be assembled
76 // using the primitives from above
77 //
78
79 // adds a quadratic Bezier curve from the current point, using a control point and an end point
80 virtual void AddQuadCurveToPoint( wxDouble cx, wxDouble cy, wxDouble x, wxDouble y );
81
82 // appends a rectangle as a new closed subpath
83 virtual void AddRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h );
84
85 // appends an ellipsis as a new closed subpath fitting the passed rectangle
86 virtual void AddCircle( wxDouble x, wxDouble y, wxDouble r );
87
88 // draws a an arc to two tangents connecting (current) to (x1,y1) and (x1,y1) to (x2,y2), also a straight line from (current) to (x1,y1)
89 virtual void AddArcToPoint( wxDouble x1, wxDouble y1 , wxDouble x2, wxDouble y2, wxDouble r );
90
91 CGPathRef GetPath() const;
92private :
93 CGMutablePathRef m_path;
94};
95
96wxMacCoreGraphicsPath::wxMacCoreGraphicsPath()
97{
98 m_path = CGPathCreateMutable();
99}
100
101wxMacCoreGraphicsPath::~wxMacCoreGraphicsPath()
102{
103 CGPathRelease( m_path );
104}
105
106// opens (starts) a new subpath
107void wxMacCoreGraphicsPath::MoveToPoint( wxDouble x1 , wxDouble y1 )
108{
109 CGPathMoveToPoint( m_path , NULL , x1 , y1 );
110}
111
112void wxMacCoreGraphicsPath::AddLineToPoint( wxDouble x1 , wxDouble y1 )
113{
114 CGPathAddLineToPoint( m_path , NULL , x1 , y1 );
115}
116
117void wxMacCoreGraphicsPath::AddCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble cx2, wxDouble cy2, wxDouble x, wxDouble y )
118{
119 CGPathAddCurveToPoint( m_path , NULL , cx1 , cy1 , cx2, cy2, x , y );
120}
121
122void wxMacCoreGraphicsPath::AddQuadCurveToPoint( wxDouble cx1, wxDouble cy1, wxDouble x, wxDouble y )
123{
124 CGPathAddQuadCurveToPoint( m_path , NULL , cx1 , cy1 , x , y );
125}
126
127void wxMacCoreGraphicsPath::AddRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h )
128{
129 CGRect cgRect = { { x , y } , { w , h } };
130 CGPathAddRect( m_path , NULL , cgRect );
131}
132
133void wxMacCoreGraphicsPath::AddCircle( wxDouble x, wxDouble y , wxDouble r )
134{
135 CGPathAddArc( m_path , NULL , x , y , r , 0.0 , 2 * M_PI , true );
136}
137
138// adds an arc of a circle centering at (x,y) with radius (r) from startAngle to endAngle
139void wxMacCoreGraphicsPath::AddArc( wxDouble x, wxDouble y, wxDouble r, wxDouble startAngle, wxDouble endAngle, bool clockwise )
140{
141 CGPathAddArc( m_path, NULL , x, y, r, startAngle, endAngle, clockwise);
142}
143
144void wxMacCoreGraphicsPath::AddArcToPoint( wxDouble x1, wxDouble y1 , wxDouble x2, wxDouble y2, wxDouble r )
145{
146 CGPathAddArcToPoint( m_path, NULL , x1, y1, x2, y2, r);
147}
148
149// closes the current subpath
150void wxMacCoreGraphicsPath::CloseSubpath()
151{
152 CGPathCloseSubpath( m_path );
153}
154
155CGPathRef wxMacCoreGraphicsPath::GetPath() const
156{
157 return m_path;
158}
159
160// gets the last point of the current path, (0,0) if not yet set
161void wxMacCoreGraphicsPath::GetCurrentPoint( wxDouble& x, wxDouble&y)
162{
163 CGPoint p = CGPathGetCurrentPoint( m_path );
164 x = p.x;
165 y = p.y;
166}
167
168//
169// Graphics Context
170//
171
172class WXDLLEXPORT wxMacCoreGraphicsContext : public wxGraphicsContext
173{
174 DECLARE_NO_COPY_CLASS(wxMacCoreGraphicsContext)
175
176public:
177 wxMacCoreGraphicsContext( CGrafPtr port );
178
179 wxMacCoreGraphicsContext( CGContextRef cgcontext );
180
181 wxMacCoreGraphicsContext();
182
183 ~wxMacCoreGraphicsContext();
184
185
186 // creates a path instance that corresponds to the type of graphics context, ie GDIPlus, cairo, CoreGraphics ...
187 virtual wxGraphicsPath * CreatePath();
188
189 // push the current state of the context, ie the transformation matrix on a stack
190 virtual void PushState();
191
192 // pops a stored state from the stack
193 virtual void PopState();
194
195 // clips drawings to the region
196 virtual void Clip( const wxRegion &region );
197
198 //
199 // transformation
200 //
201
202 // translate
203 virtual void Translate( wxDouble dx , wxDouble dy );
204
205 // scale
206 virtual void Scale( wxDouble xScale , wxDouble yScale );
207
208 // rotate (radians)
209 virtual void Rotate( wxDouble angle );
210
211 //
212 // setting the paint
213 //
214
215 // sets the pan
216 virtual void SetPen( const wxPen &pen );
217
218 // sets the brush for filling
219 virtual void SetBrush( const wxBrush &brush );
220
221 // sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
222 virtual void SetLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2,
223 const wxColour&c1, const wxColour&c2);
224
225 // sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
226 // with radius r and color cColor
227 virtual void SetRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius,
228 const wxColour &oColor, const wxColour &cColor);
229
230 // sets the font
231 virtual void SetFont( const wxFont &font );
232
233 // sets the text color
234 virtual void SetTextColor( const wxColour &col );
235
236 // strokes along a path with the current pen
237 virtual void StrokePath( const wxGraphicsPath *path );
238
239 // fills a path with the current brush
240 virtual void FillPath( const wxGraphicsPath *path, int fillStyle = wxWINDING_RULE );
241
242 // draws a path by first filling and then stroking
243 virtual void DrawPath( const wxGraphicsPath *path, int fillStyle = wxWINDING_RULE );
244
245 //
246 // text
247 //
248
249 virtual void DrawText( const wxString &str, wxDouble x, wxDouble y );
250
251 virtual void DrawText( const wxString &str, wxDouble x, wxDouble y, wxDouble angle );
252
253 virtual void GetTextExtent( const wxString &text, wxDouble *width, wxDouble *height,
254 wxDouble *descent, wxDouble *externalLeading ) const;
255
256 virtual void GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const;
257
258 //
259 // image support
260 //
261
262 virtual void DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h );
263
264 virtual void DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h );
265
266 CGContextRef GetNativeContext();
267 void SetNativeContext( CGContextRef cg );
268 CGPathDrawingMode GetDrawingMode() const { return m_mode; }
269
270private:
271 CGContextRef m_cgContext;
272 CGrafPtr m_qdPort;
273 CGPathDrawingMode m_mode;
274 ATSUStyle m_macATSUIStyle;
275 wxPen m_pen;
276 wxBrush m_brush;
277 wxColor m_textForegroundColor;
278};
279
280//-----------------------------------------------------------------------------
281// constants
282//-----------------------------------------------------------------------------
283
284#if !defined( __DARWIN__ ) || defined(__MWERKS__)
285#ifndef M_PI
286const double M_PI = 3.14159265358979;
287#endif
288#endif
289
290const double RAD2DEG = 180.0 / M_PI;
291const short kEmulatedMode = -1;
292const short kUnsupportedMode = -2;
293
294extern TECObjectRef s_TECNativeCToUnicode;
295
296//-----------------------------------------------------------------------------
297// Local functions
298//-----------------------------------------------------------------------------
299
300static inline double dmin(double a, double b) { return a < b ? a : b; }
301static inline double dmax(double a, double b) { return a > b ? a : b; }
302static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
303
304//-----------------------------------------------------------------------------
305// device context implementation
306//
307// more and more of the dc functionality should be implemented by calling
308// the appropricate wxMacCoreGraphicsContext, but we will have to do that step by step
309// also coordinate conversions should be moved to native matrix ops
310//-----------------------------------------------------------------------------
311
312// we always stock two context states, one at entry, to be able to preserve the
313// state we were called with, the other one after changing to HI Graphics orientation
314// (this one is used for getting back clippings etc)
315
316//-----------------------------------------------------------------------------
317// wxGraphicsPath implementation
318//-----------------------------------------------------------------------------
319
320//-----------------------------------------------------------------------------
321// wxGraphicsContext implementation
322//-----------------------------------------------------------------------------
323
324wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGrafPtr port )
325{
326 m_qdPort = port;
327 m_cgContext = NULL;
328 m_mode = kCGPathFill;
329 m_macATSUIStyle = NULL;
330}
331
332wxMacCoreGraphicsContext::wxMacCoreGraphicsContext( CGContextRef cgcontext )
333{
334 m_qdPort = NULL;
335 m_cgContext = cgcontext;
336 m_mode = kCGPathFill;
337 m_macATSUIStyle = NULL;
338 CGContextSaveGState( m_cgContext );
339 CGContextSaveGState( m_cgContext );
340}
341
342wxMacCoreGraphicsContext::wxMacCoreGraphicsContext()
343{
344 m_qdPort = NULL;
345 m_cgContext = NULL;
346 m_mode = kCGPathFill;
347 m_macATSUIStyle = NULL;
348}
349
350wxMacCoreGraphicsContext::~wxMacCoreGraphicsContext()
351{
352 if ( m_cgContext )
353 {
354 CGContextSynchronize( m_cgContext );
355 CGContextRestoreGState( m_cgContext );
356 CGContextRestoreGState( m_cgContext );
357 }
358
359 if ( m_qdPort )
360 CGContextRelease( m_cgContext );
361}
362
363void wxMacCoreGraphicsContext::Clip( const wxRegion &region )
364{
365// ClipCGContextToRegion ( m_cgContext, &bounds , (RgnHandle) dc->m_macCurrentClipRgn );
366}
367
368void wxMacCoreGraphicsContext::StrokePath( const wxGraphicsPath *p )
369{
370 const wxMacCoreGraphicsPath* path = dynamic_cast< const wxMacCoreGraphicsPath*>( p );
371 CGContextAddPath( m_cgContext , path->GetPath() );
372 CGContextStrokePath( m_cgContext );
373}
374
375void wxMacCoreGraphicsContext::DrawPath( const wxGraphicsPath *p , int fillStyle )
376{
377 const wxMacCoreGraphicsPath* path = dynamic_cast< const wxMacCoreGraphicsPath*>( p );
378 CGPathDrawingMode mode = m_mode;
379
380 if ( fillStyle == wxODDEVEN_RULE )
381 {
382 if ( mode == kCGPathFill )
383 mode = kCGPathEOFill;
384 else if ( mode == kCGPathFillStroke )
385 mode = kCGPathEOFillStroke;
386 }
387
388 CGContextAddPath( m_cgContext , path->GetPath() );
389 CGContextDrawPath( m_cgContext , mode );
390}
391
392void wxMacCoreGraphicsContext::FillPath( const wxGraphicsPath *p , int fillStyle )
393{
394 const wxMacCoreGraphicsPath* path = dynamic_cast< const wxMacCoreGraphicsPath*>( p );
395
396 CGContextAddPath( m_cgContext , path->GetPath() );
397 if ( fillStyle == wxODDEVEN_RULE )
398 CGContextEOFillPath( m_cgContext );
399 else
400 CGContextFillPath( m_cgContext );
401}
402
403wxGraphicsPath* wxMacCoreGraphicsContext::CreatePath()
404{
405 // make sure that we now have a real cgref, before doing
406 // anything with paths
407 CGContextRef cg = GetNativeContext();
408 cg = NULL;
409
410 return new wxMacCoreGraphicsPath();
411}
412
413CGContextRef wxMacCoreGraphicsContext::GetNativeContext()
414{
415 return m_cgContext;
416}
417
418void wxMacCoreGraphicsContext::SetNativeContext( CGContextRef cg )
419{
420 // we allow either setting or clearing but not replacing
421 wxASSERT( m_cgContext == NULL || cg == NULL );
422
423 if ( cg )
424 CGContextSaveGState( cg );
425 m_cgContext = cg;
426}
427
428void wxMacCoreGraphicsContext::Translate( wxDouble dx , wxDouble dy )
429{
430 CGContextTranslateCTM( m_cgContext, dx, dy );
431}
432
433void wxMacCoreGraphicsContext::Scale( wxDouble xScale , wxDouble yScale )
434{
435 CGContextScaleCTM( m_cgContext , xScale , yScale );
436}
437
438void wxMacCoreGraphicsContext::Rotate( wxDouble angle )
439{
440 CGContextRotateCTM( m_cgContext , angle );
441}
442
443void wxMacCoreGraphicsContext::DrawBitmap( const wxBitmap &bmp, wxDouble x, wxDouble y, wxDouble w, wxDouble h )
444{
445 CGImageRef image = (CGImageRef)( bmp.CGImageCreate() );
446 HIRect r = CGRectMake( x , y , w , h );
447 HIViewDrawCGImage( m_cgContext , &r , image );
448 CGImageRelease( image );
449}
450
451void wxMacCoreGraphicsContext::DrawIcon( const wxIcon &icon, wxDouble x, wxDouble y, wxDouble w, wxDouble h )
452{
453 CGRect r = CGRectMake( 00 , 00 , w , h );
454 CGContextSaveGState( m_cgContext );
455 CGContextTranslateCTM( m_cgContext, x , y + h );
456 CGContextScaleCTM( m_cgContext, 1, -1 );
457 PlotIconRefInContext( m_cgContext , &r , kAlignNone , kTransformNone ,
458 NULL , kPlotIconRefNormalFlags , MAC_WXHICON( icon.GetHICON() ) );
459 CGContextRestoreGState( m_cgContext );
460}
461
462void wxMacCoreGraphicsContext::PushState()
463{
464 CGContextSaveGState( m_cgContext );
465}
466
467void wxMacCoreGraphicsContext::PopState()
468{
469 CGContextRestoreGState( m_cgContext );
470}
471
472void wxMacCoreGraphicsContext::SetTextColor( const wxColour &col )
473{
474 m_textForegroundColor = col;
475}
476
477#pragma mark -
478#pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes
479
480// CGPattern wrapper class: always allocate on heap, never call destructor
481
482class wxMacCoreGraphicsPattern
483{
484public :
485 wxMacCoreGraphicsPattern() {}
486
487 // is guaranteed to be called only with a non-Null CGContextRef
488 virtual void Render( CGContextRef ctxRef ) = 0;
489
490 operator CGPatternRef() const { return m_patternRef; }
491
492protected :
493 virtual ~wxMacCoreGraphicsPattern()
494 {
495 // as this is called only when the m_patternRef is been released;
496 // don't release it again
497 }
498
499 static void _Render( void *info, CGContextRef ctxRef )
500 {
501 wxMacCoreGraphicsPattern* self = (wxMacCoreGraphicsPattern*) info;
502 if ( self && ctxRef )
503 self->Render( ctxRef );
504 }
505
506 static void _Dispose( void *info )
507 {
508 wxMacCoreGraphicsPattern* self = (wxMacCoreGraphicsPattern*) info;
509 delete self;
510 }
511
512 CGPatternRef m_patternRef;
513
514 static const CGPatternCallbacks ms_Callbacks;
515};
516
517const CGPatternCallbacks wxMacCoreGraphicsPattern::ms_Callbacks = { 0, &wxMacCoreGraphicsPattern::_Render, &wxMacCoreGraphicsPattern::_Dispose };
518
519class ImagePattern : public wxMacCoreGraphicsPattern
520{
521public :
522 ImagePattern( const wxBitmap* bmp , CGAffineTransform transform )
523 {
524 wxASSERT( bmp && bmp->Ok() );
525
526 Init( (CGImageRef) bmp->CGImageCreate() , transform );
527 }
528
529 // ImagePattern takes ownership of CGImageRef passed in
530 ImagePattern( CGImageRef image , CGAffineTransform transform )
531 {
532 if ( image )
533 CFRetain( image );
534
535 Init( image , transform );
536 }
537
538 virtual void Render( CGContextRef ctxRef )
539 {
540 if (m_image != NULL)
541 HIViewDrawCGImage( ctxRef, &m_imageBounds, m_image );
542 }
543
544protected :
545 void Init( CGImageRef image, CGAffineTransform transform )
546 {
547 m_image = image;
548 if ( m_image )
549 {
550 m_imageBounds = CGRectMake( 0.0, 0.0, (CGFloat)CGImageGetWidth( m_image ), (CGFloat)CGImageGetHeight( m_image ) );
551 m_patternRef = CGPatternCreate(
552 this , m_imageBounds, transform ,
553 m_imageBounds.size.width, m_imageBounds.size.height,
554 kCGPatternTilingNoDistortion, true , &wxMacCoreGraphicsPattern::ms_Callbacks );
555 }
556 }
557
558 virtual ~ImagePattern()
559 {
560 if ( m_image )
561 CGImageRelease( m_image );
562 }
563
564 CGImageRef m_image;
565 CGRect m_imageBounds;
566};
567
568class HatchPattern : public wxMacCoreGraphicsPattern
569{
570public :
571 HatchPattern( int hatchstyle, CGAffineTransform transform )
572 {
573 m_hatch = hatchstyle;
574 m_imageBounds = CGRectMake( 0.0, 0.0, 8.0 , 8.0 );
575 m_patternRef = CGPatternCreate(
576 this , m_imageBounds, transform ,
577 m_imageBounds.size.width, m_imageBounds.size.height,
578 kCGPatternTilingNoDistortion, false , &wxMacCoreGraphicsPattern::ms_Callbacks );
579 }
580
581 void StrokeLineSegments( CGContextRef ctxRef , const CGPoint pts[] , size_t count )
582 {
583#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
584 if ( UMAGetSystemVersion() >= 0x1040 )
585 {
586 CGContextStrokeLineSegments( ctxRef , pts , count );
587 }
588 else
589#endif
590 {
591 CGContextBeginPath( ctxRef );
592 for (size_t i = 0; i < count; i += 2)
593 {
594 CGContextMoveToPoint(ctxRef, pts[i].x, pts[i].y);
595 CGContextAddLineToPoint(ctxRef, pts[i+1].x, pts[i+1].y);
596 }
597 CGContextStrokePath(ctxRef);
598 }
599 }
600
601 virtual void Render( CGContextRef ctxRef )
602 {
603 switch ( m_hatch )
604 {
605 case wxBDIAGONAL_HATCH :
606 {
607 CGPoint pts[] =
608 {
609 { 8.0 , 0.0 } , { 0.0 , 8.0 }
610 };
611 StrokeLineSegments( ctxRef , pts , 2 );
612 }
613 break;
614
615 case wxCROSSDIAG_HATCH :
616 {
617 CGPoint pts[] =
618 {
619 { 0.0 , 0.0 } , { 8.0 , 8.0 } ,
620 { 8.0 , 0.0 } , { 0.0 , 8.0 }
621 };
622 StrokeLineSegments( ctxRef , pts , 4 );
623 }
624 break;
625
626 case wxFDIAGONAL_HATCH :
627 {
628 CGPoint pts[] =
629 {
630 { 0.0 , 0.0 } , { 8.0 , 8.0 }
631 };
632 StrokeLineSegments( ctxRef , pts , 2 );
633 }
634 break;
635
636 case wxCROSS_HATCH :
637 {
638 CGPoint pts[] =
639 {
640 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
641 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
642 };
643 StrokeLineSegments( ctxRef , pts , 4 );
644 }
645 break;
646
647 case wxHORIZONTAL_HATCH :
648 {
649 CGPoint pts[] =
650 {
651 { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
652 };
653 StrokeLineSegments( ctxRef , pts , 2 );
654 }
655 break;
656
657 case wxVERTICAL_HATCH :
658 {
659 CGPoint pts[] =
660 {
661 { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
662 };
663 StrokeLineSegments( ctxRef , pts , 2 );
664 }
665 break;
666
667 default:
668 break;
669 }
670 }
671
672protected :
673 virtual ~HatchPattern() {}
674
675 CGRect m_imageBounds;
676 int m_hatch;
677};
678
679#pragma mark -
680
681void wxMacCoreGraphicsContext::SetPen( const wxPen &pen )
682{
683 m_pen = pen;
684 if ( m_cgContext == NULL )
685 return;
686
687 bool fill = m_brush.GetStyle() != wxTRANSPARENT;
688 bool stroke = pen.GetStyle() != wxTRANSPARENT;
689
690#if 0
691 // we can benchmark performance; should go into a setting eventually
692 CGContextSetShouldAntialias( m_cgContext , false );
693#endif
694
695 if ( fill | stroke )
696 {
697 // set up brushes
698 m_mode = kCGPathFill; // just a default
699
700 if ( stroke )
701 {
702 CGContextSetRGBStrokeColor( m_cgContext , pen.GetColour().Red() / 255.0 , pen.GetColour().Green() / 255.0 ,
703 pen.GetColour().Blue() / 255.0 , pen.GetColour().Alpha() / 255.0 );
704
705 // TODO: * m_dc->m_scaleX
706 CGFloat penWidth = pen.GetWidth();
707 if (penWidth <= 0.0)
708 penWidth = 0.1;
709 CGContextSetLineWidth( m_cgContext , penWidth );
710
711 CGLineCap cap;
712 switch ( pen.GetCap() )
713 {
714 case wxCAP_ROUND :
715 cap = kCGLineCapRound;
716 break;
717
718 case wxCAP_PROJECTING :
719 cap = kCGLineCapSquare;
720 break;
721
722 case wxCAP_BUTT :
723 cap = kCGLineCapButt;
724 break;
725
726 default :
727 cap = kCGLineCapButt;
728 break;
729 }
730
731 CGLineJoin join;
732 switch ( pen.GetJoin() )
733 {
734 case wxJOIN_BEVEL :
735 join = kCGLineJoinBevel;
736 break;
737
738 case wxJOIN_MITER :
739 join = kCGLineJoinMiter;
740 break;
741
742 case wxJOIN_ROUND :
743 join = kCGLineJoinRound;
744 break;
745
746 default :
747 join = kCGLineJoinMiter;
748 break;
749 }
750
751 m_mode = kCGPathStroke;
752 int count = 0;
753
754 const CGFloat *lengths = NULL;
755 CGFloat *userLengths = NULL;
756
757 const CGFloat dashUnit = penWidth < 1.0 ? 1.0 : penWidth;
758
759 const CGFloat dotted[] = { dashUnit , dashUnit + 2.0 };
760 const CGFloat short_dashed[] = { 9.0 , 6.0 };
761 const CGFloat dashed[] = { 19.0 , 9.0 };
762 const CGFloat dotted_dashed[] = { 9.0 , 6.0 , 3.0 , 3.0 };
763
764 switch ( pen.GetStyle() )
765 {
766 case wxSOLID :
767 break;
768
769 case wxDOT :
770 lengths = dotted;
771 count = WXSIZEOF(dotted);
772 break;
773
774 case wxLONG_DASH :
775 lengths = dashed;
776 count = WXSIZEOF(dashed);
777 break;
778
779 case wxSHORT_DASH :
780 lengths = short_dashed;
781 count = WXSIZEOF(short_dashed);
782 break;
783
784 case wxDOT_DASH :
785 lengths = dotted_dashed;
786 count = WXSIZEOF(dotted_dashed);
787 break;
788
789 case wxUSER_DASH :
790 wxDash *dashes;
791 count = pen.GetDashes( &dashes );
792 if ((dashes != NULL) && (count > 0))
793 {
794 userLengths = new CGFloat[count];
795 for ( int i = 0; i < count; ++i )
796 {
797 userLengths[i] = dashes[i] * dashUnit;
798
799 if ( i % 2 == 1 && userLengths[i] < dashUnit + 2.0 )
800 userLengths[i] = dashUnit + 2.0;
801 else if ( i % 2 == 0 && userLengths[i] < dashUnit )
802 userLengths[i] = dashUnit;
803 }
804 }
805 lengths = userLengths;
806 break;
807
808 case wxSTIPPLE :
809 {
810 CGFloat alphaArray[1] = { 1.0 };
811 wxBitmap* bmp = pen.GetStipple();
812 if ( bmp && bmp->Ok() )
813 {
814 wxMacCFRefHolder<CGColorSpaceRef> patternSpace( CGColorSpaceCreatePattern( NULL ) );
815 CGContextSetStrokeColorSpace( m_cgContext , patternSpace );
816 wxMacCFRefHolder<CGPatternRef> pattern( *( new ImagePattern( bmp , CGContextGetCTM( m_cgContext ) ) ) );
817 CGContextSetStrokePattern( m_cgContext, pattern , alphaArray );
818 }
819 }
820 break;
821
822 default :
823 {
824 wxMacCFRefHolder<CGColorSpaceRef> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
825 CGContextSetStrokeColorSpace( m_cgContext , patternSpace );
826 wxMacCFRefHolder<CGPatternRef> pattern( *( new HatchPattern( pen.GetStyle() , CGContextGetCTM( m_cgContext ) ) ) );
827
828 CGFloat colorArray[4] = { pen.GetColour().Red() / 255.0 , pen.GetColour().Green() / 255.0 ,
829 pen.GetColour().Blue() / 255.0 , pen.GetColour().Alpha() / 255.0 };
830
831 CGContextSetStrokePattern( m_cgContext, pattern , colorArray );
832 }
833 break;
834 }
835
836 if ((lengths != NULL) && (count > 0))
837 {
838 CGContextSetLineDash( m_cgContext , 0 , lengths , count );
839 // force the line cap, otherwise we get artifacts (overlaps) and just solid lines
840 cap = kCGLineCapButt;
841 }
842 else
843 {
844 CGContextSetLineDash( m_cgContext , 0 , NULL , 0 );
845 }
846
847 CGContextSetLineCap( m_cgContext , cap );
848 CGContextSetLineJoin( m_cgContext , join );
849
850 delete[] userLengths;
851 }
852
853 if ( fill && stroke )
854 m_mode = kCGPathFillStroke;
855 }
856}
857
858void wxMacCoreGraphicsContext::SetBrush( const wxBrush &brush )
859{
860 m_brush = brush;
861 if ( m_cgContext == NULL )
862 return;
863
864 bool fill = brush.GetStyle() != wxTRANSPARENT;
865 bool stroke = m_pen.GetStyle() != wxTRANSPARENT;
866
867#if 0
868 // we can benchmark performance, should go into a setting later
869 CGContextSetShouldAntialias( m_cgContext , false );
870#endif
871
872 if ( fill | stroke )
873 {
874 // setup brushes
875 m_mode = kCGPathFill; // just a default
876
877 if ( fill )
878 {
879 if ( brush.GetStyle() == wxSOLID )
880 {
881 CGContextSetRGBFillColor( m_cgContext , brush.GetColour().Red() / 255.0 , brush.GetColour().Green() / 255.0 ,
882 brush.GetColour().Blue() / 255.0 , brush.GetColour().Alpha() / 255.0 );
883 }
884 else if ( brush.IsHatch() )
885 {
886 wxMacCFRefHolder<CGColorSpaceRef> patternSpace( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
887 CGContextSetFillColorSpace( m_cgContext , patternSpace );
888 wxMacCFRefHolder<CGPatternRef> pattern( *( new HatchPattern( brush.GetStyle() , CGContextGetCTM( m_cgContext ) ) ) );
889
890 CGFloat colorArray[4] = { brush.GetColour().Red() / 255.0 , brush.GetColour().Green() / 255.0 ,
891 brush.GetColour().Blue() / 255.0 , brush.GetColour().Alpha() / 255.0 };
892
893 CGContextSetFillPattern( m_cgContext, pattern , colorArray );
894 }
895 else
896 {
897 // now brush is a bitmap
898 CGFloat alphaArray[1] = { 1.0 };
899 wxBitmap* bmp = brush.GetStipple();
900 if ( bmp && bmp->Ok() )
901 {
902 wxMacCFRefHolder<CGColorSpaceRef> patternSpace( CGColorSpaceCreatePattern( NULL ) );
903 CGContextSetFillColorSpace( m_cgContext , patternSpace );
904 wxMacCFRefHolder<CGPatternRef> pattern( *( new ImagePattern( bmp , CGContextGetCTM( m_cgContext ) ) ) );
905 CGContextSetFillPattern( m_cgContext, pattern , alphaArray );
906 }
907 }
908
909 m_mode = kCGPathFill;
910 }
911
912 if ( fill && stroke )
913 m_mode = kCGPathFillStroke;
914 else if ( stroke )
915 m_mode = kCGPathStroke;
916 }
917}
918
919// sets the brush to a linear gradient, starting at (x1,y1) with color c1 to (x2,y2) with color c2
920void wxMacCoreGraphicsContext::SetLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2,
921 const wxColour&c1, const wxColour&c2)
922{
923}
924
925// sets the brush to a radial gradient originating at (xo,yc) with color oColor and ends on a circle around (xc,yc)
926// with radius r and color cColor
927void wxMacCoreGraphicsContext::SetRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius,
928 const wxColour &oColor, const wxColour &cColor)
929{
930}
931
932
933void wxMacCoreGraphicsContext::DrawText( const wxString &str, wxDouble x, wxDouble y )
934{
935 DrawText(str, x, y, 0.0);
936}
937
938void wxMacCoreGraphicsContext::DrawText( const wxString &str, wxDouble x, wxDouble y, double angle )
939{
940 OSStatus status = noErr;
941 ATSUTextLayout atsuLayout;
942 UniCharCount chars = str.length();
943 UniChar* ubuf = NULL;
944
945#if SIZEOF_WCHAR_T == 4
946 wxMBConvUTF16 converter;
947#if wxUSE_UNICODE
948 size_t unicharlen = converter.WC2MB( NULL , str.wc_str() , 0 );
949 ubuf = (UniChar*) malloc( unicharlen + 2 );
950 converter.WC2MB( (char*) ubuf , str.wc_str(), unicharlen + 2 );
951#else
952 const wxWCharBuffer wchar = str.wc_str( wxConvLocal );
953 size_t unicharlen = converter.WC2MB( NULL , wchar.data() , 0 );
954 ubuf = (UniChar*) malloc( unicharlen + 2 );
955 converter.WC2MB( (char*) ubuf , wchar.data() , unicharlen + 2 );
956#endif
957 chars = unicharlen / 2;
958#else
959#if wxUSE_UNICODE
960 ubuf = (UniChar*) str.wc_str();
961#else
962 wxWCharBuffer wchar = str.wc_str( wxConvLocal );
963 chars = wxWcslen( wchar.data() );
964 ubuf = (UniChar*) wchar.data();
965#endif
966#endif
967
968 status = ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr) ubuf , 0 , chars , chars , 1 ,
969 &chars , (ATSUStyle*) &m_macATSUIStyle , &atsuLayout );
970
971 wxASSERT_MSG( status == noErr , wxT("couldn't create the layout of the rotated text") );
972
973 status = ::ATSUSetTransientFontMatching( atsuLayout , true );
974 wxASSERT_MSG( status == noErr , wxT("couldn't setup transient font matching") );
975
976 int iAngle = int( angle * RAD2DEG );
977 if ( abs(iAngle) > 0 )
978 {
979 Fixed atsuAngle = IntToFixed( iAngle );
980 ATSUAttributeTag atsuTags[] =
981 {
982 kATSULineRotationTag ,
983 };
984 ByteCount atsuSizes[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] =
985 {
986 sizeof( Fixed ) ,
987 };
988 ATSUAttributeValuePtr atsuValues[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] =
989 {
990 &atsuAngle ,
991 };
992 status = ::ATSUSetLayoutControls(atsuLayout , sizeof(atsuTags) / sizeof(ATSUAttributeTag),
993 atsuTags, atsuSizes, atsuValues );
994 }
995
996 {
997 ATSUAttributeTag atsuTags[] =
998 {
999 kATSUCGContextTag ,
1000 };
1001 ByteCount atsuSizes[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] =
1002 {
1003 sizeof( CGContextRef ) ,
1004 };
1005 ATSUAttributeValuePtr atsuValues[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] =
1006 {
1007 &m_cgContext ,
1008 };
1009 status = ::ATSUSetLayoutControls(atsuLayout , sizeof(atsuTags) / sizeof(ATSUAttributeTag),
1010 atsuTags, atsuSizes, atsuValues );
1011 }
1012
1013 ATSUTextMeasurement textBefore, textAfter;
1014 ATSUTextMeasurement ascent, descent;
1015
1016 status = ::ATSUGetUnjustifiedBounds( atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd,
1017 &textBefore , &textAfter, &ascent , &descent );
1018
1019 wxASSERT_MSG( status == noErr , wxT("couldn't measure the rotated text") );
1020
1021 Rect rect;
1022/*
1023 // TODO
1024 if ( m_backgroundMode == wxSOLID )
1025 {
1026 wxGraphicsPath* path = m_graphicContext->CreatePath();
1027 path->MoveToPoint( drawX , drawY );
1028 path->AddLineToPoint(
1029 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent)) ,
1030 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent)) );
1031 path->AddLineToPoint(
1032 (int) (drawX + sin(angle / RAD2DEG) * FixedToInt(ascent + descent ) + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1033 (int) (drawY + cos(angle / RAD2DEG) * FixedToInt(ascent + descent) - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1034 path->AddLineToPoint(
1035 (int) (drawX + cos(angle / RAD2DEG) * FixedToInt(textAfter)) ,
1036 (int) (drawY - sin(angle / RAD2DEG) * FixedToInt(textAfter)) );
1037
1038 m_graphicContext->FillPath( path , m_textBackgroundColour );
1039 delete path;
1040 }
1041*/
1042 x += (int)(sin(angle / RAD2DEG) * FixedToInt(ascent));
1043 y += (int)(cos(angle / RAD2DEG) * FixedToInt(ascent));
1044
1045 status = ::ATSUMeasureTextImage( atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd,
1046 IntToFixed(x) , IntToFixed(y) , &rect );
1047 wxASSERT_MSG( status == noErr , wxT("couldn't measure the rotated text") );
1048
1049 CGContextSaveGState(m_cgContext);
1050 CGContextTranslateCTM(m_cgContext, x, y);
1051 CGContextScaleCTM(m_cgContext, 1, -1);
1052 status = ::ATSUDrawText( atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd,
1053 IntToFixed(0) , IntToFixed(0) );
1054
1055 wxASSERT_MSG( status == noErr , wxT("couldn't draw the rotated text") );
1056
1057 CGContextRestoreGState(m_cgContext);
1058
1059 ::ATSUDisposeTextLayout(atsuLayout);
1060
1061#if SIZEOF_WCHAR_T == 4
1062 free( ubuf );
1063#endif
1064}
1065
1066void wxMacCoreGraphicsContext::GetTextExtent( const wxString &str, wxDouble *width, wxDouble *height,
1067 wxDouble *descent, wxDouble *externalLeading ) const
1068{
1069 wxCHECK_RET( m_macATSUIStyle != NULL, wxT("wxDC(cg)::DoGetTextExtent - no valid font set") );
1070
1071 OSStatus status = noErr;
1072
1073 ATSUTextLayout atsuLayout;
1074 UniCharCount chars = str.length();
1075 UniChar* ubuf = NULL;
1076
1077#if SIZEOF_WCHAR_T == 4
1078 wxMBConvUTF16 converter;
1079#if wxUSE_UNICODE
1080 size_t unicharlen = converter.WC2MB( NULL , str.wc_str() , 0 );
1081 ubuf = (UniChar*) malloc( unicharlen + 2 );
1082 converter.WC2MB( (char*) ubuf , str.wc_str(), unicharlen + 2 );
1083#else
1084 const wxWCharBuffer wchar = str.wc_str( wxConvLocal );
1085 size_t unicharlen = converter.WC2MB( NULL , wchar.data() , 0 );
1086 ubuf = (UniChar*) malloc( unicharlen + 2 );
1087 converter.WC2MB( (char*) ubuf , wchar.data() , unicharlen + 2 );
1088#endif
1089 chars = unicharlen / 2;
1090#else
1091#if wxUSE_UNICODE
1092 ubuf = (UniChar*) str.wc_str();
1093#else
1094 wxWCharBuffer wchar = str.wc_str( wxConvLocal );
1095 chars = wxWcslen( wchar.data() );
1096 ubuf = (UniChar*) wchar.data();
1097#endif
1098#endif
1099
1100 status = ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr) ubuf , 0 , chars , chars , 1 ,
1101 &chars , (ATSUStyle*) &m_macATSUIStyle , &atsuLayout );
1102
1103 wxASSERT_MSG( status == noErr , wxT("couldn't create the layout of the text") );
1104
1105 ATSUTextMeasurement textBefore, textAfter;
1106 ATSUTextMeasurement textAscent, textDescent;
1107
1108 status = ::ATSUGetUnjustifiedBounds( atsuLayout, kATSUFromTextBeginning, kATSUToTextEnd,
1109 &textBefore , &textAfter, &textAscent , &textDescent );
1110
1111 if ( height )
1112 *height = FixedToInt(textAscent + textDescent);
1113 if ( descent )
1114 *descent = FixedToInt(textDescent);
1115 if ( externalLeading )
1116 *externalLeading = 0;
1117 if ( width )
1118 *width = FixedToInt(textAfter - textBefore);
1119
1120 ::ATSUDisposeTextLayout(atsuLayout);
1121}
1122
1123void wxMacCoreGraphicsContext::GetPartialTextExtents(const wxString& text, wxArrayDouble& widths) const
1124{
1125 widths.Empty();
1126 widths.Add(0, text.length());
1127
1128 if (text.empty())
1129 return;
1130
1131 ATSUTextLayout atsuLayout;
1132 UniCharCount chars = text.length();
1133 UniChar* ubuf = NULL;
1134
1135#if SIZEOF_WCHAR_T == 4
1136 wxMBConvUTF16 converter;
1137#if wxUSE_UNICODE
1138 size_t unicharlen = converter.WC2MB( NULL , text.wc_str() , 0 );
1139 ubuf = (UniChar*) malloc( unicharlen + 2 );
1140 converter.WC2MB( (char*) ubuf , text.wc_str(), unicharlen + 2 );
1141#else
1142 const wxWCharBuffer wchar = text.wc_str( wxConvLocal );
1143 size_t unicharlen = converter.WC2MB( NULL , wchar.data() , 0 );
1144 ubuf = (UniChar*) malloc( unicharlen + 2 );
1145 converter.WC2MB( (char*) ubuf , wchar.data() , unicharlen + 2 );
1146#endif
1147 chars = unicharlen / 2;
1148#else
1149#if wxUSE_UNICODE
1150 ubuf = (UniChar*) text.wc_str();
1151#else
1152 wxWCharBuffer wchar = text.wc_str( wxConvLocal );
1153 chars = wxWcslen( wchar.data() );
1154 ubuf = (UniChar*) wchar.data();
1155#endif
1156#endif
1157
1158 OSStatus status;
1159 status = ::ATSUCreateTextLayoutWithTextPtr( (UniCharArrayPtr) ubuf , 0 , chars , chars , 1 ,
1160 &chars , (ATSUStyle*) &m_macATSUIStyle , &atsuLayout );
1161
1162 for ( int pos = 0; pos < (int)chars; pos ++ )
1163 {
1164 unsigned long actualNumberOfBounds = 0;
1165 ATSTrapezoid glyphBounds;
1166
1167 // We get a single bound, since the text should only require one. If it requires more, there is an issue
1168 OSStatus result;
1169 result = ATSUGetGlyphBounds( atsuLayout, 0, 0, kATSUFromTextBeginning, pos + 1,
1170 kATSUseDeviceOrigins, 1, &glyphBounds, &actualNumberOfBounds );
1171 if (result != noErr || actualNumberOfBounds != 1 )
1172 return;
1173
1174 widths[pos] = FixedToInt( glyphBounds.upperRight.x - glyphBounds.upperLeft.x );
1175 //unsigned char uch = s[i];
1176 }
1177
1178 ::ATSUDisposeTextLayout(atsuLayout);
1179}
1180
1181void wxMacCoreGraphicsContext::SetFont( const wxFont &font )
1182{
1183 if ( m_macATSUIStyle )
1184 {
1185 ::ATSUDisposeStyle((ATSUStyle)m_macATSUIStyle);
1186 m_macATSUIStyle = NULL;
1187 }
1188
1189 if ( font.Ok() )
1190 {
1191 OSStatus status;
1192
1193 status = ATSUCreateAndCopyStyle( (ATSUStyle) font.MacGetATSUStyle() , (ATSUStyle*) &m_macATSUIStyle );
1194
1195 wxASSERT_MSG( status == noErr, wxT("couldn't create ATSU style") );
1196
1197 // we need the scale here ...
1198
1199 Fixed atsuSize = IntToFixed( int( /*m_scaleY*/ 1 * font.MacGetFontSize()) );
1200 RGBColor atsuColor = MAC_WXCOLORREF( m_textForegroundColor.GetPixel() );
1201 ATSUAttributeTag atsuTags[] =
1202 {
1203 kATSUSizeTag ,
1204 kATSUColorTag ,
1205 };
1206 ByteCount atsuSizes[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] =
1207 {
1208 sizeof( Fixed ) ,
1209 sizeof( RGBColor ) ,
1210 };
1211 ATSUAttributeValuePtr atsuValues[sizeof(atsuTags) / sizeof(ATSUAttributeTag)] =
1212 {
1213 &atsuSize ,
1214 &atsuColor ,
1215 };
1216
1217 status = ::ATSUSetAttributes(
1218 (ATSUStyle)m_macATSUIStyle, sizeof(atsuTags) / sizeof(ATSUAttributeTag) ,
1219 atsuTags, atsuSizes, atsuValues);
1220
1221 wxASSERT_MSG( status == noErr , wxT("couldn't modify ATSU style") );
1222 }
1223}
1224
1225wxGraphicsContext* wxGraphicsContext::Create( const wxWindowDC &dc )
1226{
1227 return new wxMacCoreGraphicsContext((CGContextRef)dc.GetWindow()->MacGetCGContextRef() );
1228}
1229
1230#endif // wxMAC_USE_CORE_GRAPHICS