【问题标题】:How to create an Icon file that contains Multiple Sizes / Images in C#如何在 C# 中创建包含多个大小/图像的图标文件
【发布时间】:2010-07-09 15:17:17
【问题描述】:

如何创建包含多种尺寸的图标文件?

我知道我使用Icon.FromHandle() 从位图创建了一个图标,但是如何向该图标添加另一个图像/大小?

编辑:我需要在我的应用程序中执行此操作,因此我无法执行外部应用程序来进行合并。

【问题讨论】:

  • 由于我认为它不能回答您的问题,因此我将其作为评论发布。我使用一个名为 IcoFX 的程序来创建图标,它可以非常方便地一次创建多种尺寸的图标,方法是将原始 256x256 图标重新采样为各种其他尺寸(即 64x64、32x32 ......)你可能会也可能不会发现它和相关信息有用。网址:icofx.ro
  • 有趣的是,即使您指定不能使用它们,很多人也会立即为您提供外部工具。想知道他们是否完整地阅读了这个问题!
  • @iamserious:规范是在这些 cmets 之后添加的,因此评论的用户很可能阅读了该问题。我已经编辑了这个问题以帮助澄清这一点。 :)
  • 公平地说,原标题确实说“在C#中”

标签: c# bitmap icons


【解决方案1】:

我一直在寻找一种将 .png 文件组合成图标的方法,这没什么花哨的。在找不到简单的东西并且这个问题是热门搜索结果后,我创建了以下代码。


如果对于每个图像,Image.RawFormatImageFormat.PngImage.PixelFormatPixelFormat.Format32bppArgb,并且尺寸小于或等于 256x256,则以下代码可以创建具有多种尺寸的图标:

/// <summary>
/// Provides methods for creating icons.
/// </summary>
public class IconFactory
{

    #region constants

    /// <summary>
    /// Represents the max allowed width of an icon.
    /// </summary>
    public const int MaxIconWidth = 256;

    /// <summary>
    /// Represents the max allowed height of an icon.
    /// </summary>
    public const int MaxIconHeight = 256;

    private const ushort HeaderReserved = 0;
    private const ushort HeaderIconType = 1;
    private const byte HeaderLength = 6;

    private const byte EntryReserved = 0;
    private const byte EntryLength = 16;

    private const byte PngColorsInPalette = 0;
    private const ushort PngColorPlanes = 1;

    #endregion

    #region methods

    /// <summary>
    /// Saves the specified <see cref="Bitmap"/> objects as a single 
    /// icon into the output stream.
    /// </summary>
    /// <param name="images">The bitmaps to save as an icon.</param>
    /// <param name="stream">The output stream.</param>
    /// <remarks>
    /// The expected input for the <paramref name="images"/> parameter are 
    /// portable network graphic files that have a <see cref="Image.PixelFormat"/> 
    /// of <see cref="PixelFormat.Format32bppArgb"/> and where the
    /// width is less than or equal to <see cref="IconFactory.MaxIconWidth"/> and the 
    /// height is less than or equal to <see cref="MaxIconHeight"/>.
    /// </remarks>
    /// <exception cref="InvalidOperationException">
    /// Occurs if any of the input images do 
    /// not follow the required image format. See remarks for details.
    /// </exception>
    /// <exception cref="ArgumentNullException">
    /// Occurs if any of the arguments are null.
    /// </exception>
    public static void SavePngsAsIcon(IEnumerable<Bitmap> images, Stream stream)
    {
        if (images == null)
            throw new ArgumentNullException("images");
        if (stream == null)
            throw new ArgumentNullException("stream");

        // validates the pngs
        IconFactory.ThrowForInvalidPngs(images);

        Bitmap[] orderedImages = images.OrderBy(i => i.Width)
                                       .ThenBy(i => i.Height)
                                       .ToArray();

        using (var writer = new BinaryWriter(stream))
        {

            // write the header
            writer.Write(IconFactory.HeaderReserved);
            writer.Write(IconFactory.HeaderIconType);
            writer.Write((ushort)orderedImages.Length);

            // save the image buffers and offsets
            Dictionary<uint, byte[]> buffers = new Dictionary<uint, byte[]>();

            // tracks the length of the buffers as the iterations occur
            // and adds that to the offset of the entries
            uint lengthSum = 0;
            uint baseOffset = (uint)(IconFactory.HeaderLength +
                                     IconFactory.EntryLength * orderedImages.Length);

            for (int i = 0; i < orderedImages.Length; i++)
            {
                Bitmap image = orderedImages[i];

                // creates a byte array from an image
                byte[] buffer = IconFactory.CreateImageBuffer(image);

                // calculates what the offset of this image will be
                // in the stream
                uint offset = (baseOffset + lengthSum);

                // writes the image entry
                writer.Write(IconFactory.GetIconWidth(image));
                writer.Write(IconFactory.GetIconHeight(image));
                writer.Write(IconFactory.PngColorsInPalette);
                writer.Write(IconFactory.EntryReserved);
                writer.Write(IconFactory.PngColorPlanes);
                writer.Write((ushort)Image.GetPixelFormatSize(image.PixelFormat));
                writer.Write((uint)buffer.Length);
                writer.Write(offset);

                lengthSum += (uint)buffer.Length;

                // adds the buffer to be written at the offset
                buffers.Add(offset, buffer);
            }

            // writes the buffers for each image
            foreach (var kvp in buffers)
            {

                // seeks to the specified offset required for the image buffer
                writer.BaseStream.Seek(kvp.Key, SeekOrigin.Begin);

                // writes the buffer
                writer.Write(kvp.Value);
            }
        }

    }

