合并System.Drawing.Bitmap []->图标 [英] Combine System.Drawing.Bitmap[] -> Icon

查看:134
本文介绍了合并System.Drawing.Bitmap []->图标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

将图标拆分为 Bitmap 部分很容易:

Splitting an icon into its Bitmap parts is easy:

Bitmap icon16 = new Icon(combinedIcon, new Size(16, 16)).ToBitmap()

但是如何将多个位图对象合并到一个 Icon 中?

Bitmap icon16, icon32, icon64;
Icon combinedIcon = [...]


我对 Icon 对象的理解不是很普遍.它确实是一组多个图像.加载时,可以将其分成其 Bitmap 部分.但是我看不到创建多图标的任何方法.无法以明显的方式迭代,添加或删除 Bitmap 部分也似乎很奇怪,例如拥有一组位图.


I'm not that clear about the Icon object in general. It is indeed a set of multiple images. When loading it, you can take separate it into its Bitmap parts. But I don't see any method to create a multi-icon. It also seems strange to not being able to iterate, add or remove Bitmap parts in an obvious fashion, like having a collection of bitmaps.

推荐答案

.Net中的 Icon 类非常基础,甚至无法访问实际的所有功能.图标格式.最好将图标构造为字节流,然后将其加载为图标.

The Icon class in .Net is very rudimentary, and doesn't even get close to giving access to all features of the actual icon format. It's probably best to construct the icon as byte stream, and then load that as icon.

我回想了一下格式,实际上它接受png数据作为内部图像.请注意,上述图片的宽度或高度不能超过256个像素,并且文件中的图片数量以两个字节保存,因此它不能超过 Int16.MaxValue 或0xFFFF,或者65535.

I looked into the format a while back, and it actually accepts png data as internal images. Do note that said images cannot have a width or height of more than 256 pixels, and the amount of images in the file is saved in two bytes, so it cannot exceed Int16.MaxValue, or 0xFFFF, or 65535.

代码应如下所示:

public static Icon ConvertImagesToIco(Image[] images)
{
    if (images == null)
        throw new ArgumentNullException("images");
    Int32 imgCount = images.Length;
    if (imgCount == 0)
        throw new ArgumentException("No images given!", "images");
    if (imgCount > 0xFFFF)
        throw new ArgumentException("Too many images!", "images");
    using (MemoryStream ms = new MemoryStream())
    using (BinaryWriter iconWriter = new BinaryWriter(ms))
    {
        Byte[][] frameBytes = new Byte[imgCount][];
        // 0-1 reserved, 0
        iconWriter.Write((Int16)0);
        // 2-3 image type, 1 = icon, 2 = cursor
        iconWriter.Write((Int16)1);
        // 4-5 number of images
        iconWriter.Write((Int16)imgCount);
        // Calculate header size for first image data offset.
        Int32 offset = 6 + (16 * imgCount);
        for (Int32 i = 0; i < imgCount; ++i)
        {
            // Get image data
            Image curFrame = images[i];
            if (curFrame.Width > 256 || curFrame.Height > 256)
                throw new ArgumentException("Image too large!", "images");
            // for these three, 0 is interpreted as 256,
            // so the cast reducing 256 to 0 is no problem.
            Byte width = (Byte)curFrame.Width;
            Byte height = (Byte)curFrame.Height;
            Byte colors = (Byte)curFrame.Palette.Entries.Length;
            Int32 bpp;
            Byte[] frameData;
            using (MemoryStream pngMs = new MemoryStream())
            {
                curFrame.Save(pngMs, ImageFormat.Png);
                frameData = pngMs.ToArray();
            }
            // Get the colour depth to save in the icon info. This needs to be
            // fetched explicitly, since png does not support certain types
            // like 16bpp, so it will convert to the nearest valid on save.
            Byte colDepth = frameData[24];
            Byte colType = frameData[25];
            // I think .Net saving only supports colour types 2, 3 and 6 anyway.
            switch (colType)
            {
                case 2: bpp = 3 * colDepth; break; // RGB
                case 6: bpp = 4 * colDepth; break; // ARGB
                default: bpp = colDepth; break; // Indexed & greyscale
            }
            frameBytes[i] = frameData;
            Int32 imageLen = frameData.Length;
            // Write image entry
            // 0 image width. 
            iconWriter.Write(width);
            // 1 image height.
            iconWriter.Write(height);
            // 2 number of colors.
            iconWriter.Write(colors);
            // 3 reserved
            iconWriter.Write((Byte)0);
            // 4-5 color planes
            iconWriter.Write((Int16)0);
            // 6-7 bits per pixel
            iconWriter.Write((Int16)bpp);
            // 8-11 size of image data
            iconWriter.Write(imageLen);
            // 12-15 offset of image data
            iconWriter.Write(offset);
            offset += imageLen;
        }
        for (Int32 i = 0; i < imgCount; i++)
        {
            // Write image data
            // png data must contain the whole png data file
            iconWriter.Write(frameBytes[i]);
        }
        iconWriter.Flush();
        ms.Position = 0;
        return new Icon(ms);
    }
}

请注意,与其他 System.Drawing 图像格式不同, Icon 类确实不需要流保持打开状态;它只是从流中读取字节并将其留在那里.

Note that unlike other System.Drawing image formats, the Icon class does not require the stream to stay open; it just reads its bytes from the stream and leaves it at that.

可以在此处此处,顺便说一句.

The png colour depth info can be found here and here, btw.

如果您希望将图标文件作为字节数组,或者想要将其写入光盘,则当然可以改编此代码以返回字节数组,甚至只是将其写入作为参数提供的流,而不是在内部创建 MemoryStream .

If you want to have the icon file as byte array, or want to write it to disc, you can of course adapt this code to return a byte array, or even just to make it write the stuff to a Stream given as argument rather than creating the MemoryStream internally.

这篇关于合并System.Drawing.Bitmap []->图标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