将 System.Drawing.Bitmap 缩放到给定大小,同时保持纵横比

Posted

技术标签:

【中文标题】将 System.Drawing.Bitmap 缩放到给定大小,同时保持纵横比【英文标题】:Scaling a System.Drawing.Bitmap to a given size while maintaining aspect ratio 【发布时间】:2012-05-13 14:39:40 【问题描述】:

我想将System.Drawing.Bitmap 缩放到至少小于某个固定宽度和高度。这是为网站上的图片库生成缩略图,所以我想保持纵横比不变。

我有很多解决方案,但似乎没有一个能真正满足我的需求;它们围绕基于保持宽度或高度相同但不改变两者的缩放。

一个例子:

如果我有一个 4272 x 2848 的图像并且我想将其缩放到 1024 x 768 的大小,那么生成的图像应该是 1024 x 683 并填充(带有黑色边框)到 1024 x 768。

如何处理大于所需尺寸和小于所需尺寸的图像,以及在缩放后无法达到我需要的确切尺寸的填充图像?

【问题讨论】:

你为什么不能只做数学来计算另一个维度? @CodyGray 究竟是什么“其他”维度?这是一个二维图像,需要在保持其纵横比的同时按比例缩小。我尝试仅采用纵横比并找出接近目标尺寸的宽度和高度的公分母,但它从未正常工作。 【参考方案1】:

位图构造函数已内置调整大小。

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

如果您想控制插值模式,请参阅this post。

【讨论】:

谢谢。这有很大帮助。我知道这是一个有点旧的答案,但是我的新图像看起来模糊有什么原因吗?我需要设置任何插值模式吗? 这不只是将原始位图绘制在一个新的较小位图中吗?图片的一部分应该被剪掉。 这不只是在一个新的更小的位图中绘制原始位图吗? TFM 的链接就在那里:初始化 Bitmap 类的新实例指定的现有图像,缩放到指定大小【参考方案2】:

目标参数:

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) 来保存生成的文件。

【讨论】:

这将创建一个纯黑色图像。 var scale 更改为float scale。我猜你的代码中的某个地方你的widthheight 可能被声明为int,这会导致问题。如果您密切关注我给出的内容,它就可以正常工作。 scale 的重点是保持纵横比。完成后删除 -1。 我知道这是旧的,但这对我有帮助,所以谢谢。但是,我将 graph 包装在 using 块中,因为它实现了 IDisposable。 我看到这也很老了,但尽管我会提到 Graphics 是 Disposable(),所以不要忘记将该图形对象放入 using 语句中 使用FillRectangle 方法而不是Clear 方法是否有特定原因?这样可以省去创建画笔和处理它的麻烦。【参考方案3】:

只是为了添加到衙役的答案,这对于图像来说是完美的,但对于文本来说却不是那么完美。

如果您尝试使用它来缩放文本,例如 Word 文档(在本例中以 Word Interop 中的字节为单位),您将需要进行一些修改,否则您会在旁边看到巨大的条形图。

可能并不完美,但对我有用!

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;

【讨论】:

以上是关于将 System.Drawing.Bitmap 缩放到给定大小,同时保持纵横比的主要内容,如果未能解决你的问题,请参考以下文章

从 Android.Graphics.Bitmap.GetPixels 获得的 RGB 值与 System.Drawing.Bitmap.GetPixel 略有不同

将 HBITMAP 句柄从非托管代码传递到托管代码以创建 System.Drawing.Bitmap 的安全性

无法从 'System.Drawing.Bitmap' 转换为 'byte[*,*,*]'

C# WPF 错误 无法将类型"System.Windows.Media.ImageSource"转换为"System.Drawing.Bitmap"

System.Drawing.Bitmap.GetHicon() 上的 GDI 对象泄漏

是否可以从 System.Drawing.Bitmap 创建 Avalonia.Media.Imaging.Bitmap?