【问题标题】:Bitmap.Save to save an icon actually saves a .pngBitmap.Save 保存一个图标实际上保存了一个.png
【发布时间】:2012-07-11 04:34:00
【问题描述】:

我需要编写一个程序,该程序将基于一个tileset图像生成108个图标组合(标准windows .ico文件)。

我使用类 System.Drawing.Bitmap 来构建每个组合,并像这样保存它们:

Bitmap IconBitmap = new Bitmap(16, 16);
// Some processing, writing different parts of the source tileset
// ...
IconBitmap.Save(Path.Combine(TargetPath, "Icon" + Counter + ".ico"),
                ImageFormat.Icon);

但我发现保存的文件实际上是PNG。 Windows Explorer 和 Visual Studio 都不能正确显示它,但 GIMP 可以,如果我在 Hex 查看器中打开它,我看到的是:

00000000  89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52  ‰PNG........IHDR
00000010  00 00 00 10 00 00 00 10 08 06 00 00 00 1F F3 FF  ..............óÿ
00000020  61 00 00 00 01 73 52 47 42 00 AE CE 1C E9 00 00  a....sRGB.®Î.é..
00000030  00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05 00 00  ..gAMA..±..üa...
00000040  00 09 70 48 59 73 00 00 0E C3 00 00 0E C3 01 C7  ..pHYs...Ã...Ã.Ç
00000050  6F A8 64 00 00 00 15 49 44 41 54 38 4F 63 60 18  o¨d....IDAT8Oc`.
00000060  05 A3 21 30 1A 02 A3 21 00 09 01 00 04 10 00 01  .£!0..£!........
00000070  72 A5 13 76 00 00 00 00 49 45 4E 44 AE 42 60 82  r¥.v....IEND®B`‚

如果我将 .ico 重命名为 .png,Windows 资源管理器也可以正常显示。

即使我在位图上什么都不做,我也会得到这个结果(我直接用newSave 构造它,这给了我一个黑色的png)。

我做错了什么?

我也试过这个,它给了我可怕的 16 色图标,但我宁愿避免这个解决方案(使用句柄):

Icon NewIcon = Icon.FromHandle(IconBitmap.GetHicon());
FileStream FS = new FileStream(Path.Combine(Target, "Icon" + Counter + ".ico"),
        FileMode.Create);
NewIcon.Save(FS);

【问题讨论】:

  • Microsoft Support上看这个,看来你倒霉了。
  • ICO 是一种容器格式,可以接受 PNG 或位图编码的图像,因此您看到的行为是正确的。分享您的处理代码可能是值得的,这样我们就可以查看您是否在那里做任何会导致您的图标无法正确显示的事情。另外,我认为显示 PNG ico 文件的能力是从 Vista 开始的,所以如果您使用的是 XP 或需要与 XP 兼容,那么您可能就不走运了。
  • Steve:感谢您提供的链接,不幸的是,这个错误是设计的...很遗憾它甚至没有抛出异常。 FurDworetzky:不,这不正确,我有一个实际的 PNG 作为结果,正如你所指出的,ICO 是一个容器。结果文件中没有容器数据。 AntonioBakula:似乎是,但我在第一次搜索时没有找到它......

标签: c# bitmap icons png system.drawing


【解决方案1】:

我自己做了一个快速而肮脏的解决方法,我把它贴在这里记录下来(它可能会帮助需要快速解决方案的人,比如我)。

我不会接受这是正确的答案,它不是真正的图标作者。 它只是将 32 位 ARGB 位图写入 ico 文件,使用 PNG 格式(适用于 Vista 或更高版本)

它基于维基百科的ICO file format文章,有些失败并重试。

