-->

在PNG保存8位索引数据改变使用的BitmapSource当原始字节,PngBitmapEncode

2019-06-27 22:32发布

保存和加载使用的BitmapSource和PngBitmapEncoder器/解码器PNG时,我有一个问题。 基本上,我希望能够保存一个最初的字节数组的图像,当PNG加载到我的程序,重新加载完全相同的字节。 原始数据的保存是很重要的。

同时,我想使用自定义调色板(256个色索引数组)的PNG。

我想救我的8位数据与自定义索引的调色板。 原始数据的范围可以从0到255。 调色板可以是“阈值化”调色板(例如0-20是颜色#1,21-50是彩色#2,等)。

什么我发现是,当我保存数据,重装,并做CopyPixels检索“原始”数据,数据值是基于调色板,而不是原来的字节数组值设定。

有没有办法保存PNG中的原始字节数组, 不失定制调色板? 还是有不同的方式来检索的BitmapSource字节数组?

以下是我保存例行:

      // This gets me a custom palette that is an array of 256 colors
      List<System.Windows.Media.Color> colors = PaletteToolsWPF.TranslatePalette(this, false, true);
      BitmapPalette myPalette = new BitmapPalette(colors);

      // This retrieves my byte data as an array of dimensions _stride * sizeY
      byte[] ldata = GetData();

      BitmapSource image = BitmapSource.Create(
        sizeX,
        sizeY,
        96,
        96,
        PixelFormats.Indexed8,
        myPalette,
        ldata,
        _stride);

      PngBitmapEncoder enc = new PngBitmapEncoder();
      enc.Interlace = PngInterlaceOption.On;
      enc.Frames.Add(BitmapFrame.Create(image));

      // save the data via FileStream
      enc.Save(fs);

这是我的负荷常规:

    // Create an array to hold the raw data
    localData = new byte[_stride * sizeY];

    // Load the data via a FileStream
    PngBitmapDecoder pd = new PngBitmapDecoder(Fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    BitmapSource bitmapSource = pd.Frames[0];    

    // When I look at the byte data, it is *not* the same as my original data 
    bitmapSource.CopyPixels(localData, _stride, 0);

任何建议,将不胜感激。 谢谢。

附录1:我发现了问题的一部分是,PNG被保存为32位色,尽管我将其设置为Indexed8并使用256项颜色的调色板。 这也似乎取决于所设置的调色板。 任何想法,为什么?

Answer 1:

我知道为什么你的PNG文件被保存在高显色性。 事实上,他们不是 “保存为高色8位图像”; 问题是,从具有透明度的那一刻,他们正在加载由.NET Framework高色。 这些图像本身是完全正常的,它只是搞乱真实起来的框架。

造成这种情况的解决办法是张贴在这里:

答:加载一个索引的彩色图像文件正确

作为WIP说 ,不过,如果你想保留原始字节,调色板的实际变化应该使用分区块完成,而不是通过净图形类,因为重新编码的.Net必然会改变字节。 而且,有趣的是,对于调色板问题,我只是给了修复已经包含你所需要的代码的一半,即块读取代码。

该块代码将是这样的:

/// <summary>
/// Writes a png data chunk.
/// </summary>
/// <param name="target">Target array to write into.</param>
/// <param name="offset">Offset in the array to write the data to.</param>
/// <param name="chunkName">4-character chunk name.</param>
/// <param name="chunkData">Data to write into the new chunk.</param>
/// <returns>The new offset after writing the new chunk. Always equal to the offset plus the length of chunk data plus 12.</returns>
private static Int32 WritePngChunk(Byte[] target, Int32 offset, String chunkName, Byte[] chunkData)
{
    if (offset + chunkData.Length + 12 > target.Length)
        throw new ArgumentException("Data does not fit in target array!", "chunkData");
    if (chunkName.Length != 4)
        throw new ArgumentException("Chunk must be 4 characters!", "chunkName");
    Byte[] chunkNamebytes = Encoding.ASCII.GetBytes(chunkName);
    if (chunkNamebytes.Length != 4)
        throw new ArgumentException("Chunk must be 4 bytes!", "chunkName");
    Int32 curLength;
    ArrayUtils.WriteIntToByteArray(target, offset, curLength = 4, false, (UInt32)chunkData.Length);
    offset += curLength;
    Int32 nameOffset = offset;
    Array.Copy(chunkNamebytes, 0, target, offset, curLength = 4);
    offset += curLength;
    Array.Copy(chunkData, 0, target, offset, curLength = chunkData.Length);
    offset += curLength;
    UInt32 crcval = Crc32.ComputeChecksum(target, nameOffset, chunkData.Length + 4);
    ArrayUtils.WriteIntToByteArray(target, offset, curLength = 4, false, crcval);
    offset += curLength;
    return offset;
}

Crc32.ComputeChecksum我使用的功能是一个在阵列适应的理智自由编码实现CRC 。 它不应该是坚硬以使其适应于给定的阵列内的可变的开始和长度。

字节写入类ArrayUtils是一个工具集我提出读取和从与指定的字节顺序阵列值写入/。 在此对SO张贴在结束这个答案 。



文章来源: Saving 8-bit indexed data in a PNG alters original bytes when using BitmapSource, PngBitmapEncoder/Decoder