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