void SaveAsIcon(Bitmap SourceBitmap, string FilePath)
{
    FileStream FS = new FileStream(FilePath, FileMode.Create);
    // ICO header
    FS.WriteByte(0); FS.WriteByte(0);
    FS.WriteByte(1); FS.WriteByte(0);
    FS.WriteByte(1); FS.WriteByte(0);

    // Image size
    FS.WriteByte((byte)SourceBitmap.Width);
    FS.WriteByte((byte)SourceBitmap.Height);
    // Palette
    FS.WriteByte(0);
    // Reserved
    FS.WriteByte(0);
    // Number of color planes
    FS.WriteByte(0); FS.WriteByte(0);
    // Bits per pixel
    FS.WriteByte(32); FS.WriteByte(0);

    // Data size, will be written after the data
    FS.WriteByte(0);
    FS.WriteByte(0);
    FS.WriteByte(0);
    FS.WriteByte(0);

    // Offset to image data, fixed at 22
    FS.WriteByte(22);
    FS.WriteByte(0);
    FS.WriteByte(0);
    FS.WriteByte(0);

    // Writing actual data
    SourceBitmap.Save(FS, ImageFormat.Png);

    // Getting data length (file length minus header)
    long Len = FS.Length - 22;

    // Write it in the correct place
    FS.Seek(14, SeekOrigin.Begin);
    FS.WriteByte((byte)Len);
    FS.WriteByte((byte)(Len >> 8));

    FS.Close();
}

【讨论】:

    【解决方案2】:

    这是我今天写的一个简单的 ICO 文件编写器,它将多个 System.Drawing.Image 图像输出到一个文件中。

    // https://en.wikipedia.org/wiki/ICO_(file_format)
    
    public static class IconWriter
    {
        public static void Write(Stream stream, IReadOnlyList<Image> images)
        {
            if (images.Any(image => image.Width > 256 || image.Height > 256))
                throw new ArgumentException("Image cannot have height or width greater than 256px.", "images");
    
            //
            // ICONDIR structure
            //
    
            WriteInt16(stream, 0); // reserved
            WriteInt16(stream, 1); // image type (icon)
            WriteInt16(stream, (short) images.Count); // number of images
    
            var encodedImages = images.Select(image => new
            {
                image.Width,
                image.Height,
                Bytes = EncodeImagePng(image)
            }).ToList();
    
            //
            // ICONDIRENTRY structure
            //
    
            const int iconDirSize = 6;
            const int iconDirEntrySize = 16;
    
            var offset = iconDirSize + (images.Count*iconDirEntrySize);
    
            foreach (var image in encodedImages)
            {
                stream.WriteByte((byte) image.Width);
                stream.WriteByte((byte) image.Height);
                stream.WriteByte(0); // no pallete
                stream.WriteByte(0); // reserved
                WriteInt16(stream, 0); // no color planes
                WriteInt16(stream, 32); // 32 bpp
    
                // image data length
                WriteInt32(stream, image.Bytes.Length);
    
                // image data offset
                WriteInt32(stream, offset);
    
                offset += image.Bytes.Length;
            }
    
            //
            // Image data
            //
    
            foreach (var image in encodedImages)
                stream.Write(image.Bytes, 0, image.Bytes.Length);
        }
    
        private static byte[] EncodeImagePng(Image image)
        {
            var stream = new MemoryStream();
            image.Save(stream, ImageFormat.Png);
            return stream.ToArray();
        }
    
        private static void WriteInt16(Stream stream, short s)
        {
            stream.WriteByte((byte) s);
            stream.WriteByte((byte) (s >> 8));
        }
    
        private static void WriteInt32(Stream stream, int i)
        {
            stream.WriteByte((byte) i);
            stream.WriteByte((byte) (i >> 8));
            stream.WriteByte((byte) (i >> 16));
            stream.WriteByte((byte) (i >> 24));
        }
    }
    

    【讨论】:

      【解决方案3】:

      ImageFormat.Icon 确实不能像您想象的那样用于写入,.NET 根本不支持写入 .ico 文件,只是转储 PNG 数据。

      CodeProject(和this one)(和another one)上有一些项目可以让你编写一个.ico 文件,实际上并不难。 file format 非常简单,支持 BMP 和 PNG 数据。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-13
        • 1970-01-01
        • 2022-07-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-18
        • 1970-01-01
        相关资源
        最近更新 更多