Playing with ColorMatrix
Environment: VC++ 7.0, XP
Introduction
GDI+, the new Windows graphics API, comes with a ColorMatrix. It's a pity that ColorMatrix is just a simple struct, without any member functions. And it's also a pity that Microsoft's documentation is very, very sketchy. It's a pity, because you can do some really nice things with color matrices. Some of these things are near to impossible with other means. That's why I wrote an extension class, QColorMatrix, to open up the possibilities of color matrices in GDI+.
I also wrote a mini-application, Tinter, to demonstrate some of the things you can do. See the pictures at the end of this article for the effects you can apply to bitmap pictures. Tinter changes the coloring of JPG, GIF, BMP, and TIFF files interactively, can convert the types into one another, and prints. Pretty impressive for an EXE of just over 70 Kb, I think.
What Is a Color Matrix?
Just as the co-ordinates x, y, and z define a single point in 3D space, the three color components R (red), G (green), and B (blue) define a single point in color space. In 3D geometrics (and in 2D geometrics as well), matrices can be used to transform the position of a point, and, more importantly, the position of a group of points. Change the matrix, apply the matrix to a group of points, and all the points are moved together. If you apply the matrix to all the points in a specific scene, the whole scene is transformed.
A color matrix works the same way in color space. With a color matrix, you can change one specific color, say dark blue into light red, but you can also change all colors together, in a co-ordinated way. Just like a scene in 3D space, you can scale, translate, rotate, and shear the color space, or apply any combination of these basic transformations to it.
For technical reasons, points in 3D space are transformed with 4x4 matrices. A color in RGB color space can also be transformed with a 4x4 matrix. But as GDI+ works with a fourth 'color', A (alpha, or opacity) throughout, ColorMatrix is defined as a 5x5 matrix of REALs:
typedef struct { REAL m[5][5]; } ColorMatrix;
QColorMatrix
Microsoft leaves it to us to fill in the 25 REALs of ColorMatrix, which is no easy task for even a simple rotation. My class, QColorMatrix, adds some useful member functions to put meaningful values in the matrix. You can scale, translate, rotate, and shear the color space in one step. There are also two member functions for more complex, but very useful operations: one to set the saturation of the color space, and one to rotate the hue, while preserving the luminance. Better than trying to describe what this all means, I'll let you have a look at the pictures (yes, that's me!).
Tinter
Tinter is a mini-application, demonstrating what you can do with QColorMatrix. It is even a small tool in its own right, for correcting bitmap pictures—or disforming, if you like. You can change the contrast, brightness, saturation, and hue. You can even change the gamma, although this has nothing to do with color matrices. I just threw it in for free.
Tinter is built in MFC 7.0. Of course, your system must support GDI+, which currently only XP does. However, Windows 98 and later versions can be upgraded.
More Information
There is not much information on color matrices on the Web. I leant heavily on a rather dense, 1993 article by Paul Haeberli, published by Silicon Graphics. Also, the documentation of SGI's Color Matrix Extension for OpenGL may come in useful.
| Contrast | ![]() |
![]() |
![]() |
![]() |
![]() |
| Brightness | ![]() |
![]() |
![]() |
![]() |
![]() |
| Saturation | ![]() |
![]() |
![]() |
![]() |
![]() |
| Hue | ![]() |
![]() |
![]() |
![]() |
![]() |
| Gamma | ![]() |
![]() |
![]() |
![]() |
![]() |






















Comments
Matrices
Posted by didula on 10/18/2007 12:44pmHi, great interesting article. I have a few questions: Could you tell what matrices you are using for the hue rotation? Is it possible to multiply all of them to get one master matrix which can change the hue? It would be great to see more of the math behind this. I didnt find much about it at the links.
ReplyRotating Hues with this code
Posted by jimmygyuma on 05/21/2007 08:37pmUsing this code or any of its progeny, which are legion, to rotate hues in an image will give you bogus results. It seems to be doing what you want it to do, but the results are so inaccurate that it is only useful for effects. I translated this code, as well as a VB version, into C#, but my results were so far off I thought I had done something wrong. But, no. I got out Mr. Priester's demo and ran the same test, with the same results. I had an image consisting of red, green, blue, yellow, magenta, and cyan bands. I rotated it 120 degrees. Red should become green, green should become blue, blue red, and so forth. And they did, sort of. Red became a kind of forest green, green became sky blue, and blue became maroon, and so forth. It amazes me that this code has been propagated far and wide, with everyone singing its praises, when it gives these kind of results. It's not even remotely useful for any halfway serious work.
-
-
Reply
ReplyRe: QColorMatrix
Posted by jimmygyuma on 07/15/2007 05:48pmAll well and good, but it is still inaccurate. You need to preserve both the luminance and the saturation. The only thing you want to change is the hue. I went back to LockBits, UnlockBits, so that I could convert each pixel to HSL, rotate the Hue, a simple addition, and convert the result back to RGB. The saturation and luminance are never changed. Of course you have to have accurate conversion routines. I have a class JColor, which I have been refining and translating since my Java days, to handle this. I use your saturation and brightness routines, which are fine for subjective work, but, I'm sorry, when I rotate pure yellow 120 degrees and get something other than pure cyan, it doesn't work for me.
ReplyRE: Rotating Hues with this code
Posted by Sjaakp on 05/24/2007 08:41amSuperb ..............................
Posted by Legacy on 10/21/2003 12:00amOriginally posted by: Vishal G
Fantastic code given....................
ReplyFantastic !!!
Posted by Legacy on 09/12/2003 12:00amOriginally posted by: Oleg Salafonov
VC6 version really works, but I am still getting
the error: LNK2001: unresolved external symbol _GdiplusShutdown@8. I have to comment the line
"Gdiplus::GdiplusShutdown(m_Token);" in the QGdiPlus.
Can anybody help?
-
ReplyI think you got the wrong typdef
Posted by AviadOffer on 05/18/2004 05:48pmtypedef unsigned __int64 ULONG_PTR; should be typedef unsigned __int32 ULONG_PTR;
ReplyThe best article i've read so far ....
Posted by Legacy on 08/23/2003 12:00amOriginally posted by: Vergil Cola
This has to be the best GDI+ article i've read so far.
ReplyThanks. This has been a great help
What is the best way to learn GDI+ ???
Posted by Legacy on 08/20/2003 12:00amOriginally posted by: SK
Same as Subject.
ReplyExcellent job - the first GDI+ sample I have seen
Posted by Legacy on 08/12/2003 12:00amOriginally posted by: Ajay
That's excellent and works very well without flikering at all. Also the speed the image changes is also not noticeable. And it also allows saving the changed image!
Fine!!
ReplyVC6 version, please
Posted by Legacy on 08/07/2003 12:00amOriginally posted by: kimchi
How can I compile the your sample?
I tried to compile on the VC6 with GDI+ Macro(http://www.codeproject.com/vcpp/gdiplus/vc6gdiplusmacro.asp?target=gdiplus%7Cmacro).
But I met following error:
C:\Program Files\Microsoft SDK\include\GdiplusEnums.h(28) : error C2146: syntax error : missing ';' before identifier 'GraphicsState'
C:\Program Files\Microsoft SDK\include\GdiplusEnums.h(28) : fatal error C1004: unexpected end of file found
Reply
NIce!
Posted by Legacy on 08/01/2003 12:00amOriginally posted by: Andreas
Good Introduction and Demo!
ReplyGood
Posted by Legacy on 07/31/2003 12:00amOriginally posted by: Jeff Zhang
push
Reply