Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle empty dht in jpegli #128

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions server/bin/build-libjxl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ git clone https://github.com/libjxl/libjxl.git
cd libjxl
git reset --hard "$LIBJXL_REVISION"
git submodule update --init --recursive --depth 1 --recommend-shallow
git apply ../jpegli-empty-dht-marker.patch # adapted from https://github.com/libjxl/libjxl/pull/2704

mkdir build
cd build
Expand Down
99 changes: 99 additions & 0 deletions server/bin/jpegli-empty-dht-marker.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
diff --git a/lib/jpegli/decode_marker.cc b/lib/jpegli/decode_marker.cc
index 2621ed08..933210c5 100644
--- a/lib/jpegli/decode_marker.cc
+++ b/lib/jpegli/decode_marker.cc
@@ -282,7 +282,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
void ProcessDHT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
size_t pos = 2;
if (pos == len) {
- JPEGLI_ERROR("DHT marker: no Huffman table found");
+ return;
}
while (pos < len) {
JPEG_VERIFY_LEN(1 + kJpegHuffmanMaxBitLength);
diff --git a/lib/jxl/jpeg/dec_jpeg_data_writer.cc b/lib/jxl/jpeg/dec_jpeg_data_writer.cc
index 9fb664d3..e055ef9a 100644
--- a/lib/jxl/jpeg/dec_jpeg_data_writer.cc
+++ b/lib/jxl/jpeg/dec_jpeg_data_writer.cc
@@ -384,10 +384,12 @@ bool EncodeDHT(const JPEGData& jpg, SerializationState* state) {
size_t marker_len = 2;
for (size_t i = state->dht_index; i < huffman_code.size(); ++i) {
const JPEGHuffmanCode& huff = huffman_code[i];
- marker_len += kJpegHuffmanMaxBitLength;
for (uint32_t count : huff.counts) {
marker_len += count;
}
+ // special case: empty DHT marker
+ if (marker_len == 2) break;
+ marker_len += kJpegHuffmanMaxBitLength;
if (huff.is_last) break;
}
state->output_queue.emplace_back(marker_len + 2);
@@ -405,6 +407,17 @@ bool EncodeDHT(const JPEGData& jpg, SerializationState* state) {
const JPEGHuffmanCode& huff = huffman_code[huffman_code_index];
size_t index = huff.slot_id;
HuffmanCodeTable* huff_table;
+ size_t total_count = 0;
+ size_t max_length = 0;
+ for (size_t i = 0; i < huff.counts.size(); ++i) {
+ if (huff.counts[i] != 0) {
+ max_length = i;
+ }
+ total_count += huff.counts[i];
+ }
+ // Empty DHT marker
+ if (total_count == 0) break;
+
if (index & 0x10) {
index -= 0x10;
huff_table = &state->ac_huff_table[index];
@@ -417,14 +430,6 @@ bool EncodeDHT(const JPEGData& jpg, SerializationState* state) {
return false;
}
huff_table->initialized = true;
- size_t total_count = 0;
- size_t max_length = 0;
- for (size_t i = 0; i < huff.counts.size(); ++i) {
- if (huff.counts[i] != 0) {
- max_length = i;
- }
- total_count += huff.counts[i];
- }
--total_count;
data[pos++] = huff.slot_id;
for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) {
diff --git a/lib/jxl/jpeg/enc_jpeg_data_reader.cc b/lib/jxl/jpeg/enc_jpeg_data_reader.cc
index 149bde1c..70ad6a30 100644
--- a/lib/jxl/jpeg/enc_jpeg_data_reader.cc
+++ b/lib/jxl/jpeg/enc_jpeg_data_reader.cc
@@ -226,7 +226,12 @@ bool ProcessDHT(const uint8_t* data, const size_t len, JpegReadMode mode,
JXL_JPEG_VERIFY_LEN(2);
size_t marker_len = ReadUint16(data, pos);
if (marker_len == 2) {
- return JXL_FAILURE("DHT marker: no Huffman table found");
+ // Empty DHT marker. Useless but does seem to occur in the wild.
+ // We represent this situation with a dummy all-zeroes Huffman table.
+ JPEGHuffmanCode huff;
+ huff.is_last = true;
+ jpg->huffman_code.push_back(huff);
+ return true;
}
while (*pos < start_pos + marker_len) {
JXL_JPEG_VERIFY_LEN(1 + kJpegHuffmanMaxBitLength);
diff --git a/lib/jxl/jpeg/jpeg_data.cc b/lib/jxl/jpeg/jpeg_data.cc
index f3144dd6..1ae50a77 100644
--- a/lib/jxl/jpeg/jpeg_data.cc
+++ b/lib/jxl/jpeg/jpeg_data.cc
@@ -228,9 +228,10 @@ Status JPEGData::VisitFields(Visitor* visitor) {
Bits(8), 0, &hc.counts[i]));
num_symbols += hc.counts[i];
}
- if (num_symbols < 1) {
+ if (num_symbols == 0) {
// Actually, at least 2 symbols are required, since one of them is EOI.
- return JXL_FAILURE("Empty Huffman table");
+ // This case is used to represent an empty DHT marker.
+ continue;
}
if (num_symbols > hc.values.size()) {
return JXL_FAILURE("Huffman code too large (%" PRIuS ")", num_symbols);