OSX using ref data for colour and NSColor native support (#878)

* First attempt using ref data for colour and spliting implementation for CGColorRef and NSColor

* correcting SDK dependency

* Implementing feedback suggestions
This commit is contained in:
Stefan Csomor
2018-08-19 18:02:06 +02:00
committed by GitHub
parent 36d21ce6c8
commit 91aa6ba36e
4 changed files with 349 additions and 168 deletions

View File

@@ -65,7 +65,7 @@ DECLARE_VARIANT_OBJECT_EXPORTED(wxColour,WXDLLIMPEXP_CORE)
not need the wxGDIObject machinery to handle colors, please add it to the not need the wxGDIObject machinery to handle colors, please add it to the
list of ports which do not need it. list of ports which do not need it.
*/ */
#if defined( __WXMAC__ ) || defined( __WXMSW__ ) || defined( __WXQT__ ) #if defined( __WXMSW__ ) || defined( __WXQT__ )
#define wxCOLOUR_IS_GDIOBJECT 0 #define wxCOLOUR_IS_GDIOBJECT 0
#else #else
#define wxCOLOUR_IS_GDIOBJECT 1 #define wxCOLOUR_IS_GDIOBJECT 1

View File

@@ -29,60 +29,76 @@ public:
// default copy ctor and dtor are ok // default copy ctor and dtor are ok
// accessors // accessors
virtual bool IsOk() const { return m_cgColour != NULL; } virtual ChannelType Red() const wxOVERRIDE;
virtual ChannelType Green() const wxOVERRIDE;
virtual ChannelType Blue() const wxOVERRIDE;
virtual ChannelType Alpha() const wxOVERRIDE;
virtual WXDLLIMPEXP_INLINE_CORE ChannelType Red() const { return m_red; } wxColour& operator=(const wxColour& col);
virtual WXDLLIMPEXP_INLINE_CORE ChannelType Green() const { return m_green; }
virtual WXDLLIMPEXP_INLINE_CORE ChannelType Blue() const { return m_blue; }
virtual WXDLLIMPEXP_INLINE_CORE ChannelType Alpha() const { return m_alpha; }
// comparison // comparison
bool operator == (const wxColour& colour) const; bool operator == (const wxColour& colour) const;
bool operator != (const wxColour& colour) const { return !(*this == colour); } bool operator != (const wxColour& colour) const { return !(*this == colour); }
CGColorRef GetPixel() const { return m_cgColour; } // CoreGraphics CGColor
// --------------------
CGColorRef GetCGColor() const { return m_cgColour; } // This ctor does take ownership of the color.
CGColorRef CreateCGColor() const { return wxCFRetain( (CGColorRef)m_cgColour ); } wxColour( CGColorRef col );
// don't take ownership of the returned value
CGColorRef GetCGColor() const;
// do take ownership of the returned value
CGColorRef CreateCGColor() const { return wxCFRetain(GetCGColor()); }
#if wxOSX_USE_COCOA_OR_CARBON #if wxOSX_USE_COCOA_OR_CARBON
// Quickdraw RGBColor
// ------------------
wxColour(const RGBColor& col);
void GetRGBColor( RGBColor *col ) const; void GetRGBColor( RGBColor *col ) const;
#endif #endif
// Mac-specific ctor and assignment operator from the native colour
// assumes ownership of CGColorRef
wxColour( CGColorRef col );
#if wxOSX_USE_COCOA_OR_CARBON
wxColour(const RGBColor& col);
wxColour& operator=(const RGBColor& col);
#endif
#if wxOSX_USE_COCOA #if wxOSX_USE_COCOA
// NSColor Cocoa
// -------------
// This ctor does not take ownership of the color. // This ctor does not take ownership of the color.
explicit wxColour(WX_NSColor color); explicit wxColour(WX_NSColor color);
WX_NSColor OSXGetNSColor() const; WX_NSColor OSXGetNSColor() const;
#endif #endif
wxColour& operator=(CGColorRef col);
wxColour& operator=(const wxColour& col);
protected : protected :
virtual void virtual void
InitRGBA(ChannelType r, ChannelType g, ChannelType b, ChannelType a); InitRGBA(ChannelType r, ChannelType g, ChannelType b, ChannelType a) wxOVERRIDE;
#if wxOSX_USE_COCOA_OR_CARBON
void InitRGBColor( const RGBColor& col ); virtual wxGDIRefData *CreateGDIRefData() const wxOVERRIDE;
#endif virtual wxGDIRefData *CloneGDIRefData(const wxGDIRefData *data) const wxOVERRIDE;
void InitCGColorRef( CGColorRef col );
private: private:
wxCFRef<CGColorRef> m_cgColour;
ChannelType m_red;
ChannelType m_blue;
ChannelType m_green;
ChannelType m_alpha;
wxDECLARE_DYNAMIC_CLASS(wxColour); wxDECLARE_DYNAMIC_CLASS(wxColour);
}; };
class wxColourRefData : public wxGDIRefData
{
public:
wxColourRefData() {}
virtual ~wxColourRefData() {}
virtual CGFloat Red() const = 0;
virtual CGFloat Green() const = 0;
virtual CGFloat Blue() const = 0;
virtual CGFloat Alpha() const = 0;
virtual CGColorRef GetCGColor() const = 0;
virtual wxColourRefData* Clone() const = 0;
#if wxOSX_USE_COCOA
virtual WX_NSColor GetNSColor() const;
#endif
};
#endif #endif
// _WX_COLOUR_H_ // _WX_COLOUR_H_

