Link to home
Start Free TrialLog in
Avatar of jorgemgonzalez
jorgemgonzalezFlag for United States of America

asked on

Image resizing

I'm in serious need of advise on this one.

I have a code that resizes jpeg images to 200px wide or 150px high depending on portrait or landscape attributes, that I have accomplished without problems, now, obviously the target size should be 200px wide by 150px high. Therefore I need some space around the images to accomplish that requirement, meaning that I need every resulting image to be 200px wide by 150px high without cropping or distorting the originals.

It would be great if the space around the image could be of a color of choice.

Please help.

Thanks in advance.
Avatar of Ammar Gaffar
Ammar Gaffar
Flag of United Arab Emirates image

In ASP.NET or VB.NET Windows Application?
In the code below (I know, it is C#, I'll come to that) that I just created and tested, it does exactly what you want. The key is the PicureBox_Paint event and the drawScaledImage. The background color is done by the eraseImage method.

To make this work, create a form, place a button, a picturebox and a openfiledialog on it. On the buttonclick event you assign the filename of the image to a field, m_image_filename. The picturebox will be redrawn (.Invalidate()) and the drawScaledImage is called. The method DrawImage does the scaling for you, the hard part is actually the calculation of the various rectangles. I kept the code clean and simple as far as I could, the result is that it is not very optimized. But it should do for this basic task.

In my next post I'll give an update in VB code of the same.

-- Abel --

public partial class ResizeKeepRatio : Form
{
    private string m_image_filename = null;
 
    public ResizeKeepRatio()
    {
        InitializeComponent();
    }
 
    private void button1_Click(object sender, EventArgs e)
    {
        openFileDialog1.ShowDialog();
        m_image_filename = openFileDialog1.FileName;
        pictureBox1.Invalidate();
    }
 
    private void eraseImage(Graphics graph)
    {
        Rectangle rect = new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height);
        graph.DrawRectangle(new Pen(Brushes.Orange, (float) rect.Width), rect);
    }
 
    private void drawScaledImage(Graphics graph)
    {
        if(m_image_filename == null)
            return;
 
        Bitmap bmp = new Bitmap(m_image_filename);
        Rectangle sourceRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
        Rectangle drawAreaRect = new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height);
        Rectangle targetRect;
 
        // determine whether to resize horizontally or vertically
        // based on the ratio width/height of source image and target rectangle
        double sourceRatio = (double)sourceRect.Height / (double)sourceRect.Width;
        double targetRatio = (double)drawAreaRect.Height / (double)drawAreaRect.Width;
        double drawRatio;
        if (sourceRatio > targetRatio)
            drawRatio = (double) drawAreaRect.Height / (double) sourceRect.Height;
        else
            drawRatio = (double) drawAreaRect.Width / (double) sourceRect.Width;
 
        // calculate target rectangle and center
        targetRect = new Rectangle(
            (int) ((drawAreaRect.Width - drawRatio * sourceRect.Width) / 2), 
            (int) ((drawAreaRect.Height - drawRatio * sourceRect.Height) / 2), 
            (int) (drawRatio * sourceRect.Width), 
            (int) (drawRatio * sourceRect.Height));
 
        // draw the image
        graph.DrawImage(bmp, targetRect);
    }
 
    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        eraseImage(e.Graphics);
        drawScaledImage(e.Graphics);
    }
 
}

Open in new window

