Какой самый простой способ прочитать битовые поля различной длины в байтовом массиве? - PullRequest
0 голосов
/ 24 мая 2019

Я пытаюсь прочитать двоичный файл (формат изображения), в котором используются битовые поля разного размера, чтобы ссылаться на разные цвета, делая это со словарем для цветовой палитры.Например,

Использование следующей палитры:

0   -> #FFFFFF
10  -> #FF0000  
110 -> #FF00DC  
111 -> #FF5A0C

Двоичные данные выглядят следующим образом

0101101110101100010111

Проблемаявляется то, что при чтении этого файла я получаю byte[], и я не знаю, как обрабатывать эти поля переменной длины с байтами.Основная проблема (с использованием приведенного выше примера) при чтении byte[0] я получаю 01011011, с этим я могу преобразовать часть данных в #FFFFFF, #FF0000, #FF00DC, но у меня остается00000011.

Итак, вопрос в том, как я могу объединить то, что осталось от этого байта, со следующим, чтобы я мог прочитать полный код.Например,

00000011 + 10101100 = 0000001110101100

И читать это нормально.

Obs: я использую c #

Edit: это формат, который я разрабатываю для сжатия изображений без потерь

1 Ответ

0 голосов
/ 25 мая 2019

Вот пример считывателя битов.Это не слишком эффективно, потому что я возвращаю считанные биты в младшей битовой позиции, а затем сдвигаюсь, чтобы накапливать следующее поле.

Сначала класс, который отслеживает битовую и байтовую позицию в 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;
}

Теперь класс для описания каждой записи в сжатой цветовой палитре:

public class ColorEntry {
    public byte marker;
    public int color;
    public int sizeInBits;
}

Примечание. Если вам нужны маркеры большего размера, вы можете заменить byte с int или uint.Если вам нужны до 64-битных маркеров, вам нужно изменить ColorReader, чтобы использовать uint64.

И, наконец, класс ColorReader, который считывает цвета из byte[], используясжатая палитра и класс BitPosition:

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);
        }
    }
}

Вы можете использовать класс следующим образом:

var data = new byte[] { 0b01011011, 0b10101100, 0b01011100 };
var cr = new ColorReader(data);
var ans = cr.Colors().Select(c => c.color).ToList();
...