Coverage Report

Created: 2025-12-13 07:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/jxl/enc_huffman.cc
Line
Count
Source
1
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
2
//
3
// Use of this source code is governed by a BSD-style
4
// license that can be found in the LICENSE file.
5
6
#include "lib/jxl/enc_huffman.h"
7
8
#include <algorithm>
9
#include <cstddef>
10
#include <cstdint>
11
#include <memory>
12
13
#include "lib/jxl/base/common.h"
14
#include "lib/jxl/base/status.h"
15
#include "lib/jxl/enc_bit_writer.h"
16
#include "lib/jxl/enc_huffman_tree.h"
17
18
namespace jxl {
19
20
namespace {
21
22
constexpr int kCodeLengthCodes = 18;
23
24
void StoreHuffmanTreeOfHuffmanTreeToBitMask(const int num_codes,
25
                                            const uint8_t* code_length_bitdepth,
26
8.77k
                                            BitWriter* writer) {
27
8.77k
  static const uint8_t kStorageOrder[kCodeLengthCodes] = {
28
8.77k
      1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15};
29
  // The bit lengths of the Huffman code over the code length alphabet
30
  // are compressed with the following static Huffman code:
31
  //   Symbol   Code
32
  //   ------   ----
33
  //   0          00
34
  //   1        1110
35
  //   2         110
36
  //   3          01
37
  //   4          10
38
  //   5        1111
39
8.77k
  static const uint8_t kHuffmanBitLengthHuffmanCodeSymbols[6] = {0, 7, 3,
40
8.77k
                                                                 2, 1, 15};
41
8.77k
  static const uint8_t kHuffmanBitLengthHuffmanCodeBitLengths[6] = {2, 4, 3,
42
8.77k
                                                                    2, 2, 4};
43
44
  // Throw away trailing zeros:
45
8.77k
  size_t codes_to_store = kCodeLengthCodes;
46
8.77k
  if (num_codes > 1) {
47
120k
    for (; codes_to_store > 0; --codes_to_store) {
48
120k
      if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) {
49
8.69k
        break;
50
8.69k
      }
51
120k
    }
52
8.69k
  }
53
8.77k
  size_t skip_some = 0;  // skips none.
54
8.77k
  if (code_length_bitdepth[kStorageOrder[0]] == 0 &&
55
4.40k
      code_length_bitdepth[kStorageOrder[1]] == 0) {
56
359
    skip_some = 2;  // skips two.
57
359
    if (code_length_bitdepth[kStorageOrder[2]] == 0) {
58
32
      skip_some = 3;  // skips three.
59
32
    }
60
359
  }
61
8.77k
  writer->Write(2, skip_some);
62
54.4k
  for (size_t i = skip_some; i < codes_to_store; ++i) {
63
45.6k
    size_t l = code_length_bitdepth[kStorageOrder[i]];
64
45.6k
    writer->Write(kHuffmanBitLengthHuffmanCodeBitLengths[l],
65
45.6k
                  kHuffmanBitLengthHuffmanCodeSymbols[l]);
66
45.6k
  }
67
8.77k
}
68
69
Status StoreHuffmanTreeToBitMask(const size_t huffman_tree_size,
70
                                 const uint8_t* huffman_tree,
71
                                 const uint8_t* huffman_tree_extra_bits,
72
                                 const uint8_t* code_length_bitdepth,
73
                                 const uint16_t* code_length_bitdepth_symbols,
74
8.77k
                                 BitWriter* writer) {
75
83.9k
  for (size_t i = 0; i < huffman_tree_size; ++i) {
76
75.2k
    size_t ix = huffman_tree[i];
77
75.2k
    writer->Write(code_length_bitdepth[ix], code_length_bitdepth_symbols[ix]);
78
75.2k
    JXL_ENSURE(ix <= 17);
79
    // Extra bits
80
75.2k
    switch (ix) {
81
111
      case 16:
82
111
        writer->Write(2, huffman_tree_extra_bits[i]);
83
111
        break;
84
1.28k
      case 17:
85
1.28k
        writer->Write(3, huffman_tree_extra_bits[i]);
86
1.28k
        break;
87
73.8k
      default:
88
        // no-op
89
73.8k
        break;
90
75.2k
    }
91
75.2k
  }
92
8.77k
  return true;
93
8.77k
}
94
95
void StoreSimpleHuffmanTree(const uint8_t* depths, size_t symbols[4],
96
                            size_t num_symbols, size_t max_bits,
97
9.54k
                            BitWriter* writer) {
98
  // value of 1 indicates a simple Huffman code
99
9.54k
  writer->Write(2, 1);
100
9.54k
  writer->Write(2, num_symbols - 1);  // NSYM - 1
101
102
  // Sort
103
37.2k
  for (size_t i = 0; i < num_symbols; i++) {
104
56.5k
    for (size_t j = i + 1; j < num_symbols; j++) {
105
28.8k
      if (depths[symbols[j]] < depths[symbols[i]]) {
106
3.12k
        std::swap(symbols[j], symbols[i]);
107
3.12k
      }
108
28.8k
    }
109
27.7k
  }
110
111
9.54k
  if (num_symbols == 2) {
112
2.86k
    writer->Write(max_bits, symbols[0]);
113
2.86k
    writer->Write(max_bits, symbols[1]);
114
6.67k
  } else if (num_symbols == 3) {
115
4.69k
    writer->Write(max_bits, symbols[0]);
116
4.69k
    writer->Write(max_bits, symbols[1]);
117
4.69k
    writer->Write(max_bits, symbols[2]);
118
4.69k
  } else {
119
1.97k
    writer->Write(max_bits, symbols[0]);
120
1.97k
    writer->Write(max_bits, symbols[1]);
121
1.97k
    writer->Write(max_bits, symbols[2]);
122
1.97k
    writer->Write(max_bits, symbols[3]);
123
    // tree-select
124
1.97k
    writer->Write(1, depths[symbols[0]] == 1 ? 1 : 0);
125
1.97k
  }
126
9.54k
}
127
128
// num = alphabet size
129
// depths = symbol depths
130
8.77k
Status StoreHuffmanTree(const uint8_t* depths, size_t num, BitWriter* writer) {
131
  // Write the Huffman tree into the compact representation.
132
8.77k
  auto arena = jxl::make_uninitialized_vector<uint8_t>(2 * num);
133
8.77k
  uint8_t* huffman_tree = arena.data();
134
8.77k
  uint8_t* huffman_tree_extra_bits = arena.data() + num;
135
8.77k
  size_t huffman_tree_size = 0;
136
8.77k
  WriteHuffmanTree(depths, num, &huffman_tree_size, huffman_tree,
137
8.77k
                   huffman_tree_extra_bits);
138
139
  // Calculate the statistics of the Huffman tree in the compact representation.
140
8.77k
  uint32_t huffman_tree_histogram[kCodeLengthCodes] = {0};
141
83.9k
  for (size_t i = 0; i < huffman_tree_size; ++i) {
142
75.2k
    ++huffman_tree_histogram[huffman_tree[i]];
143
75.2k
  }
144
145
8.77k
  int num_codes = 0;
146
8.77k
  int code = 0;
147
33.0k
  for (int i = 0; i < kCodeLengthCodes; ++i) {
148
32.9k
    if (huffman_tree_histogram[i]) {
149
17.4k
      if (num_codes == 0) {
150
8.77k
        code = i;
151
8.77k
        num_codes = 1;
152
8.77k
      } else if (num_codes == 1) {
153
8.69k
        num_codes = 2;
154
8.69k
        break;
155
8.69k
      }
156
17.4k
    }
157
32.9k
  }
158
159
  // Calculate another Huffman tree to use for compressing both the
160
  // earlier Huffman tree with.
161
8.77k
  uint8_t code_length_bitdepth[kCodeLengthCodes] = {0};
162
8.77k
  uint16_t code_length_bitdepth_symbols[kCodeLengthCodes] = {0};
163
8.77k
  CreateHuffmanTree(&huffman_tree_histogram[0], kCodeLengthCodes, 5,
164
8.77k
                    &code_length_bitdepth[0]);
165
8.77k
  ConvertBitDepthsToSymbols(code_length_bitdepth, kCodeLengthCodes,
166
8.77k
                            &code_length_bitdepth_symbols[0]);
167
168
  // Now, we have all the data, let's start storing it
169
8.77k
  StoreHuffmanTreeOfHuffmanTreeToBitMask(num_codes, code_length_bitdepth,
170
8.77k
                                         writer);
171
172
8.77k
  if (num_codes == 1) {
173
75
    code_length_bitdepth[code] = 0;
174
75
  }
175
176
  // Store the real huffman tree now.
177
8.77k
  JXL_RETURN_IF_ERROR(StoreHuffmanTreeToBitMask(
178
8.77k
      huffman_tree_size, huffman_tree, huffman_tree_extra_bits,
179
8.77k
      &code_length_bitdepth[0], code_length_bitdepth_symbols, writer));
180
8.77k
  return true;
181
8.77k
}
182
183
}  // namespace
184
185
Status BuildAndStoreHuffmanTree(const uint32_t* histogram, const size_t length,
186
                                uint8_t* depth, uint16_t* bits,
187
18.3k
                                BitWriter* writer) {
188
18.3k
  size_t count = 0;
189
18.3k
  size_t s4[4] = {0};
190
94.7k
  for (size_t i = 0; i < length; i++) {
191
84.5k
    if (histogram[i]) {
192
79.6k
      if (count < 4) {
193
62.8k
        s4[count] = i;
194
62.8k
      } else if (count > 4) {
195
8.07k
        break;
196
8.07k
      }
197
71.5k
      count++;
198
71.5k
    }
199
84.5k
  }
200
201
18.3k
  size_t max_bits_counter = length - 1;
202
18.3k
  size_t max_bits = 0;
203
64.4k
  while (max_bits_counter) {
204
46.1k
    max_bits_counter >>= 1;
205
46.1k
    ++max_bits;
206
46.1k
  }
207
208
18.3k
  if (count <= 1) {
209
    // Output symbol bits and depths are initialized with 0, nothing to do.
210
3
    writer->Write(4, 1);
211
3
    writer->Write(max_bits, s4[0]);
212
3
    return true;
213
3
  }
214
215
18.3k
  CreateHuffmanTree(histogram, length, 15, depth);
216
18.3k
  ConvertBitDepthsToSymbols(depth, length, bits);
217
218
18.3k
  if (count <= 4) {
219
9.54k
    StoreSimpleHuffmanTree(depth, s4, count, max_bits, writer);
220
9.54k
  } else {
221
8.77k
    JXL_RETURN_IF_ERROR(StoreHuffmanTree(depth, length, writer));
222
8.77k
  }
223
18.3k
  return true;
224
18.3k
}
225
226
}  // namespace jxl