DrawImage scales original image

8.9k views Asked by At

I am stitching several images together vertically and horizontally to create one bigger image (in which the total width and height are the sum of the widths and heights of the individual images) using Bitmap and System.Drawing.Graphics in C#. The size of the individual images are 256 px by 256 px.

When I use DrawImage from System.Drawing.Graphics, why do I get a scaled and/or zoomed in version of the original image?


Here is the original image:

original image


When I retrieve the image programmatically and write to file in code, I get the following:

var result = httpClient.GetStreamAsync(/* url */);
var bitmap = new Bitmap(await result);
...
using (var memory = new MemoryStream())
{ 
   using (var fs = new FileStream(/* path */, FileMode.OpenOrCreate, FileAccess.ReadWrite))
   {
        bitmap.Save(memory, ImageFormat.Png); // I save to .png if that makes a difference
        var bytes = memory.ToArray();
        await fs.WriteAsync(bytes, 0, bytes.Length);
   }
}

programmatic retrieval of image in code

I see no difference. So far, so good.


However, when I attempt to stitch images horizontally, I get the following:

Note: For reference, the image above is at the far-right in the stitched image below.

horizontally stitched image

It looks scaled, zoomed in, and definitely not like the original or programmatically retrieved individual image above.


Here is the code I use to stitch the images together:

Note: If byWidth is true, I stitch images horizontally.

private Bitmap MergeImages(IEnumerable<Bitmap> images, bool byWidth)
{
    var enumerable = images as IList<Bitmap> ?? images.ToList();

    var width = 0;
    var height = 0;

    foreach (var image in enumerable)
    {
        if (byWidth)
        {
            width += image.Width;
            height = image.Height;
        }
        else
        {
            width = image.Width;
            height += image.Height;
        }
    }

    var bitmap = new Bitmap(width, height);
    using (var g = Graphics.FromImage(bitmap))
    {
        var localWidth = 0;
        var localHeight = 0;

        foreach (var image in enumerable)
        {
            if (byWidth)
            {
                g.DrawImage(image, localWidth, 0);
                localWidth += image.Width;
            }
            else
            {
                g.DrawImage(image, 0, localHeight);
                localHeight += image.Height;
            }
        }
    }

    return bitmap;
}
1

There are 1 answers

1
Cody Gray - on strike On BEST ANSWER

Yes, DrawImage scales the image output based on the DPI setting of the image being drawn. A bitmap image saved at 72 DPI will be scaled to match the 96 DPI of the screen you're drawing it to (or whatever DPI your screen is actually set to). The idea here is that the source resolution (DPI) should be made to match the destination resolution (DPI), in order to maintain resolution-independence.

Confusingly, even the DrawImageUnscaled method uses this DPI-based scaling logic. Practically, this is because the DrawImageUnscaled method is just a simple wrapper around one of the overloads of DrawImage. Conceptually, this seems like a bug to me.

Either way, the solution is simple: explicitly pass the desired size of the image to the DrawImage call. Essentially, you are forcing it to scale to the desired size:

g.DrawImage(img, 0, 0, img.Width, img.Height);

This is quasi-documented in KB317174.