I'm looking to replicate the CSS3 hue rotation behaviour found here
original image
image with hue rotated 180deg
I can already accurately convert an RGB value to a HSL value and back again but I'm not sure what the mathematical function to apply to the hue component to replicate the output is.
I wanted to point out how to actually do this. This was the exact question, but no good answer.
Given a GDI+ Image
, i want to apply a hue shift and return a new image. In practice it will return a new Bitmap
. I'll use C# style pseudo-code.
First is the basic guts to clone a GDI+ image (but without the hue shift yet):
Bitmap CloneImage(Image sourceImage, Single hueShiftAngleDegrees) { Int32 width = sourceImage.GetWidth(); Int32 height = sourceImage.Getheight(); //Create destination bitmap Bitmap bmpDest = new Bitmap(width, height, PixelFormat32bppARGB); //Create a Graphics that will draw onto our destination bitmap Graphics g = new Graphics(destinationBitmap); //Draw the source image into the destination g.DrawImage(sourceImage, MakeRect(0, 0, width, height), 0, 0, width, height, UnitPixel); return bmpDest; }
Next is the idea that when we use the Graphics.DrawImage method, we can supply an ImageAttributes
class.
ImageAttributes attributes = new ImageAttributes(); g.DrawImage(sourceImage, MakeRect(0, 0, width, height), 0, 0, width, height, UnitPixel, attributes);
One of these attributes can be a 5x5 ColorMatrix
:
ColorMatrix cm = ( ( rr, gr, br, ar, 0 ), ( rg, gg, bg, ag, 0 ), ( rb, gb, bb, ab, 0 ), ( ra, ga, ba, aa, 0 ), ( r1, g1, b1, a1, 1 ) ); ImageAttributes attributes = new ImageAttributes(); attributes.SetColorMatrix(cm); g.DrawImage(sourceImage, MakeRect(0, 0, width, height), 0, 0, width, height, UnitPixel, attributes);
The magic comes from the color matrix that can perform a hue shift. I create a function that can return the ColorMatrix that performs the desired hue shift:
ColorMatrix GetHueShiftColorMax(Single hueShiftDegrees) { /* Return the matrix A00 A01 A02 0 0 A10 A11 A12 0 0 A20 A21 A22 0 0 0 0 0 1 0 0 0 0 0 1 */ Single theta = hueShiftDegrees/360 * 2*pi; //Degrees --> Radians Single c = cos(theta); Single s = sin(theta); Single A00 = 0.213 + 0.787*c - 0.213*s; Single A01 = 0.213 - 0.213*c + 0.413*s; Single A02 = 0.213 - 0.213*c - 0.787*s; Single A10 = 0.715 - 0.715*c - 0.715*s; Single A11 = 0.715 + 0.285*c + 0.140*s; Single A12 = 0.715 - 0.715*c + 0.715*s; Single A20 = 0.072 - 0.072*c + 0.928*s; Single A21 = 0.072 - 0.072*c - 0.283*s; Single A22 = 0.072 + 0.928*c + 0.072*s; ColorMatrix cm = new ColorMatrix( ( A00, A01, A02, 0, 0 ), ( A10, A11, A12, 0, 0 ), ( A20, A21, A22, 0, 0 ), ( 0, 0, 0, 0, 0 ), ( 0, 0, 0, 0, 1 ) ) return cm; }
So i'll create a new kind of function, one that makes a copy of an image and applies a ColorMatrix
to it:
Bitmap Multiply(Image sourceImage, ColorMatrix cm) { Int32 width = sourceImage.GetWidth(); Int32 height = sourceImage.Getheight(); //Create destination bitmap Bitmap bmpDest = new Bitmap(width, height, PixelFormat32bppARGB); //Create a Graphics that will draw onto our destination bitmap Graphics g = new Graphics(destinationBitmap); //Draw the source image into the destination ImageAttributes attributes = new ImageAttributes(); attributes.SetColorMatrix(cm); g.DrawImage(sourceImage, MakeRect(0, 0, width, height), 0, 0, width, height, UnitPixel, attributes); return bmpDest; }
And our hue shift algorithm becomes:
Bitmap ApplyHueShift(Image sourceImage, Single hueShiftAngleDegrees) { ColorMatrix cm = GetHueShiftColorMatrix(hueShiftAngleDegrees); return Multiply(cm); }
I have no idea where the hue shift color matrix comes from. It just exists on MSDN page Hue rotation effect:
