// 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.
void BuildHuffmanLookupTable(j_decompress_ptr cinfo, JHUFF_TBL* table,
HuffmanTableEntry* huff_lut) {
uint32_t counts[kJpegHuffmanMaxBitLength + 1] = {};
counts[0] = 0; int total_count = 0; int space = 1 << kJpegHuffmanMaxBitLength; int max_depth = 1; for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) { int count = table->bits[i]; if (count != 0) {
max_depth = i;
}
counts[i] = count;
total_count += count;
space -= count * (1 << (kJpegHuffmanMaxBitLength - i));
}
uint32_t values[kJpegHuffmanAlphabetSize + 1] = {};
uint8_t values_seen[256] = {0}; for (int i = 0; i < total_count; ++i) { int value = table->huffval[i]; if (values_seen[value]) {
JPEGLI_ERROR("Duplicate Huffman code value %d", value);
}
values_seen[value] = 1;
values[i] = value;
} // Add an invalid symbol that will have the all 1 code.
++counts[max_depth];
values[total_count] = kJpegHuffmanAlphabetSize;
space -= (1 << (kJpegHuffmanMaxBitLength - max_depth)); if (space < 0) {
JPEGLI_ERROR("Invalid Huffman code lengths.");
} elseif (space > 0 && huff_lut[0].value != 0xffff) { // Re-initialize the values to an invalid symbol so that we can recognize // it when reading the bit stream using a Huffman code with space > 0. for (int i = 0; i < kJpegHuffmanLutSize; ++i) {
huff_lut[i].bits = 0;
huff_lut[i].value = 0xffff;
}
}
BuildJpegHuffmanTable(&counts[0], &values[0], huff_lut);
}
void PrepareForScan(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master; for (int i = 0; i < cinfo->comps_in_scan; ++i) { int comp_idx = cinfo->cur_comp_info[i]->component_index; int* prev_coef_bits = cinfo->coef_bits[comp_idx + cinfo->num_components]; for (int k = std::min(cinfo->Ss, 1); k <= std::max(cinfo->Se, 9); k++) {
prev_coef_bits[k] =
(cinfo->input_scan_number > 0) ? cinfo->coef_bits[comp_idx][k] : 0;
} for (int k = cinfo->Ss; k <= cinfo->Se; ++k) {
cinfo->coef_bits[comp_idx][k] = cinfo->Al;
}
}
AddStandardHuffmanTables(reinterpret_cast<j_common_ptr>(cinfo), /*is_dc=*/false);
AddStandardHuffmanTables(reinterpret_cast<j_common_ptr>(cinfo), /*is_dc=*/true); // Check that all the Huffman tables needed for this scan are defined and // build derived lookup tables. for (int i = 0; i < cinfo->comps_in_scan; ++i) { if (cinfo->Ss == 0) { int dc_tbl_idx = cinfo->cur_comp_info[i]->dc_tbl_no;
JHUFF_TBL* table = cinfo->dc_huff_tbl_ptrs[dc_tbl_idx];
HuffmanTableEntry* huff_lut =
&m->dc_huff_lut_[dc_tbl_idx * kJpegHuffmanLutSize]; if (!table) {
JPEGLI_ERROR("DC Huffman table %d not found", dc_tbl_idx);
}
BuildHuffmanLookupTable(cinfo, table, huff_lut);
} if (cinfo->Se > 0) { int ac_tbl_idx = cinfo->cur_comp_info[i]->ac_tbl_no;
JHUFF_TBL* table = cinfo->ac_huff_tbl_ptrs[ac_tbl_idx];
HuffmanTableEntry* huff_lut =
&m->ac_huff_lut_[ac_tbl_idx * kJpegHuffmanLutSize]; if (!table) {
JPEGLI_ERROR("AC Huffman table %d not found", ac_tbl_idx);
}
BuildHuffmanLookupTable(cinfo, table, huff_lut);
}
} // Copy quantization tables into comp_info. for (int i = 0; i < cinfo->comps_in_scan; ++i) {
jpeg_component_info* comp = cinfo->cur_comp_info[i]; int quant_tbl_idx = comp->quant_tbl_no;
JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[quant_tbl_idx]; if (!quant_table) {
JPEGLI_ERROR("Quantization table with index %d not found", quant_tbl_idx);
} if (comp->quant_table == nullptr) {
comp->quant_table = Allocate<JQUANT_TBL>(cinfo, 1, JPOOL_IMAGE);
memcpy(comp->quant_table, quant_table, sizeof(JQUANT_TBL));
}
} if (cinfo->comps_in_scan == 1) { constauto& comp = *cinfo->cur_comp_info[0];
cinfo->MCUs_per_row = DivCeil(cinfo->image_width * comp.h_samp_factor,
cinfo->max_h_samp_factor * DCTSIZE);
cinfo->MCU_rows_in_scan = DivCeil(cinfo->image_height * comp.v_samp_factor,
cinfo->max_v_samp_factor * DCTSIZE);
m->mcu_rows_per_iMCU_row_ = cinfo->cur_comp_info[0]->v_samp_factor;
} else {
cinfo->MCU_rows_in_scan = cinfo->total_iMCU_rows;
cinfo->MCUs_per_row = m->iMCU_cols_;
m->mcu_rows_per_iMCU_row_ = 1;
size_t mcu_size = 0; for (int i = 0; i < cinfo->comps_in_scan; ++i) {
jpeg_component_info* comp = cinfo->cur_comp_info[i];
mcu_size += comp->h_samp_factor * comp->v_samp_factor;
} if (mcu_size > D_MAX_BLOCKS_IN_MCU) {
JPEGLI_ERROR("MCU size too big");
}
}
memset(m->last_dc_coeff_, 0, sizeof(m->last_dc_coeff_));
m->restarts_to_go_ = cinfo->restart_interval;
m->next_restart_marker_ = 0;
m->eobrun_ = -1;
m->scan_mcu_row_ = 0;
m->scan_mcu_col_ = 0;
m->codestream_bits_ahead_ = 0;
++cinfo->input_scan_number;
cinfo->input_iMCU_row = 0;
PrepareForiMCURow(cinfo);
cinfo->global_state = kDecProcessScan;
}
int ConsumeInput(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master; if (cinfo->global_state == kDecProcessScan && m->streaming_mode_ &&
cinfo->input_iMCU_row > cinfo->output_iMCU_row) { // Prevent input from getting ahead of output in streaming mode. return JPEG_SUSPENDED;
}
jpeg_source_mgr* src = cinfo->src; int status; for (;;) { const uint8_t* data;
size_t len; if (m->input_buffer_.empty()) {
data = cinfo->src->next_input_byte;
len = cinfo->src->bytes_in_buffer;
} else {
data = &m->input_buffer_[m->input_buffer_pos_];
len = m->input_buffer_.size() - m->input_buffer_pos_;
}
size_t pos = 0; if (cinfo->global_state == kDecProcessScan) {
status = ProcessScan(cinfo, data, len, &pos, &m->codestream_bits_ahead_);
} else {
status = ProcessMarkers(cinfo, data, len, &pos);
} if (m->input_buffer_.empty()) {
cinfo->src->next_input_byte += pos;
cinfo->src->bytes_in_buffer -= pos;
} else {
m->input_buffer_pos_ += pos;
size_t bytes_left = m->input_buffer_.size() - m->input_buffer_pos_; if (bytes_left <= src->bytes_in_buffer) {
src->next_input_byte += (src->bytes_in_buffer - bytes_left);
src->bytes_in_buffer = bytes_left;
m->input_buffer_.clear();
m->input_buffer_pos_ = 0;
}
} if (status == kHandleRestart) {
JXL_DASSERT(m->input_buffer_.size() <=
m->input_buffer_pos_ + src->bytes_in_buffer);
m->input_buffer_.clear();
m->input_buffer_pos_ = 0; if (cinfo->unread_marker == 0xd0 + m->next_restart_marker_) {
cinfo->unread_marker = 0;
} else { if (!(*cinfo->src->resync_to_restart)(cinfo, m->next_restart_marker_)) { return JPEG_SUSPENDED;
}
}
m->next_restart_marker_ += 1;
m->next_restart_marker_ &= 0x7;
m->restarts_to_go_ = cinfo->restart_interval; if (cinfo->unread_marker != 0) {
JPEGLI_WARN("Failed to resync to next restart marker, skipping scan."); return JPEG_SCAN_COMPLETED;
} continue;
} if (status == kHandleMarkerProcessor) {
JXL_DASSERT(m->input_buffer_.size() <=
m->input_buffer_pos_ + src->bytes_in_buffer);
m->input_buffer_.clear();
m->input_buffer_pos_ = 0; if (!(*GetMarkerProcessor(cinfo))(cinfo)) { return JPEG_SUSPENDED;
}
cinfo->unread_marker = 0; continue;
} if (status != kNeedMoreInput) { break;
} if (m->input_buffer_.empty()) {
JXL_DASSERT(m->input_buffer_pos_ == 0);
m->input_buffer_.assign(src->next_input_byte,
src->next_input_byte + src->bytes_in_buffer);
} if (!(*cinfo->src->fill_input_buffer)(cinfo)) {
m->input_buffer_.clear();
m->input_buffer_pos_ = 0; return JPEG_SUSPENDED;
} if (src->bytes_in_buffer == 0) {
JPEGLI_ERROR("Empty input.");
}
m->input_buffer_.insert(m->input_buffer_.end(), src->next_input_byte,
src->next_input_byte + src->bytes_in_buffer);
} if (status == JPEG_SCAN_COMPLETED) {
cinfo->global_state = kDecProcessMarkers;
} elseif (status == JPEG_REACHED_SOS) { if (cinfo->global_state == kDecInHeader) {
cinfo->global_state = kDecHeaderDone;
} else {
PrepareForScan(cinfo);
}
} return status;
}
boolean jpegli_start_output(j_decompress_ptr cinfo, int scan_number) {
jpeg_decomp_master* m = cinfo->master; if (!cinfo->buffered_image) {
JPEGLI_ERROR("jpegli_start_output: buffered image mode was not set");
} if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_start_output: unexpected state %d",
cinfo->global_state);
}
cinfo->output_scan_number = std::max(1, scan_number); if (m->found_eoi_) {
cinfo->output_scan_number =
std::min(cinfo->output_scan_number, cinfo->input_scan_number);
}
jpegli::InitProgressMonitorForOutput(cinfo);
jpegli::PrepareForOutput(cinfo); if (cinfo->quantize_colors) { return jpegli::PrepareQuantizedOutput(cinfo);
} else { returnTRUE;
}
}
boolean jpegli_finish_output(j_decompress_ptr cinfo) { if (!cinfo->buffered_image) {
JPEGLI_ERROR("jpegli_finish_output: buffered image mode was not set");
} if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_finish_output: unexpected state %d",
cinfo->global_state);
} // Advance input to the start of the next scan, or to the end of input. while (cinfo->input_scan_number <= cinfo->output_scan_number &&
!cinfo->master->found_eoi_) { if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { returnFALSE;
}
} returnTRUE;
}
JDIMENSION jpegli_read_scanlines(j_decompress_ptr cinfo, JSAMPARRAY scanlines,
JDIMENSION max_lines) {
jpeg_decomp_master* m = cinfo->master; if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_read_scanlines: unexpected state %d",
cinfo->global_state);
} if (cinfo->buffered_image) { if (cinfo->output_scan_number == 0) {
JPEGLI_ERROR( "jpegli_read_scanlines: " "jpegli_start_output() was not called");
}
} elseif (m->is_multiscan_ && !m->found_eoi_) {
JPEGLI_ERROR( "jpegli_read_scanlines: " "jpegli_start_decompress() did not finish");
} if (cinfo->output_scanline + max_lines > cinfo->output_height) {
max_lines = cinfo->output_height - cinfo->output_scanline;
}
jpegli::ProgressMonitorOutputPass(cinfo);
size_t num_output_rows = 0; while (num_output_rows < max_lines) { if (jpegli::IsInputReady(cinfo)) {
jpegli::ProcessOutput(cinfo, &num_output_rows, scanlines, max_lines);
} elseif (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { break;
}
} return num_output_rows;
}
JDIMENSION jpegli_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) { // TODO(szabadka) Skip the IDCT for skipped over blocks. return jpegli_read_scanlines(cinfo, nullptr, num_lines);
}
void jpegli_crop_scanline(j_decompress_ptr cinfo, JDIMENSION* xoffset,
JDIMENSION* width) {
jpeg_decomp_master* m = cinfo->master; if ((cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) ||
cinfo->output_scanline != 0) {
JPEGLI_ERROR("jpegli_crop_decompress: unexpected state %d",
cinfo->global_state);
} if (cinfo->raw_data_out) {
JPEGLI_ERROR("Output cropping is not supported in raw data mode");
} if (xoffset == nullptr || width == nullptr || *width == 0 ||
*xoffset + *width > cinfo->output_width) {
JPEGLI_ERROR("jpegli_crop_scanline: Invalid arguments");
} // TODO(szabadka) Skip the IDCT for skipped over blocks.
size_t xend = *xoffset + *width;
size_t iMCU_width = m->min_scaled_dct_size * cinfo->max_h_samp_factor;
*xoffset = (*xoffset / iMCU_width) * iMCU_width;
*width = xend - *xoffset;
cinfo->master->xoffset_ = *xoffset;
cinfo->output_width = *width;
}
JDIMENSION jpegli_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data,
JDIMENSION max_lines) { if ((cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) ||
!cinfo->raw_data_out) {
JPEGLI_ERROR("jpegli_read_raw_data: unexpected state %d",
cinfo->global_state);
}
size_t iMCU_height = cinfo->max_v_samp_factor * DCTSIZE; if (max_lines < iMCU_height) {
JPEGLI_ERROR("jpegli_read_raw_data: output buffer too small");
}
jpegli::ProgressMonitorOutputPass(cinfo); while (!jpegli::IsInputReady(cinfo)) { if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { return 0;
}
} if (cinfo->output_iMCU_row < cinfo->total_iMCU_rows) {
jpegli::ProcessRawOutput(cinfo, data); return iMCU_height;
} return 0;
}
jvirt_barray_ptr* jpegli_read_coefficients(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
m->streaming_mode_ = false; if (!cinfo->buffered_image && cinfo->global_state == jpegli::kDecHeaderDone) {
jpegli::AllocateCoefficientBuffer(cinfo);
jpegli_calc_output_dimensions(cinfo);
jpegli::InitProgressMonitor(cinfo, /*coef_only=*/true);
jpegli::PrepareForScan(cinfo);
} if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_read_coefficients: unexpected state %d",
cinfo->global_state);
} if (!cinfo->buffered_image) { while (!m->found_eoi_) {
jpegli::ProgressMonitorInputPass(cinfo); if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { return nullptr;
}
}
cinfo->output_scanline = cinfo->output_height;
} return m->coef_arrays;
}
boolean jpegli_finish_decompress(j_decompress_ptr cinfo) { if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_finish_decompress: unexpected state %d",
cinfo->global_state);
} if (!cinfo->buffered_image && cinfo->output_scanline < cinfo->output_height) {
JPEGLI_ERROR("Incomplete output");
} while (!cinfo->master->found_eoi_) { if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) { returnFALSE;
}
}
(*cinfo->src->term_source)(cinfo);
jpegli_abort_decompress(cinfo); returnTRUE;
}
boolean jpegli_resync_to_restart(j_decompress_ptr cinfo, int desired) {
JPEGLI_WARN("Invalid restart marker found: 0x%02x vs 0x%02x.",
cinfo->unread_marker, 0xd0 + desired); // This is a trivial implementation, we just let the decoder skip the entire // scan and attempt to render the partial input. returnTRUE;
}
void jpegli_new_colormap(j_decompress_ptr cinfo) { if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_new_colormap: unexpected state %d",
cinfo->global_state);
} if (!cinfo->buffered_image) {
JPEGLI_ERROR("jpegli_new_colormap: not in buffered image mode");
} if (!cinfo->enable_external_quant) {
JPEGLI_ERROR("external colormap quantizer was not enabled");
} if (!cinfo->quantize_colors || cinfo->colormap == nullptr) {
JPEGLI_ERROR("jpegli_new_colormap: not in external colormap mode");
}
cinfo->master->regenerate_inverse_colormap_ = true;
}
void jpegli_set_output_format(j_decompress_ptr cinfo, JpegliDataType data_type,
JpegliEndianness endianness) { switch (data_type) { case JPEGLI_TYPE_UINT8: case JPEGLI_TYPE_UINT16: case JPEGLI_TYPE_FLOAT:
cinfo->master->output_data_type_ = data_type; break; default:
JPEGLI_ERROR("Unsupported data type %d", data_type);
} switch (endianness) { case JPEGLI_NATIVE_ENDIAN:
cinfo->master->swap_endianness_ = false; break; case JPEGLI_LITTLE_ENDIAN:
cinfo->master->swap_endianness_ = !IsLittleEndian(); break; case JPEGLI_BIG_ENDIAN:
cinfo->master->swap_endianness_ = IsLittleEndian(); break; default:
JPEGLI_ERROR("Unsupported endianness %d", endianness);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.