// 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.
// Max 14 block per MCU (when 1 channel is subsampled) // Max 64 nonzero coefficients per block // Max 16 symbol bits plus 11 extra bits per nonzero symbol // Max 2 bytes per 8 bits (worst case is all bytes are escaped 0xff)
constexpr int kMaxMCUByteSize = 6048;
// Helper structure to read bits from the entropy coded data segment. struct BitReaderState {
BitReaderState(const uint8_t* data, const size_t len, size_t pos)
: data_(data), len_(len), start_pos_(pos) {
Reset(pos);
}
// Returns the next byte and skips the 0xff/0x00 escape sequences.
uint8_t GetNextByte() { if (pos_ >= next_marker_pos_) {
++pos_; return 0;
}
uint8_t c = data_[pos_++]; if (c == 0xff) {
uint8_t escape = pos_ < len_ ? data_[pos_] : 0; if (escape == 0) {
++pos_;
} else { // 0xff was followed by a non-zero byte, which means that we found the // start of the next marker segment.
next_marker_pos_ = pos_ - 1;
}
} return c;
}
// Sets *pos to the next stream position, and *bit_pos to the bit position // within the next byte where parsing should continue. // Returns false if the stream ended too early. bool FinishStream(size_t* pos, size_t* bit_pos) {
*bit_pos = (8 - (bits_left_ & 7)) & 7; // Give back some bytes that we did not use. int unused_bytes_left = DivCeil(bits_left_, 8); while (unused_bytes_left-- > 0) {
--pos_; // If we give back a 0 byte, we need to check if it was a 0xff/0x00 escape // sequence, and if yes, we need to give back one more byte. if (((pos_ == len_ && pos_ == next_marker_pos_) ||
(pos_ > 0 && pos_ < next_marker_pos_ && data_[pos_] == 0)) &&
(data_[pos_ - 1] == 0xff)) {
--pos_;
}
} if (pos_ >= next_marker_pos_) {
*pos = next_marker_pos_; if (pos_ > next_marker_pos_ || *bit_pos > 0) { // Data ran out before the scan was complete. returnfalse;
}
}
*pos = pos_; returntrue;
}
// Returns the next Huffman-coded symbol. int ReadSymbol(const HuffmanTableEntry* table, BitReaderState* br) { int nbits;
br->FillBitWindow(); int val = (br->val_ >> (br->bits_left_ - 8)) & 0xff;
table += val;
nbits = table->bits - 8; if (nbits > 0) {
br->bits_left_ -= 8;
table += table->value;
val = (br->val_ >> (br->bits_left_ - nbits)) & ((1 << nbits) - 1);
table += val;
}
br->bits_left_ -= table->bits; return table->value;
}
/** * Returns the DC diff or AC value for extra bits value x and prefix code s. * * CCITT Rec. T.81 (1992 E) * Table F.1 – Difference magnitude categories for DC coding * SSSS | DIFF values * ------+-------------------------- * 0 | 0 * 1 | –1, 1 * 2 | –3, –2, 2, 3 * 3 | –7..–4, 4..7 * ......|.......................... * 11 | –2047..–1024, 1024..2047 * * CCITT Rec. T.81 (1992 E) * Table F.2 – Categories assigned to coefficient values * [ Same as Table F.1, but does not include SSSS equal to 0 and 11] * * * CCITT Rec. T.81 (1992 E) * F.1.2.1.1 Structure of DC code table * For each category,... additional bits... appended... to uniquely identify * which difference... occurred... When DIFF is positive... SSSS... bits of DIFF * are appended. When DIFF is negative... SSSS... bits of (DIFF – 1) are * appended... Most significant bit... is 0 for negative differences and 1 for * positive differences. * * In other words the upper half of extra bits range represents DIFF as is. * The lower half represents the negative DIFFs with an offset.
*/ int HuffExtend(int x, int s) {
JXL_DASSERT(s > 0); int half = 1 << (s - 1); if (x >= half) {
JXL_DASSERT(x < (1 << s)); return x;
} else { return x - (1 << s) + 1;
}
}
// Decodes one 8x8 block of DCT coefficients from the bit stream. bool DecodeDCTBlock(const HuffmanTableEntry* dc_huff, const HuffmanTableEntry* ac_huff, int Ss, int Se, int Al, int* eobrun, BitReaderState* br, coeff_t* last_dc_coeff,
coeff_t* coeffs) { // Nowadays multiplication is even faster than variable shift. int Am = 1 << Al; bool eobrun_allowed = Ss > 0; if (Ss == 0) { int s = ReadSymbol(dc_huff, br); if (s >= kJpegDCAlphabetSize) { returnfalse;
} int diff = 0; if (s > 0) { int bits = br->ReadBits(s);
diff = HuffExtend(bits, s);
} int coeff = diff + *last_dc_coeff; constint dc_coeff = coeff * Am;
coeffs[0] = dc_coeff; // TODO(eustas): is there a more elegant / explicit way to check this? if (dc_coeff != coeffs[0]) { returnfalse;
}
*last_dc_coeff = coeff;
++Ss;
} if (Ss > Se) { returntrue;
} if (*eobrun > 0) {
--(*eobrun); returntrue;
} for (int k = Ss; k <= Se; k++) { int sr = ReadSymbol(ac_huff, br); if (sr >= kJpegHuffmanAlphabetSize) { returnfalse;
} int r = sr >> 4; int s = sr & 15; if (s > 0) {
k += r; if (k > Se) { returnfalse;
} if (s + Al >= kJpegDCAlphabetSize) { returnfalse;
} int bits = br->ReadBits(s); int coeff = HuffExtend(bits, s);
coeffs[kJPEGNaturalOrder[k]] = coeff * Am;
} elseif (r == 15) {
k += 15;
} else {
*eobrun = 1 << r; if (r > 0) { if (!eobrun_allowed) { returnfalse;
}
*eobrun += br->ReadBits(r);
} break;
}
}
--(*eobrun); returntrue;
}
bool RefineDCTBlock(const HuffmanTableEntry* ac_huff, int Ss, int Se, int Al, int* eobrun, BitReaderState* br, coeff_t* coeffs) { // Nowadays multiplication is even faster than variable shift. int Am = 1 << Al; bool eobrun_allowed = Ss > 0; if (Ss == 0) { int s = br->ReadBits(1);
coeff_t dc_coeff = coeffs[0];
dc_coeff |= s * Am;
coeffs[0] = dc_coeff;
++Ss;
} if (Ss > Se) { returntrue;
} int p1 = Am; int m1 = -Am; int k = Ss; int r; int s; bool in_zero_run = false; if (*eobrun <= 0) { for (; k <= Se; k++) {
s = ReadSymbol(ac_huff, br); if (s >= kJpegHuffmanAlphabetSize) { returnfalse;
}
r = s >> 4;
s &= 15; if (s) { if (s != 1) { returnfalse;
}
s = br->ReadBits(1) ? p1 : m1;
in_zero_run = false;
} else { if (r != 15) {
*eobrun = 1 << r; if (r > 0) { if (!eobrun_allowed) { returnfalse;
}
*eobrun += br->ReadBits(r);
} break;
}
in_zero_run = true;
} do {
coeff_t thiscoef = coeffs[kJPEGNaturalOrder[k]]; if (thiscoef != 0) { if (br->ReadBits(1)) { if ((thiscoef & p1) == 0) { if (thiscoef >= 0) {
thiscoef += p1;
} else {
thiscoef += m1;
}
}
}
coeffs[kJPEGNaturalOrder[k]] = thiscoef;
} else { if (--r < 0) { break;
}
}
k++;
} while (k <= Se); if (s) { if (k > Se) { returnfalse;
}
coeffs[kJPEGNaturalOrder[k]] = s;
}
}
} if (in_zero_run) { returnfalse;
} if (*eobrun > 0) { for (; k <= Se; k++) {
coeff_t thiscoef = coeffs[kJPEGNaturalOrder[k]]; if (thiscoef != 0) { if (br->ReadBits(1)) { if ((thiscoef & p1) == 0) { if (thiscoef >= 0) {
thiscoef += p1;
} else {
thiscoef += m1;
}
}
}
coeffs[kJPEGNaturalOrder[k]] = thiscoef;
}
}
}
--(*eobrun); returntrue;
}
bool FinishScan(j_decompress_ptr cinfo, const uint8_t* data, const size_t len,
size_t* pos, size_t* bit_pos) {
jpeg_decomp_master* m = cinfo->master; if (m->eobrun_ > 0) {
JPEGLI_ERROR("End-of-block run too long.");
}
m->eobrun_ = -1;
memset(m->last_dc_coeff_, 0, sizeof(m->last_dc_coeff_)); if (*bit_pos == 0) { returntrue;
} if (data[*pos] == 0xff) { // After last br.FinishStream we checked that there is at least 2 bytes // in the buffer.
JXL_DASSERT(*pos + 1 < len); // br.FinishStream would have detected an early marker.
JXL_DASSERT(data[*pos + 1] == 0);
*pos += 2;
} else {
*pos += 1;
}
*bit_pos = 0; returntrue;
}
} // namespace
void PrepareForiMCURow(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master; for (int i = 0; i < cinfo->comps_in_scan; ++i) { const jpeg_component_info* comp = cinfo->cur_comp_info[i]; int c = comp->component_index; int by0 = cinfo->input_iMCU_row * comp->v_samp_factor; int block_rows_left = comp->height_in_blocks - by0; int max_block_rows = std::min(comp->v_samp_factor, block_rows_left); int offset = m->streaming_mode_ ? 0 : by0;
m->coeff_rows[c] = (*cinfo->mem->access_virt_barray)( reinterpret_cast<j_common_ptr>(cinfo), m->coef_arrays[c], offset,
max_block_rows, TRUE);
}
}
int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data, const size_t len, size_t* pos, size_t* bit_pos) { if (len == 0) { return kNeedMoreInput;
}
jpeg_decomp_master* m = cinfo->master; for (;;) { // Handle the restart intervals. if (cinfo->restart_interval > 0 && m->restarts_to_go_ == 0) { if (!FinishScan(cinfo, data, len, pos, bit_pos)) { return kNeedMoreInput;
} // Go to the next marker, warn if we had to skip any data.
size_t num_skipped = 0; while (*pos + 1 < len && (data[*pos] != 0xff || data[*pos + 1] == 0 ||
data[*pos + 1] == 0xff)) {
++(*pos);
++num_skipped;
} if (num_skipped > 0) {
JPEGLI_WARN("Skipped %d bytes before restart marker", static_cast<int>(num_skipped));
} if (*pos + 2 > len) { return kNeedMoreInput;
}
cinfo->unread_marker = data[*pos + 1];
*pos += 2; return kHandleRestart;
}
// Decode one MCU.
HWY_ALIGN_MAX static coeff_t sink_block[DCTSIZE2] = {0}; bool scan_ok = true; for (int i = 0; i < cinfo->comps_in_scan; ++i) { const jpeg_component_info* comp = cinfo->cur_comp_info[i]; int c = comp->component_index; const HuffmanTableEntry* dc_lut =
&m->dc_huff_lut_[comp->dc_tbl_no * kJpegHuffmanLutSize]; const HuffmanTableEntry* ac_lut =
&m->ac_huff_lut_[comp->ac_tbl_no * kJpegHuffmanLutSize]; for (int iy = 0; iy < comp->MCU_height; ++iy) {
size_t block_y = m->scan_mcu_row_ * comp->MCU_height + iy; int biy = block_y % comp->v_samp_factor; for (int ix = 0; ix < comp->MCU_width; ++ix) {
size_t block_x = m->scan_mcu_col_ * comp->MCU_width + ix;
coeff_t* coeffs; if (block_x >= comp->width_in_blocks ||
block_y >= comp->height_in_blocks) { // Note that it is OK that sink_block is uninitialized because // it will never be used in any branches, even in the RefineDCTBlock // case, because only DC scans can be interleaved and we don't use // the zero-ness of the DC coeff in the DC refinement code-path.
coeffs = sink_block;
} else {
coeffs = &m->coeff_rows[c][biy][block_x][0];
} if (cinfo->Ah == 0) { if (!DecodeDCTBlock(dc_lut, ac_lut, cinfo->Ss, cinfo->Se, cinfo->Al,
&m->eobrun_, &br,
&m->last_dc_coeff_[comp->component_index],
coeffs)) {
scan_ok = false;
}
} else { if (!RefineDCTBlock(ac_lut, cinfo->Ss, cinfo->Se, cinfo->Al,
&m->eobrun_, &br, coeffs)) {
scan_ok = false;
}
}
}
}
}
size_t new_pos;
size_t new_bit_pos; bool stream_ok = br.FinishStream(&new_pos, &new_bit_pos); if (new_pos + 2 > len) { // If reading stopped within the last two bytes, we have to request more // input even if FinishStream() returned true, since the Huffman code // reader could have peaked ahead some bits past the current input chunk // and thus the last prefix code length could have been wrong. We can do // this because a valid JPEG bit stream has two extra bytes at the end.
RestoreMCUCodingState(cinfo); return kNeedMoreInput;
}
*pos = new_pos;
*bit_pos = new_bit_pos; if (!stream_ok) { // We hit a marker during parsing.
JXL_DASSERT(data[*pos] == 0xff);
JXL_DASSERT(data[*pos + 1] != 0);
RestoreMCUCodingState(cinfo);
JPEGLI_WARN("Incomplete scan detected."); return JPEG_SCAN_COMPLETED;
} if (!scan_ok) {
JPEGLI_ERROR("Failed to decode DCT block");
} if (m->restarts_to_go_ > 0) {
--m->restarts_to_go_;
}
++m->scan_mcu_col_; if (m->scan_mcu_col_ == cinfo->MCUs_per_row) {
++m->scan_mcu_row_;
m->scan_mcu_col_ = 0; if (m->scan_mcu_row_ == cinfo->MCU_rows_in_scan) { if (!FinishScan(cinfo, data, len, pos, bit_pos)) { return kNeedMoreInput;
} break;
} elseif ((m->scan_mcu_row_ % m->mcu_rows_per_iMCU_row_) == 0) { // Current iMCU row is done. break;
}
}
}
++cinfo->input_iMCU_row; if (cinfo->input_iMCU_row < cinfo->total_iMCU_rows) {
PrepareForiMCURow(cinfo); return JPEG_ROW_COMPLETED;
} return JPEG_SCAN_COMPLETED;
}
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.