Save for Web: Colorspaces

Colorspaces are tricky things. For many apps, we never have to worry about them, leaving handling entirely to the system. But for something like Sandvox, we have to start thinking about colorspaces and how the web handles them. As a layman to this sort of business, I’ve had to do some digging to get my head round what we need.

Here’s what I’ve learnt about colorspaces so far:

  1. They're a pain
  2. "color" isn't in the UK spelling dictionary, but weirdly, "colorspace" is

Oh, you actually wanted some useful information? Read on then.

Core Image

Our common case in Sandvox is: A high quality (meaning lots of pixels here) photo is placed into a site. For preview purposes, it's fine to let WebKit handle scaling and display of the image. Come publishing time, Sandvox needs to generate a number of scaled copies of the photo, so as to save bandwidth for viewers of the site.

We do that using Core Image, roughly as described by Dan Wood. A CIContext is used to render CIImages into something more concrete, like a graphics context, or a CGImage. We're generating the latter and then passing into CGImageDestination to produce JPEGs/PNGs for publishing.

CIContexts have two colorspaces associated with them: working, and output. By default, both are matched to your screen, which makes sense since that's where Core Image typically spends its time rendering to. For the web though, the accepted standard is the sRGB colorspace, so when creating the context, specify both of those in the options dictionary.

Even after setting the context’s colorspaces, I find it advisable to use the -createCGImage:fromRect:format:colorSpace: method, since Core Image does sometimes detect that no actual processing is required, and, for performance, returns an image in its original colorspace.

Think that all sounds a lot to take in? Well worry no longer, as I do in fact have a tasty bit of code for you to use: KSCreateCGImageForWebOperation. It works back to Mac OS X 10.5, and is BSD licensed.

The Fast Path

What if the image is already sRGB and the correct size? I’m not going to go into how to handle this scenario in this post, but how do you even detect it?

It turns out there’s no obvious API for this. Pointer equality is a no-go in my testing. You can use CGColorSpaceCopyName(), but that’s only available on 10.6+ and the docs suggest it shouldn’t actually work! Another option might be to compare the ICC Profile data.

What I’ve settled upon instead though, is using CGImageSource to report the name of the colorspace. If it’s equal to @"sRGB IEC61966-2.1” then assume the space really is sRGB. This feels like a bit of a hack, but it’s fast to do, and you can then bypass Core Image. Let me know if there’s a better way!

I’ve got no public sample code for this yet, but hope to soon.

So there you go, that's what Sandvox does. I admit this is as much brain-dump for my benefit, as it is yours, but let me know if you think I've missed something.

© Mike Abdullah 2007-2015