Масштабирование System.Drawing.Bitmap до заданного размера при сохранении пропорций

Я хочу масштабировать System.Drawing.Bitmap, по крайней мере, до некоторой фиксированной ширины и высоты. Это делается для создания эскизов для галереи изображений на веб-сайте, поэтому я хочу сохранить соотношение сторон одинаковым.

У меня есть несколько решений, но ни один из них, похоже, не делает то, что мне нужно; они вращаются вокруг масштабирования, основываясь на том, что ширина или высота одинаковы, но не изменяются.

Пример:

Если у меня есть изображение 4272 на 2848, и я хочу масштабировать его до 1024 на 768, то получившееся изображение должно быть 1024 на 683 и дополнено (с черной рамкой) до 1024 на 768.

Как я могу сделать это с изображениями большего размера, чем требуемый размер, и меньше, чем требуемые размеры, а также изображения на планшете, которые не выходят за точный размер, который мне нужен после масштабирования?

Ответ 1

Целевые параметры:

float width = 1024;
float height = 768;
var brush = new SolidBrush(Color.Black);

Ваш оригинальный файл:

var image = new Bitmap(file);

Целевой размер (масштабный коэффициент):

float scale = Math.Min(width / image.Width, height / image.Height);

Изменение размера, включая чистку холста в первую очередь:

var bmp = new Bitmap((int)width, (int)height);
var graph = Graphics.FromImage(bmp);

// uncomment for higher quality output
//graph.InterpolationMode = InterpolationMode.High;
//graph.CompositingQuality = CompositingQuality.HighQuality;
//graph.SmoothingMode = SmoothingMode.AntiAlias;

var scaleWidth = (int)(image.Width * scale);
var scaleHeight = (int)(image.Height * scale);

graph.FillRectangle(brush, new RectangleF(0, 0, width, height));
graph.DrawImage(image, ((int)width - scaleWidth)/2, ((int)height - scaleHeight)/2, scaleWidth, scaleHeight);

И не забудьте сделать bmp.Save(filename) чтобы сохранить полученный файл.

Ответ 2

Конструктор растрового изображения имеет встроенное изменение размера.

Bitmap original = (Bitmap)Image.FromFile("DSC_0002.jpg");
Bitmap resized = new Bitmap(original,new Size(original.Width/4,original.Height/4));
resized.Save("DSC_0002_thumb.jpg");

http://msdn.microsoft.com/en-us/library/0wh0045z.aspx

Если вы хотите управлять режимами интерполяции, см. этот пост.

Ответ 3

Просто чтобы добавить к yamen ответ, который идеально подходит для изображений, но не так много для текста. Если вы пытаетесь использовать это для масштабирования текста... как, скажем, документ Word (который в данном случае в байтах от взаимодействия слов), вам нужно будет сделать несколько модификаций, иначе вы получите гигантские полосы на стороне. Может быть не идеален, но работает на меня!

using (MemoryStream ms = new MemoryStream(wordBytes))
                {
                    float width = 3840;
                    float height = 2160;

                    var brush = new SolidBrush(Color.White);
                    var rawImage = Image.FromStream(ms);
                    float scale = Math.Min(width / rawImage.Width, height / rawImage.Height);
                    var scaleWidth = (int)(rawImage.Width * scale);
                    var scaleHeight = (int)(rawImage.Height * scale);
                    var scaledBitmap = new Bitmap(scaleWidth, scaleHeight);
                    Graphics graph = Graphics.FromImage(scaledBitmap);
                    graph.InterpolationMode = InterpolationMode.High;
                    graph.CompositingQuality = CompositingQuality.HighQuality;
                    graph.SmoothingMode = SmoothingMode.AntiAlias;
                    graph.FillRectangle(brush, new RectangleF(0, 0, width, height));
                    graph.DrawImage(rawImage, new Rectangle(0, 0 , scaleWidth, scaleHeight));
                    scaledBitmap.Save(fileName, ImageFormat.Png);
                    return scaledBitmap;
                }

Ответ 4

private static Size CalculateDimensions(Size originalSize, Size targetSize)
{
    Size newSize = new Size();
    int cooficientA = targetSize.Height * originalSize.Width;
    int cooficientB = targetSize.Width * originalSize.Height;

    if (cooficientA < cooficientB)
    {
        newSize.Width = (int)Math.Round((double)cooficientA / originalSize.Height);
        newSize.Height = targetSize.Height;
    }
    else
    {
        newSize.Width = targetSize.Width;
        newSize.Height = (int)Math.Round((double)cooficientB / originalSize.Width);
    }
    return newSize;
}

Size newSize = CalculateDimensions(originalSize, targetSize);
x = (targetSize.Width - newSize.Width) / 2;
y = (targetSize.Height - newSize.Height) / 2;