Coverage Report

Created: 2025-06-22 08:04

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