summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/lib/jxl/enc_huffman.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/jpeg-xl/lib/jxl/enc_huffman.cc')
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_huffman.cc214
1 files changed, 214 insertions, 0 deletions
diff --git a/third_party/jpeg-xl/lib/jxl/enc_huffman.cc b/third_party/jpeg-xl/lib/jxl/enc_huffman.cc
new file mode 100644
index 0000000000..3eab2c218a
--- /dev/null
+++ b/third_party/jpeg-xl/lib/jxl/enc_huffman.cc
@@ -0,0 +1,214 @@
+// Copyright (c) the JPEG XL Project Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "lib/jxl/enc_huffman.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "lib/jxl/enc_huffman_tree.h"
+
+namespace jxl {
+
+namespace {
+
+constexpr int kCodeLengthCodes = 18;
+
+void StoreHuffmanTreeOfHuffmanTreeToBitMask(const int num_codes,
+ const uint8_t* code_length_bitdepth,
+ BitWriter* writer) {
+ static const uint8_t kStorageOrder[kCodeLengthCodes] = {
+ 1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+ // The bit lengths of the Huffman code over the code length alphabet
+ // are compressed with the following static Huffman code:
+ // Symbol Code
+ // ------ ----
+ // 0 00
+ // 1 1110
+ // 2 110
+ // 3 01
+ // 4 10
+ // 5 1111
+ static const uint8_t kHuffmanBitLengthHuffmanCodeSymbols[6] = {0, 7, 3,
+ 2, 1, 15};
+ static const uint8_t kHuffmanBitLengthHuffmanCodeBitLengths[6] = {2, 4, 3,
+ 2, 2, 4};
+
+ // Throw away trailing zeros:
+ size_t codes_to_store = kCodeLengthCodes;
+ if (num_codes > 1) {
+ for (; codes_to_store > 0; --codes_to_store) {
+ if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) {
+ break;
+ }
+ }
+ }
+ size_t skip_some = 0; // skips none.
+ if (code_length_bitdepth[kStorageOrder[0]] == 0 &&
+ code_length_bitdepth[kStorageOrder[1]] == 0) {
+ skip_some = 2; // skips two.
+ if (code_length_bitdepth[kStorageOrder[2]] == 0) {
+ skip_some = 3; // skips three.
+ }
+ }
+ writer->Write(2, skip_some);
+ for (size_t i = skip_some; i < codes_to_store; ++i) {
+ size_t l = code_length_bitdepth[kStorageOrder[i]];
+ writer->Write(kHuffmanBitLengthHuffmanCodeBitLengths[l],
+ kHuffmanBitLengthHuffmanCodeSymbols[l]);
+ }
+}
+
+void StoreHuffmanTreeToBitMask(const size_t huffman_tree_size,
+ const uint8_t* huffman_tree,
+ const uint8_t* huffman_tree_extra_bits,
+ const uint8_t* code_length_bitdepth,
+ const uint16_t* code_length_bitdepth_symbols,
+ BitWriter* writer) {
+ for (size_t i = 0; i < huffman_tree_size; ++i) {
+ size_t ix = huffman_tree[i];
+ writer->Write(code_length_bitdepth[ix], code_length_bitdepth_symbols[ix]);
+ // Extra bits
+ switch (ix) {
+ case 16:
+ writer->Write(2, huffman_tree_extra_bits[i]);
+ break;
+ case 17:
+ writer->Write(3, huffman_tree_extra_bits[i]);
+ break;
+ }
+ }
+}
+
+void StoreSimpleHuffmanTree(const uint8_t* depths, size_t symbols[4],
+ size_t num_symbols, size_t max_bits,
+ BitWriter* writer) {
+ // value of 1 indicates a simple Huffman code
+ writer->Write(2, 1);
+ writer->Write(2, num_symbols - 1); // NSYM - 1
+
+ // Sort
+ for (size_t i = 0; i < num_symbols; i++) {
+ for (size_t j = i + 1; j < num_symbols; j++) {
+ if (depths[symbols[j]] < depths[symbols[i]]) {
+ std::swap(symbols[j], symbols[i]);
+ }
+ }
+ }
+
+ if (num_symbols == 2) {
+ writer->Write(max_bits, symbols[0]);
+ writer->Write(max_bits, symbols[1]);
+ } else if (num_symbols == 3) {
+ writer->Write(max_bits, symbols[0]);
+ writer->Write(max_bits, symbols[1]);
+ writer->Write(max_bits, symbols[2]);
+ } else {
+ writer->Write(max_bits, symbols[0]);
+ writer->Write(max_bits, symbols[1]);
+ writer->Write(max_bits, symbols[2]);
+ writer->Write(max_bits, symbols[3]);
+ // tree-select
+ writer->Write(1, depths[symbols[0]] == 1 ? 1 : 0);
+ }
+}
+
+// num = alphabet size
+// depths = symbol depths
+void StoreHuffmanTree(const uint8_t* depths, size_t num, BitWriter* writer) {
+ // Write the Huffman tree into the compact representation.
+ std::unique_ptr<uint8_t[]> arena(new uint8_t[2 * num]);
+ uint8_t* huffman_tree = arena.get();
+ uint8_t* huffman_tree_extra_bits = arena.get() + num;
+ size_t huffman_tree_size = 0;
+ WriteHuffmanTree(depths, num, &huffman_tree_size, huffman_tree,
+ huffman_tree_extra_bits);
+
+ // Calculate the statistics of the Huffman tree in the compact representation.
+ uint32_t huffman_tree_histogram[kCodeLengthCodes] = {0};
+ for (size_t i = 0; i < huffman_tree_size; ++i) {
+ ++huffman_tree_histogram[huffman_tree[i]];
+ }
+
+ int num_codes = 0;
+ int code = 0;
+ for (int i = 0; i < kCodeLengthCodes; ++i) {
+ if (huffman_tree_histogram[i]) {
+ if (num_codes == 0) {
+ code = i;
+ num_codes = 1;
+ } else if (num_codes == 1) {
+ num_codes = 2;
+ break;
+ }
+ }
+ }
+
+ // Calculate another Huffman tree to use for compressing both the
+ // earlier Huffman tree with.
+ uint8_t code_length_bitdepth[kCodeLengthCodes] = {0};
+ uint16_t code_length_bitdepth_symbols[kCodeLengthCodes] = {0};
+ CreateHuffmanTree(&huffman_tree_histogram[0], kCodeLengthCodes, 5,
+ &code_length_bitdepth[0]);
+ ConvertBitDepthsToSymbols(code_length_bitdepth, kCodeLengthCodes,
+ &code_length_bitdepth_symbols[0]);
+
+ // Now, we have all the data, let's start storing it
+ StoreHuffmanTreeOfHuffmanTreeToBitMask(num_codes, code_length_bitdepth,
+ writer);
+
+ if (num_codes == 1) {
+ code_length_bitdepth[code] = 0;
+ }
+
+ // Store the real huffman tree now.
+ StoreHuffmanTreeToBitMask(huffman_tree_size, huffman_tree,
+ huffman_tree_extra_bits, &code_length_bitdepth[0],
+ code_length_bitdepth_symbols, writer);
+}
+
+} // namespace
+
+void BuildAndStoreHuffmanTree(const uint32_t* histogram, const size_t length,
+ uint8_t* depth, uint16_t* bits,
+ BitWriter* writer) {
+ size_t count = 0;
+ size_t s4[4] = {0};
+ for (size_t i = 0; i < length; i++) {
+ if (histogram[i]) {
+ if (count < 4) {
+ s4[count] = i;
+ } else if (count > 4) {
+ break;
+ }
+ count++;
+ }
+ }
+
+ size_t max_bits_counter = length - 1;
+ size_t max_bits = 0;
+ while (max_bits_counter) {
+ max_bits_counter >>= 1;
+ ++max_bits;
+ }
+
+ if (count <= 1) {
+ // Output symbol bits and depths are initialized with 0, nothing to do.
+ writer->Write(4, 1);
+ writer->Write(max_bits, s4[0]);
+ return;
+ }
+
+ CreateHuffmanTree(histogram, length, 15, depth);
+ ConvertBitDepthsToSymbols(depth, length, bits);
+
+ if (count <= 4) {
+ StoreSimpleHuffmanTree(depth, s4, count, max_bits, writer);
+ } else {
+ StoreHuffmanTree(depth, length, writer);
+ }
+}
+
+} // namespace jxl