261 lines
7.4 KiB
C++
261 lines
7.4 KiB
C++
/* Copyright 2018 Dirk Steinke
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License. */
|
|
|
|
#ifndef ARITHMETIC_CODER_H
|
|
#define ARITHMETIC_CODER_H
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "bitstream.h"
|
|
#include "const_division.h"
|
|
|
|
class ArithmeticCodecBase {
|
|
public:
|
|
ArithmeticCodecBase();
|
|
|
|
// array for fast check if normalization is required
|
|
static const uint8_t _normCheckLUT[8];
|
|
bool _needsNormalization() const {
|
|
return (_normCheckLUT[_low >> 29] & (1 << (_high >> 29))) != 0;
|
|
}
|
|
// arithmetic coding variables
|
|
uint32_t _low;
|
|
uint32_t _high;
|
|
};
|
|
|
|
class ArithmeticEncoder : public ArithmeticCodecBase {
|
|
public:
|
|
ArithmeticEncoder(BitOutputStream& bos);
|
|
void flush();
|
|
void encode(const uint32_t scale, const uint32_t low, const uint32_t high) {
|
|
// update steps, low count, high count
|
|
uint32_t step = ((_high - _low) + 1) / scale;
|
|
_high = _low + step * high - 1;
|
|
_low += step * low;
|
|
_checkNormalize();
|
|
}
|
|
void encodeShiftScale(const uint32_t shift, const uint32_t low, const uint32_t high) {
|
|
// update steps, low count, high count
|
|
uint32_t step = ((_high - _low) + 1) >> shift;
|
|
_high = _low + step * high - 1;
|
|
_low += step * low;
|
|
_checkNormalize();
|
|
}
|
|
void encode(const udivider_t<32>& scale, const uint32_t low, const uint32_t high) {
|
|
// update steps, low count, high count
|
|
uint32_t step = divide((_high - _low) + 1, scale);
|
|
_high = _low + step * high - 1;
|
|
_low += step * low;
|
|
_checkNormalize();
|
|
}
|
|
void encodeBits(const uint32_t value, const uint32_t bits) {
|
|
uint32_t step = ((_high - _low) + 1) >> bits;
|
|
_low += step * value;
|
|
_high = _low + step - 1;
|
|
_normalize();
|
|
}
|
|
|
|
private:
|
|
void _checkNormalize() {
|
|
if (_needsNormalization()) {
|
|
_normalize();
|
|
}
|
|
}
|
|
void _normalize();
|
|
void _writeE3(const unsigned w);
|
|
|
|
BitOutputStream& _bos;
|
|
|
|
// arithmetic coding variables
|
|
uint32_t _e3cnt;
|
|
};
|
|
|
|
class ArithmeticDecoder : public ArithmeticCodecBase {
|
|
public:
|
|
ArithmeticDecoder(BitInputStream& bis);
|
|
unsigned decode(const uint32_t scale, const unsigned bounds[], const unsigned N) {
|
|
uint32_t step = ((_high - _low) + 1) / scale;
|
|
return _decode(step, bounds, N);
|
|
}
|
|
unsigned decodeShiftScale(const uint32_t shift, const unsigned bounds[], const unsigned N) {
|
|
uint32_t step = ((_high - _low) + 1) >> shift;
|
|
return _decode(step, bounds, N);
|
|
}
|
|
unsigned decode(const udivider_t<32>& scale, const unsigned bounds[], const unsigned N) {
|
|
uint32_t step = divide((_high - _low) + 1, scale);
|
|
return _decode(step, bounds, N);
|
|
}
|
|
|
|
unsigned decodeBinary(const uint32_t scale, const unsigned bounds[]) {
|
|
uint32_t step = ((_high - _low) + 1) / scale;
|
|
return _decodeBinary(step, bounds);
|
|
}
|
|
unsigned decodeBinaryShiftScale(const uint32_t shift, const unsigned bounds[]) {
|
|
uint32_t step = ((_high - _low) + 1) >> shift;
|
|
return _decodeBinary(step, bounds);
|
|
}
|
|
unsigned decodeBinary(const udivider_t<32>& scale, const unsigned bounds[]) {
|
|
uint32_t step = divide((_high - _low) + 1, scale);
|
|
return _decodeBinary(step, bounds);
|
|
}
|
|
|
|
unsigned decodeBits(const uint32_t bits) {
|
|
uint32_t step = ((_high - _low) + 1) >> bits;
|
|
unsigned result = (_value - _low) / step;
|
|
_low += step * result;
|
|
_high = _low + step - 1;
|
|
_normalize();
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
unsigned _findIndex(const unsigned bounds[],
|
|
const unsigned N,
|
|
const unsigned val) {
|
|
for (unsigned i = N; i > 1; --i) {
|
|
if (val >= bounds[i - 1]) {
|
|
return i - 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned _decode(const uint32_t step, const unsigned bounds[], const unsigned N) {
|
|
uint32_t val = (_value - _low) / step;
|
|
unsigned result = _findIndex(bounds, N, val);
|
|
_high = _low + step * bounds[result + 1] - 1;
|
|
_low += step * bounds[result];
|
|
_checkNormalize();
|
|
return result;
|
|
}
|
|
unsigned _decodeBinary(const uint32_t step, const unsigned bounds[]) {
|
|
unsigned result = (_value >= _low + bounds[1] * step);
|
|
_high = _low + step * bounds[result + 1] - 1;
|
|
_low += step * bounds[result];
|
|
_checkNormalize();
|
|
return result;
|
|
}
|
|
void _checkNormalize() {
|
|
if (_needsNormalization()) {
|
|
_normalize();
|
|
}
|
|
}
|
|
void _normalize();
|
|
|
|
BitInputStream& _bis;
|
|
|
|
// arithmetic coding variables
|
|
uint32_t _value;
|
|
};
|
|
|
|
bool modelCheckFixed(unsigned bounds[], unsigned short ids[], unsigned short rids[],
|
|
const unsigned N);
|
|
void modelSortBounds(unsigned bounds[], unsigned short ids[], unsigned short rids[],
|
|
unsigned backup[], const unsigned N);
|
|
void modelRecreateBounds(unsigned bounds[], const unsigned N);
|
|
|
|
template <unsigned N>
|
|
struct ACModelBase {
|
|
static const unsigned L = N;
|
|
bool isEqualTo(const ACModelBase& m) const {
|
|
for (unsigned i = 0; i < N; ++i) {
|
|
if (bounds[i] != m.bounds[i]) {
|
|
return false;
|
|
}
|
|
if (bounds[i + 1] > 0 && ids[i] != m.ids[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
if (bounds[N] != m.bounds[N]) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
unsigned bounds[N + 1];
|
|
unsigned short ids[N], rids[N];
|
|
bool _fixed;
|
|
};
|
|
|
|
struct ACFixedScaleBinaryModel : public ACModelBase<2> {
|
|
ACFixedScaleBinaryModel() {}
|
|
ACFixedScaleBinaryModel(const unsigned(&arr)[2]) {
|
|
memcpy(this->bounds, arr, sizeof(arr));
|
|
build();
|
|
}
|
|
void build();
|
|
void encode(ArithmeticEncoder* encoder, const unsigned item) {
|
|
if (!this->_fixed) {
|
|
unsigned pos = this->rids[item];
|
|
encoder->encodeShiftScale(16, this->bounds[pos], this->bounds[pos + 1]);
|
|
}
|
|
}
|
|
#if 0
|
|
unsigned decode(aricoder* codec) {
|
|
symbol s;
|
|
s.scale = 1 << 16;
|
|
unsigned cnt = codec->decode_count(&s);
|
|
for (unsigned i = 0; i < N; ++i) {
|
|
if (cnt < bounds[i + 1]) {
|
|
s.low_count = bounds[i];
|
|
s.high_count = bounds[i + 1];
|
|
codec->decode(&s);
|
|
return ids[i];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
template <unsigned N>
|
|
struct ACFixedScaleModel : public ACModelBase<N> {
|
|
ACFixedScaleModel() {}
|
|
ACFixedScaleModel(const unsigned(&arr)[N]) {
|
|
memcpy(this->bounds, arr, sizeof(arr));
|
|
build();
|
|
}
|
|
void build() {
|
|
unsigned backup[N];
|
|
if (!(this->_fixed = modelCheckFixed(this->bounds, this->ids, this->rids, N))) {
|
|
modelSortBounds(this->bounds, this->ids, this->rids, backup, N);
|
|
modelRecreateBounds(this->bounds, N);
|
|
}
|
|
}
|
|
void encode(ArithmeticEncoder* encoder, const unsigned item) {
|
|
if (!this->_fixed) {
|
|
unsigned pos =this->rids[item];
|
|
encoder->encodeShiftScale(16, this->bounds[pos], this->bounds[pos + 1]);
|
|
}
|
|
}
|
|
#if 0
|
|
unsigned decode(aricoder* codec) {
|
|
symbol s;
|
|
s.scale = 1 << 16;
|
|
unsigned cnt = codec->decode_count(&s);
|
|
for (unsigned i = 0; i < N; ++i) {
|
|
if (cnt < bounds[i + 1]) {
|
|
s.low_count = bounds[i];
|
|
s.high_count = bounds[i + 1];
|
|
codec->decode(&s);
|
|
return this->ids[i];
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
#endif /* ARITHMETIC_CODER_H */
|