Virtual Developer Workshop: Containerized Development with Docker

Environment: VC++ 7.0, XP


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;


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 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.



Download demo project and source - 63 Kb


  • Matrices

    Posted by didula on 10/18/2007 07:44pm

    Hi, 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.

  • Rotating Hues with this code

    Posted by jimmygyuma on 05/22/2007 03:37am

    Using 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.

    • Re: QColorMatrix

      Posted by jimmygyuma on 07/16/2007 12:48am

      All 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.

    • RE: Rotating Hues with this code

      Posted by Sjaakp on 05/24/2007 03:41pm

      QColorMatrix rotates hues while preserving the luminance. In other words: omly the color information is modified, not the black-and-white levels. If you would remove the color from an image (by setting the saturation to zero), rotating the hue has no effect.
      Because the luminance of the brightest red on the computer screen (RGB 255, 0, 0) is way lower than the luminance of the brightest green (RGB 0, 255, 0), the brightest red does not translate into the brightest green, but into a darker green.
      It all has to do with the 'luminance weights' which are assigned to the R, G, and B elements of the color (see the const's in the top part of QColorMatrix.cpp). If all three luminance weights were equal (1.0), the brightest red would translate to the brightest green after rotation. However, rotating the hue would also mean modifying the luminance.
      See http://www.graficaobscura.com/matrix/ for some more information on hue rotation.
      For what it's worth, the Photoshop hue control works similar to QColormatrix in this respect.

  • Superb ..............................

    Posted by Legacy on 10/21/2003 07:00am

    Originally posted by: Vishal G

    Fantastic code given....................

  • Fantastic !!!

    Posted by Legacy on 09/12/2003 07:00am

    Originally 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?

    • I think you got the wrong typdef

      Posted by AviadOffer on 05/19/2004 12:48am

      typedef unsigned __int64 ULONG_PTR; should be typedef unsigned __int32 ULONG_PTR;

  • The best article i've read so far ....

    Posted by Legacy on 08/23/2003 07:00am

    Originally posted by: Vergil Cola

    This has to be the best GDI+ article i've read so far.
    Thanks. This has been a great help

  • What is the best way to learn GDI+ ???

    Posted by Legacy on 08/20/2003 07:00am

    Originally posted by: SK

    Same as Subject.

  • Excellent job - the first GDI+ sample I have seen

    Posted by Legacy on 08/12/2003 07:00am

    Originally 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!


  • VC6 version, please

    Posted by Legacy on 08/07/2003 07:00am

    Originally 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

  • NIce!

    Posted by Legacy on 08/01/2003 07:00am

    Originally posted by: Andreas

    Good Introduction and Demo!

  • Good

    Posted by Legacy on 07/31/2003 07:00am

    Originally posted by: Jeff Zhang


  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date