+#include "wx/graphics.h"
+
+#ifndef WX_PRECOMP
+ #include "wx/dcclient.h"
+ #include "wx/dcmemory.h"
+ #include "wx/log.h"
+ #include "wx/region.h"
+ #include "wx/image.h"
+ #include "wx/icon.h"
+#endif
+
+#include "wx/mac/uma.h"
+
+#ifdef __MSL__
+ #if __MSL__ >= 0x6000
+ #include "math.h"
+ // in case our functions were defined outside std, we make it known all the same
+ namespace std { }
+ using namespace std;
+ #endif
+#endif
+
+#include "wx/mac/private.h"
+
+//-----------------------------------------------------------------------------
+// constants
+//-----------------------------------------------------------------------------
+
+#if !defined( __DARWIN__ ) || defined(__MWERKS__)
+#ifndef M_PI
+const double M_PI = 3.14159265358979;
+#endif
+#endif
+
+static const double RAD2DEG = 180.0 / M_PI;
+
+//
+// Pen, Brushes and Fonts
+//
+
+#pragma mark -
+#pragma mark wxMacCoreGraphicsPattern, ImagePattern, HatchPattern classes
+
+OSStatus wxMacDrawCGImage(
+ CGContextRef inContext,
+ const HIRect * inBounds,
+ CGImageRef inImage)
+{
+#ifdef __LP64__
+ // todo flip
+ CGContextDrawImage(inContext, *inBounds, inImage );
+ return noErr;
+#else
+ return HIViewDrawCGImage( inContext, inBounds, inImage );
+#endif
+}
+
+// CGPattern wrapper class: always allocate on heap, never call destructor
+
+class wxMacCoreGraphicsPattern
+{
+public :
+ wxMacCoreGraphicsPattern() {}
+
+ // is guaranteed to be called only with a non-Null CGContextRef
+ virtual void Render( CGContextRef ctxRef ) = 0;
+
+ operator CGPatternRef() const { return m_patternRef; }
+
+protected :
+ virtual ~wxMacCoreGraphicsPattern()
+ {
+ // as this is called only when the m_patternRef is been released;
+ // don't release it again
+ }
+
+ static void _Render( void *info, CGContextRef ctxRef )
+ {
+ wxMacCoreGraphicsPattern* self = (wxMacCoreGraphicsPattern*) info;
+ if ( self && ctxRef )
+ self->Render( ctxRef );
+ }
+
+ static void _Dispose( void *info )
+ {
+ wxMacCoreGraphicsPattern* self = (wxMacCoreGraphicsPattern*) info;
+ delete self;
+ }
+
+ CGPatternRef m_patternRef;
+
+ static const CGPatternCallbacks ms_Callbacks;
+};
+
+const CGPatternCallbacks wxMacCoreGraphicsPattern::ms_Callbacks = { 0, &wxMacCoreGraphicsPattern::_Render, &wxMacCoreGraphicsPattern::_Dispose };
+
+class ImagePattern : public wxMacCoreGraphicsPattern
+{
+public :
+ ImagePattern( const wxBitmap* bmp , const CGAffineTransform& transform )
+ {
+ wxASSERT( bmp && bmp->Ok() );
+
+ Init( (CGImageRef) bmp->CreateCGImage() , transform );
+ }
+
+ // ImagePattern takes ownership of CGImageRef passed in
+ ImagePattern( CGImageRef image , const CGAffineTransform& transform )
+ {
+ if ( image )
+ CFRetain( image );
+
+ Init( image , transform );
+ }
+
+ virtual void Render( CGContextRef ctxRef )
+ {
+ if (m_image != NULL)
+ wxMacDrawCGImage( ctxRef, &m_imageBounds, m_image );
+ }
+
+protected :
+ void Init( CGImageRef image, const CGAffineTransform& transform )
+ {
+ m_image = image;
+ if ( m_image )
+ {
+ m_imageBounds = CGRectMake( 0.0, 0.0, (CGFloat)CGImageGetWidth( m_image ), (CGFloat)CGImageGetHeight( m_image ) );
+ m_patternRef = CGPatternCreate(
+ this , m_imageBounds, transform ,
+ m_imageBounds.size.width, m_imageBounds.size.height,
+ kCGPatternTilingNoDistortion, true , &wxMacCoreGraphicsPattern::ms_Callbacks );
+ }
+ }
+
+ virtual ~ImagePattern()
+ {
+ if ( m_image )
+ CGImageRelease( m_image );
+ }
+
+ CGImageRef m_image;
+ CGRect m_imageBounds;
+};
+
+class HatchPattern : public wxMacCoreGraphicsPattern
+{
+public :
+ HatchPattern( int hatchstyle, const CGAffineTransform& transform )
+ {
+ m_hatch = hatchstyle;
+ m_imageBounds = CGRectMake( 0.0, 0.0, 8.0 , 8.0 );
+ m_patternRef = CGPatternCreate(
+ this , m_imageBounds, transform ,
+ m_imageBounds.size.width, m_imageBounds.size.height,
+ kCGPatternTilingNoDistortion, false , &wxMacCoreGraphicsPattern::ms_Callbacks );
+ }
+
+ void StrokeLineSegments( CGContextRef ctxRef , const CGPoint pts[] , size_t count )
+ {
+ CGContextStrokeLineSegments( ctxRef , pts , count );
+ }
+
+ virtual void Render( CGContextRef ctxRef )
+ {
+ switch ( m_hatch )
+ {
+ case wxBDIAGONAL_HATCH :
+ {
+ CGPoint pts[] =
+ {
+ { 8.0 , 0.0 } , { 0.0 , 8.0 }
+ };
+ StrokeLineSegments( ctxRef , pts , 2 );
+ }
+ break;
+
+ case wxCROSSDIAG_HATCH :
+ {
+ CGPoint pts[] =
+ {
+ { 0.0 , 0.0 } , { 8.0 , 8.0 } ,
+ { 8.0 , 0.0 } , { 0.0 , 8.0 }
+ };
+ StrokeLineSegments( ctxRef , pts , 4 );
+ }
+ break;
+
+ case wxFDIAGONAL_HATCH :
+ {
+ CGPoint pts[] =
+ {
+ { 0.0 , 0.0 } , { 8.0 , 8.0 }
+ };
+ StrokeLineSegments( ctxRef , pts , 2 );
+ }
+ break;
+
+ case wxCROSS_HATCH :
+ {
+ CGPoint pts[] =
+ {
+ { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
+ { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
+ };
+ StrokeLineSegments( ctxRef , pts , 4 );
+ }
+ break;
+
+ case wxHORIZONTAL_HATCH :
+ {
+ CGPoint pts[] =
+ {
+ { 0.0 , 4.0 } , { 8.0 , 4.0 } ,
+ };
+ StrokeLineSegments( ctxRef , pts , 2 );
+ }
+ break;
+
+ case wxVERTICAL_HATCH :
+ {
+ CGPoint pts[] =
+ {
+ { 4.0 , 0.0 } , { 4.0 , 8.0 } ,
+ };
+ StrokeLineSegments( ctxRef , pts , 2 );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+protected :
+ virtual ~HatchPattern() {}
+
+ CGRect m_imageBounds;
+ int m_hatch;
+};
+
+class wxMacCoreGraphicsPenData : public wxGraphicsObjectRefData
+{
+public:
+ wxMacCoreGraphicsPenData( wxGraphicsRenderer* renderer, const wxPen &pen );
+ ~wxMacCoreGraphicsPenData();
+
+ void Init();
+ virtual void Apply( wxGraphicsContext* context );
+ virtual wxDouble GetWidth() { return m_width; }
+
+protected :
+ CGLineCap m_cap;
+ wxMacCFRefHolder<CGColorRef> m_color;
+ wxMacCFRefHolder<CGColorSpaceRef> m_colorSpace;
+
+ CGLineJoin m_join;
+ CGFloat m_width;
+
+ int m_count;
+ const CGFloat *m_lengths;
+ CGFloat *m_userLengths;
+
+
+ bool m_isPattern;
+ wxMacCFRefHolder<CGPatternRef> m_pattern;
+ CGFloat* m_patternColorComponents;
+};
+
+wxMacCoreGraphicsPenData::wxMacCoreGraphicsPenData( wxGraphicsRenderer* renderer, const wxPen &pen ) :
+ wxGraphicsObjectRefData( renderer )
+{
+ Init();
+
+ CGFloat components[4] = { pen.GetColour().Red() / 255.0 , pen.GetColour().Green() / 255.0 ,
+ pen.GetColour().Blue() / 255.0 , pen.GetColour().Alpha() / 255.0 } ;
+ m_color.Set( CGColorCreate( wxMacGetGenericRGBColorSpace() , components ) ) ;
+
+ // TODO: * m_dc->m_scaleX
+ m_width = pen.GetWidth();
+ if (m_width <= 0.0)
+ m_width = 0.1;
+
+ switch ( pen.GetCap() )
+ {
+ case wxCAP_ROUND :
+ m_cap = kCGLineCapRound;
+ break;
+
+ case wxCAP_PROJECTING :
+ m_cap = kCGLineCapSquare;
+ break;
+
+ case wxCAP_BUTT :
+ m_cap = kCGLineCapButt;
+ break;
+
+ default :
+ m_cap = kCGLineCapButt;
+ break;
+ }
+
+ switch ( pen.GetJoin() )
+ {
+ case wxJOIN_BEVEL :
+ m_join = kCGLineJoinBevel;
+ break;
+
+ case wxJOIN_MITER :
+ m_join = kCGLineJoinMiter;
+ break;
+
+ case wxJOIN_ROUND :
+ m_join = kCGLineJoinRound;
+ break;
+
+ default :
+ m_join = kCGLineJoinMiter;
+ break;
+ }
+
+ const CGFloat dashUnit = m_width < 1.0 ? 1.0 : m_width;
+
+ const CGFloat dotted[] = { dashUnit , dashUnit + 2.0 };
+ static const CGFloat short_dashed[] = { 9.0 , 6.0 };
+ static const CGFloat dashed[] = { 19.0 , 9.0 };
+ static const CGFloat dotted_dashed[] = { 9.0 , 6.0 , 3.0 , 3.0 };
+
+ switch ( pen.GetStyle() )
+ {
+ case wxSOLID :
+ break;
+
+ case wxDOT :
+ m_count = WXSIZEOF(dotted);
+ m_userLengths = new CGFloat[ m_count ] ;
+ memcpy( m_userLengths, dotted, sizeof(dotted) );
+ m_lengths = m_userLengths;
+ break;
+
+ case wxLONG_DASH :
+ m_count = WXSIZEOF(dashed);
+ m_lengths = dashed;
+ break;
+
+ case wxSHORT_DASH :
+ m_count = WXSIZEOF(short_dashed);
+ m_lengths = short_dashed;
+ break;
+
+ case wxDOT_DASH :
+ m_count = WXSIZEOF(dotted_dashed);
+ m_lengths = dotted_dashed;
+ break;
+
+ case wxUSER_DASH :
+ wxDash *dashes;
+ m_count = pen.GetDashes( &dashes );
+ if ((dashes != NULL) && (m_count > 0))
+ {
+ m_userLengths = new CGFloat[m_count];
+ for ( int i = 0; i < m_count; ++i )
+ {
+ m_userLengths[i] = dashes[i] * dashUnit;
+
+ if ( i % 2 == 1 && m_userLengths[i] < dashUnit + 2.0 )
+ m_userLengths[i] = dashUnit + 2.0;
+ else if ( i % 2 == 0 && m_userLengths[i] < dashUnit )
+ m_userLengths[i] = dashUnit;
+ }
+ }
+ m_lengths = m_userLengths;
+ break;
+
+ case wxSTIPPLE :
+ {
+ wxBitmap* bmp = pen.GetStipple();
+ if ( bmp && bmp->Ok() )
+ {
+ m_colorSpace.Set( CGColorSpaceCreatePattern( NULL ) );
+ m_pattern.Set( *( new ImagePattern( bmp , CGAffineTransformMakeScale( 1,-1 ) ) ) );
+ m_patternColorComponents = new CGFloat[1] ;
+ m_patternColorComponents[0] = 1.0;
+ m_isPattern = true;
+ }
+ }
+ break;
+
+ default :
+ {
+ m_isPattern = true;
+ m_colorSpace.Set( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
+ m_pattern.Set( *( new HatchPattern( pen.GetStyle() , CGAffineTransformMakeScale( 1,-1 ) ) ) );
+ m_patternColorComponents = new CGFloat[4] ;
+ m_patternColorComponents[0] = pen.GetColour().Red() / 255.0;
+ m_patternColorComponents[1] = pen.GetColour().Green() / 255.0;
+ m_patternColorComponents[2] = pen.GetColour().Blue() / 255.0;
+ m_patternColorComponents[3] = pen.GetColour().Alpha() / 255.0;
+ }
+ break;
+ }
+ if ((m_lengths != NULL) && (m_count > 0))
+ {
+ // force the line cap, otherwise we get artifacts (overlaps) and just solid lines
+ m_cap = kCGLineCapButt;
+ }
+}
+
+wxMacCoreGraphicsPenData::~wxMacCoreGraphicsPenData()
+{
+ delete[] m_userLengths;
+ delete[] m_patternColorComponents;
+}
+
+void wxMacCoreGraphicsPenData::Init()
+{
+ m_lengths = NULL;
+ m_userLengths = NULL;
+ m_width = 0;
+ m_count = 0;
+ m_patternColorComponents = NULL;
+ m_isPattern = false;
+}
+
+void wxMacCoreGraphicsPenData::Apply( wxGraphicsContext* context )
+{
+ CGContextRef cg = (CGContextRef) context->GetNativeContext();
+ CGContextSetLineWidth( cg , m_width );
+ CGContextSetLineJoin( cg , m_join );
+
+ CGContextSetLineDash( cg , 0 , m_lengths , m_count );
+ CGContextSetLineCap( cg , m_cap );
+
+ if ( m_isPattern )
+ {
+ CGAffineTransform matrix = CGContextGetCTM( cg );
+ CGContextSetPatternPhase( cg, CGSizeMake(matrix.tx, matrix.ty) );
+ CGContextSetStrokeColorSpace( cg , m_colorSpace );
+ CGContextSetStrokePattern( cg, m_pattern , m_patternColorComponents );
+ }
+ else
+ {
+ if ( context->GetLogicalFunction() == wxINVERT || context->GetLogicalFunction() == wxXOR )
+ {
+ CGContextSetRGBStrokeColor( cg , 1.0, 1.0 , 1.0, 1.0 );
+ }
+ else
+ CGContextSetStrokeColorWithColor( cg , m_color );
+ }
+}
+
+//
+// Brush
+//
+
+static const char *gs_stripedback_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"4 4 2 1",
+". c #F0F0F0",
+"X c #ECECEC",
+/* pixels */
+"....",
+"....",
+"XXXX",
+"XXXX"
+};
+
+wxBitmap gs_stripedback_bmp( wxImage( (const char* const* ) gs_stripedback_xpm ), -1 ) ;
+
+// make sure we all use one class for all conversions from wx to native colour
+
+class wxMacCoreGraphicsColour
+{
+ public:
+ wxMacCoreGraphicsColour();
+ wxMacCoreGraphicsColour(const wxBrush &brush);
+ ~wxMacCoreGraphicsColour();
+
+ void Apply( CGContextRef cgContext );
+ protected:
+ void Init();
+ wxMacCFRefHolder<CGColorRef> m_color;
+ wxMacCFRefHolder<CGColorSpaceRef> m_colorSpace;
+
+ bool m_isPattern;
+ wxMacCFRefHolder<CGPatternRef> m_pattern;
+ CGFloat* m_patternColorComponents;
+} ;
+
+wxMacCoreGraphicsColour::~wxMacCoreGraphicsColour()
+{
+ delete[] m_patternColorComponents;
+}
+
+void wxMacCoreGraphicsColour::Init()
+{
+ m_isPattern = false;
+ m_patternColorComponents = NULL;
+}
+
+void wxMacCoreGraphicsColour::Apply( CGContextRef cgContext )
+{
+ if ( m_isPattern )
+ {
+ CGAffineTransform matrix = CGContextGetCTM( cgContext );
+ CGContextSetPatternPhase( cgContext, CGSizeMake(matrix.tx, matrix.ty) );
+ CGContextSetFillColorSpace( cgContext , m_colorSpace );
+ CGContextSetFillPattern( cgContext, m_pattern , m_patternColorComponents );
+ }
+ else
+ {
+ CGContextSetFillColorWithColor( cgContext, m_color );
+ }
+}
+
+wxMacCoreGraphicsColour::wxMacCoreGraphicsColour()
+{
+ Init();
+}
+
+wxMacCoreGraphicsColour::wxMacCoreGraphicsColour( const wxBrush &brush )
+{
+ Init();
+ if ( brush.GetStyle() == wxSOLID )
+ {
+ m_color.Set( brush.GetColour().CreateCGColor() );
+ }
+ else if ( brush.IsHatch() )
+ {
+ m_isPattern = true;
+ m_colorSpace.Set( CGColorSpaceCreatePattern( wxMacGetGenericRGBColorSpace() ) );
+ m_pattern.Set( *( new HatchPattern( brush.GetStyle() , CGAffineTransformMakeScale( 1,-1 ) ) ) );
+
+ m_patternColorComponents = new CGFloat[4] ;
+ m_patternColorComponents[0] = brush.GetColour().Red() / 255.0;
+ m_patternColorComponents[1] = brush.GetColour().Green() / 255.0;
+ m_patternColorComponents[2] = brush.GetColour().Blue() / 255.0;
+ m_patternColorComponents[3] = brush.GetColour().Alpha() / 255.0;
+ }
+ else
+ {
+ // now brush is a bitmap
+ wxBitmap* bmp = brush.GetStipple();
+ if ( bmp && bmp->Ok() )
+ {
+ m_isPattern = true;
+ m_patternColorComponents = new CGFloat[1] ;
+ m_patternColorComponents[0] = 1.0;
+ m_colorSpace.Set( CGColorSpaceCreatePattern( NULL ) );
+ m_pattern.Set( *( new ImagePattern( bmp , CGAffineTransformMakeScale( 1,-1 ) ) ) );
+ }
+ }
+}
+
+class wxMacCoreGraphicsBrushData : public wxGraphicsObjectRefData
+{
+public:
+ wxMacCoreGraphicsBrushData( wxGraphicsRenderer* renderer );
+ wxMacCoreGraphicsBrushData( wxGraphicsRenderer* renderer, const wxBrush &brush );
+ ~wxMacCoreGraphicsBrushData ();
+
+ virtual void Apply( wxGraphicsContext* context );
+ void CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2,
+ const wxColour&c1, const wxColour&c2 );
+ void CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius,
+ const wxColour &oColor, const wxColour &cColor );
+
+ virtual bool IsShading() { return m_isShading; }
+ CGShadingRef GetShading() { return m_shading; }
+protected:
+ CGFunctionRef CreateGradientFunction( const wxColour& c1, const wxColour& c2 );
+ static void CalculateShadingValues (void *info, const CGFloat *in, CGFloat *out);
+ virtual void Init();
+
+ wxMacCoreGraphicsColour m_cgColor;
+
+ bool m_isShading;
+ CGFunctionRef m_gradientFunction;
+ CGShadingRef m_shading;
+ CGFloat *m_gradientComponents;
+};
+
+wxMacCoreGraphicsBrushData::wxMacCoreGraphicsBrushData( wxGraphicsRenderer* renderer) : wxGraphicsObjectRefData( renderer )
+{
+ Init();
+}
+
+void wxMacCoreGraphicsBrushData::CreateLinearGradientBrush( wxDouble x1, wxDouble y1, wxDouble x2, wxDouble y2,
+ const wxColour&c1, const wxColour&c2 )
+{
+ m_gradientFunction = CreateGradientFunction( c1, c2 );
+ m_shading = CGShadingCreateAxial( wxMacGetGenericRGBColorSpace(), CGPointMake(x1,y1), CGPointMake(x2,y2), m_gradientFunction, true, true ) ;
+ m_isShading = true ;
+}
+
+void wxMacCoreGraphicsBrushData::CreateRadialGradientBrush( wxDouble xo, wxDouble yo, wxDouble xc, wxDouble yc, wxDouble radius,
+ const wxColour &oColor, const wxColour &cColor )
+{
+ m_gradientFunction = CreateGradientFunction( oColor, cColor );
+ m_shading = CGShadingCreateRadial( wxMacGetGenericRGBColorSpace(), CGPointMake(xo,yo), 0, CGPointMake(xc,yc), radius, m_gradientFunction, true, true ) ;
+ m_isShading = true ;
+}
+
+wxMacCoreGraphicsBrushData::wxMacCoreGraphicsBrushData(wxGraphicsRenderer* renderer, const wxBrush &brush) : wxGraphicsObjectRefData( renderer ),
+ m_cgColor( brush )
+{
+ Init();
+
+}
+
+wxMacCoreGraphicsBrushData::~wxMacCoreGraphicsBrushData()
+{
+ if ( m_shading )
+ CGShadingRelease(m_shading);
+
+ if( m_gradientFunction )
+ CGFunctionRelease(m_gradientFunction);
+
+ delete[] m_gradientComponents;
+}
+
+void wxMacCoreGraphicsBrushData::Init()
+{
+ m_gradientFunction = NULL;
+ m_shading = NULL;
+ m_gradientComponents = NULL;
+ m_isShading = false;
+}
+
+void wxMacCoreGraphicsBrushData::Apply( wxGraphicsContext* context )
+{
+ CGContextRef cg = (CGContextRef) context->GetNativeContext();
+
+ if ( m_isShading )
+ {
+ // nothing to set as shades are processed by clipping using the path and filling
+ }
+ else
+ {
+ m_cgColor.Apply( cg );
+ }
+}
+
+void wxMacCoreGraphicsBrushData::CalculateShadingValues (void *info, const CGFloat *in, CGFloat *out)
+{
+ CGFloat* colors = (CGFloat*) info ;
+ CGFloat f = *in;
+ for( int i = 0 ; i < 4 ; ++i )
+ {
+ out[i] = colors[i] + ( colors[4+i] - colors[i] ) * f;
+ }
+}
+
+CGFunctionRef wxMacCoreGraphicsBrushData::CreateGradientFunction( const wxColour& c1, const wxColour& c2 )
+{
+ static const CGFunctionCallbacks callbacks = { 0, &CalculateShadingValues, NULL };
+ static const CGFloat input_value_range [2] = { 0, 1 };
+ static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
+ m_gradientComponents = new CGFloat[8] ;
+ m_gradientComponents[0] = c1.Red() / 255.0;
+ m_gradientComponents[1] = c1.Green() / 255.0;
+ m_gradientComponents[2] = c1.Blue() / 255.0;
+ m_gradientComponents[3] = c1.Alpha() / 255.0;
+ m_gradientComponents[4] = c2.Red() / 255.0;
+ m_gradientComponents[5] = c2.Green() / 255.0;
+ m_gradientComponents[6] = c2.Blue() / 255.0;
+ m_gradientComponents[7] = c2.Alpha() / 255.0;
+
+ return CGFunctionCreate ( m_gradientComponents, 1,
+ input_value_range,
+ 4,
+ output_value_ranges,
+ &callbacks);
+}
+
+//
+// Font
+//
+
+class wxMacCoreGraphicsFontData : public wxGraphicsObjectRefData
+{
+public:
+ wxMacCoreGraphicsFontData( wxGraphicsRenderer* renderer, const wxFont &font, const wxColour& col );
+ ~wxMacCoreGraphicsFontData();