.NET - Bitmap.Save ignores Bitmap.SetResolution on Windows 7

心不动则不痛 提交于 2019-12-01 17:05:50

Hmya, this is a bug in a Windows component. The Windows group is always very reluctant to get bugs like this fixed, breaking changes are postponed to a next Windows version. It did get fixed in Windows 8. Do consider how unusual it is what you are doing, the DPI of an image should always be set by the device that recorded the image. Like the camera or scanner, they never get this wrong. There just isn't any device around that has a 200 dots-per-inch resolution.

If you are desperate enough to find a workaround then you could consider patching the file itself. Not hard to do for a JPEG file, the fields in the file header are pretty easy to get to:

using System.IO;
...
    public static void SetJpegResolution(string path, int dpi) {
        using (var jpg = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) 
        using (var br = new BinaryReader(jpg)) {
            bool ok = br.ReadUInt16() == 0xd8ff;        // Check header
            ok = ok && br.ReadUInt16() == 0xe0ff;
            br.ReadInt16();                             // Skip length
            ok = ok && br.ReadUInt32() == 0x4649464a;   // Should be JFIF
            ok = ok && br.ReadByte() == 0;
            ok = ok && br.ReadByte() == 0x01;           // Major version should be 1
            br.ReadByte();                              // Skip minor version
            byte density = br.ReadByte();
            ok = ok && (density == 1 || density == 2);
            if (!ok) throw new Exception("Not a valid JPEG file");
            if (density == 2) dpi = (int)Math.Round(dpi / 2.56);
            var bigendian = BitConverter.GetBytes((short)dpi);
            Array.Reverse(bigendian);
            jpg.Write(bigendian, 0, 2);
            jpg.Write(bigendian, 0, 2);
        }
    }

I've found a workaround that will do the job. It's not elegant but...

Instead of applying the resolution to the original image, make a copy of it and work on the copy:

Bitmap bitmap = (Bitmap)Image.FromFile(originalFile);
Bitmap newBitmap = new Bitmap(bitmap)
newBitmap.SetResolution(200, 200);
newBitmap.Save(newFile, ImageFormat.Jpeg);

Now it works on Windows 7. Go figure.

I like Hans Passant's idea, though, it's cleaner. I don't know if what I did messes up with the image, if there is recompression or not.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!