Have a question? ask www.kenlet.com
Home  |  FAQ  |  About  |  Contact  |  View Source   
 
SEARCH:
 
BROWSE:
    My Hood
Edit My Info
View Events
Read Tutorials
Training Modules
View Presentations
Download Tools
Scan News
Get Jobs
Message Forums
School Forums
Member Directory
   
CONTRIBUTE:
    Sign me up!
Post an Event
Submit Tutorials
Upload Tools
Link News
Post Jobs
   
   
Home >  Tutorials >  C# >  Resizing an Image in a PictureBox for Best Fit
Add to MyHood
   Resizing an Image in a PictureBox for Best Fit   [ printer friendly ]
Stats
  Rating: 3.6 out of 5 by 5 users
  Submitted: 05/06/02
Michael Procopio (michael1@ufl.edu)

 
Adding image functionality to applications can greatly enrichen the user experience. Images, of course, come in all sorts of shapes and sizes, and this variance needs to be handled correctly in applications.

On the display of any device, screen real estate is limited, and it usually makes sense to confine an image to some pre-defined dimensions. The idea is, you want to display the image as large as possible within its container. However, to maintain the integrity of the image, the picture must of course be resized while maintaining the aspect ratio. Sounds simple? You would like to think so.

Unfortunately, this very useful resize functionality has been omitted in the Micrososft PictureBox control that ships with the .NET Vramework. It was also absent in the equivalent image controls that shipped with Visual Studio 6.0. Oddly enough, it is included in the image control that comes with Microsoft Access, with a resize mode called "Zoom." Depsite universal requests for its inclusion by developers, this feature remains awkwardly absent from today's imaging controls.

