C# GDI+ ScaleTransform ok on picturebox but image

2019-09-14 11:55发布

问题:

Hi I have the issue that when I use ScaleTransform(zoomFactor,zoomFactor) the image saved on disk is the original version always, while on screen in the picturebox the image is distorted in proportion to the zoomFactor. Why this could be happening ? Shouldn't I have the final result as applied from e.Graphics on disk written image ?

My code is the following which is a version with matrix. but the instead of matrix I have used the ScaleTransform as well. Result is always the same:

                g=e.Graphics;//inside picturebox_paint()
                g.ScaleTransform(ratio * zoomFac, ratio * zoomFac);
                e.Graphics.DrawImage((Bitmap)bmp, 0, 0);
                int seed = Convert.ToInt32(Regex.Match(Guid.NewGuid().ToString(), @"\d+").Value);
                String destinationFile = @"C:\tmp\photoid\" + new Random(seed).Next() + "_conv.jpg";
//Here I get always the original image back!!!!
                bmp.Save(destinationFile);

I have used as well the following idiom but with same results:

            //Matrix matrix = new Matrix();
            //matrix.Scale(zoomFac, zoomFac);
            //e.Graphics.Transform = matrix;                                            

回答1:

You need to make the PictureBox draw the things it shows on screen into a new Bitmap, which you then can save!

As it is the Image will be saved in the original form and nothing you did in the Paint event, which actually painst onto the surface of the PictureBox will be saved.

So to save everything, i.e. The Image, possibly a BackgroundImage and all you draw in the Paint event you would call DrawToBitmap somehwere.

Somewhere means somewhere else, not in the Paint event, as it will call the Paint event to create the new Bitmap, causing an endless loop..

To call it you would do something like this:

Bitmap bmpSave = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
pictureBox1.DrawToBitmap(bmpSave, pictureBox1.ClientRectangle);

But maybe this is not really what you want? Maybe you actually want to modify the Image? In that case do not use the Paint event at all!

Instead do something like this:

Bitmap bmpSave = new Bitmap(yourNewWidth, yourNewHeight);
using (Graphics g = Graphics.FromImage(bmpSave))
{
    g.ScaleTransform(ratio * zoomFac, ratio * zoomFac);
    g.DrawImage((Bitmap)pictureBox1.Image, 0, 0);  // 
    pictureBox1.Image = bmpSave;
    bmpSave.Save(...);
}

You could call this from somewhere where the scaling is being triggered from.

Note that doing the scaling repeatedly and each time from the previoulsy scaled version will degrade the quality rather fast. For this always scale from a saved version of the original!!

Btw: Using a Matrix for scaling doesn't really make a difference over ScaleTransform.

But if you want to do a direct scaling why not use the DrawImage overload which takes two Rectangles? This is the most common solution if all you want to to scale and maybe draw other stuff additionally..:

int newWidth = 100; int newHeight = 100; string yourFileName = "D:\\xyz123.jpg";

Bitmap bmpSave = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
Rectangle newRectangle = new Rectangle(0, 0, newWidth, newHeight);
Rectangle oldRectangle = new Rectangle(Point.Empty, pictureBox1.Image.Size);
using (Graphics g = Graphics.FromImage(bmpSave))
{
    g.DrawImage((Bitmap)pictureBox1.Image, newRectangle, oldRectangle, GraphicsUnit.Pixel);
    bmpSave.Save(yourFileName, ImageFormat.Jpeg);
} 

And there there is the scaling Bitmap constructor:

Bitmap bmp = new Bitmap(pictureBox1.Image, newWidth, newHeight);

Which I would recommend if all you want is to scale the Image. As the other solutions it will not change the Image displayed until you assign it back into the PictureBox..:

pictureBox1.Image = bmp ;

Don't forget to dispose of the old Image..



回答2:

Been a while since I messed with GDI but I think you need to copy back to the Bitmap here.

g.DrawImage(bmp, scaledwidth, scaledheight);

Try something like that before bmp.Save

Edit Apologies for not seeing that you were copying back to the bitmap. Perhaps the overload which specifies the output rectangle is what you need. Try a DrawImage overload which has the destination Rect. https://msdn.microsoft.com/en-us/library/ms142040(v=vs.110).aspx