// 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 CheckState(j_compress_ptr cinfo, int state) { if (cinfo->global_state != state) {
JPEGLI_ERROR("Unexpected global state %d [expected %d]",
cinfo->global_state, state);
}
}
void CheckState(j_compress_ptr cinfo, int state1, int state2) { if (cinfo->global_state != state1 && cinfo->global_state != state2) {
JPEGLI_ERROR("Unexpected global state %d [expected %d or %d]",
cinfo->global_state, state1, state2);
}
}
jpeg_scan_info* next_scan = cinfo->script_space; for (constauto& scan : progressive_mode) { int comps = scan.interleaved ? MAX_COMPS_IN_SCAN : 1; for (int c = 0; c < cinfo->num_components; c += comps) {
next_scan->Ss = scan.Ss;
next_scan->Se = scan.Se;
next_scan->Ah = scan.Ah;
next_scan->Al = scan.Al;
next_scan->comps_in_scan = std::min(comps, cinfo->num_components - c); for (int j = 0; j < next_scan->comps_in_scan; ++j) {
next_scan->component_index[j] = c + j;
}
++next_scan;
}
}
JPEGLI_CHECK(next_scan - cinfo->script_space == cinfo->script_space_size);
cinfo->scan_info = cinfo->script_space;
cinfo->num_scans = cinfo->script_space_size;
}
void ValidateScanScript(j_compress_ptr cinfo) { // Mask of coefficient bits defined by the scan script, for each component // and coefficient index.
uint16_t comp_mask[kMaxComponents][DCTSIZE2] = {}; static constexpr int kMaxRefinementBit = 10;
for (int i = 0; i < cinfo->num_scans; ++i) { const jpeg_scan_info& si = cinfo->scan_info[i]; if (si.comps_in_scan < 1 || si.comps_in_scan > MAX_COMPS_IN_SCAN) {
JPEGLI_ERROR("Invalid number of components in scan %d", si.comps_in_scan);
} int last_ci = -1; for (int j = 0; j < si.comps_in_scan; ++j) { int ci = si.component_index[j]; if (ci < 0 || ci >= cinfo->num_components) {
JPEGLI_ERROR("Invalid component index %d in scan", ci);
} elseif (ci == last_ci) {
JPEGLI_ERROR("Duplicate component index %d in scan", ci);
} elseif (ci < last_ci) {
JPEGLI_ERROR("Out of order component index %d in scan", ci);
}
last_ci = ci;
} if (si.Ss < 0 || si.Se < si.Ss || si.Se >= DCTSIZE2) {
JPEGLI_ERROR("Invalid spectral range %d .. %d in scan", si.Ss, si.Se);
} if (si.Ah < 0 || si.Al < 0 || si.Al > kMaxRefinementBit) {
JPEGLI_ERROR("Invalid refinement bits %d/%d", si.Ah, si.Al);
} if (!cinfo->progressive_mode) { if (si.Ss != 0 || si.Se != DCTSIZE2 - 1 || si.Ah != 0 || si.Al != 0) {
JPEGLI_ERROR("Invalid scan for sequential mode");
}
} else { if (si.Ss == 0 && si.Se != 0) {
JPEGLI_ERROR("DC and AC together in progressive scan");
}
} if (si.Ss != 0 && si.comps_in_scan != 1) {
JPEGLI_ERROR("Interleaved AC only scan.");
} for (int j = 0; j < si.comps_in_scan; ++j) { int ci = si.component_index[j]; if (si.Ss != 0 && comp_mask[ci][0] == 0) {
JPEGLI_ERROR("AC before DC in component %d of scan", ci);
} for (int k = si.Ss; k <= si.Se; ++k) { if (comp_mask[ci][k] == 0) { if (si.Ah != 0) {
JPEGLI_ERROR("Invalid first scan refinement bit");
}
comp_mask[ci][k] = ((0xffff << si.Al) & 0xffff);
} else { if (comp_mask[ci][k] != ((0xffff << si.Ah) & 0xffff) ||
si.Al != si.Ah - 1) {
JPEGLI_ERROR("Invalid refinement bit progression.");
}
comp_mask[ci][k] |= 1 << si.Al;
}
}
} if (si.comps_in_scan > 1) {
size_t mcu_size = 0; for (int j = 0; j < si.comps_in_scan; ++j) { int ci = si.component_index[j];
jpeg_component_info* comp = &cinfo->comp_info[ci];
mcu_size += comp->h_samp_factor * comp->v_samp_factor;
} if (mcu_size > C_MAX_BLOCKS_IN_MCU) {
JPEGLI_ERROR("MCU size too big");
}
}
} for (int c = 0; c < cinfo->num_components; ++c) { for (int k = 0; k < DCTSIZE2; ++k) { if (comp_mask[c][k] != 0xffff) {
JPEGLI_ERROR("Incomplete scan of component %d and frequency %d", c, k);
}
}
}
}
void ProcessCompressionParams(j_compress_ptr cinfo) { if (cinfo->dest == nullptr) {
JPEGLI_ERROR("Missing destination.");
} if (cinfo->image_width < 1 || cinfo->image_height < 1 ||
cinfo->input_components < 1) {
JPEGLI_ERROR("Empty input image.");
} if (cinfo->image_width > static_cast<int>(JPEG_MAX_DIMENSION) ||
cinfo->image_height > static_cast<int>(JPEG_MAX_DIMENSION) ||
cinfo->input_components > static_cast<int>(kMaxComponents)) {
JPEGLI_ERROR("Input image too big.");
} if (cinfo->num_components < 1 ||
cinfo->num_components > static_cast<int>(kMaxComponents)) {
JPEGLI_ERROR("Invalid number of components.");
} if (cinfo->data_precision != kJpegPrecision) {
JPEGLI_ERROR("Invalid data precision");
} if (cinfo->arith_code) {
JPEGLI_ERROR("Arithmetic coding is not implemented.");
} if (cinfo->CCIR601_sampling) {
JPEGLI_ERROR("CCIR601 sampling is not implemented.");
} if (cinfo->restart_interval > 65535u) {
JPEGLI_ERROR("Restart interval too big");
} if (cinfo->smoothing_factor < 0 || cinfo->smoothing_factor > 100) {
JPEGLI_ERROR("Invalid smoothing factor %d", cinfo->smoothing_factor);
}
jpeg_comp_master* m = cinfo->master;
cinfo->max_h_samp_factor = cinfo->max_v_samp_factor = 1; for (int c = 0; c < cinfo->num_components; ++c) {
jpeg_component_info* comp = &cinfo->comp_info[c]; if (comp->component_index != c) {
JPEGLI_ERROR("Invalid component index");
} for (int j = 0; j < c; ++j) { if (cinfo->comp_info[j].component_id == comp->component_id) {
JPEGLI_ERROR("Duplicate component id %d", comp->component_id);
}
} if (comp->h_samp_factor <= 0 || comp->v_samp_factor <= 0 ||
comp->h_samp_factor > MAX_SAMP_FACTOR ||
comp->v_samp_factor > MAX_SAMP_FACTOR) {
JPEGLI_ERROR("Invalid sampling factor %d x %d", comp->h_samp_factor,
comp->v_samp_factor);
} if (cinfo->num_components == 1) { // Force samp factors to 1x1 for single-component images.
comp->h_samp_factor = comp->v_samp_factor = 1;
}
cinfo->max_h_samp_factor =
std::max(comp->h_samp_factor, cinfo->max_h_samp_factor);
cinfo->max_v_samp_factor =
std::max(comp->v_samp_factor, cinfo->max_v_samp_factor);
}
size_t iMCU_width = DCTSIZE * cinfo->max_h_samp_factor;
size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor;
size_t total_iMCU_cols = DivCeil(cinfo->image_width, iMCU_width);
cinfo->total_iMCU_rows = DivCeil(cinfo->image_height, iMCU_height);
m->xsize_blocks = total_iMCU_cols * cinfo->max_h_samp_factor;
m->ysize_blocks = cinfo->total_iMCU_rows * cinfo->max_v_samp_factor;
void InitProgressMonitor(j_compress_ptr cinfo) { if (cinfo->progress == nullptr) { return;
} if (IsStreamingSupported(cinfo)) { // We have only one input pass.
cinfo->progress->total_passes = 1;
} else { // We have one input pass, a histogram pass for each scan, and an encode // pass for each scan.
cinfo->progress->total_passes = 1 + 2 * cinfo->num_scans;
}
}
// Common setup code between streaming and transcoding code paths. Called in // both jpegli_start_compress() and jpegli_write_coefficients(). void InitCompress(j_compress_ptr cinfo, boolean write_all_tables) {
jpeg_comp_master* m = cinfo->master;
(*cinfo->err->reset_error_mgr)(reinterpret_cast<j_common_ptr>(cinfo));
ProcessCompressionParams(cinfo);
InitProgressMonitor(cinfo);
AllocateBuffers(cinfo); if (cinfo->global_state != kEncWriteCoeffs) {
ChooseInputMethod(cinfo); if (!cinfo->raw_data_in) {
ChooseColorTransform(cinfo);
ChooseDownsampleMethods(cinfo);
}
QuantPass pass = m->psnr_target > 0 ? QuantPass::SEARCH_FIRST_PASS
: QuantPass::NO_SEARCH;
InitQuantizer(cinfo, pass);
} if (write_all_tables) {
jpegli_suppress_tables(cinfo, FALSE);
} if (!cinfo->optimize_coding && !cinfo->progressive_mode) {
CopyHuffmanTables(cinfo);
InitEntropyCoder(cinfo);
}
(*cinfo->dest->init_destination)(cinfo);
WriteFileHeader(cinfo);
JpegBitWriterInit(cinfo);
m->next_iMCU_row = 0;
m->last_restart_interval = 0;
m->next_dht_index = 0;
}
void ProcessiMCURows(j_compress_ptr cinfo) {
jpeg_comp_master* m = cinfo->master;
size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor; // To have context rows both above and below the current iMCU row, we delay // processing the first iMCU row and process two iMCU rows after we receive // the last input row. if (m->next_input_row % iMCU_height == 0 && m->next_input_row > iMCU_height) {
ProcessiMCURow(cinfo);
} if (m->next_input_row >= cinfo->image_height) {
ProcessiMCURow(cinfo);
}
}
// // Non-streaming part //
void ZigZagShuffleBlocks(j_compress_ptr cinfo) {
JCOEF tmp[DCTSIZE2]; for (int c = 0; c < cinfo->num_components; ++c) {
jpeg_component_info* comp = &cinfo->comp_info[c]; for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) {
JBLOCKARRAY blocks = GetBlockRow(cinfo, c, by); for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) {
JCOEF* block = &blocks[0][bx][0]; for (int k = 0; k < DCTSIZE2; ++k) {
tmp[k] = block[kJPEGNaturalOrder[k]];
}
memcpy(block, tmp, sizeof(tmp));
}
}
}
}
void jpegli_default_colorspace(j_compress_ptr cinfo) {
CheckState(cinfo, jpegli::kEncStart); if (cinfo->in_color_space == JCS_RGB && cinfo->master->xyb_mode) {
jpegli_set_colorspace(cinfo, JCS_RGB); return;
} switch (cinfo->in_color_space) { case JCS_GRAYSCALE:
jpegli_set_colorspace(cinfo, JCS_GRAYSCALE); break; case JCS_RGB: #ifdef JCS_EXTENSIONS case JCS_EXT_RGB: case JCS_EXT_BGR: case JCS_EXT_RGBX: case JCS_EXT_BGRX: case JCS_EXT_XRGB: case JCS_EXT_XBGR: #endif #if JCS_ALPHA_EXTENSIONS case JCS_EXT_RGBA: case JCS_EXT_BGRA: case JCS_EXT_ARGB: case JCS_EXT_ABGR: #endif
jpegli_set_colorspace(cinfo, JCS_YCbCr); break; case JCS_YCbCr:
jpegli_set_colorspace(cinfo, JCS_YCbCr); break; case JCS_CMYK:
jpegli_set_colorspace(cinfo, JCS_CMYK); break; case JCS_YCCK:
jpegli_set_colorspace(cinfo, JCS_YCCK); break; case JCS_UNKNOWN:
jpegli_set_colorspace(cinfo, JCS_UNKNOWN); break; default:
JPEGLI_ERROR("Unsupported input colorspace %d", cinfo->in_color_space);
}
}
void jpegli_set_colorspace(j_compress_ptr cinfo, J_COLOR_SPACE colorspace) {
CheckState(cinfo, jpegli::kEncStart);
cinfo->jpeg_color_space = colorspace; switch (colorspace) { case JCS_GRAYSCALE:
cinfo->num_components = 1; break; case JCS_RGB: case JCS_YCbCr:
cinfo->num_components = 3; break; case JCS_CMYK: case JCS_YCCK:
cinfo->num_components = 4; break; case JCS_UNKNOWN:
cinfo->num_components =
std::min<int>(jpegli::kMaxComponents, cinfo->input_components); break; default:
JPEGLI_ERROR("Unsupported jpeg colorspace %d", colorspace);
} // Adobe marker is only needed to distinguish CMYK and YCCK JPEGs.
cinfo->write_Adobe_marker = TO_JXL_BOOL(cinfo->jpeg_color_space == JCS_YCCK); if (cinfo->comp_info == nullptr) {
cinfo->comp_info =
jpegli::Allocate<jpeg_component_info>(cinfo, MAX_COMPONENTS);
}
memset(cinfo->comp_info, 0,
jpegli::kMaxComponents * sizeof(jpeg_component_info)); for (int c = 0; c < cinfo->num_components; ++c) {
jpeg_component_info* comp = &cinfo->comp_info[c];
comp->component_index = c;
comp->component_id = c + 1;
comp->h_samp_factor = 1;
comp->v_samp_factor = 1;
comp->quant_tbl_no = 0;
comp->dc_tbl_no = 0;
comp->ac_tbl_no = 0;
} if (colorspace == JCS_RGB) {
cinfo->comp_info[0].component_id = 'R';
cinfo->comp_info[1].component_id = 'G';
cinfo->comp_info[2].component_id = 'B'; if (cinfo->master->xyb_mode) { // Subsample blue channel.
cinfo->comp_info[0].h_samp_factor = cinfo->comp_info[0].v_samp_factor = 2;
cinfo->comp_info[1].h_samp_factor = cinfo->comp_info[1].v_samp_factor = 2;
cinfo->comp_info[2].h_samp_factor = cinfo->comp_info[2].v_samp_factor = 1; // Use separate quantization tables for each component
cinfo->comp_info[1].quant_tbl_no = 1;
cinfo->comp_info[2].quant_tbl_no = 2;
}
} elseif (colorspace == JCS_CMYK) {
cinfo->comp_info[0].component_id = 'C';
cinfo->comp_info[1].component_id = 'M';
cinfo->comp_info[2].component_id = 'Y';
cinfo->comp_info[3].component_id = 'K';
} elseif (colorspace == JCS_YCbCr || colorspace == JCS_YCCK) { // Use separate quantization and Huffman tables for luma and chroma
cinfo->comp_info[1].quant_tbl_no = 1;
cinfo->comp_info[2].quant_tbl_no = 1;
cinfo->comp_info[1].dc_tbl_no = cinfo->comp_info[1].ac_tbl_no = 1;
cinfo->comp_info[2].dc_tbl_no = cinfo->comp_info[2].ac_tbl_no = 1; // Use chroma subsampling by default
cinfo->comp_info[0].h_samp_factor = cinfo->comp_info[0].v_samp_factor = 2; if (colorspace == JCS_YCCK) {
cinfo->comp_info[3].h_samp_factor = cinfo->comp_info[3].v_samp_factor = 2;
}
}
}
void jpegli_set_input_format(j_compress_ptr cinfo, JpegliDataType data_type,
JpegliEndianness endianness) {
CheckState(cinfo, jpegli::kEncStart); switch (data_type) { case JPEGLI_TYPE_UINT8: case JPEGLI_TYPE_UINT16: case JPEGLI_TYPE_FLOAT:
cinfo->master->data_type = data_type; break; default:
JPEGLI_ERROR("Unsupported data type %d", data_type);
} switch (endianness) { case JPEGLI_NATIVE_ENDIAN: case JPEGLI_LITTLE_ENDIAN: case JPEGLI_BIG_ENDIAN:
cinfo->master->endianness = endianness; break; default:
JPEGLI_ERROR("Unsupported endianness %d", endianness);
}
}
#if JPEG_LIB_VERSION >= 70 void jpegli_calc_jpeg_dimensions(j_compress_ptr cinfo) { // Since input scaling is not supported, we just copy the image dimensions.
cinfo->jpeg_width = cinfo->image_width;
cinfo->jpeg_height = cinfo->image_height;
} #endif
if (cinfo->global_state == jpegli::kEncWriteCoeffs) { // Zig-zag shuffle all the blocks. For non-transcoding case it was already // done in EncodeiMCURow().
jpegli::ZigZagShuffleBlocks(cinfo);
}
if (m->psnr_target > 0) {
jpegli::QuantizetoPSNR(cinfo);
}
if (!tokens_done) {
jpegli::TokenizeJpeg(cinfo);
}
if (cinfo->optimize_coding || cinfo->progressive_mode) {
jpegli::OptimizeHuffmanCodes(cinfo);
jpegli::InitEntropyCoder(cinfo);
}
if (!bitstream_done) {
jpegli::WriteFrameHeader(cinfo); for (int i = 0; i < cinfo->num_scans; ++i) {
jpegli::WriteScanHeader(cinfo, i);
jpegli::WriteScanData(cinfo, i);
}
} else {
JumpToByteBoundary(&m->bw); if (!EmptyBitWriterBuffer(&m->bw)) {
JPEGLI_ERROR("Output suspension is not supported in finish_compress");
}
}
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.