I made an app that displays images in the desktop Canvas. If I use the Mousedown Event and RGBSurface to sample a color, I get the exact color as when I sample the same area in the image in Photoshop or Clip Studio Paint.
However, when I use Photoshop or Clip Studio Paint eyedropper tool to sample the same color from the image in the Xojo App, I get different colors. When sampling reds and magentas and some deep blues from the Xojo Desktop app I get different results than when I sample the same area inside Photoshop or Clip Studio Paint
Other colors are spot on (yellows, oranges, light blues, flesh tones), but it’s inconsistent.
How do I get the image to display properly in the Xojo App so I get an identical result whether I sample that area inside Photoshop, or use Photoshop’s eyedropper to sample the Xojo app image?
It’s important to keep in mind that different applications on the same system may render images using different color spaces (ICC profiles). This can lead to different results based on how the image is being viewed. It’s likely that Photoshop and Clip Studio Paint are applying a different color space than the Xojo app.
That sounds like what you’re encountering if some colors are correct but others are not. There are a number of posts here on the forums discussing the application of color spaces.
I’ve seen this many times through the years @Mark_Sweeney and it’s quite a pain to address. The only work around I’ve found is to manually adjust the RGB colors of the Xojo color to compensate but this is entirely a guess and check sort of operation. I just logged a ticket here:
Maybe as others have said there’s some kind of color correction going on here. But if that’s the case, then maybe Xojo should offer a way to render the true color without any color correction. Maybe it does and maybe I’ve simply missed it in the docs.
My overarching point though is if Acorn, my browser and Xojo’s own DesktopImageViewer all have the same hex color but Xojo’s other controls render it a different color, something is off.
From what I’ve experienced, Xojo renders color using a generic RBG color profile regardless of the embedded profile. That leads to inaccuracies in images.
The macOS built in Digital Color Meter used to have a option called “Generic RGB” that would match Xojo colors exactly, but that has been removed in the current OS.
The only way that I’ve found to have images display correctly is to use NSImageMBS. Then they are spot on and respect the embedded profile.
@Jared_Feder That’s some really useful info and many thanks for sharing.
After more sleuthing this is indeed due to color space issues but there are actually several issues at play. I’ve renamed and updated the ticket I placed with many more specifics including an method in Xojo to do an sRGB to Generic RGB conversation of colors:
If interested, view the ticket for more details. In short this has turned into a feature request that is asking for the following:
Standardize Xojo Color on sRGB by default across platforms. Preserve a legacy Generic-RGB mode for existing macOS projects if necessary.
Add explicit color-space support to Color and Picture. Let developers specify, inspect, assign, and convert profiles/spaces.
Separate image storage/working space from display conversion. Loaded images should retain their embedded ICC profile, and newly created Pictures should have an explicit predictable working space-preferably sRGB. The OS should convert from that working space to the display profile only when displaying.
So far, nothing I’m trying is making any difference. Still getting wrong color in the reds. Have no idea how to open it with the correct color profile and have it the colors spot on.
Dim ColorSpace As CGColorSpaceMBS = CGColorSpaceMBS.CreateWithName(CGColorSpaceMBS.kCGColorSpaceSRGB)
Dim White As new NSColorMBS(1.0, 1.0, 1.0, 1.0)
Var img As NSImageMBS
img = new NSImageMBS(f)
ImageViewer1.Image = img.CopyPicture(ColorSpace, White)
CanvasComic.Backdrop = img.CopyPicture(ColorSpace, White)
You can also use a DesktopImageViewer like this (Opening event in this case):
Var n As New NSImageMBS(file)
Var iv As NSImageViewMBS = Me.NSImageViewMBS
// just use standard color range, not HDR here
// iv.preferredImageDynamicRange = NSImageViewMBS.NSImageDynamicRangeStandard
// and here we use HDR colors
iv.preferredImageDynamicRange = NSImageViewMBS.NSImageDynamicRangeHigh
iv.Image = n
That one also enables HDR on images if you need that.
Thanks @Christian_Schmitz for the example app.
Yep, at this point you need to start using NSGraphicsMBS to composite the images together.
This isn’t exactly what you need to do, but it’ll give you a bit of an idea of how to use NSGraphicsMBS
dim warningImage as new NSImageMBS(warningTemplate)
warningImage = warningImage.imageWithTintColor(Colors.warningYellowNSColor)
dim baseImage as new NSImageMBS(91,80)
dim nsGraphics as new NSGraphicsMBS(baseImage)
dim layoutRect as new NSRectMBS(35,5,20,70)
dim bezierPath as NSBezierPathMBS = NSBezierPathMBS.bezierPathWithOvalInRect(layoutRect)
nsGraphics.SetColorRGB(0,0,0)
nsGraphics.fill(bezierPath)
nsGraphics.drawInRect(warningImage, 0, 0, baseImage.width, baseImage.height, 0, 0, warningImage.width, warningImage.height, nsGraphics.NSCompositeSourceOver, 1.0)
nsGraphics = nil
warningIcon = baseImage
This is just adding a black circle behind the transparent part of an ! character in an icon, but you can hopefully see how you can use drawInRect to place images into an NSImage
The problem is that you are assuming the colour values you are assigning are sRGB where in fact, they are in a generic RGB colour space which is what Xojo on macOS uses.
If you have the MBS plugins you can use the function below to convert colours before you assign them to Graphics.DrawingColor and draw. You can also use it to assign colours to UI controls if you want them to match.
NOTE. My function doesn’t handle the alpha property but I imagine it would be easy for you to change that.
Public Function ConvertSRGBColourForUI(pColour As Color) As Color
#Pragma DisableBackgroundTasks
#Pragma DisableBoundsChecking
#Pragma StackOverflowChecking False
Dim theResult As Color
'under osx we convert the input colour defined in srgb space into the device space used by xojo
'under win32 we just return a clone of the input colour
#If TargetMacOS Then
Static sUIColourDictionary As Dictionary
Dim key As Int32
Dim srgbNSColorObj As NSColorMBS
Dim deviceNSColorObj As NSColorMBS
'initialise our colour dictionary which will hold previously converted colours
If sUIColourDictionary = Nil Then
sUIColourDictionary = New Dictionary
End If
'build a dictionary key based on the colour values
key = (pColour.Red * 65536) + (pColour.Green * 256) + pColour.Blue
If sUIColourDictionary.HasKey(key) = True Then
'colour has been cached
theResult = sUIColourDictionary.Value(key)
Else
'colour has not been cached
'convert the input srgb colour to our output colour via nscolor
srgbNSColorObj = NSColorMBS.colorWithSRGB(pColour.Red / 255, pColour.Green / 255, pColour.Blue / 255)
If srgbNSColorObj <> Nil Then
deviceNSColorObj = srgbNSColorObj.colorUsingColorSpace(NSColorSpaceMBS.genericRGBColorSpace)
If deviceNSColorObj <> Nil Then
'convert the floating point colour back to an integer colour
'note. the algorithm came from: https://developer.apple.com/library/archive/qa/qa1576/_index.html
theResult = RGB(deviceNSColorObj.redComponent * 255.99999, deviceNSColorObj.greenComponent * 255.99999, deviceNSColorObj.blueComponent * 255.99999)
'store the converted colour in our cache
sUIColourDictionary.Value(key) = theResult
Else
'colour conversion failed
'default back to the input colour
theResult = pColour
End If
Else
'colour conversion failed
'default back to the input colour
theResult = pColour
End If
End If
#Else
'win32
'just clone the input colour as the result
theResult = pColour
#EndIf
'return a clone of the colour to avoid the original being modified
Return RGB(theResult.Red, theResult.Green, theResult.Blue)
End Function