// 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; } }
// Master selection of compression modules. // This is done once at the start of processing an image. We determine // which modules will be used and give them appropriate initialization calls. static void jinit_compress_master(jpeg_compress cinfo) { // Initialize master control (includes parameter checking/processing) jinit_c_master_control(cinfo, false); // full compression // Initialize compression codec jinit_c_codec(cinfo); // Preprocessing if(!cinfo.raw_data_in) { jinit_color_converter(cinfo); jinit_downsampler(cinfo); jinit_c_prep_controller(cinfo, false); // never need full buffer here } jinit_c_main_controller(cinfo, false); // never need full buffer here jinit_marker_writer(cinfo); // Write the datastream header (SOI) immediately. // Frame and scan headers are postponed till later. // This lets application insert special markers after the SOI. cinfo.marker.write_file_header(cinfo); }
// Initialize for RGB->YCC colorspace conversion. static void rgb_ycc_start(jpeg_compress cinfo) { my_color_converter cconvert=(my_color_converter)cinfo.cconvert; int[] rgb_ycc_tab=null; try { // Allocate and fill in the conversion tables. cconvert.rgb_ycc_tab=rgb_ycc_tab=new int[TABLE_SIZE]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } for(int i=0; i<=MAXJSAMPLE; i++) { rgb_ycc_tab[i+R_Y_OFF]=FIX_029900*i; rgb_ycc_tab[i+G_Y_OFF]=FIX_058700*i; rgb_ycc_tab[i+B_Y_OFF]=FIX_011400*i+ONE_HALF; rgb_ycc_tab[i+R_CB_OFF]=(-FIX_016874)*i; rgb_ycc_tab[i+G_CB_OFF]=(-FIX_033126)*i; // We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. // This ensures that the maximum output will round to MAXJSAMPLE // not MAXJSAMPLE+1, and thus that we don't have to range-limit. rgb_ycc_tab[i+B_CB_OFF]=FIX_050000*i+CBCR_OFFSET+ONE_HALF-1; // B=>Cb and R=>Cr tables are the same // rgb_ycc_tab[i+R_CR_OFF]=FIX_050000*i+CBCR_OFFSET+ONE_HALF-1; rgb_ycc_tab[i+G_CR_OFF]=(-FIX_041869)*i; rgb_ycc_tab[i+B_CR_OFF]=(-FIX_008131)*i; } }
// 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); }
// Initialize the compression codec. // This is called only once, during master selection. static void jinit_c_codec(jpeg_compress cinfo) { if(cinfo.process==J_CODEC_PROCESS.JPROC_LOSSLESS) { #if C_LOSSLESS_SUPPORTED jinit_lossless_c_codec(cinfo); #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif } else jinit_lossy_c_codec(cinfo); }
// Finish JPEG compression. // // If a multipass operating mode was selected, this may do a great deal of // work including most of the actual output. public static void jpeg_finish_compress(jpeg_compress cinfo) { if (cinfo.global_state == STATE.CSCANNING || cinfo.global_state == STATE.CRAW_OK) { // Terminate first pass if (cinfo.next_scanline < cinfo.image_height) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_TOO_LITTLE_DATA); } cinfo.master.finish_pass(cinfo); } else if (cinfo.global_state != STATE.CWRCOEFS) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } // Perform any remaining passes while (!cinfo.master.is_last_pass) { cinfo.master.prepare_for_pass(cinfo); for (uint iMCU_row = 0; iMCU_row < cinfo.total_iMCU_rows; iMCU_row++) { if (cinfo.progress != null) { cinfo.progress.pass_counter = (int)iMCU_row; cinfo.progress.pass_limit = (int)cinfo.total_iMCU_rows; cinfo.progress.progress_monitor(cinfo); } // We bypass the main controller and invoke coef controller directly; // all work is being done from the coefficient buffer. if (!cinfo.coef.compress_data(cinfo, null)) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CANT_SUSPEND); } } cinfo.master.finish_pass(cinfo); } // Write EOI, do final cleanup cinfo.marker.write_file_trailer(cinfo); cinfo.dest.term_destination(cinfo); // We can use jpeg_abort to release memory and reset global_state jpeg_abort(cinfo); }
// Initialize for a processing pass. static void start_pass_c_main(jpeg_compress cinfo, J_BUF_MODE pass_mode) { my_c_main_controller main=(my_c_main_controller)cinfo.main; // Do nothing in raw-data mode. if(cinfo.raw_data_in) return; main.cur_iMCU_row=0; // initialize counters main.rowgroup_ctr=0; main.suspended=false; main.pass_mode=pass_mode; // save mode for use by process_data switch(pass_mode) { case J_BUF_MODE.JBUF_PASS_THRU: main.process_data=process_data_simple_c_main; break; default: ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); break; } }
// 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. // This routine handles the simple pass-through mode, // where we have only a strip buffer. static void process_data_simple_c_main(jpeg_compress cinfo, byte[][] input_buf, ref uint in_row_ctr, uint in_rows_avail) { my_c_main_controller main=(my_c_main_controller)cinfo.main; uint DCT_size=cinfo.DCT_size; while(main.cur_iMCU_row<cinfo.total_iMCU_rows) { // Read input data if we haven't filled the main buffer yet if(main.rowgroup_ctr<DCT_size) cinfo.prep.pre_process_data(cinfo, input_buf, ref in_row_ctr, in_rows_avail, main.buffer, ref main.rowgroup_ctr, (uint)DCT_size); // If we don't have a full iMCU row buffered, return to application for // more data. Note that preprocessor will always pad to fill the iMCU row // at the bottom of the image. if(main.rowgroup_ctr!=DCT_size) return; // Send the completed row to the compressor if(!cinfo.coef.compress_data(cinfo, main.buffer)) { // If compressor did not consume the whole row, then we must need to // suspend processing and return to the application. In this situation // we pretend we didn't yet consume the last input row; otherwise, if // it happened to be the last row of the image, the application would // think we were done. if(!main.suspended) { in_row_ctr--; main.suspended=true; } return; } // We did finish the row. Undo our little suspension hack if a previous // call suspended; then mark the main buffer empty. if(main.suspended) { in_row_ctr++; main.suspended=false; } main.rowgroup_ctr=0; main.cur_iMCU_row++; } // while(...) }
// 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; }
// Emit a restart marker & resynchronize predictions. static void emit_restart_arith(jpeg_compress cinfo, int restart_num) { arith_entropy_encoder entropy = (arith_entropy_encoder)cinfo.coef; finish_pass_c_arith(cinfo); emit_byte(cinfo, 0xFF); emit_byte(cinfo, JPEG_RST0 + restart_num); for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; // Re-initialize statistics areas if (cinfo.process != J_CODEC_PROCESS.JPROC_PROGRESSIVE || (cinfo.Ss == 0 && cinfo.Ah == 0)) { for (int i = 0; i < DC_STAT_BINS; i++) { entropy.dc_stats[compptr.dc_tbl_no][i] = 0; } // Reset DC predictions to 0 entropy.last_dc_val[ci] = 0; entropy.dc_context[ci] = 0; } if (cinfo.process != J_CODEC_PROCESS.JPROC_PROGRESSIVE || cinfo.Ss != 0) { for (int i = 0; i < AC_STAT_BINS; i++) { entropy.ac_stats[compptr.ac_tbl_no][i] = 0; } } } // Reset arithmetic encoding variables entropy.c = 0; entropy.a = 0x10000; entropy.sc = 0; entropy.zc = 0; entropy.ct = 11; entropy.buffer = -1; // empty }
// 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; }
// Write an abbreviated table-specification datastream. // This consists of SOI, DQT and DHT tables, and EOI. // Any table that is defined and not marked sent_table = true will be // emitted. Note that all tables will be marked sent_table = true at exit. static void write_tables_only(jpeg_compress cinfo) { emit_marker(cinfo, JPEG_MARKER.M_SOI); for(int i=0; i<NUM_QUANT_TBLS; i++) { if(cinfo.quant_tbl_ptrs[i]!=null) emit_dqt(cinfo, i); } if(!cinfo.arith_code) { for(int i=0; i<NUM_HUFF_TBLS; i++) { if(cinfo.dc_huff_tbl_ptrs[i]!=null) emit_dht(cinfo, i, false); if(cinfo.ac_huff_tbl_ptrs[i]!=null) emit_dht(cinfo, i, true); } } emit_marker(cinfo, JPEG_MARKER.M_EOI); }
// Convert some rows of samples to the JPEG colorspace. // This version handles multi-component colorspaces without conversion. // We assume input_components == num_components. static void null_convert(jpeg_compress cinfo, byte[][] input_buf, uint in_row_index, byte[][][] output_buf, uint output_row, int num_rows) { int nc = cinfo.num_components; uint num_cols = cinfo.image_width; for (uint input_row = 0; input_row < num_rows; input_row++) { // It seems fastest to make a separate pass for each component. byte[] inptr = input_buf[in_row_index + input_row]; for (int ci = 0; ci < nc; ci++) { byte[] outptr = output_buf[ci][output_row]; for (uint col = 0, off = (uint)ci; col < num_cols; col++, off += (uint)nc) { outptr[col] = inptr[off]; } } output_row++; } }
// Differencers for the all rows but the first in a scan or restart interval. // The first sample in the row is differenced using the vertical // predictor (2). The rest of the samples are differenced using the // predictor specified in the scan header. static void jpeg_difference1(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; int samp=input_buf[0]; diff_buf[0]=samp-prev_row[0]; 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); } }
// 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; } }
// Initialize main buffer controller. static void jinit_c_main_controller(jpeg_compress cinfo, bool need_full_buffer) { my_c_main_controller main = null; try { main = new my_c_main_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.main = main; main.start_pass = start_pass_c_main; // We don't need to create a buffer in raw-data mode. if (cinfo.raw_data_in) { return; } // Create the buffer. It holds downsampled data, so each component // may be of a different size. if (need_full_buffer) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); } else { uint DCT_size = cinfo.DCT_size; // Allocate a strip buffer for each component for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; main.buffer[ci] = alloc_sarray(cinfo, compptr.width_in_blocks * DCT_size, (uint)(compptr.v_samp_factor * DCT_size)); } } }
// *************** Cases other than RGB -> YCbCr ************* // Convert some rows of samples to the JPEG colorspace. // This version handles RGB->grayscale conversion, which is the same // as the RGB->Y portion of RGB->YCbCr. // We assume rgb_ycc_start has been called (we only use the Y tables). static void rgb_gray_convert(jpeg_compress cinfo, byte[][] input_buf, uint in_row_index, byte[][][] output_buf, uint output_row, int num_rows) { my_color_converter cconvert = (my_color_converter)cinfo.cconvert; int r, g, b; int[] ctab = cconvert.rgb_ycc_tab; uint num_cols = cinfo.image_width; for (uint input_row = 0; input_row < num_rows; input_row++) { byte[] inptr = input_buf[in_row_index + input_row]; byte[] outptr = output_buf[0][output_row]; output_row++; for (uint col = 0, off = 0; col < num_cols; col++, off += RGB_PIXELSIZE) { r = inptr[off + RGB_RED]; g = inptr[off + RGB_GREEN]; b = inptr[off + RGB_BLUE]; outptr[col] = (byte)((ctab[r + R_Y_OFF] + ctab[g + G_Y_OFF] + ctab[b + B_Y_OFF]) >> SCALEBITS); // Y } } }
// Initialize for a processing pass. static void start_pass_c_main(jpeg_compress cinfo, J_BUF_MODE pass_mode) { my_c_main_controller main = (my_c_main_controller)cinfo.main; // Do nothing in raw-data mode. if (cinfo.raw_data_in) { return; } main.cur_iMCU_row = 0; // initialize counters main.rowgroup_ctr = 0; main.suspended = false; main.pass_mode = pass_mode; // save mode for use by process_data switch (pass_mode) { case J_BUF_MODE.JBUF_PASS_THRU: main.process_data = process_data_simple_c_main; break; default: ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); break; } }
// Emit a SOF marker static void emit_sof(jpeg_compress cinfo, JPEG_MARKER code) { emit_marker(cinfo, code); emit_2bytes(cinfo, 3*cinfo.num_components+2+5+1); // length // Make sure image isn't bigger than SOF field can handle if(cinfo.image_height>65535||cinfo.image_width>65535) ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, 65535); emit_byte(cinfo, cinfo.data_precision); emit_2bytes(cinfo, (int)cinfo.image_height); emit_2bytes(cinfo, (int)cinfo.image_width); emit_byte(cinfo, cinfo.num_components); for(int ci=0; ci<cinfo.num_components; ci++) { emit_byte(cinfo, cinfo.comp_info[ci].component_id); emit_byte(cinfo, (cinfo.comp_info[ci].h_samp_factor<<4)+cinfo.comp_info[ci].v_samp_factor); emit_byte(cinfo, cinfo.comp_info[ci].quant_tbl_no); } }
// Emit a DAC marker // Since the useful info is so small, we want to emit all the tables in // one DAC marker. Therefore this routine does its own scan of the table. static void emit_dac(jpeg_compress cinfo) { #if C_ARITH_CODING_SUPPORTED byte[] dc_in_use=new byte[NUM_ARITH_TBLS]; byte[] ac_in_use=new byte[NUM_ARITH_TBLS]; for(int i=0; i<NUM_ARITH_TBLS; i++) dc_in_use[i]=ac_in_use[i]=0; for(int i=0; i<cinfo.comps_in_scan; i++) { jpeg_component_info compptr=cinfo.cur_comp_info[i]; // DC needs no table for refinement scan if(cinfo.Ss==0&&cinfo.Ah==0) dc_in_use[compptr.dc_tbl_no]=1; // AC needs no table when not present if(cinfo.Se!=0) ac_in_use[compptr.ac_tbl_no]=1; } int length=0; for(int i=0; i<NUM_ARITH_TBLS; i++) length+=dc_in_use[i]+ac_in_use[i]; emit_marker(cinfo, JPEG_MARKER.M_DAC); emit_2bytes(cinfo, length*2+2); for(int i=0; i<NUM_ARITH_TBLS; i++) { if(dc_in_use[i]!=0) { emit_byte(cinfo, i); emit_byte(cinfo, cinfo.arith_dc_L[i]+(cinfo.arith_dc_U[i]<<4)); } if(ac_in_use[i]!=0) { emit_byte(cinfo, i+0x10); emit_byte(cinfo, cinfo.arith_ac_K[i]); } } #endif // C_ARITH_CODING_SUPPORTED }
// Emit an Adobe APP14 marker static void emit_adobe_app14(jpeg_compress cinfo) { // Length of APP14 block (2 bytes) // Block ID (5 bytes - ASCII "Adobe") // Version Number (2 bytes - currently 100) // Flags0 (2 bytes - currently 0) // Flags1 (2 bytes - currently 0) // Color transform (1 byte) // // Although Adobe TN 5116 mentions Version = 101, all the Adobe files // now in circulation seem to use Version = 100, so that's what we write. // // We write the color transform byte as 1 if the JPEG color space is // YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with // whether the encoder performed a transformation, which is pretty useless. emit_marker(cinfo, JPEG_MARKER.M_APP14); emit_2bytes(cinfo, 2+5+2+2+2+1); // length emit_byte(cinfo, 0x41); // Identifier: ASCII "Adobe" emit_byte(cinfo, 0x64); emit_byte(cinfo, 0x6F); emit_byte(cinfo, 0x62); emit_byte(cinfo, 0x65); emit_2bytes(cinfo, 100); // Version emit_2bytes(cinfo, 0); // Flags0 emit_2bytes(cinfo, 0); // Flags1 switch(cinfo.jpeg_color_space) { case J_COLOR_SPACE.JCS_YCbCr: emit_byte(cinfo, 1); // Color transform = 1 break; case J_COLOR_SPACE.JCS_YCCK: emit_byte(cinfo, 2); // Color transform = 2 break; default: emit_byte(cinfo, 0); // Color transform = 0 break; } }
// Emit a EXIF-compliant APP1 marker static void emit_exif_app1(jpeg_compress cinfo) { byte[] data=cinfo.exif.Generate(); if(data==null) return; if(data.Length<8) return; // at least the TIFF header must be written completely // Length of APP1 block (2 bytes) // Block ID (4 bytes - ASCII "Exif") // Zero byte (1 byte to terminate the ID string) // Zero byte (1 byte padding) // Data (n bytes) emit_marker(cinfo, JPEG_MARKER.M_APP1); emit_2bytes(cinfo, 2+4+1+1+data.Length); // length emit_byte(cinfo, 0x45); // Identifier: ASCII "Exif" emit_byte(cinfo, 0x78); emit_byte(cinfo, 0x69); emit_byte(cinfo, 0x66); emit_byte(cinfo, 0); // NULL emit_byte(cinfo, 0); // Padding foreach(byte b in data) emit_byte(cinfo, b); }
// 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; } } }
// NOTE: Uncomment the following #define if you want to use the // given formula for calculating the AC conditioning parameter Kx // for spectral selection progressive coding in section G.1.3.2 // of the spec (Kx = Kmin + SRL (8 + Se - Kmin) 4). // Although the spec and P&M authors claim that this "has proven // to give good results for 8 bit precision samples", I'm not // convinced yet that this is really beneficial. // Early tests gave only very marginal compression enhancements // (a few - around 5 or so - bytes even for very large files), // which would turn out rather negative if we'd suppress the // DAC (Define Arithmetic Conditioning) marker segments for // the default parameters in the future. // Note that currently the marker writing module emits 12-byte // DAC segments for a full-component scan in a color image. // This is not worth worrying about IMHO. However, since the // spec defines the default values to be used if the tables // are omitted (unlike Huffman tables, which are required // anyway), one might optimize this behaviour in the future, // and then it would be disadvantageous to use custom tables if // they don't provide sufficient gain to exceed the DAC size. // // On the other hand, I'd consider it as a reasonable result // that the conditioning has no significant influence on the // compression performance. This means that the basic // statistical model is already rather stable. // // Thus, at the moment, we use the default conditioning values // anyway, and do not use the custom formula. //#define CALCULATE_SPECTRAL_CONDITIONING // Finish up at the end of an arithmetic-compressed scan. static void finish_pass_c_arith(jpeg_compress cinfo) { arith_entropy_encoder e=(arith_entropy_encoder)cinfo.coef; // Section D.1.8: Termination of encoding // Find the e.c in the coding interval with the largest // number of trailing zero bits int temp=(int)((uint)(e.a-1+e.c)&(uint)0xFFFF0000); if(temp<e.c) e.c=temp+0x8000; else e.c=temp; // Send remaining bytes to output e.c<<=e.ct; if(((uint)e.c&0xF8000000)!=0) { // One final overflow has to be handled if(e.buffer>=0) { if(e.zc!=0) { do emit_byte(cinfo, 0x00); while((--e.zc)!=0); } emit_byte(cinfo, e.buffer+1); if((e.buffer+1)==0xFF) emit_byte(cinfo, 0x00); } e.zc+=e.sc; // carry-over converts stacked 0xFF bytes to 0x00 e.sc=0; } else { if(e.buffer==0) ++e.zc; else if(e.buffer>=0) { if(e.zc!=0) { do emit_byte(cinfo, 0x00); while((--e.zc)!=0); } emit_byte(cinfo, e.buffer); } if(e.sc!=0) { if(e.zc!=0) { do emit_byte(cinfo, 0x00); while((--e.zc)!=0); } do { emit_byte(cinfo, 0xFF); emit_byte(cinfo, 0x00); } while((--e.sc)!=0); } } // Output final bytes only if they are not 0x00 if((e.c&0x7FFF800)!=0) { if(e.zc!=0) // output final pending zero bytes { do emit_byte(cinfo, 0x00); while((--e.zc)!=0); } emit_byte(cinfo, (e.c>>19)&0xFF); if(((e.c>>19)&0xFF)==0xFF) emit_byte(cinfo, 0x00); if((e.c&0x7F800)!=0) { emit_byte(cinfo, (e.c>>11)&0xFF); if(((e.c>>11)&0xFF)==0xFF) emit_byte(cinfo, 0x00); } } }
// Module initialization routine for arithmetic entropy encoding. static void jinit_arith_encoder(jpeg_compress cinfo) { arith_entropy_encoder entropy=cinfo.coef as arith_entropy_encoder; if(entropy==null) { try { entropy=new arith_entropy_encoder(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.coef=entropy; } entropy.entropy_start_pass=start_pass_c_arith; entropy.entropy_finish_pass=finish_pass_c_arith; // Mark tables unallocated for(int i=0; i<NUM_ARITH_TBLS; i++) { entropy.dc_stats[i]=null; entropy.ac_stats[i]=null; } }
// MCU encoding for AC initial scan (either spectral selection, // or first pass of successive approximation). static bool encode_mcu_AC_first_arith(jpeg_compress cinfo, short[][] MCU_data) { arith_entropy_encoder entropy=(arith_entropy_encoder)cinfo.coef; // Emit restart marker if needed if(cinfo.restart_interval!=0) { if(entropy.restarts_to_go==0) { emit_restart_arith(cinfo, entropy.next_restart_num); entropy.restarts_to_go=cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num&=7; } entropy.restarts_to_go--; } // Encode the MCU data block short[] block=MCU_data[0]; int tbl=cinfo.cur_comp_info[0].ac_tbl_no; // Sections F.1.4.2 & F.1.4.4.2: Encoding of AC coefficients int k, ke, v; // Establish EOB (end-of-block) index for(ke=cinfo.Se+1; ke>1; ke--) { // 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. v=block[jpeg_natural_order[ke-1]]; if(v>=0) { v>>=cinfo.Al; if(v!=0) break; } else { v=-v; v>>=cinfo.Al; if(v!=0) break; } } // Figure F.5: Encode_AC_Coefficients for(k=cinfo.Ss; k<ke; k++) { byte[] st=entropy.ac_stats[tbl]; int st_ind=3*(k-1); arith_encode(cinfo, ref st[st_ind], 0); // EOB decision entropy.ac_stats[tbl][245]=0; for(; ; ) { v=block[jpeg_natural_order[k]]; if(v>=0) { v>>=cinfo.Al; if(v!=0) { arith_encode(cinfo, ref st[st_ind+1], 1); arith_encode(cinfo, ref entropy.ac_stats[tbl][245], 0); break; } } else { v=-v; v>>=cinfo.Al; if(v!=0) { arith_encode(cinfo, ref st[st_ind+1], 1); arith_encode(cinfo, ref entropy.ac_stats[tbl][245], 1); break; } } arith_encode(cinfo, ref st[st_ind+1], 0); st_ind+=3; k++; } st_ind+=2; // Figure F.8: Encoding the magnitude category of v int m=0; v--; if(v!=0) { arith_encode(cinfo, ref st[st_ind], 1); m=1; int v2=v; v2>>=1; if(v2!=0) { arith_encode(cinfo, ref st[st_ind], 1); m<<=1; st=entropy.ac_stats[tbl]; st_ind=(k<=cinfo.arith_ac_K[tbl]?189:217); while((v2>>=1)!=0) { arith_encode(cinfo, ref st[st_ind], 1); m<<=1; st_ind+=1; } } } arith_encode(cinfo, ref st[st_ind], 0); // Figure F.9: Encoding the magnitude bit pattern of v st_ind+=14; while((m>>=1)!=0) arith_encode(cinfo, ref st[st_ind], ((m&v)!=0)?1:0); } // Encode EOB decision only if k <= cinfo.Se if(k<=cinfo.Se) { byte[] st=entropy.ac_stats[tbl]; int st_ind=3*(k-1); arith_encode(cinfo, ref st[st_ind], 1); } return true; }
// MCU encoding for AC successive approximation refinement scan. static bool encode_mcu_AC_refine_arith(jpeg_compress cinfo, short[][] MCU_data) { arith_entropy_encoder entropy=(arith_entropy_encoder)cinfo.coef; // Emit restart marker if needed if(cinfo.restart_interval!=0) { if(entropy.restarts_to_go==0) { emit_restart_arith(cinfo, entropy.next_restart_num); entropy.restarts_to_go=cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num&=7; } entropy.restarts_to_go--; } // Encode the MCU data block short[] block=MCU_data[0]; int tbl=cinfo.cur_comp_info[0].ac_tbl_no; // Section G.1.3.3: Encoding of AC coefficients int k, ke, kex, v; // Establish EOB (end-of-block) index for(ke=cinfo.Se+1; ke>1; ke--) { // 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. v=block[jpeg_natural_order[ke-1]]; if(v>=0) { v>>=cinfo.Al; if(v!=0) break; } else { v=-v; v>>=cinfo.Al; if(v!=0) break; } } // Establish EOBx (previous stage end-of-block) index for(kex=ke; kex>1; kex--) { v=block[jpeg_natural_order[kex-1]]; if(v>=0) { v>>=cinfo.Ah; if(v!=0) break; } else { v=-v; v>>=cinfo.Ah; if(v!=0) break; } } // Figure G.10: Encode_AC_Coefficients_SA for(k=cinfo.Ss; k<ke; k++) { byte[] st=entropy.ac_stats[tbl]; int st_ind=3*(k-1); if(k>=kex) arith_encode(cinfo, ref st[st_ind], 0); // EOB decision entropy.ac_stats[tbl][245]=0; for(; ; ) { v=block[jpeg_natural_order[k]]; if(v>=0) { v>>=cinfo.Al; if(v!=0) { if((v>>1)!=0) arith_encode(cinfo, ref st[st_ind+2], (v&1)); // previously nonzero coef else { // newly nonzero coef arith_encode(cinfo, ref st[st_ind+1], 1); arith_encode(cinfo, ref entropy.ac_stats[tbl][245], 0); } break; } } else { v=-v; v>>=cinfo.Al; if(v!=0) { if((v>>1)!=0) arith_encode(cinfo, ref st[st_ind+2], (v&1)); // previously nonzero coef else { // newly nonzero coef arith_encode(cinfo, ref st[st_ind+1], 1); arith_encode(cinfo, ref entropy.ac_stats[tbl][245], 1); } break; } } arith_encode(cinfo, ref st[st_ind+1], 0); st_ind+=3; k++; } } // Encode EOB decision only if k <= cinfo.Se if(k<=cinfo.Se) { byte[] st=entropy.ac_stats[tbl]; int st_ind=3*(k-1); arith_encode(cinfo, ref st[st_ind], 1); } return true; }
// Write frame header. // This consists of DQT and SOFn markers. // Note that we do not emit the SOF until we have emitted the DQT(s). // This avoids compatibility problems with incorrect implementations that // try to error-check the quant table numbers as soon as they see the SOF. static void write_frame_header(jpeg_compress cinfo) { int prec=0; bool is_baseline; if(cinfo.process!=J_CODEC_PROCESS.JPROC_LOSSLESS) { // Emit DQT for each quantization table. // Note that emit_dqt() suppresses any duplicate tables. prec=0; for(int ci=0; ci<cinfo.num_components; ci++) { prec+=emit_dqt(cinfo, cinfo.comp_info[ci].quant_tbl_no); } // now prec is nonzero iff there are any 16-bit quant tables. } // Check for a non-baseline specification. // Note we assume that Huffman table numbers won't be changed later. if(cinfo.arith_code||cinfo.process!=J_CODEC_PROCESS.JPROC_SEQUENTIAL||cinfo.data_precision!=8) { is_baseline=false; } else { is_baseline=true; for(int ci=0; ci<cinfo.num_components; ci++) { if(cinfo.comp_info[ci].dc_tbl_no>1||cinfo.comp_info[ci].ac_tbl_no>1) { is_baseline=false; break; } } if(prec!=0&&is_baseline) { is_baseline=false; // If it's baseline except for quantizer size, warn the user TRACEMS(cinfo, 0, J_MESSAGE_CODE.JTRC_16BIT_TABLES); } } // Emit the proper SOF marker if(cinfo.arith_code) { if(cinfo.process==J_CODEC_PROCESS.JPROC_PROGRESSIVE) emit_sof(cinfo, JPEG_MARKER.M_SOF10); // SOF code for progressive arithmetic else if(cinfo.process==J_CODEC_PROCESS.JPROC_SEQUENTIAL) emit_sof(cinfo, JPEG_MARKER.M_SOF9); // SOF code for sequential arithmetic else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOTIMPL); } else { if(cinfo.process==J_CODEC_PROCESS.JPROC_PROGRESSIVE) emit_sof(cinfo, JPEG_MARKER.M_SOF2); // SOF code for progressive Huffman else if(cinfo.process==J_CODEC_PROCESS.JPROC_LOSSLESS) emit_sof(cinfo, JPEG_MARKER.M_SOF3); // SOF code for lossless Huffman else if(is_baseline) emit_sof(cinfo, JPEG_MARKER.M_SOF0); // SOF code for baseline implementation else emit_sof(cinfo, JPEG_MARKER.M_SOF1); // SOF code for non-baseline Huffman file } }
// Emit a restart marker & resynchronize predictions. static void emit_restart_arith(jpeg_compress cinfo, int restart_num) { arith_entropy_encoder entropy=(arith_entropy_encoder)cinfo.coef; finish_pass_c_arith(cinfo); emit_byte(cinfo, 0xFF); emit_byte(cinfo, JPEG_RST0+restart_num); for(int ci=0; ci<cinfo.comps_in_scan; ci++) { jpeg_component_info compptr=cinfo.cur_comp_info[ci]; // Re-initialize statistics areas if(cinfo.process!=J_CODEC_PROCESS.JPROC_PROGRESSIVE||(cinfo.Ss==0&&cinfo.Ah==0)) { for(int i=0; i<DC_STAT_BINS; i++) entropy.dc_stats[compptr.dc_tbl_no][i]=0; // Reset DC predictions to 0 entropy.last_dc_val[ci]=0; entropy.dc_context[ci]=0; } if(cinfo.process!=J_CODEC_PROCESS.JPROC_PROGRESSIVE||cinfo.Ss!=0) for(int i=0; i<AC_STAT_BINS; i++) entropy.ac_stats[compptr.ac_tbl_no][i]=0; } // Reset arithmetic encoding variables entropy.c=0; entropy.a=0x10000; entropy.sc=0; entropy.zc=0; entropy.ct=11; entropy.buffer=-1; // empty }
// *************** Cases other than RGB -> YCbCr ************* // Convert some rows of samples to the JPEG colorspace. // This version handles RGB->grayscale conversion, which is the same // as the RGB->Y portion of RGB->YCbCr. // We assume rgb_ycc_start has been called (we only use the Y tables). static void rgb_gray_convert(jpeg_compress cinfo, byte[][] input_buf, uint in_row_index, byte[][][] output_buf, uint output_row, int num_rows) { my_color_converter cconvert=(my_color_converter)cinfo.cconvert; int r, g, b; int[] ctab=cconvert.rgb_ycc_tab; uint num_cols=cinfo.image_width; for(uint input_row=0; input_row<num_rows; input_row++) { byte[] inptr=input_buf[in_row_index+input_row]; byte[] outptr=output_buf[0][output_row]; output_row++; for(uint col=0, off=0; col<num_cols; col++, off+=RGB_PIXELSIZE) { r=inptr[off+RGB_RED]; g=inptr[off+RGB_GREEN]; b=inptr[off+RGB_BLUE]; outptr[col]=(byte)((ctab[r+R_Y_OFF]+ctab[g+G_Y_OFF]+ctab[b+B_Y_OFF])>>SCALEBITS); // Y } } }
// 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); }
// Convert some rows of samples to the JPEG colorspace. // This version handles Adobe-style CMYK->YCCK conversion, // where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same // conversion as above, while passing K (black) unchanged. // We assume rgb_ycc_start has been called. static void cmyk_ycck_convert(jpeg_compress cinfo, byte[][] input_buf, uint in_row_index, byte[][][] output_buf, uint output_row, int num_rows) { my_color_converter cconvert=(my_color_converter)cinfo.cconvert; int r, g, b; int[] ctab=cconvert.rgb_ycc_tab; uint num_cols=cinfo.image_width; for(uint input_row=0; input_row<num_rows; input_row++) { byte[] inptr=input_buf[in_row_index+input_row]; byte[] outptr0=output_buf[0][output_row]; byte[] outptr1=output_buf[1][output_row]; byte[] outptr2=output_buf[2][output_row]; byte[] outptr3=output_buf[3][output_row]; output_row++; for(uint col=0, off=0; col<num_cols; col++, off+=4) { r=MAXJSAMPLE-inptr[off+0]; g=MAXJSAMPLE-inptr[off+1]; b=MAXJSAMPLE-inptr[off+2]; // K passes through as-is outptr3[col]=inptr[off+3]; // If the inputs are 0..MAXJSAMPLE, the outputs of these equations // must be too; we do not need an explicit range-limiting operation. outptr0[col]=(byte)((ctab[r+R_Y_OFF]+ctab[g+G_Y_OFF]+ctab[b+B_Y_OFF])>>SCALEBITS); // Y outptr1[col]=(byte)((ctab[r+R_CB_OFF]+ctab[g+G_CB_OFF]+ctab[b+B_CB_OFF])>>SCALEBITS); // Cb outptr2[col]=(byte)((ctab[r+R_CR_OFF]+ctab[g+G_CR_OFF]+ctab[b+B_CR_OFF])>>SCALEBITS); // Cr } } }
// 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); }
// 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); }
// Module initialization routine for input colorspace conversion. static void jinit_color_converter(jpeg_compress cinfo) { my_color_converter cconvert=null; try { cinfo.cconvert=cconvert=new my_color_converter(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } // set start_pass to null method until we find out differently cconvert.start_pass=null_method; // Make sure input_components agrees with in_color_space switch(cinfo.in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if(cinfo.input_components!=1) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_YCbCr: if(cinfo.input_components!=3) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; case J_COLOR_SPACE.JCS_CMYK: case J_COLOR_SPACE.JCS_YCCK: if(cinfo.input_components!=4) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; default: // JCS_UNKNOWN can be anything if(cinfo.input_components<1) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; } // Check num_components, set conversion method based on requested space switch(cinfo.jpeg_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if(cinfo.num_components!=1) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if(cinfo.in_color_space==J_COLOR_SPACE.JCS_GRAYSCALE) cconvert.color_convert=grayscale_convert; else if(cinfo.in_color_space==J_COLOR_SPACE.JCS_RGB) { cconvert.start_pass=rgb_ycc_start; cconvert.color_convert=rgb_gray_convert; } else if(cinfo.in_color_space==J_COLOR_SPACE.JCS_YCbCr) cconvert.color_convert=grayscale_convert; else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_RGB: if(cinfo.num_components!=3) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if(cinfo.in_color_space==J_COLOR_SPACE.JCS_RGB&&RGB_PIXELSIZE==3) cconvert.color_convert=null_convert; else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_YCbCr: if(cinfo.num_components!=3) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if(cinfo.in_color_space==J_COLOR_SPACE.JCS_RGB) { cconvert.start_pass=rgb_ycc_start; cconvert.color_convert=rgb_ycc_convert; } else if(cinfo.in_color_space==J_COLOR_SPACE.JCS_YCbCr) cconvert.color_convert=null_convert; else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_CMYK: if(cinfo.num_components!=4) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if(cinfo.in_color_space==J_COLOR_SPACE.JCS_CMYK) cconvert.color_convert=null_convert; else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_YCCK: if(cinfo.num_components!=4) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if(cinfo.in_color_space==J_COLOR_SPACE.JCS_CMYK) { cconvert.start_pass=rgb_ycc_start; cconvert.color_convert=cmyk_ycck_convert; } else if(cinfo.in_color_space==J_COLOR_SPACE.JCS_YCCK) cconvert.color_convert=null_convert; else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; default: // allow null conversion of JCS_UNKNOWN if(cinfo.jpeg_color_space!=cinfo.in_color_space|| cinfo.num_components!=cinfo.input_components) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); cconvert.color_convert=null_convert; break; } }
// Empty method for start_pass. static void null_method(jpeg_compress cinfo) { // no work needed }
// 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); }
static bool need_optimization_pass_ls(jpeg_compress cinfo) { return(true); }
// Convert some rows of samples to the JPEG colorspace. // This version handles multi-component colorspaces without conversion. // We assume input_components == num_components. static void null_convert(jpeg_compress cinfo, byte[][] input_buf, uint in_row_index, byte[][][] output_buf, uint output_row, int num_rows) { int nc=cinfo.num_components; uint num_cols=cinfo.image_width; for(uint input_row=0; input_row<num_rows; input_row++) { // It seems fastest to make a separate pass for each component. byte[] inptr=input_buf[in_row_index+input_row]; for(int ci=0; ci<nc; ci++) { byte[] outptr=output_buf[ci][output_row]; for(uint col=0, off=(uint)ci; col<num_cols; col++, off+=(uint)nc) outptr[col]=inptr[off]; } output_row++; } }
// 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; }
// 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)); }
// Convert some rows of samples to the JPEG colorspace. // // Note that we change from the application's interleaved-pixel format // to our internal noninterleaved, one-plane-per-component format. // The input buffer is therefore three times as wide as the output buffer. // // A starting row offset is provided only for the output buffer. The caller // can easily adjust the passed input_buf value to accommodate any row // offset required on that side. static void rgb_ycc_convert(jpeg_compress cinfo, byte[][] input_buf, uint in_row_index, byte[][][] output_buf, uint output_row, int num_rows) { my_color_converter cconvert=(my_color_converter)cinfo.cconvert; int r, g, b; int[] ctab=cconvert.rgb_ycc_tab; uint num_cols=cinfo.image_width; for(uint input_row=0; input_row<num_rows; input_row++) { byte[] inptr=input_buf[in_row_index+input_row]; byte[] outptr0=output_buf[0][output_row]; byte[] outptr1=output_buf[1][output_row]; byte[] outptr2=output_buf[2][output_row]; output_row++; for(uint col=0, off=0; col<num_cols; col++, off+=RGB_PIXELSIZE) { r=inptr[off+RGB_RED]; g=inptr[off+RGB_GREEN]; b=inptr[off+RGB_BLUE]; // If the inputs are 0..MAXJSAMPLE, the outputs of these equations // must be too; we do not need an explicit range-limiting operation. outptr0[col]=(byte)((ctab[r+R_Y_OFF]+ctab[g+G_Y_OFF]+ctab[b+B_Y_OFF])>>SCALEBITS); // Y outptr1[col]=(byte)((ctab[r+R_CB_OFF]+ctab[g+G_CB_OFF]+ctab[b+B_CB_OFF])>>SCALEBITS); // Cb outptr2[col]=(byte)((ctab[r+R_CR_OFF]+ctab[g+G_CR_OFF]+ctab[b+B_CR_OFF])>>SCALEBITS); // Cr //outptr0[col]=(byte)(0.299*r+0.587*g+0.114*b); //outptr1[col]=(byte)(-0.168736*r-0.331264*g+0.5*b+CENTERJSAMPLE); //outptr2[col]=(byte)(0.5*r-0.418688*g-0.081312*b+CENTERJSAMPLE); } } }
// The core arithmetic encoding routine (common in JPEG and JBIG). // This needs to go as fast as possible. // Machine-dependent optimization facilities // are not utilized in this portable implementation. // However, this code should be fairly efficient and // may be a good base for further optimizations anyway. // // Parameter 'val' to be encoded may be 0 or 1 (binary decision). // // Note: I've added full "Pacman" termination support to the // byte output routines, which is equivalent to the optional // Discard_final_zeros procedure (Figure D.15) in the spec. // Thus, we always produce the shortest possible output // stream compliant to the spec (no trailing zero bytes, // except for FF stuffing). // // I've also introduced a new scheme for accessing // the probability estimation state machine table, // derived from Markus Kuhn's JBIG implementation. static void arith_encode(jpeg_compress cinfo, ref byte st, int val) { arith_entropy_encoder e=(arith_entropy_encoder)cinfo.coef; // Fetch values from our compact representation of Table D.2: // Qe values and probability estimation state machine int sv=st; int qe=jaritab[sv&0x7F]; // => Qe_Value byte nl=(byte)(qe&0xFF); qe>>=8; // Next_Index_LPS + Switch_MPS byte nm=(byte)(qe&0xFF); qe>>=8; // Next_Index_MPS // Encode & estimation procedures per sections D.1.4 & D.1.5 e.a-=qe; if(val!=(sv>>7)) { // Encode the less probable symbol if(e.a>=qe) { // If the interval size (qe) for the less probable symbol (LPS) // is larger than the interval size for the MPS, then exchange // the two symbols for coding efficiency, otherwise code the LPS // as usual: e.c+=e.a; e.a=qe; } st=(byte)((sv&0x80)^nl); // Estimate_after_LPS } else { // Encode the more probable symbol if(e.a>=0x8000) return; // A >= 0x8000 -> ready, no renormalization required if(e.a<qe) { // If the interval size (qe) for the less probable symbol (LPS) // is larger than the interval size for the MPS, then exchange // the two symbols for coding efficiency: e.c+=e.a; e.a=qe; } st=(byte)((sv&0x80)^nm); // Estimate_after_MPS } // Renormalization & data output per section D.1.6 do { e.a<<=1; e.c<<=1; e.ct--; if(e.ct==0) { // Another byte is ready for output int temp=e.c>>19; if(temp>0xFF) { // Handle overflow over all stacked 0xFF bytes if(e.buffer>=0) { if(e.zc!=0) { do emit_byte(cinfo, 0x00); while((--e.zc)!=0); } emit_byte(cinfo, e.buffer+1); if(e.buffer+1==0xFF) emit_byte(cinfo, 0x00); } e.zc+=e.sc; // carry-over converts stacked 0xFF bytes to 0x00 e.sc=0; // Note: The 3 spacer bits in the C register guarantee // that the new buffer byte can't be 0xFF here // (see page 160 in the P&M JPEG book). e.buffer=temp&0xFF; // new output byte, might overflow later } else if(temp==0xFF) { e.sc++; // stack 0xFF byte (which might overflow later) } else { // Output all stacked 0xFF bytes, they will not overflow any more if(e.buffer==0) e.zc++; else if(e.buffer>=0) { if(e.zc!=0) { do emit_byte(cinfo, 0x00); while((--e.zc)!=0); } emit_byte(cinfo, e.buffer); } if(e.sc!=0) { if(e.zc!=0) { do emit_byte(cinfo, 0x00); while((--e.zc)!=0); } do { emit_byte(cinfo, 0xFF); emit_byte(cinfo, 0x00); } while((--e.sc)!=0); } e.buffer=temp&0xFF; // new output byte (can still overflow) } e.c&=0x7FFFF; e.ct+=8; } } while(e.a<0x8000); }
// 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 }
// MCU encoding for DC initial scan (either spectral selection, // or first pass of successive approximation). static bool encode_mcu_DC_first_arith(jpeg_compress cinfo, short[][] MCU_data) { arith_entropy_encoder entropy=(arith_entropy_encoder)cinfo.coef; // Emit restart marker if needed if(cinfo.restart_interval!=0) { if(entropy.restarts_to_go==0) { emit_restart_arith(cinfo, entropy.next_restart_num); entropy.restarts_to_go=cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num&=7; } entropy.restarts_to_go--; } // 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]; int tbl=cinfo.cur_comp_info[ci].dc_tbl_no; // Compute the DC value after the required point transform by Al. // This is simply an arithmetic right shift. int m=(int)(block[0])>>cinfo.Al; // Sections F.1.4.1 & F.1.4.4.1: Encoding of DC coefficients // Table F.4: Point to statistics bin S0 for DC coefficient coding byte[] st=entropy.dc_stats[tbl]; int st_ind=entropy.dc_context[ci]; // Figure F.4: Encode_DC_DIFF int v=m-entropy.last_dc_val[ci]; if(v==0) { arith_encode(cinfo, ref st[st_ind], 0); entropy.dc_context[ci]=0; // zero diff category } else { entropy.last_dc_val[ci]=m; arith_encode(cinfo, ref st[st_ind], 1); // Figure F.6: Encoding nonzero value v // Figure F.7: Encoding the sign of v if(v>0) { arith_encode(cinfo, ref st[st_ind+1], 0); // Table F.4: SS = S0 + 1 st_ind+=2; // Table F.4: SP = S0 + 2 entropy.dc_context[ci]=4; // small positive diff category } else { v=-v; arith_encode(cinfo, ref st[st_ind+1], 1); // Table F.4: SS = S0 + 1 st_ind+=3; // Table F.4: SN = S0 + 3 entropy.dc_context[ci]=8; // small negative diff category } // Figure F.8: Encoding the magnitude category of v m=0; v--; if(v!=0) { arith_encode(cinfo, ref st[st_ind], 1); m=1; int v2=v; st=entropy.dc_stats[tbl]; st_ind=20; // Table F.4: X1 = 20 while((v2>>=1)!=0) { arith_encode(cinfo, ref st[st_ind], 1); m<<=1; st_ind+=1; } } arith_encode(cinfo, ref st[st_ind], 0); // Section F.1.4.4.1.2: Establish dc_context conditioning category if(m<(int)((1<<cinfo.arith_dc_L[tbl])>>1)) entropy.dc_context[ci]=0; // zero diff category else if(m>(int)((1<<cinfo.arith_dc_U[tbl])>>1)) entropy.dc_context[ci]+=8; // large diff category // Figure F.9: Encoding the magnitude bit pattern of v st_ind+=14; while((m>>=1)!=0) arith_encode(cinfo, ref st[st_ind], ((m&v)!=0)?1:0); } } return true; }
static bool need_optimization_pass_phuff(jpeg_compress cinfo) { return (cinfo.Ss!=0||cinfo.Ah==0); }
// MCU encoding for DC successive approximation refinement scan. static bool encode_mcu_DC_refine_arith(jpeg_compress cinfo, short[][] MCU_data) { arith_entropy_encoder entropy=(arith_entropy_encoder)cinfo.coef; // Emit restart marker if needed if(cinfo.restart_interval!=0) { if(entropy.restarts_to_go==0) { emit_restart_arith(cinfo, entropy.next_restart_num); entropy.restarts_to_go=cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num&=7; } entropy.restarts_to_go--; } int Al=cinfo.Al; // Encode the MCU data blocks for(int blkn=0; blkn<cinfo.block_in_MCU; blkn++) { byte st=0; // use fixed probability estimation // We simply emit the Al'th bit of the DC coefficient value. arith_encode(cinfo, ref st, (MCU_data[blkn][0]>>Al)&1); } return true; }
// 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; } } }
// Encode and output one MCU's worth of arithmetic-compressed coefficients. static bool encode_mcu_arith(jpeg_compress cinfo, short[][] MCU_data) { arith_entropy_encoder entropy=(arith_entropy_encoder)cinfo.coef; // Emit restart marker if needed if(cinfo.restart_interval!=0) { if(entropy.restarts_to_go==0) { emit_restart_arith(cinfo, entropy.next_restart_num); entropy.restarts_to_go=cinfo.restart_interval; entropy.next_restart_num++; entropy.next_restart_num&=7; } entropy.restarts_to_go--; } // 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]; // Sections F.1.4.1 & F.1.4.4.1: Encoding of DC coefficients int tbl=compptr.dc_tbl_no; // Table F.4: Point to statistics bin S0 for DC coefficient coding byte[] st=entropy.dc_stats[tbl]; int st_ind=entropy.dc_context[ci]; // Figure F.4: Encode_DC_DIFF int v=block[0]-entropy.last_dc_val[ci]; if(v==0) { arith_encode(cinfo, ref st[st_ind], 0); entropy.dc_context[ci]=0; // zero diff category } else { entropy.last_dc_val[ci]=block[0]; arith_encode(cinfo, ref st[st_ind], 1); // Figure F.6: Encoding nonzero value v // Figure F.7: Encoding the sign of v if(v>0) { arith_encode(cinfo, ref st[st_ind+1], 0); // Table F.4: SS = S0 + 1 st_ind+=2; // Table F.4: SP = S0 + 2 entropy.dc_context[ci]=4; // small positive diff category } else { v=-v; arith_encode(cinfo, ref st[st_ind+1], 1); //Table F.4: SS = S0 + 1 st_ind+=3; // Table F.4: SN = S0 + 3 entropy.dc_context[ci]=8; // small negative diff category } // Figure F.8: Encoding the magnitude category of v int m=0; v--; if(v!=0) { arith_encode(cinfo, ref st[st_ind], 1); m=1; int v2=v; st=entropy.dc_stats[tbl]; st_ind=20; // Table F.4: X1 = 20 while((v2>>=1)!=0) { arith_encode(cinfo, ref st[st_ind], 1); m<<=1; st_ind+=1; } } arith_encode(cinfo, ref st[st_ind], 0); // Section F.1.4.4.1.2: Establish dc_context conditioning category if(m<(int)((1<<cinfo.arith_dc_L[tbl])>>1)) entropy.dc_context[ci]=0; // zero diff category else if(m>(int)((1<<cinfo.arith_dc_U[tbl])>>1)) entropy.dc_context[ci]+=8; // large diff category // Figure F.9: Encoding the magnitude bit pattern of v st_ind+=14; while((m>>=1)!=0) arith_encode(cinfo, ref st[st_ind], ((m&v)!=0)?1:0); } // Sections F.1.4.2 & F.1.4.4.2: Encoding of AC coefficients tbl=compptr.ac_tbl_no; int k, ke; // Establish EOB (end-of-block) index for(ke=DCTSIZE2; ke>1; ke--) { if(block[jpeg_natural_order[ke-1]]!=0) break; } // Figure F.5: Encode_AC_Coefficients for(k=1; k<ke; k++) { st=entropy.ac_stats[tbl]; st_ind=3*(k-1); arith_encode(cinfo, ref st[st_ind], 0); // EOB decision while((v=block[jpeg_natural_order[k]])==0) { arith_encode(cinfo, ref st[st_ind+1], 0); st_ind+=3; k++; } arith_encode(cinfo, ref st[st_ind+1], 1); // Figure F.6: Encoding nonzero value v // Figure F.7: Encoding the sign of v entropy.ac_stats[tbl][245]=0; if(v>0) { arith_encode(cinfo, ref entropy.ac_stats[tbl][245], 0); } else { v=-v; arith_encode(cinfo, ref entropy.ac_stats[tbl][245], 1); } st_ind+=2; // Figure F.8: Encoding the magnitude category of v int m=0; v--; if(v!=0) { arith_encode(cinfo, ref st[st_ind], 1); m=1; int v2=v; v2>>=1; if(v2!=0) { arith_encode(cinfo, ref st[st_ind], 1); m<<=1; st=entropy.ac_stats[tbl]; st_ind=(k<=cinfo.arith_ac_K[tbl]?189:217); while((v2>>=1)!=0) { arith_encode(cinfo, ref st[st_ind], 1); m<<=1; st_ind+=1; } } } arith_encode(cinfo, ref st[st_ind], 0); // Figure F.9: Encoding the magnitude bit pattern of v st_ind+=14; while((m>>=1)!=0) arith_encode(cinfo, ref st[st_ind], ((m&v)!=0)?1:0); } // Encode EOB decision only if k < DCTSIZE2 if(k<DCTSIZE2) { st=entropy.ac_stats[tbl]; st_ind=3*(k-1); arith_encode(cinfo, ref st[st_ind], 1); } } return true; }
// Initialize for an arithmetic-compressed scan. static void start_pass_c_arith(jpeg_compress cinfo, bool gather_statistics) { arith_entropy_encoder entropy=(arith_entropy_encoder)cinfo.coef; if(gather_statistics) { // Make sure to avoid that in the master control logic! // We are fully adaptive here and need no extra // statistics gathering pass! ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); } // We assume jcmaster.cs already validated the progressive scan parameters. // Select execution routines if(cinfo.process==J_CODEC_PROCESS.JPROC_PROGRESSIVE) { if(cinfo.Ah==0) { if(cinfo.Ss==0) entropy.entropy_encode_mcu=encode_mcu_DC_first_arith; else entropy.entropy_encode_mcu=encode_mcu_AC_first_arith; } else { if(cinfo.Ss==0) entropy.entropy_encode_mcu=encode_mcu_DC_refine_arith; else entropy.entropy_encode_mcu=encode_mcu_AC_refine_arith; } } else if(cinfo.process==J_CODEC_PROCESS.JPROC_SEQUENTIAL) entropy.entropy_encode_mcu=encode_mcu_arith; else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOTIMPL); for(int ci=0; ci<cinfo.comps_in_scan; ci++) { jpeg_component_info compptr=cinfo.cur_comp_info[ci]; // Allocate & initialize requested statistics areas if(cinfo.process!=J_CODEC_PROCESS.JPROC_PROGRESSIVE||(cinfo.Ss==0&&cinfo.Ah==0)) { int tbl=compptr.dc_tbl_no; if(tbl<0||tbl>=NUM_ARITH_TBLS) ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_NO_ARITH_TABLE, tbl); if(entropy.dc_stats[tbl]==null) { try { entropy.dc_stats[tbl]=new byte[DC_STAT_BINS]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } else { for(int i=0; i<DC_STAT_BINS; i++) entropy.dc_stats[tbl][i]=0; } // Initialize DC predictions to 0 entropy.last_dc_val[ci]=0; entropy.dc_context[ci]=0; } if(cinfo.process!=J_CODEC_PROCESS.JPROC_PROGRESSIVE||cinfo.Ss!=0) { int tbl=compptr.ac_tbl_no; if(tbl<0||tbl>=NUM_ARITH_TBLS) ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_NO_ARITH_TABLE, tbl); if(entropy.ac_stats[tbl]==null) { try { entropy.ac_stats[tbl]=new byte[AC_STAT_BINS]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } else { for(int i=0; i<AC_STAT_BINS; i++) entropy.ac_stats[tbl][i]=0; } #if CALCULATE_SPECTRAL_CONDITIONING if(cinfo.process==J_CODEC_PROCESS.JPROC_PROGRESSIVE) { // Section G.1.3.2: Set appropriate arithmetic conditioning value Kx cinfo.arith_ac_K[tbl]=(byte)(cinfo.Ss+((8+cinfo.Se-cinfo.Ss)>>4)); } #endif } } // Initialize arithmetic encoding variables entropy.c=0; entropy.a=0x10000; entropy.sc=0; entropy.zc=0; entropy.ct=11; entropy.buffer=-1; // empty // Initialize restart stuff entropy.restarts_to_go=cinfo.restart_interval; entropy.next_restart_num=0; }
// Module initialization routine for input colorspace conversion. static void jinit_color_converter(jpeg_compress cinfo) { my_color_converter cconvert = null; try { cinfo.cconvert = cconvert = new my_color_converter(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } // set start_pass to null method until we find out differently cconvert.start_pass = null_method; // Make sure input_components agrees with in_color_space switch (cinfo.in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if (cinfo.input_components != 1) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_YCbCr: if (cinfo.input_components != 3) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; case J_COLOR_SPACE.JCS_CMYK: case J_COLOR_SPACE.JCS_YCCK: if (cinfo.input_components != 4) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; default: // JCS_UNKNOWN can be anything if (cinfo.input_components < 1) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; } // Check num_components, set conversion method based on requested space switch (cinfo.jpeg_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if (cinfo.num_components != 1) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.in_color_space == J_COLOR_SPACE.JCS_GRAYSCALE) { cconvert.color_convert = grayscale_convert; } else if (cinfo.in_color_space == J_COLOR_SPACE.JCS_RGB) { cconvert.start_pass = rgb_ycc_start; cconvert.color_convert = rgb_gray_convert; } else if (cinfo.in_color_space == J_COLOR_SPACE.JCS_YCbCr) { cconvert.color_convert = grayscale_convert; } else { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; case J_COLOR_SPACE.JCS_RGB: if (cinfo.num_components != 3) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.in_color_space == J_COLOR_SPACE.JCS_RGB && RGB_PIXELSIZE == 3) { cconvert.color_convert = null_convert; } else { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; case J_COLOR_SPACE.JCS_YCbCr: if (cinfo.num_components != 3) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.in_color_space == J_COLOR_SPACE.JCS_RGB) { cconvert.start_pass = rgb_ycc_start; cconvert.color_convert = rgb_ycc_convert; } else if (cinfo.in_color_space == J_COLOR_SPACE.JCS_YCbCr) { cconvert.color_convert = null_convert; } else { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; case J_COLOR_SPACE.JCS_CMYK: if (cinfo.num_components != 4) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.in_color_space == J_COLOR_SPACE.JCS_CMYK) { cconvert.color_convert = null_convert; } else { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; case J_COLOR_SPACE.JCS_YCCK: if (cinfo.num_components != 4) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.in_color_space == J_COLOR_SPACE.JCS_CMYK) { cconvert.start_pass = rgb_ycc_start; cconvert.color_convert = cmyk_ycck_convert; } else if (cinfo.in_color_space == J_COLOR_SPACE.JCS_YCCK) { cconvert.color_convert = null_convert; } else { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; default: // allow null conversion of JCS_UNKNOWN if (cinfo.jpeg_color_space != cinfo.in_color_space || cinfo.num_components != cinfo.input_components) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } cconvert.color_convert = null_convert; break; } }
// 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); }
// Write datastream trailer. static void write_file_trailer(jpeg_compress cinfo) { emit_marker(cinfo, JPEG_MARKER.M_EOI); }
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; }
// Convert some rows of samples to the JPEG colorspace. // This version handles grayscale output with no conversion. // The source can be either plain grayscale or YCbCr (since Y == gray). static void grayscale_convert(jpeg_compress cinfo, byte[][] input_buf, uint in_row_index, byte[][][] output_buf, uint output_row, int num_rows) { uint num_cols=cinfo.image_width; int instride=cinfo.input_components; for(uint input_row=0; input_row<num_rows; input_row++) { byte[] inptr=input_buf[in_row_index+input_row]; byte[] outptr=output_buf[0][output_row]; output_row++; for(uint col=0, off=0; col<num_cols; col++, off+=(uint)instride) outptr[col]=inptr[off]; } }
static bool need_optimization_pass_phuff(jpeg_compress cinfo) { return(cinfo.Ss != 0 || cinfo.Ah == 0); }
// 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); }