C#如何将24位深度GIF图片转换为8位深度并解决索引像素格式异常
问题根因
Graphics类仅支持非索引像素格式的图像操作,Format8bppIndexed属于索引像素格式,自带256色调色板,因此无法直接调用DrawImage绘制,会抛出对应异常。- .NET原生没有提供
Format8bppRgb枚举,8位深度的非透明图像均通过索引格式+调色板实现。
实现代码
你需要通过锁位直接操作像素数据的方式完成转换,以下是可直接运行的实现:
using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; namespace Gif24BitTo8Bit { class Program { static void Main(string[] args) { var test = Directory.GetFiles(@"D:\Downloaded Images\New folder", "*.gif", SearchOption.AllDirectories).OrderBy(x => x).ToArray(); for (int i = 0; i < test.Length; i++) { using Bitmap orig = new Bitmap(test[i]); // 先把原图统一为24位RGB格式,保证颜色采样正常 using Bitmap orig24Bpp = new Bitmap(orig.Width, orig.Height, PixelFormat.Format24bppRgb); using (Graphics g = Graphics.FromImage(orig24Bpp)) { g.DrawImage(orig, new Rectangle(0, 0, orig24Bpp.Width, orig24Bpp.Height)); } // 创建8位索引格式目标位图 Bitmap target8Bpp = new Bitmap(orig24Bpp.Width, orig24Bpp.Height, PixelFormat.Format8bppIndexed); // 生成调色板,这里示例是灰度调色板,如需保留原图彩色可替换为八叉树量化生成的专属调色板 ColorPalette palette = target8Bpp.Palette; for (int c = 0; c < 256; c++) { palette.Entries[c] = Color.FromArgb(255, c, c, c); } target8Bpp.Palette = palette; // 锁位操作像素数据 BitmapData origData = orig24Bpp.LockBits(new Rectangle(0, 0, orig24Bpp.Width, orig24Bpp.Height), ImageLockMode.ReadOnly, orig24Bpp.PixelFormat); BitmapData targetData = target8Bpp.LockBits(new Rectangle(0, 0, target8Bpp.Width, target8Bpp.Height), ImageLockMode.WriteOnly, target8Bpp.PixelFormat); unsafe { byte* origPtr = (byte*)origData.Scan0; byte* targetPtr = (byte*)targetData.Scan0; int origStride = origData.Stride; int targetStride = targetData.Stride; for (int y = 0; y < orig24Bpp.Height; y++) { for (int x = 0; x < orig24Bpp.Width; x++) { // 24位RGB像素存储顺序为BGR byte b = origPtr[y * origStride + x * 3]; byte g = origPtr[y * origStride + x * 3 + 1]; byte r = origPtr[y * origStride + x * 3 + 2]; // 计算灰度值映射到调色板索引,彩色场景替换为查找最近调色板颜色的逻辑即可 byte grayVal = (byte)(r * 0.299 + g * 0.587 + b * 0.114); targetPtr[y * targetStride + x] = grayVal; } } } orig24Bpp.UnlockBits(origData); target8Bpp.UnlockBits(targetData); // 保存转换后的文件 string savePath = Path.Combine(Path.GetDirectoryName(test[i]), $"{Path.GetFileNameWithoutExtension(test[i])}_8bit.gif"); target8Bpp.Save(savePath, ImageFormat.Gif); target8Bpp.Dispose(); } } } }
注意:代码使用了不安全代码块,需要在项目属性→生成设置中勾选「允许不安全代码」才可正常编译。
若需要保留原图彩色效果,只需替换调色板生成逻辑和索引匹配逻辑为八叉树颜色量化的实现即可。
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

