You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#预设颜色匹配生成PNG异常:色块/空图问题排查

问题:PNG图像格式转换后不符合预期(色块/空白)

需求说明

  • 现有资源:
    • 文本文件:存储RGB颜色值及对应参数的列表
    • 原图:940x940分辨率、8位深度的PNG图像,所有像素颜色均来自上述文本列表
  • 目标:生成同尺寸、32位深度的PNG图像,仅保留原图中与列表匹配的颜色(因原图颜色全部符合列表,最终新图需与原图完全一致)

问题现象

  1. 初始版本代码:生成的新图仅为零散颜色色块,完全无法还原原图内容
  2. 修改后代码:生成的新图为空白状态——磁盘中显示为黑色文件,用画图打开则是白色空图

代码片段

初始版本ProcessImage类

public class ProcessImage
{
    private List<Color> _colorList;

    public ProcessImage(string colorFilePath)
    {
        _colorList = LoadColorsFromFile(colorFilePath);
    }

    private List<Color> LoadColorsFromFile(string path)
    {
        var colors = new List<Color>();
        // 读取文本文件并解析为Color对象的逻辑
        return colors;
    }

    public Bitmap ConvertTo32Bit(string sourcePath)
    {
        var bmp = new Bitmap(940, 940, PixelFormat.Format32bppArgb);
        using (var g = Graphics.FromImage(bmp))
        {
            // 错误逻辑:直接按颜色列表绘制色块,未遍历原图像素
            for (int i = 0; i < _colorList.Count; i++)
            {
                var brush = new SolidBrush(_colorList[i]);
                g.FillRectangle(brush, i*50, 0, 50, 50);
            }
        }
        return bmp;
    }
}

修改后ProcessImage类

public class ProcessImage
{
    private List<Color> _colorList;

    public ProcessImage(string colorFilePath)
    {
        _colorList = LoadColorsFromFile(colorFilePath);
    }

    private List<Color> LoadColorsFromFile(string path)
    {
        var colors = new List<Color>();
        // 读取文本文件解析Color逻辑不变
        return colors;
    }

    public Bitmap ConvertTo32Bit(string sourcePath)
    {
        var sourceBmp = new Bitmap(sourcePath);
        var targetBmp = new Bitmap(sourceBmp.Width, sourceBmp.Height, PixelFormat.Format32bppArgb);

        for (int y = 0; y < sourceBmp.Height; y++)
        {
            for (int x = 0; x < sourceBmp.Width; x++)
            {
                var pixelColor = sourceBmp.GetPixel(x, y);
                // 错误逻辑:颜色匹配失败,导致无像素被绘制
                if (_colorList.Any(c => c.R == pixelColor.R && c.G == pixelColor.G && c.B == pixelColor.B))
                {
                    targetBmp.SetPixel(x, y, pixelColor);
                }
                // 未匹配时未设置默认值,或Alpha通道未处理
            }
        }
        sourceBmp.Dispose();
        return targetBmp;
    }
}

Form1调用代码

private void btnConvert_Click(object sender, EventArgs e)
{
    var processor = new ProcessImage("colors.txt");
    var result = processor.ConvertTo32Bit("source.png");
    result.Save("output.png", ImageFormat.Png);
    result.Dispose();
    MessageBox.Show("转换完成");
}

问题原因与解决方法

初始版本问题根源

代码逻辑完全偏离需求:没有遍历原图的每个像素进行颜色映射,而是直接根据颜色列表绘制固定大小的色块,完全忽略了原图的像素分布,因此生成的是色块而非原图内容。

