Image color display problem

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?

That may be the same phenomenon as the one discussed here: Right color of text, wrong RGB

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:

#81813 - Inaccurate Color Rendering

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:

#81813 - Add Explicit Color Space Support for Consistent Colors Across Platforms

If interested, view the ticket for more details. In short this has turned into a feature request that is asking for the following:

  1. Standardize Xojo Color on sRGB by default across platforms. Preserve a legacy Generic-RGB mode for existing macOS projects if necessary.
  2. Add explicit color-space support to Color and Picture. Let developers specify, inspect, assign, and convert profiles/spaces.
  3. 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)

The issue is that you can’t convert the image to a Xojo picture object. That’s when it loses the color profile.

Give this a try

NSImageViewControl.zip (8.5 KB)

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.

That works great for NSImageViewControl. I’m gettin the right colors being sampled everywhere.

Just not sure how I can apply this to a canvas where each rect in it that has its own image. But thank you for something that works.

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