Bitmap裁剪用Clone最直接高效,但矩形必须在原图范围内;缩放旋转需Graphics.DrawImage与Transform配合;加水印优先用Graphics.DrawString/DrawImage并控制透明度;Bitmap和Graphics必须显式Dispose防内存泄漏。

Bitmap 裁剪:用 Clone 方法最直接,但矩形必须在原图范围内
裁剪本质是提取原图的一个矩形子区域,Bitmap.Clone 是最轻量、最常用的方式。它不涉及重采样或像素重计算,纯内存复制,快且不失真。
- 传入的
Rectangle坐标和尺寸必须完全落在原图内,否则抛出ArgumentException: 参数错误 - 注意坐标系:左上角是 (0, 0),X 向右增,Y 向下增;别把
Height当成 Y 起始点 - 如果需要“越界裁剪”(比如让部分区域填黑),得自己用
Graphics.DrawImage+TextureBrush或先创建新Bitmap再绘制,Clone不支持
示例:
var src = new Bitmap("photo.jpg");
var cropRect = new Rectangle(100, 50, 300, 200); // x, y, width, height
var cropped = src.Clone(cropRect, src.PixelFormat); // 成功前提是 cropRect 在 src 尺寸内
缩放与旋转必须用 Graphics,Bitmap.SetResolution 不改变像素数
很多人误以为调 SetResolution 能“放大图片”,其实它只改 DPI 元数据,输出到屏幕或打印时才影响渲染尺寸,像素阵列完全没变——这不是缩放。
- 真正缩放要用
Graphics.DrawImage,指定目标Rectangle,内部自动重采样;质量靠InterpolationMode控制(HighQualityBicubic较稳,NearestNeighbor适合像素风) - 旋转必须绕点进行,
Graphics.RotateTransform只影响后续绘制;常见错误是忘了平移原点到中心再旋转,导致图被切掉 - 旋转后外接矩形会变大,若硬塞进原尺寸
Bitmap,必然裁边;建议先算旋转后包围盒尺寸,再创建目标Bitmap
示例(安全旋转 30°):
var rotated = new Bitmap(src.Width, src.Height);
using (var g = Graphics.FromImage(rotated)) {
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.TranslateTransform(rotated.Width / 2f, rotated.Height / 2f);
g.RotateTransform(30);
g.TranslateTransform(-src.Width / 2f, -src.Height / 2f);
g.DrawImage(src, new Point(0, 0));
}
加水印:文字优先用 Graphics.DrawString,图章用 DrawImage,透明度靠 Color.FromArgb
水印核心是“叠加”,不是替换。直接操作 Bitmap 像素效率低且难控透明,Graphics 是标准路径。
- 文字水印:用
Font和Brush控制样式;半透明用new SolidBrush(Color.FromArgb(128, 0, 0, 0)),Alpha 值 0–255 - 图片水印(如 logo):用
DrawImage绘制小图,同样可设透明度(通过ImageAttributes设置颜色矩阵,或提前把 PNG 的 alpha 通道保留好) - 位置计算容易错:比如想右下角留边 20px,别写
width - 20,要减去水印自身的宽高:width - watermark.Width - 20
示例(右下角灰色半透文字):
using (var g = Graphics.FromImage(src)) {
var font = new Font("Arial", 16f);
var brush = new SolidBrush(Color.FromArgb(100, 120, 120, 120));
var size = g.MeasureString("© 2024", font);
g.DrawString("© 2024", font, brush,
src.Width - size.Width - 10,
src.Height - size.Height - 10);
}
释放资源:Bitmap 和 Graphics 都必须显式 Dispose,GC 不及时
这是 C# 图像处理里最高频的泄漏源。Bitmap 底层持 GDI+ 句柄,不 Dispose 就算局部变量出了作用域,句柄也不会立刻释放,撑满后直接抛 OutOfMemoryException ——哪怕你内存还剩 8GB。
-
Graphics.FromImage创建的对象也必须Dispose,不能只放任Bitmap - 用
using最省心;如果需跨方法传参,确保接收方明确知道要负责释放 - 别对同一个
Bitmap多次调Dispose,会抛ObjectDisposedException;也不要在Dispose后继续用它绘图
最后提醒一句:所有这些操作都在 CPU 上做,没用 GPU 加速;如果批量处理千张图,考虑 ImageSharp(纯托管、无 GDI+ 依赖、可异步),但那是另一套 API 了。


