View File

@@ -13,78 +13,136 @@
#include "wx/osx/private.h" #include "wx/osx/private.h"
// Helper function to avoid writing too many casts in wxColour ctor. class wxNSColorRefData : public wxColourRefData
static inline wxColour::ChannelType NSColorChannelToWX(CGFloat c)
{ {
return static_cast<wxColour::ChannelType>(c * 255 + 0.5); public:
wxNSColorRefData(WX_NSColor color);
wxNSColorRefData(const wxNSColorRefData& other);
virtual ~wxNSColorRefData();
virtual CGFloat Red() const wxOVERRIDE;
virtual CGFloat Green() const wxOVERRIDE;
virtual CGFloat Blue() const wxOVERRIDE;
virtual CGFloat Alpha() const wxOVERRIDE;
CGColorRef GetCGColor() const wxOVERRIDE;
virtual wxColourRefData* Clone() const wxOVERRIDE { return new wxNSColorRefData(*this); }
virtual WX_NSColor GetNSColor() const wxOVERRIDE;
private:
WX_NSColor m_nsColour;
wxDECLARE_NO_ASSIGN_CLASS(wxNSColorRefData);
};
wxNSColorRefData::wxNSColorRefData(WX_NSColor color)
{
m_nsColour = [color retain];
} }
wxColour::wxColour(WX_NSColor col) wxNSColorRefData::wxNSColorRefData(const wxNSColorRefData& other)
{
m_nsColour = [other.m_nsColour retain];
}
wxNSColorRefData::~wxNSColorRefData()
{
[m_nsColour release];
}
WX_NSColor wxNSColorRefData::GetNSColor() const
{
return m_nsColour;
}
CGFloat wxNSColorRefData::Red() const
{
if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace] )
return [colRGBA redComponent];
return 0.0;
}
CGFloat wxNSColorRefData::Green() const
{
if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace] )
return [colRGBA greenComponent];
return 0.0;
}
CGFloat wxNSColorRefData::Blue() const
{
if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace] )
return [colRGBA blueComponent];
return 0.0;
}
CGFloat wxNSColorRefData::Alpha() const
{
if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace] )
return [colRGBA alphaComponent];
return 0.0;
}
CGColorRef wxNSColorRefData::GetCGColor() const
{ {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
if ( wxPlatformInfo::Get().CheckOSVersion(10, 8) ) if (wxPlatformInfo::Get().CheckOSVersion(10, 8))
{ return [m_nsColour CGColor];
CGColorRef cgcolor = [col CGColor];
CFRetain(cgcolor);
InitCGColorRef(cgcolor);
return ;
}
#endif #endif
CGColorRef cgcolor = NULL;
// Simplest case is when we can directly get the RGBA components:
if ( NSColor* colRGBA = [col colorUsingColorSpaceName:NSCalibratedRGBColorSpace] )
{
InitRGBA
(
NSColorChannelToWX([colRGBA redComponent]),
NSColorChannelToWX([colRGBA greenComponent]),
NSColorChannelToWX([colRGBA blueComponent]),
NSColorChannelToWX([colRGBA alphaComponent])
);
return;
}
// Simplest case is when we can directly get the RGBA components:
if (NSColor* colRGBA = [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace])
{
CGFloat components[4];
[colRGBA getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
cgcolor = CGColorCreate(wxMacGetGenericRGBColorSpace(), components);
}
// Some colours use patterns, we can handle them with the help of CGColorRef // Some colours use patterns, we can handle them with the help of CGColorRef
if ( NSColor* colPat = [col colorUsingColorSpaceName:NSPatternColorSpace] ) else if (NSColor* colPat = [m_nsColour colorUsingColorSpaceName:NSPatternColorSpace])
{ {
NSImage* const nsimage = [colPat patternImage]; NSImage* const nsimage = [colPat patternImage];
if ( nsimage ) if (nsimage)
{ {
NSSize size = [nsimage size]; NSSize size = [nsimage size];
NSRect r = NSMakeRect(0, 0, size.width, size.height); NSRect r = NSMakeRect(0, 0, size.width, size.height);
CGImageRef cgimage = [nsimage CGImageForProposedRect:&r context:nil hints:nil]; CGImageRef cgimage = [nsimage CGImageForProposedRect:&r context:nil hints:nil];
if ( cgimage ) if (cgimage)
{ {
// Callbacks for CGPatternCreate() // Callbacks for CGPatternCreate()
struct PatternCreateCallbacks struct PatternCreateCallbacks
{ {
static void Draw(void *info, CGContextRef ctx) static void Draw(void* info, CGContextRef ctx)
{ {
CGImageRef image = (CGImageRef) info; CGImageRef image = (CGImageRef)info;
CGContextDrawImage CGContextDrawImage(
(
ctx, ctx,
CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)),
image image);
);
} }
static void Release(void * WXUNUSED(info)) static void Release(void* WXUNUSED(info))
{ {
// Do not release the image here, we don't own it as it // Do not release the image here, we don't own it as it
// comes from NSImage. // comes from NSImage.
} }
}; };
const CGPatternCallbacks callbacks = const CGPatternCallbacks callbacks = {
{
/* version: */ 0, /* version: */ 0,
&PatternCreateCallbacks::Draw, &PatternCreateCallbacks::Draw,
&PatternCreateCallbacks::Release &PatternCreateCallbacks::Release
}; };
CGPatternRef pattern = CGPatternCreate CGPatternRef pattern = CGPatternCreate(
(
cgimage, cgimage,
CGRectMake(0, 0, size.width, size.height), CGRectMake(0, 0, size.width, size.height),
CGAffineTransformMake(1, 0, 0, 1, 0, 0), CGAffineTransformMake(1, 0, 0, 1, 0, 0),
@@ -96,24 +154,32 @@ wxColour::wxColour(WX_NSColor col)
); );
CGColorSpaceRef space = CGColorSpaceCreatePattern(NULL); CGColorSpaceRef space = CGColorSpaceCreatePattern(NULL);
CGFloat components[1] = { 1.0 }; CGFloat components[1] = { 1.0 };
CGColorRef cgcolor = CGColorCreateWithPattern(space, pattern, components); cgcolor = CGColorCreateWithPattern(space, pattern, components);
CGColorSpaceRelease(space); CGColorSpaceRelease(space);
CGPatternRelease(pattern); CGPatternRelease(pattern);
InitCGColorRef(cgcolor);
return;
} }
} }
} }
// Don't assert here, this will more likely than not result in a crash as if (cgcolor == NULL)
// colours are often created in drawing code which will be called again {
// when the assert dialog is shown, resulting in a recursive assertion // Don't assert here, this will more likely than not result in a crash as
// failure and, hence, a crash. // colours are often created in drawing code which will be called again
NSLog(@"Failed to convert NSColor \"%@\" to wxColour.", col); // when the assert dialog is shown, resulting in a recursive assertion
// failure and, hence, a crash.
NSLog(@"Failed to convert NSColor \"%@\" to CGColorRef.", m_nsColour);
}
return cgcolor;
} }
WX_NSColor wxColour::OSXGetNSColor() const WX_NSColor wxColourRefData::GetNSColor() const
{ {
return [NSColor colorWithCalibratedRed:m_red / 255.0 green:m_green / 255.0 blue:m_blue / 255.0 alpha:m_alpha / 255.0]; return [NSColor colorWithCalibratedRed:Red() green:Green() blue:Blue() alpha:Alpha() ];
} }
wxColour::wxColour(WX_NSColor col)
{
m_refData = new wxNSColorRefData(col);
}

