// Finish up at the end of a Huffman-compressed scan. static void finish_pass_huff_ls(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; lhuff_entropy_encoder entropy = (lhuff_entropy_encoder)losslsc.entropy_private; // Load up working state ... flush_bits needs it working_state_ls state; 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; 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; }
// Reset within-iMCU-row counters for a new row static void start_iMCU_row_c_diff(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_diff_controller diff = (c_diff_controller)losslsc.diff_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) { diff.MCU_rows_per_iMCU_row = 1; } else { if (diff.iMCU_row_num < (cinfo.total_iMCU_rows - 1)) { diff.MCU_rows_per_iMCU_row = cinfo.cur_comp_info[0].v_samp_factor; } else { diff.MCU_rows_per_iMCU_row = cinfo.cur_comp_info[0].last_row_height; } } diff.mcu_ctr = 0; diff.MCU_vert_offset = 0; }
// Module initialization routine for Huffman entropy encoding. static void jinit_lhuff_encoder(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; lhuff_entropy_encoder entropy = null; try { entropy = new lhuff_entropy_encoder(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } losslsc.entropy_private = entropy; losslsc.entropy_start_pass = start_pass_huff_ls; losslsc.need_optimization_pass = need_optimization_pass_ls; // Mark tables unallocated for (int i = 0; i < NUM_HUFF_TBLS; i++) { entropy.derived_tbls[i] = null; #if ENTROPY_OPT_SUPPORTED entropy.count_ptrs[i] = null; #endif } }
// Initialize for a processing pass. static void start_pass_ls(jpeg_compress cinfo, J_BUF_MODE pass_mode) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; losslsc.scaler_start_pass(cinfo); losslsc.predict_start_pass(cinfo); losslsc.diff_start_pass(cinfo, pass_mode); }
static void simple_downscale(jpeg_compress cinfo, byte[] input_buf, byte[] output_buf, uint width) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; for (uint xindex = 0; xindex < width; xindex++) { output_buf[xindex] = (byte)(input_buf[xindex] >> cinfo.Al); } }
// Reset predictor at the start of a pass or restart interval. static void reset_predictor(jpeg_compress cinfo, int ci) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_predictor pred = (c_predictor)losslsc.pred_private; // Initialize restart counter pred.restart_rows_to_go[ci] = cinfo.restart_interval / cinfo.MCUs_per_row; // Set difference function to first row function losslsc.predict_difference[ci] = jpeg_difference_first_row; }
// Differencer for the first row in a scan or restart interval. The first // sample in the row is differenced using the special predictor constant // x=2^(P-Pt-1). The rest of the samples are differenced using the // 1-D horizontal predictor (1). static void jpeg_difference_first_row(jpeg_compress cinfo, int ci, byte[] input_buf, byte[] prev_row, int[] diff_buf, uint width) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_predictor pred = (c_predictor)losslsc.pred_private; bool restart = false; int samp = input_buf[0]; diff_buf[0] = samp - (1 << (cinfo.data_precision - cinfo.Al - 1)); for (uint xindex = 1; xindex < width; xindex++) { int Ra = samp; samp = input_buf[xindex]; diff_buf[xindex] = samp - Ra; } // Account for restart interval (no-op if not using restarts) if (cinfo.restart_interval != 0) { if (--(pred.restart_rows_to_go[ci]) == 0) { reset_predictor(cinfo, ci); restart = true; } } // Now that we have differenced the first row, we want to use the // differencer which corresponds to the predictor specified in the // scan header. // // Note that we don't to do this if we have just reset the predictor // for a new restart interval. if (!restart) { switch (cinfo.Ss) { case 1: losslsc.predict_difference[ci] = jpeg_difference1; break; case 2: losslsc.predict_difference[ci] = jpeg_difference2; break; case 3: losslsc.predict_difference[ci] = jpeg_difference3; break; case 4: losslsc.predict_difference[ci] = jpeg_difference4; break; case 5: losslsc.predict_difference[ci] = jpeg_difference5; break; case 6: losslsc.predict_difference[ci] = jpeg_difference6; break; case 7: losslsc.predict_difference[ci] = jpeg_difference7; break; } } }
static void scaler_start_pass(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; // Set scaler function based on Pt if (cinfo.Al != 0) { losslsc.scaler_scale = simple_downscale; } else { losslsc.scaler_scale = noscale; } }
// Module initialization routine for the differencer. static void jinit_differencer(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_predictor pred = null; try { pred = new c_predictor(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } losslsc.pred_private = pred; losslsc.predict_start_pass = start_pass_c_pred; }
// Initialize for an input processing pass. static void start_pass_c_pred(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_predictor pred = (c_predictor)losslsc.pred_private; // Check that the restart interval is an integer multiple of the number // of MCU in an MCU-row. if (cinfo.restart_interval % cinfo.MCUs_per_row != 0) { ERREXIT2(cinfo, J_MESSAGE_CODE.JERR_BAD_RESTART, (int)cinfo.restart_interval, (int)cinfo.MCUs_per_row); } // Set predictors for start of pass for (int ci = 0; ci < cinfo.num_components; ci++) { reset_predictor(cinfo, ci); } }
// 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 rows for each component in the image. // This amount of data is read from the source buffer and saved into the arrays. // // We must also emit the data to the compressor. This is conveniently // done by calling compress_output_diff() after we've loaded the current strip // of the arrays. // // NB: input_buf contains a plane for each component in image. All components // are loaded into the arrays in this pass. However, it may be that // only a subset of the components are emitted to the compressor during // this first pass; be careful about looking at the scan-dependent variables // (MCU dimensions, etc). static bool compress_first_pass_diff(jpeg_compress cinfo, byte[][][] input_buf) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_diff_controller diff = (c_diff_controller)losslsc.diff_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]; // Count non-dummy sample rows in this iMCU row. int samp_rows; if (diff.iMCU_row_num < last_iMCU_row) { samp_rows = compptr.v_samp_factor; } else { // NB: can't use last_row_height here, since may not be set! samp_rows = (int)(compptr.height_in_blocks % compptr.v_samp_factor); if (samp_rows == 0) { samp_rows = compptr.v_samp_factor; } } uint samps_across = compptr.width_in_blocks; // Perform point transform scaling and prediction/differencing for all // non-dummy rows in this iMCU row. Each call on these functions // process a complete row of samples. for (int samp_row = 0; samp_row < samp_rows; samp_row++) { Array.Copy(input_buf[ci][samp_row], diff.whole_image[ci][samp_row + diff.iMCU_row_num * compptr.v_samp_factor], samps_across); } } // 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 compressor, sharing code with subsequent passes return(compress_output_diff(cinfo, input_buf)); }
// Initialize the lossless compression codec. // This is called only once, during master selection. static void jinit_lossless_c_codec(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc=null; // Create subobject in permanent pool try { losslsc=new jpeg_lossless_c_codec(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.coef=losslsc; // Initialize sub-modules // Scaler jinit_c_scaler(cinfo); // Differencer jinit_differencer(cinfo); // Entropy encoding: either Huffman or arithmetic coding. if(cinfo.arith_code) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_ARITH_NOTIMPL); } else { jinit_lhuff_encoder(cinfo); } // Need a full-image difference buffer in any multi-pass mode. jinit_c_diff_controller(cinfo, (bool)(cinfo.num_scans>1|| cinfo.optimize_coding)); // Initialize method pointers. // // Note: entropy_start_pass and entropy_finish_pass are assigned in // jclhuff.cs and compress_data is assigned in jcdiffct.cs. losslsc.start_pass=start_pass_ls; }
// Initialize the lossless compression codec. // This is called only once, during master selection. static void jinit_lossless_c_codec(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc = null; // Create subobject in permanent pool try { losslsc = new jpeg_lossless_c_codec(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.coef = losslsc; // Initialize sub-modules // Scaler jinit_c_scaler(cinfo); // Differencer jinit_differencer(cinfo); // Entropy encoding: either Huffman or arithmetic coding. if (cinfo.arith_code) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_ARITH_NOTIMPL); } else { jinit_lhuff_encoder(cinfo); } // Need a full-image difference buffer in any multi-pass mode. jinit_c_diff_controller(cinfo, (bool)(cinfo.num_scans > 1 || cinfo.optimize_coding)); // Initialize method pointers. // // Note: entropy_start_pass and entropy_finish_pass are assigned in // jclhuff.cs and compress_data is assigned in jcdiffct.cs. losslsc.start_pass = start_pass_ls; }
// 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 rows for each component in the scan. // The data is obtained from the arrays and fed to the compressor. // 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_diff(jpeg_compress cinfo, byte[][][] input_buf) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_diff_controller diff = (c_diff_controller)losslsc.diff_private; byte[][][] buffer = new byte[MAX_COMPONENTS][][]; int[] buffer_ind = new int[MAX_COMPONENTS]; // 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 comp = 0; comp < cinfo.comps_in_scan; comp++) { jpeg_component_info compptr = cinfo.cur_comp_info[comp]; int ci = compptr.component_index; buffer[ci] = diff.whole_image[ci]; buffer_ind[ci] = (int)diff.iMCU_row_num * compptr.v_samp_factor; } return(compress_data_diff(cinfo, buffer, buffer_ind)); }
// Initialize for a processing pass. static void start_pass_diff(jpeg_compress cinfo, J_BUF_MODE pass_mode) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_diff_controller diff = (c_diff_controller)losslsc.diff_private; diff.iMCU_row_num = 0; start_iMCU_row_c_diff(cinfo); switch (pass_mode) { case J_BUF_MODE.JBUF_PASS_THRU: if (diff.whole_image[0] != null) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); } losslsc.compress_data = compress_data_diff; break; #if FULL_SAMP_BUFFER_SUPPORTED case J_BUF_MODE.JBUF_SAVE_AND_PASS: if (diff.whole_image[0] == null) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); } losslsc.compress_data = compress_first_pass_diff; break; case J_BUF_MODE.JBUF_CRANK_DEST: if (diff.whole_image[0] == null) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); } losslsc.compress_data = compress_output_diff; 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_ls(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; lhuff_entropy_encoder entropy = (lhuff_entropy_encoder)losslsc.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]; 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; 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.count_ptrs[dctbl]); did_dc[dctbl] = true; } } }
static void jinit_c_scaler(jpeg_compress cinfo) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; losslsc.scaler_start_pass = scaler_start_pass; }
// 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_ls(jpeg_compress cinfo, bool gather_statistics) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; lhuff_entropy_encoder entropy = (lhuff_entropy_encoder)losslsc.entropy_private; if (gather_statistics) { #if ENTROPY_OPT_SUPPORTED losslsc.entropy_encode_mcus = encode_mcus_gather_ls; losslsc.entropy_finish_pass = finish_pass_gather_ls; #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif } else { losslsc.entropy_encode_mcus = encode_mcus_huff_ls; losslsc.entropy_finish_pass = finish_pass_huff_ls; } 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; 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); } // Allocate and zero the statistics tables // Note that jpeg_gen_optimal_table expects 257 entries in each table! if (entropy.count_ptrs[dctbl] == null) { entropy.count_ptrs[dctbl] = new int[257]; } else { for (int i = 0; i < 257; i++) { entropy.count_ptrs[dctbl][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.derived_tbls[dctbl]); } } // Precalculate encoding info for each sample in an MCU of this scan int ptrn = 0; for (int sampn = 0; sampn < cinfo.block_in_MCU;) { jpeg_component_info compptr = cinfo.cur_comp_info[cinfo.MCU_membership[sampn]]; int ci = compptr.component_index; //ci=cinfo.MCU_membership[sampn]; //compptr=cinfo.cur_comp_info[ci]; for (int yoffset = 0; yoffset < compptr.MCU_height; yoffset++, ptrn++) { // Precalculate the setup info for each input pointer entropy.input_ptr_info[ptrn].ci = ci; entropy.input_ptr_info[ptrn].yoffset = yoffset; entropy.input_ptr_info[ptrn].MCU_width = compptr.MCU_width; for (int xoffset = 0; xoffset < compptr.MCU_width; xoffset++, sampn++) { // Precalculate the input pointer index for each sample entropy.input_ptr_index[sampn] = ptrn; // Precalculate which tables to use for each sample entropy.cur_tbls[sampn] = entropy.derived_tbls[compptr.dc_tbl_no]; entropy.cur_counts[sampn] = entropy.count_ptrs[compptr.dc_tbl_no]; } } } entropy.num_input_ptrs = ptrn; // 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; }
// Initialize difference buffer controller. static void jinit_c_diff_controller(jpeg_compress cinfo, bool need_full_buffer) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_diff_controller diff = null; try { diff = new c_diff_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } losslsc.diff_private = diff; losslsc.diff_start_pass = start_pass_diff; // Create the prediction row buffers. for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; try { diff.cur_row[ci] = new byte[jround_up(compptr.width_in_blocks, compptr.h_samp_factor)]; diff.prev_row[ci] = new byte[jround_up(compptr.width_in_blocks, compptr.h_samp_factor)]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } // Create the difference buffer. for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; diff.diff_buf[ci] = alloc_darray(cinfo, (uint)jround_up(compptr.width_in_blocks, compptr.h_samp_factor), (uint)compptr.v_samp_factor); // Prefill difference rows with zeros. We do this because only actual // data is placed in the buffers during prediction/differencing, leaving // any dummy differences at the right edge as zeros, which will encode // to the smallest amount of data. for (int row = 0; row < compptr.v_samp_factor; row++) { int c = (int)jround_up(compptr.width_in_blocks, compptr.h_samp_factor); for (int i = 0; i < c; i++) { diff.diff_buf[ci][row][i] = 0; } } } // Create the sample buffer. if (need_full_buffer) { #if FULL_SAMP_BUFFER_SUPPORTED // Allocate a full-image array for each component, // padded to a multiple of samp_factor differences in each direction. for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; diff.whole_image[ci] = alloc_sarray(cinfo, (uint)jround_up(compptr.width_in_blocks, compptr.h_samp_factor), (uint)jround_up(compptr.height_in_blocks, compptr.v_samp_factor)); } #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); #endif } else { diff.whole_image[0] = null; // flag for no arrays } }
// Huffman coding optimization. // // We first scan the supplied data and count the number of uses of each symbol // that is to be Huffman-coded. (This process MUST agree with the code above.) // Then we build a Huffman coding tree for the observed counts. // Symbols which are not needed at all for the particular image are not // assigned any code, which saves space in the DHT marker as well as in // the compressed data. #if ENTROPY_OPT_SUPPORTED // Trial-encode one nMCU's worth of Huffman-compressed differences. // No data is actually output, so no suspension return is possible. static uint encode_mcus_gather_ls(jpeg_compress cinfo, int[][][] diff_buf, uint MCU_row_num, uint MCU_col_num, uint nMCU) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; lhuff_entropy_encoder entropy = (lhuff_entropy_encoder)losslsc.entropy_private; // Take care of restart intervals if needed if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { entropy.restarts_to_go = cinfo.restart_interval; // Update restart state } entropy.restarts_to_go--; } // Set input pointer locations based on MCU_col_num for (int ptrn = 0; ptrn < entropy.num_input_ptrs; ptrn++) { int ci = entropy.input_ptr_info[ptrn].ci; int yoffset = entropy.input_ptr_info[ptrn].yoffset; int MCU_width = entropy.input_ptr_info[ptrn].MCU_width; entropy.input_ptr[ptrn] = diff_buf[ci][MCU_row_num + yoffset]; entropy.input_ptr_ind[ptrn] = (int)MCU_col_num * MCU_width; } for (uint mcu_num = 0; mcu_num < nMCU; mcu_num++) { // Inner loop handles the samples in the MCU for (int sampn = 0; sampn < cinfo.block_in_MCU; sampn++) { c_derived_tbl dctbl = entropy.cur_tbls[sampn]; int[] counts = entropy.cur_counts[sampn]; // Encode the difference per section H.1.2.2 // Input the sample difference int temp3 = entropy.input_ptr_index[sampn]; int temp = entropy.input_ptr[temp3][entropy.input_ptr_ind[temp3]++]; if ((temp & 0x8000) != 0) { // instead of temp < 0 temp = (-temp) & 0x7FFF; // absolute value, mod 2^16 if (temp == 0) { temp = 0x8000; // special case: magnitude = 32768 } } else { temp &= 0x7FFF; // abs value mod 2^16 } // Find the number of bits needed for the magnitude of the difference int nbits = 0; while (temp != 0) { nbits++; temp >>= 1; } // Check for out-of-range difference values. if (nbits > MAX_DIFF_BITS) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_DIFF); } // Count the Huffman symbol for the number of bits counts[nbits]++; } } return(nMCU); }
// Encode and output one nMCU's worth of Huffman-compressed differences. static uint encode_mcus_huff_ls(jpeg_compress cinfo, int[][][] diff_buf, uint MCU_row_num, uint MCU_col_num, uint nMCU) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; lhuff_entropy_encoder entropy = (lhuff_entropy_encoder)losslsc.entropy_private; // Load up working state working_state_ls state; 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; 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(0); } } } // Set input pointer locations based on MCU_col_num for (int ptrn = 0; ptrn < entropy.num_input_ptrs; ptrn++) { int ci = entropy.input_ptr_info[ptrn].ci; int yoffset = entropy.input_ptr_info[ptrn].yoffset; int MCU_width = entropy.input_ptr_info[ptrn].MCU_width; entropy.input_ptr[ptrn] = diff_buf[ci][MCU_row_num + yoffset]; entropy.input_ptr_ind[ptrn] = (int)MCU_col_num * MCU_width; } for (uint mcu_num = 0; mcu_num < nMCU; mcu_num++) { // Inner loop handles the samples in the MCU for (int sampn = 0; sampn < cinfo.block_in_MCU; sampn++) { c_derived_tbl dctbl = entropy.cur_tbls[sampn]; // Encode the difference per section H.1.2.2 // Input the sample difference int temp3 = entropy.input_ptr_index[sampn]; int temp = entropy.input_ptr[temp3][entropy.input_ptr_ind[temp3]++]; int temp2; if ((temp & 0x8000) != 0) { // instead of temp < 0 temp = (-temp) & 0x7FFF; // absolute value, mod 2^16 if (temp == 0) { temp2 = temp = 0x8000; // special case: magnitude = 32768 } temp2 = ~temp; // one's complement of magnitude } else { temp &= 0x7FFF; // abs value mod 2^16 temp2 = temp; // magnitude } // Find the number of bits needed for the magnitude of the difference int nbits = 0; while (temp != 0) { nbits++; temp >>= 1; } // Check for out-of-range difference values. if (nbits > MAX_DIFF_BITS) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_DIFF); } // Emit the Huffman-coded symbol for the number of bits if (!emit_bits(ref state, dctbl.ehufco[nbits], dctbl.ehufsi[nbits])) { return(mcu_num); } // Emit that number of bits of the value, if positive, // or the complement of its magnitude, if negative. if (nbits != 0 && // emit_bits rejects calls with size 0 nbits != 16) // special case: no bits should be emitted { if (!emit_bits(ref state, (uint)temp2, nbits)) { return(mcu_num); } } } // 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; entropy.saved = state.cur; // 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(nMCU); }
// Special version of compress_data_diff with input_buf offsets. static bool compress_data_diff(jpeg_compress cinfo, byte[][][] input_buf, int[] input_buf_ind) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_diff_controller diff = (c_diff_controller)losslsc.diff_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 = diff.MCU_vert_offset; yoffset < diff.MCU_rows_per_iMCU_row; yoffset++) { uint MCU_col_num = diff.mcu_ctr; // index of current MCU within row // Scale and predict each scanline of the MCU-row separately. // // Note: We only do this if we are at the start of a MCU-row, ie, // we don't want to reprocess a row suspended by the output. if (MCU_col_num == 0) { for (int comp = 0; comp < cinfo.comps_in_scan; comp++) { jpeg_component_info compptr = cinfo.cur_comp_info[comp]; int ci = compptr.component_index; int samp_rows; if (diff.iMCU_row_num < last_iMCU_row) { samp_rows = compptr.v_samp_factor; } else { // NB: can't use last_row_height here, since may not be set! samp_rows = (int)(compptr.height_in_blocks % compptr.v_samp_factor); if (samp_rows == 0) { samp_rows = compptr.v_samp_factor; } else { // Fill dummy difference rows at the bottom edge with zeros, which // will encode to the smallest amount of data. for (int samp_row = samp_rows; samp_row < compptr.v_samp_factor; samp_row++) { int c = jround_up((int)compptr.width_in_blocks, (int)compptr.h_samp_factor); for (int i = 0; i < c; i++) { diff.diff_buf[ci][samp_row][i] = 0; } } } } uint samps_across = compptr.width_in_blocks; for (int samp_row = 0; samp_row < samp_rows; samp_row++) { losslsc.scaler_scale(cinfo, input_buf[ci][input_buf_ind[ci] + samp_row], diff.cur_row[ci], samps_across); losslsc.predict_difference[ci](cinfo, ci, diff.cur_row[ci], diff.prev_row[ci], diff.diff_buf[ci][samp_row], samps_across); byte[] temp = diff.cur_row[ci]; diff.cur_row[ci] = diff.prev_row[ci]; diff.prev_row[ci] = temp; } } } // Try to write the MCU-row (or remaining portion of suspended MCU-row). uint MCU_count = losslsc.entropy_encode_mcus(cinfo, diff.diff_buf, (uint)yoffset, MCU_col_num, cinfo.MCUs_per_row - MCU_col_num); if (MCU_count != cinfo.MCUs_per_row - MCU_col_num) { // Suspension forced; update state counters and exit diff.MCU_vert_offset = yoffset; diff.mcu_ctr += MCU_col_num; return(false); } // Completed an MCU row, but perhaps not an iMCU row diff.mcu_ctr = 0; } // Completed the iMCU row, advance counters for next one diff.iMCU_row_num++; start_iMCU_row_c_diff(cinfo); return(true); }