|
Q: How do I create color spaces so that I can make sure my colors are matched?A: To work with color effectively and to understand the Quartz 2D functions for using color spaces and color, you should be familiar with the terminology discussed in the Color Management Overview. That document discusses color perception, color values, device-independent and device color spaces, the color-matching problem, rendering intent, color management modules, and ColorSync. Devices (displays, printers, scanners, cameras) don't treat color the same way; each has its own range of colors that the device can produce faithfully. A color produced on one device might not be able to be produced on another device. The best way to avoid these kinds of problems is to use a color space with a "calibration profile" ? for example, an ICCBased color space which uses an ICC profile for calibration. In such a case the system will be able to translate color from this color space to any destination (e.g. display) in the best possible way. Unless there is already a profile for the destination, the best solution is to use a generic color space. Generic color spaces, for example Generic RGB or Generic CYMK, are color spaces that will always be calibrated when copied to a destination device. New in Mac OS X 10.4, the constants Listing 1: Creating a generic RGB color space using the kCGColorSpaceGenericRGB avalible in 10.4 and later CGColorSpaceRef genericRGBColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); Device color spaces are device-dependent and assume all colors are already calibrated for the destination device. The following APIs create device-dependent color spaces and should be avoided: Mac OS X provides the following ICC profile files in the directory
While there isn't a simple API for creating ICC color space from an ICC color profile, Listing 2 provides an example of how to create one. Listing 2: Creating a color space from an ICC profile, and examples for creating a Generic RGB Color Space and an sRGB Color Space. CGColorSpaceRef CreateICCColorSpaceFromPathToProfile (const char * iccProfilePath) { CMProfileRef iccProfile = (CMProfileRef) 0; CGColorSpaceRef iccColorSpace = NULL; CMProfileLocation loc; // Specify that the location of the profile will be a POSIX path to the profile. loc.locType = cmPathBasedProfile; // Make sure the path is not larger then the buffer if(strlen(iccProfilePath) > sizeof(loc.u.pathLoc.path)) return NULL; // Copy the path the profile into the CMProfileLocation structure strcpy (loc.u.pathLoc.path, iccProfilePath); // Open the profile if (CMOpenProfile(&iccProfile, &loc) != noErr) { iccProfile = (CMProfileRef) 0; return NULL; } // Create the ColorSpace with the open profile. iccColorSpace = CGColorSpaceCreateWithPlatformColorSpace( iccProfile ); // Close the profile now that we have what we need from it. CMCloseProfile(iccProfile); return iccColorSpace; } CGColorSpaceRef CreateColorSpaceFromSystemICCProfileName(CFStringRef profileName) { FSRef pathToProfilesFolder; FSRef pathToProfile; // Find the Systems Color Sync Profiles folder if(FSFindFolder(kOnSystemDisk, kColorSyncProfilesFolderType, kDontCreateFolder, &pathToProfilesFolder) == noErr) { // Make a UniChar string of the profile name UniChar uniBuffer[sizeof(CMPathLocation)]; CFStringGetCharacters (profileName,CFRangeMake(0,CFStringGetLength(profileName)),uniBuffer); // Create a FSRef to the profile in the Systems Color Sync Profile folder if(FSMakeFSRefUnicode (&pathToProfilesFolder,CFStringGetLength(profileName),uniBuffer, kUnicodeUTF8Format,&pathToProfile) == noErr) { char path[sizeof(CMPathLocation)]; // Write the posix path to the profile into our path buffer from the FSRef if(FSRefMakePath (&pathToProfile,path,sizeof(CMPathLocation)) == noErr) return CreateICCColorSpaceFromPathToProfile(path); } } return NULL; } CGColorSpaceRef CreateICCGenericRGBColorSpace() { return CreateColorSpaceFromSystemICCProfileName(CFSTR("Generic RGB Profile.icc")); } CGColorSpaceRef CreateICCsRGBColorSpace() { return CreateColorSpaceFromSystemICCProfileName(CFSTR("sRGB Profile.icc")); } Offscreen caches are generally used to enhance performance, but if a color space is used to create an offscreen cache that doesn't match the display device, then every time the data is copied to the display device it must go though a color calibration process. Since every display device maps colors differently to the same color values, the optimum method is to ask the system for the display devices profile. Once the display devices profile is obtained, it can then be used to create a display color space which can be used to create a CGBitmapContext to be used as an offscreen cache. Listing 2 shows how to create a color space based on the current display profile. Listing 3: Creating a color space that represents the main display CGColorSpaceRef CreateSystemColorSpace () { CMProfileRef sysprof = NULL; CGColorSpaceRef dispColorSpace = NULL; // Get the Systems Profile for the main display if (CMGetSystemProfile(&sysprof) == noErr) { // Create a colorspace with the systems profile dispColorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysprof); // Close the profile CMCloseProfile(sysprof); } return dispColorSpace; } Using "display color space", based on the current display profile, is telling the system that you don't want the color to be matched when it is copied to the screen, but you do want color matching when drawing to the offscreen cache. Since this "display color space" is based on the current display, if the cached data is going to be repeated on another system with a different display, the color appearance will be different because each device reacts differently to the same color values. Document Revision History
Posted: 2005-10-04 |
|