Revision 59c738ae
Added by david.sorber 8 months ago
| software/libpackjpg/lib_src/CMakeLists.txt | ||
|---|---|---|
|
project("packJPG")
|
||
|
cmake_minimum_required(VERSION 3.2)
|
||
|
CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
|
||
|
PROJECT("packJPG")
|
||
|
|
||
|
set(CMAKE_CXX_STANDARD 14)
|
||
|
################################################################################
|
||
|
# Check GCC version
|
||
|
################################################################################
|
||
|
IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||
|
# Require at least gcc 10.3 for C++20 support
|
||
|
IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.3)
|
||
|
MESSAGE(FATAL_ERROR "GCC version must be at least 10.3!")
|
||
|
ENDIF()
|
||
|
ENDIF()
|
||
|
|
||
|
include_directories(..)
|
||
|
# This is a neat trick to prevent in source builds
|
||
|
IF (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||
|
MESSAGE(FATAL_ERROR "Prevented in-tree build. This is bad practice; you "
|
||
|
"should be running cmake from inside a build directory.")
|
||
|
ENDIF()
|
||
|
|
||
|
set(packjpg_sources
|
||
|
../aricoder.cpp
|
||
|
../bitops.cpp
|
||
|
../packjpg.cpp
|
||
|
|
||
|
################################################################################
|
||
|
# Build configuration options
|
||
|
################################################################################
|
||
|
|
||
|
IF (NOT CMAKE_BUILD_TYPE)
|
||
|
SET(CMAKE_BUILD_TYPE ReleaseWithDebug CACHE STRING
|
||
|
"Choose the type of build, options are: None Debug ReceiverRelease ReleaseWithDebug."
|
||
|
FORCE )
|
||
|
ENDIF()
|
||
|
|
||
|
MESSAGE(STATUS "Build Type: " ${CMAKE_BUILD_TYPE})
|
||
|
|
||
|
ADD_CUSTOM_TARGET(Debug
|
||
|
COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${CMAKE_SOURCE_DIR}
|
||
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
|
||
|
COMMENT "Switch CMAKE_BUILD_TYPE to Debug"
|
||
|
)
|
||
|
|
||
|
ADD_CUSTOM_TARGET(ReceiverRelease
|
||
|
COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=LibRelease ${CMAKE_SOURCE_DIR}
|
||
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
|
||
|
COMMENT "Switch CMAKE_BUILD_TYPE to ReceiverRelease"
|
||
|
)
|
||
|
|
||
|
ADD_CUSTOM_TARGET(ReleaseWithDebug
|
||
|
COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=ReleaseWithDebug ${CMAKE_SOURCE_DIR}
|
||
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
|
||
|
COMMENT "Switch CMAKE_BUILD_TYPE to ReleaseWithDebug"
|
||
|
)
|
||
|
|
||
|
|
||
|
# Use /etc/os-release to determine Linux flavor and version
|
||
|
EXECUTE_PROCESS(COMMAND bash -c "grep \"^NAME=\" /etc/os-release | cut -c7- | rev | cut -c2- | rev"
|
||
|
OUTPUT_VARIABLE DISTRIB_ID
|
||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||
|
)
|
||
|
|
||
|
EXECUTE_PROCESS(COMMAND bash -c "grep \"^VERSION_ID=\" /etc/os-release | cut -c13- | rev | cut -c2- | rev"
|
||
|
OUTPUT_VARIABLE DISTRIB_RELEASE
|
||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||
|
)
|
||
|
|
||
|
# Output the parsed ID and release version
|
||
|
MESSAGE(STATUS "DISTRIB_ID: " ${DISTRIB_ID})
|
||
|
MESSAGE(STATUS "DISTRIB_RELEASE: " ${DISTRIB_RELEASE})
|
||
|
|
||
|
# Do not let CMake add the build directory path to RPATH. This means that CL/SL
|
||
|
# cannot be run from the build directory and must be properly installed.
|
||
|
SET(CMAKE_SKIP_BUILD_RPATH TRUE)
|
||
|
|
||
|
# Uncomment this line to enable AddressSanitizer
|
||
|
# SET(CMAKE_CXX_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
|
||
|
|
||
|
# Detect presence of mold linker and use it if found
|
||
|
IF (EXISTS "/usr/bin/mold")
|
||
|
MESSAGE(STATUS "Using mold linker!")
|
||
|
# Note this only works on gcc >= 12.1
|
||
|
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=mold")
|
||
|
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=mold")
|
||
|
ELSE()
|
||
|
MESSAGE(WARNING "Mold linker not detected. Consider installing mold (add BL APT repo: "
|
||
|
"https://athena.blacklynx.tech/apt/): sudo apt install mold")
|
||
|
ENDIF()
|
||
|
|
||
|
#
|
||
|
# Set the compile flags
|
||
|
#
|
||
|
SET(CMAKE_CXX_STANDARD 20)
|
||
|
SET(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||
|
SET(CMAKE_CXX_EXTENSIONS OFF)
|
||
|
# SET(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||
|
|
||
|
# This length is used along with the __FILE__ macro for logging
|
||
|
STRING(LENGTH ${CMAKE_CURRENT_SOURCE_DIR} BPLEN)
|
||
|
ADD_DEFINITIONS("-DBUILD_PATH_LEN=${BPLEN}")
|
||
|
|
||
|
# Ensure symbols from statically linked libraries are hidden
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--exclude-libs=ALL")
|
||
|
|
||
|
# Enable warnings
|
||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter")
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter")
|
||
|
|
||
|
IF (CMAKE_BUILD_TYPE STREQUAL "LibRelease")
|
||
|
|
||
|
# Tune for Haswell minimum
|
||
|
SET(COMPILE_FLAGS "-O3 -flto=auto -ffast-math -ffp-contract=fast -fno-verbose-asm -march=skylake -mfma -mavx2")
|
||
|
|
||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
|
||
|
|
||
|
ELSEIF (CMAKE_BUILD_TYPE STREQUAL "ReleaseWithDebug")
|
||
|
|
||
|
# Tune for Haswell minimum
|
||
|
# SET(COMPILE_FLAGS "-g -O3 -flto=auto -march=haswell -mtune=generic -mavx2")
|
||
|
SET(COMPILE_FLAGS "-g -O3 -flto=auto -ffast-math -ffp-contract=fast -fno-verbose-asm -march=skylake -mfma -mavx2")
|
||
|
|
||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
|
||
|
|
||
|
ELSEIF (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||
|
|
||
|
SET(COMPILE_FLAGS "-g -O0 -march=skylake -mtune=generic")
|
||
|
|
||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
|
||
|
|
||
|
ENDIF()
|
||
|
|
||
|
# Set the library install path depending on the Linux flavor
|
||
|
SET(LIBRARY_INSTALL_PATH "")
|
||
|
IF ((DISTRIB_ID STREQUAL "Ubuntu") OR (DISTRIB_ID STREQUAL "Linux Mint"))
|
||
|
SET(LIBRARY_INSTALL_PATH "/usr/lib/x86_64-linux-gnu/")
|
||
|
ELSE()
|
||
|
# CentOS/RedHat/Scientific
|
||
|
SET(LIBRARY_INSTALL_PATH "/usr/lib64/")
|
||
|
ENDIF()
|
||
|
|
||
|
IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||
|
set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "..." FORCE)
|
||
|
ENDIF()
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
# Library Dependencies
|
||
|
################################################################################
|
||
|
SET(EXTERNAL_LIBS)
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
# Source
|
||
|
################################################################################
|
||
|
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||
|
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/external)
|
||
|
|
||
|
SET(packjpg_sources
|
||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/aricoder.cpp
|
||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/bitops.cpp
|
||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/packjpg.cpp
|
||
|
)
|
||
|
|
||
|
add_definitions("-std=c++14 -O3 -Wall -pedantic")
|
||
|
add_definitions("-funroll-loops -ffast-math -fomit-frame-pointer")
|
||
|
add_definitions("-march=native -fuse-ld=gold -flto")
|
||
|
|
||
|
add_library(packjpg SHARED
|
||
|
${packjpg_sources})
|
||
|
################################################################################
|
||
|
# Target: shared library
|
||
|
################################################################################
|
||
|
ADD_LIBRARY(packjpg SHARED ${packjpg_sources})
|
||
|
|
||
|
install(TARGETS packjpg
|
||
|
INSTALL(TARGETS packjpg
|
||
|
#~ LIBRARY DESTINATION "/usr/lib/x86_64-linux-gnu/")
|
||
|
LIBRARY DESTINATION "/opt/local/lib/")
|
||
|
|
||
|
install(FILES "packjpg.h"
|
||
|
INSTALL(FILES "packjpg.h"
|
||
|
DESTINATION "/opt/local/include/")
|
||
|
|
||
|
# packjpg_simple executable
|
||
|
find_package(Boost 1.54.0
|
||
|
REQUIRED
|
||
|
COMPONENTS filesystem)
|
||
|
|
||
|
add_executable(packjpg_simple
|
||
|
../bin/packjpg_simple.cc)
|
||
|
################################################################################
|
||
|
# Target: simple utility
|
||
|
################################################################################
|
||
|
ADD_EXECUTABLE(packjpg_simple
|
||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/bin/packjpg_simple.cc)
|
||
|
|
||
|
target_link_libraries(packjpg_simple ${Boost_LIBRARIES} packjpg stdc++fs)
|
||
|
TARGET_LINK_LIBRARIES(packjpg_simple packjpg )
|
||
| software/libpackjpg/lib_src/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/libpackjpg/lib_src/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/libpackjpg/lib_src/src/bin/packjpg_simple.cc | ||
|---|---|---|
|
#include <cstdint>
|
||
|
#include <cstring>
|
||
|
#include <filesystem>
|
||
|
#include <fstream>
|
||
|
#include <iostream>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
|
||
|
|
||
|
#include "../packjpg.h"
|
||
|
|
||
|
namespace bfs = std::filesystem;
|
||
|
|
||
|
const std::string JPG_EXTENSION(".jpg");
|
||
|
const std::string PJG_EXTENSION(".pjg");
|
||
|
|
||
|
int main(int argc, char** argv)
|
||
|
{
|
||
|
std::cout << "packJPG Simple Test Utility" << std::endl;
|
||
|
std::cout << " [" << packJPG::pjglib_version_info() << "]\n" << std::endl;
|
||
|
|
||
|
// Verify that input was provided
|
||
|
if (argc < 2)
|
||
|
{
|
||
|
std::cerr << "ERROR: please provide an input file\n" << std::endl;
|
||
|
return -1;
|
||
|
}
|
||
|
char* file_path = argv[1];
|
||
|
|
||
|
// Verify that input is a path to a file
|
||
|
if (! bfs::is_regular_file(file_path))
|
||
|
{
|
||
|
std::cerr << "ERROR: \"" << file_path << "\" is not a file\n" << std::endl;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
uint32_t file_size = bfs::file_size(file_path);
|
||
|
std::cout << "File size: " << file_size << std::endl;
|
||
|
auto file_buffer = new std::vector<unsigned char>(file_size);
|
||
|
|
||
|
// Read in the input file
|
||
|
std::ifstream input_stream(file_path, std::ios::binary | std::ios::in);
|
||
|
if (! input_stream)
|
||
|
{
|
||
|
std::cerr << "ERROR: unable to read from \"" << file_path << "\"\n"
|
||
|
<< std::endl;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
input_stream.read((char*)file_buffer->data(), file_size);
|
||
|
input_stream.close();
|
||
Modernize packJPG CMakeLists.txt. Remove Boost dependency.