Coverage Report

Created: 2025-07-23 08:18

/src/libjxl/lib/jxl/enc_huffman.cc
Line
Count
Source (jump to first uncovered line)
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/status.h"
14
#include "lib/jxl/enc_bit_writer.h"
15
#include "lib/jxl/enc_huffman_tree.h"
16
17
namespace jxl {
18
19
namespace {
20
21
constexpr int kCodeLengthCodes = 18;
22
23
void StoreHuffmanTreeOfHuffmanTreeToBitMask(const int num_codes,
24
                                            const uint8_t* code_length_bitdepth,
25
12.0k
                                            BitWriter* writer) {
26
12.0k
  static const uint8_t kStorageOrder[kCodeLengthCodes] = {
27
12.0k
      1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15};
28
  // The bit lengths of the Huffman code over the code length alphabet
29
  // are compressed with the following static Huffman code:
30
  //   Symbol   Code
31
  //   ------   ----
32
  //   0          00
33
  //   1        1110
34
  //   2         110
35
  //   3          01
36
  //   4          10
37
  //   5        1111
38
12.0k
  static const uint8_t kHuffmanBitLengthHuffmanCodeSymbols[6] = {0, 7, 3,
39
12.0k
                                                                 2, 1, 15};
40
12.0k
  static const uint8_t kHuffmanBitLengthHuffmanCodeBitLengths[6] = {2, 4, 3,
41
12.0k
                                                                    2, 2, 4};
42
43
  // Throw away trailing zeros:
44
12.0k
  size_t codes_to_store = kCodeLengthCodes;
45
12.0k
  if (num_codes > 1) {
46
165k
    for (; codes_to_store > 0; --codes_to_store) {
47
165k
      if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) {
48
11.9k
        break;
49
11.9k
      }
50
165k
    }
51
11.9k
  }
52
12.0k
  size_t skip_some = 0;  // skips none.
53
12.0k
  if (code_length_bitdepth[kStorageOrder[0]] == 0 &&
54
12.0k
      code_length_bitdepth[kStorageOrder[1]] == 0) {
55
509
    skip_some = 2;  // skips two.
56
509
    if (code_length_bitdepth[kStorageOrder[2]] == 0) {
57
37
      skip_some = 3;  // skips three.
58
37
    }
59
509
  }
60
12.0k
  writer->Write(2, skip_some);
61
74.6k
  for (size_t i = skip_some; i < codes_to_store; ++i) {
62
62.6k
    size_t l = code_length_bitdepth[kStorageOrder[i]];
63
62.6k
    writer->Write(kHuffmanBitLengthHuffmanCodeBitLengths[l],
64
62.6k
                  kHuffmanBitLengthHuffmanCodeSymbols[l]);
65
62.6k
  }
66
12.0k
}
67
68
Status StoreHuffmanTreeToBitMask(const size_t huffman_tree_size,
69
                                 const uint8_t* huffman_tree,
70
                                 const uint8_t* huffman_tree_extra_bits,
71
                                 const uint8_t* code_length_bitdepth,
72
                                 const uint16_t* code_length_bitdepth_symbols,
73
12.0k
                                 BitWriter* writer) {
74
113k
  for (size_t i = 0; i < huffman_tree_size; ++i) {
75
101k
    size_t ix = huffman_tree[i];
76
101k
    writer->Write(code_length_bitdepth[ix], code_length_bitdepth_symbols[ix]);
77
101k
    JXL_ENSURE(ix <= 17);
78
    // Extra bits
79
101k
    switch (ix) {
80
121
      case 16:
81
121
        writer->Write(2, huffman_tree_extra_bits[i]);
82
121
        break;
83
1.10k
      case 17:
84
1.10k
        writer->Write(3, huffman_tree_extra_bits[i]);
85
1.10k
        break;
86
100k
      default:
87
        // no-op
88
100k
        break;
89
101k
    }
90
101k
  }
91
12.0k
  return true;
92
12.0k
}
93
94
void StoreSimpleHuffmanTree(const uint8_t* depths, size_t symbols[4],
95
                            size_t num_symbols, size_t max_bits,
96
10.1k
                            BitWriter* writer) {
97
  // value of 1 indicates a simple Huffman code
98
10.1k
  writer->Write(2, 1);
99
10.1k
  writer->Write(2, num_symbols - 1);  // NSYM - 1
100
101
  // Sort
102
38.6k
  for (size_t i = 0; i < num_symbols; i++) {
103
56.7k
    for (size_t j = i + 1; j < num_symbols; j++) {
104
28.3k
      if (depths[symbols[j]] < depths[symbols[i]]) {
105
2.90k
        std::swap(symbols[j], symbols[i]);
106
2.90k
      }
107
28.3k
    }
108
28.4k
  }
109
110
10.1k
  if (num_symbols == 2) {
111
3.94k
    writer->Write(max_bits, symbols[0]);
112
3.94k
    writer->Write(max_bits, symbols[1]);
113
6.21k
  } else if (num_symbols == 3) {
114
4.29k
    writer->Write(max_bits, symbols[0]);
115
4.29k
    writer->Write(max_bits, symbols[1]);
116
4.29k
    writer->Write(max_bits, symbols[2]);
117
4.29k
  } else {
118
1.91k
    writer->Write(max_bits, symbols[0]);
119
1.91k
    writer->Write(max_bits, symbols[1]);
120
1.91k
    writer->Write(max_bits, symbols[2]);
121
1.91k
    writer->Write(max_bits, symbols[3]);
122
    // tree-select
123
1.91k
    writer->Write(1, depths[symbols[0]] == 1 ? 1 : 0);
124
1.91k
  }
125
10.1k
}
126
127
// num = alphabet size
128
// depths = symbol depths
129
12.0k
Status StoreHuffmanTree(const uint8_t* depths, size_t num, BitWriter* writer) {
130
  // Write the Huffman tree into the compact representation.
131
12.0k
  std::unique_ptr<uint8_t[]> arena(new uint8_t[2 * num]);
132
12.0k
  uint8_t* huffman_tree = arena.get();
133
12.0k
  uint8_t* huffman_tree_extra_bits = arena.get() + num;
134
12.0k
  size_t huffman_tree_size = 0;
135
12.0k
  WriteHuffmanTree(depths, num, &huffman_tree_size, huffman_tree,
136
12.0k
                   huffman_tree_extra_bits);
137
138
  // Calculate the statistics of the Huffman tree in the compact representation.
139
12.0k
  uint32_t huffman_tree_histogram[kCodeLengthCodes] = {0};
140
113k
  for (size_t i = 0; i < huffman_tree_size; ++i) {
141
101k
    ++huffman_tree_histogram[huffman_tree[i]];
142
101k
  }
143
144
12.0k
  int num_codes = 0;
145
12.0k
  int code = 0;
146
45.2k
  for (int i = 0; i < kCodeLengthCodes; ++i) {
147
45.1k
    if (huffman_tree_histogram[i]) {
148
24.0k
      if (num_codes == 0) {
149
12.0k
        code = i;
150
12.0k
        num_codes = 1;
151
12.0k
      } else if (num_codes == 1) {
152
11.9k
        num_codes = 2;
153
11.9k
        break;
154
11.9k
      }
155
24.0k
    }
156
45.1k
  }
157
158
  // Calculate another Huffman tree to use for compressing both the
159
  // earlier Huffman tree with.
160
12.0k
  uint8_t code_length_bitdepth[kCodeLengthCodes] = {0};
161
12.0k
  uint16_t code_length_bitdepth_symbols[kCodeLengthCodes] = {0};
162
12.0k
  CreateHuffmanTree(&huffman_tree_histogram[0], kCodeLengthCodes, 5,
163
12.0k
                    &code_length_bitdepth[0]);
164
12.0k
  ConvertBitDepthsToSymbols(code_length_bitdepth, kCodeLengthCodes,
165
12.0k
                            &code_length_bitdepth_symbols[0]);
166
167
  // Now, we have all the data, let's start storing it
168
12.0k
  StoreHuffmanTreeOfHuffmanTreeToBitMask(num_codes, code_length_bitdepth,
169
12.0k
                                         writer);
170
171
12.0k
  if (num_codes == 1) {
172
94
    code_length_bitdepth[code] = 0;
173
94
  }
174
175
  // Store the real huffman tree now.
176
12.0k
  JXL_RETURN_IF_ERROR(StoreHuffmanTreeToBitMask(
177
12.0k
      huffman_tree_size, huffman_tree, huffman_tree_extra_bits,
178
12.0k
      &code_length_bitdepth[0], code_length_bitdepth_symbols, writer));
179
12.0k
  return true;
180
12.0k
}
181
182
}  // namespace
183
184
Status BuildAndStoreHuffmanTree(const uint32_t* histogram, const size_t length,
185
                                uint8_t* depth, uint16_t* bits,
186
22.2k
                                BitWriter* writer) {
187
22.2k
  size_t count = 0;
188
22.2k
  size_t s4[4] = {0};
189
116k
  for (size_t i = 0; i < length; i++) {
190
105k
    if (histogram[i]) {
191
99.9k
      if (count < 4) {
192
76.6k
        s4[count] = i;
193
76.6k
      } else if (count > 4) {
194
11.2k
        break;
195
11.2k
      }
196
88.7k
      count++;
197
88.7k
    }
198
105k
  }
199
200
22.2k
  size_t max_bits_counter = length - 1;
201
22.2k
  size_t max_bits = 0;
202
78.6k
  while (max_bits_counter) {
203
56.4k
    max_bits_counter >>= 1;
204
56.4k
    ++max_bits;
205
56.4k
  }
206
207
22.2k
  if (count <= 1) {
208
    // Output symbol bits and depths are initialized with 0, nothing to do.
209
0
    writer->Write(4, 1);
210
0
    writer->Write(max_bits, s4[0]);
211
0
    return true;
212
0
  }
213
214
22.2k
  CreateHuffmanTree(histogram, length, 15, depth);
215
22.2k
  ConvertBitDepthsToSymbols(depth, length, bits);
216
217
22.2k
  if (count <= 4) {
218
10.1k
    StoreSimpleHuffmanTree(depth, s4, count, max_bits, writer);
219
12.0k
  } else {
220
12.0k
    JXL_RETURN_IF_ERROR(StoreHuffmanTree(depth, length, writer));
221
12.0k
  }
222
22.2k
  return true;
223
22.2k
}
224
225
}  // namespace jxl