/// <summary> /// 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 virtual arrays. We also generate suitable dummy blocks /// as needed at the right and lower edges. (The dummy blocks are constructed /// in the virtual 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() after we've loaded the current strip /// of the virtual arrays. /// /// NB: input_buf contains a plane for each component in image. All /// components are DCT'd and loaded into the virtual 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). /// </summary> private bool compressFirstPass(byte[][][] input_buf) { int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; /* Align the virtual buffer for this component. */ JBLOCK[][] buffer = m_whole_image[ci].Access(m_iMCU_row_num * componentInfo.V_samp_factor, componentInfo.V_samp_factor); /* Count non-dummy DCT block rows in this iMCU row. */ int block_rows; if (m_iMCU_row_num < last_iMCU_row) { block_rows = componentInfo.V_samp_factor; } else { /* NB: can't use last_row_height here, since may not be set! */ block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; if (block_rows == 0) { block_rows = componentInfo.V_samp_factor; } } int blocks_across = componentInfo.Width_in_blocks; int h_samp_factor = componentInfo.H_samp_factor; /* Count number of dummy blocks to be added at the right margin. */ int ndummy = blocks_across % h_samp_factor; if (ndummy > 0) { ndummy = h_samp_factor - ndummy; } jpeg_forward_dct.forward_DCT_ptr forward_DCT = m_cinfo.m_fdct.forward_DCT[ci]; /* 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++) { forward_DCT(componentInfo, input_buf[ci], buffer[block_row], block_row * componentInfo.DCT_v_scaled_size, 0, blocks_across); if (ndummy > 0) { /* Create dummy blocks at the right edge of the image. */ Array.Clear(buffer[block_row][blocks_across].data, 0, buffer[block_row][blocks_across].data.Length); short lastDC = buffer[block_row][blocks_across - 1][0]; for (int bi = 0; bi < ndummy; bi++) { buffer[block_row][blocks_across + bi][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 (m_iMCU_row_num == last_iMCU_row) { blocks_across += ndummy; /* include lower right corner */ int MCUs_across = blocks_across / h_samp_factor; for (int block_row = block_rows; block_row < componentInfo.V_samp_factor; block_row++) { for (int i = 0; i < blocks_across; i++) { Array.Clear(buffer[block_row][i].data, 0, buffer[block_row][i].data.Length); } int thisOffset = 0; int lastOffset = 0; for (int MCUindex = 0; MCUindex < MCUs_across; MCUindex++) { short lastDC = buffer[block_row - 1][lastOffset + h_samp_factor - 1][0]; for (int bi = 0; bi < h_samp_factor; bi++) { buffer[block_row][thisOffset + bi][0] = lastDC; } thisOffset += h_samp_factor; /* advance to next MCU in row */ lastOffset += 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(compressOutput()); }
/// <summary> /// 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. /// </summary> private bool compressDataImpl(byte[][][] input_buf) { int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; /* Loop to write as much as one whole iMCU row */ for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) { for (int MCU_col_num = m_mcu_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++) { /* 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 < m_cinfo.m_comps_in_scan; ci++) { jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; jpeg_forward_dct.forward_DCT_ptr forward_DCT = m_cinfo.m_fdct.forward_DCT[componentInfo.Component_index]; int blockcnt = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; int xpos = MCU_col_num * componentInfo.MCU_sample_width; int ypos = yoffset * componentInfo.DCT_v_scaled_size; for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) { if (m_iMCU_row_num < last_iMCU_row || yoffset + yindex < componentInfo.last_row_height) { forward_DCT(componentInfo, input_buf[componentInfo.Component_index], m_MCU_buffer[blkn], ypos, xpos, blockcnt); if (blockcnt < componentInfo.MCU_width) { /* Create some dummy blocks at the right edge of the image. */ for (int i = 0; i < (componentInfo.MCU_width - blockcnt); i++) { Array.Clear(m_MCU_buffer[blkn + blockcnt][i].data, 0, m_MCU_buffer[blkn + blockcnt][i].data.Length); } for (int bi = blockcnt; bi < componentInfo.MCU_width; bi++) { m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn + bi - 1][0][0]; } } } else { /* Create a row of dummy blocks at the bottom of the image. */ for (int i = 0; i < componentInfo.MCU_width; i++) { Array.Clear(m_MCU_buffer[blkn][i].data, 0, m_MCU_buffer[blkn][i].data.Length); } for (int bi = 0; bi < componentInfo.MCU_width; bi++) { m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn - 1][0][0]; } } blkn += componentInfo.MCU_width; ypos += componentInfo.DCT_v_scaled_size; } } /* 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 (!m_cinfo.m_entropy.encode_mcu(m_MCU_buffer)) { /* Suspension forced; update state counters and exit */ m_MCU_vert_offset = yoffset; m_mcu_ctr = MCU_col_num; return(false); } } /* Completed an MCU row, but perhaps not an iMCU row */ m_mcu_ctr = 0; } /* Completed the iMCU row, advance counters for next one */ m_iMCU_row_num++; start_iMCU_row(); return(true); }