0
votes

im looking for a way to resize a Bitmap, without losing its actually shape, while having in mind that the new size is musn't be proportional to the original image dimensions.

To be exact I want to resize a image of a upright eight (40x110) to a 29x29 Bitmap and im looking for a function to convert the original proportions relativ to its new image, while filling the new created space (in width) with white and having the same side clearance from the actually eight to its white baselines.

Unfortunately this hasn't satisfy my requirements:

 public static Bitmap ResizeImage(Image image, int width, int height)
    {
        var destRect = new Rectangle(0, 0, width, height);
        var destImage = new Bitmap(width, height);

        destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

        using (var graphics = Graphics.FromImage(destImage))
        {
            graphics.CompositingMode = CompositingMode.SourceCopy;
            graphics.CompositingQuality = CompositingQuality.HighQuality;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

            using (var wrapMode = new ImageAttributes())
            {
                wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
            }
        }

        return destImage;
    }

Update 1: New function for proportional resizing:

 public static Bitmap ResizeImageProportional(Bitmap bitmap, int width, int height)
    {
        Bitmap destImage;
        Rectangle destRect;
        int destH, destW, destX, dextY;

        if (bitmap.Height > bitmap.Width)
        {
            destH = height;
            destW = bitmap.Width / bitmap.Height * height;
            destX = (width - destW) / 2;
            dextY = 0;
        }
        else if (bitmap.Height < bitmap.Width) 
        {
            destH = bitmap.Height / bitmap.Width * width;
            destW = width;
            destX = 0;
            dextY = (height - destH) / 2;
        }
        else
        // if (bitmap.Width == bitmap.Height)
        {
            destH = height;
            destW = width;
            destX = 0;
            dextY = 0;
        }

        destRect = new Rectangle(destX, dextY, destW, destH);
        destImage = new Bitmap(width, height);

        destImage.SetResolution(bitmap.HorizontalResolution, bitmap.VerticalResolution);

        using (var graphics = Graphics.FromImage(destImage))
        {
            graphics.CompositingMode = CompositingMode.SourceCopy;
            graphics.CompositingQuality = CompositingQuality.HighQuality;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

            using (var wrapMode = new ImageAttributes())
            {
                wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                graphics.DrawImage(bitmap, destRect, 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, wrapMode);
            }
        }

        return destImage;
    }

Unfortunately I always get a empty bitmap with this solution. I first crop the original bitmap to a rect in which the element of my bitmap does precisely fit in. Otherwise put the left baseline of the bitmap itself is also the baseline of the graphics element in it, while the whole background is white. Then I put this bitmap (approx. 40x80) in to the "ResizeImageProportional" (original post edit.) function with a width and height value of 29.

1
What is the problem with the current solution?Nader
You are using (0,0) for the source and the destination location. That's not what you want, right? You need to calculate the destination location!TaW

1 Answers

0
votes

If you want to keep the proportion you need to calculate the destination rectnagle.

Here are the measures, off the top of my head:

destH = height;
destW = image.Width / image.Height * height; 
destX = (width - destW) / 2;
dextY = 0;

You should clear the background to the color you want before: Graphics.Clear(someColor);

If you don't you use the bounds of the source and the destination images, thereby distorting the image. Which is what you seem to have.., no?

In that case there is no new space, of course.

When calculating, use float if necessary..

Update:

Maybe you want to analyze the original image and preserve or remove the spaces from the foreground to its edges. That means either looking into the pixels or maybe going one step back to how the original was created..

enter image description here

For the example I have added the bounds of the figure.

Here is a simple cropping function; it assumes the top left corner to hold the background color and returns a cropping rectangle:

public static Rectangle CropBounds(Image image)
{
    Bitmap bmp = (Bitmap)image;
    int x0 = 0;
    int x1 = bmp.Width - 1;
    int y0 = 0;
    int y1 = bmp.Height - 1;
    Color c = bmp.GetPixel(0, 0);
    while (x0==0)
    {  for (int x = 0; x < x1; x++)
            if ((x0==0)) for (int y = 0; y < y1; y++)
                if ( bmp.GetPixel(x,y)!= c) {x0 = x-1; break;} }
    while (x1 == bmp.Width - 1)
    {
        for (int x = bmp.Width - 1; x > 0; x--)
            if ((x1 == bmp.Width - 1)) for (int y = 0; y < y1; y++)
                    if (bmp.GetPixel(x, y) != c) { x1 = x + 1; break; }  }
    while (y0 == 0)
    {
        for (int y = 0; y < y1; y++)
            if ((y0 == 0)) for (int x = 0; x < bmp.Width; x++)
                    if (bmp.GetPixel(x, y) != c) { y0 = y - 1; break; }
    }
    while (y1 == bmp.Height - 1)
    {
        for (int y = bmp.Height - 1; y > 0; y--)
            if ((y1 == bmp.Height - 1)) for (int x = 0; x < bmp.Width; x++)
                    if (bmp.GetPixel(x, y) != c) { y1 = y + 1; break; }
    }

    return new Rectangle(x0, y0, x1 - x0, y1 - y0);
}

Now you can change your code to use it like this:

graphics.DrawImage(image, destRect, CropBounds(image) , GraphicsUnit.Pixel);

If you really need to flip, simply store the rectangle and change to the appropriate format..!

Update 2:

There was a reason why I wrote When calculating, use float if necessary..:

In your ResizeImageProportional function you need to avoid integer precision loss! Change the divisions to:

destW = (int) (1f * bitmap.Width / bitmap.Height * height);

and

destH =  (int) (1f *bitmap.Height / bitmap.Width * width);