James South
James South

Reputation: 10635

Rotate Hue in C#

I'm looking to replicate the CSS3 hue rotation behaviour found here

original image

original image

image with hue rotated 180deg

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.

Upvotes: 5

Views: 2191

Answers (2)

Ian Boyd
Ian Boyd

Reputation: 256711

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(sourceImage, cm);
}

I have no idea where the hue shift color matrix comes from. It just exists on MSDN page Hue rotation effect archive:

enter image description here

Upvotes: 2

Guffa
Guffa

Reputation: 700322

Addition.

It's that simple, just add 180 to the hue value, then make sure that it wraps around at 360:

hue = (hue + 180) % 360;

Upvotes: 5

Related Questions