View File

@@ -18,122 +18,221 @@
#include "wx/osx/private.h" #include "wx/osx/private.h"
CGColorSpaceRef wxMacGetGenericRGBColorSpace();
class wxCGColorRefData : public wxColourRefData
{
public:
wxCGColorRefData(CGFloat r, CGFloat g, CGFloat b, CGFloat a = 1.0);
wxCGColorRefData(CGFloat components[4]);
wxCGColorRefData(CGColorRef);
wxCGColorRefData(const wxCGColorRefData& other);
virtual bool IsOk() const wxOVERRIDE{ return m_cgColour != NULL; }
virtual CGFloat Red() const wxOVERRIDE { return m_red; }
virtual CGFloat Green() const wxOVERRIDE { return m_green; }
virtual CGFloat Blue() const wxOVERRIDE { return m_blue; }
virtual CGFloat Alpha() const wxOVERRIDE { return m_alpha; }
CGColorRef GetCGColor() const wxOVERRIDE { return m_cgColour; }
virtual wxColourRefData* Clone() const wxOVERRIDE { return new wxCGColorRefData(*this); }
private:
void Init(CGFloat components[4]);
wxCFRef<CGColorRef> m_cgColour;
CGFloat m_red;
CGFloat m_blue;
CGFloat m_green;
CGFloat m_alpha;
wxDECLARE_NO_ASSIGN_CLASS(wxCGColorRefData);
};
wxCGColorRefData::wxCGColorRefData(CGFloat r, CGFloat g, CGFloat b, CGFloat a)
{
CGFloat components[4];
components[0] = r;
components[1] = b;
components[2] = g;
components[3] = a;
Init(components);
}
wxCGColorRefData::wxCGColorRefData(CGFloat components[4])
{
Init(components);
}
wxCGColorRefData::wxCGColorRefData(const wxCGColorRefData& other)
{
m_red = other.m_red;
m_blue = other.m_blue;
m_green = other.m_green;
m_alpha = other.m_alpha;
m_cgColour = other.m_cgColour;
}
void wxCGColorRefData::Init(CGFloat components[4])
{
m_red = components[0];
m_blue = components[1];
m_green = components[2];
m_alpha = components[3];
m_cgColour = CGColorCreate(wxMacGetGenericRGBColorSpace(), components);
}
wxCGColorRefData::wxCGColorRefData(CGColorRef col)
{
wxASSERT_MSG(col != NULL, "Invalid CoreGraphics Color");
m_cgColour.reset(col);
wxCFRef<CGColorRef> rgbacol;
size_t noComp = CGColorGetNumberOfComponents(col);
const CGFloat* components = CGColorGetComponents(col);
// set default alpha
m_alpha = 1.0;
bool isRGB = true;
CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(col));
if (model == kCGColorSpaceModelMonochrome)
{
wxASSERT_MSG(1 <= noComp && noComp <= 2, "Monochrome Color unexpected components");
m_red = components[0];
m_green = components[0];
m_blue = components[0];
if (noComp > 1)
m_alpha = components[1];
isRGB = false;
}
else if (model != kCGColorSpaceModelRGB)
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_11
if (wxPlatformInfo::Get().CheckOSVersion(10, 11))
{
rgbacol = CGColorCreateCopyByMatchingToColorSpace(wxMacGetGenericRGBColorSpace(), kCGRenderingIntentDefault, col, NULL);
noComp = CGColorGetNumberOfComponents(rgbacol);
components = CGColorGetComponents(rgbacol);
}
else
#endif
{
isRGB = false;
}
}
if (isRGB)
{
wxASSERT_MSG(3 <= noComp && noComp <= 4, "RGB Color unexpected components");
m_red = components[0];
m_green = components[1];
m_blue = components[2];
if (noComp == 4)
m_alpha = components[3];
}
}
#define M_COLDATA static_cast<wxColourRefData*>(m_refData)
#if wxOSX_USE_COCOA_OR_CARBON #if wxOSX_USE_COCOA_OR_CARBON
wxColour::wxColour(const RGBColor& col) wxColour::wxColour(const RGBColor& col)
{ {
InitRGBColor(col); CGFloat components[4] = { (CGFloat)(col.red / 65535.0), (CGFloat)(col.green / 65535.0),
(CGFloat)(col.blue / 65535.0), (CGFloat)1.0 };
m_refData = new wxCGColorRefData(components);
} }
#endif #endif
wxColour::wxColour(CGColorRef col) wxColour::wxColour(CGColorRef col)
{ {
InitCGColorRef(col); wxASSERT_MSG(col != NULL, "Invalid CoreGraphics Color");
m_refData = new wxCGColorRefData(col);
}
wxColour::ChannelType wxColour::Red() const
{
return wxRound(M_COLDATA->Red() * 255.0);
}
wxColour::ChannelType wxColour::Green() const
{
return wxRound(M_COLDATA->Red() * 255.0);
}
wxColour::ChannelType wxColour::Blue() const
{
return wxRound(M_COLDATA->Red() * 255.0);
}
wxColour::ChannelType wxColour::Alpha() const
{
return wxRound(M_COLDATA->Red() * 255.0);
} }
#if wxOSX_USE_COCOA_OR_CARBON #if wxOSX_USE_COCOA_OR_CARBON
void wxColour::GetRGBColor( RGBColor *col ) const void wxColour::GetRGBColor(RGBColor* col) const
{ {
col->red = (m_red << 8) + m_red; col->red = M_COLDATA->Red() * 65535.0;
col->blue = (m_blue << 8) + m_blue; col->blue = M_COLDATA->Blue() * 65535.0;
col->green = (m_green << 8) + m_green; col->green = M_COLDATA->Green() * 65535.0;
}
wxColour& wxColour::operator=(const RGBColor& col)
{
InitRGBColor(col);
return *this;
} }
#endif #endif
wxColour& wxColour::operator=(CGColorRef col) CGColorRef wxColour::GetCGColor() const
{ {
InitCGColorRef(col); return M_COLDATA->GetCGColor();
return *this; }
#if wxOSX_USE_COCOA
WX_NSColor wxColour::OSXGetNSColor() const
{
return M_COLDATA->GetNSColor();
}
#endif
void wxColour::InitRGBA(ChannelType r, ChannelType g, ChannelType b, ChannelType a)
{
CGFloat components[4] = { (CGFloat)(r / 255.0), (CGFloat)(g / 255.0), (CGFloat)(b / 255.0), (CGFloat)(a / 255.0) };
m_refData = new wxCGColorRefData(components);
} }
wxColour& wxColour::operator=(const wxColour& col) wxColour& wxColour::operator=(const wxColour& col)
{ {
m_red = col.m_red; wxObject::operator=(col);
m_green = col.m_green;
m_blue = col.m_blue;
m_alpha = col.m_alpha;
m_cgColour = col.m_cgColour;
return *this; return *this;
} }
void wxColour::InitRGBA (ChannelType r, ChannelType g, ChannelType b, ChannelType a) bool wxColour::operator==(const wxColour& other) const
{ {
m_red = r; if (m_refData == other.m_refData)
m_green = g; return true;
m_blue = b;
m_alpha = a ; if (!m_refData || !other.m_refData)
return false;
CGColorRef col = 0 ;
CGFloat components[4] = { (CGFloat)(r / 255.0), (CGFloat) (g / 255.0), (CGFloat) (b / 255.0), (CGFloat) (a / 255.0) } ; return CGColorEqualToColor(GetCGColor(), other.GetCGColor());
col = CGColorCreate( wxMacGetGenericRGBColorSpace() , components ) ;
wxASSERT_MSG(col != NULL, "Invalid CoreGraphics Color");
m_cgColour.reset( col );
} }
#if wxOSX_USE_COCOA_OR_CARBON wxGDIRefData* wxColour::CreateGDIRefData() const
void wxColour::InitRGBColor( const RGBColor& col )
{ {
m_red = col.red >> 8; // black
m_blue = col.blue >> 8; return new wxCGColorRefData(0.0, 0.0, 0.0);
m_green = col.green >> 8;
m_alpha = wxALPHA_OPAQUE;
CGColorRef cfcol;
CGFloat components[4] = { (CGFloat)(col.red / 65535.0), (CGFloat)(col.green / 65535.0),
(CGFloat)(col.blue / 65535.0), (CGFloat) 1.0 } ;
cfcol = CGColorCreate( wxMacGetGenericRGBColorSpace() , components ) ;
wxASSERT_MSG(cfcol != NULL, "Invalid CoreGraphics Color");
m_cgColour.reset( cfcol );
}
#endif
void wxColour::InitCGColorRef( CGColorRef col )
{
wxASSERT_MSG(col != NULL, "Invalid CoreGraphics Color");
m_cgColour.reset( col );
size_t noComp = CGColorGetNumberOfComponents( col );
const CGFloat *components = NULL;
if ( noComp >= 1 && noComp <= 4 )
{
// TODO verify whether we really are on a RGB color space
m_alpha = wxALPHA_OPAQUE;
components = CGColorGetComponents( col );
}
if ( noComp < 1 || !components )
{
m_alpha = wxALPHA_OPAQUE;
m_red = m_green = m_blue = 0;
return;
}
if ( noComp >= 3 )
{
m_red = (int)(components[0]*255+0.5);
m_green = (int)(components[1]*255+0.5);
m_blue = (int)(components[2]*255+0.5);
if ( noComp == 4 )
m_alpha = (int)(components[3]*255+0.5);
}
else
{
m_red = (int)(components[0]*255+0.5);
m_green = (int)(components[0]*255+0.5);
m_blue = (int)(components[0]*255+0.5);
}
} }
bool wxColour::operator == (const wxColour& colour) const wxGDIRefData* wxColour::CloneGDIRefData(const wxGDIRefData* data) const
{ {
return ( (IsOk() == colour.IsOk()) && (!IsOk() || return static_cast<const wxColourRefData*>(data)->Clone();
CGColorEqualToColor( m_cgColour, colour.m_cgColour ) ) );
} }