diff options
| author | Michele Calgaro <michele.calgaro@yahoo.it> | 2020-09-11 14:38:47 +0900 |
|---|---|---|
| committer | Michele Calgaro <michele.calgaro@yahoo.it> | 2020-09-11 14:38:47 +0900 |
| commit | 884c8093d63402a1ad0b502244b791e3c6782be3 (patch) | |
| tree | a600d4ab0d431a2bdfe4c15b70df43c14fbd8dd0 /debian/lcms/lcms-1.19.dfsg2/doc/TUTORIAL.TXT | |
| parent | 14e1aa2006796f147f3f4811fb908a6b01e79253 (diff) | |
| download | extra-dependencies-884c8093d63402a1ad0b502244b791e3c6782be3.tar.gz extra-dependencies-884c8093d63402a1ad0b502244b791e3c6782be3.zip | |
Added debian extra dependency packages.
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'debian/lcms/lcms-1.19.dfsg2/doc/TUTORIAL.TXT')
| -rwxr-xr-x | debian/lcms/lcms-1.19.dfsg2/doc/TUTORIAL.TXT | 1911 |
1 files changed, 1911 insertions, 0 deletions
diff --git a/debian/lcms/lcms-1.19.dfsg2/doc/TUTORIAL.TXT b/debian/lcms/lcms-1.19.dfsg2/doc/TUTORIAL.TXT new file mode 100755 index 00000000..1a0096ef --- /dev/null +++ b/debian/lcms/lcms-1.19.dfsg2/doc/TUTORIAL.TXT @@ -0,0 +1,1911 @@ + + + + little cms Engine + http://www.littlecms.com + + How to use the engine in your applications + + by Marti Maria + + Ver 1.19 + + --------------------------------------------------- + + Introduction + + BASIC USAGE + =========== + + 1. Basic Concepts + 2. Step-by-step example + 3. Embedded profiles + 4. Device-link profiles + + 5. Built-in profiles + 6. On-the-fly profiles + 7. Gamma tables + 8. Proofing + 9.1 Black point compensation + 9.2 Black preservation + + 10. Error handling + 11. Getting information from profiles. + + ADVANCED TOPICS + =============== + + 12. Creating and writting new profiles + 13. LUT handling + 14. Helper functions + 15. Color difference functions + 16. PostScript operators + 17. CIECAM02 + 18. Named color profiles + + 19. Conclusion + + Sample 1: How to convert RGB to CMYK and back + Sample 2: How to deal with Lab/XYZ spaces + + Annex A. About intents + Annex B. Apparent bug in XYZ -> sRGB transforms + + ---------------------------- + +Introduction: + + This file has been written to present the lcms core library to + would-be writers of applications. It first describes the + concepts on which the engine is based, and then how to use it + to obtain transformations, colorspace conversions and + separations. Then, a guided step-by-step example, shows how to + use the engine in a simple or more sophisticated way. + + This document doesn't even try to explain the basic concepts of + color management. For a comprehensive explanation, I will + recommend the excellent color & gamma FAQs by Charles A. + Poynton, + + http://www.poynton.com + + For more details about profile architecture, you can reach the + latest ICC specs on: + + http://www.color.org + + + **PLEASE NOTE THAN lcms IS NOT AN ICC SUPPORTED LIBRARY** + + + I will assume the reader does have a working knowledge of the C + programming language. This don't mean lcms can only be used by + C applications, but it seems the easiest way to present the API + functionality. I currently have successfully used the lcms DLL + from Delphi, C++ Builder, Visual C++, Tcl/Tk, and even Visual + Basic. + + DELPHI USERS: + + If you plan to use lcms from Delphi, there is a folder in the + package containing units and samples for Delphi interface. Rest + of document does refer to C API, but you can use same functions + on Delphi. + + +1. Basic Concepts: +============================================================================ + + lcms defines two kinds of structures, that are used to manage + the various abstractions required to access ICC profiles. These + are profiles and transforms. + + In a care of good encapsulation, these objects are not directly + accessible from a client application. Rather, the user receives + a 'handle' for each object it queries and wants to use. This + handle is a stand-alone reference; it cannot be used like a + pointer to access directly the object's data. + + There are typedef's for such handles: + + cmsHPROFILE identifies a handle to an open profile. + cmsHTRANSFORM identifies a handle to a transform. + + Conventions of use: + + o All API functions and types have their label prefixed + by 'cms' (lower-case). + + o #defined constants are always in upper case + + o Some functions does accepts flags. In such cases, + you can build the flags specifier joining the values with the + bitwise-or operator '|' + + o An important note is that the engine should not leak + memory when returning an error, e.g., querying the + creation of an object will allocate several internal + tables that will be freed if a disk error occurs during + a load. + + Since these are a very generic conventions widely used, I will + no further discuss this stuff. + + + +2. Step-by-step Example: +============================================================================ + + Here is an example to show, step by step, how a client application + can transform a bitmap between two ICC profiles using the lcms API + + This is an example of how to do the whole thing: + + + +#include "lcms.h" + + +int main(void) +{ + + cmsHPROFILE hInProfile, hOutProfile; + cmsHTRANSFORM hTransform; + int i; + + + hInProfile = cmsOpenProfileFromFile("HPSJTW.ICM", "r"); + hOutProfile = cmsOpenProfileFromFile("sRGBColorSpace.ICM", "r"); + + + + hTransform = cmsCreateTransform(hInProfile, + TYPE_BGR_8, + hOutProfile, + TYPE_BGR_8, + INTENT_PERCEPTUAL, 0); + + + for (i=0; i < AllScanlinesTilesOrWatseverBlocksYouUse; i++) + { + cmsDoTransform(hTransform, YourInputBuffer, + YourOutputBuffer, + YourBuffersSizeInPixels); + } + + + + cmsDeleteTransform(hTransform); + cmsCloseProfile(hInProfile); + cmsCloseProfile(hOutProfile); + + return 0; +} + + + Let's discuss how it works. + + a) Open the profiles + + You will need the profile handles for create the transform. In + this example, I will create a transform using a HP Scan Jet + profile present in Win95 as input, and sRGB profile as output. + + This task can be done by following lines: + + cmsHPROFILE hInProfile, hOutProfile; + + hInProfile = cmsOpenProfileFromFile("HPSJTW.ICM", "r") + hOutProfile = cmsOpenProfileFromFile("sRGBColorSpace.ICM", "r") + + + You surely have noticed a second parameter with a small "r". + This parameter is used to set the access mode. It describes + the "opening mode" like the C function fopen(). + + Currently lcms does support both read and write profiles. + + WARNING!: opening with 'w' WILL OVERWRITE YOUR PROFILE! + Don't do this except if you want to create a NEW profile. + + + NOTES: + + This only will take a small fraction of memory. The BToA or AToB tables, + which usually are big, are only loaded at transform-time, and on demand. + You can safely open a lot of profiles if you wish so. + + If cmsOpenProfileFromFile() fails, it raises an error signal that can + or cannot be catched by the application depending of the state of the + error handler. In this example, I'm using the "if-error-abort-whole- + application" behaviour, corresponding with the LCMS_ERROR_ABORT setting + of cmsErrorAction(). See the error handling paragraph below for more + information. + + lcms is a standalone color engine, it knows nothing about where + the profiles are placed. lcms does assume nothing about + a specific directory (as Windows does, currently expects profiles + to be located on SYSTEM32/SPOOL/DRIVERS/COLOR folder in main windows + directory), so for get this example working, you need to copy the + profiles in the local directory. + + + b) Identify the desired format of pixels. + + lcms can handle a lot of formats. In fact, it can handle: + + - 8 and 16 bits per channel + - up to 16 channels + - extra channels like alpha + - swapped-channels like BGR + - endian-swapped 16 bps formats like PNG + - chunky and planar organization + - Reversed (negative) channels + - Floating-point numbers + + + For describing such formats, lcms does use a 32-bit value, referred + below as "format specifiers". + + There are several (most usual) encodings predefined as constants, but there + are a lot more. See lcms.h to review the current list. + + TYPE_RGB_DBL + TYPE_CMYK_DBL + TYPE_Lab_DBL + TYPE_XYZ_DBL + TYPE_YCbCr_DBL Takes directly the floating-point structs + + TYPE_GRAY_8 Grayscale 8 bits + TYPE_GRAY_16 Grayscale 16 bits + TYPE_GRAY_16_SE Grayscale 16 bits, swap endian + TYPE_GRAYA_8 Grayscale + alpha, 8 bits + TYPE_GRAYA_16 Grayscale + alpha, 16 bits + TYPE_GRAYA_16_SE Grayscale + alpha, 16 bits + TYPE_GRAYA_8_PLANAR Grayscale + alpha, 8 bits, separate planes + TYPE_GRAYA_16_PLANAR Grayscale + alpha, 16 bits, separate planes + + TYPE_RGB_8 RGB, 8 bits + TYPE_RGB_8_PLANAR RGB, 8 bits, separate planes + TYPE_BGR_8 BGR, 8 bits (windows uses this format for BMP) + TYPE_BGR_8_PLANAR BGR, 8 bits, separate planes + TYPE_RGB_16 RGB, 16 bits + TYPE_RGB_16_PLANAR ... + TYPE_RGB_16_SE + TYPE_BGR_16 + TYPE_BGR_16_PLANAR + TYPE_BGR_16_SE + + TYPE_RGBA_8 These ones with alpha channel + TYPE_RGBA_8_PLANAR + TYPE_RGBA_16 + TYPE_RGBA_16_PLANAR + TYPE_RGBA_16_SE + TYPE_ABGR_8 + TYPE_ABGR_16 + TYPE_ABGR_16_PLANAR + TYPE_ABGR_16_SE + + TYPE_CMY_8 These ones for CMY separations + TYPE_CMY_8_PLANAR + TYPE_CMY_16 + TYPE_CMY_16_PLANAR + TYPE_CMY_16_SE + + TYPE_CMYK_8 These ones for CMYK separations + TYPE_CMYK_8_PLANAR + TYPE_CMYK_16 + TYPE_CMYK_16_PLANAR + TYPE_CMYK_16_SE + + TYPE_KYMC_8 Reversed CMYK + TYPE_KYMC_16 + TYPE_KYMC_16_SE + + TYPE_XYZ_16 XYZ, xyY and CIELab + TYPE_Yxy_16 + TYPE_Lab_8 + TYPE_Lab_16 + + TYPE_CMYKcm_8 HiFi separations + TYPE_CMYKcm_8_PLANAR + TYPE_CMYKcm_16 + TYPE_CMYKcm_16_PLANAR + TYPE_CMYKcm_16_SE + + TYPE_CMYK7_8 + TYPE_CMYK7_16 + TYPE_CMYK7_16_SE + TYPE_KYMC7_8 + TYPE_KYMC7_16 + TYPE_KYMC7_16_SE + TYPE_CMYK8_8 + TYPE_CMYK8_16 + TYPE_CMYK8_16_SE + + .. etc... + + For example, if you are transforming a windows .bmp to a bitmap for + display, you will use TYPE_BGR_8 for both, input and output + buffers, windows does store images as B,G,R and not as R,G,B. + + Other example, you need to convert from a CMYK separation to + RGB in order to display; then you would use TYPE_CMYK_8 on + input and TYPE_BGR_8 on output. If you need to do the + separation from a TIFF, TYPE_RGB_8 on input and TYPE_CMYK_8 on + output. Please note TYPE_RGB_8 and TYPE_BGR_8 are *not* same. + + + The format specifiers are useful above color management. This will + provide a way to handle a lot of formats, converting them in a single, + well-known one. For example, if you need to deal with several pixel + layouts coming from a file (TIFF for example), you can use a fixed + output format, say TYPE_BGR_8 and then, vary the input format + on depending on the file parameters. lcms also provides a flag for + inhibit color management if you want speed and don't care about + profiles. see cmsFLAGS_NULLTRANSFORM for more info. + + + + c) Create the transform + + When creating transform, you are giving to lcms all information it + needs about how to translate your pixels. The syntax for simpler + transforms is: + + + cmsHTRANSFORM hTransform; + + hTransform = cmsCreateTransform(hInputProfile, + TYPE_BGR_8, + hOutputProfile, + TYPE_BGR_8, + INTENT_PERCEPTUAL, 0); + + You give the profile handles, the format of your buffers, the rendering + intent and a combination of flags controlling the transform behaviour. + + It's out of scope of this document to define the exact meaning + of rendering intents. I will try to make a quick explanation + here, but often the meaning of intents depends on the profile + manufacturer. See appendix A for more information. + + + INTENT_PERCEPTUAL: + + Hue hopefully maintained (but not required), + lightness and saturation sacrificed to maintain + the perceived color. White point changed to + result in neutral grays. Intended for images. + + In lcms: Default intent of profiles is used + + INTENT_RELATIVE_COLORIMETRIC: + + Within and outside gamut; same as Absolute + Colorimetric. White point changed to result in + neutral grays. + + In lcms: If adequate table is present in profile, + then, it is used. Else reverts to perceptual + intent. + + INTENT_SATURATION: + + Hue and saturation maintained with lightness + sacrificed to maintain saturation. White point + changed to result in neutral grays. Intended for + business graphics (make it colorful charts, + graphs, overheads, ...) + + In lcms: If adequate table is present in profile, + then, it is used. Else reverts to perceptual + intent. + + INTENT_ABSOLUTE_COLORIMETRIC: + + Within the destination device gamut; hue, + lightness and saturation are maintained. Outside + the gamut; hue and lightness are maintained, + saturation is sacrificed. White point for source + and destination; unchanged. Intended for spot + colors (Pantone, TruMatch, logo colors, ...) + + In lcms: relative colorimetric intent is used + with undoing of chromatic adaptation. + + + Not all profiles does support all intents, there is a function + for inquiring which intents are really supported, but if you + specify a intent that the profile doesn't handle, lcms will + select default intent instead. Usually perceptual one. This + will force to "look nice", no matter the intent is not the one + really desired. + + + lcms tries to "smelt" input and output profiles in a single + matrix-shaper or in a big 3D CLUT of 33 points. This will + improve greatly the performance of the transform, but may + induce a small delay of 1-2 seconds on some really old + machines. + + If you are willing to transform just a palette or a few + colors, you don't need this precalculations. Then, the flag + cmsFLAGS_NOTPRECALC in cmsCreateTransform() can be used to + inhibit the 3D CLUT creation. + + See the API reference for a more detailed discussion of the flags. + + NOTES: + + Some old display profiles, only archives absolute colorimetric + intents. For these profiles, default intents are absolute + colorimetric ones. This is really a rare case. + + + d) Next, you can translate your bitmap, calling repeatedly the processing + function: + + + cmsDoTransform(hTransform, YourInputBuffer, + YourOutputBuffer, + YourBuffersSize); + + + + This function is intended to be quite fast. You can use this + function for translating a scan line, a tile, a strip, or whole + image at time. + + + NOTES: + + Windows, stores the bitmaps in a particular way... for speed + purposes, does align the scan lines to double word boundaries, + a bitmap has in windows always a size multiple of 4. This is + OK, since no matter if you waste a couple of bytes, but if you + call cmsDoTransform() and passes it WHOLE image, lcms doesn't + know nothing about this extra padding bytes. It assumes that + you are passing a block of BGR triplets with no alignment at + all. This result in a strange looking "lines" in obtained + bitmap. + + The solution most evident is to convert scan line by scan line + instead of whole image. This is as easy as to add a for() + loop, and the time penalty is so low that is impossible to + detect. + + It is safe to use same block for input and output, but only if + the input and output are coded in same format. For example, + you can safely use only one buffer for RGB to RGB but you + cannot use same buffer for RGB as input and CMYK as output. + + + + e) Last, free all stuff. + + This can be done by calling + + cmsDeleteTransform(hTransform); + cmsCloseProfile(hInputProfile); + cmsCloseProfile(hOutputProfile); + + And you are done! + + Note that cmsDeleteTransform() does NOT automatically free + associated profiles. This works in such way to let + programmers to use a open profile in more than one transform. + + + +3. Embedded profiles +============================================================================ + + Some image file formats, like TIFF, JPEG or PNG, does include + the ability of embed profiles. This means that the input + profile for the bitmap is stored inside the image file. lcms + provides a specialised profile-opening function for deal with + such profiles. + + + cmsHPROFILE cmsOpenProfileFromMem(LPVOID MemPtr, DWORD dwSize); + + + This function works like cmsOpenProfileFromFile(), but assuming + that you are given full profile in a memory block rather than a + filename. Here is not any "r", since these profiles are always + read-only. A successful call will return a handle to an opened + profile that behaves just like any other file-based. + + Memory based profiles does not waste more resources than memory, + so you can have tons of profiles opened sumultaneously using this + function. + +NOTES: + + Once opened, you can safely FREE the memory block. lcms keeps a + temporary copy. + + You can retrieve information of this profile, but generally + these are minimal shaper-matrix profiles with little if none + handy info present. + + Be also warned that some software does embed WRONG profiles, + i.e., profiles marked as using different colorspace that + one the profile really manages. lcms is NOT likely to understand + these profiles since they are wrong after all. + + +4. Device-link profiles +============================================================================ + + Device-link profiles are "smelted" profiles that represents + a whole transform rather than single-device profiles. In theory, + device-link profiles may have greater precision that single ones + and are faster to load. If you plan to use device-link profiles, + be warned there are drawbacks about its inter-operability and the + gain of speed is almost null. + Perhaps their only advantage is when restoration from CMYK + with great precision is required, since CMYK to pcs CLUTs can + become very, very big. + + + For creating a device-link transform, you must open the device link + profile as usual, using cmsOpenProfileFromFile(). Then, create + the transform with the device link profile as input and the output + profile parameter equal to NULL: + + + hDeviceLink = cmsOpenProfileFromFile("MYDEVLINK.ICC", "r"); + + hTransform = cmsCreateTransform(hDeviceLink, TYPE_RGB_8, + NULL, TYPE_BGR_8, + INTENT_PERCEPTUAL, + 0); + + + That's all. lcms will understand and transparently handle the + device-link profile. + + There is also a function for dumping a transform into a devicelink + profile + + cmsHPROFILE cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, + DWORD dwFlags); + + This profile can be used in any other transform or saved to disk/memory. + + + + +5. - Built-in profiles +============================================================================ + + In order to make things ease, there are several built-in profiles that + programmer can use without the need of any disk file. These does include: + + - sRGB profile + - L*a*b profiles + - XYZ profile + - Gray profiles + - RGB matrix-shaper. + - Linearization device link + - Ink-Limiting + - Bright/Contrast/Hue/Saturation/White point adjust devicelink. + + sRGB, Lab and XYZ are very usefull for tricking & trapping. For example, + creating a transform from sRGB to Lab could be done without any disk file. + Something like: + + hsRGB = cmsCreate_sRGBProfile(); + hLab = cmsCreateLabProfile() + + xform = cmsCreateTransform(hSRGB, TYPE_RGB_DBL, hLab, TYPE_Lab_DBL, + INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC); + + + Then you can convert directly form double sRGB values (in 0..1.0 range) to + Lab by using: + + double RGB[3]; + cmsCIELab Lab; + + RGB[0] = 0.1; RGB[1] = 0.2 RGB[2] = 0.3; + cmsDoTransform(xform, RGB, &Lab, 1); + + .. get result on "Lab" variable .. + + + Even more, you can create your own RGB or Gray profiles "on the fly" by + using cmsCreateRGBProfile() and cmsCreateGrayProfile(). See next section + for a explanation on how to do. + + + + +6. - On-the-fly profiles. +============================================================================ + + There are several situations where it will be useful to build + a minimal profile using adjusts only available at run time. + + Surely you have seen the classical pattern-gray trick for adjusting + gamma: the end user moves a scroll bar and when pattern seems to + match background gray, then gamma is adjusted. + Another trick is to use a black background with some gray rectangles. + The user chooses the most neutral grey, giving the white point or the + temperature in °K. + + All these visual gadgets are not part of lcms, you must implement + them by yourself if you like. But lcms will help you with a function for + generating a virtual profile based on the results of these tests. + + Another usage would be to build colorimetric descriptors for + file images that does not include any embedded profile, but + does include fields for identifying original colorspace. + + One example is TIFF files. The TIFF 6.0 spec talks about + "RGB Image Colorimetry" (See section 20) a "colorimetric" TIFF + image has all needed parameters (WhitePointTag=318, + PrimaryChromacitiesTag=318, TransferFunction=301,TransferRange=342) + + Obtain a emulated profile from such files is easy since the contents + of these tags does match the cmsCreateRGBProfile() parameters. + + Also PNG can come with information for build a virtual profile, + See the gAMA and cHRM chunks. + + + This is the main function for creating virtual RGB profiles: + + + cmsHPROFILE cmsCreateRGBProfile(LPcmsCIExyY WhitePoint, + LPcmsCIExyYTRIPLE Primaries, + LPGAMMATABLE TransferFunction[3]); + + + It takes as arguments the white point, the primaries and 3 + gamma curves. The profile emulated is always operating in RGB + space. Once created, a handle to a profile is returned. This + opened profile behaves like any other file or memory based + profile. + + Virtual RGB profiles are implemented as matrix-shaper, so they + cannot compete against CLUT based ones, but generally are good enough + to provide a reasonable alternative to generic profiles. + + + For simplify the parameters construction, there are additional + functions: + + LCMSBOOL cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint); + + This function computes the xyY chromacity of white point using + the temperature. Screen manufacturers often includes a white + point hard switch in monitors, but they refer as "Temperature" + instead of chromacity. Most extended temperatures are 5000K, + 6500K and 9300K + + It returns TRUE if a valid white point can be computed, or FALSE + if the temperature were non valid. You must give a pointer to a + cmsCIExyY struct for holding resulting white point. + + For primaries, currently I don't know any trick or proof for + identifying primaries, so here are a few chromacities of most + extended. Y is always 1.0 + + + RED GREEN BLUE + x y x y x y + ---- ---- ---- ---- ---- ---- + NTSC 0.67, 0.33, 0.21, 0.71, 0.14, 0.08 + EBU(PAL/SECAM) 0.64, 0.33, 0.29, 0.60, 0.15, 0.06 + SMPTE 0.630, 0.340, 0.310, 0.595, 0.155, 0.070 + HDTV 0.670, 0.330, 0.210, 0.710, 0.150, 0.060 + CIE 0.7355,0.2645,0.2658,0.7243,0.1669,0.0085 + + + These are TRUE primaries, not colorants. lcms does include a + white-point balancing and a chromatic adaptation using a method + called Bradford Transform for D50 adaptation. + + NOTE: Additional information about Bradford transform math can be found + on the sRGB site: + + http://www.srgb.com + + Another kind of profiles that can be built on runtime are GrayScale + profiles. This can be accomplished by the function: + + cmsHPROFILE cmsCreateGrayProfile(LPcmsCIExyY WhitePoint, + LPGAMMATABLE TransferFunction); + + This one is somehow easier, since it only takes the gray curve (transfer + function) and the media white point. Of course gray scale does not need + primaries! + + + 7. - Gamma tables + ============================================================================ + + The gamma tables or transfer functions are stored in a simple way, + let's examine the GAMMATABLE typedef: + + typedef struct { + int nEntries; + WORD GammaTable[1]; + + } GAMMATABLE, FAR* LPGAMMATABLE; + + That is, first it comes a 32 integer for entry count, followed of + a variable number of words describing the table. The easiest way to + generate a gamma table is to use the function + + LPGAMMATABLE cmsBuildGamma(int nEntries, double Gamma); + + You must specify the number of entries your table will consist of, + and the float value for gamma. The generated table has linear and non-linear + steps, the linear ramp near 0 is for minimising noise. + + If you want to fill yourself the values, you can allocate space for your + table by using + + LPGAMMATABLE cmsAllocGamma(int nEntries); + + This function only creates memory for the table. The entries does + not contain any useful value (garbage) since it is expected you will fill + this table after created. + + + You can find the inverse of a tabulated curve by using: + + + LPGAMMATABLE cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma); + + This function reverses the gamma table if it can be done. lcms does not + detect whatever a non-monotonic function is given, so wrong input can + result in ugly results: not to be a problem since "normal" gamma curves + are not collapsing inputs at same output value. The new curve will be + re-sampled to nResultSamples entries. + + You can also smooth the curve by using: + + LCMSBOOL cmsSmoothGamma(LPGAMMATABLE Tab, double lambda); + + "Smooth" curves does work better and are more pleasant to eyes. + + + You can join two gamma curves with: + + LPGAMMATABLE cmsJoinGamma(LPGAMMATABLE InGamma, + LPGAMMATABLE OutGamma); + + + This will let you to "refine" the generic gamma for monitors (2.1 or 2.2 + are usual values) to match viewing conditions of more or less background + light. Note that this function uses TABULATED functions, so very exotic + curves can be obtained by combining transfer functions with reversed + gamma curves. Normally there is no need of worry about such gamma + manipulations, but the functionality is here if you wish to use. + + There is a Extended join function that let specify the point it will have: + + LPGAMMATABLE cmsJoinGammaEx(LPGAMMATABLE InGamma, + LPGAMMATABLE OutGamma, + int nPoints); + + + You must free all gamma tables you allocate (or create via + cmsReverseGamma() or cmsJoinGamma()) by using: + + void cmsFreeGamma(LPGAMMATABLE Gamma); + + + Another functions for dealing with gamma curves are: + + LPGAMMATABLE cmsDupGamma(LPGAMMATABLE Src); + + Duplicates a gamma table, allocatine a new memory block + + double cmsEstimateGamma(LPGAMMATABLE t); + + This one does a coarse estimation of the apparent gamma of a given curve. + It is intended mainly for informational purposes. + + double cmsEstimateGammaEx(LPGAMMATABLE t, double Thereshold); + + This is similar to anterior, but it let specify the Standard deviation + below that the curve is considered pure exponential. cmsEstimateGamma() + does use a default value of 0.7 + + + LPGAMMATABLE cmsBuildParametricGamma(int nEntries, + int Type, + double Params[]); + + This one is intended to build parametric curves, as stated in ICC + 4.0 spec. "Type" refers to ICC type plus one. If type is negative, then + the curve is analitically reversed. This function is still experimental. + + + + + +8. Proofing. +============================================================================ + + An additional ability of lcms is to create "proofing" transforms. + A proofing transform does emulate the colors that will appear if + a image is rendered on a specific device. That is, for example, + with a proofing transform I can see how will look a photo of my + little daughter if rendered on my HP. Since most printer profiles + does include some sort of gamut-remapping, it is likely colors + will not look *exactly* as the original. Using a proofing + transform, it can be done by using the appropriate function. + + Note that this is an important feature for final users, it is worth + of all color-management stuff if the final media is not cheap. + + The creation of a proofing transform involves three profiles, the input + and output ones as cmsCreateTransform() plus another, representing the + emulated profile. + + + cmsHTRANSFORM cmsCreateProofingTransform(cmsHPROFILE Input, + DWORD InputFormat, + cmsHPROFILE Output, + DWORD OutputFormat, + cmsHPROFILE Proofing, + int Intent, + int ProofingIntent, + DWORD dwFlags); + + Also, there is another parameter for specifying the intent for + the proof. The Intent here, represents the intent the user will select + when printing, and the proofing intent represent the intent system is + using for showing the proofed color. Since some printers can archive + colors that displays cannot render (darker ones) some gamut-remapping must + be done to accommodate such colors. Normally INTENT_ABSOLUTE_COLORIMETRIC + is to be used: is is likely the user wants to see the exact colors on screen, + cutting off these unrepresentable colors. INTENT_RELATIVE_COLORIMETRIC could + serve as well. + + + Proofing transforms can also be used to show the colors that are out of the + printer gamut. You can activate this feature by using the + cmsFLAGS_GAMUTCHECK flag in dwFlags field. + + Then, the function: + + void cmsSetAlarmCodes(int r, int g, int b); + + Can be used to define the marker. rgb are expected to be integers + in range 0..255 + + + NOTES: For activating the preview or gamut check features, you MUST + include the corresponding flags + + cmsFLAGS_SOFTPROOFING + cmsFLAGS_GAMUTCHECK + + This is done in such way because the user usually wants to compare + with/without softproofing. Then, you can share same code. If any of the flags + is present, the transform does the proofing stuff. If not, the transform + ignores the proofing profile/intent and behaves like a normal input-output + transform. In practical usage, you need only to associate the check boxes of + "softproofing" and "gamut check" with these flags. + + +9.1 Black point compensation +============================================================================ + + The black point compensation feature does work in conjunction + with relative colorimetric intent. Perceptual intent should make no + difference, although it affects some profiles. + + The mechanics are simple. BPC does scale full image across gray + axis in order to accommodate the darkest tone origin media can + render to darkest tone destination media can render. As a such, + BPC is primarily targeting CMYK. + + Let's take an example. You have a separation (CMYK image) for, + say, SWOP. Then you want to translate this separation to another + media on another printer. The destination media/printer can deliver + a black darker that original SWOP. Now you have several options. + + a) use perceptual intent, and let profile do the gamut remapping for + you. Some users complains about the profiles moving too much the + colors. This is the "normal" ICC way. + + b) use relative colorimetric.This will not move any color, but + depending on different media you would end with "flat" images, + taking only a fraction of available grays or a "collapsed" images, + with loss of detail in dark shadows. + + c) Use relative colorimetric + black point compensation. This is + the discussion theme. Colors are unmoved *except* gray balance + that is scaled in order to accommodate to the dynamic range of new + media. Is not a smart CMM, but a fist step letting the CMM to do + some remapping. + + The algorithm used for black point compensation is a XYZ linear scaling + in order to match endpoints. + + You can enable the BPC feature by using this in the dwFlags field, it works + on softproofs too. + + cmsFLAGS_BLACKPOINTCOMPENSATION + + +9.2 - Black preserving transforms +=========================================================================== + +Black preservation deals with CMYK -> CMYK transforms, and is intended +to preserve, as much as possible, the black (K) channel +whilst matching color by using CMY inks. There is a tradeoff between +accuracy and black preservation, so you lost some accuracy +in order to preserve the original separation. Not to be a big problem +in most cases, benefits of keeping K channel are huge! + + +For sure you have seen prints of gray images with a huge +color cast towards magenta or green. That's very unpleasant. + +Mainly, this happens because metamerism is not taken into +account when doing the profile. Black ink chromaticity changes on +different illuminants. For example, a profile is done measuring black +ink under D50, then, under D50 this black ink have tendency to +magenta. Ok, the profile captures such chroma and, when +reproducing colorimetrically, replaces the destination black with +CMY reproducing this magenta. Now, If you take the original K ink +and examine it under sunlight, it is not magenta anymore! But the +reproduction using CMY keeps going magenta. Result: a nasty color +cast . + +And this is just one of the reasons why keeping black ink is so important. +Maybe there is a slight discontinuity, as big as the chromaticity of +blacks differ, but it is so small that the smoothing induced by the +CLUT is enough to compensate. And this is almost nothing when +compared with the huge cast on grays a CMYK->Lab->CMYK +may create. + + + You can enable the black preservation feature by using this flag: + + cmsFLAGS_PRESERVEBLACK + + + +10. Error handling +============================================================================ + + lcms primary goal is to be quite simple, so error handling is managed + in a simple way. If you are using lcms as a DLL, you can tell lcms what is + supposed to happen when an error is detected. For doing that, you can use + this function. + + void cmsErrorAction(int nAction); + + + 'nAction' can be one of the following values: + + LCMS_ERROR_ABORT 0 + LCMS_ERROR_SHOW 1 + LCMS_ERROR_IGNORE 2 + + + Default is LCMS_ERROR_ABORT. That is, if an error is detected, lcms + will show a MessageBox with a small explanation about the error and + then WILL ABORT WHOLE APPLICATION. This behaviour is desirable when + debugging, but not in final releases. For inhibit such aborting, + you can use LCMS_ERROR_SHOW. This setting will show the error text, but + doesn't finish the application. Some functions like cmsOpenProfileFromFile() + or cmsCreateTransform() will return NULL instead of a valid handle as + error-marker. Others will return FALSE. + The last setting is LCMS_ERROR_IGNORE, that is, no message is displayed + and only a NULL or FALSE is returned if operation fails. + + Note that if you use LCMS_ERROR_SHOW or LCMS_ERROR_IGNORE, your code + must check the return code. This is not necessary if you are using + LCMS_ERROR_ABORT, since the application will be terminated as soon as + the error is detected. + + + If you doesn't like this scheme, you can provide your own error handling, + function by using: + + void cmsSetErrorHandler(cmsErrorHandlerFunction Fn); + + You need to write your own error handling function, in the form: + + int MyErrorHandlerFunction(int ErrorCode, const char *ErrorText) + { + ... do whatsever you want with error codes .. + + } + + And then register the error handler by using: + + cmsSetErrorHandler(MyErrorHandlerFunction); + + + + ErrorCode can be one of the following values: + + LCMS_ERRC_WARNING 0x1000 + LCMS_ERRC_RECOVERABLE 0x2000 + LCMS_ERRC_ABORTED 0x3000 + + ErrorText is a text holding an english description of error. + + You should return 1 if you are handling the error. Returning 0, + throws the error back to the default error handler. + + WARNING: lcms is *not* supposed to recover from all errors. If you are + using your own error handling, please note that you have to abort + all process if you recive LCMS_ERRC_ABORTED in ErrorCode parameter. + Incoming versions will handle this issue more properly. + +11. Getting information from profiles. +============================================================================ + + There are some functions for retrieve information on opened profiles. + These are: + + LCMSBOOL cmsIsTag(cmsHPROFILE hProfile, icTagSignature sig); + + This one does check if a particular tag is present. Remaining does take + useful information about general parameters. + + LCMSBOOL cmsTakeMediaWhitePoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile); + LCMSBOOL cmsTakeMediaBlackPoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile); + LCMSBOOL cmsTakeIluminant(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile); + LCMSBOOL cmsTakeColorants(LPcmsCIEXYZTRIPLE Dest, cmsHPROFILE hProfile); + + const char* cmsTakeProductName(cmsHPROFILE hProfile); + const char* cmsTakeProductDesc(cmsHPROFILE hProfile); + + int cmsTakeRenderingIntent(cmsHPROFILE hProfile); + + icColorSpaceSignature cmsGetPCS(cmsHPROFILE hProfile); + icColorSpaceSignature cmsGetColorSpace(cmsHPROFILE hProfile); + icProfileClassSignature cmsGetDeviceClass(cmsHPROFILE hProfile); + + + These functions are given mainly for building user interfaces, + you don't need to use them if you just want a plain translation. + Other usage would be to identify "families" of profiles. + The functions returning strings are using an static buffer that is + overwritten in each call, others does accept a pointer to an specific + struct that is filled if function is successful. + + + #define LCMS_USED_AS_INPUT 0 + #define LCMS_USED_AS_OUTPUT 1 + #define LCMS_USED_AS_PROOF 2 + + LCMSBOOL cmsIsIntentSupported(cmsHPROFILE hProfile, int Intent, int UsedDirection); + + This one helps on inquiring if a determinate intent is + supported by an opened profile. You must give a handle to + profile, the intent and a third parameter specifying how the + profile would be used. The function does return TRUE if intent + is supported or FALSE if not. If the intent is not supported, + lcms will use default intent (usually perceptual). + + + ADVANCED TOPICS + =============== + + The following section does describe additional functions for advanced + use of lcms. Several of these are expanding the capabilities of lcms + above a 'pure' CMM. In this way, they do not belong to the CMM layer, + but as a low level primitives for the CMS formed by the CMM and the + profilers. There is no need of these function on "normal" usage. + + +12. Creating and writting new profiles. +============================================================================ + + Since version 1.09, lcms has the capability of writting profiles. The + interface is very simple, despite its use does imply certain knowleage + of profile internals. + + These are the functions needed to create a new profile: + + void cmsSetDeviceClass(cmsHPROFILE hProfile, icProfileClassSignature sig); + void cmsSetColorSpace(cmsHPROFILE hProfile, icColorSpaceSignature sig); + void cmsSetPCS(cmsHPROFILE hProfile, icColorSpaceSignature pcs); + void cmsSetRenderingIntent(cmsHPROFILE hProfile, int RenderingIntent); + + LCMSBOOL cmsAddTag(cmsHPROFILE hProfile, icTagSignature sig, void* data); + + And the generic file management ones: + + cmsOpenProfileFromFile() and cmsCloseProfile() + + Device class, colorspace and PCS type does affect to header and overall + profile. cmsSetRenderingIntent() sets the (informative) intent field on + header. Rest of information is included using cmsAddTag(). + + Of course you must know which tags to include and the meaning of each tag. + LittleCms does nothing to check the validity of newly created profiles. + These functions are only intended to be a low level interface to profile + creation. + + Creating a new profile + ----------------------- + + When you open a profile with 'w' as access mode, you got a simpler + Lab identity. That is, a profile marked as Lab colorspace that passes + input untouched to output. You need to fully qualify your profile by + setting its colorspace, device class, PCS and then add the required + tags. cmsAddTag() does understand following tags: + + + + Signature Expected data type + ========================== ================== + + icSigCharTargetTag const char* + icSigCopyrightTag const char* + icSigProfileDescriptionTag const char* + icSigDeviceMfgDescTag const char* + icSigDeviceModelDescTag const char* + icSigRedColorantTag LPcmsCIEXYZ + icSigGreenColorantTag LPcmsCIEXYZ + icSigBlueColorantTag LPcmsCIEXYZ + icSigMediaWhitePointTag LPcmsCIEXYZ + icSigMediaBlackPointTag LPcmsCIEXYZ + icSigRedTRCTag LPGAMMATABLE + icSigGreenTRCTag LPGAMMATABLE + icSigBlueTRCTag LPGAMMATABLE + icSigGrayTRCTag LPGAMMATABLE + icSigAToB0Tag LPLUT + icSigAToB1Tag LPLUT + icSigAToB2Tag LPLUT + icSigBToA0Tag LPLUT + icSigBToA1Tag LPLUT + icSigBToA2Tag LPLUT + icSigGamutTag LPLUT + icSigPreview0Tag LPLUT + icSigPreview1Tag LPLUT + icSigPreview2Tag LPLUT + icSigChromaticityTag LPcmsCIExyYTRIPLE + icSigNamedColor2Tag LPcmsNAMEDCOLORLIST + icSigColorantTableTag LPcmsNAMEDCOLORLIST + icSigColorantTableOutTag LPcmsNAMEDCOLORLIST + icSigCalibrationDateTimeTag const struct tm* + + More tags are expected to be added in future revisions. + + + Another way to create profiles is by using _cmsSaveProfile() or + _cmsSaveProfileToMem(). See the API reference for details. + + + +13. LUT handling +============================================================================ + + LUT stands for L)ook U)p T)ables. This is a generalizad way to handle + workflows consisting of a quite complex stages. Here is the pipeline + scheme, Don't panic! + + + [Mat] -> [L1] -> [Mat3] -> [Ofs3] -> [L3] -> + [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2] + + + Mat{n} are matrices of 3x3 + Ofs{n} are offsets + CLUT is a multidimensional LUT table + + + Some or all of these stages may be missing. LUTS can handle up to + 8 channels on input and 16 channels of output. + + The programmer can allocate a LUT by calling: + + NewLUT = cmsAllocLUT(); + + This allocates an empty LUT. Input is passed transparently to output + by default. The programmer can optionally add pre/post linearization + tables by using: + + cmsAllocLinearTable(LPLUT NewLUT, LPGAMMATABLE Tables[], int nTable); + + Being Table: + + 1 - Prelinearization 1D table L1 + 2 - Postlinearization 1D table L2 + 3 - linearization 1D table L3 + 4 - linearization 1D table L4 + + + The 3x3 matrices can be set by: + + + LPLUT cmsSetMatrixLUT4(LPLUT Lut, LPMAT3 M, LPVEC3 off, DWORD dwFlags); + + Flags define the matrix to set: + + LUT_HASMATRIX: Mat + LUT_HASMATRIX3: Mat3 + LUT_HASMATRIX4: Mat4 + + The CLUT is a multidimensional table where most of the magic of + colormanagement is done. It holds as many (hyper)cubes as ouput + channels and the dimension of these hypercubes is the number of + input channels. + + To fill the CLUT with sampled values, the programmer can call: + + LCMSBOOL cmsSample3DGrid(LPLUT Lut, + _cmsSAMPLER Sampler, + LPVOID Cargo, + DWORD dwFlags) + + + This function builds the CLUT table by calling repeatly a + callback function: + + + typedef int (* _cmsSAMPLER)(register WORD In[], + register WORD Out[], + register LPVOID Cargo); + + + The programmer has to write his callback function. This function + should calculate Out[] values given a In[] set. For example, if we + want a LUT to invert (negate) channels, a sampler could be: + + int InvertSampler(register WORD In[], + register WORD Out[], + register LPVOID Cargo) + { + for (i=0; i < 3; i++) + Out[i] = ~ In[i]; + + return TRUE; + } + + + cmsSample3DGrid does call this function to build the CLUT. + Pre/post linearization tables may be taken into account across + flags parameter + + Flags Meaning + ================ ======================================= + LUT_HASTL1 Do reverse linear interpolation on + prelinearization table before calling + the callback. + + LUT_HASTL2 Do reverse linear interpolation on + postlinearization table after calling + the callback. + + LUT_INSPECT Does NOT write any data to the 3D CLUT, + instead, retrieve the coordinates for inspection + only. If using such flag, Out[] will hold + the CLUT contents. + In[] -> The CLUT indexes + Out[] -> The CLUT contents + + + HASTL1 and HASTL2 Flags are intended to be used as an aid for + building non-uniformly spaced CLUTs. Using these flags results in + "undoing" any linearization that tables could apply. In such way, + the callback is expected to be called with In[] always the original + colorspace, and must return Out[] values always in original + (non-postlinearized) space as well. The linearization cooking is done + automatically. + + The callback must return TRUE if all is ok, or zero + to indicate error. If error condition is raised, whole CLUT + construction is aborted. + + Once builded, programmer can evaluate the LUT by using: + + void cmsEvalLUT(LPLUT Lut, WORD In[], WORD Out[]); + + That does interpolate values according on pipeline tables. + + Finally, a LUT can be freed by + + void cmsFreeLUT(LPLUT Lut); + + Or retrieved from a profile using: + + LPLUT cmsReadICCLut(cmsHPROFILE hProfile, icTagSignature sig); + + To include a LUT in a profile, use cmsAddTag() with tag signature and + a pointer to LUT structure as parameters. See cmsAddTag() in the API + reference for more info. + + + + 14. Helper functions +============================================================================ + + + Here are some functions that could be useful. They are not needed + in "normal" usage. + + Colorimetric space conversions: + + void cmsXYZ2xyY(LPcmsCIExyY Dest, const LPcmsCIEXYZ Source); + void cmsxyY2XYZ(LPcmsCIEXYZ Dest, const LPcmsCIExyY Source); + void cmsXYZ2Lab(LPcmsCIEXYZ WhitePoint, LPcmsCIELab Lab, const LPcmsCIEXYZ xyz); + void cmsLab2XYZ(LPcmsCIEXYZ WhitePoint, LPcmsCIEXYZ xyz, const LPcmsCIELab Lab); + void cmsLab2LCh(LPcmsCIELCh LCh, const LPcmsCIELab Lab); + void cmsLCh2Lab(LPcmsCIELab Lab, const LPcmsCIELCh LCh); + + Notation conversion: (converts from PT_* colorspaces to ICC notation) + + icColorSpaceSignature _cmsICCcolorSpace(int OurNotation); + + Channels of a given colorspace: + + int _cmsChannelsOf(icColorSpaceSignature ColorSpace); + + + Chromatic Adaptation: + + LCMSBOOL cmsAdaptToIlluminant(LPcmsCIEXYZ Result, LPcmsCIEXYZ SourceWhitePt, + LPcmsCIEXYZ Illuminant, LPcmsCIEXYZ Value); + + Build a balanced transfer matrix with chromatic adaptation, this + is equivalent to "cooking" required to conform a colorant matrix. + + LCMSBOOL cmsBuildRGB2XYZtransferMatrix(LPMAT3 r, + LPcmsCIExyY WhitePoint, + LPcmsCIExyYTRIPLE Primaries); + + + + 15. Color difference functions +============================================================================ + + These functions does compute the difference between two Lab colors, + using several difference spaces + + +double cmsDeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2); +double cmsCIE94DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2); +double cmsBFDdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2); +double cmsCMCdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2); +double cmsCIE2000DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2, double Kl, double Kc, double Kh); + + + + 16. PostScript generation + ============================================================================ + 3 functions carry the task of obtaining CRD and CSA. + + + DWORD cmsGetPostScriptCSA(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen); + DWORD cmsGetPostScriptCRD(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen); + DWORD cmsGetPostScriptCRDEx(cmsHPROFILE hProfile, int Intent, DWORD dwFlags, LPVOID Buffer, DWORD dwBufferLen); + + + cmsGetPostScriptCRDEx allows black point compensation using + cmsFLAGS_WHITEBLACKCOMPENSATION in flags field. + + PostScrip colorflow is often done in a different way. Insted of creating a + transform, it is sometimes desirable to delegate the color management to + PostScript interpreter. These functions does translate input and output + profiles into Color Space Arrays (CSA) and Color Rendering Dictionaries (CRD) + + · CRD are equivalent to output (printer) profiles. Can be + loaded into printer at startup and can be stored as resources. + + · CSA are equivalent to input and workspace profiles, and are + intended to be included in the document definition. + + These functions does generate the PostScript equivalents. Since the lenght of + the resultant PostScript code is unknown in advance, you can call the + functions with len=0 and Buffer=NULL to get the lenght. After that, you need + to allocate enough memory to contain the whole block + + Example: + + Size = cmsGetPostScriptCSA(hProfile, INTENT_PERCEPTUAL, NULL, 0); + If (Size == 0) error() + + Block = malloc(Size); + cmsGetPostScriptCSA(hProfile, INTENT_PERCEPTUAL, Block, Size); + + + Devicelink profiles are supported, as long as input colorspace matches + Lab/XYZ for CSA or output colorspace matches Lab/XYZ for CRD. This can + be used in conjuntion with cmsCreateMultiprofileTransform(), + and cmsTransform2DeviceLink() to embed complex color flow into PostScript. + + + + WARNING: Preccision of PostScript is limited to 8 bits per sample. If you + can choose between normal transforms and CSA/CRD, normal transforms will + give more accurancy. However, there are situations where there is no + chance. + + + + 17. CIECAM02 + ============================================================================ + + The model input data are the adapting field luminance in cd/m2 + (normally taken to be 20% of the luminance of white in the adapting field), + La , the relative tristimulus values of the stimulus, XYZ, the relative + tristimulus values of white in the same viewing conditions, "whitePoint", + and the relative luminance of the background, Yb . Relative tristimulus + values should be expressed on a scale from Y = 0 for a perfect black + to Y = 100 for a perfect reflecting diffuser. + + All CIE tristimulus values are obtained using the CIE 1931 Standard + Colorimetric Observer (2°). + + + typedef struct { + + cmsCIEXYZ whitePoint; // The media white in XYZ + + double Yb; + double La; + int surround; + double D_value; + + } cmsViewingConditions, FAR* LPcmsViewingConditions; + + + Surround can be one of these + + + #define AVG_SURROUND 1 + #define DIM_SURROUND 2 + #define DARK_SURROUND 3 + + + + D_value (adaptation degree) is any value between 0 and 1 + + + The functions for dealing with CAM02 appearance model are: + + + LCMSHANDLE cmsCIECAM02Init(LPcmsViewingConditions pVC); + void cmsCIECAM02Done(LCMSHANDLE hModel); + void cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut); + void cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut); + + + For example, to convert XYZ values from a given viewing condition to another: + + a) Create descriptions of both viewing conditions by using cmsCIECAM02Init + b) Convert XYZ to JCh using cmsCIECAM02Forward for viewing condition 1 + c) Convert JCh back to XYZ using cmsCIECAM02Reverse for viewing condition 2 + d) when done, free both descriptions + + + cmsViewingConditions vc1, vc2; + cmsJCh Out; + cmsCIEXYZ In; + HANDLE h1, h2; + + + vc.whitePoint.X = 98.88; + vc.whitePoint.Y = 90.00; + vc.whitePoint.Z = 32.03; + vc.Yb = 18; + vc.La = 200; + vc.surround = AVG_SURROUND; + vc.D_value = 1.0; + + h1 = cmsCIECAM02Init(&vc); + + + vc2.whitePoint.X = 98.88; + vc2.whitePoint.Y = 100.00; + vc2.whitePoint.Z = 32.03; + vc2.Yb = 20; + vc2.La = 20; + vc2.surround = AVG_SURROUND; + vc2.D_value = 1.0; + + h2 = cmsCIECAM02Init(&vc); + + In.X= 19.31; + In.Y= 23.93; + In.Z =10.14; + + cmsCIECAM02Forward(h1, &In, &Out); + cmsCIECAM02Reverse(h2, &Out, &In); + + cmsCIECAM02Done(h1); + cmsCIECAM02Done(h2); + + See the CIECAM02 paper on CIE site for further details. + + + 18. Named color profiles + ============================================================================ + + Named color profiles are a special kind of profiles handling lists of spot + colors. The typical example is PANTONE. CMM deals with named color profiles like + all other types, except they must be in input stage and the encoding supported + is limited to a one single channel of 16-bit indexes. + + Let's assume we have a Named color profile holding only 4 colors: + + · CYAN + · MAGENTA + · YELLOW + · BLACK + + We create a transform using: + + + hTransform = cmsCreateTransform(hNamedColorProfile, + TYPE_NAMED_COLOR_INDEX, + hOutputProfile, + TYPE_BGR_8, + INTENT_PERCEPTUAL, 0); + + "TYPE_NAMED_COLOR_INDEX" is a special encoding for these profiles, it + represents a single channel holding the spot color index. In our case + value 0 will be "CYAN", value 1 "MAGENTA" and so one. + + For converting between string and index there is an auxiliary function: + + int cmsNamedColorIndex(cmsHTRANSFORM hTransform, const char* ColorName); + + That will perform a look up on the spot colors database and return the color + number or -1 if the color was not found. Other additional functions for named + color transforms are: + + + int cmsNamedColorCount(cmsHTRANSFORM hTransform); + + That returns the number of colors present on transform database. + + + LCMSBOOL cmsNamedColorInfo(cmsHTRANSFORM xform, int nColor, + char* Name, char* Prefix, char* Suffix); + + That returns extended information about a given color. Named color profiles + can also be grouped by using multiprofile transforms. In such case, the database + will be formed by the union of all colors in all named color profiles present in + transform. + + + Named color profiles does hold two coordinates for each color, let's + take our PANTONE example. This profile would contain for each color + the CMYK colorants plus its PCS coordinates, usually in Lab space. + + lcms can work with named color using both coordinates. Creating a + transform with two profiles, if the input one is a named color, then you + obtain the translated color using PCS. + + Example, named color -> sRGB will give the color patches in sRGB + + In the other hand, setting second profile to NULL, returns the device + coordinates, that is, CMYK colorants in our PANTONE sample. + + Example: Named color -> NULL will give the CMYK amount for each spot color. + + + The transform must use TYPE_NAMED_COLOR_INDEX on input. That is, a single + channel containing the 0-based color index. + + Then you have: + + cmsNamedColorIndex(cmsHTRANSFORM xform, const char* Name) + + for obtaining index from color name, and + + cmsNamedColorInfo(), cmsNamedColorCount() to retrieve the list. + + The profile is supposed to be for a unique device. Then the CMYK + values does represent the amount of inks THIS PRINTER needs to render + the spot color. The profile also has the Lab values corresponding to + the color. This really would have no sense if gamut of printer were + infinite, but since printers does have a limited gamut a PANTONE-certified + printer renders colors near gamut boundaries with some limitations. + The named color profile is somehow explaining which are these limitation + for that printer. + + So, you can use a named color profile in two different ways, as output, + giving the index and getting the CMYK values or as input and getting the + Lab for that color. + + A transform named color -> NULL will give the CMYK values for the spot + color on the printer the profile is describing. This would be the normal usage. + + A transform Named color -> another printer will give on the output printer + the spot colors as if they were printed in the printer named color profile + is describing. This is useful for soft proofing. + + As an additional feature, lcms can "group" several named color profiles + into a single database by means of cmsCreateMultiprofileTransform(). + Such case works as described above, but joining all named colors as they + were in a single profile. + + + + 19. Conclusion. + ============================================================================ + + That's almost all you must know to begin experimenting with profiles, + just a couple of words about the possibilities ICC profiles can + give to programmers: + + o ColorSpace profiles are valuable tools for converting + from/to exotic file formats. I'm using lcms to read + Lab TIFF using the popular Sam Leffler's TIFFLib. Also, + the ability to restore separations are much better that + the infamous 1-CMY method. + + o Abstract profiles can be used to manipulate color of + images, contrast, brightness and true-gray reductions can + be done fast and accurately. Grayscale conversions can be + done exceptionally well, and even in tweaked colorspaces + that does emulate more gray levels that the output device + can effectively render. + + o lcms does all calculation on 16 bit per component basis, + the display and output profiles can take advantage of these + precision and efficiently emulate more than 8 bits per sample. + You probably will not notice this effect on screen, but it can + be seen on printed or film media. + + + o There is a huge quantity of profiles moving around the net, + and there is very good software for generating them, so + future compatibility seems to be assured. + + + + I thank you for your time and consideration. + + Enjoy! + + + +Sample 1: How to convert RGB to CMYK and back +============================================= + +This is easy. Just use a transform between RGB profile to CMYK profile. + + +#include "lcms.h" + + +int main(void) +{ + + cmsHPROFILE hInProfile, hOutProfile; + cmsHTRANSFORM hTransform; + int i; + + + hInProfile = cmsOpenProfileFromFile("sRGBColorSpace.ICM", "r"); + hOutProfile = cmsOpenProfileFromFile("MyCmyk.ICM", "r"); + + + + hTransform = cmsCreateTransform(hInProfile, + TYPE_RGB_8, + hOutProfile, + TYPE_CMYK_8, + INTENT_PERCEPTUAL, 0); + + + for (i=0; i < AllScanlinesTilesOrWatseverBlocksYouUse; i++) + { + cmsDoTransform(hTransform, YourInputBuffer, + YourOutputBuffer, + YourBuffersSizeInPixels); + } + + + + cmsDeleteTransform(hTransform); + cmsCloseProfile(hInProfile); + cmsCloseProfile(hOutProfile); + + return 0; +} + + +And Back....? Same. Just exchange profiles and format descriptors: + + + +int main(void) +{ + + cmsHPROFILE hInProfile, hOutProfile; + cmsHTRANSFORM hTransform; + int i; + + + hInProfile = cmsOpenProfileFromFile("MyCmyk.ICM", "r"); + hOutProfile = cmsOpenProfileFromFile("sRGBColorSpace.ICM", "r"); + + + + hTransform = cmsCreateTransform(hInProfile, + TYPE_CMYK_8, + hOutProfile, + TYPE_RGB_8, + INTENT_PERCEPTUAL, 0); + + + for (i=0; i < AllScanlinesTilesOrWatseverBlocksYouUse; i++) + { + cmsDoTransform(hTransform, YourInputBuffer, + YourOutputBuffer, + YourBuffersSizeInPixels); + } + + + + cmsDeleteTransform(hTransform); + cmsCloseProfile(hInProfile); + cmsCloseProfile(hOutProfile); + + return 0; +} + + +Sample 2: How to deal with Lab/XYZ spaces +========================================== + +This is more elaborated. There is a Lab identity Built-In profile involved. + + +// Converts Lab(D50) to sRGB: + +int main(void) +{ + + cmsHPROFILE hInProfile, hOutProfile; + cmsHTRANSFORM hTransform; + int i; + BYTE RGB[3]; + cmsCIELab Lab[..]; + + + hInProfile = cmsCreateLabProfile(NULL); + hOutProfile = cmsOpenProfileFromFile("sRGBColorSpace.ICM", "r"); + + + + hTransform = cmsCreateTransform(hInProfile, + TYPE_Lab_DBL, + hOutProfile, + TYPE_RGB_8, + INTENT_PERCEPTUAL, 0); + + + for (i=0; i < AllLabValuesToConvert; i++) + { + // Fill in the Float Lab + + Lab[i].L = Your L; + Lab[i].a = Your a; + Lab[i].b = Your b; + + cmsDoTransform(hTransform, Lab, RGB, 1); + + .. Do whatsever with the RGB values in RGB[3] + } + + + + cmsDeleteTransform(hTransform); + cmsCloseProfile(hInProfile); + cmsCloseProfile(hOutProfile); + + return 0; +} + + + + +Annex A. About intents +============================================================================ + + Charles Cowens gives to me a clear explanation about + accomplished intents. Since it is very useful to understand how + intents are internally implemented, I will reproduce here. + + + AtoBX/BtoAX LUTs and Rendering Intents + + The ICC spec is pretty detailed about the LUTs and their + varying meaning according to context in tables 20, 21, and 22 + in section 6.3. My reading of this is that even though there + are 4 rendering intent selectors there are really 6 rendering + styles: + + Relative Indefinite + (Relative) Perceptual + Relative Colorimetric + (Relative) Saturation + Absolute Indefinite + Absolute Colorimetric + + If a device profile has a single-LUT or matrix: + + * Perceptual, Relative Colorimetric, Saturation selectors + produce the same Relative Indefinite rendering style + + * Absolute Colorimetric selector produces an Absolute + Indefinite rendering style derived from the single LUT or + matrix, the media white point tag, and the inverse of a + white point compensation method designated by the CMS + + If a device profile has 3 LUTs: + + * Perceptual, Relative Colorimetric, Saturation selectors + produce the appropriate rendering styles using the 0, 1, and + 2 LUTs respectively + + * Absolute Colorimetric selector produces an Absolute + Colorimetric rendering style derived from the Relative + Colorimetric LUT (numbered "1"), the media white point tag, + and the inverse of a white point compensation method + designated by the CMS + + + This would explain why perceptual is the default rendering style + because a single-LUT profile's LUT is numbered "0". + + +Annex B Apparent bug in XYZ -> sRGB transforms +============================================================================ + +John van den Heuvel warns me about an apparent bug on +XYZ -> sRGB transforms. Ver 1.10 should minimize this effect. + +The obtained results are visually OK, but numbers seems to be wrong. +It appears only when following conditions: + + a) You are using a transform from a colorspace with + a gamut a lot bigger that output space, i.e. XYZ. + Note than sRGB -> XYZ does work OK. + + b) You are using absolute colorimetric intent. + + c) You transform a color near gamut hull boundary + + d) The output profile is implemented as a matrix-shaper, + i.e. sRGB. + + e) You are using precalculated device link tables. + +The numbers lcms returns doesn't match closely that color, but other +perceptually close to the intended one. + +It happens that since XYZ has a very big gamut, and sRGB a narrow +one on compared to XYZ, when lcms tries to compute the device link +between XYZ -> sRGB, got most values as negative RGB (out of gamut). +lcms assumes this is effectively out of gamut and clamps to 0. +Then, since (127, 0, 0) is just over gamut boundary (for example +(127, -1, -1) would be out of gamut), lcms does interpolate +wrongly, not between -n to n but between 0 to n. + +I could put an If() in the code for dealing with such situation, +but I guess it is better not touch anything and document this +behaviour. + +XYZ is almost never used as a storage space, and since most monitor +profiles are implemented as matrix shaper touching this would slow +down common operations. The solution is quite simple, +if you want to deal with numbers, then use cmsFLAGS_NOTPRECALC. +If you deal with images, let lcms optimize the transform. +Visual results should appear OK, no matter numbers doesn't match. + + |
