Colorspaces in MacOS with Xojo
The example project used for a test shows various colorspaces and you need to look very close at them on the screen so you can spot the difference between the red in the three color spaces: For my screen left is RGB(237, 65, 36), middle is RGB(235, 51, 36) and right is RGB(255, 2, 0). So the right one is basically 1 to 1 to the screen. In that screen colorspace a SRGB or generic RGB red color is quite a difference.
The drawing is done in Paint event of canvas with code like this:
dim context as CGContextMBS = CGContextMBS.contextWithCGContext(g.Handle(g.HandleTypeCGContextRef))
dim ColorSpace as CGColorSpaceMBS
ColorSpace = CGColorSpaceMBS.CreateWithName(CGColorSpaceMBS.kCGColorSpaceGenericRGB)
dim Red as CGColorMBS = CGColorMBS.Create(ColorSpace, array(1.0, 0.0, 0.0, 1.0))
context.SetFillColorSpace(ColorSpace)
context.SetFillColor(Red)
context.FillRect CGMakeRectMBS(0, 0, g.Width, g.Height)
context.Flush
This draws a red area in generic RGB. You can swap the colorspace name with other names or as the following example use the screen colorspace:
dim context as CGContextMBS = CGContextMBS.contextWithCGContext(g.Handle(g.HandleTypeCGContextRef))
dim screen as NSScreenMBS = NSScreenMBS.mainScreen
dim ScreenColorSpace as NSColorSpaceMBS = screen.colorSpace
dim ColorSpace as CGColorSpaceMBS = CGColorSpaceMBS.CreateWithHandle(ScreenColorSpace.CGColorSpaceHandle)
dim Red as CGColorMBS = CGColorMBS.Create(ColorSpace, array(1.0, 0.0, 0.0, 1.0))
context.SetFillColorSpace(ColorSpace)
context.SetFillColor(Red)
context.FillRect CGMakeRectMBS(0, 0, g.Width, g.Height)
context.Flush
As you see we query colorspace from screen, get CGColorSpaceMBS object and use it for drawing.
Xojo uses pictures with generic RGB colorspace as default as you see with this code snippet:
// show colorspace of picture
dim p as new Picture(100, 100)
dim colorspace as CGColorSpaceMBS = p.CGColorSpaceMBS
MsgBox colorspace.Name // generic RGB
The color space reported is generic RGB. That means you can use your picture color handling and just convert all your colors to generic RGB and draw them into a picture. For example you can use a Xojo Picture and access pixels with PictureMBS. You can do color matching with our LCMS plugin functions to match colors to the generic RGB colorspace into the Xojo picture. This should look correct in most cases.
If you need colorspace for LCMS to convert, you can query ICCProfileData from the NSColorSpaceMBS like the following code:
// shows Display profile
dim screen as NSScreenMBS = NSScreenMBS.mainScreen
dim ScreenColorSpace as NSColorSpaceMBS = screen.colorSpace
MsgBox ScreenColorSpace.localizedName
// open same profile in LCMS for conversion
dim screenICCProfile as Memoryblock = ScreenColorSpace.ICCProfileData
dim LCMS2Profile as LCMS2ProfileMBS = LCMS2ProfileMBS.OpenProfileFromMemory(screenICCProfile)
MsgBox LCMS2Profile.Name
The final example for today is to make a bitmap ourself with setting pixels in memory block. So first get the target colorspace, in our case the one from screen. Than we build a memory block with red pixels. To draw we first build a CGBitmapContextMBS referencing pixels in the memoryblock. To draw we must make a CGImageMBS, which copies pixels to VRAM, so further changes to memoryblock are not longer affecting the image. You can cache the image until you do changes to the bitmap. The image can now be drawn into the window via CGContextMBS object.
dim context as CGContextMBS = CGContextMBS.contextWithCGContext(g.Handle(g.HandleTypeCGContextRef))
dim ColorSpace as CGColorSpaceMBS
ColorSpace = CGColorSpaceMBS.CreateWithName(CGColorSpaceMBS.kCGColorSpaceGenericRGB)
dim w as integer = g.Width * 2
dim h as integer = g.Height * 2
const kCGImageAlphaNone = 0
const kCGImageAlphaPremultipliedLast = 1 // For example, premultiplied RGBA
const kCGImageAlphaPremultipliedFirst = 2 // For example, premultiplied ARGB
const kCGImageAlphaLast = 3 // For example, non-premultiplied RGBA
const kCGImageAlphaFirst = 4 // For example, non-premultiplied ARGB
const kCGImageAlphaNoneSkipLast = 5 // Equivalent to kCGImageAlphaNone.
const kCGImageAlphaNoneSkipFirst = 6
dim rowBytes as integer = w * 4
dim data as new MemoryBlock(rowBytes * h)
for y as integer = 0 to 199
for x as integer = 0 to 199
data.UInt32Value( y * rowBytes + x * 4) = &h0000FF00 // red
next
next
dim bitmap as CGBitmapContextMBS
bitmap = CGBitmapContextMBS.Create(data, w, h, 8, rowBytes, ColorSpace, kCGImageAlphaNoneSkipFirst)
dim image as CGImageMBS = bitmap.CreateImage
context.DrawPicture(image, CGMakeRectMBS(0, 0, g.Width, g.Height))
context.Flush
if you have further questions, please do not hesitate to contact us.
For above call to CGColorMBS.Create in older plugins, please add an extra component. This bug is fixed for final 18.0 plugins.