// Process some data in subsequent passes of a multi-pass case. // We process the equivalent of one fully interleaved MCU row ("iMCU" row) // per call, ie, v_samp_factor block rows for each component in the scan. // The data is obtained from the arrays and fed to the entropy coder. // Returns true if the iMCU row is completed, false if suspended. // // NB: input_buf is ignored; it is likely to be a null pointer. static bool compress_output_coef(jpeg_compress cinfo, byte[][][] input_buf) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = (c_coef_controller)lossyc.coef_private; short[][][][] buffer = new short[MAX_COMPS_IN_SCAN][][][]; int[] buffer_ind = new int[MAX_COMPS_IN_SCAN]; // Align the buffers for the components used in this scan. // NB: during first pass, this is safe only because the buffers will // already be aligned properly, so jmemmgr.cs won't need to do any I/O. for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; buffer[ci] = coef.whole_image[compptr.component_index]; buffer_ind[ci] = (int)coef.iMCU_row_num * compptr.v_samp_factor; } // Loop to process one whole iMCU row for (int yoffset = coef.MCU_vert_offset; yoffset < coef.MCU_rows_per_iMCU_row; yoffset++) { for (uint MCU_col_num = coef.mcu_ctr; MCU_col_num < cinfo.MCUs_per_row; MCU_col_num++) { // Construct list of pointers to DCT blocks belonging to this MCU int blkn = 0; // index of current DCT block within MCU for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; uint start_col = MCU_col_num * (uint)compptr.MCU_width; for (int yindex = 0; yindex < compptr.MCU_height; yindex++) { short[][] buffer_ptr = buffer[ci][buffer_ind[ci] + yindex + yoffset]; uint buffer_ptr_ind = start_col; for (int xindex = 0; xindex < compptr.MCU_width; xindex++) { coef.MCU_buffer[blkn++] = buffer_ptr[buffer_ptr_ind++]; } } } // Try to write the MCU. if (!lossyc.entropy_encode_mcu(cinfo, coef.MCU_buffer)) { // Suspension forced; update state counters and exit coef.MCU_vert_offset = yoffset; coef.mcu_ctr = MCU_col_num; return(false); } } // Completed an MCU row, but perhaps not an iMCU row coef.mcu_ctr = 0; } // Completed the iMCU row, advance counters for next one coef.iMCU_row_num++; start_iMCU_row_c_coef(cinfo); return(true); }
// 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; }
static void jinit_c_coef_controller(jpeg_compress cinfo, bool need_full_buffer) { jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = null; try { coef = new c_coef_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } lossyc.coef_private = coef; lossyc.coef_start_pass = start_pass_coef; // Create the coefficient buffer. if (need_full_buffer) { #if FULL_COEF_BUFFER_SUPPORTED // Allocate a full-image array for each component, // padded to a multiple of samp_factor DCT blocks in each direction. for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; coef.whole_image[ci] = alloc_barray(cinfo, (uint)jround_up((int)compptr.width_in_blocks, compptr.h_samp_factor), (uint)jround_up((int)compptr.height_in_blocks, compptr.v_samp_factor)); } #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); #endif } else { // We only need a single-MCU buffer. try { for (int i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { coef.MCU_buffer[i] = new short[DCTSIZE2]; } } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } coef.whole_image[0] = null; // flag for no arrays } }
// 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; } }
// 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)); }
// 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); }
public static uint jpeg_write_image(jpeg_compress cinfo, byte[] image, bool swapChannels, bool alpha) { if (cinfo.input_components != 3 || cinfo.lossless || cinfo.in_color_space != J_COLOR_SPACE.JCS_RGB || cinfo.num_components != 3 || cinfo.jpeg_color_space != J_COLOR_SPACE.JCS_YCbCr || cinfo.data_precision != 8 || cinfo.DCT_size != 8 || cinfo.block_in_MCU != 3 || cinfo.arith_code || cinfo.max_h_samp_factor != 1 || cinfo.max_v_samp_factor != 1 || cinfo.next_scanline != 0 || cinfo.num_scans != 1) { throw new Exception(); } if (cinfo.global_state != STATE.CSCANNING) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_STATE, cinfo.global_state); } // Give master control module another chance if this is first call to // jpeg_write_scanlines. This lets output of the frame/scan headers be // delayed so that application can write COM, etc, markers between // jpeg_start_compress and jpeg_write_scanlines. if (cinfo.master.call_pass_startup) { cinfo.master.pass_startup(cinfo); } jpeg_lossy_c_codec lossyc = (jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef = (c_coef_controller)lossyc.coef_private; fdct_controller fdct = (fdct_controller)lossyc.fdct_private; double[] workspaceY = new double[DCTSIZE2]; double[] workspaceCr = new double[DCTSIZE2]; double[] workspaceCb = new double[DCTSIZE2]; short[][] coefs = new short[][] { new short[DCTSIZE2], new short[DCTSIZE2], new short[DCTSIZE2] }; short[] coefY = coefs[0]; short[] coefCb = coefs[1]; short[] coefCr = coefs[2]; double[] divisorY = fdct.float_divisors[0]; double[] divisorC = fdct.float_divisors[1]; int bpp = alpha?4:3; for (int y = 0; y < (cinfo.image_height + DCTSIZE - 1) / DCTSIZE; y++) { int yFormImage = Math.Min((int)cinfo.image_height - y * DCTSIZE, DCTSIZE); for (int x = 0; x < (cinfo.image_width + DCTSIZE - 1) / DCTSIZE; x++) { int xFormImage = Math.Min((int)cinfo.image_width - x * DCTSIZE, DCTSIZE); int workspacepos = 0; for (int j = 0; j < yFormImage; j++) { int imagepos = ((y * DCTSIZE + j) * (int)cinfo.image_width + x * DCTSIZE) * bpp; for (int i = 0; i < xFormImage; i++, workspacepos++) { byte r = image[imagepos++]; byte g = image[imagepos++]; byte b = image[imagepos++]; if (alpha) { imagepos++; } if (!swapChannels) { workspaceY[workspacepos] = 0.299 * r + 0.587 * g + 0.114 * b - CENTERJSAMPLE; workspaceCb[workspacepos] = -0.168736 * r - 0.331264 * g + 0.5 * b; workspaceCr[workspacepos] = 0.5 * r - 0.418688 * g - 0.081312 * b; } else { workspaceY[workspacepos] = 0.299 * b + 0.587 * g + 0.114 * r - CENTERJSAMPLE; workspaceCb[workspacepos] = -0.168736 * b - 0.331264 * g + 0.5 * r; workspaceCr[workspacepos] = 0.5 * b - 0.418688 * g - 0.081312 * r; } } int lastworkspacepos = workspacepos - 1; for (int i = xFormImage; i < DCTSIZE; i++, workspacepos++) { workspaceY[workspacepos] = workspaceY[lastworkspacepos]; workspaceCb[workspacepos] = workspaceCb[lastworkspacepos]; workspaceCr[workspacepos] = workspaceCr[lastworkspacepos]; } } int lastworkspacelinepos = (yFormImage - 1) * DCTSIZE; for (int j = yFormImage; j < DCTSIZE; j++) { int lastworkspacepos = lastworkspacelinepos; for (int i = 0; i < DCTSIZE; i++, workspacepos++, lastworkspacepos++) { workspaceY[workspacepos] = workspaceY[lastworkspacepos]; workspaceCb[workspacepos] = workspaceCb[lastworkspacepos]; workspaceCr[workspacepos] = workspaceCr[lastworkspacepos]; } } // ein block (3 componenten) jpeg_fdct_float(workspaceY); jpeg_fdct_float(workspaceCb); jpeg_fdct_float(workspaceCr); for (int i = 0; i < DCTSIZE2; i++) { // Apply the quantization and scaling factor double tempY = workspaceY[i] * divisorY[i]; double tempCb = workspaceCb[i] * divisorC[i]; double tempCr = workspaceCr[i] * divisorC[i]; // Round to nearest integer. // Since C does not specify the direction of rounding for negative // quotients, we have to force the dividend positive for portability. // The maximum coefficient size is +-16K (for 12-bit data), so this // code should work for either 16-bit or 32-bit ints. coefY[i] = (short)((int)(tempY + 16384.5) - 16384); coefCb[i] = (short)((int)(tempCb + 16384.5) - 16384); coefCr[i] = (short)((int)(tempCr + 16384.5) - 16384); } lossyc.entropy_encode_mcu(cinfo, coefs); } } cinfo.next_scanline = cinfo.image_height; return(cinfo.image_height); }
static void jinit_c_coef_controller(jpeg_compress cinfo, bool need_full_buffer) { jpeg_lossy_c_codec lossyc=(jpeg_lossy_c_codec)cinfo.coef; c_coef_controller coef=null; try { coef=new c_coef_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } lossyc.coef_private=coef; lossyc.coef_start_pass=start_pass_coef; // Create the coefficient buffer. if(need_full_buffer) { #if FULL_COEF_BUFFER_SUPPORTED // Allocate a full-image array for each component, // padded to a multiple of samp_factor DCT blocks in each direction. for(int ci=0; ci<cinfo.num_components; ci++) { jpeg_component_info compptr=cinfo.comp_info[ci]; coef.whole_image[ci]=alloc_barray(cinfo, (uint)jround_up((int)compptr.width_in_blocks, compptr.h_samp_factor), (uint)jround_up((int)compptr.height_in_blocks, compptr.v_samp_factor)); } #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); #endif } else { // We only need a single-MCU buffer. try { for(int i=0; i<C_MAX_BLOCKS_IN_MCU; i++) coef.MCU_buffer[i]=new short[DCTSIZE2]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } coef.whole_image[0]=null; // flag for no arrays } }