PRB: Drawing Dimensions Are Inconsistent with Image.Size Property When You Use DrawImageUnscaled() Method (317174)



The information in this article applies to:

  • Microsoft GDI+ 1.0
  • Microsoft .NET Framework
  • Microsoft Windows XP Professional
  • the operating system: Microsoft Windows XP 64-Bit Edition

This article was previously published under Q317174
This article references the following .NET Framework Class Library namespaces:
  • System.Drawing

SYMPTOMS

When you use the DrawImageUnscaled() method in the .NET Framework, or a DrawImage() method without dimension parameters in GDI+, the results of the drawing may appear to be inconsistent with the dimensions of the image reported by the Image.Size property.

CAUSE

This behavior occurs because the Image.Size property in the .NET Framework or the Image.Width() and Image.Height() methods in GDI+ return the pixel dimensions of the image.

However, the "unscaled" image drawing methods of the Graphics class, by default, draw the image as it should be drawn for the image's logical size (as determined by its internal resolution). This default behavior for the unscaled image drawing methods is by design, so that they conform to the resolution-independent intent of GDI+.

This result can be very confusing, because the resulting pixel dimension of the image that is drawn by the Graphics class does not match the pixel dimension that is reported by the Image class.

Consider the following C# sample code, which demonstrates this opportunity for confusion:
protected override void OnPaint(PaintEventArgs pea)
{
    Pen     pen = new Pen(Color.Black);
    Size    ImageSize;

    ImageSize = image.Size;
    int x = 50;
    int y = 50;

    pea.Graphics.PageUnit = GraphicsUnit.Pixel;
    // Draw a line offset from the Image origin
    // Line length to be the same as Image dimension
    pea.Graphics.DrawLine(pen, x, y-5,x+ImageSize.Width-1, y-5);
    pea.Graphics.DrawLine(pen, x-5, y, x-5, y+ImageSize.Height-1);
    // Draw the Image
    pea.Graphics.DrawImageUnscaled(image, x, y);
}
				
The intent of this code, if one assumes that the results of DrawImageUnscaled() are consistent with the dimensions of ImageSize, should be two black lines drawn along the top and side of the image. The length of these lines are intended to be the same as the dimensions of the image.

However, for most images, you will notice a discrepancy between the dimensions of the lines and those of the image. To understand why, consider the case of a specific image.

An image has an inherently defined dimension that is comprised of the number of pixels along the horizontal and vertical axes. Image resolution is also often expressed as dots per inch (dpi). The dpi value relates the pixel size of the image to its logical size, as measured in real time and space.

For example, it is possible that a picture with a pixel size of 600 by 300 dots can be defined as having a dpi value of 300. Therefore, the logical size of the image is 2 inches by 1 inch ( its physical size, as a photo perhaps ).

If displayed on a computer monitor that has a dpi of 100, you can use one of two methods to draw the image:
  • Display it as 600 by 300 pixels, which is 6 inches by 3 inches on the 100-dpi screen.

    -or-
  • Use the 300-dpi value in the image to convert the image size to a pixel size of 2 inches by 1 inch on the 100-dpi screen.
The image size returned by Image.Size or from the Image class Width() and Height() methods indicate that the image is 600 pixels by 300 pixels. This is correct if the DrawImage or DrawImageUnscaled methods use the first of the preceding two methods to draw the image.

However, the second method is used to preserve the 2-inch by 1-inch logical size of the image on the 100-dpi screen. As a result, the image that is drawn would actually paint a field of pixels that is 200 pixels by 100 pixels. The image remains at its unscaled 2-inch by 1-inch logical size, but its dimensions in pixels have actually shrunk.

If the dpi of the screen is 300, the two methods are equivalent, because 600 by 300 pixels on a 300-dpi screen is equivalent to 2 inches by 1 inch.

It is a good practice to use the logical size value of an image, as defined by its dpi, because this maintains consistency in the image size when it is viewed by a user. GDI+ is designed to be resolution-independent, which is the reason for the preceding GDI+ methods, as well as for the .NET Framework default to the logical size when an image is drawn unscaled.

RESOLUTION

If you need to know the pixel dimensions of an image, you can determine the pixel size in the same way that the DrawImage method draws the image. To determine the pixel dimensions of the logical size of the image, perform a unit conversion on the image dimensions by using the dpi of the image and the dpi of the Graphics object.

The following sample C# code demonstrates how this is done:
protected override void OnPaint(PaintEventArgs pea)
{
    Pen     pen = new Pen(Color.Black);
    Size    ImageSize;
    int cx;
    int cy;

    ImageSize = image.Size;

    // calculate the image dimensions in pixels from its logical size
    cx = (int)((float)ImageSize.Width/image.HorizontalResolution * pea.Graphics.DpiX);
    cy = (int)((float)ImageSize.Height/image.VerticalResolution * pea.Graphics.DpiY);
    int x = 50;
    int y = 50;

    pea.Graphics.PageUnit = GraphicsUnit.Pixel;
    // Draw a line offset from the Image origin
    // Line length to be the same as Image dimension
    pea.Graphics.DrawLine(pen, x, y-5,x+cx-1, y-5);
    pea.Graphics.DrawLine(pen, x-5, y, x-5, y+cy-1);
    // Draw the Image
    pea.Graphics.DrawImageUnscaled(image, x, y);
}
				

STATUS

This behavior is by design.

MORE INFORMATION

Although it is important that code be able to properly express the size of the resulting image drawn by the unscaled versions of the DrawImage method, it may also be important to draw the image at its pixel size.

You can draw the image at its pixel size by using one of the DrawImage methods that can scale the image to the destination dimension that you want, expressed as pixels.

The following sample C# code demonstrates how this can be done:
protected override void OnPaint(PaintEventArgs pea)
{
    Pen     pen = new Pen(Color.Black);
    Size    ImageSize;

    ImageSize = image.Size;
    int x = 50;
    int y = 50;

    pea.Graphics.PageUnit = GraphicsUnit.Pixel;
    // Draw a line offset from the Image origin
    // Line length to be the same as Image dimension
    pea.Graphics.DrawLine(pen, x, y-5,x+ImageSize.Width-1, y-5);
    pea.Graphics.DrawLine(pen, x-5, y, x-5, y+ImageSize.Height-1);
    // Draw the Image confined to its pixel bounds
    pea.Graphics.DrawImage(image, x, y, ImageSize.Width, ImageSize.Height);
}
				
NOTE: In all of the C# code samples in this article, the Image object that is queried and drawn by the code whose symbol is "image" is assumed to be defined on the C# form. It is also assumed that the Image object has already been initialized with a picture.

Modification Type:MinorLast Reviewed:4/3/2006
Keywords:kbDSWGDI2003Swept kbgdipimaging kbprb KB317174