Tuesday, June 30, 2009

Regression tests

Right now it is pretty clear lcms2 would need an exhaustive test bed if we want some sort of robustness in the code. I'm spending a lot of time with this program. It began as a small one and now it is a huge file of 5600 lines. Maybe I should split it in several modules...

My intent is to have a test on every single feature. Since this is close to impossible, the actual test is focused on usual cases. Now the question, what are usual cases? Sure, sRGB to screen and aRGB to printer output are pretty common, but there are people over there using lcms1 to do multi-ink separations on 12 channels. Why I should not check this case as well? Another big area that deserves careful testing are plug-ins. How should the code base react to a wrong plugin? is a segfault admissible in this particular case?

Monday, June 29, 2009

When/where to clip?

An interesting effect I've found, you can reproduce it with Photoshop CS4 as well. Set the working space as sRGB, intent relative colorimetric, no BPC. Then using color picker enter this Lab value:

0, -120, 0

As you can see, the obtained RGB values are 0, 0, 0. So far so good. Or not? Try this other Lab value:

0, 0, -120

Oops! now we got 0, 27, 182! What's happening? The answer, as far as I can tell, is clipping. If you take the work (I did) of implementing Lab -> XYZ and then XYZ -> sRGB by using entirely floating point formulas, the second value of 0, 0, -120 getting converted to some color with lots of L* is just a consequence of the math. From where it comes all this L*?

lcms does clip negative XYZ numbers, so in both you will obtain 0, 0, 0. What makes me wonder is why photoshop does clip one axis (a) and does not clip the other (b) in the same fashion?

Maybe is just clipping XYZ negative numbers? Ok. lcms2 gives 0, 48, 0 for a Lab of 0, 0, -120. This is the result of not clipping anything at middle stages and letting XYZ negative numbers as well. Clipping happens only on the last stage or when is absolutely required (indexing LUTs, for example). I guess more experimentation is needed in this part...

Saturday, June 27, 2009

Threads and cmsThreadID

One of the things lcms 2 is different to lcms is multithreading support. That is, indeed, a difficult feature. One may argue that just avoiding global variables should be enough to support multiple threads, but I think this is not necessarily true. Sometimes some global settings have to apply to all threads. On the other hand, it would be desirable for error handling and memory allocation to have some clues on which are the thread currently active or the environment where the current operation takes place.

So the solution I've found is the cmsThreadID type. That is just a void pointer.

Most of the high-level functions in lcms2 does have two forms. The first one is defaulting the ThreadID parameter to zero, which means "user don't care about threads". The second form allows to specify the ThreadID. This ID will be passed to memory allocation, error handler and plug-ins, so the user code may be smart enough to react differently on different threads. One example is to use different memory pools, which is useful, among others, when a given thread crashes and you want to recover gracefully.

Related functions :

cmsThreadID cmsGetProfileThreadID()
cmsThreadID cmsGetTransformThreadID()

And all functions with THR ending the function name, for example:

cmsHPROFILE cmsCreateRGBProfileTHR();

Hello, World!

Howdy and welcome. This is an informal blog dealing with color management stuff. More precisely, this blog will discuss all the gory details I am facing when writting the version two of littlecms. I think people over here would already know what am I talking about, but if it not your case, see here an abstract.
So what is the blog about? Mainly software architecture. I would like to discuss the reasons I'm designing the API in such way or why that specific limitation. To what extent this would be a good idea, time will tell... stay tuned!