Project

General

Profile

« Previous | Next » 

Revision 277af910

Added by David Sorber about 8 years ago

Adding unifdef'ed files and CMake config to build.

View differences:

software/packJPG_library/lib_src/CMakeLists.txt
project("packJPG")
cmake_minimum_required(VERSION 3.2)
include_directories(.)
set(packjpg_sources
../aricoder.cpp
../bitops.cpp
../packjpg.cpp
)
add_definitions("-std=c++14 -O3 -Wall -pedantic")
add_definitions("-funroll-loops -ffast-math -fomit-frame-pointer")
add_definitions("-march=native")
add_library(packjpg SHARED
${packjpg_sources})
software/packJPG_library/lib_src/aricoder.cpp
#include "aricoder.h"
#include "bitops.h"
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <limits>
template <std::uint8_t bit>
void ArithmeticBitWriter::write_bit() {
// add bit at last position
curr_byte_ = (curr_byte_ << 1) | bit;
// increment bit position
curr_bit_++;
// write bit if done
if (curr_bit_ == 8) {
data_.emplace_back(curr_byte_);
curr_bit_ = 0;
}
}
void ArithmeticBitWriter::write_n_zero_bits(std::size_t n) {
if (n + curr_bit_ >= 8) {
auto remainingBits = 8 - curr_bit_;
n -= remainingBits;
curr_byte_ <<= remainingBits;
data_.emplace_back(curr_byte_);
curr_bit_ = 0;
}
while (n >= 8) {
data_.emplace_back(0);
n -= 8;
}
curr_byte_ <<= n;
curr_bit_ += n;
}
void ArithmeticBitWriter::write_n_one_bits(std::size_t n) {
constexpr std::uint8_t all_ones = std::numeric_limits<std::uint8_t>::max();
if (n + curr_bit_ >= 8) {
auto remainingBits = 8 - curr_bit_;
n -= remainingBits;
curr_byte_ <<= remainingBits;
curr_byte_ |= all_ones >> (8 - remainingBits);
data_.emplace_back(curr_byte_);
curr_bit_ = 0;
}
while (n >= 8) {
data_.emplace_back(all_ones);
n -= 8;
}
curr_byte_ = (curr_byte_ << n) | (all_ones >> (8 - n));
curr_bit_ += n;
}
void ArithmeticBitWriter::pad() {
while (curr_bit_ > 0) {
write_bit<0>();
}
}
std::vector<std::uint8_t> ArithmeticBitWriter::get_data() const {
return data_;
}
ArithmeticDecoder::ArithmeticDecoder(Reader& reader) : reader_(reader) {
// code buffer has to be filled before starting decoding
for (std::uint32_t i = 0; i < CODER_USE_BITS; i++ ) {
ccode = ( ccode << 1 ) | read_bit();
}
}
ArithmeticEncoder::ArithmeticEncoder(Writer& writer) : writer_(writer) {}
ArithmeticEncoder::~ArithmeticEncoder() {
if (!finalized) {
this->finalize();
}
}
void ArithmeticEncoder::finalize() {
if (finalized) {
return;
}
// due to clow < CODER_LIMIT050, and chigh >= CODER_LIMIT050
// there are only two possible cases
if (clow < CODER_LIMIT025) {
bitwriter_->write_bit<0>();
bitwriter_->write_bit<1>();
bitwriter_->write_n_one_bits(nrbits);
nrbits = 0;
} else {
// case b.), clow >= CODER_LIMIT025
bitwriter_->write_bit<1>();
}
// done, zeroes are auto-read by the decoder
bitwriter_->pad(); // Pad code with zeroes.
writer_.write(bitwriter_->get_data());
finalized = true;
}
/* -----------------------------------------------
arithmetic encoder function
----------------------------------------------- */
void ArithmeticEncoder::encode( symbol* s )
{
// Make local copies of clow_ and chigh_ for cache performance:
uint32_t clow_local = clow;
uint32_t chigh_local = chigh;
// update steps, low count, high count
cstep = (chigh_local - clow_local + 1) / s->scale;
chigh_local = clow_local + (cstep * s->high_count) - 1;
clow_local = clow_local + (cstep * s->low_count);
// e3 scaling is performed for speed and to avoid underflows
// if both, low and high are either in the lower half or in the higher half
// one bit can be safely shifted out
while ( clow_local >= CODER_LIMIT050 || chigh_local < CODER_LIMIT050 ) {
if (chigh_local < CODER_LIMIT050 ) { // this means both, high and low are below, and 0 can be safely shifted out
// write 0 bit
bitwriter_->write_bit<0>();
// shift out remaing e3 bits
bitwriter_->write_n_one_bits(nrbits);
nrbits = 0;
}
else { // if the first wasn't the case, it's clow >= CODER_LIMIT050
// write 1 bit
bitwriter_->write_bit<1>();
clow_local &= CODER_LIMIT050 - 1;
chigh_local &= CODER_LIMIT050 - 1;
// shift out remaing e3 bits
bitwriter_->write_n_zero_bits(nrbits);
nrbits = 0;
}
clow_local <<= 1;
chigh_local <<= 1;
chigh_local++;
}
// e3 scaling, to make sure that theres enough space between low and high
while ( (clow_local >= CODER_LIMIT025 ) && (chigh_local < CODER_LIMIT075 ) ) {
nrbits++;
clow_local &= CODER_LIMIT025 - 1;
chigh_local ^= CODER_LIMIT025 + CODER_LIMIT050;
// clow -= CODER_LIMIT025;
// chigh -= CODER_LIMIT025;
clow_local <<= 1;
chigh_local <<= 1;
chigh_local++;
}
clow = clow_local;
chigh = chigh_local;
}
/* -----------------------------------------------
arithmetic decoder get count function
----------------------------------------------- */
unsigned int ArithmeticDecoder::decode_count( symbol* s )
{
// update cstep, which is needed to remove the symbol from the stream later
cstep = ( ( chigh - clow ) + 1 ) / s->scale;
// return counts, needed to decode the symbol from the statistical model
return ( ccode - clow ) / cstep;
}
/* -----------------------------------------------
arithmetic decoder function
----------------------------------------------- */
void ArithmeticDecoder::decode( symbol* s )
{
// no actual decoding takes place, as this has to happen in the statistical model
// the symbol has to be removed from the stream, though
// alread have steps updated from decoder_count
// update low count and high count
uint32_t ccode_local = ccode;
uint32_t clow_local = clow;
uint32_t chigh_local = clow_local + (cstep * s->high_count) - 1;
clow_local = clow_local + (cstep * s->low_count);
// e3 scaling is performed for speed and to avoid underflows
// if both, low and high are either in the lower half or in the higher half
// one bit can be safely shifted out
while ( (clow_local >= CODER_LIMIT050 ) || (chigh_local < CODER_LIMIT050 ) ) {
if (clow_local >= CODER_LIMIT050 ) {
clow_local &= CODER_LIMIT050 - 1;
chigh_local &= CODER_LIMIT050 - 1;
ccode_local &= CODER_LIMIT050 - 1;
} // if the first wasn't the case, it's chigh < CODER_LIMIT050
clow_local <<= 1;
chigh_local <<= 1;
chigh_local++;
ccode_local <<= 1;
ccode_local |= read_bit();
}
// e3 scaling, to make sure that theres enough space between low and high
while ( (clow_local >= CODER_LIMIT025 ) && (chigh_local < CODER_LIMIT075 ) ) {
clow_local &= CODER_LIMIT025 - 1;
chigh_local ^= CODER_LIMIT025 + CODER_LIMIT050;
// clow -= CODER_LIMIT025;
// chigh -= CODER_LIMIT025;
ccode_local -= CODER_LIMIT025;
clow_local <<= 1;
chigh_local <<= 1;
chigh_local++;
ccode_local <<= 1;
ccode_local |= read_bit();
}
chigh = chigh_local;
clow = clow_local;
ccode = ccode_local;
}
/* -----------------------------------------------
bit reader function
----------------------------------------------- */
unsigned char ArithmeticDecoder::read_bit()
{
// read in new byte if needed
if ( cbit == 0 ) {
if ( !reader_.read_byte(&bbyte)) // read next byte if available
bbyte = 0; // if no more data is left in the stream
cbit = 8;
}
// decrement current bit position
cbit--;
// return bit at cbit position
return BITN( bbyte, cbit );
}
/* -----------------------------------------------
universal statistical model for arithmetic coding
boundaries of this model:
max_s (maximum symbol) -> 1 <= max_s <= 1024 (???)
max_c (maximum context) -> 1 <= max_c <= 1024 (???)
max_o (maximum order) -> -1 <= max_o <= 4
c_lim (maximum count) -> 2 <= c_lim <= 4096 (???)
WARNING: this can be memory intensive, so don't overdo it
max_s == 256; max_c == 256; max_o == 4 would be way too much
----------------------------------------------- */
model_s::model_s( int max_s, int max_c, int max_o, int c_lim ) :
// Copy settings into the model:
max_symbol(max_s),
max_context(max_c),
max_order(max_o + 1),
max_count(c_lim),
current_order(max_o + 1),
sb0_count(max_s),
totals(max_s + 2),
scoreboard(new bool[max_s]),
contexts(max_o + 3)
{
std::fill(scoreboard, scoreboard + max_symbol, false);
// set up null table
table_s* null_table = new table_s;
null_table->counts = std::vector<uint16_t>(max_symbol, uint16_t(1)); // Set all probabilities to 1.
// set up internal counts
null_table->max_count = 1;
null_table->max_symbol = max_symbol;
// set up start table
table_s* start_table = new table_s;
start_table->links = std::vector<table_s*>(max_context);
// integrate tables into contexts
contexts[ 0 ] = null_table;
contexts[ 1 ] = start_table;
// build initial 'normal' tables
for (int i = 2; i <= max_order; i++ ) {
// set up current order table
contexts[i] = new table_s;
// build forward links
if ( i < max_order ) {
contexts[i]->links = std::vector<table_s*>(max_context);
}
contexts[ i - 1 ]->links[ 0 ] = contexts[ i ];
}
}
/* -----------------------------------------------
model class destructor - recursive cleanup of memory is done here
----------------------------------------------- */
model_s::~model_s()
{
// clean up each 'normal' table
delete contexts[1];
// clean up null table
delete contexts[0];
// free everything else
delete[] scoreboard;
}
/* -----------------------------------------------
Updates statistics for a specific symbol / resets to highest order.
Use -1 if you just want to reset without updating statistics.
----------------------------------------------- */
void model_s::update_model( int symbol )
{
// only contexts, that were actually used to encode
// the symbol get its count updated
if ( symbol >= 0 ) {
for (int local_order = ( current_order < 1 ) ? 1 : current_order;
local_order <= max_order; local_order++ ) {
table_s* context = contexts[ local_order ];
auto& count = context->counts[symbol];
// update count for specific symbol & scale
count++;
// store side information for totalize_table
context->max_count = std::max(count, context->max_count);
context->max_symbol = std::max(uint16_t(symbol + 1), context->max_symbol);
// if count for that symbol have gone above the maximum count
// the table has to be resized (scale factor 2)
if (count == max_count) {
context->rescale_table();
}
}
}
// reset scoreboard and current order
current_order = max_order;
std::fill(scoreboard, scoreboard + max_symbol, false);
sb0_count = max_symbol;
}
/* -----------------------------------------------
shift in one context (max no of contexts is max_c)
----------------------------------------------- */
void model_s::shift_context( int c )
{
// shifting is not possible if max_order is below 1
// or context index is negative
if ( ( max_order < 2 ) || ( c < 0 ) ) return;
// shift each orders' context
for (int i = max_order; i > 1; i-- ) {
// this is the new current order context
table_s* context = contexts[ i - 1 ]->links[ c ];
// check if context exists, build if needed
if ( context == nullptr ) {
// reserve memory for next table_s
context = new table_s;
// finished here if this is a max order context
if ( i < max_order ) {
// build links to higher order tables otherwise
context->links.resize(max_context);
}
// put context to its right place
contexts[ i - 1 ]->links[ c ] = context;
}
// switch context
contexts[ i ] = context;
}
}
/* -----------------------------------------------
Flushes the entire model by calling rescale_table on all contexts.
----------------------------------------------- */
void model_s::flush_model()
{
contexts[1]->recursive_flush();
}
/* -----------------------------------------------
Excludes every symbol above c.
----------------------------------------------- */
void model_s::exclude_symbols(int c)
{
// exclusions are back to normal after update_model is used
for ( c = c + 1; c < max_symbol; c++ ) {
if ( !scoreboard[ c ] ) {
scoreboard[ c ] = true;
sb0_count--;
}
}
}
/* -----------------------------------------------
converts an int to a symbol, needed only when encoding
----------------------------------------------- */
int model_s::convert_int_to_symbol( int c, symbol *s )
{
// search the symbol c in the current context table_s,
// return scale, low- and high counts
// totalize table for the current context
table_s* context = contexts[ current_order ];
totalize_table( context );
// finding the scale is easy
s->scale = totals[ 0 ];
// check if that symbol exists in the current table. send escape otherwise
if ( context->counts[ c ] > 0 ) {
// return high and low count for the current symbol
s->low_count = totals[ c + 2 ];
s->high_count = totals[ c + 1 ];
return 0;
}
// return high and low count for the escape symbol
s->low_count = totals[ 1 ];
s->high_count = totals[ 0 ];
current_order--;
return 1;
}
/* -----------------------------------------------
returns the current context scale needed only when decoding
----------------------------------------------- */
void model_s::get_symbol_scale( symbol *s )
{
// getting the scale is easy: totalize the table_s, use accumulated count -> done
totalize_table( contexts[ current_order ] );
s->scale = totals[ 0 ];
}
/* -----------------------------------------------
converts a count to an int, called after get_symbol_scale
----------------------------------------------- */
int model_s::convert_symbol_to_int(uint32_t count, symbol *s)
{
// seek the symbol that matches the count,
// also, set low- and high count for the symbol - it has to be removed from the stream
// go through the totals table, search the symbol that matches the count
int c;
for (c = 1; c < int(totals.size()); c++) {
if (count >= totals[c]) {
break;
}
}
// set up the current symbol
s->low_count = totals[c]; // It is guaranteed that there exists such a symbol.
s->high_count = totals[c - 1]; // This is guaranteed to not go out of bounds since the search started at index 1 of totals.
// send escape if escape symbol encountered
if (c == 1) {
current_order--;
return ESCAPE_SYMBOL;
}
// return symbol value
return c - 2 ; // Since c is not one and is a positive number, this will be nonnegative.
}
/* -----------------------------------------------
totals are calculated by accumulating counts in the current table_s
----------------------------------------------- */
void model_s::totalize_table( table_s* context )
{
// update exclusion is used, so this has to be done each time
// escape probability calculation also takes place here
// accumulated counts must never exceed CODER_MAXSCALE
// as CODER_MAXSCALE is big enough, though, (2^29), this shouldn't happen and is not checked
const auto& counts = context->counts;
// check counts
if (!counts.empty()) { // if counts are already set
// locally store current fill/symbol count
int local_symb = sb0_count;
// set the last symbol of the totals to zero
int i = context->max_symbol - 1;
totals[i + 2] = 0;
// (re)set current total
uint32_t curr_total = 0;
// go reverse though the whole counts table and accumulate counts
// leave space at the beginning of the table for the escape symbol
for (; i >= 0; i--) {
// only count probability if the current symbol is not 'scoreboard - excluded'
if (!scoreboard[i]) {
uint16_t curr_count = counts[i];
if (curr_count > 0) {
// add counts for the current symbol
curr_total += curr_count;
// exclude symbol from scoreboard
scoreboard[i] = true;
sb0_count--;
}
}
totals[i + 1] = curr_total;
}
// here the escape calculation needs to take place
uint32_t esc_prob;
if (local_symb == sb0_count) {
esc_prob = 1;
} else if (sb0_count == 0) {
esc_prob = 0;
} else {
// esc_prob = 1;
esc_prob = sb0_count * ( local_symb - sb0_count );
esc_prob /= ( local_symb * context->max_count );
esc_prob++;
}
// include escape probability in totals table
totals[ 0 ] = totals[ 1 ] + esc_prob;
} else { // if counts are not already set
// setup counts for current table
context->counts.resize(max_symbol);
// set totals table -> only escape probability included
totals[ 0 ] = 1;
totals[ 1 ] = 0;
}
}
/* -----------------------------------------------
special version of model_s for binary coding
boundaries of this model:
... (maximum symbol) -> 2 (0 or 1 )
max_c (maximum context) -> 1 <= max_c <= 1024 (???)
max_o (maximum order) -> -1 <= max_o <= 4
----------------------------------------------- */
model_b::model_b( int max_c, int max_o, int c_lim ) :
// Copy settings into the model:
max_context(max_c),
max_order(max_o + 1),
max_count(c_lim),
contexts(max_o + 3)
{
// set up null table
table* null_table = new table;
null_table->counts = std::vector<uint16_t>(2, uint16_t(1));
null_table->scale = uint32_t(2);
// set up start table
table* start_table = new table;
start_table->links = std::vector<table*>(max_context);
// integrate tables into contexts
contexts[ 0 ] = null_table;
contexts[ 1 ] = start_table;
// build initial 'normal' tables
for (int i = 2; i <= max_order; i++ ) {
// set up current order table
contexts[i] = new table;
// build forward links
if ( i < max_order ) {
contexts[i]->links = std::vector<table*>(max_context);
}
contexts[ i - 1 ]->links[ 0 ] = contexts[ i ];
}
}
/* -----------------------------------------------
model class destructor - recursive cleanup of memory is done here
----------------------------------------------- */
model_b::~model_b()
{
// clean up each 'normal' table
delete contexts[1];
// clean up null table
delete contexts[0];
}
/* -----------------------------------------------
updates statistics for a specific symbol / resets to highest order
----------------------------------------------- */
void model_b::update_model( int symbol )
{
// use -1 if you just want to reset without updating statistics
table* context = contexts[ max_order ];
// only contexts, that were actually used to encode
// the symbol get their counts updated
if ( ( symbol >= 0 ) && ( max_order >= 0 ) ) {
// update count for specific symbol & scale
context->counts[ symbol ]++;
context->scale++;
// if counts for that symbol have gone above the maximum count
// the table has to be resized (scale factor 2)
if ( context->counts[ symbol ] >= max_count )
context->rescale_table();
}
}
/* -----------------------------------------------
shift in one context (max no of contexts is max_c)
----------------------------------------------- */
void model_b::shift_context( int c )
{
// shifting is not possible if max_order is below 1
// or context index is negative
if ( (max_order < 2 ) || ( c < 0 ) ) return;
// shift each orders' context
for (int i = max_order; i > 1; i-- ) {
// this is the new current order context
table* context = contexts[ i - 1 ]->links[ c ];
// check if context exists, build if needed
if ( context == nullptr ) {
// reserve memory for next table
context = new table;
// finished here if this is a max order context
if ( i < max_order) {
// build links to higher order tables otherwise
context->links.resize(max_context);
}
// put context to its right place
contexts[ i - 1 ]->links[ c ] = context;
}
// switch context
contexts[ i ] = context;
}
}
/* -----------------------------------------------
Flushes the entire model by calling rescale_table on all contexts.
----------------------------------------------- */
void model_b::flush_model()
{
contexts[1]->recursive_flush();
}
/* -----------------------------------------------
converts an int to a symbol, needed only when encoding
----------------------------------------------- */
int model_b::convert_int_to_symbol( int c, symbol *s )
{
table* context = contexts[ max_order ];
// check if counts are available
context->check_counts();
// finding the scale is easy
s->scale = context->scale;
// return high and low count for current symbol
if ( c == 0 ) { // if 0 is to be encoded
s->low_count = uint32_t(0);
s->high_count = context->counts[ 0 ];
}
else { // if 1 is to be encoded
s->low_count = context->counts[ 0 ];
s->high_count = context->scale;
}
return 1;
}
/* -----------------------------------------------
returns the current context scale needed only when decoding
----------------------------------------------- */
void model_b::get_symbol_scale( symbol *s )
{
table* context = contexts[ max_order ];
// check if counts are available
context->check_counts();
// getting the scale is easy
s->scale = context->scale;
}
/* -----------------------------------------------
converts a count to an int, called after get_symbol_scale
----------------------------------------------- */
int model_b::convert_symbol_to_int(uint32_t count, symbol *s)
{
table* context = contexts[ max_order ];
auto counts0 = context->counts[ 0 ];
// set up the current symbol
if ( count < counts0 ) {
s->low_count = uint32_t(0);
s->high_count = counts0;
return 0;
}
else {
s->low_count = counts0;
s->high_count = s->scale;
return 1;
}
}
software/packJPG_library/lib_src/aricoder.h
#ifndef ARICODER_H
#define ARICODER_H
#include "bitops.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <vector>
// defines for coder
constexpr uint32_t CODER_USE_BITS = 31; // Must never be above 31.
constexpr uint32_t CODER_LIMIT100 = uint32_t(1 << CODER_USE_BITS);
constexpr uint32_t CODER_LIMIT025 = CODER_LIMIT100 / 4;
constexpr uint32_t CODER_LIMIT050 = (CODER_LIMIT100 / 4) * 2;
constexpr uint32_t CODER_LIMIT075 = (CODER_LIMIT100 / 4) * 3;
constexpr uint32_t CODER_MAXSCALE = CODER_LIMIT025 - 1;
constexpr uint32_t ESCAPE_SYMBOL = CODER_LIMIT025;
// symbol struct, used in arithmetic coding
struct symbol {
uint32_t low_count;
uint32_t high_count;
uint32_t scale;
};
// table struct, used in in statistical models,
// holding all info needed for one context
struct table {
// counts for each symbol contained in the table
std::vector<uint16_t> counts;
// links to higher order contexts
std::vector<table*> links;
// accumulated counts
uint32_t scale = uint32_t(0);
/* -----------------------------------------------
Recursively deletes all the tables pointed to in links.
----------------------------------------------- */
~table() {
for (auto& link : links) {
if (link != nullptr) {
delete link;
}
}
}
/* -----------------------------------------------
Checks if counts exist, creating it if it does not.
----------------------------------------------- */
inline void check_counts() {
// check if counts are available
if (counts.empty()) {
// setup counts for current table
counts.resize(2, uint16_t(1));
// set scale
scale = uint32_t(2);
}
}
/* -----------------------------------------------
Resizes the table by rightshifting each count by 1.
----------------------------------------------- */
inline void rescale_table() {
// Do nothing if counts is not set:
if (!counts.empty()) {
// Scale the table by bitshifting each count, be careful not to set any count zero:
counts[0] = std::max(uint16_t(1), uint16_t(counts[0] >> 1));
counts[1] = std::max(uint16_t(1), uint16_t(counts[1] >> 1));
scale = counts[0] + counts[1];
}
}
/* -----------------------------------------------
Recursively runs rescale_table on this and all linked contexts.
----------------------------------------------- */
inline void recursive_flush() {
for (auto& link : links) {
if (link != nullptr) {
link->recursive_flush();
}
}
// rescale specific table
rescale_table();
}
};
// special table struct, used in in model_s,
// holding additional info for a speedier 'totalize_table'
struct table_s {
// counts for each symbol contained in the table
std::vector<uint16_t> counts;
// links to higher order contexts
std::vector<table_s*> links;
// speedup info
uint16_t max_count = uint16_t(0);
uint16_t max_symbol = uint16_t(0);
/* -----------------------------------------------
Recursively deletes all the tables pointed to in links.
----------------------------------------------- */
~table_s() {
for (auto& link : links) {
if (link != nullptr) {
delete link;
}
}
}
/* -----------------------------------------------
Resizes the table by rightshifting each count by 1.
----------------------------------------------- */
inline void rescale_table() {
// Nothing to do if counts has not been set.
if (counts.empty()) return;
// now scale the table by bitshifting each count
int lst_symbol = max_symbol;
int i;
for (i = 0; i < lst_symbol; i++) {
counts[i] >>= 1; // Counts will not become negative since it is an unsigned type.
}
// also rescale tables max count
max_count >>= 1;
// seek for new last symbol
for (i = lst_symbol - 1; i >= 0; i--) {
if (counts[i] > 0) {
break;
}
}
max_symbol = i + 1;
}
/* -----------------------------------------------
Recursively runs rescale_table on this and all linked contexts.
----------------------------------------------- */
inline void recursive_flush() {
for (auto& link : links) {
if (link != nullptr) {
link->recursive_flush();
}
}
// rescale specific table
rescale_table();
}
};
class ArithmeticBitWriter {
public:
template <std::uint8_t bit>
void write_bit();
void write_n_zero_bits(std::size_t n);
void write_n_one_bits(std::size_t n);
void pad();
std::vector<std::uint8_t> get_data() const;
private:
std::vector<std::uint8_t> data_;
std::uint8_t curr_byte_ = 0;
std::size_t curr_bit_ = 0;
};
/* -----------------------------------------------
class for arithmetic coding of data to/from iostream
----------------------------------------------- */
class ArithmeticEncoder
{
public:
ArithmeticEncoder(Writer& writer);
~ArithmeticEncoder();
void encode( symbol* s );
void finalize();
private:
// i/o variables
bool finalized = false;
Writer& writer_;
std::unique_ptr<ArithmeticBitWriter> bitwriter_ = std::make_unique<ArithmeticBitWriter>();
// arithmetic coding variables
unsigned int ccode = 0;
unsigned int clow = 0;
unsigned int chigh = CODER_LIMIT100 - 1;
unsigned int cstep = 0;
unsigned int nrbits = 0;
};
class ArithmeticDecoder {
public:
ArithmeticDecoder(Reader& reader);
~ArithmeticDecoder() {}
unsigned int decode_count( symbol* s );
void decode( symbol* s );
private:
unsigned char read_bit();
// i/o variables
Reader& reader_;
unsigned char bbyte = 0;
unsigned char cbit = 0;
// arithmetic coding variables
unsigned int ccode = 0;
unsigned int clow = 0;
unsigned int chigh = CODER_LIMIT100 - 1;
unsigned int cstep = 0;
};
/* -----------------------------------------------
universal statistical model for arithmetic coding
----------------------------------------------- */
class model_s
{
public:
model_s( int max_s, int max_c, int max_o, int c_lim );
~model_s();
void update_model( int symbol );
void shift_context( int c );
void flush_model();
void exclude_symbols(int c);
int convert_int_to_symbol( int c, symbol *s );
void get_symbol_scale( symbol *s );
int convert_symbol_to_int(uint32_t count, symbol *s);
private:
inline void totalize_table(table_s* context);
const int max_symbol;
const int max_context;
const int max_order;
const int max_count;
int current_order;
int sb0_count;
std::vector<uint32_t> totals;
bool* scoreboard;
std::vector<table_s*> contexts;
};
/* -----------------------------------------------
binary statistical model for arithmetic coding
----------------------------------------------- */
class model_b
{
public:
model_b( int max_c, int max_o, int c_lim );
~model_b();
void update_model( int symbol );
void shift_context( int c );
void flush_model();
int convert_int_to_symbol( int c, symbol *s );
void get_symbol_scale( symbol *s );
int convert_symbol_to_int(uint32_t count, symbol *s);
private:
const int max_context;
const int max_order;
const int max_count;
std::vector<table*> contexts;
};
// Base case for shifting an arbitrary number of contexts into the model.
template <typename M>
static void shift_model(M) {}
// Shift an arbitrary number of contexts into the model (at most max_c contexts).
template <typename M, typename C, typename... Cargs>
static void shift_model(M model, C context, Cargs ... contextList) {
model->shift_context(context);
shift_model(model, contextList...);
}
/* -----------------------------------------------
generic model_s encoder function
----------------------------------------------- */
static inline void encode_ari( ArithmeticEncoder* encoder, model_s* model, int c )
{
symbol s;
int esc;
do {
esc = model->convert_int_to_symbol( c, &s );
encoder->encode( &s );
} while ( esc );
model->update_model( c );
}
/* -----------------------------------------------
generic model_s decoder function
----------------------------------------------- */
static inline int decode_ari( ArithmeticDecoder* decoder, model_s* model )
{
symbol s;
uint32_t count;
int c;
do{
model->get_symbol_scale( &s );
count = decoder->decode_count( &s );
c = model->convert_symbol_to_int( count, &s );
decoder->decode( &s );
} while ( c == ESCAPE_SYMBOL );
model->update_model( c );
return c;
}
/* -----------------------------------------------
generic model_b encoder function
----------------------------------------------- */
static inline void encode_ari( ArithmeticEncoder* encoder, model_b* model, int c )
{
symbol s;
model->convert_int_to_symbol( c, &s );
encoder->encode( &s );
model->update_model( c );
}
/* -----------------------------------------------
generic model_b decoder function
----------------------------------------------- */
static inline int decode_ari( ArithmeticDecoder* decoder, model_b* model )
{
symbol s;
model->get_symbol_scale( &s );
uint32_t count = decoder->decode_count( &s );
int c = model->convert_symbol_to_int( count, &s );
decoder->decode( &s );
model->update_model( c );
return c;
}
#endif
software/packJPG_library/lib_src/bitops.cpp
/*
This file contains special classes for bitwise
reading and writing of arrays
*/
#include "bitops.h"
#include <algorithm>
#include <array>
#include <cstdio>
#include <cstdlib>
#include <experimental/filesystem>
#include <fstream>
#include <stdexcept>
#if defined(_WIN32) || defined(WIN32)
#include <fcntl.h>
#include <io.h>
#endif
/* -----------------------------------------------
constructor for BitReader class
----------------------------------------------- */
BitReader::BitReader( unsigned char* array, int size ) {
data = array;
lbyte = size;
}
/* -----------------------------------------------
destructor for BitReader class
----------------------------------------------- */
BitReader::~BitReader() {}
/* -----------------------------------------------
reads n bits from BitReader
----------------------------------------------- */
unsigned int BitReader::read( int nbits ) {
unsigned int retval = 0;
if ( eof()) {
peof_ += nbits;
return 0;
}
while ( nbits >= cbit ) {
nbits -= cbit;
retval |= ( RBITS( data[cbyte], cbit ) << nbits );
update_curr_byte();
if (eof()) {
peof_ = nbits;
return retval;
}
}
if ( nbits > 0 ) {
retval |= ( MBITS( data[cbyte], cbit, (cbit-nbits) ) );
cbit -= nbits;
}
return retval;
}
/* -----------------------------------------------
reads one bit from BitReader
----------------------------------------------- */
unsigned char BitReader::read_bit() {
if (eof()) {
peof_++;
return 0;
}
// read one bit
unsigned char bit = BITN( data[cbyte], --cbit );
if ( cbit == 0 ) {
update_curr_byte();
}
return bit;
}
void BitReader::update_curr_byte() {
cbyte++;
eof_ = cbyte == lbyte;
cbit = 8;
}
/* -----------------------------------------------
to skip padding from current byte
----------------------------------------------- */
unsigned char BitReader::unpad( unsigned char fillbit ) {
if ( ( cbit == 8 ) || eof()) {
return fillbit;
} else {
fillbit = read( 1 );
while ( cbit != 8 ) {
read( 1 );
}
}
return fillbit;
}
/* -----------------------------------------------
get current position in array
----------------------------------------------- */
int BitReader::getpos() {
return cbyte;
}
/* -----------------------------------------------
get current bit position
----------------------------------------------- */
int BitReader::getbitp() {
return cbit;
}
/* -----------------------------------------------
set byte and bit position
----------------------------------------------- */
void BitReader::setpos( int pbyte, int pbit ) {
if ( pbyte < lbyte ) {
// reset eof
eof_ = false;
// set positions
cbyte = pbyte;
cbit = pbit;
} else {
// set eof
eof_ = true;
// set positions
cbyte = lbyte;
cbit = 8;
peof_ = ( ( pbyte - lbyte ) * 8 ) + 8 - pbit;
}
}
/* -----------------------------------------------
rewind n bits
----------------------------------------------- */
void BitReader::rewind_bits( int nbits ) {
if (eof()) {
if (nbits > peof_) {
nbits -= peof_;
peof_ = 0;
} else {
peof_ -= nbits;
return;
}
eof_ = false;
}
cbit += nbits;
cbyte -= cbit / 8;
cbit = cbit % 8;
if ( cbyte < 0 ) {
cbyte = 0;
cbit = 8;
}
}
bool BitReader::eof() {
return eof_;
}
int BitReader::peof() {
return peof_;
}
BitWriter::BitWriter(std::uint8_t padbit) : padbit_(padbit) {}
BitWriter::~BitWriter() {}
std::uint32_t rbits32(std::uint32_t val, std::size_t n) {
return val & (0xFFFFFFFF >> (32 - n));
}
std::uint32_t mbits32(std::uint32_t val, std::size_t l, std::size_t r) {
return rbits32(val, l) >> r;
}
void BitWriter::write_u16(std::uint16_t val, std::size_t num_bits) {
while (num_bits >= curr_bit_) {
curr_byte_ |= mbits32(val, num_bits, num_bits - curr_bit_);
num_bits -= curr_bit_;
write_curr_byte();
}
if (num_bits > 0) {
curr_byte_ |= rbits32(val, num_bits) << (curr_bit_ - num_bits);
curr_bit_ -= num_bits;
}
}
void BitWriter::write_bit(std::uint8_t bit) {
curr_bit_--;
curr_byte_ |= bit << curr_bit_;
if (curr_bit_ == 0) {
write_curr_byte();
}
}
void BitWriter::write_curr_byte() {
bytes_.emplace_back(curr_byte_);
curr_byte_ = 0;
curr_bit_ = 8;
}
void BitWriter::pad() {
while (curr_bit_ < 8) {
write_bit(padbit_);
}
}
std::vector<std::uint8_t> BitWriter::get_bytes() {
pad(); // Pad the last bits of the current byte before returning the written bytes.
return bytes_;
}
unsigned char* BitWriter::get_c_bytes() {
pad(); // Pad the last bits of the current byte before returning the written bytes.
unsigned char* c_bytes = new unsigned char[bytes_.size()];
std::copy(std::begin(bytes_), std::end(bytes_), c_bytes);
return c_bytes;
}
std::size_t BitWriter::num_bytes_written() const {
return bytes_.size();
}
unsigned char* Reader::get_c_data() {
const auto data = this->get_data();
auto c_data_copy = (unsigned char*)std::malloc(data.size() * sizeof data[0]);
if (c_data_copy == nullptr) {
return nullptr;
}
std::copy(std::begin(data), std::end(data), c_data_copy);
return c_data_copy;
}
MemoryReader::MemoryReader(const std::vector<std::uint8_t>& bytes) :
data_(bytes),
cbyte_(std::begin(data_)) {
}
MemoryReader::MemoryReader(const std::uint8_t* bytes, std::size_t size) :
data_(bytes, bytes + size),
cbyte_(std::begin(data_)) {
}
std::size_t MemoryReader::read(std::uint8_t* to, std::size_t num_to_read) {
if (num_to_read == 0 || to == nullptr) {
return 0;
}
auto numAvailable = std::distance(cbyte_, std::end(data_));
auto numRead = std::min(static_cast<std::size_t>(numAvailable), num_to_read);
auto end = std::next(cbyte_, numRead);
std::copy(cbyte_, end, to);
cbyte_ = end;
return numRead;
}
std::size_t MemoryReader::read(std::vector<std::uint8_t>& into, std::size_t n, std::size_t offset) {
const std::size_t num_available = get_size() - num_bytes_read(); // The number of bytes in the reader not yet read.
const std::size_t num_to_read = std::min(n, num_available); // How many bytes will be read.
if (into.size() < num_to_read + offset) {
into.resize(num_to_read + offset);
}
const auto end = std::next(cbyte_, num_to_read);
const auto write_start = std::next(std::begin(into), offset);
std::copy(cbyte_, end, write_start);
cbyte_ = end;
return num_to_read;
}
std::uint8_t MemoryReader::read_byte() {
if (end_of_reader()) {
throw std::runtime_error("No bytes left to read");
} else {
std::uint8_t the_byte = *cbyte_;
++cbyte_;
return the_byte;
}
}
bool MemoryReader::read_byte(std::uint8_t* byte) {
if (end_of_reader()) {
return false;
} else {
*byte = *cbyte_;
++cbyte_;
return true;
}
}
void MemoryReader::skip(std::size_t n) {
auto num_to_skip = std::min(n, std::size_t(std::distance(cbyte_, std::end(data_))));
cbyte_ += num_to_skip;
}
void MemoryReader::rewind_bytes(std::size_t n) {
auto num_to_rewind = std::min(n, std::size_t(std::distance(std::begin(data_), cbyte_)));
auto new_pos = std::distance(std::begin(data_), cbyte_) - num_to_rewind;
cbyte_ = std::next(std::begin(data_), new_pos);
}
void MemoryReader::rewind() {
cbyte_ = std::begin(data_);
}
std::size_t MemoryReader::num_bytes_read() {
return std::distance(std::begin(data_), cbyte_);
}
std::size_t MemoryReader::get_size() {
return data_.size();
}
std::vector<std::uint8_t> MemoryReader::get_data() {
return data_;
}
bool MemoryReader::error() {
return false;
}
bool MemoryReader::end_of_reader() {
return cbyte_ == std::end(data_);
}
unsigned char* Writer::get_c_data() {
try {
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff