C#生成多尺寸bmp格式ICO图标代码

代码取自deepseek,且已经过本地执行测试

//.cs  文件类型,便于外部编辑时使用
// 引用必要的命名空间
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

// Quicker将会调用的函数。可以根据需要修改返回值类型。
public static void Exec(Quicker.Public.IStepContext context)
{
    //var oldValue = context.GetVarValue("varName");  // 读取动作里的变量值
    //MessageBox.Show(oldValue as string);
    //context.SetVarValue("varName", "从脚本输出的内容。"); // 向变量里输出值
    //MessageBox.Show("Hello World!");
    List<string> Paths = new List<string> { 
        @"D:\SoftwareCache\Microsoft VS Code\图标\合成\16X16.png",
        @"D:\SoftwareCache\Microsoft VS Code\图标\合成\32X32.png",
        @"D:\SoftwareCache\Microsoft VS Code\图标\合成\48X48.png"
    };
    IcoConverter.SaveAsLegacyIco(Paths,@"D:\SoftwareCache\Microsoft VS Code\图标\合成\test.ico");
}
//bmp格式写入
public static class IcoConverter
{
    // ICO文件头结构 [[2]]
    [StructLayout(LayoutKind.Sequential)]
    private struct IconDir
    {
        public ushort Reserved;
        public ushort Type;
        public ushort Count;
    }

    // ICO目录项结构 [[2]]
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct IconDirEntry
    {
        public byte Width;
        public byte Height;
        public byte ColorCount;
        public byte Reserved;
        public ushort Planes;
        public ushort BitCount;
        public uint BytesInRes;
        public uint ImageOffset;
    }

    /// <summary>
    /// 将多个BMP合并为多帧ICO文件
    /// </summary>
    public static void SaveAsLegacyIco(IEnumerable<string> bitmapPaths, string outputPath)
    {
        var entries = new List<IconDirEntry>();
        var imageDataList = new List<byte[]>();
        uint currentOffset = 6 + (uint)(16 * bitmapPaths.Count()); // 头部长度 [[2]]

        // 生成所有图标的BMP数据
        foreach (string bmpPath in bitmapPaths)
        {    
            using (Bitmap bmp = new Bitmap(bmpPath)) //保证使用后即释放资源
            {
                var data = GetBitmapData(bmp);
                var entry = new IconDirEntry
                {
                    Width = (byte)(bmp.Width >= 256 ? 0 : bmp.Width),
                    Height = (byte)(bmp.Height >= 256 ? 0 : bmp.Height),
                    ColorCount = 0,
                    Planes = 1,
                    BitCount = 32,
                    BytesInRes = (uint)data.Length,
                    ImageOffset = currentOffset
                };
                
                entries.Add(entry);
                imageDataList.Add(data);
                currentOffset += (uint)data.Length;
            }
        }

        // 写入文件
        using (var fs = new FileStream(outputPath, FileMode.Create))
        using (var writer = new BinaryWriter(fs))
        {
            // 写入ICO头部 [[2]]
            writer.Write((ushort)0); // Reserved
            writer.Write((ushort)1); // Type=ICO
            writer.Write((ushort)entries.Count); // Image count

            // 写入目录项
            foreach (var entry in entries)
            {
                writer.Write(entry.Width);
                writer.Write(entry.Height);
                writer.Write(entry.ColorCount);
                writer.Write(entry.Reserved);
                writer.Write(entry.Planes);
                writer.Write(entry.BitCount);
                writer.Write(entry.BytesInRes);
                writer.Write(entry.ImageOffset);
            }

            // 写入图像数据 [[11]]
            foreach (var data in imageDataList)
            {
                writer.Write(data);
            }
        }
    }

    /// <summary>
    /// 将Bitmap转为ICO兼容的BMP格式数据
    /// </summary>
    private static byte[] GetBitmapData(Bitmap bmp)
    {
        var formattedBmp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format32bppArgb);
        using (var g = Graphics.FromImage(formattedBmp))
        {
            g.DrawImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
        }
    
        var bmpData = formattedBmp.LockBits(
            new Rectangle(0, 0, formattedBmp.Width, formattedBmp.Height),
            ImageLockMode.ReadOnly,
            formattedBmp.PixelFormat);
    
        try
        {
            int width = bmp.Width;
            int height = bmp.Height;
            int stride = bmpData.Stride;
    
            // BMP信息头
            var infoHeader = new byte[40];
            using (var ms = new MemoryStream(infoHeader))
            using (var writer = new BinaryWriter(ms))
            {
                writer.Write(40);               // 信息头大小
                writer.Write(width);            // 宽度
                writer.Write(height * 2);       // 高度(颜色+掩码)
                writer.Write((ushort)1);        // 位面数
                writer.Write((ushort)32);       // 位深
                writer.Write(0);                // 压缩方式(无压缩)
                writer.Write(stride * height + ((width + 31) / 32 * 4) * height); // 图像数据大小
                writer.Write(0);                // 水平分辨率
                writer.Write(0);                // 垂直分辨率
                writer.Write(0);                // 调色板颜色数
                writer.Write(0);                // 重要颜色数
            }
    
            // 计算掩码参数
            int maskStride = (width + 31) / 32 * 4;
            int maskDataSize = maskStride * height;
            int colorDataSize = stride * height;
            var buffer = new byte[infoHeader.Length + colorDataSize + maskDataSize];
    
            // 复制信息头
            Buffer.BlockCopy(infoHeader, 0, buffer, 0, infoHeader.Length);
    
            // 复制颜色数据(逆序行)
            byte[] colorData = new byte[colorDataSize];
            Marshal.Copy(bmpData.Scan0, colorData, 0, colorDataSize);
            int colorOffset = infoHeader.Length;
            for (int y = 0; y < height; y++)
            {
                int srcIndex = y * stride;
                int destIndex = colorOffset + (height - 1 - y) * stride;
                Buffer.BlockCopy(colorData, srcIndex, buffer, destIndex, stride);
            }
    
            // 生成并复制掩码数据
            byte[] maskData = GenerateMaskData(colorData, width, height, stride, maskStride);
            Buffer.BlockCopy(maskData, 0, buffer, infoHeader.Length + colorDataSize, maskDataSize);
    
            return buffer;
        }
        finally
        {
            formattedBmp.UnlockBits(bmpData);
            formattedBmp.Dispose();
        }
    }
    /// <summary>
    /// 生成AND掩码位图(1位/像素)
    /// </summary>
    private static byte[] GenerateMaskData(byte[] colorData, int width, int height, int colorStride, int maskStride)
    {
        byte[] maskData = new byte[maskStride * height];
    
        for (int y = 0; y < height; y++)
        {
            int srcY = y; // 原图的行   
            byte[] maskRow = new byte[maskStride];
            int bitPos = 7;
            int byteIndex = 0;
    
            for (int x = 0; x < width; x++)
            {
                int pixelIndex = srcY * colorStride + x * 4;
                byte alpha = colorData[pixelIndex + 3]; // Alpha通道
    
                if (alpha == 0)
                    maskRow[byteIndex] |= (byte)(1 << bitPos);
    
                if (--bitPos < 0)
                {
                    bitPos = 7;
                    byteIndex++;
                }
            }
    
            // 掩码行逆序存储
            int destY = height - 1 - y;
            Buffer.BlockCopy(maskRow, 0, maskData, destY * maskStride, maskStride);
        }
    
        return maskData;
    }
}

生成的ICO在VS的效果显示

来源链接:https://www.cnblogs.com/MingKongLing/p/18717069

© 版权声明
THE END
支持一下吧
点赞9 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容