// Decompression startup: read start of JPEG datastream to see what's there. // Need only initialize JPEG object and supply a data source before calling. // // This routine will read as far as the first SOS marker (ie, actual start of // compressed data), and will save all tables and parameters in the JPEG // object. It will also initialize the decompression parameters to default // values, and finally return JPEG_HEADER_OK. On return, the application may // adjust the decompression parameters and then call jpeg_start_decompress. // (Or, if the application only wanted to determine the image parameters, // the data need not be decompressed. In that case, call jpeg_abort or // jpeg_destroy to release any temporary space.) // If an abbreviated (tables only) datastream is presented, the routine will // return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then // re-use the JPEG object to read the abbreviated image datastream(s). // It is unnecessary (but OK) to call jpeg_abort in this case. // The JPEG_SUSPENDED return code only occurs if the data source module // requests suspension of the decompressor. In this case the application // should load more source data and then re-call jpeg_read_header to resume // processing. // If a non-suspending data source is used and require_image is true, then the // return code need not be inspected since only JPEG_HEADER_OK is possible. // // This routine is now just a front end to jpeg_consume_input, with some // extra error checking. public static CONSUME_INPUT jpeg_read_header(jpeg_decompress cinfo, bool require_image) { if (cinfo.global_state != STATE.DSTART && cinfo.global_state != STATE.DINHEADER) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } CONSUME_INPUT retcode = jpeg_consume_input(cinfo); switch (retcode) { case CONSUME_INPUT.JPEG_REACHED_SOS: return(CONSUME_INPUT.JPEG_HEADER_OK); case CONSUME_INPUT.JPEG_REACHED_EOI: if (require_image) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NO_IMAGE); // Complain if application wanted an image } // Reset to start state; it would be safer to require the application to // call jpeg_abort, but we can't change it now for compatibility reasons. // A side effect is to free any temporary memory (there shouldn't be any). jpeg_abort(cinfo); // sets state=DSTART return(CONSUME_INPUT.JPEG_HEADER_TABLES_ONLY); } return(retcode); }
const int CTX_POSTPONED_ROW=2; // feeding postponed row group #endif // Initialize for a processing pass. static void start_pass_d_main(jpeg_decompress cinfo, J_BUF_MODE pass_mode) { my_d_main_controller main=(my_d_main_controller)cinfo.main; switch(pass_mode) { case J_BUF_MODE.JBUF_PASS_THRU: #if UPSCALING_CONTEXT if(cinfo.upsample.need_context_rows) { main.process_data=process_data_context_d_main; main.context_state=CTX_PREPARE_FOR_IMCU; main.iMCU_row_ctr=0; } else #endif main.process_data=process_data_simple_d_main; // Simple case with no context needed main.buffer_full=false; // Mark buffer empty main.rowgroup_ctr=0; break; #if QUANT_2PASS_SUPPORTED case J_BUF_MODE.JBUF_CRANK_DEST: main.process_data=process_data_crank_post_d_main; break; // For last pass of 2-pass quantization, just crank the postprocessor #endif default: ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); break; } }
// *************** YCbCr -> RGB conversion: most common case ************* // YCbCr is defined per CCIR 601-1, except that Cb and Cr are // normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. // The conversion equations to be implemented are therefore // R = Y + 1.40200 * Cr // G = Y - 0.34414 * Cb - 0.71414 * Cr // B = Y + 1.77200 * Cb // where Cb and Cr represent the incoming values less CENTERJSAMPLE. // (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) // // To avoid floating-point arithmetic, we represent the fractional constants // as integers scaled up by 2^16 (about 4 digits precision); we have to divide // the products by 2^16, with appropriate rounding, to get the correct answer. // Notice that Y, being an integral input, does not contribute any fraction // so it need not participate in the rounding. // // For even more speed, we avoid doing any multiplications in the inner loop // by precalculating the constants times Cb and Cr for all possible values. // For 8-bit samples this is very reasonable (only 256 entries per table); // for 12-bit samples it is still acceptable. It's not very reasonable for // 16-bit samples, but if you want lossless storage you shouldn't be changing // colorspace anyway. // The Cr=>R and Cb=>B values can be rounded to integers in advance; the // values for the G calculation are left scaled up, since we must add them // together before rounding. // Initialize tables for YCC->RGB colorspace conversion. static void build_ycc_rgb_table(jpeg_decompress cinfo) { my_color_deconverter cconvert = (my_color_deconverter)cinfo.cconvert; try { cconvert.Cr_r_tab = new int[MAXJSAMPLE + 1]; cconvert.Cb_b_tab = new int[MAXJSAMPLE + 1]; cconvert.Cr_g_tab = new int[MAXJSAMPLE + 1]; cconvert.Cb_g_tab = new int[MAXJSAMPLE + 1]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { // i is the actual input pixel value, in the range 0..MAXJSAMPLE // The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE // Cr=>R value is nearest int to 1.40200 * x cconvert.Cr_r_tab[i] = (int)((FIX_140200 * x + ONE_HALF) >> SCALEBITS); // Cb=>B value is nearest int to 1.77200 * x cconvert.Cb_b_tab[i] = (int)((FIX_177200 * x + ONE_HALF) >> SCALEBITS); // Cr=>G value is scaled-up -0.71414 * x cconvert.Cr_g_tab[i] = (-FIX_071414) * x; // Cb=>G value is scaled-up -0.34414 * x // We also add in ONE_HALF so that need not do it in inner loop cconvert.Cb_g_tab[i] = (-FIX_034414) * x + ONE_HALF; } }
// Convert some rows of samples to the output colorspace. // // Note that we change from noninterleaved, one-plane-per-component format // to interleaved-pixel format. The output buffer is therefore three times // as wide as the input buffer. // A starting row offset is provided only for the input buffer. The caller // can easily adjust the passed output_buf value to accommodate any row // offset required on that side. #if !USE_UNSAFE_STUFF static void ycc_rgb_convert(jpeg_decompress cinfo, byte[][][] input_buf, uint input_row, byte[][] output_buf, uint output_row, int num_rows) { my_color_deconverter cconvert = (my_color_deconverter)cinfo.cconvert; uint num_cols = cinfo.output_width; int[] Crrtab = cconvert.Cr_r_tab; int[] Cbbtab = cconvert.Cb_b_tab; int[] Crgtab = cconvert.Cr_g_tab; int[] Cbgtab = cconvert.Cb_g_tab; while (--num_rows >= 0) { byte[] inptr0 = input_buf[0][input_row]; byte[] inptr1 = input_buf[1][input_row]; byte[] inptr2 = input_buf[2][input_row]; input_row++; byte[] outptr = output_buf[output_row++]; for (uint col = 0, outptr_ind = 0; col < num_cols; col++, outptr_ind += RGB_PIXELSIZE) { int y = inptr0[col]; int cb = inptr1[col]; int cr = inptr2[col]; // Range-limiting is essential due to noise introduced by DCT losses. int tmp = y + Crrtab[cr]; outptr[outptr_ind + RGB_RED] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); tmp = y + ((int)((Cbgtab[cb] + Crgtab[cr]) >> SCALEBITS)); outptr[outptr_ind + RGB_GREEN] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); tmp = y + Cbbtab[cb]; outptr[outptr_ind + RGB_BLUE] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); } } }
// Determine whether merged upsample/color conversion should be used. // CRUCIAL: this must match the actual capabilities of jdmerge.cs! static bool use_merged_upsample(jpeg_decompress cinfo) { #if UPSAMPLE_MERGING_SUPPORTED // Merging is the equivalent of plain box-filter upsampling if(cinfo.do_fancy_upsampling||cinfo.CCIR601_sampling) return false; // jdmerge.cs only supports YCC=>RGB color conversion if(cinfo.jpeg_color_space!=J_COLOR_SPACE.JCS_YCbCr||cinfo.num_components!=3|| cinfo.out_color_space!=J_COLOR_SPACE.JCS_RGB||cinfo.out_color_components!=RGB_PIXELSIZE) return false; // and it only handles 2h1v or 2h2v sampling ratios if(cinfo.comp_info[0].h_samp_factor!=2||cinfo.comp_info[1].h_samp_factor!=1|| cinfo.comp_info[2].h_samp_factor!=1||cinfo.comp_info[0].v_samp_factor>2|| cinfo.comp_info[1].v_samp_factor!=1||cinfo.comp_info[2].v_samp_factor!=1) return false; // furthermore, it doesn't work if each component has been processed differently if(cinfo.comp_info[0].DCT_scaled_size!=cinfo.min_DCT_scaled_size|| cinfo.comp_info[1].DCT_scaled_size!=cinfo.min_DCT_scaled_size|| cinfo.comp_info[2].DCT_scaled_size!=cinfo.min_DCT_scaled_size) return false; // ??? also need to test for upsample-time rescaling, when & if supported return true; // by golly, it'll work... #else return false; #endif }
// Process some data. // This handles the simple case where no context is required. static void process_data_simple_d_main(jpeg_decompress cinfo, byte[][] output_buf, ref uint out_row_ctr, uint out_rows_avail) { my_d_main_controller main = (my_d_main_controller)cinfo.main; uint rowgroups_avail; // Read input data if we haven't filled the main buffer yet if (!main.buffer_full) { if (cinfo.coef.decompress_data(cinfo, main.buffer) == CONSUME_INPUT.JPEG_SUSPENDED) { return; // suspension forced, can do nothing more } main.buffer_full = true; // OK, we have an iMCU row to work with } // There are always min_codec_data_unit row groups in an iMCU row. rowgroups_avail = (uint)cinfo.min_DCT_scaled_size; // Note: at the bottom of the image, we may pass extra garbage row groups // to the postprocessor. The postprocessor has to check for bottom // of image anyway (at row resolution), so no point in us doing it too. // Feed the postprocessor cinfo.post.post_process_data(cinfo, main.buffer, ref main.rowgroup_ctr, rowgroups_avail, output_buf, 0, ref out_row_ctr, out_rows_avail); // Has postprocessor consumed all the data yet? If so, mark buffer empty if (main.rowgroup_ctr >= rowgroups_avail) { main.buffer_full = false; main.rowgroup_ctr = 0; } }
// MCU decoding for DC successive approximation refinement scan. static bool decode_mcu_DC_refine_arith(jpeg_decompress cinfo, short[][] MCU_data) { arith_entropy_decoder entropy = (arith_entropy_decoder)cinfo.coef; // Process restart marker if needed if (cinfo.restart_interval != 0) { if (entropy.restarts_to_go == 0) { process_restart_arith(cinfo); } entropy.restarts_to_go--; } short p1 = (short)(1 << cinfo.Al); // 1 in the bit position being coded // Outer loop handles each block in the MCU for (int blkn = 0; blkn < cinfo.blocks_in_MCU; blkn++) { byte st = 0; // use fixed probability estimation // Encoded data is simply the next bit of the two's-complement DC value if (arith_decode(cinfo, ref st) != 0) { MCU_data[blkn][0] |= p1; } } return(true); }
public const int SAVED_COEFS = 6; // we save coef_bits[0..5] // Reset within-iMCU-row counters for a new row (input side) static void start_iMCU_row_d_coef(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd = (jpeg_lossy_d_codec)cinfo.coef; d_coef_controller coef = (d_coef_controller)lossyd.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 (cinfo.input_iMCU_row < (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; }
// Process some data in the first pass of 2-pass quantization. static void post_process_prepass(jpeg_decompress cinfo, byte[][][] input_buf, ref uint in_row_group_ctr, uint in_row_groups_avail, byte[][] output_buf, uint ignore_me, ref uint out_row_ctr, uint out_rows_avail) { my_post_controller post = (my_post_controller)cinfo.post; // Reposition virtual buffer if at start of strip. if (post.next_row == 0) { post.buffer = post.whole_image; post.buffer_offset = post.starting_row; } // Upsample some data (up to a strip height's worth). uint old_next_row = post.next_row; cinfo.upsample.upsample(cinfo, input_buf, ref in_row_group_ctr, in_row_groups_avail, post.buffer, post.buffer_offset, ref post.next_row, post.strip_height); // Allow quantizer to scan new data. No data is emitted, // but we advance out_row_ctr so outer loop can tell when we're done. if (post.next_row > old_next_row) { uint num_rows = post.next_row - old_next_row; cinfo.cquantize.color_quantize(cinfo, post.buffer, post.buffer_offset + old_next_row, null, 0, (int)num_rows); out_row_ctr += num_rows; } // Advance if we filled the strip. if (post.next_row >= post.strip_height) { post.starting_row += post.strip_height; post.next_row = 0; } }
// *************** YCbCr -> RGB conversion: most common case ************* // YCbCr is defined per CCIR 601-1, except that Cb and Cr are // normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. // The conversion equations to be implemented are therefore // R = Y + 1.40200 * Cr // G = Y - 0.34414 * Cb - 0.71414 * Cr // B = Y + 1.77200 * Cb // where Cb and Cr represent the incoming values less CENTERJSAMPLE. // (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) // // To avoid floating-point arithmetic, we represent the fractional constants // as integers scaled up by 2^16 (about 4 digits precision); we have to divide // the products by 2^16, with appropriate rounding, to get the correct answer. // Notice that Y, being an integral input, does not contribute any fraction // so it need not participate in the rounding. // // For even more speed, we avoid doing any multiplications in the inner loop // by precalculating the constants times Cb and Cr for all possible values. // For 8-bit samples this is very reasonable (only 256 entries per table); // for 12-bit samples it is still acceptable. It's not very reasonable for // 16-bit samples, but if you want lossless storage you shouldn't be changing // colorspace anyway. // The Cr=>R and Cb=>B values can be rounded to integers in advance; the // values for the G calculation are left scaled up, since we must add them // together before rounding. // Initialize tables for YCC->RGB colorspace conversion. static void build_ycc_rgb_table(jpeg_decompress cinfo) { my_color_deconverter cconvert=(my_color_deconverter)cinfo.cconvert; try { cconvert.Cr_r_tab=new int[MAXJSAMPLE+1]; cconvert.Cb_b_tab=new int[MAXJSAMPLE+1]; cconvert.Cr_g_tab=new int[MAXJSAMPLE+1]; cconvert.Cb_g_tab=new int[MAXJSAMPLE+1]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } for(int i=0, x=-CENTERJSAMPLE; i<=MAXJSAMPLE; i++, x++) { // i is the actual input pixel value, in the range 0..MAXJSAMPLE // The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE // Cr=>R value is nearest int to 1.40200 * x cconvert.Cr_r_tab[i]=(int)((FIX_140200*x+ONE_HALF)>>SCALEBITS); // Cb=>B value is nearest int to 1.77200 * x cconvert.Cb_b_tab[i]=(int)((FIX_177200*x+ONE_HALF)>>SCALEBITS); // Cr=>G value is scaled-up -0.71414 * x cconvert.Cr_g_tab[i]=(-FIX_071414)*x; // Cb=>G value is scaled-up -0.34414 * x // We also add in ONE_HALF so that need not do it in inner loop cconvert.Cb_g_tab[i]=(-FIX_034414)*x+ONE_HALF; } }
// Read some scanlines of data from the JPEG decompressor. // // The return value will be the number of lines actually read. // This may be less than the number requested in several cases, // including bottom of image, data source suspension, and operating // modes that emit multiple scanlines at a time. // // Note: we warn about excess calls to jpeg_read_scanlines() since // this likely signals an application programmer error. However, // an oversize buffer (max_lines > scanlines remaining) is not an error. public static uint jpeg_read_scanlines(jpeg_decompress cinfo, byte[][] scanlines, uint max_lines) { if (cinfo.global_state != STATE.DSCANNING) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } if (cinfo.output_scanline >= cinfo.output_height) { WARNMS(cinfo, J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA); return(0); } // Call progress monitor hook if present if (cinfo.progress != null) { cinfo.progress.pass_counter = (int)cinfo.output_scanline; cinfo.progress.pass_limit = (int)cinfo.output_height; cinfo.progress.progress_monitor(cinfo); } // Process some data uint row_ctr = 0; cinfo.main.process_data(cinfo, scanlines, ref row_ctr, max_lines); cinfo.output_scanline += row_ctr; return(row_ctr); }
static void scaler_start_pass(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; scaler scaler = (scaler)losslsd.scaler_private; // Downscale by the difference in the input vs. output precision. If the // output precision >= input precision, then do not downscale. int downscale = BITS_IN_JSAMPLE < cinfo.data_precision?cinfo.data_precision - BITS_IN_JSAMPLE:0; scaler.scale_factor = cinfo.Al - downscale; // Set scaler functions based on scale_factor (positive = left shift) if (scaler.scale_factor > 0) { losslsd.scaler_scale = simple_upscale; } else if (scaler.scale_factor < 0) { scaler.scale_factor = -scaler.scale_factor; losslsd.scaler_scale = simple_downscale; } else { losslsd.scaler_scale = noscale; } }
// Module initialization routine for the undifferencer. static void jinit_undifferencer(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; losslsd.predict_start_pass = start_pass_d_pred; losslsd.predict_process_restart = start_pass_d_pred; }
// Initialize IDCT manager. static void jinit_inverse_dct(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd = (jpeg_lossy_d_codec)cinfo.coef; idct_controller idct = null; try { idct = new idct_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } lossyd.idct_private = idct; lossyd.idct_start_pass = start_pass_idctmgr; for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; // Allocate and pre-zero a multiplier table for each component try { compptr.dct_table = new int[DCTSIZE2]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } // Mark multiplier table not yet set up for any method idct.cur_method[ci] = -1; } }
// Initialize for an output processing pass. static void start_output_pass_lossy(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd = (jpeg_lossy_d_codec)cinfo.coef; lossyd.idct_start_pass(cinfo); lossyd.coef_start_output_pass(cinfo); }
static void noscale(jpeg_decompress cinfo, int[] diff_buf, byte[] output_buf, uint width) { for (uint xindex = 0; xindex < width; xindex++) { output_buf[xindex] = (byte)diff_buf[xindex]; } }
// Initialize for an output processing pass. static void start_output_pass_lossy(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; lossyd.idct_start_pass(cinfo); lossyd.coef_start_output_pass(cinfo); }
// Finish up after an output pass in buffered-image mode. // // Returns false if suspended. The return value need be inspected only if // a suspending data source is used. public static bool jpeg_finish_output(jpeg_decompress cinfo) { if ((cinfo.global_state == STATE.DSCANNING || cinfo.global_state == STATE.DRAW_OK) && cinfo.buffered_image) { // Terminate this pass. // We do not require the whole pass to have been completed. cinfo.master.finish_output_pass(cinfo); cinfo.global_state = STATE.DBUFPOST; } else if (cinfo.global_state != STATE.DBUFPOST) { // BUFPOST = repeat call after a suspension, anything else is error ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } // Read markers looking for SOS or EOI while (cinfo.input_scan_number <= cinfo.output_scan_number && !cinfo.inputctl.eoi_reached) { if (cinfo.inputctl.consume_input(cinfo) == CONSUME_INPUT.JPEG_SUSPENDED) { return(false); // Suspend, come back later } } cinfo.global_state = STATE.DBUFIMAGE; return(true); }
// Undifferencer for the first row in a scan or restart interval. The first // sample in the row is undifferenced using the special predictor constant // x=2^(P-Pt-1). The rest of the samples are undifferenced using the // 1-D horizontal predictor (1). static void jpeg_undifference_first_row(jpeg_decompress cinfo, int comp_index, int[] diff_buf, int[] prev_row, int[] undiff_buf, uint width) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; int Ra = (diff_buf[0] + (1 << (cinfo.data_precision - cinfo.Al - 1))) & 0xFFFF; undiff_buf[0] = Ra; for (uint xindex = 1; xindex < width; xindex++) { Ra = (diff_buf[xindex] + Ra) & 0xFFFF; undiff_buf[xindex] = Ra; } // Now that we have undifferenced the first row, we want to use the // undifferencer which corresponds to the predictor specified in the // scan header. switch (cinfo.Ss) { case 1: losslsd.predict_undifference[comp_index] = jpeg_undifference1; break; case 2: losslsd.predict_undifference[comp_index] = jpeg_undifference2; break; case 3: losslsd.predict_undifference[comp_index] = jpeg_undifference3; break; case 4: losslsd.predict_undifference[comp_index] = jpeg_undifference4; break; case 5: losslsd.predict_undifference[comp_index] = jpeg_undifference5; break; case 6: losslsd.predict_undifference[comp_index] = jpeg_undifference6; break; case 7: losslsd.predict_undifference[comp_index] = jpeg_undifference7; break; } }
const int CTX_POSTPONED_ROW = 2; // feeding postponed row group #endif // Initialize for a processing pass. static void start_pass_d_main(jpeg_decompress cinfo, J_BUF_MODE pass_mode) { my_d_main_controller main = (my_d_main_controller)cinfo.main; switch (pass_mode) { case J_BUF_MODE.JBUF_PASS_THRU: #if UPSCALING_CONTEXT if (cinfo.upsample.need_context_rows) { main.process_data = process_data_context_d_main; main.context_state = CTX_PREPARE_FOR_IMCU; main.iMCU_row_ctr = 0; } else #endif main.process_data = process_data_simple_d_main; // Simple case with no context needed main.buffer_full = false; // Mark buffer empty main.rowgroup_ctr = 0; break; #if QUANT_2PASS_SUPPORTED case J_BUF_MODE.JBUF_CRANK_DEST: main.process_data = process_data_crank_post_d_main; break; // For last pass of 2-pass quantization, just crank the postprocessor #endif default: ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); break; } }
// Routines to process JPEG markers. // // Entry condition: JPEG marker itself has been read and its code saved // in cinfo.unread_marker; input restart point is just after the marker. // // Exit: if return true, have read and processed any parameters, and have // updated the restart point to point after the parameters. // If return false, was forced to suspend before reaching end of // marker parameters; restart point has not been moved. Same routine // will be called again after application supplies more input data. // // This approach to suspension assumes that all of a marker's parameters // can fit into a single input bufferload. This should hold for "normal" // markers. Some COM/APPn markers might have large parameter segments // that might not fit. If we are simply dropping such a marker, we use // skip_input_data to get past it, and thereby put the problem on the // source manager's shoulders. If we are saving the marker's contents // into memory, we use a slightly different convention: when forced to // suspend, the marker processor updates the restart point to the end of // what it's consumed (ie, the end of the buffer) before returning false. // On resumption, cinfo.unread_marker still contains the marker code, // but the data source will point to the next chunk of marker data. // The marker processor must retain internal state to deal with this. // // Note that we don't bother to avoid duplicate trace messages if a // suspension occurs within marker parameters. Other side effects // require more care. // Process an SOI marker static bool get_soi(jpeg_decompress cinfo) { TRACEMS(cinfo, 1, J_MESSAGE_CODE.JTRC_SOI); if(cinfo.marker.saw_SOI) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_SOI_DUPLICATE); // Reset all parameters that are defined to be reset by SOI for(int i=0; i<NUM_ARITH_TBLS; i++) { cinfo.arith_dc_L[i]=0; cinfo.arith_dc_U[i]=1; cinfo.arith_ac_K[i]=5; } cinfo.restart_interval=0; // Set initial assumptions for colorspace etc cinfo.jpeg_color_space=J_COLOR_SPACE.JCS_UNKNOWN; cinfo.CCIR601_sampling=false; // Assume non-CCIR sampling??? cinfo.saw_JFIF_marker=false; cinfo.JFIF_major_version=1; // set default JFIF APP0 values cinfo.JFIF_minor_version=1; cinfo.density_unit=0; cinfo.X_density=1; cinfo.Y_density=1; cinfo.saw_Adobe_marker=false; cinfo.Adobe_transform=0; cinfo.marker.saw_SOI=true; return true; }
// Save away a copy of the Q-table referenced by each component present // in the current scan, unless already saved during a prior scan. // // In a multiple-scan JPEG file, the encoder could assign different components // the same Q-table slot number, but change table definitions between scans // so that each component uses a different Q-table. (The IJG encoder is not // currently capable of doing this, but other encoders might.) Since we want // to be able to dequantize all the components at the end of the file, this // means that we have to save away the table actually used for each component. // We do this by copying the table at the start of the first scan containing // the component. // The JPEG spec prohibits the encoder from changing the contents of a Q-table // slot between scans of a component using that slot. If the encoder does so // anyway, this decoder will simply use the Q-table values that were current // at the start of the first scan for the component. // // The decompressor output side looks only at the saved quant tables, // not at the current Q-table slots. static void latch_quant_tables_lossy(jpeg_decompress cinfo) { for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; // No work if we already saved Q-table for this component if (compptr.quant_table != null) { continue; } // Make sure specified quantization table is present int qtblno = compptr.quant_tbl_no; if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || cinfo.quant_tbl_ptrs[qtblno] == null) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); } // OK, save away the quantization table JQUANT_TBL qtbl = new JQUANT_TBL(); cinfo.quant_tbl_ptrs[qtblno].quantval.CopyTo(qtbl.quantval, 0); qtbl.sent_table = cinfo.quant_tbl_ptrs[qtblno].sent_table; compptr.quant_table = qtbl; } }
// Initialize for a Huffman-compressed scan. static void start_pass_huff_decoder(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd = (jpeg_lossy_d_codec)cinfo.coef; shuff_entropy_decoder entropy = (shuff_entropy_decoder)lossyd.entropy_private; // Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. // This ought to be an error condition, but we make it a warning because // there are some baseline files out there with all zeroes in these bytes. if (cinfo.Ss != 0 || cinfo.Se != DCTSIZE2 - 1 || cinfo.Ah != 0 || cinfo.Al != 0) { WARNMS(cinfo, J_MESSAGE_CODE.JWRN_NOT_SEQUENTIAL); } 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; // Compute derived values for Huffman tables // We may do this more than once for a table, but it's not expensive jpeg_make_d_derived_tbl(cinfo, true, dctbl, ref entropy.dc_derived_tbls[dctbl]); jpeg_make_d_derived_tbl(cinfo, false, actbl, ref entropy.ac_derived_tbls[actbl]); // Initialize DC predictions to 0 entropy.saved.last_dc_val[ci] = 0; } // Precalculate decoding info for each block in an MCU of this scan for (int blkn = 0; blkn < cinfo.blocks_in_MCU; blkn++) { int ci = cinfo.MCU_membership[blkn]; jpeg_component_info compptr = cinfo.cur_comp_info[ci]; // Precalculate which table to use for each block entropy.dc_cur_tbls[blkn] = entropy.dc_derived_tbls[compptr.dc_tbl_no]; entropy.ac_cur_tbls[blkn] = entropy.ac_derived_tbls[compptr.ac_tbl_no]; // Decide whether we really care about the coefficient values if (compptr.component_needed) { entropy.dc_needed[blkn] = true; // we don't need the ACs if producing a 1/8th-size image entropy.ac_needed[blkn] = (compptr.DCT_scaled_size > 1); } else { entropy.dc_needed[blkn] = entropy.ac_needed[blkn] = false; } } // Initialize bitread state variables entropy.bitstate.bits_left = 0; entropy.bitstate.get_buffer = 0; // unnecessary, but keeps Purify quiet entropy.insufficient_data = false; // Initialize restart counter entropy.restarts_to_go = cinfo.restart_interval; }
// Initialize for an input processing pass. static void start_input_pass_lossy(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd = (jpeg_lossy_d_codec)cinfo.coef; latch_quant_tables_lossy(cinfo); lossyd.entropy_start_pass(cinfo); lossyd.coef_start_input_pass(cinfo); }
// Compute output image dimensions and related values. static void calc_output_dimensions_lossy(jpeg_decompress cinfo) { // Hardwire it to "no scaling" cinfo.output_width=cinfo.image_width; cinfo.output_height=cinfo.image_height; // jdinput.cs has already initialized codec_data_unit to DCTSIZE, // and has computed unscaled downsampled_width and downsampled_height. }
// Initialize for an input processing pass. static void start_input_pass_lossy(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; latch_quant_tables_lossy(cinfo); lossyd.entropy_start_pass(cinfo); lossyd.coef_start_input_pass(cinfo); }
static void simple_downscale(jpeg_decompress cinfo, int[] diff_buf, byte[] output_buf, uint width) { jpeg_lossless_d_codec losslsd=(jpeg_lossless_d_codec)cinfo.coef; scaler scaler=(scaler)losslsd.scaler_private; int scale_factor=scaler.scale_factor; for(uint xindex=0; xindex<width; xindex++) output_buf[xindex]=(byte)(diff_buf[xindex]>>scale_factor); }
// Compute output image dimensions and related values. static void calc_output_dimensions_lossy(jpeg_decompress cinfo) { // Hardwire it to "no scaling" cinfo.output_width = cinfo.image_width; cinfo.output_height = cinfo.image_height; // jdinput.cs has already initialized codec_data_unit to DCTSIZE, // and has computed unscaled downsampled_width and downsampled_height. }
// Initialize for an input processing pass. static void start_input_pass_ls(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd=(jpeg_lossless_d_codec)cinfo.coef; losslsd.entropy_start_pass(cinfo); losslsd.predict_start_pass(cinfo); losslsd.scaler_start_pass(cinfo); losslsd.diff_start_input_pass(cinfo); }
// Initialize for an input processing pass. static void start_input_pass_ls(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; losslsd.entropy_start_pass(cinfo); losslsd.predict_start_pass(cinfo); losslsd.scaler_start_pass(cinfo); losslsd.diff_start_input_pass(cinfo); }
// Have we finished reading the input file? public static bool jpeg_input_complete(jpeg_decompress cinfo) { // Check for valid jpeg object if (cinfo.global_state < STATE.DSTART || cinfo.global_state > STATE.DSTOPPING) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } return(cinfo.inputctl.eoi_reached); }
// Is there more than one scan? public static bool jpeg_has_multiple_scans(jpeg_decompress cinfo) { // Only valid after jpeg_read_header completes if (cinfo.global_state < STATE.DREADY || cinfo.global_state > STATE.DSTOPPING) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } return(cinfo.inputctl.has_multiple_scans); }
// Finish up at end of an output pass. static void finish_output_pass(jpeg_decompress cinfo) { my_decomp_master master = (my_decomp_master)cinfo.master; if (cinfo.quantize_colors) { cinfo.cquantize.finish_pass(cinfo); } master.pass_number++; }
// Read next input byte; we do not support suspension in this module. static int get_byte_arith(jpeg_decompress cinfo) { jpeg_source_mgr src=cinfo.src; if(src.bytes_in_buffer==0) { if(!src.fill_input_buffer(cinfo)) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CANT_SUSPEND); } src.bytes_in_buffer--; return src.input_bytes[src.next_input_byte++]; }
// Undifferencers for the all rows but the first in a scan or restart interval. // The first sample in the row is undifferenced using the vertical // predictor (2). The rest of the samples are undifferenced using the // predictor specified in the scan header. static void jpeg_undifference1(jpeg_decompress cinfo, int comp_index, int[] diff_buf, int[] prev_row, int[] undiff_buf, uint width) { int Ra=(diff_buf[0]+prev_row[0])&0xFFFF; undiff_buf[0]=Ra; for(uint xindex=1; xindex<width; xindex++) { Ra=(diff_buf[xindex]+Ra)&0xFFFF; undiff_buf[xindex]=Ra; } }
// Initialize for a Huffman-compressed scan. static void start_pass_huff_decoder(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; shuff_entropy_decoder entropy=(shuff_entropy_decoder)lossyd.entropy_private; // Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. // This ought to be an error condition, but we make it a warning because // there are some baseline files out there with all zeroes in these bytes. if(cinfo.Ss!=0||cinfo.Se!=DCTSIZE2-1||cinfo.Ah!=0||cinfo.Al!=0) WARNMS(cinfo, J_MESSAGE_CODE.JWRN_NOT_SEQUENTIAL); 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; // Compute derived values for Huffman tables // We may do this more than once for a table, but it's not expensive jpeg_make_d_derived_tbl(cinfo, true, dctbl, ref entropy.dc_derived_tbls[dctbl]); jpeg_make_d_derived_tbl(cinfo, false, actbl, ref entropy.ac_derived_tbls[actbl]); // Initialize DC predictions to 0 entropy.saved.last_dc_val[ci]=0; } // Precalculate decoding info for each block in an MCU of this scan for(int blkn=0; blkn<cinfo.blocks_in_MCU; blkn++) { int ci=cinfo.MCU_membership[blkn]; jpeg_component_info compptr=cinfo.cur_comp_info[ci]; // Precalculate which table to use for each block entropy.dc_cur_tbls[blkn]=entropy.dc_derived_tbls[compptr.dc_tbl_no]; entropy.ac_cur_tbls[blkn]=entropy.ac_derived_tbls[compptr.ac_tbl_no]; // Decide whether we really care about the coefficient values if(compptr.component_needed) { entropy.dc_needed[blkn]=true; // we don't need the ACs if producing a 1/8th-size image entropy.ac_needed[blkn]=(compptr.DCT_scaled_size>1); } else entropy.dc_needed[blkn]=entropy.ac_needed[blkn]=false; } // Initialize bitread state variables entropy.bitstate.bits_left=0; entropy.bitstate.get_buffer=0; // unnecessary, but keeps Purify quiet entropy.insufficient_data=false; // Initialize restart counter entropy.restarts_to_go=cinfo.restart_interval; }
// Initialize coefficient buffer controller. static void jinit_d_coef_controller(jpeg_decompress cinfo, bool need_full_buffer) { jpeg_lossy_d_codec lossyd = (jpeg_lossy_d_codec)cinfo.coef; d_coef_controller coef = null; try { coef = new d_coef_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } lossyd.coef_private = coef; lossyd.coef_start_input_pass = start_input_pass_d_coef; lossyd.coef_start_output_pass = start_output_pass_d_coef; #if BLOCK_SMOOTHING_SUPPORTED coef.coef_bits_latch = null; #endif // Create the coefficient buffer. if (need_full_buffer) { #if D_MULTISCAN_FILES_SUPPORTED // Allocate a full-image array for each component, // padded to a multiple of samp_factor DCT blocks in each direction. // Note we ask for a pre-zeroed array. for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; int access_rows = compptr.v_samp_factor; coef.whole_image[ci] = alloc_barray(cinfo, (uint)jround_up(compptr.width_in_blocks, compptr.h_samp_factor), (uint)jround_up(compptr.height_in_blocks, compptr.v_samp_factor)); } lossyd.consume_data = consume_data; lossyd.decompress_data = decompress_data; lossyd.coef_arrays = coef.whole_image; // link to arrays #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif } else { // We only need a single-MCU buffer. for (int i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { coef.MCU_buffer[i] = new short[DCTSIZE2]; } lossyd.consume_data = dummy_consume_data_d_coef; lossyd.decompress_data = decompress_onepass; lossyd.coef_arrays = null; // flag for no arrays } }
// Undifferencers for the all rows but the first in a scan or restart interval. // The first sample in the row is undifferenced using the vertical // predictor (2). The rest of the samples are undifferenced using the // predictor specified in the scan header. static void jpeg_undifference1(jpeg_decompress cinfo, int comp_index, int[] diff_buf, int[] prev_row, int[] undiff_buf, uint width) { int Ra = (diff_buf[0] + prev_row[0]) & 0xFFFF; undiff_buf[0] = Ra; for (uint xindex = 1; xindex < width; xindex++) { Ra = (diff_buf[xindex] + Ra) & 0xFFFF; undiff_buf[xindex] = Ra; } }
static void simple_downscale(jpeg_decompress cinfo, int[] diff_buf, byte[] output_buf, uint width) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; scaler scaler = (scaler)losslsd.scaler_private; int scale_factor = scaler.scale_factor; for (uint xindex = 0; xindex < width; xindex++) { output_buf[xindex] = (byte)(diff_buf[xindex] >> scale_factor); } }
// Initialize the decompression codec. // This is called only once, during master selection. static void jinit_d_codec(jpeg_decompress cinfo) { if(cinfo.process==J_CODEC_PROCESS.JPROC_LOSSLESS) { #if D_LOSSLESS_SUPPORTED jinit_lossless_d_codec(cinfo); #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif } else jinit_lossy_d_codec(cinfo); }
// Initialize for a processing pass. static void start_pass_dpost(jpeg_decompress cinfo, J_BUF_MODE pass_mode) { my_post_controller post = (my_post_controller)cinfo.post; switch (pass_mode) { case J_BUF_MODE.JBUF_PASS_THRU: if (cinfo.quantize_colors) { // Single-pass processing with color quantization. post.post_process_data = post_process_1pass; // We could be doing buffered-image output before starting a 2-pass // color quantization; in that case, jinit_d_post_controller did not // allocate a strip buffer. Use the virtual-array buffer as workspace. if (post.buffer == null) { post.buffer = post.whole_image; post.buffer_offset = 0; } } else { // For single-pass processing without color quantization, // I have no work to do; just call the upsampler directly. post.post_process_data = cinfo.upsample.upsample; } break; #if QUANT_2PASS_SUPPORTED case J_BUF_MODE.JBUF_SAVE_AND_PASS: // First pass of 2-pass quantization if (post.whole_image == null) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); } post.post_process_data = post_process_prepass; break; case J_BUF_MODE.JBUF_CRANK_DEST: // Second pass of 2-pass quantization if (post.whole_image == null) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); } post.post_process_data = post_process_2pass; break; #endif // QUANT_2PASS_SUPPORTED default: ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); break; } post.starting_row = post.next_row = 0; }
// Decompression initialization. // jpeg_read_header must be completed before calling this. // // If a multipass operating mode was selected, this will do all but the // last pass, and thus may take a great deal of time. // // Returns false if suspended. The return value need be inspected only if // a suspending data source is used. public static bool jpeg_start_decompress(jpeg_decompress cinfo) { if(cinfo.global_state==STATE.DREADY) { // First call: initialize master control, select active modules jinit_master_decompress(cinfo); if(cinfo.buffered_image) { // No more work here; expecting jpeg_start_output next cinfo.global_state=STATE.DBUFIMAGE; return true; } cinfo.global_state=STATE.DPRELOAD; } if(cinfo.global_state==STATE.DPRELOAD) { // If file has multiple scans, absorb them all into the coef buffer if(cinfo.inputctl.has_multiple_scans) { #if D_MULTISCAN_FILES_SUPPORTED for(; ; ) { // Call progress monitor hook if present if(cinfo.progress!=null) cinfo.progress.progress_monitor(cinfo); // Absorb some more input CONSUME_INPUT retcode=cinfo.inputctl.consume_input(cinfo); if(retcode==CONSUME_INPUT.JPEG_SUSPENDED) return false; if(retcode==CONSUME_INPUT.JPEG_REACHED_EOI) break; // Advance progress counter if appropriate if(cinfo.progress!=null&&(retcode==CONSUME_INPUT.JPEG_ROW_COMPLETED||retcode==CONSUME_INPUT.JPEG_REACHED_SOS)) { if(++cinfo.progress.pass_counter>=cinfo.progress.pass_limit) { // jdmaster underestimated number of scans; ratchet up one scan cinfo.progress.pass_limit+=(int)cinfo.total_iMCU_rows; } } } #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif // D_MULTISCAN_FILES_SUPPORTED } cinfo.output_scan_number=cinfo.input_scan_number; } else if(cinfo.global_state!=STATE.DPRESCAN) ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); // Perform any dummy output passes, and set up for the final pass return output_pass_setup(cinfo); }
// Convert grayscale to RGB: just duplicate the graylevel three times. // This is provided to support applications that don't want to cope // with grayscale as a separate case. static void gray_rgb_convert(jpeg_decompress cinfo, byte[][][] input_buf, uint input_row, byte[][] output_buf, uint output_row, int num_rows) { uint num_cols = cinfo.output_width; while (--num_rows >= 0) { byte[] inptr = input_buf[0][input_row++]; byte[] outptr = output_buf[output_row++]; for (uint col = 0, outptr_ind = 0; col < num_cols; col++, outptr_ind += RGB_PIXELSIZE) { outptr[outptr_ind + RGB_RED] = outptr[outptr_ind + RGB_GREEN] = outptr[outptr_ind + RGB_BLUE] = inptr[col]; } } }
// Read next input byte; we do not support suspension in this module. static int get_byte_arith(jpeg_decompress cinfo) { jpeg_source_mgr src = cinfo.src; if (src.bytes_in_buffer == 0) { if (!src.fill_input_buffer(cinfo)) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CANT_SUSPEND); } } src.bytes_in_buffer--; return(src.input_bytes[src.next_input_byte++]); }
static void jpeg_undifference5(jpeg_decompress cinfo, int comp_index, int[] diff_buf, int[] prev_row, int[] undiff_buf, uint width) { int Rb=prev_row[0]; int Ra=(diff_buf[0]+Rb)&0xFFFF; undiff_buf[0]=Ra; for(uint xindex=1; xindex<width; xindex++) { int Rc=Rb; Rb=prev_row[xindex]; Ra=(diff_buf[xindex]+Ra+((Rb-Rc)>>1))&0xFFFF; undiff_buf[xindex]=Ra; } }
// Initialize for an output processing pass. static void start_output_pass_d_coef(jpeg_decompress cinfo) { #if BLOCK_SMOOTHING_SUPPORTED jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; d_coef_controller coef=(d_coef_controller)lossyd.coef_private; // If multipass, check to see whether to use block smoothing on this pass if(lossyd.coef_arrays!=null) { if(cinfo.do_block_smoothing&&smoothing_ok(cinfo)) lossyd.decompress_data=decompress_smooth_data; else lossyd.decompress_data=decompress_data; } #endif cinfo.output_iMCU_row=0; }
static void jinit_d_scaler(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd=(jpeg_lossless_d_codec)cinfo.coef; scaler scaler=null; try { scaler=new scaler(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } losslsd.scaler_private=scaler; losslsd.scaler_start_pass=scaler_start_pass; }
// Set up for an output pass, and perform any dummy pass(es) needed. // Common subroutine for jpeg_start_decompress and jpeg_start_output. // Entry: global_state = DPRESCAN only if previously suspended. // Exit: If done, returns true and sets global_state for proper output mode. // If suspended, returns false and sets global_state = DPRESCAN. static bool output_pass_setup(jpeg_decompress cinfo) { if(cinfo.global_state!=STATE.DPRESCAN) { // First call: do pass setup cinfo.master.prepare_for_output_pass(cinfo); cinfo.output_scanline=0; cinfo.global_state=STATE.DPRESCAN; } // Loop over any required dummy passes while(cinfo.master.is_dummy_pass) { #if QUANT_2PASS_SUPPORTED // Crank through the dummy pass while(cinfo.output_scanline<cinfo.output_height) { // Call progress monitor hook if present if(cinfo.progress!=null) { cinfo.progress.pass_counter=(int)cinfo.output_scanline; cinfo.progress.pass_limit=(int)cinfo.output_height; cinfo.progress.progress_monitor(cinfo); } // Process some data uint last_scanline=cinfo.output_scanline; cinfo.main.process_data(cinfo, null, ref cinfo.output_scanline, 0); if(cinfo.output_scanline==last_scanline) return false; // No progress made, must suspend } // Finish up dummy pass, and set up for another one cinfo.master.finish_output_pass(cinfo); cinfo.master.prepare_for_output_pass(cinfo); cinfo.output_scanline=0; #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif // QUANT_2PASS_SUPPORTED } // Ready for application to drive output pass through // jpeg_read_scanlines or jpeg_read_raw_data. cinfo.global_state=cinfo.raw_data_out?STATE.DRAW_OK:STATE.DSCANNING; return true; }
public const int SAVED_COEFS=6; // we save coef_bits[0..5] // Reset within-iMCU-row counters for a new row (input side) static void start_iMCU_row_d_coef(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; d_coef_controller coef=(d_coef_controller)lossyd.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(cinfo.input_iMCU_row<(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; }
// Initialize for a processing pass. static void start_pass_dpost(jpeg_decompress cinfo, J_BUF_MODE pass_mode) { my_post_controller post=(my_post_controller)cinfo.post; switch(pass_mode) { case J_BUF_MODE.JBUF_PASS_THRU: if(cinfo.quantize_colors) { // Single-pass processing with color quantization. post.post_process_data=post_process_1pass; // We could be doing buffered-image output before starting a 2-pass // color quantization; in that case, jinit_d_post_controller did not // allocate a strip buffer. Use the virtual-array buffer as workspace. if(post.buffer==null) { post.buffer=post.whole_image; post.buffer_offset=0; } } else { // For single-pass processing without color quantization, // I have no work to do; just call the upsampler directly. post.post_process_data=cinfo.upsample.upsample; } break; #if QUANT_2PASS_SUPPORTED case J_BUF_MODE.JBUF_SAVE_AND_PASS: // First pass of 2-pass quantization if(post.whole_image==null) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); post.post_process_data=post_process_prepass; break; case J_BUF_MODE.JBUF_CRANK_DEST: // Second pass of 2-pass quantization if(post.whole_image==null) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); post.post_process_data=post_process_2pass; break; #endif // QUANT_2PASS_SUPPORTED default: ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); break; } post.starting_row=post.next_row=0; }
// Initialize the lossless decompression codec. // This is called only once, during master selection. static void jinit_lossless_d_codec(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd=null; // Create subobject try { losslsd=new jpeg_lossless_d_codec(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.coef=losslsd; // Initialize sub-modules // Entropy decoding: either Huffman or arithmetic coding. if(cinfo.arith_code) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_ARITH_NOTIMPL); } else { jinit_lhuff_decoder(cinfo); } // Undifferencer jinit_undifferencer(cinfo); // Scaler jinit_d_scaler(cinfo); bool use_c_buffer=cinfo.inputctl.has_multiple_scans||cinfo.buffered_image; jinit_d_diff_controller(cinfo, use_c_buffer); // Initialize method pointers. // // Note: consume_data, start_output_pass and decompress_data are // assigned in jddiffct.cs. losslsd.calc_output_dimensions=calc_output_dimensions_ls; losslsd.start_input_pass=start_input_pass_ls; }
static void scaler_start_pass(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd=(jpeg_lossless_d_codec)cinfo.coef; scaler scaler=(scaler)losslsd.scaler_private; // Downscale by the difference in the input vs. output precision. If the // output precision >= input precision, then do not downscale. int downscale=BITS_IN_JSAMPLE<cinfo.data_precision?cinfo.data_precision-BITS_IN_JSAMPLE:0; scaler.scale_factor=cinfo.Al-downscale; // Set scaler functions based on scale_factor (positive = left shift) if(scaler.scale_factor>0) losslsd.scaler_scale=simple_upscale; else if(scaler.scale_factor<0) { scaler.scale_factor=-scaler.scale_factor; losslsd.scaler_scale=simple_downscale; } else losslsd.scaler_scale=noscale; }
// Save away a copy of the Q-table referenced by each component present // in the current scan, unless already saved during a prior scan. // // In a multiple-scan JPEG file, the encoder could assign different components // the same Q-table slot number, but change table definitions between scans // so that each component uses a different Q-table. (The IJG encoder is not // currently capable of doing this, but other encoders might.) Since we want // to be able to dequantize all the components at the end of the file, this // means that we have to save away the table actually used for each component. // We do this by copying the table at the start of the first scan containing // the component. // The JPEG spec prohibits the encoder from changing the contents of a Q-table // slot between scans of a component using that slot. If the encoder does so // anyway, this decoder will simply use the Q-table values that were current // at the start of the first scan for the component. // // The decompressor output side looks only at the saved quant tables, // not at the current Q-table slots. static void latch_quant_tables_lossy(jpeg_decompress cinfo) { for(int ci=0; ci<cinfo.comps_in_scan; ci++) { jpeg_component_info compptr=cinfo.cur_comp_info[ci]; // No work if we already saved Q-table for this component if(compptr.quant_table!=null) continue; // Make sure specified quantization table is present int qtblno=compptr.quant_tbl_no; if(qtblno<0||qtblno>=NUM_QUANT_TBLS||cinfo.quant_tbl_ptrs[qtblno]==null) ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); // OK, save away the quantization table JQUANT_TBL qtbl=new JQUANT_TBL(); cinfo.quant_tbl_ptrs[qtblno].quantval.CopyTo(qtbl.quantval, 0); qtbl.sent_table=cinfo.quant_tbl_ptrs[qtblno].sent_table; compptr.quant_table=qtbl; } }
// Compute output image dimensions and related values. // NOTE: this is exported for possible use by application. // Hence it mustn't do anything that can't be done twice. // Also note that it may be called before the master module is initialized! // Do computations that are needed before master selection phase public static void jpeg_calc_output_dimensions(jpeg_decompress cinfo) { // Prevent application from calling me at wrong times if(cinfo.global_state!=STATE.DREADY) ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); cinfo.coef.calc_output_dimensions(cinfo); // Report number of components in selected colorspace. // Probably this should be in the color conversion module... switch(cinfo.out_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: cinfo.out_color_components=1; break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_YCbCr: cinfo.out_color_components=3; break; case J_COLOR_SPACE.JCS_CMYK: case J_COLOR_SPACE.JCS_YCCK: cinfo.out_color_components=4; break; default: cinfo.out_color_components=cinfo.num_components; break; // else must be same colorspace as in file } cinfo.output_components=(cinfo.quantize_colors?1:cinfo.out_color_components); // See if upsampler will want to emit more than one row at a time if(use_merged_upsample(cinfo)) cinfo.rec_outbuf_height=cinfo.max_v_samp_factor; else cinfo.rec_outbuf_height=1; }
// Initialize for an input processing pass. static void start_input_pass_d_coef(jpeg_decompress cinfo) { cinfo.input_iMCU_row=0; start_iMCU_row_d_coef(cinfo); }
// Initialize coefficient buffer controller. static void jinit_d_coef_controller(jpeg_decompress cinfo, bool need_full_buffer) { jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; d_coef_controller coef=null; try { coef=new d_coef_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } lossyd.coef_private=coef; lossyd.coef_start_input_pass=start_input_pass_d_coef; lossyd.coef_start_output_pass=start_output_pass_d_coef; #if BLOCK_SMOOTHING_SUPPORTED coef.coef_bits_latch=null; #endif // Create the coefficient buffer. if(need_full_buffer) { #if D_MULTISCAN_FILES_SUPPORTED // Allocate a full-image array for each component, // padded to a multiple of samp_factor DCT blocks in each direction. // Note we ask for a pre-zeroed array. for(int ci=0; ci<cinfo.num_components; ci++) { jpeg_component_info compptr=cinfo.comp_info[ci]; int access_rows=compptr.v_samp_factor; coef.whole_image[ci]=alloc_barray(cinfo, (uint)jround_up(compptr.width_in_blocks, compptr.h_samp_factor), (uint)jround_up(compptr.height_in_blocks, compptr.v_samp_factor)); } lossyd.consume_data=consume_data; lossyd.decompress_data=decompress_data; lossyd.coef_arrays=coef.whole_image; // link to arrays #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif } else { // We only need a single-MCU buffer. for(int i=0; i<D_MAX_BLOCKS_IN_MCU; i++) coef.MCU_buffer[i]=new short[DCTSIZE2]; lossyd.consume_data=dummy_consume_data_d_coef; lossyd.decompress_data=decompress_onepass; lossyd.coef_arrays=null; // flag for no arrays } }
// Variant of decompress_data for use when doing block smoothing. static CONSUME_INPUT decompress_smooth_data(jpeg_decompress cinfo, byte[][][] output_buf) { jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; d_coef_controller coef=(d_coef_controller)lossyd.coef_private; // Force some input to be done if we are getting ahead of the input. while(cinfo.input_scan_number<=cinfo.output_scan_number&&!cinfo.inputctl.eoi_reached) { if(cinfo.input_scan_number==cinfo.output_scan_number) { // If input is working on current scan, we ordinarily want it to // have completed the current row. But if input scan is DC, // we want it to keep one row ahead so that next block row's DC // values are up to date. uint delta=(cinfo.Ss==0)?1u:0u; if(cinfo.input_iMCU_row>cinfo.output_iMCU_row+delta) break; } if(cinfo.inputctl.consume_input(cinfo)==CONSUME_INPUT.JPEG_SUSPENDED) return CONSUME_INPUT.JPEG_SUSPENDED; } uint last_iMCU_row=cinfo.total_iMCU_rows-1; short[] workspace=new short[DCTSIZE2]; // OK, output from the arrays. for(int ci=0; ci<cinfo.num_components; ci++) { jpeg_component_info compptr=cinfo.comp_info[ci]; // Don't bother to IDCT an uninteresting component. if(!compptr.component_needed) continue; // Count non-dummy DCT block rows in this iMCU row. int block_rows, access_rows; bool last_row; if(cinfo.output_iMCU_row<last_iMCU_row) { block_rows=compptr.v_samp_factor; access_rows=block_rows*2; // this and next iMCU row last_row=false; } else { // NB: can't use last_row_height here; it is input-side-dependent! block_rows=(int)(compptr.height_in_blocks%compptr.v_samp_factor); if(block_rows==0) block_rows=compptr.v_samp_factor; access_rows=block_rows; // this iMCU row only last_row=true; } // Align the buffer for this component. bool first_row; short[][][] buffer; int buffer_ind; if(cinfo.output_iMCU_row>0) { access_rows+=compptr.v_samp_factor; // prior iMCU row too buffer=coef.whole_image[ci]; buffer_ind=(int)cinfo.output_iMCU_row*compptr.v_samp_factor; // point to current iMCU row first_row=false; } else { buffer=coef.whole_image[ci]; buffer_ind=0; first_row=true; } // Fetch component-dependent info int[] coef_bits=coef.coef_bits_latch[ci]; JQUANT_TBL quanttbl=compptr.quant_table; int Q00=quanttbl.quantval[0]; int Q01=quanttbl.quantval[Q01_POS]; int Q10=quanttbl.quantval[Q10_POS]; int Q20=quanttbl.quantval[Q20_POS]; int Q11=quanttbl.quantval[Q11_POS]; int Q02=quanttbl.quantval[Q02_POS]; inverse_DCT_method_ptr inverse_DCT=lossyd.inverse_DCT[ci]; uint output_buf_ind=0; // Loop over all DCT blocks to be processed. for(int block_row=0; block_row<block_rows; block_row++) { short[][] buffer_ptr=buffer[buffer_ind+block_row]; short[][] prev_block_row; short[][] next_block_row; if(first_row&&block_row==0) prev_block_row=buffer_ptr; else prev_block_row=buffer[buffer_ind+block_row-1]; if(last_row&&block_row==block_rows-1) next_block_row=buffer_ptr; else next_block_row=buffer[buffer_ind+block_row+1]; // We fetch the surrounding DC values using a sliding-register approach. // Initialize all nine here so as to do the right thing on narrow pics. int DC1, DC2, DC3, DC4, DC5, DC6, DC7, DC8, DC9; DC1=DC2=DC3=(int)prev_block_row[0][0]; DC4=DC5=DC6=(int)buffer_ptr[0][0]; DC7=DC8=DC9=(int)next_block_row[0][0]; int ind=1; uint output_col=0; uint last_block_column=compptr.width_in_blocks-1; for(uint block_num=0; block_num<=last_block_column; block_num++) { // Fetch current DCT block into workspace so we can modify it. buffer_ptr.CopyTo(workspace, 0); // Update DC values if(block_num<last_block_column) { DC3=(int)prev_block_row[ind][0]; DC6=(int)buffer_ptr[ind][0]; DC9=(int)next_block_row[ind][0]; } // Compute coefficient estimates per K.8. // An estimate is applied only if coefficient is still zero, // and is not known to be fully accurate. // AC01 int Al=coef_bits[1]; if(Al!=0&&workspace[1]==0) { int num=36*Q00*(DC4-DC6); int pred; if(num>=0) { pred=(int)(((Q01<<7)+num)/(Q01<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; } else { pred=(int)(((Q01<<7)-num)/(Q01<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; pred=-pred; } workspace[1]=(short)pred; } // AC10 Al=coef_bits[2]; if(Al!=0&&workspace[8]==0) { int num=36*Q00*(DC2-DC8); int pred; if(num>=0) { pred=(int)(((Q10<<7)+num)/(Q10<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; } else { pred=(int)(((Q10<<7)-num)/(Q10<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; pred=-pred; } workspace[8]=(short)pred; } // AC20 Al=coef_bits[3]; if(Al!=0&&workspace[16]==0) { int num=9*Q00*(DC2+DC8-2*DC5); int pred; if(num>=0) { pred=(int)(((Q20<<7)+num)/(Q20<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; } else { pred=(int)(((Q20<<7)-num)/(Q20<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; pred=-pred; } workspace[16]=(short)pred; } // AC11 Al=coef_bits[4]; if(Al!=0&&workspace[9]==0) { int num=5*Q00*(DC1-DC3-DC7+DC9); int pred; if(num>=0) { pred=(int)(((Q11<<7)+num)/(Q11<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; } else { pred=(int)(((Q11<<7)-num)/(Q11<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; pred=-pred; } workspace[9]=(short)pred; } // AC02 Al=coef_bits[5]; if(Al!=0&&workspace[2]==0) { int num=9*Q00*(DC4+DC6-2*DC5); int pred; if(num>=0) { pred=(int)(((Q02<<7)+num)/(Q02<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; } else { pred=(int)(((Q02<<7)-num)/(Q02<<8)); if(Al>0&&pred>=(1<<Al)) pred=(1<<Al)-1; pred=-pred; } workspace[2]=(short)pred; } // OK, do the IDCT inverse_DCT(cinfo, compptr, workspace, output_buf[ci], output_buf_ind, output_col); // Advance for next column DC1=DC2; DC2=DC3; DC4=DC5; DC5=DC6; DC7=DC8; DC8=DC9; ind++; output_col+=compptr.DCT_scaled_size; } output_buf_ind+=compptr.DCT_scaled_size; } } if(++(cinfo.output_iMCU_row)<cinfo.total_iMCU_rows) return CONSUME_INPUT.JPEG_ROW_COMPLETED; return CONSUME_INPUT.JPEG_SCAN_COMPLETED; }
// Determine whether block smoothing is applicable and safe. // We also latch the current states of the coef_bits[] entries for the // AC coefficients; otherwise, if the input side of the decompressor // advances into a new scan, we might think the coefficients are known // more accurately than they really are. static bool smoothing_ok(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; d_coef_controller coef=(d_coef_controller)lossyd.coef_private; if(cinfo.process!=J_CODEC_PROCESS.JPROC_PROGRESSIVE||cinfo.coef_bits==null) return false; // Allocate latch area if not already done if(coef.coef_bits_latch==null) { try { coef.coef_bits_latch=new int[cinfo.num_components][]; for(int i=0; i<cinfo.num_components; i++) coef.coef_bits_latch[i]=new int[SAVED_COEFS]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } bool smoothing_useful=false; for(int ci=0; ci<cinfo.num_components; ci++) { jpeg_component_info compptr=cinfo.comp_info[ci]; // All components' quantization values must already be latched. JQUANT_TBL qtable=compptr.quant_table; if(qtable==null) return false; // Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. if(qtable.quantval[0]==0||qtable.quantval[Q01_POS]==0||qtable.quantval[Q10_POS]==0||qtable.quantval[Q20_POS]==0||qtable.quantval[Q11_POS]==0||qtable.quantval[Q02_POS]==0) return false; // DC values must be at least partly known for all components. int[] coef_bits=cinfo.coef_bits[ci]; if(coef_bits[0]<0) return false; // Block smoothing is helpful if some AC coefficients remain inaccurate. for(int coefi=1; coefi<=5; coefi++) { coef.coef_bits_latch[ci][coefi]=coef_bits[coefi]; if(coef_bits[coefi]!=0) smoothing_useful=true; } } return smoothing_useful; }
// Decompress and return some data in the multi-pass case. // Always attempts to emit one fully interleaved MCU row ("iMCU" row). // Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. // // NB: output_buf contains a plane for each component in image. static CONSUME_INPUT decompress_data(jpeg_decompress cinfo, byte[][][] output_buf) { jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; d_coef_controller coef=(d_coef_controller)lossyd.coef_private; uint last_iMCU_row=cinfo.total_iMCU_rows-1; // Force some input to be done if we are getting ahead of the input. while(cinfo.input_scan_number<cinfo.output_scan_number||(cinfo.input_scan_number==cinfo.output_scan_number&&cinfo.input_iMCU_row<=cinfo.output_iMCU_row)) { if(cinfo.inputctl.consume_input(cinfo)==CONSUME_INPUT.JPEG_SUSPENDED) return CONSUME_INPUT.JPEG_SUSPENDED; } // OK, output from the arrays. for(int ci=0; ci<cinfo.num_components; ci++) { jpeg_component_info compptr=cinfo.comp_info[ci]; // Don't bother to IDCT an uninteresting component. if(!compptr.component_needed) continue; // Align the buffer for this component. short[][][] buffer=coef.whole_image[ci]; uint buffer_ind=cinfo.output_iMCU_row*(uint)compptr.v_samp_factor; // Count non-dummy DCT block rows in this iMCU row. int block_rows; if(cinfo.output_iMCU_row<last_iMCU_row) block_rows=compptr.v_samp_factor; else { // NB: can't use last_row_height here; it is input-side-dependent! block_rows=(int)(compptr.height_in_blocks%compptr.v_samp_factor); if(block_rows==0) block_rows=compptr.v_samp_factor; } inverse_DCT_method_ptr inverse_DCT=lossyd.inverse_DCT[ci]; byte[][] output_ptr=output_buf[ci]; uint output_ptr_ind=0; // Loop over all DCT blocks to be processed. for(int block_row=0; block_row<block_rows; block_row++) { short[][] buffer_ptr=buffer[buffer_ind+block_row]; uint output_col=0; for(uint block_num=0; block_num<compptr.width_in_blocks; block_num++) { inverse_DCT(cinfo, compptr, buffer_ptr[block_num], output_ptr, output_ptr_ind, output_col); output_col+=compptr.DCT_scaled_size; } output_ptr_ind+=compptr.DCT_scaled_size; } } if(++(cinfo.output_iMCU_row)<cinfo.total_iMCU_rows) return CONSUME_INPUT.JPEG_ROW_COMPLETED; return CONSUME_INPUT.JPEG_SCAN_COMPLETED; }
// Consume input data and store it in the full-image coefficient buffer. // We read as much as one fully interleaved MCU row ("iMCU" row) per call, // ie, v_samp_factor block rows for each component in the scan. // Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. static CONSUME_INPUT consume_data(jpeg_decompress cinfo) { jpeg_lossy_d_codec lossyd=(jpeg_lossy_d_codec)cinfo.coef; d_coef_controller coef=(d_coef_controller)lossyd.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. 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)cinfo.input_iMCU_row*compptr.v_samp_factor; // Note: entropy decoder expects buffer to be zeroed, // but this is handled automatically by the memory manager // because we requested a pre-zeroed array. } // 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++) // index of current MCU within row { // 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 fetch the MCU. if(!lossyd.entropy_decode_mcu(cinfo, coef.MCU_buffer)) { // Suspension forced; update state counters and exit coef.MCU_vert_offset=yoffset; coef.MCU_ctr=MCU_col_num; return CONSUME_INPUT.JPEG_SUSPENDED; } } // Completed an MCU row, but perhaps not an iMCU row coef.MCU_ctr=0; } // Completed the iMCU row, advance counters for next one if(++(cinfo.input_iMCU_row)<cinfo.total_iMCU_rows) { start_iMCU_row_d_coef(cinfo); return CONSUME_INPUT.JPEG_ROW_COMPLETED; } // Completed the scan cinfo.inputctl.finish_input_pass(cinfo); return CONSUME_INPUT.JPEG_SCAN_COMPLETED; }