The VB code is a bit less readable, as is often with calculation code (matter of taste, of course), but here you go (courtesy to http://www.developerfusion.com/tools/convert/csharp-to-vb/):

Public Partial Class ResizeKeepRatio
    Inherits Form
    Private m_image_filename As String = Nothing
    
    Public Sub New()
        InitializeComponent()
    End Sub
    
    Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)
        openFileDialog1.ShowDialog()
        m_image_filename = openFileDialog1.FileName
        pictureBox1.Invalidate()
    End Sub
    
    Private Sub eraseImage(ByVal graph As Graphics)
        Dim rect As New Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height)
        graph.DrawRectangle(New Pen(Brushes.Orange, CSng(rect.Width)), rect)
    End Sub
    
    Private Sub drawScaledImage(ByVal graph As Graphics)
        If m_image_filename Is Nothing Then
            Exit Sub
        End If
        
        Dim bmp As New Bitmap(m_image_filename)
        Dim sourceRect As New Rectangle(0, 0, bmp.Width, bmp.Height)
        Dim drawAreaRect As New Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height)
        Dim targetRect As Rectangle
        
        ' determine whether to resize horizontally or vertically '
        ' based on the ratio width/height of source image and target 'rectangle
        Dim sourceRatio As Double = CDbl(sourceRect.Height) / CDbl(sourceRect.Width)
        Dim targetRatio As Double = CDbl(drawAreaRect.Height) / CDbl(drawAreaRect.Width)
        Dim drawRatio As Double
        If sourceRatio > targetRatio Then
            drawRatio = CDbl(drawAreaRect.Height) / CDbl(sourceRect.Height)
        Else
            drawRatio = CDbl(drawAreaRect.Width) / CDbl(sourceRect.Width)
        End If
        
        ' calculate target rectangle and center '
        targetRect = New Rectangle(CInt(((drawAreaRect.Width - drawRatio * sourceRect.Width) / 2)), CInt(((drawAreaRect.Height - drawRatio * sourceRect.Height) / 2)), CInt((drawRatio * sourceRect.Width)), CInt((drawRatio * sourceRect.Height)))
        
        ' draw the image '
        graph.DrawImage(bmp, targetRect)
    End Sub
    
    Private Sub pictureBox1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs)
        eraseImage(e.Graphics)
        drawScaledImage(e.Graphics)
    End Sub
    
End Class

Open in new window

In the screenshot you can see one of Window's Vista's sample pictures with a green border. It is a test with the VB code because I wasn't sure if would compile. Don't forget to add the click and paint events! (I did). You can use a ColorDialog for picking the color, which goes into line 17 above.

Happy coding with it! ;-)

ScreenShot419.png
ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of jorgemgonzalez

ASKER

Thank you experts for your fast reply.

I created a separate project for each solution and copied the code exactly as posted. For some reason Abel's one did not work at all after clicking button1. Is there something missing?

Idle Mind's (And I.ve always said that mind is whatever but idle) worked like a charm, I love the idea.

Now, what I missed in my question (my apologies) is that the code I have (And need) is for  resizing ingmultiple images, (For ---> Next) actually all the images in a folder via folder browser dialog, I have no picture boxes in my form!

Attached is part of the code so you can have a better idea.

Is there a way of getting the same result without the PicBox?

Otherwise please advise on how to load - unload the images on the PicBox for each file in directory.
Dim bp As New Bitmap(newSize.Width, newSize.Height)
                Dim g As Graphics = Graphics.FromImage(bp)
 
                ' Controls the quality of the image
                g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
                g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
                g.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
 
                Dim rect As New Rectangle(0, 0, newSize.Width, newSize.Height)
                g.DrawImage(currentImage, rect, 0, 0, currentImage.Width, currentImage.Height, GraphicsUnit.Pixel)
 
                ' Copy Meta from old image to new image
                For Each pItem As System.Drawing.Imaging.PropertyItem In currentImage.PropertyItems
                    bp.SetPropertyItem(pItem)
                Next
                newFileName = "Thumb_" & file.ToString
                ' Saves as jpeg
                bp.Save(NewDir & "\Thumbnails\" & newFileName, Imaging.ImageFormat.Jpeg)

Open in new window

You can actually use my concept with a dynamic PictureBox that is never even visible to the user...

    Dim pb As New PictureBox
    pb.Size = New Size(200, 150)    
    pb.BackColor = Color.DarkGreen
    pb.SizeMode = PictureBoxSizeMode.Zoom

Then iterate thru your files and use "pb" in code just like in my example above.
Thanks a million Idle_Mind!
Thanks again!
> For some reason Abel's one did not work at all after clicking button1. Is there something missing?
You just needed to add the click event and the paint event in the properties of the button and the picture box.

> Is there a way of getting the same result without the PicBox?
you can draw on almost anything using my method, but I see you already chosen Idle's, which good, because it is simpler. I just didn't even thought of it while working the solution out. Was a nice exercise regardless ;-)

-- Abel --