mirror of
https://github.com/jwetzell/bmip10-js.git
synced 2026-07-26 08:58:40 +00:00
107 lines
3.1 KiB
JavaScript
107 lines
3.1 KiB
JavaScript
/** JS Implementation of BMIP10-ref.c found in https://downloads.blackmagicdesign.com/Developer/Converters/20240613-76d445/Blackmagic_IP10_Codec_v1.1.zip */
|
|
|
|
function SetupBMIP10(sample_bits, coded_bits) {
|
|
const config = {};
|
|
|
|
config.sample_states = 1 << sample_bits;
|
|
config.coded_states = 1 << coded_bits;
|
|
config.lossy_code_width = (1 << (sample_bits - coded_bits + 1)) - 1;
|
|
config.lossy_rounding = Math.floor(config.lossy_code_width / 2);
|
|
config.lossless_codes = Math.floor(config.coded_states / 2);
|
|
config.lossy_codes = Math.floor(config.coded_states / 2);
|
|
config.code_tables = 1 + Math.floor(config.coded_states / 2);
|
|
config.default_table = Math.floor(config.code_tables / 2);
|
|
config.lowest_table_thresh = Math.floor(config.lossless_codes / 2);
|
|
config.highest_table_thresh =
|
|
config.sample_states - Math.floor(config.lossless_codes / 2);
|
|
config.lossy_flag = Math.floor((1 << coded_bits) / 2);
|
|
return config;
|
|
}
|
|
|
|
function NextTable(config, sample) {
|
|
if (sample < config.lowest_table_thresh) {
|
|
return 0;
|
|
} else if (sample >= config.highest_table_thresh) {
|
|
return config.lossy_codes;
|
|
} else {
|
|
return Math.floor(
|
|
(sample - config.lowest_table_thresh + config.lossy_rounding) /
|
|
config.lossy_code_width
|
|
);
|
|
}
|
|
}
|
|
|
|
function EncodeSample(config, table, sample) {
|
|
const lossless_low = table * config.lossy_code_width;
|
|
const lossless_high = lossless_low + config.lossless_codes;
|
|
|
|
if (sample >= lossless_low && sample < lossless_high) {
|
|
return sample - lossless_low;
|
|
} else if (sample < lossless_low) {
|
|
return config.lossy_flag | Math.floor(sample / config.lossy_code_width);
|
|
} else {
|
|
return (
|
|
config.lossy_flag |
|
|
(Math.floor((sample - lossless_high) / config.lossy_code_width) + table)
|
|
);
|
|
}
|
|
}
|
|
|
|
function DecodeSample(config, table, code_word) {
|
|
const index = code_word & ~config.lossy_flag;
|
|
|
|
if (code_word & config.lossy_flag) {
|
|
if (index < table) {
|
|
return index * config.lossy_code_width + config.lossy_rounding;
|
|
} else {
|
|
return (
|
|
index * config.lossy_code_width +
|
|
config.lossy_rounding +
|
|
config.lossless_codes
|
|
);
|
|
}
|
|
} else {
|
|
return table * config.lossy_code_width + index;
|
|
}
|
|
}
|
|
|
|
class Encoder {
|
|
constructor(sample_bits, coded_bits) {
|
|
this.config = SetupBMIP10(sample_bits, coded_bits);
|
|
this.table = this.config.default_table;
|
|
}
|
|
|
|
encode(sample) {
|
|
const code_word = EncodeSample(this.config, this.table, sample);
|
|
const decoded = DecodeSample(this.config, this.table, code_word);
|
|
this.table = NextTable(this.config, decoded);
|
|
return code_word;
|
|
}
|
|
|
|
reset() {
|
|
this.table = this.config.default_table;
|
|
}
|
|
}
|
|
|
|
class Decoder {
|
|
constructor(sample_bits, coded_bits) {
|
|
this.config = SetupBMIP10(sample_bits, coded_bits);
|
|
this.table = this.config.default_table;
|
|
}
|
|
|
|
decode(code_word) {
|
|
const decoded = DecodeSample(this.config, this.table, code_word);
|
|
this.table = NextTable(this.config, decoded);
|
|
return decoded;
|
|
}
|
|
|
|
reset() {
|
|
this.table = this.config.default_table;
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
Encoder,
|
|
Decoder,
|
|
};
|