【问题标题】:Combine System.Drawing.Bitmap[] -> Icon结合 System.Drawing.Bitmap[] -> 图标
【发布时间】:2019-07-15 00:27:31
【问题描述】:

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

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

但是你如何合并多个Bitmap对象到一个Icon

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


我对Icon 对象的一般情况并不清楚。它确实是一组多个图像。加载时,您可以将其分成Bitmap 部分。但我没有看到任何创建多图标的方法。不能以明显的方式迭代、添加或删除Bitmap 部分似乎也很奇怪,比如拥有一组位图。

【问题讨论】:

    标签: c# icons system.drawing ico


    【解决方案1】:

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

    我不久前研究了该格式,它实际上接受 png 数据作为内部图像。请注意,上述图像的宽度或高度不能超过 256 像素,并且文件中的图像数量以两个字节保存,因此不能超过 Int16.MaxValue,或 0xFFFF,或 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 at index " + i + " is 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要求流保持打开状态;它只是从流中读取它的字节并保留它。

    png颜色深度信息可以在herehere找到,顺便说一句。

    如果您想将图标文件作为字节数组,或者想将其写入磁盘,您当然可以修改此代码以返回一个字节数组,甚至只是让它将内容写入Stream作为参数给出,而不是在内部创建 MemoryStream

    【讨论】:

    • 使用 Vista 的 PNG 支持并直接应用格式是一个绝妙的主意。为这个 +1!
    • 不久前,我制作了一个用于转换一堆 90 年代游戏图形格式的工具,在制作过程中,我注意到 .Net 对其某些原生格式的支持有多差。这就是为什么我深入研究这些东西,为它们制作自己的加载和保存方法。
    • 哎呀,在转换为字节后做了 >256 检查。现已修复。
    猜你喜欢
    • 1970-01-01
    • 2011-04-24
    • 2017-03-29
    • 2012-07-20
    • 2014-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多