水平翻转应避免RotateTransform,正确方法是TranslateTransform平移原点后ScaleTransform(-1,1);Bitmap可直接用RotateFlip(RotateNoneFlipX);高性能场景用LockBits逐行倒序复制;WPF需用RenderTransform。

用 Graphics.RotateTransform 会翻错方向,别这么干
水平翻转不是旋转,RotateTransform 是绕中心点转角度,哪怕转 180°,结果也是上下+左右全反,不是纯镜像。真要水平翻,得靠坐标系翻转,不是图像内容旋转。
正确做法是先用 Graphics.TranslateTransform 把原点移到图像右边界,再用 Graphics.ScaleTransform(-1, 1) 沿 X 轴翻倍缩放 —— 负号即翻转,1 表示 Y 不动。这样画出来的图才是严格左右镜像。
-
ScaleTransform(-1, 1)前必须TranslateTransform(width, 0),否则图像会画到画布外左侧(负坐标区) - 如果直接对原
Bitmap操作,推荐用RotateFlip(RotateFlipType.RotateNoneFlipX),更轻量、不依赖Graphics - 在 WinForms 的
Paint事件里用ScaleTransform,记得用g.ResetTransform()收尾,否则后续绘制全乱
RotateFlipType.RotateNoneFlipX 是最稳的 API
这是 GDI+ 封装好的原地翻转操作,直接作用于 Bitmap 对象,不涉及绘图上下文,没状态干扰,性能也好。适用于预处理图片、生成缩略图、保存前修正方向等场景。
注意它会修改原始位图数据,如果原图还要继续用,得先 Clone() 一份:
Bitmap original = new Bitmap("photo.jpg");
Bitmap flipped = (Bitmap)original.Clone();
flipped.RotateFlip(RotateFlipType.RotateNoneFlipX); // ← 这行就完事- 只支持四种翻转类型:
RotateNoneFlipX/RotateNoneFlipY/Rotate180FlipXY/Rotate180FlipNone,没有“仅翻 Y+旋转 90°”这种组合 - .NET 6+ 中仍可用,但
System.Drawing.Common在 Linux/macOS 上需额外安装libgdiplus - 如果图像带 Alpha 通道,这个方法保留透明度,不会出现黑边或色值溢出
用 LockBits 手动翻转适合高性能批量处理
当你要每秒翻转上百张图(比如实时摄像头帧),RotateFlip 的托管开销会变明显。LockBits 直接操作像素内存,把每行像素从右往左拷过去,速度能快 3–5 倍。
关键点在于:必须按扫描线(scan line)逐行倒序复制,且注意 stride 对齐(Windows 默认按 4 字节对齐,实际宽度可能被补零):
- 读取
BitmapData.Stride,不是Width * BytesPerPixel,否则 memcpy 会越界 - 目标地址 = 每行起始 + (width - x - 1) * bytesPerPixel,而不是简单反转整块内存
- 多线程处理时,每个
Bitmap必须单独LockBits,不能共享BitmapData实例
WPF 里别碰 WriteableBitmap 的 Lock,改用 Transform
WPF 的图像管线和 GDI+ 分离,硬套 RotateFlip 会失败(抛 NotSupportedException)。正确路径是走 Image.Source + RenderTransform:
<Image Source="face.png">
<Image.RenderTransform>
<ScaleTransform ScaleX="-1" CenterX="0.5" />
</Image.RenderTransform>
</Image>这本质是 GPU 渲染层翻转,不改原始像素,响应快、无内存拷贝。但注意:CenterX="0.5" 是相对自身宽归一化坐标,不设的话图像会水平偏移一倍宽度。
- 如果后续要截图保存,这个翻转不会自动生效到输出图里,得用
RenderTargetBitmap+VisualBrush重新渲染一次 -
ScaleTransform和RotateTransform可叠加,但顺序影响结果:先 Scale 再 Rotate,和先 Rotate 再 Scale,最终画面不同
真正麻烦的是跨平台一致性 —— System.Drawing 在非 Windows 上行为略有差异,而 WPF 又根本跑不了。如果项目要同时支持桌面和 MAUI,翻转逻辑最好抽成接口,运行时按环境选实现。