修改后空白问题根源

  1. 颜色匹配逻辑失效:8位PNG依赖调色板存储颜色,GetPixel返回的Color对象可能包含调色板索引信息,若文本列表中的Color未统一处理Alpha值(比如默认Alpha为0),会导致RGB分量对比失败,没有任何像素被写入目标Bitmap。
  2. Alpha通道未初始化:32位PNG的默认像素Alpha值为0(完全透明),若仅在匹配成功时设置像素,未匹配时未设置不透明的默认值(但需求中原图所有颜色都符合列表,实际是匹配失败导致全透明)。
  3. 8位PNG调色板处理缺失:直接用GetPixel获取RGB存在精度问题,应先获取原图的调色板,通过索引映射颜色更准确。

修正方案

1. 正确读取文本列表的颜色

确保解析出的Color对象为不透明(Alpha=255),仅保留RGB分量:

private List<Color> LoadColorsFromFile(string path)
{
    var colors = new List<Color>();
    foreach (var line in File.ReadAllLines(path))
    {
        var parts = line.Split(',');
        if (parts.Length >= 3)
        {
            byte r = byte.Parse(parts[0]);
            byte g = byte.Parse(parts[1]);
            byte b = byte.Parse(parts[2]);
            colors.Add(Color.FromArgb(255, r, g, b)); // 强制设置Alpha为255
        }
    }
    return colors;
}

2. 正确处理8位PNG的调色板与像素遍历

使用LockBits提升性能,同时直接处理调色板索引(比GetPixel/SetPixel高效且准确):

public Bitmap ConvertTo32Bit(string sourcePath)
{
    using (var sourceBmp = new Bitmap(sourcePath))
    {
        var targetBmp = new Bitmap(sourceBmp.Width, sourceBmp.Height, PixelFormat.Format32bppArgb);
        var sourceData = sourceBmp.LockBits(new Rectangle(0, 0, sourceBmp.Width, sourceBmp.Height), ImageLockMode.ReadOnly, sourceBmp.PixelFormat);
        var targetData = targetBmp.LockBits(new Rectangle(0, 0, targetBmp.Width, targetBmp.Height), ImageLockMode.WriteOnly, targetBmp.PixelFormat);

        // 获取8位图像的调色板
        ColorPalette palette = sourceBmp.Palette;
        int sourceStride = sourceData.Stride;
        int targetStride = targetData.Stride;
        IntPtr sourcePtr = sourceData.Scan0;
        IntPtr targetPtr = targetData.Scan0;

        unsafe
        {
            byte* sourceBytes = (byte*)sourcePtr;
            int* targetBytes = (int*)targetPtr;

            for (int y = 0; y < sourceBmp.Height; y++)
            {
                for (int x = 0; x < sourceBmp.Width; x++)
                {
                    // 获取调色板索引对应的颜色
                    byte colorIndex = sourceBytes[y * sourceStride + x];
                    Color sourceColor = palette.Entries[colorIndex];

                    // 匹配文本列表中的颜色(需求中原图颜色都符合,可直接映射)
                    if (_colorList.Any(c => c.R == sourceColor.R && c.G == sourceColor.G && c.B == sourceColor.B))
                    {
                        // 转换为32位不透明ARGB
                        targetBytes[y * targetStride / 4 + x] = Color.FromArgb(255, sourceColor.R, sourceColor.G, sourceColor.B).ToArgb();
                    }
                    else
                    {
                        // 需求中原图无此情况,可设为透明
                        targetBytes[y * targetStride / 4 + x] = Color.FromArgb(0, 0, 0, 0).ToArgb();
                    }
                }
            }
        }

        sourceBmp.UnlockBits(sourceData);
        targetBmp.UnlockBits(targetData);
        return targetBmp;
    }
}

3. 调用时确保资源释放

使用using语句自动释放Bitmap资源,避免内存泄漏:

private void btnConvert_Click(object sender, EventArgs e)
{
    using (var processor = new ProcessImage("colors.txt"))
    using (var result = processor.ConvertTo32Bit("source.png"))
    {
        result.Save("output.png", ImageFormat.Png);
        MessageBox.Show("转换完成");
    }
}

内容的提问来源于stack exchange,提问作者Daniel Lip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 00:13:21