Instead of this simple resize mode, we instead get several similar, but wholly inadequate, options for manipulating our images. Examples include "Autosize," (enlarge the control to fit the image), "Stretch" (more appropriately named 'distort', which mangles you image in whatever way necessary to MAKE it fit the bounds of its containing control, and "Normal," which simply crops the image to the bottom and right as necessary.

Essentially, this results in ugly images, since most developers resort to some sort of "stretch" functionality. Case in point: Check out the little Dalmation, my DevHood image, that appears with this tutorial. No, he's not sick, and there's nothing wrong with the pup's body structure. Rather, he is subject to a SizeMode of "Stretch," ignoring the fact that this image isn't perfectly square. Not a good thing to have!

There is a solution. I have outlined one here for you. This code will look at a given image, look at where that image is being displayed, and then make it AS LARGE AS POSSIBLE to fit within its containing control. However, the aspect ratio will be maintained (that is, each dimension is multiplied by the same scaling factor), and the result is the image will not appear distorted.

Perhaps one day, we can have this functionality intrinsic to the imaging controls we use. For now, we can instead make do with code along the lines of that shown below. Feel free to use them in your own applications. They are self-contained, and the main code consists of two methods.

The first method is handleFitToWindow(bool). This references a PictureBox on the form (could be a passed in window handle). To be effective, there needs to be an image already loaded in the PictureBox control. Here is the code:

        private void handleFitToWindow(bool doFit)
        {
            try
            {
                /* Don't perform any operation if no image is loaded. */
                if (this.pboxMain.Tag != null)
                {
                    if (doFit)  /* We're fitting it to the window, and centering it. */
                    {
                        /* Create a temporary Image.

                         * Always work from the original image, stored in the Tag. 
                         */

                        Image tempImage = (Image)this.pboxMain.Tag;

                        /* Calculate the dimensions necessary for an image to fit. */
                        Size fitImageSize = this.getScaledImageDimensions(
                            tempImage.Width, tempImage.Height, this.pboxMain.Width, this.pboxMain.Height);

                        /* Create a new Bitmap from the original image with the new dimensions.

                         * The constructor for the Bitmap object automatically scales as necessary.
                         */

                        Bitmap imgOutput = new Bitmap(tempImage, fitImageSize.Width, fitImageSize.Height);

                        /* Clear any existing image in the PictureBox. */
                        this.pboxMain.Image = null;

                        /* When fitting the image to the window, we want to keep it centered. */
                        this.pboxMain.SizeMode = PictureBoxSizeMode.CenterImage;

                        /* Finally, set the Image property to point to the new, resized image. */
                        this.pboxMain.Image = imgOutput;
                    }

                    else  /* Restore the image to its original size */
                    {
                        /* Clear any existing image int he PictureBox. */
                        this.pboxMain.Image = null;

                        /* Set the resize more to Normal; this will place the image

                         * in the upper left-hand corner, clipping the image as required.
                         */

                        this.pboxMain.SizeMode = PictureBoxSizeMode.Normal;

                        /* Finally, set the Image property to point to the original image. */
                        this.pboxMain.Image = (Image)this.pboxMain.Tag;
                    }
                }
            }
            catch (System.Exception e)
            {
                Console.WriteLine(e);
            }
        }



The second method, getScaledImageDimensions(int, int, int, int), is called by the previous method shown above. It is responsible for determining the dimensions of the resized image. In all cases, the aspect ratio is mantained, since both dimensions get multiplied by the same scaling factor.

        private Size getScaledImageDimensions(
            int currentImageWidth, 
            int currentImageHeight,
            int desiredImageWidth,
            int desiredImageHeight)  
        {
            /* First, we must calculate a multiplier that will be used

             * to get the dimensions of the new, scaled image.
             */

            double scaleImageMultiplier = 0;

            /* This multiplier is defined as the ratio of the

             * Desired Dimension to the Current Dimension.
             * Specifically which dimension is used depends on the larger
             * dimension of the image, as this will be the constraining dimension
             * when we fit to the window.
             */


            /* Determine if Image is Portrait or Landscape. */
            if (currentImageHeight > currentImageWidth)    /* Image is Portrait */
            {
                /* Calculate the multiplier based on the heights. */
                if (desiredImageHeight > desiredImageWidth)
                {
                    scaleImageMultiplier = (double)desiredImageWidth / (double)currentImageWidth;
                }

                else
                {
                    scaleImageMultiplier = (double)desiredImageHeight / (double)currentImageHeight;
                }
            }

            else /* Image is Landscape */
            {
                /* Calculate the multiplier based on the widths. */
                if (desiredImageHeight > desiredImageWidth)
                {
                    scaleImageMultiplier = (double)desiredImageWidth / (double)currentImageWidth;
                }

                else
                {
                    scaleImageMultiplier = (double)desiredImageHeight / (double)currentImageHeight;
                }
            }
        
            /* Generate and return the new scaled dimensions.

             * Essentially, we multiply each dimension of the original image
             * by the multiplier calculated above to yield the dimensions
             * of the scaled image. The scaled image can be larger or smaller
             * than the original.
             */

            return new Size(
                (int)(currentImageWidth * scaleImageMultiplier), 
                (int)(currentImageHeight * scaleImageMultiplier));
        }





That's it! I've tried to be very thorough in commenting the code, so you should have some idea of what's going on. However, you don't really need to know how these work to make use of them.

As far as future work goes, using these methods, it would be straightforward to inherit from the existing PictureBox control and extend its functionality to include the ability to "Zoom" the image as described above.

Now, if we could only convince the DevHood guys to implement this code, so my poor dalmation pup would finally look normal!


Michael


Return to Browsing Tutorials

Email this Tutorial to a Friend

Rate this Content:  
low quality  1 2 3 4 5  high quality

Reader's Comments Post a Comment
 
Coolio!
-- Radu Grama, May 10, 2002
 
very useful ... no more distorted pics!
-- Edward Kim, May 29, 2002
 
cool
reminds me of the js i used to write to control window dimensions.
-- John Woo, November 01, 2002
 
This rules, you saved me hours of work. Two years later and its still not a part of the .net control. Why not??
-- Steve Gibbs, June 21, 2004
 
Superb! Figures that they still don't have this as part of the standard image manipulation functions in Microsoft's tools.
One other comment: you may want to have the function receive a reference to the picture box as follows:
handleFitToWindow(bool doFit, ref PictureBox pboxMain)
and make it a little clearer that you need to set the .Tag to the image befure calling the handleFitToWindow:
pictureBox1.Tag = pictureBox1.Image;
handleFitToWindow(true, ref pictureBox1);
-- Dave Wolfson, July 19, 2004
 
Thanks for the code! It works great and saved me the headache. I still can not believe this is not part of PictureBox control after all of this time. I am still working out the differences between the VB IDE and c#'s IDE in VS.NET. In VB they nicely provided you a list of events to choose from for a control where in C# I have to know what I am looking for or consult the API. Sorry to ramble, but thanks for the code and the concept. I am using part of this to create a zoom in and zoom out button.
-- Todd Dennis, August 02, 2004
 
Hello Michael,

first off all I want to thank you for that great part of code. That saved me some hours of work.

Unfortunatly i had some problems with the "getScaledImageDimensions" method.

I replaced it by this:

private Size getScaledImageDimensions(
int currentImageWidth,
int currentImageHeight,
int desiredImageWidth,
int desiredImageHeight) {

double scaleImageMultiplier =

Math.Max((double)desiredImageWidth, (double)desiredImageHeight)/Math.Max((double)currentImageWidth, (double)currentImageHeight);

return new Size(
(int)(currentImageWidth * scaleImageMultiplier)-BORDER*2,
(int)(currentImageHeight * scaleImageMultiplier)-BORDER*2);
}

Thanks
-- Thorsten Roth, February 10, 2005
 
Hi...
it works fine for the image smaller one to fit the window...but how do we shirnk the bigger image to make it fit to the picture box????????
-- Abhishek Ranjan, October 24, 2008
 

Copyright © 2001 DevHood® All Rights Reserved