paulb1989
asked on
Fading Between Colors
I have a program in which I need to fade between two colors (to create a mouse over effect). Drawing is performed by a ColorBlend object and a LinearGradientBrush with its InterpolationColors property. The control I am writing should fade from one set of colors (in an array) to another, with a float parameter from 0.0f (use the first color) to 1.0f (use the second color). I have written code to do this by using a temporary bitmap object and drawing to it, however I have found this method to be quite slow...
I'm looking for a faster method to replace the one below that will take the same parameters and function in the same way to fade between two arrays of colors. Below is the code for my current method:
internal static Color[] GetFadeColors(Color[] Original, Color[] New, float Progress)
{
if (Original.Length != New.Length)
{
throw new Exception("Parameters Incorrect: Original & New Color Arrays Must Have Same Length");
}
Color[] ret = new Color[Original.Length];
Bitmap tmp = new Bitmap(1, Original.Length);
Graphics g = Graphics.FromImage(tmp);
int alpha = (int)(Progress * 255);
for (int i = 0; i < Original.Length; i++)
{
Pen p = new Pen(Original[i]);
g.DrawLine(p, 0, i, 1, i);
p.Dispose();
Color newcolor = Color.FromArgb(alpha, New[i]);
p = new Pen(newcolor);
g.DrawLine(p, 0, i, 1, i);
p.Dispose();
ret[i] = tmp.GetPixel(0, i);
}
g.Dispose();
tmp.Dispose();
return ret;
}
I'm looking for a faster method to replace the one below that will take the same parameters and function in the same way to fade between two arrays of colors. Below is the code for my current method:
internal static Color[] GetFadeColors(Color[] Original, Color[] New, float Progress)
{
if (Original.Length != New.Length)
{
throw new Exception("Parameters Incorrect: Original & New Color Arrays Must Have Same Length");
}
Color[] ret = new Color[Original.Length];
Bitmap tmp = new Bitmap(1, Original.Length);
Graphics g = Graphics.FromImage(tmp);
int alpha = (int)(Progress * 255);
for (int i = 0; i < Original.Length; i++)
{
Pen p = new Pen(Original[i]);
g.DrawLine(p, 0, i, 1, i);
p.Dispose();
Color newcolor = Color.FromArgb(alpha, New[i]);
p = new Pen(newcolor);
g.DrawLine(p, 0, i, 1, i);
p.Dispose();
ret[i] = tmp.GetPixel(0, i);
}
g.Dispose();
tmp.Dispose();
return ret;
}
I was wondering if HLS (Hue-Luminance-Saturation) functions might help. Here is a VB.NET class that we can duplicate in C#, if it applies to your situation:
https://www.experts-exchange.com/questions/21633738/I-have-a-panel-with-a-backcolor-and-two-buttons-one-button-is-to-make-the-panel's-backcolor-brighter-and-the-other-button-is-to-darken-it-How-do-you-change-brightness-or-hue-of-a-color.html
Bob
https://www.experts-exchange.com/questions/21633738/I-have-a-panel-with-a-backcolor-and-two-buttons-one-button-is-to-make-the-panel's-backcolor-brighter-and-the-other-button-is-to-darken-it-How-do-you-change-brightness-or-hue-of-a-color.html
Bob
ASKER
But I'm not just lightening and darkening colors, the two colors can be completely different
It not just lightening and darkening, but affecting color values, but I see your point, which is why I asked before continuing.
There is also color transformations in GDI+, if that might prove to be useful:
GDI+ Graphics Transformation
http://www.awprofessional.com/articles/article.asp?p=102607&seqNum=8&rl=1
It looks like you are drawing a progress bar that has a color transform from one end of the bar to the other.
Bob
There is also color transformations in GDI+, if that might prove to be useful:
GDI+ Graphics Transformation
http://www.awprofessional.com/articles/article.asp?p=102607&seqNum=8&rl=1
It looks like you are drawing a progress bar that has a color transform from one end of the bar to the other.
Bob
ASKER
I'm not drawing a progress bar, I'm writing controls that immitate the office 2007 ribbon style. This code is used to fade in the colors that are used when the mouse is over a control (button, tab, etc) so that rather than just skipping from its normal look to its mouse over look, the control fades the mouse over look in, slowly (well, currently too slowly) changing from the normal colors to the colors used when the mouse is over the object.
As for the GDI+ link, I don't see anything about blending two colors to fade between them as I am doing, just ways to modify one color in a specific way. My code needs to take two colors and combine them to give back a color that is part way between the original color and the new color, when the colors are completely different (eg, typically the colors are moving from shades of blue to orange, or dark grays to orange).
As for the GDI+ link, I don't see anything about blending two colors to fade between them as I am doing, just ways to modify one color in a specific way. My code needs to take two colors and combine them to give back a color that is part way between the original color and the new color, when the colors are completely different (eg, typically the colors are moving from shades of blue to orange, or dark grays to orange).
>>the control fades the mouse over look in, slowly (well, currently too slowly)
Very good points. I wasn't getting the implication of the color blend (it's been a long day).
What is driving the slowly part? Is there a timer or some other kind of similar method?
Bob
Very good points. I wasn't getting the implication of the color blend (it's been a long day).
What is driving the slowly part? Is there a timer or some other kind of similar method?
Bob
ASKER
I have a timer set to fire every 50ms which increments the progress value by 0.1f each time, so in theory it should take 500ms to go from the original colors through to the new colors, but it actually takes around a second (may not sound like much but its noticeably slow).
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
OK I just tried incrementing by 0.2f and it does look a lot better, so unless anyone posts a more efficient method of fading the colors in the next few hours or so (I'm sure there must be a more efficient way than drawing to a bitmap), the points are yours. Otherwise they'll be split.
There are probably faster ways of getting information from the bitmap other than using GetPixel call, like using Scan0 + Stride.
Here is an example of what I mean:
Converting an RGB image to 1 bit-per-pixel monochrome.
http://www.bobpowell.net/onebit.htm
Bitmap img = (Bitmap)Image.FromFile(dlg .FileName) ;
//Ensure that it's a 32 bit per pixel file
if(img.PixelFormat!=PixelF ormat.Form at32bppPAr gb)
{
Bitmap temp=new Bitmap(img.Width,img.Heigh t,PixelFor mat.Format 32bppPArgb );
Graphics g=Graphics.FromImage(temp) ;
g.DrawImage(img, new Rectangle(0,0,img.Width,im g.Height), 0,0,img.Wi dth,img.He ight,Graph icsUnit.Pi xel);
img.Dispose();
g.Dispose();
img=temp;
}
this.pictureBox1.Image=img ;
//lock the bits of the original bitmap
BitmapData bmdo=img.LockBits(new Rectangle(0,0,img.Width,im g.Height), ImageLockM ode.ReadOn ly,img.Pix elFormat);
//and the new 1bpp bitmap
bm=new Bitmap(this.pictureBox1.Im age.Width, this.pictu reBox1.Ima ge.Height, PixelForma t.Format1b ppIndexed) ;
BitmapData bmdn=bm.LockBits(new Rectangle(0,0,bm.Width,bm. Height),Im ageLockMod e.ReadWrit e,PixelFor mat.Format 1bppIndexe d);
//for diagnostics
DateTime dt=DateTime.Now;
//scan through the pixels Y by X
int x,y;
for(y=0;y<img.Height;y++)
{
for(x=0;x<img.Width;x++)
{
//generate the address of the colour pixel
int index=y*bmdo.Stride+(x*4);
//check its brightness
if(Color.FromArgb(Marshal. ReadByte(b mdo.Scan0, index+2),
Marshal.ReadByte(bmdo.Scan 0,index+1) ,
Marshal.ReadByte(bmdo.Scan 0,index)). GetBrightn ess()>0.5f )
this.SetIndexedPixel(x,y,b mdn,true); //set it if its bright.
}
}
//tidy up
bm.UnlockBits(bmdn);
img.UnlockBits(bmdo);
//show the time taken to do the conversion
TimeSpan ts=dt-DateTime.Now;
System.Diagnostics.Trace.W riteLine(" Conversion time was:"+ts.ToString());
//display the 1bpp image.
this.pictureBox2.Image=bm;
Bob
Here is an example of what I mean:
Converting an RGB image to 1 bit-per-pixel monochrome.
http://www.bobpowell.net/onebit.htm
Bitmap img = (Bitmap)Image.FromFile(dlg
//Ensure that it's a 32 bit per pixel file
if(img.PixelFormat!=PixelF
{
Bitmap temp=new Bitmap(img.Width,img.Heigh
Graphics g=Graphics.FromImage(temp)
g.DrawImage(img, new Rectangle(0,0,img.Width,im
img.Dispose();
g.Dispose();
img=temp;
}
this.pictureBox1.Image=img
//lock the bits of the original bitmap
BitmapData bmdo=img.LockBits(new Rectangle(0,0,img.Width,im
//and the new 1bpp bitmap
bm=new Bitmap(this.pictureBox1.Im
BitmapData bmdn=bm.LockBits(new Rectangle(0,0,bm.Width,bm.
//for diagnostics
DateTime dt=DateTime.Now;
//scan through the pixels Y by X
int x,y;
for(y=0;y<img.Height;y++)
{
for(x=0;x<img.Width;x++)
{
//generate the address of the colour pixel
int index=y*bmdo.Stride+(x*4);
//check its brightness
if(Color.FromArgb(Marshal.
Marshal.ReadByte(bmdo.Scan
Marshal.ReadByte(bmdo.Scan
this.SetIndexedPixel(x,y,b
}
}
//tidy up
bm.UnlockBits(bmdn);
img.UnlockBits(bmdo);
//show the time taken to do the conversion
TimeSpan ts=dt-DateTime.Now;
System.Diagnostics.Trace.W
//display the 1bpp image.
this.pictureBox2.Image=bm;
Bob
ASKER