[英]What is the easiest way to read different length bit fields in a byte array?
I'm trying to read a binary file (a image format) that uses different sized bit fields to refer to different colors, doing so with a dictionary for a color palette. 我正在尝试读取使用不同大小的位字段来引用不同颜色的二进制文件(图像格式),使用颜色调色板的字典来执行此操作。 Eg 例如
Using the following palette: 使用以下调色板:
0 -> #FFFFFF
10 -> #FF0000
110 -> #FF00DC
111 -> #FF5A0C
The binary data looks like this 二进制数据看起来像这样
0101101110101100010111
The problem is that when a read this file I get a byte[]
and I don't know how to handle these variable length fields with bytes. 问题是,当读取此文件时,我得到一个byte[]
,我不知道如何处理这些带字节的可变长度字段。 The main issue is (using the exemple above) when reading byte[0]
I get 01011011
, with this I'm able to convert part of the data to #FFFFFF
, #FF0000
, #FF00DC
but I am left with 00000011
. 主要问题是(使用上面的例子)当读取byte[0]
我得到01011011
,这样我就可以将部分数据转换为#FFFFFF
, #FF0000
, #FF00DC
但我留下了00000011
。
So, the question is, how could I concatenate what is left of this byte with the next one so that I could be able to read the full code. 所以,问题是,我怎么能连接这个字节的剩余部分和下一个字节,以便我能够读取完整的代码。 Eg 例如
00000011 + 10101100 = 0000001110101100
And read this normally. 并正常阅读。
Obs: I'm using c# Obs:我正在使用c#
Edit: this is a format that I'm developing for lossless image compression 编辑:这是我正在为无损图像压缩开发的格式
Here is a sample bit reader. 这是一个示例位读取器。 This is not too efficient because I am returning the read bits in the lowest bit position, and then shifting to accumulate the next field. 这不是太有效,因为我在最低位位置返回读取位,然后移位以累积下一个字段。
First, a class that tracks the bit and byte position in a byte[]
and returns the next bit. 首先,跟踪byte[]
的位和字节位置并返回下一位的类。
public class BitPosition {
int bytePos = 0;
int bitPos = 0; // 0 - 7 only
Byte[] data;
public BitPosition(Byte[] src) => data = src;
static byte[] byteBitMasks = new byte[] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
public int ReadNextBit() {
if (bytePos >= data.Length)
throw new IndexOutOfRangeException("ReadNextBit");
int bit = (data[bytePos] & byteBitMasks[bitPos]) == 0 ? 0 : 1;
if (++bitPos > 7) {
bitPos = 0;
++bytePos;
}
return bit;
}
public bool HasMoreData => bytePos < data.Length;
}
Now, a class to describe each entry in the compressed color palette: 现在,一个类来描述压缩调色板中的每个条目:
public class ColorEntry {
public byte marker;
public int color;
public int sizeInBits;
}
Note: If you need larger markers, you can replace byte
with int
or uint
. 注意:如果需要更大的标记,可以用int
或uint
替换byte
。 If you need up to 64-bit markers, you will need to modify the ColorReader
to use uint64
. 如果您需要最多64位标记,则需要修改ColorReader
以使用uint64
。
And finally, a ColorReader
class that reads the colors from a byte[]
using a compressed palette and the BitPosition
class: 最后,一个ColorReader
类使用压缩调色板和BitPosition
类从byte[]
读取颜色:
public class ColorReader {
BitPosition bp;
public ColorReader(byte[] data) => bp = new BitPosition(data);
static ColorEntry[] palette = new[] {
new ColorEntry { marker = 0b0, color = 0xFFFFFF, sizeInBits = 1 },
new ColorEntry { marker = 0b10, color = 0xFF0000, sizeInBits = 2 },
new ColorEntry { marker = 0b110, color = 0xFF00DC, sizeInBits = 3 },
new ColorEntry { marker = 0b111, color = 0xFF5A0C, sizeInBits = 3 },
};
public IEnumerable<ColorEntry> Colors() {
while (bp.HasMoreData) {
int bitsSoFar = 0;
int numBits = 0;
do {
int nextBit = bp.ReadNextBit();
++numBits;
bitsSoFar |= nextBit;
var nextCE = palette.FirstOrDefault(ce => ce.sizeInBits == numBits && ce.marker == bitsSoFar);
if (nextCE != null) {
yield return nextCE;
break;
}
else
bitsSoFar <<= 1;
} while (true);
}
}
}
You can use the class like so: 您可以像这样使用类:
var data = new byte[] { 0b01011011, 0b10101100, 0b01011100 };
var cr = new ColorReader(data);
var ans = cr.Colors().Select(c => c.color).ToList();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.