Tuesday, June 2, 2009

qcms is a brand-new color management system replacing Mozilla's previous solution, lcms. The browser's color management system allows us to use data in photos on the web and data about your monitor to make sure that colors look the same everywhere. This helps photographers and site designers control the appearance of their images more precisely, and makes the web a prettier place.

Normally, rewriting a large chunk of code from scratch is a bad idea. However, we were in a situation that made rewriting pretty attractive. First, we were only using a small portion of lcms's functionality, but we were paying for the large code base in both maintainability and threat surface. Second, we were already maintaining substantial modifications which was an additional maintenance burden. Finally, our requirements for a color management library are different then the goals of lcms: we need something fast and secure, while lcms seems more focused on functionality, completeness and correctness.

What's new?

qcms is made up of two main parts: the ICC profile parser and the transformation engine. The transformation engine reuses a lot of code from lcms. The ICC profile parser is completely new and written with security and robustness in mind.

The new parser has some key design differences compared to lcms. One of these is the I/O model. lcms has an I/O abstraction layer that abstracts reading from memory and reading from a file. It uses this layer to read what it needs from the profile as it's needed. This means that the full file or memory copy of the profile needs to be kept around for the lifetime of the profile object.1

qcms uses a simpler model. The entire profile is read into memory and then parsed to construct a profile object. We only keep the information that's needed to construct a transformation between color spaces. After parsing we can close the file or free the memory used to store the profile. Furthermore, since we only parse in-memory, we also don't have to deal with any possible read errors or other file I/O problems during parsing.

Error handling during parsing can be tricky and ridden with security holes. To help deal with this problem, qcms adopts an error handling strategy similar to cairo. Instead of trying to deal with the error immediately and returning an error result up the call stack, we often set a flag to note the brokenness, putting us into an error state, and continue on. To continue successfully, all of the following operations must be completable even while in an error state. However, ensuring this is usually easy, especially if the results will be discarded. When we do get to a place where it's convenient to return, we do so. The big advantage to this approach is that it keeps the error state control flow as similar to the normal control flow as possible. This makes the code easier to read, easier to reason about, and easier to test.

Speed

Last summer, Bobby Holley did a bunch of work to make lcms faster. I was able to reuse that work in qcms. The result is that qcms is one of the fastest color management systems around. Here's a simple test that transforms all the possible RGB components from one RGB colorspace to another. It compares lcms, qcms and ColorSync, the system color management system on OS X.

Current Limitations

qcms currently only supports transformations to and from RGB colorspaces. This covers the vast majority of uses on web; however, it means there's no support for CMYK and many additional profile types. If you are interested in making this code better, let me know!

Conclusion

In the end, we have a library about a tenth of the size and 5 times as fast than what we had previously. I've put the code up at git://git.freedesktop.org/~jrmuizel/qcms. It's portable C and licensed under the same license as lcms so it's pretty easy to change from one api to another. Hopefully other projects can find it useful. Finally, a big thanks to Marti Maria for lcms, without which qcms wouldn't been possible.