    private static void ThrowForInvalidPngs(IEnumerable<Bitmap> images)
    {
        foreach (var image in images)
        {
            if (image.PixelFormat != PixelFormat.Format32bppArgb)
            {
                throw new InvalidOperationException
                    (string.Format("Required pixel format is PixelFormat.{0}.",
                                   PixelFormat.Format32bppArgb.ToString()));
            }

            if (image.RawFormat.Guid != ImageFormat.Png.Guid)
            {
                throw new InvalidOperationException
                    ("Required image format is a portable network graphic (png).");
            }

            if (image.Width > IconFactory.MaxIconWidth ||
                image.Height > IconFactory.MaxIconHeight)
            {
                throw new InvalidOperationException
                    (string.Format("Dimensions must be less than or equal to {0}x{1}",
                                   IconFactory.MaxIconWidth, 
                                   IconFactory.MaxIconHeight));
            }
        }
    }

    private static byte GetIconHeight(Bitmap image)
    {
        if (image.Height == IconFactory.MaxIconHeight)
            return 0;

        return (byte)image.Height;
    }

    private static byte GetIconWidth(Bitmap image)
    {
        if (image.Width == IconFactory.MaxIconWidth)
            return 0;

        return (byte)image.Width;
    }

    private static byte[] CreateImageBuffer(Bitmap image)
    {
        using (var stream = new MemoryStream())
        {
            image.Save(stream, image.RawFormat);

            return stream.ToArray();
        }
    }

    #endregion

}

用法:

using (var png16 = (Bitmap)Bitmap.FromFile(@"C:\Test\3dGlasses16.png"))
using (var png32 = (Bitmap)Bitmap.FromFile(@"C:\Test\3dGlasses32.png"))
using (var stream = new FileStream(@"C:\Test\Combined.ico", FileMode.Create))
{
    IconFactory.SavePngsAsIcon(new[] { png16, png32 }, stream);
}

【讨论】:

  • 谢谢。这真的很有用。
  • 差不多了。 Icos 在 XP 中无法工作(我必须支持)。
【解决方案2】:

快速CYA:我只是做了一个谷歌搜索,并没有测试下面的方法。 YMMV。

我找到了this article,它提到了一个这样做的类(尽管在 VB.Net 中,但很容易翻译),并告诉他如何使用它。虽然线程指向的页面似乎不再提到源代码,但我确实找到了它的一个版本here.

【讨论】:

    【解决方案3】:

    这可以通过IconLib 完成。您可以从 CodeProject 文章中获取源代码,也可以获取 compiled dll from my GitHub mirror

    public void Convert(string pngPath, string icoPath)
    {
        MultiIcon mIcon = new MultiIcon();
        mIcon.Add("Untitled").CreateFrom(pngPath, IconOutputFormat.FromWin95);
        mIcon.SelectedIndex = 0;
        mIcon.Save(icoPath, MultiIconFormat.ICO);
    }
    

    CreateFrom 可以采用 256x256 png 或 System.Drawing.Bitmap 对象的路径。

    【讨论】:

    • 太棒了。谢谢。
    【解决方案4】:

    您无法使用System.Drawing API 创建图标。它们是为从图标文件中访问特定图标而构建的,而不是为了将多个图标写入回一个 .ico 文件。

    如果您只是想制作图标,您可以使用 GIMP 或其他图像处理程序来创建您的 .ico 文件。否则,如果您确实需要以编程方式制作 .ico 文件,您可以使用 png2ico(使用 System.Diagnostics.Process.Start 调用)或类似的东西。

    【讨论】:

    • 当然你可以制作图标文件...从任何图像:go4expert.com/forums/showthread.php?t=19250
    • @NickAldwin:您可以保存单层图标(如链接所示),但不能保存 多个 分层图标。我已经编辑澄清。
    • 只是想澄清一个可能的误解,感谢编辑;)
    【解决方案5】:

    使用 IcoFX:http://icofx.ro/

    它可以创建 Windows 图标并在 1 个 ico 文件中存储多种尺寸和颜色

    【讨论】:

    • OP 要求编程方法
    猜你喜欢
    • 2014-03-13
    • 2015-03-28
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 2017-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多