// Finish up at the end of a Huffman-compressed scan. static void finish_pass_huff_sq(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; shuff_entropy_encoder entropy = (shuff_entropy_encoder)lossyc.entropy_private; working_state_sq state; state.cur.last_dc_val = new int[MAX_COMPS_IN_SCAN]; // Load up working state ... flush_bits needs it state.output_bytes = cinfo.dest.output_bytes; state.next_output_byte = cinfo.dest.next_output_byte; state.free_in_buffer = cinfo.dest.free_in_buffer; state.cur = entropy.saved; entropy.saved.last_dc_val.CopyTo(state.cur.last_dc_val, 0); state.cinfo = cinfo; // Flush out the last data if (!flush_bits(ref state)) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CANT_SUSPEND); } // Update state cinfo.dest.output_bytes = state.output_bytes; cinfo.dest.next_output_byte = state.next_output_byte; cinfo.dest.free_in_buffer = state.free_in_buffer; entropy.saved = state.cur; state.cur.last_dc_val.CopyTo(entropy.saved.last_dc_val, 0); }
// Process some data in subsequent passes of a multi-pass case. // We process the equivalent of one fully interleaved MCU row ("iMCU" row) // per call, ie, v_samp_factor block rows for each component in the scan. // The data is obtained from the arrays and fed to the entropy coder. // Returns true if the iMCU row is completed, false if suspended. // // NB: input_buf is ignored; it is likely to be a null pointer. static bool compress_output_coef(jpeg_compress cinfo, byte[][][] input_buf) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = (c_coef_controller)lossyc.coef_private; short[][][][] buffer = new short[MAX_COMPS_IN_SCAN][][][]; int[] buffer_ind = new int[MAX_COMPS_IN_SCAN]; // Align the buffers for the components used in this scan. // NB: during first pass, this is safe only because the buffers will // already be aligned properly, so jmemmgr.cs won't need to do any I/O. for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; buffer[ci] = coef.whole_image[compptr.component_index]; buffer_ind[ci] = (int)coef.iMCU_row_num * compptr.v_samp_factor; } // Loop to process one whole iMCU row for (int yoffset = coef.MCU_vert_offset; yoffset < coef.MCU_rows_per_iMCU_row; yoffset++) { for (uint MCU_col_num = coef.mcu_ctr; MCU_col_num < cinfo.MCUs_per_row; MCU_col_num++) { // Construct list of pointers to DCT blocks belonging to this MCU int blkn = 0; // index of current DCT block within MCU for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; uint start_col = MCU_col_num * (uint)compptr.MCU_width; for (int yindex = 0; yindex < compptr.MCU_height; yindex++) { short[][] buffer_ptr = buffer[ci][buffer_ind[ci] + yindex + yoffset]; uint buffer_ptr_ind = start_col; for (int xindex = 0; xindex < compptr.MCU_width; xindex++) { coef.MCU_buffer[blkn++] = buffer_ptr[buffer_ptr_ind++]; } } } // Try to write the MCU. if (!lossyc.entropy_encode_mcu(cinfo, coef.MCU_buffer)) { // Suspension forced; update state counters and exit coef.MCU_vert_offset = yoffset; coef.mcu_ctr = MCU_col_num; return(false); } } // Completed an MCU row, but perhaps not an iMCU row coef.mcu_ctr = 0; } // Completed the iMCU row, advance counters for next one coef.iMCU_row_num++; start_iMCU_row_c_coef(cinfo); return(true); }
// Initialize for a processing pass. static void start_pass_lossy(jpeg_compress cinfo, J_BUF_MODE pass_mode) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; lossyc.fdct_start_pass(cinfo); lossyc.coef_start_pass(cinfo, pass_mode); }
// Module initialization routine for Huffman entropy encoding. static void jinit_shuff_encoder(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; shuff_entropy_encoder entropy = null; try { entropy = new shuff_entropy_encoder(); entropy.saved.last_dc_val = new int[MAX_COMPS_IN_SCAN]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } lossyc.entropy_private = entropy; lossyc.entropy_start_pass = start_pass_huff; lossyc.need_optimization_pass = need_optimization_pass_sq; // Mark tables unallocated for (int i = 0; i < NUM_HUFF_TBLS; i++) { entropy.dc_derived_tbls[i] = entropy.ac_derived_tbls[i] = null; #if ENTROPY_OPT_SUPPORTED entropy.dc_count_ptrs[i] = entropy.ac_count_ptrs[i] = null; #endif } }
// Initialize FDCT manager. static void jinit_forward_dct(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; fdct_controller fdct = null; try { fdct = new fdct_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } lossyc.fdct_private = fdct; lossyc.fdct_start_pass = start_pass_fdctmgr; fdct.do_dct = jpeg_fdct_ifast; lossyc.fdct_forward_DCT = forward_DCT; #if DCT_FLOAT_SUPPORTED fdct.do_float_dct = jpeg_fdct_float; if (cinfo.useFloatDCT) { lossyc.fdct_forward_DCT = forward_DCT_float; } #endif // Mark divisor tables unallocated for (int i = 0; i < NUM_QUANT_TBLS; i++) { fdct.divisors[i] = null; #if DCT_FLOAT_SUPPORTED fdct.float_divisors[i] = null; #endif } }
// Reset within-iMCU-row counters for a new row static void start_iMCU_row_c_coef(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = (c_coef_controller)lossyc.coef_private; // In an interleaved scan, an MCU row is the same as an iMCU row. // In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. // But at the bottom of the image, process only what's left. if (cinfo.comps_in_scan > 1) { coef.MCU_rows_per_iMCU_row = 1; } else { if (coef.iMCU_row_num < (cinfo.total_iMCU_rows - 1)) { coef.MCU_rows_per_iMCU_row = cinfo.cur_comp_info[0].v_samp_factor; } else { coef.MCU_rows_per_iMCU_row = cinfo.cur_comp_info[0].last_row_height; } } coef.mcu_ctr = 0; coef.MCU_vert_offset = 0; }
// Trial-encode one MCU's worth of Huffman-compressed coefficients. // No data is actually output, so no suspension return is possible. static bool encode_mcu_gather_sq(jpeg_compress cinfo, short[][] MCU_data) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; shuff_entropy_encoder entropy = (shuff_entropy_encoder)lossyc.entropy_private; // Take care of restart intervals if needed if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { // Re-initialize DC predictions to 0 for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { entropy.saved.last_dc_val[ci] = 0; } // Update restart state entropy.restarts_to_go = cinfo.restart_interval; } entropy.restarts_to_go--; } for (int blkn = 0; blkn < cinfo.block_in_MCU; blkn++) { int ci = cinfo.MCU_membership[blkn]; jpeg_component_info compptr = cinfo.cur_comp_info[ci]; htest_one_block_sq(cinfo, MCU_data[blkn], entropy.saved.last_dc_val[ci], entropy.dc_count_ptrs[compptr.dc_tbl_no], entropy.ac_count_ptrs[compptr.ac_tbl_no]); entropy.saved.last_dc_val[ci] = MCU_data[blkn][0]; } return(true); }
// Module initialization routine for progressive Huffman entropy encoding. static void jinit_phuff_encoder(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; phuff_entropy_encoder entropy = null; try { entropy = new phuff_entropy_encoder(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } lossyc.entropy_private = entropy; lossyc.entropy_start_pass = start_pass_phuff; lossyc.need_optimization_pass = need_optimization_pass_phuff; // Mark tables unallocated for (int i = 0; i < NUM_HUFF_TBLS; i++) { entropy.derived_tbls[i] = null; entropy.count_ptrs[i] = null; } entropy.bit_buffer = null; // needed only in AC refinement scan }
static void jinit_c_coef_controller(jpeg_compress cinfo, bool need_full_buffer) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = null; try { coef = new c_coef_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } lossyc.coef_private = coef; lossyc.coef_start_pass = start_pass_coef; // Create the coefficient buffer. if (need_full_buffer) { #if FULL_COEF_BUFFER_SUPPORTED // Allocate a full-image array for each component, // padded to a multiple of samp_factor DCT blocks in each direction. for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; coef.whole_image[ci] = alloc_barray(cinfo, (uint)jround_up((int)compptr.width_in_blocks, compptr.h_samp_factor), (uint)jround_up((int)compptr.height_in_blocks, compptr.v_samp_factor)); } #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); #endif } else { // We only need a single-MCU buffer. try { for (int i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { coef.MCU_buffer[i] = new short[DCTSIZE2]; } } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } coef.whole_image[0] = null; // flag for no arrays } }
// Finish up a statistics-gathering pass and create the new Huffman tables. static void finish_pass_gather_phuff(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; phuff_entropy_encoder entropy = (phuff_entropy_encoder)lossyc.entropy_private; // Flush out buffered data (all we care about is counting the EOB symbol) emit_eobrun(entropy); bool is_DC_band = (cinfo.Ss == 0); // It's important not to apply jpeg_gen_optimal_table more than once // per table, because it clobbers the input frequency counts! bool[] did = new bool[NUM_HUFF_TBLS]; for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; int tbl; if (is_DC_band) { if (cinfo.Ah != 0) { continue; // DC refinement needs no table } tbl = compptr.dc_tbl_no; } else { tbl = compptr.ac_tbl_no; } if (!did[tbl]) { if (is_DC_band) { if (cinfo.dc_huff_tbl_ptrs[tbl] == null) { cinfo.dc_huff_tbl_ptrs[tbl] = jpeg_alloc_huff_table(cinfo); } jpeg_gen_optimal_table(cinfo, cinfo.dc_huff_tbl_ptrs[tbl], entropy.count_ptrs[tbl]); } else { if (cinfo.ac_huff_tbl_ptrs[tbl] == null) { cinfo.ac_huff_tbl_ptrs[tbl] = jpeg_alloc_huff_table(cinfo); } jpeg_gen_optimal_table(cinfo, cinfo.ac_huff_tbl_ptrs[tbl], entropy.count_ptrs[tbl]); } did[tbl] = true; } } }
// This version is used for floating-point DCT implementations. static void forward_DCT_float(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] sample_data, short[][] coef_blocks, int coef_offset, uint start_row, uint start_col, uint num_blocks) { // This routine is heavily used, so it's worth coding it tightly jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; fdct_controller fdct = (fdct_controller)lossyc.fdct_private; float_DCT_method_ptr do_dct = fdct.do_float_dct; double[] divisors = fdct.float_divisors[compptr.quant_tbl_no]; double[] workspace = new double[DCTSIZE2]; // work area for FDCT subroutine for (int bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { // Load data into workspace, applying unsigned->signed conversion int wsptr = 0; for (int elemr = 0; elemr < DCTSIZE; elemr++) { byte[] elem = sample_data[start_row + elemr]; uint eptr = start_col; // unroll the inner loop workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; } // Perform the DCT do_dct(workspace); // Quantize/descale the coefficients, and store into coef_blocks[] //old short[] output_ptr=coef_blocks[coef_offset][bi]; short[] output_ptr = coef_blocks[coef_offset + bi]; for (int i = 0; i < DCTSIZE2; i++) { // Apply the quantization and scaling factor double temp = workspace[i] * divisors[i]; // Round to nearest integer. // Since C does not specify the direction of rounding for negative // quotients, we have to force the dividend positive for portability. // The maximum coefficient size is +-16K (for 12-bit data), so this // code should work for either 16-bit or 32-bit ints. output_ptr[i] = (short)((int)(temp + 16384.5) - 16384); } } }
// MCU encoding for DC successive approximation refinement scan. // Note: we assume such scans can be multi-component, although the spec // is not very clear on the point. static bool encode_mcu_DC_refine_phuff(jpeg_compress cinfo, short[][] MCU_data) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; phuff_entropy_encoder entropy = (phuff_entropy_encoder)lossyc.entropy_private; int Al = cinfo.Al; entropy.output_bytes = cinfo.dest.output_bytes; entropy.next_output_byte = cinfo.dest.next_output_byte; entropy.free_in_buffer = cinfo.dest.free_in_buffer; // Emit restart marker if needed if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { emit_restart(entropy, entropy.next_restart_num); } } // Encode the MCU data blocks for (int blkn = 0; blkn < cinfo.block_in_MCU; blkn++) { short[] block = MCU_data[blkn]; // We simply emit the Al'th bit of the DC coefficient value. int temp = block[0]; emit_bits(entropy, (uint)(temp >> Al), 1); } cinfo.dest.output_bytes = entropy.output_bytes; cinfo.dest.next_output_byte = entropy.next_output_byte; cinfo.dest.free_in_buffer = entropy.free_in_buffer; // Update restart-interval state too if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { entropy.restarts_to_go = cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num &= 7; } entropy.restarts_to_go--; } return(true); }
// Finish up at the end of a Huffman-compressed progressive scan. static void finish_pass_phuff(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; phuff_entropy_encoder entropy = (phuff_entropy_encoder)lossyc.entropy_private; entropy.output_bytes = cinfo.dest.output_bytes; entropy.next_output_byte = cinfo.dest.next_output_byte; entropy.free_in_buffer = cinfo.dest.free_in_buffer; // Flush out any buffered data emit_eobrun(entropy); flush_bits(entropy); cinfo.dest.output_bytes = entropy.output_bytes; cinfo.dest.next_output_byte = entropy.next_output_byte; cinfo.dest.free_in_buffer = entropy.free_in_buffer; }
// Initialize for a processing pass. static void start_pass_coef(jpeg_compress cinfo, J_BUF_MODE pass_mode) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = (c_coef_controller)lossyc.coef_private; coef.iMCU_row_num = 0; start_iMCU_row_c_coef(cinfo); switch (pass_mode) { case J_BUF_MODE.JBUF_PASS_THRU: if (coef.whole_image[0] != null) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); } lossyc.compress_data = compress_data_coef; break; #if FULL_COEF_BUFFER_SUPPORTED case J_BUF_MODE.JBUF_SAVE_AND_PASS: if (coef.whole_image[0] == null) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); } lossyc.compress_data = compress_first_pass_coef; break; case J_BUF_MODE.JBUF_CRANK_DEST: if (coef.whole_image[0] == null) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); } lossyc.compress_data = compress_output_coef; break; #endif default: ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); break; } }
// Finish up a statistics-gathering pass and create the new Huffman tables. static void finish_pass_gather_sq(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; shuff_entropy_encoder entropy = (shuff_entropy_encoder)lossyc.entropy_private; // It's important not to apply jpeg_gen_optimal_table more than once // per table, because it clobbers the input frequency counts! bool[] did_dc = new bool[NUM_HUFF_TBLS]; bool[] did_ac = new bool[NUM_HUFF_TBLS]; for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; int dctbl = compptr.dc_tbl_no; int actbl = compptr.ac_tbl_no; if (!did_dc[dctbl]) { if (cinfo.dc_huff_tbl_ptrs[dctbl] == null) { cinfo.dc_huff_tbl_ptrs[dctbl] = jpeg_alloc_huff_table(cinfo); } jpeg_gen_optimal_table(cinfo, cinfo.dc_huff_tbl_ptrs[dctbl], entropy.dc_count_ptrs[dctbl]); did_dc[dctbl] = true; } if (!did_ac[actbl]) { if (cinfo.ac_huff_tbl_ptrs[actbl] == null) { cinfo.ac_huff_tbl_ptrs[actbl] = jpeg_alloc_huff_table(cinfo); } jpeg_gen_optimal_table(cinfo, cinfo.ac_huff_tbl_ptrs[actbl], entropy.ac_count_ptrs[actbl]); did_ac[actbl] = true; } } }
public static uint jpeg_write_image(jpeg_compress cinfo, byte[] image, bool swapChannels, bool alpha) { if (cinfo.input_components != 3 || cinfo.lossless || cinfo.in_color_space != J_COLOR_SPACE.JCS_RGB || cinfo.num_components != 3 || cinfo.jpeg_color_space != J_COLOR_SPACE.JCS_YCbCr || cinfo.data_precision != 8 || cinfo.DCT_size != 8 || cinfo.block_in_MCU != 3 || cinfo.arith_code || cinfo.max_h_samp_factor != 1 || cinfo.max_v_samp_factor != 1 || cinfo.next_scanline != 0 || cinfo.num_scans != 1) { throw new Exception(); } if (cinfo.global_state != STATE.CSCANNING) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } // Give master control module another chance if this is first call to // jpeg_write_scanlines. This lets output of the frame/scan headers be // delayed so that application can write COM, etc, markers between // jpeg_start_compress and jpeg_write_scanlines. if (cinfo.master.call_pass_startup) { cinfo.master.pass_startup(cinfo); } jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = (c_coef_controller)lossyc.coef_private; fdct_controller fdct = (fdct_controller)lossyc.fdct_private; double[] workspaceY = new double[DCTSIZE2]; double[] workspaceCr = new double[DCTSIZE2]; double[] workspaceCb = new double[DCTSIZE2]; short[][] coefs = new short[][] { new short[DCTSIZE2], new short[DCTSIZE2], new short[DCTSIZE2] }; short[] coefY = coefs[0]; short[] coefCb = coefs[1]; short[] coefCr = coefs[2]; double[] divisorY = fdct.float_divisors[0]; double[] divisorC = fdct.float_divisors[1]; int bpp = alpha?4:3; for (int y = 0; y < (cinfo.image_height + DCTSIZE - 1) / DCTSIZE; y++) { int yFormImage = Math.Min((int)cinfo.image_height - y * DCTSIZE, DCTSIZE); for (int x = 0; x < (cinfo.image_width + DCTSIZE - 1) / DCTSIZE; x++) { int xFormImage = Math.Min((int)cinfo.image_width - x * DCTSIZE, DCTSIZE); int workspacepos = 0; for (int j = 0; j < yFormImage; j++) { int imagepos = ((y * DCTSIZE + j) * (int)cinfo.image_width + x * DCTSIZE) * bpp; for (int i = 0; i < xFormImage; i++, workspacepos++) { byte r = image[imagepos++]; byte g = image[imagepos++]; byte b = image[imagepos++]; if (alpha) { imagepos++; } if (!swapChannels) { workspaceY[workspacepos] = 0.299 * r + 0.587 * g + 0.114 * b - CENTERJSAMPLE; workspaceCb[workspacepos] = -0.168736 * r - 0.331264 * g + 0.5 * b; workspaceCr[workspacepos] = 0.5 * r - 0.418688 * g - 0.081312 * b; } else { workspaceY[workspacepos] = 0.299 * b + 0.587 * g + 0.114 * r - CENTERJSAMPLE; workspaceCb[workspacepos] = -0.168736 * b - 0.331264 * g + 0.5 * r; workspaceCr[workspacepos] = 0.5 * b - 0.418688 * g - 0.081312 * r; } } int lastworkspacepos = workspacepos - 1; for (int i = xFormImage; i < DCTSIZE; i++, workspacepos++) { workspaceY[workspacepos] = workspaceY[lastworkspacepos]; workspaceCb[workspacepos] = workspaceCb[lastworkspacepos]; workspaceCr[workspacepos] = workspaceCr[lastworkspacepos]; } } int lastworkspacelinepos = (yFormImage - 1) * DCTSIZE; for (int j = yFormImage; j < DCTSIZE; j++) { int lastworkspacepos = lastworkspacelinepos; for (int i = 0; i < DCTSIZE; i++, workspacepos++, lastworkspacepos++) { workspaceY[workspacepos] = workspaceY[lastworkspacepos]; workspaceCb[workspacepos] = workspaceCb[lastworkspacepos]; workspaceCr[workspacepos] = workspaceCr[lastworkspacepos]; } } // ein block (3 componenten) jpeg_fdct_float(workspaceY); jpeg_fdct_float(workspaceCb); jpeg_fdct_float(workspaceCr); for (int i = 0; i < DCTSIZE2; i++) { // Apply the quantization and scaling factor double tempY = workspaceY[i] * divisorY[i]; double tempCb = workspaceCb[i] * divisorC[i]; double tempCr = workspaceCr[i] * divisorC[i]; // Round to nearest integer. // Since C does not specify the direction of rounding for negative // quotients, we have to force the dividend positive for portability. // The maximum coefficient size is +-16K (for 12-bit data), so this // code should work for either 16-bit or 32-bit ints. coefY[i] = (short)((int)(tempY + 16384.5) - 16384); coefCb[i] = (short)((int)(tempCb + 16384.5) - 16384); coefCr[i] = (short)((int)(tempCr + 16384.5) - 16384); } lossyc.entropy_encode_mcu(cinfo, coefs); } } cinfo.next_scanline = cinfo.image_height; return(cinfo.image_height); }
// MCU encoding for AC successive approximation refinement scan. static bool encode_mcu_AC_refine_phuff(jpeg_compress cinfo, short[][] MCU_data) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; phuff_entropy_encoder entropy = (phuff_entropy_encoder)lossyc.entropy_private; int r; byte[] BR_buffer; uint BR; int Se = cinfo.Se; int Al = cinfo.Al; int[] absvalues = new int[DCTSIZE2]; entropy.output_bytes = cinfo.dest.output_bytes; entropy.next_output_byte = cinfo.dest.next_output_byte; entropy.free_in_buffer = cinfo.dest.free_in_buffer; // Emit restart marker if needed if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { emit_restart(entropy, entropy.next_restart_num); } } // Encode the MCU data block short[] block = MCU_data[0]; // It is convenient to make a pre-pass to determine the transformed // coefficients' absolute values and the EOB position. int EOB = 0; int k; for (k = cinfo.Ss; k <= Se; k++) { int temp = block[jpeg_natural_order[k]]; // We must apply the point transform by Al. For AC coefficients this // is an integer division with rounding towards 0. To do this portably // in C, we shift after obtaining the absolute value. if (temp < 0) { temp = -temp; // temp is abs value of input } temp >>= Al; // apply the point transform absvalues[k] = temp; // save abs value for main pass if (temp == 1) { EOB = k; // EOB = index of last newly-nonzero coef } } // Encode the AC coefficients per section G.1.2.3, fig. G.7 r = 0; // r = run length of zeros BR = 0; // BR = count of buffered bits added now BR_buffer = entropy.bit_buffer; // Append bits to buffer uint BR_buffer_ind = entropy.BE; for (k = cinfo.Ss; k <= Se; k++) { int temp = absvalues[k]; if (temp == 0) { r++; continue; } // Emit any required ZRLs, but not if they can be folded into EOB while (r > 15 && k <= EOB) { // emit any pending EOBRUN and the BE correction bits emit_eobrun(entropy); // Emit ZRL emit_symbol(entropy, entropy.ac_tbl_no, 0xF0); r -= 16; // Emit buffered correction bits that must be associated with ZRL emit_buffered_bits(entropy, BR_buffer, BR_buffer_ind, BR); BR_buffer = entropy.bit_buffer; // BE bits are gone now BR_buffer_ind = 0; BR = 0; } // If the coef was previously nonzero, it only needs a correction bit. // NOTE: a straight translation of the spec's figure G.7 would suggest // that we also need to test r > 15. But if r > 15, we can only get here // if k > EOB, which implies that this coefficient is not 1. if (temp > 1) { // The correction bit is the next bit of the absolute value. BR_buffer[BR_buffer_ind + BR++] = (byte)(temp & 1); continue; } // Emit any pending EOBRUN and the BE correction bits emit_eobrun(entropy); // Count/emit Huffman symbol for run length / number of bits emit_symbol(entropy, entropy.ac_tbl_no, (r << 4) + 1); // Emit output bit for newly-nonzero coef temp = (block[jpeg_natural_order[k]] < 0)?0:1; emit_bits(entropy, (uint)temp, 1); // Emit buffered correction bits that must be associated with this code emit_buffered_bits(entropy, BR_buffer, BR_buffer_ind, BR); BR_buffer = entropy.bit_buffer; // BE bits are gone now BR_buffer_ind = 0; BR = 0; r = 0; // reset zero run length } if (r > 0 || BR > 0) // If there are trailing zeroes, { entropy.EOBRUN++; // count an EOB entropy.BE += BR; // concat my correction bits to older ones // We force out the EOB if we risk either: // 1. overflow of the EOB counter; // 2. overflow of the correction bit buffer during the next MCU. if (entropy.EOBRUN == 0x7FFF || entropy.BE > (MAX_CORR_BITS - DCTSIZE2 + 1)) { emit_eobrun(entropy); } } cinfo.dest.output_bytes = entropy.output_bytes; cinfo.dest.next_output_byte = entropy.next_output_byte; cinfo.dest.free_in_buffer = entropy.free_in_buffer; // Update restart-interval state too if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { entropy.restarts_to_go = cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num &= 7; } entropy.restarts_to_go--; } return(true); }
const int MAX_CORR_BITS = 1000; // Max # of correction bits I can buffer // Initialize for a Huffman-compressed scan using progressive JPEG. static void start_pass_phuff(jpeg_compress cinfo, bool gather_statistics) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; phuff_entropy_encoder entropy = (phuff_entropy_encoder)lossyc.entropy_private; entropy.cinfo = cinfo; entropy.gather_statistics = gather_statistics; bool is_DC_band = (cinfo.Ss == 0); // We assume jcmaster.cs already validated the scan parameters. // Select execution routines if (cinfo.Ah == 0) { if (is_DC_band) { lossyc.entropy_encode_mcu = encode_mcu_DC_first_phuff; } else { lossyc.entropy_encode_mcu = encode_mcu_AC_first_phuff; } } else { if (is_DC_band) { lossyc.entropy_encode_mcu = encode_mcu_DC_refine_phuff; } else { lossyc.entropy_encode_mcu = encode_mcu_AC_refine_phuff; // AC refinement needs a correction bit buffer if (entropy.bit_buffer == null) { try { entropy.bit_buffer = new byte[MAX_CORR_BITS]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } } } if (gather_statistics) { lossyc.entropy_finish_pass = finish_pass_gather_phuff; } else { lossyc.entropy_finish_pass = finish_pass_phuff; } // Only DC coefficients may be interleaved, so cinfo.comps_in_scan = 1 // for AC coefficients. for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; // Initialize DC predictions to 0 entropy.last_dc_val[ci] = 0; // Get table index int tbl; if (is_DC_band) { if (cinfo.Ah != 0) { continue; // DC refinement needs no table } tbl = compptr.dc_tbl_no; } else { entropy.ac_tbl_no = tbl = compptr.ac_tbl_no; } if (gather_statistics) { // Check for invalid table index // (make_c_derived_tbl does this in the other path) if (tbl < 0 || tbl >= NUM_HUFF_TBLS) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tbl); } // Allocate and zero the statistics tables // Note that jpeg_gen_optimal_table expects 257 entries in each table! if (entropy.count_ptrs[tbl] == null) { try { entropy.count_ptrs[tbl] = new int[257]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } else { for (int i = 0; i < 257; i++) { entropy.count_ptrs[tbl][i] = 0; } } } else { // Compute derived values for Huffman table // We may do this more than once for a table, but it's not expensive jpeg_make_c_derived_tbl(cinfo, is_DC_band, tbl, ref entropy.derived_tbls[tbl]); } } // Initialize AC stuff entropy.EOBRUN = 0; entropy.BE = 0; // Initialize bit buffer to empty entropy.put_buffer = 0; entropy.put_bits = 0; // Initialize restart stuff entropy.restarts_to_go = cinfo.restart_interval; entropy.next_restart_num = 0; }
// Initialize for a Huffman-compressed scan. // If gather_statistics is true, we do not output anything during the scan, // just count the Huffman symbols used and generate Huffman code tables. static void start_pass_huff(jpeg_compress cinfo, bool gather_statistics) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; shuff_entropy_encoder entropy = (shuff_entropy_encoder)lossyc.entropy_private; if (gather_statistics) { #if ENTROPY_OPT_SUPPORTED lossyc.entropy_encode_mcu = encode_mcu_gather_sq; lossyc.entropy_finish_pass = finish_pass_gather_sq; #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif } else { lossyc.entropy_encode_mcu = encode_mcu_huff_sq; lossyc.entropy_finish_pass = finish_pass_huff_sq; } for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; int dctbl = compptr.dc_tbl_no; int actbl = compptr.ac_tbl_no; if (gather_statistics) { #if ENTROPY_OPT_SUPPORTED // Check for invalid table indexes // (make_c_derived_tbl does this in the other path) if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, dctbl); } if (actbl < 0 || actbl >= NUM_HUFF_TBLS) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, actbl); } // Allocate and zero the statistics tables // Note that jpeg_gen_optimal_table expects 257 entries in each table! if (entropy.dc_count_ptrs[dctbl] == null) { try { entropy.dc_count_ptrs[dctbl] = new int[257]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } else { for (int i = 0; i < 257; i++) { entropy.dc_count_ptrs[dctbl][i] = 0; } } if (entropy.ac_count_ptrs[actbl] == null) { try { entropy.ac_count_ptrs[actbl] = new int[257]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } else { for (int i = 0; i < 257; i++) { entropy.ac_count_ptrs[actbl][i] = 0; } } #endif } else { // Compute derived values for Huffman tables // We may do this more than once for a table, but it's not expensive jpeg_make_c_derived_tbl(cinfo, true, dctbl, ref entropy.dc_derived_tbls[dctbl]); jpeg_make_c_derived_tbl(cinfo, false, actbl, ref entropy.ac_derived_tbls[actbl]); } // Initialize DC predictions to 0 entropy.saved.last_dc_val[ci] = 0; } // Initialize bit buffer to empty entropy.saved.put_buffer = 0; entropy.saved.put_bits = 0; // Initialize restart stuff entropy.restarts_to_go = cinfo.restart_interval; entropy.next_restart_num = 0; }
// MCU encoding for AC initial scan (either spectral selection, // or first pass of successive approximation). static bool encode_mcu_AC_first_phuff(jpeg_compress cinfo, short[][] MCU_data) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; phuff_entropy_encoder entropy = (phuff_entropy_encoder)lossyc.entropy_private; int Se = cinfo.Se; int Al = cinfo.Al; entropy.output_bytes = cinfo.dest.output_bytes; entropy.next_output_byte = cinfo.dest.next_output_byte; entropy.free_in_buffer = cinfo.dest.free_in_buffer; // Emit restart marker if needed if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { emit_restart(entropy, entropy.next_restart_num); } } // Encode the MCU data block short[] block = MCU_data[0]; // Encode the AC coefficients per section G.1.2.2, fig. G.3 int r = 0; // r = run length of zeros for (int k = cinfo.Ss; k <= Se; k++) { int temp = block[jpeg_natural_order[k]]; if (temp == 0) { r++; continue; } // We must apply the point transform by Al. For AC coefficients this // is an integer division with rounding towards 0. To do this portably // in C, we shift after obtaining the absolute value; so the code is // interwoven with finding the abs value (temp) and output bits (temp2). int temp2; if (temp < 0) { temp = -temp; // temp is abs value of input temp >>= Al; // apply the point transform // For a negative coef, want temp2 = bitwise complement of abs(coef) temp2 = ~temp; } else { temp >>= Al; // apply the point transform temp2 = temp; } // Watch out for case that nonzero coef is zero after point transform if (temp == 0) { r++; continue; } // Emit any pending EOBRUN if (entropy.EOBRUN > 0) { emit_eobrun(entropy); } // if run length > 15, must emit special run-length-16 codes (0xF0) while (r > 15) { emit_symbol(entropy, entropy.ac_tbl_no, 0xF0); r -= 16; } // Find the number of bits needed for the magnitude of the coefficient int nbits = 1; // there must be at least one 1 bit while ((temp >>= 1) != 0) { nbits++; } // Check for out-of-range coefficient values if (nbits > MAX_COEF_BITS) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_DCT_COEF); } // Count/emit Huffman symbol for run length / number of bits emit_symbol(entropy, entropy.ac_tbl_no, (r << 4) + nbits); // Emit that number of bits of the value, if positive, // or the complement of its magnitude, if negative. emit_bits(entropy, (uint)temp2, nbits); r = 0; // reset zero run length } if (r > 0) // If there are trailing zeroes, { entropy.EOBRUN++; // count an EOB if (entropy.EOBRUN == 0x7FFF) { emit_eobrun(entropy); // force it out to avoid overflow } } cinfo.dest.output_bytes = entropy.output_bytes; cinfo.dest.next_output_byte = entropy.next_output_byte; cinfo.dest.free_in_buffer = entropy.free_in_buffer; // Update restart-interval state too if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { entropy.restarts_to_go = cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num &= 7; } entropy.restarts_to_go--; } return(true); }
// MCU encoding for DC initial scan (either spectral selection, // or first pass of successive approximation). static bool encode_mcu_DC_first_phuff(jpeg_compress cinfo, short[][] MCU_data) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; phuff_entropy_encoder entropy = (phuff_entropy_encoder)lossyc.entropy_private; int Al = cinfo.Al; entropy.output_bytes = cinfo.dest.output_bytes; entropy.next_output_byte = cinfo.dest.next_output_byte; entropy.free_in_buffer = cinfo.dest.free_in_buffer; // Emit restart marker if needed if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { emit_restart(entropy, entropy.next_restart_num); } } // Encode the MCU data blocks for (int blkn = 0; blkn < cinfo.block_in_MCU; blkn++) { short[] block = MCU_data[blkn]; int ci = cinfo.MCU_membership[blkn]; jpeg_component_info compptr = cinfo.cur_comp_info[ci]; // Compute the DC value after the required point transform by Al. // This is simply an arithmetic right shift. int temp2 = (int)block[0] >> Al; // DC differences are figured on the point-transformed values. int temp = temp2 - entropy.last_dc_val[ci]; entropy.last_dc_val[ci] = temp2; // Encode the DC coefficient difference per section G.1.2.1 temp2 = temp; if (temp < 0) { temp = -temp; // temp is abs value of input // For a negative input, want temp2 = bitwise complement of abs(input) // This code assumes we are on a two's complement machine temp2--; } // Find the number of bits needed for the magnitude of the coefficient int nbits = 0; while (temp != 0) { nbits++; temp >>= 1; } // Check for out-of-range coefficient values. // Since we're encoding a difference, the range limit is twice as much. if (nbits > MAX_COEF_BITS + 1) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_DCT_COEF); } // Count/emit the Huffman-coded symbol for the number of bits emit_symbol(entropy, compptr.dc_tbl_no, nbits); // Emit that number of bits of the value, if positive, // or the complement of its magnitude, if negative. if (nbits != 0) { emit_bits(entropy, (uint)temp2, nbits); // emit_bits rejects calls with size 0 } } cinfo.dest.output_bytes = entropy.output_bytes; cinfo.dest.next_output_byte = entropy.next_output_byte; cinfo.dest.free_in_buffer = entropy.free_in_buffer; // Update restart-interval state too if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { entropy.restarts_to_go = cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num &= 7; } entropy.restarts_to_go--; } return(true); }
// Perform forward DCT on one or more blocks of a component. // // The input samples are taken from the sample_data[] array starting at // position start_row/start_col, and moving to the right for any additional // blocks. The quantized coefficients are returned in coef_blocks[]. // This version is used for integer DCT implementations. static void forward_DCT(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] sample_data, short[][] coef_blocks, int coef_offset, uint start_row, uint start_col, uint num_blocks) { // This routine is heavily used, so it's worth coding it tightly jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; fdct_controller fdct = (fdct_controller)lossyc.fdct_private; forward_DCT_method_ptr do_dct = fdct.do_dct; int[] divisors = fdct.divisors[compptr.quant_tbl_no]; int[] workspace = new int[DCTSIZE2]; // work area for FDCT subroutine for (int bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { // Load data into workspace, applying unsigned->signed conversion int wsptr = 0; for (int elemr = 0; elemr < DCTSIZE; elemr++) { byte[] elem = sample_data[start_row + elemr]; uint eptr = start_col; // unroll the inner loop workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; workspace[wsptr++] = elem[eptr++] - CENTERJSAMPLE; } // Perform the DCT do_dct(workspace); // Quantize/descale the coefficients, and store into coef_blocks[] //old short[] output_ptr=coef_blocks[coef_offset][bi]; short[] output_ptr = coef_blocks[coef_offset + bi]; for (int i = 0; i < DCTSIZE2; i++) { int qval = divisors[i]; int temp = workspace[i]; // Divide the coefficient value by qval, ensuring proper rounding. // Since C does not specify the direction of rounding for negative // quotients, we have to force the dividend positive for portability. if (temp < 0) { temp = -temp; temp += qval >> 1; // for rounding temp /= qval; temp = -temp; } else { temp += qval >> 1; // for rounding temp /= qval; } output_ptr[i] = (short)temp; } } }
// Initialize for a processing pass. // Verify that all referenced Q-tables are present, and set up // the divisor table for each one. // In the current implementation, DCT of all components is done during // the first pass, even if only some components will be output in the // first scan. Hence all components should be examined here. static void start_pass_fdctmgr(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; fdct_controller fdct = (fdct_controller)lossyc.fdct_private; for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; int qtblno = compptr.quant_tbl_no; // Make sure specified quantization table is present if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || cinfo.quant_tbl_ptrs[qtblno] == null) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); } JQUANT_TBL qtbl = cinfo.quant_tbl_ptrs[qtblno]; // Compute divisors for this quant table // We may do this more than once for same table, but it's not a big deal if (fdct.divisors[qtblno] == null) { try { fdct.divisors[qtblno] = new int[DCTSIZE2]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } int[] dtbl = fdct.divisors[qtblno]; for (int i = 0; i < DCTSIZE2; i++) { dtbl[i] = ((int)qtbl.quantval[i] * aanscales[i] + 1024) >> 11; } #if DCT_FLOAT_SUPPORTED if (fdct.float_divisors[qtblno] == null) { try { fdct.float_divisors[qtblno] = new double[DCTSIZE2]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } double[] fdtbl = fdct.float_divisors[qtblno]; int di = 0; for (int row = 0; row < DCTSIZE; row++) { for (int col = 0; col < DCTSIZE; col++) { fdtbl[di] = 1.0 / (qtbl.quantval[di] * aanscalefactor[row] * aanscalefactor[col] * 8.0); di++; } } #endif } }
// Process some data in the first pass of a multi-pass case. // We process the equivalent of one fully interleaved MCU row ("iMCU" row) // per call, ie, v_samp_factor block rows for each component in the image. // This amount of data is read from the source buffer, DCT'd and quantized, // and saved into the arrays. We also generate suitable dummy blocks // as needed at the right and lower edges. (The dummy blocks are constructed // in the arrays, which have been padded appropriately.) This makes // it possible for subsequent passes not to worry about real vs. dummy blocks. // // We must also emit the data to the entropy encoder. This is conveniently // done by calling compress_output_coef() after we've loaded the current strip // of the arrays. // // NB: input_buf contains a plane for each component in image. All // components are DCT'd and loaded into the arrays in this pass. // However, it may be that only a subset of the components are emitted to // the entropy encoder during this first pass; be careful about looking // at the scan-dependent variables (MCU dimensions, etc). static bool compress_first_pass_coef(jpeg_compress cinfo, byte[][][] input_buf) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = (c_coef_controller)lossyc.coef_private; uint last_iMCU_row = cinfo.total_iMCU_rows - 1; for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; // Align the buffer for this component. short[][][] buffer = coef.whole_image[ci]; int buffer_ind = (int)coef.iMCU_row_num * compptr.v_samp_factor; // Count non-dummy DCT block rows in this iMCU row. int block_rows; if (coef.iMCU_row_num < last_iMCU_row) { block_rows = compptr.v_samp_factor; } else { // NB: can't use last_row_height here, since may not be set! block_rows = (int)(compptr.height_in_blocks % compptr.v_samp_factor); if (block_rows == 0) { block_rows = compptr.v_samp_factor; } } uint blocks_across = compptr.width_in_blocks; int h_samp_factor = compptr.h_samp_factor; // Count number of dummy blocks to be added at the right margin. int ndummy = (int)(blocks_across % h_samp_factor); if (ndummy > 0) { ndummy = h_samp_factor - ndummy; } // Perform DCT for all non-dummy blocks in this iMCU row. Each call // on forward_DCT processes a complete horizontal row of DCT blocks. for (int block_row = 0; block_row < block_rows; block_row++) { short[][] row = buffer[buffer_ind + block_row]; lossyc.fdct_forward_DCT(cinfo, compptr, input_buf[ci], row, 0, (uint)(block_row * DCTSIZE), 0, blocks_across); if (ndummy > 0) { // Create dummy blocks at the right edge of the image. short lastDC = row[blocks_across - 1][0]; for (int bi = 0; bi < ndummy; bi++) { short[] block = row[blocks_across + bi]; for (int i = 1; i < DCTSIZE2; i++) { block[i] = 0; } block[0] = lastDC; } } } // If at end of image, create dummy block rows as needed. // The tricky part here is that within each MCU, we want the DC values // of the dummy blocks to match the last real block's DC value. // This squeezes a few more bytes out of the resulting file... if (coef.iMCU_row_num == last_iMCU_row) { blocks_across += (uint)ndummy; // include lower right corner uint MCUs_across = blocks_across / (uint)h_samp_factor; for (int block_row = block_rows; block_row < compptr.v_samp_factor; block_row++) { short[][] thisblockrow = buffer[buffer_ind + block_row]; short[][] lastblockrow = buffer[buffer_ind + block_row - 1]; int thisblockrow_ind = 0; int lastblockrow_ind = h_samp_factor - 1; for (int j = 0; j < blocks_across; j++) { short[] block = thisblockrow[j]; for (int i = 0; i < DCTSIZE2; i++) { block[i] = 0; } } for (uint MCUindex = 0; MCUindex < MCUs_across; MCUindex++) { short lastDC = lastblockrow[lastblockrow_ind][0]; for (int bi = 0; bi < h_samp_factor; bi++) { thisblockrow[thisblockrow_ind + bi][0] = lastDC; } thisblockrow_ind += h_samp_factor; // advance to next MCU in row lastblockrow_ind += h_samp_factor; } } } } // NB: compress_output will increment iMCU_row_num if successful. // A suspension return will result in redoing all the work above next time. // Emit data to the entropy encoder, sharing code with subsequent passes return(compress_output_coef(cinfo, input_buf)); }
// Initialize the lossy compression codec. // This is called only once, during master selection. static void jinit_lossy_c_codec(jpeg_compress cinfo) { jpeg_lossy_c_codec lossyc = null; // Create subobject try { if (cinfo.arith_code) { #if C_ARITH_CODING_SUPPORTED lossyc = new arith_entropy_encoder(); #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_ARITH_NOTIMPL); #endif } else { lossyc = new jpeg_lossy_c_codec(); } } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.coef = lossyc; // Initialize sub-modules // Forward DCT jinit_forward_dct(cinfo); // Entropy encoding: either Huffman or arithmetic coding. if (cinfo.arith_code) { #if C_ARITH_CODING_SUPPORTED jinit_arith_encoder(cinfo); #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_ARITH_NOTIMPL); #endif } else { if (cinfo.process == J_CODEC_PROCESS.JPROC_PROGRESSIVE) { #if C_PROGRESSIVE_SUPPORTED jinit_phuff_encoder(cinfo); #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif } else { jinit_shuff_encoder(cinfo); } } // Need a full-image coefficient buffer in any multi-pass mode. jinit_c_coef_controller(cinfo, cinfo.num_scans > 1 || cinfo.optimize_coding); // Initialize method pointers. // // Note: entropy_start_pass and entropy_finish_pass are assigned in // jcshuff.cs, jcphuff.cs or jcarith.cs and compress_data is assigned in jccoefct.cs. lossyc.start_pass = start_pass_lossy; }
// Process some data in the single-pass case. // We process the equivalent of one fully interleaved MCU row ("iMCU" row) // per call, ie, v_samp_factor block rows for each component in the image. // Returns true if the iMCU row is completed, false if suspended. // // NB: input_buf contains a plane for each component in image, // which we index according to the component's SOF position. static bool compress_data_coef(jpeg_compress cinfo, byte[][][] input_buf) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = (c_coef_controller)lossyc.coef_private; uint last_MCU_col = cinfo.MCUs_per_row - 1; uint last_iMCU_row = cinfo.total_iMCU_rows - 1; // Loop to write as much as one whole iMCU row for (int yoffset = coef.MCU_vert_offset; yoffset < coef.MCU_rows_per_iMCU_row; yoffset++) { for (uint MCU_col_num = coef.mcu_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++) // index of current MCU within row { // Determine where data comes from in input_buf and do the DCT thing. // Each call on forward_DCT processes a horizontal row of DCT blocks // as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks // sequentially. Dummy blocks at the right or bottom edge are filled in // specially. The data in them does not matter for image reconstruction, // so we fill them with values that will encode to the smallest amount of // data, viz: all zeroes in the AC entries, DC entries equal to previous // block's DC value. (Thanks to Thomas Kinsman for this idea.) int blkn = 0; for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; int blockcnt = (MCU_col_num < last_MCU_col)?compptr.MCU_width:compptr.last_col_width; uint xpos = MCU_col_num * (uint)compptr.MCU_sample_width; uint ypos = (uint)yoffset * DCTSIZE; // ypos == (yoffset+yindex) * DCTSIZE for (int yindex = 0; yindex < compptr.MCU_height; yindex++) { if (coef.iMCU_row_num < last_iMCU_row || yoffset + yindex < compptr.last_row_height) { lossyc.fdct_forward_DCT(cinfo, compptr, input_buf[compptr.component_index], coef.MCU_buffer, blkn, ypos, xpos, (uint)blockcnt); if (blockcnt < compptr.MCU_width) { // Create some dummy blocks at the right edge of the image. for (int bi = blockcnt; bi < compptr.MCU_width; bi++) { short[] block = coef.MCU_buffer[blkn + bi]; for (int i = 1; i < DCTSIZE2; i++) { block[i] = 0; // ACs } block[0] = coef.MCU_buffer[blkn + bi - 1][0]; // DCs } } } else { // Create a row of dummy blocks at the bottom of the image. for (int bi = 0; bi < compptr.MCU_width; bi++) { short[] block = coef.MCU_buffer[blkn + bi]; for (int i = 1; i < DCTSIZE2; i++) { block[i] = 0; // ACs } block[0] = coef.MCU_buffer[blkn - 1][0]; // DCs } } blkn += compptr.MCU_width; ypos += DCTSIZE; } } // Try to write the MCU. In event of a suspension failure, we will // re-DCT the MCU on restart (a bit inefficient, could be fixed...) if (!lossyc.entropy_encode_mcu(cinfo, coef.MCU_buffer)) { // Suspension forced; update state counters and exit coef.MCU_vert_offset = yoffset; coef.mcu_ctr = MCU_col_num; return(false); } } // Completed an MCU row, but perhaps not an iMCU row coef.mcu_ctr = 0; } // Completed the iMCU row, advance counters for next one coef.iMCU_row_num++; start_iMCU_row_c_coef(cinfo); return(true); }
// Encode and output one MCU's worth of Huffman-compressed coefficients. static bool encode_mcu_huff_sq(jpeg_compress cinfo, short[][] MCU_data) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; shuff_entropy_encoder entropy = (shuff_entropy_encoder)lossyc.entropy_private; // Load up working state working_state_sq state; state.cur.last_dc_val = new int[MAX_COMPS_IN_SCAN]; state.output_bytes = cinfo.dest.output_bytes; state.next_output_byte = cinfo.dest.next_output_byte; state.free_in_buffer = cinfo.dest.free_in_buffer; //was state.cur=entropy.saved; state.cur.put_bits = entropy.saved.put_bits; state.cur.put_buffer = entropy.saved.put_buffer; entropy.saved.last_dc_val.CopyTo(state.cur.last_dc_val, 0); state.cinfo = cinfo; // Emit restart marker if needed if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { if (!emit_restart(ref state, entropy.next_restart_num)) { return(false); } } } // Encode the MCU data blocks for (int blkn = 0; blkn < cinfo.block_in_MCU; blkn++) { int ci = cinfo.MCU_membership[blkn]; jpeg_component_info compptr = cinfo.cur_comp_info[ci]; if (!encode_one_block(ref state, MCU_data[blkn], state.cur.last_dc_val[ci], entropy.dc_derived_tbls[compptr.dc_tbl_no], entropy.ac_derived_tbls[compptr.ac_tbl_no])) { return(false); } // Update last_dc_val state.cur.last_dc_val[ci] = MCU_data[blkn][0]; } // Completed MCU, so update state cinfo.dest.output_bytes = state.output_bytes; cinfo.dest.next_output_byte = state.next_output_byte; cinfo.dest.free_in_buffer = state.free_in_buffer; //was entropy.saved=state.cur; entropy.saved.put_bits = state.cur.put_bits; entropy.saved.put_buffer = state.cur.put_buffer; state.cur.last_dc_val.CopyTo(entropy.saved.last_dc_val, 0); // Update restart-interval state too if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { entropy.restarts_to_go = cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num &= 7; } entropy.restarts_to_go--; } return(true); }