StrechImage SizeMode does not work as expected when I assign a code-generated gradient Bitmap to a PictureBox. The StrechImage function seems to add a row (or column) of gray pixels to the Bitmap before the Bitmap is stretched. To see what I'm talking about, do the following:
1. Create a new WindowsForm Project
2. In design mode, add a Button, a TextBox, and a PictureBox (keeping the original names for the controls)
3. Set the SizeMode property of the PictureBox to StretchImage
4. Copy and paste the following code to the Form1 class
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.TextBox1.Text = 1
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim i, j As Integer
Dim c As Drawing.Color
Dim c_Blue_Temp As Integer
Dim Bmp As Drawing.Bitmap
Const Bmp_Wid As Integer = 256 'Make sure that Bmp_Wid is not greater than 256!
Dim Bmp_Ht As Integer = CInt(Me.TextBox1.Text)
'Force the Width of PictureBox1 to be Bmp_Wid pixels wide so that there won't be any stretching in the x-direction
Me.PictureBox1.Width = Bmp_Wid
'Create a Bitmap
Bmp = New Drawing.Bitmap(Bmp_Wid, Bmp_Ht, Drawing.Imaging.PixelFormat.Format32bppArgb)
'Fill the Bitmap with a Gradient
For i = 0 To Bmp_Wid - 1
c_Blue_Temp = i
c = c.FromArgb(255, 0, 0, c_Blue_Temp)
For j = 0 To Bmp.Height - 1
Bmp.SetPixel(i, j, c)
Next j
Next i
'Assign the Bitmap to PictureBox1
Me.PictureBox1.Image = Bmp
End Sub
5. Run the program and click the Button
Notice that the colors in the lower half of PictureBox's image seem to be obtained by interpolating between the colors in our Bitmap and Gray. In other words, it is as if a row of gray pixels was added to our Bitmap BEFORE the image was stretched.
Now, increment the value in the TextBox and press the Button after each increment. That phantom row of gray pixels is always present until the value in the TextBox equals or exceeds the height of the PictureBox.
So why do I care? Well, I'm glad you asked. Stretched 1D Bitmaps update MUCH faster in a PictureBox than 2D Bitmaps that completely fill a PictureBox. Of course, creating a 1D Bitmap is also much faster than creating a 2D Bitmap. So, the above method is the best way that I've found for creating a permanent gradient image.
A work around for this problem is to create a PictureBox inside of a Container. The, you could just make the height of the PictureBox larger than the container so that the interpolated gray region was not visible. But, lets face it, there must be a better way.
I followed your steps, and I see no grey line...maybe it is just me. I tried many different values, still no grey line.
I am on Win XP, with the updated .Net Framework. Have you updated to the newest version of the Framework? I am also on a 14.1 inch LCD laptop display at 1024 X 768 res... if you want to know all that...lol. Good luck in finding your problem.
Originally posted by hellswraith I followed your steps, and I see no grey line...maybe it is just me. I tried many different values, still no grey line.
Thanks for the info. Just knowing that somebody else isn't noticing the problem is useful.
I've tryied it on a PC (1024x768) and a laptop (1600x1200) with Win2k and .NET Framework SP1. So, maybe its Win2k?
I've attached some screen captures so people can see what I'm observing.
Here is a screen capture from mine...I put the form bg to black so I could verify if it was the form or the image (the button is black too, but that doesn't matter does it..lol).
Originally posted by hellswraith Here is a screen capture from mine...I put the form bg to black so I could verify if it was the form or the image (the button is black too, but that doesn't matter does it..lol).
Actually, setting the form bg to black has revealed something fairly important. The final color for interpolation is determined by the Backcolor of the PictureBox. To verify this I changed the Backcolor of my PictureBox from Gray to Red to Yellow, etc. In each case the image was interpolated to the Backcolor of the PictureBox.
So, maybe one of you programming experts can tell me what this means. I can't imagine that this wasn't caught in the testing phases. Is there some advantage to having your image fade to a color that isn't part of the Bitmap? Personally, I'd prefer to include the colors when I want them and leave them out when I don't.
Originally posted by ljlevend So why do I care? Well, I'm glad you asked. Stretched 1D Bitmaps update MUCH faster in a PictureBox than 2D Bitmaps that completely fill a PictureBox. Of course, creating a 1D Bitmap is also much faster than creating a 2D Bitmap. So, the above method is the best way that I've found for creating a permanent gradient image.
You're not quite correct here. It is actually faster to draw one big bitmap that takes up the whole picturebox than it is to stretch a smaller one. But you are right about it being quicker to actually create the smaller one.
So you're saying that you tested this out by just moving the picturebox and seeing a visual difference? That doesn't sound like a very accurate way of testing. I tried it and saw no visible difference in speed.
Originally posted by Tygur So you're saying that you tested this out by just moving the picturebox and seeing a visual difference? That doesn't sound like a very accurate way of testing. I tried it and saw no visible difference in speed.
Hey, who ever said anything about accuracy? Maybe you just didn’t look hard enough
Anyway, here is some sample code that is a bit more quantitative. As it turns out, we both were right (which sounds a lot better than saying we both were wrong). The relative speed of Stretched vs. Normal (i.e., non-stretched) Bitmaps in a PictureBox depends on what you are doing with the PictureBox. The sample code test four scenarios. Here are the results on my computer (1.33MHz Athlon, 266DDR, GeForce3):
1. Set PictureBox.Image = Nothing and then set PictureBox.Image = (Pre-made Bitmap): The Stretched image takes 10% longer to update.
2. Move the PictureBox a small (i.e., <10 pixels) amount: The Normal image takes 150% - 170+% longer to update (the ratio increases as the distance that the PictureBoxes move decreases).
3. Move the PictureBox off of the screen and then back on to the screen: The Stretched image takes 14% longer to update.
4. Resize the PictureBox by a small amount: The Stretched image takes 40% - 50+% longer to update (the ratio increases as the length of the PictureBoxes increases).
So there you have it. If you're Resizing a PictureBox, then is seems like it is faster to set SizeMode to Normal and use a Bitmap that fills the entire PictureBox. But, if you're moving a PictureBox, then it is faster to set SizeMode to StretchImage and use a 1D Bitmap.
Originally posted by wolfofthenorth ...What the heck is a 1D bitmap?
A "1D" Bitmap is a Bitmap with a width or height of 1. Sorry, I couldn't come up with a better way to refer to such a thing.
The idea that I had is that you could create a 1D Bitmap with a linear gradient and then use a PictureBox's StretchImage SizeMode to quickly fill in the other dimension of the image. Unfortunately, this doesn't work all that well (see above posts).
Originally posted by ljlevend So there you have it. If you're Resizing a PictureBox, then is seems like it is faster to set SizeMode to Normal and use a Bitmap that fills the entire PictureBox. But, if you're moving a PictureBox, then it is faster to set SizeMode to StretchImage and use a 1D Bitmap.
These speed ratios actually make some sense after a bit of thought. Of course, I suggest that you look into using the LinearGradientBrush to make a full-sized bitmap and forget about stretching a smaller one
Originally posted by hellswraith LOL, your operating on a pretty slow processor....didn't know they made the Athlons that slow..lol
Ooops, did I say Athlon? I meant 286
Originally posted by Tygur
These speed ratios actually make some sense after a bit of thought. Of course, I suggest that you look into using the LinearGradientBrush to make a full-sized bitmap and forget about stretching a smaller one
Is there a way to create a Bitmap from a LinearGradientBrush? I'm assuming your talking about using a LinearGradientBrush with the CreateGraphics thingy, but if not then PLEASE LET ME KNOW! I would love to be able to create a Bitmap from a LinearGradientBrush.
The reason why I don't like to use a LinearGradientBrush with CreateGraphics is that then you have to make sure that your Graphics are updated after a Resize, a Move, or when a control with a lower ZOrder moves. In some cases this can cause problems because an event might not finish before the CreateGraphics is called so the resultant Graphic can use old information. Maybe there is a slick way to avoid this problem, but I haven't found it. (DoEvents scare me so I try to avoid them whenever possible.) With a Bitmap you only have to worry about the Resize event so Bitmaps are much easier for me to manage. Don't get me wrong, CreateGraphics is definitely my top pick when I'm sure that controls aren't moving.
Dim myBitMap As New Bitmap(Me.Width, Me.Height)
Dim myRect As New Rectangle(10, 10, 400, 200)
Dim myBrush As New LinearGradientBrush(myRect, Color.Purple, Color.Red, LinearGradientMode.Horizontal)
Me.BackgroundImage = myBitMap
Dim G As Graphics = Graphics.FromImage(myBitMap)
G.Clear(Me.BackColor)
G.FillRectangle(myBrush, myRect)
That which does not kill us, only makes us stronger.