/// <summary> /// This version handles any integral sampling ratios. /// This is not used for typical JPEG files, so it need not be fast. /// Nor, for that matter, is it particularly accurate: the algorithm is /// simple replication of the input pixel onto the corresponding output /// pixels. The hi-falutin sampling literature refers to this as a /// "box filter". A box filter tends to introduce visible artifacts, /// so if you are actually going to use 3:1 or 4:1 sampling ratios /// you would be well advised to improve this code. /// </summary> private void int_upsample(ref ComponentBuffer input_data) { ComponentBuffer output_data = m_color_buf[m_currentComponent]; int h_expand = m_h_expand[m_currentComponent]; int v_expand = m_v_expand[m_currentComponent]; int inrow = 0; int outrow = 0; while (outrow < m_cinfo.m_max_v_samp_factor) { /* Generate one output row with proper horizontal expansion */ int row = m_upsampleRowOffset + inrow; for (int col = 0; col < m_cinfo.m_output_width; col++) { byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ int outIndex = 0; for (int h = h_expand; h > 0; h--) { output_data[outrow][outIndex] = invalue; outIndex++; } } /* Generate any additional output rows by duplicating the first one */ if (v_expand > 1) { JpegUtils.jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, v_expand - 1, m_cinfo.m_output_width); } inrow++; outrow += v_expand; } }
public jpeg_d_coef_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) { m_cinfo = cinfo; /* Create the coefficient buffer. */ if (need_full_buffer) { /* Allocate a full-image virtual array for each component, */ /* padded to a multiple of samp_factor DCT blocks in each direction. */ /* Note we ask for a pre-zeroed array. */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( JpegUtils.jround_up(cinfo.Comp_info[ci].Width_in_blocks, cinfo.Comp_info[ci].H_samp_factor), JpegUtils.jround_up(cinfo.Comp_info[ci].height_in_blocks, cinfo.Comp_info[ci].V_samp_factor)); m_whole_image[ci].ErrorProcessor = cinfo; } m_useDummyConsumeData = false; m_decompressor = DecompressorType.Ordinary; m_coef_arrays = m_whole_image; /* link to virtual arrays */ } else { /* We only need a single-MCU buffer. */ for (int i = 0; i < JpegConstants.D_MAX_BLOCKS_IN_MCU; i++) { m_MCU_buffer[i] = new JBLOCK(); } m_useDummyConsumeData = true; m_decompressor = DecompressorType.OnePass; m_coef_arrays = null; /* flag for no virtual arrays */ } }
/**************** YCbCr -> RGB conversion: most common case **************/ /* * YCbCr is defined per CCIR 601-1, except that Cb and Cr are * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. * The conversion equations to be implemented are therefore * R = Y + 1.40200 * Cr * G = Y - 0.34414 * Cb - 0.71414 * Cr * B = Y + 1.77200 * Cb * where Cb and Cr represent the incoming values less CENTERJSAMPLE. * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) * * To avoid floating-point arithmetic, we represent the fractional constants * as integers scaled up by 2^16 (about 4 digits precision); we have to divide * the products by 2^16, with appropriate rounding, to get the correct answer. * Notice that Y, being an integral input, does not contribute any fraction * so it need not participate in the rounding. * * For even more speed, we avoid doing any multiplications in the inner loop * by precalculating the constants times Cb and Cr for all possible values. * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); * for 12-bit samples it is still acceptable. It's not very reasonable for * 16-bit samples, but if you want lossless storage you shouldn't be changing * colorspace anyway. * The Cr=>R and Cb=>B values can be rounded to integers in advance; the * values for the G calculation are left scaled up, since we must add them * together before rounding. */ /// <summary> /// Initialize tables for YCC->RGB colorspace conversion. /// </summary> private void build_ycc_rgb_table() { m_Cr_r_tab = new int[JpegConstants.MAXJSAMPLE + 1]; m_Cb_b_tab = new int[JpegConstants.MAXJSAMPLE + 1]; m_Cr_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; m_Cb_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; for (int i = 0, x = -JpegConstants.CENTERJSAMPLE; i <= JpegConstants.MAXJSAMPLE; i++, x++) { /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ /* Cr=>R value is nearest int to 1.40200 * x */ m_Cr_r_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); /* Cb=>B value is nearest int to 1.77200 * x */ m_Cb_b_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); /* Cr=>G value is scaled-up -0.71414 * x */ m_Cr_g_tab[i] = (-FIX(0.71414)) * x; /* Cb=>G value is scaled-up -0.34414 * x */ /* We also add in ONE_HALF so that need not do it in inner loop */ m_Cb_g_tab[i] = (-FIX(0.34414)) * x + ONE_HALF; } }
private int m_next_row; /* index of next row to fill/empty in strip */ /// <summary> /// Initialize postprocessing controller. /// </summary> public jpeg_d_post_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) { m_cinfo = cinfo; /* Create the quantization buffer, if needed */ if (cinfo.m_quantize_colors) { /* The buffer strip height is max_v_samp_factor, which is typically * an efficient number of rows for upsampling to return. * (In the presence of output rescaling, we might want to be smarter?) */ m_strip_height = cinfo.m_max_v_samp_factor; if (need_full_buffer) { /* Two-pass color quantization: need full-image storage. */ /* We round up the number of rows to a multiple of the strip height. */ m_whole_image = jpeg_common_struct.CreateSamplesArray( cinfo.m_output_width * cinfo.m_out_color_components, JpegUtils.jround_up(cinfo.m_output_height, m_strip_height)); m_whole_image.ErrorProcessor = cinfo; } else { /* One-pass color quantization: just make a strip buffer. */ m_buffer = jpeg_common_struct.AllocJpegSamples( cinfo.m_output_width * cinfo.m_out_color_components, m_strip_height); } } }
/// <summary> /// Expand an image vertically from height input_rows to height output_rows, /// by duplicating the bottom row. /// </summary> private static void expand_bottom_edge(byte[][] image_data, int rowsOffset, int num_cols, int input_rows, int output_rows) { for (int row = input_rows; row < output_rows; row++) { JpegUtils.jcopy_sample_rows(image_data, rowsOffset + input_rows - 1, image_data, row, 1, num_cols); } }
/// <summary> /// Fast processing for the common case of 2:1 horizontal and 2:1 vertical. /// It's still a box filter. /// </summary> private void h2v2_upsample(ref ComponentBuffer input_data) { ComponentBuffer output_data = m_color_buf[m_currentComponent]; int inrow = 0; int outrow = 0; while (outrow < m_cinfo.m_max_v_samp_factor) { int row = m_upsampleRowOffset + inrow; int outIndex = 0; for (int col = 0; outIndex < m_cinfo.m_output_width; col++) { byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ output_data[outrow][outIndex] = invalue; outIndex++; output_data[outrow][outIndex] = invalue; outIndex++; } JpegUtils.jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, 1, m_cinfo.m_output_width); inrow++; outrow += 2; } }
/// <summary> /// Multiply a DCTELEM variable by an int constant, and immediately /// descale to yield a DCTELEM result. /// </summary> private static int FAST_INTEGER_MULTIPLY(int var, int c) { #if !USE_ACCURATE_ROUNDING return(JpegUtils.RIGHT_SHIFT((var) * (c), FAST_INTEGER_CONST_BITS)); #else return(JpegUtils.DESCALE((var) * (c), FAST_INTEGER_CONST_BITS)); #endif }
/// <summary> /// Downsample pixel values of a single component. /// This version handles the special case of a full-size component, /// without smoothing. /// </summary> private void fullsize_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) { /* Copy the data */ JpegUtils.jcopy_sample_rows(input_data, startInputRow, output_data, startOutRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width); /* Edge-expand */ expand_right_edge(output_data, startOutRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE); }
/// <summary> /// Control routine to do upsampling (and color conversion). /// The control routine just handles the row buffering considerations. /// 2:1 vertical sampling case: may need a spare row. /// </summary> private void merged_2v_upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) { int num_rows; /* number of rows returned to caller */ if (m_spare_full) { /* If we have a spare row saved from a previous cycle, just return it. */ byte[][] temp = new byte[1][]; temp[0] = m_spare_row; JpegUtils.jcopy_sample_rows(temp, 0, output_buf, out_row_ctr, 1, m_out_row_width); num_rows = 1; m_spare_full = false; } else { /* Figure number of rows to return to caller. */ num_rows = 2; /* Not more than the distance to the end of the image. */ if (num_rows > m_rows_to_go) { num_rows = m_rows_to_go; } /* And not more than what the client can accept: */ out_rows_avail -= out_row_ctr; if (num_rows > out_rows_avail) { num_rows = out_rows_avail; } /* Create output pointer array for upsampler. */ byte[][] work_ptrs = new byte[2][]; work_ptrs[0] = output_buf[out_row_ctr]; if (num_rows > 1) { work_ptrs[1] = output_buf[out_row_ctr + 1]; } else { work_ptrs[1] = m_spare_row; m_spare_full = true; } /* Now do the upsampling. */ h2v2_merged_upsample(input_buf, in_row_group_ctr, work_ptrs); } /* Adjust counts */ out_row_ctr += num_rows; m_rows_to_go -= num_rows; /* When the buffer is emptied, declare this input row group consumed */ if (!m_spare_full) { in_row_group_ctr++; } }
/* * These are the routines invoked by the control routines to do * the actual upsampling/conversion. One row group is processed per call. * * Note: since we may be writing directly into application-supplied buffers, * we have to be honest about the output width; we can't assume the buffer * has been rounded up to an even width. */ /// <summary> /// Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. /// </summary> private void h2v1_merged_upsample(ComponentBuffer[] input_buf, int in_row_group_ctr, byte[][] output_buf, int outRow) { int inputIndex0 = 0; int inputIndex1 = 0; int inputIndex2 = 0; int outputIndex = 0; byte[] limit = m_cinfo.m_sample_range_limit; int limitOffset = m_cinfo.m_sampleRangeLimitOffset; /* Loop for each pair of output pixels */ for (int col = m_cinfo.m_output_width >> 1; col > 0; col--) { /* Do the chroma part of the calculation */ int cb = input_buf[1][in_row_group_ctr][inputIndex1]; inputIndex1++; int cr = input_buf[2][in_row_group_ctr][inputIndex2]; inputIndex2++; int cred = m_Cr_r_tab[cr]; int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); int cblue = m_Cb_b_tab[cb]; /* Fetch 2 Y values and emit 2 pixels */ int y = input_buf[0][in_row_group_ctr][inputIndex0]; inputIndex0++; output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; outputIndex += JpegConstants.RGB_PIXELSIZE; y = input_buf[0][in_row_group_ctr][inputIndex0]; inputIndex0++; output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; outputIndex += JpegConstants.RGB_PIXELSIZE; } /* If image width is odd, do the last output column separately */ if ((m_cinfo.m_output_width & 1) != 0) { int cb = input_buf[1][in_row_group_ctr][inputIndex1]; int cr = input_buf[2][in_row_group_ctr][inputIndex2]; int cred = m_Cr_r_tab[cr]; int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); int cblue = m_Cb_b_tab[cb]; int y = input_buf[0][in_row_group_ctr][inputIndex0]; output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; } }
public my_c_coef_controller(jpeg_compress_struct cinfo, bool need_full_buffer) { m_cinfo = cinfo; /* Create the coefficient buffer. */ if (need_full_buffer) { /* Allocate a full-image virtual array for each component, */ /* padded to a multiple of samp_factor DCT blocks in each direction. */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( JpegUtils.jround_up(cinfo.Component_info[ci].Width_in_blocks, cinfo.Component_info[ci].H_samp_factor), JpegUtils.jround_up(cinfo.Component_info[ci].height_in_blocks, cinfo.Component_info[ci].V_samp_factor)); m_whole_image[ci].ErrorProcessor = cinfo; } } else { /* We only need a single-MCU buffer. */ JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) { buffer[i] = new JBLOCK(); } for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) { m_MCU_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) { m_MCU_buffer[i][j - i] = buffer[j]; } } /* flag for no virtual arrays */ m_whole_image[0] = null; } }
/// <summary> /// Process some data in the context case. /// </summary> private void pre_process_context(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) { while (out_row_group_ctr < out_row_groups_avail) { if (in_row_ctr < in_rows_avail) { /* Do color conversion to fill the conversion buffer. */ int inrows = in_rows_avail - in_row_ctr; int numrows = m_next_buf_stop - m_next_buf_row; numrows = Math.Min(numrows, inrows); m_cinfo.m_cconvert.color_convert(input_buf, in_row_ctr, m_color_buf, m_colorBufRowsOffset + m_next_buf_row, numrows); /* Pad at top of image, if first time through */ if (m_rows_to_go == m_cinfo.m_image_height) { for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { for (int row = 1; row <= m_cinfo.m_max_v_samp_factor; row++) { JpegUtils.jcopy_sample_rows(m_color_buf[ci], m_colorBufRowsOffset, m_color_buf[ci], m_colorBufRowsOffset - row, 1, m_cinfo.m_image_width); } } } in_row_ctr += numrows; m_next_buf_row += numrows; m_rows_to_go -= numrows; } else { /* Return for more data, unless we are at the bottom of the image. */ if (m_rows_to_go != 0) { break; } /* When at bottom of image, pad to fill the conversion buffer. */ if (m_next_buf_row < m_next_buf_stop) { for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { expand_bottom_edge(m_color_buf[ci], m_colorBufRowsOffset, m_cinfo.m_image_width, m_next_buf_row, m_next_buf_stop); } m_next_buf_row = m_next_buf_stop; } } /* If we've gotten enough data, downsample a row group. */ if (m_next_buf_row == m_next_buf_stop) { m_cinfo.m_downsample.downsample(m_color_buf, m_colorBufRowsOffset + m_this_row_group, output_buf, out_row_group_ctr); out_row_group_ctr++; /* Advance pointers with wraparound as necessary. */ m_this_row_group += m_cinfo.m_max_v_samp_factor; int buf_height = m_cinfo.m_max_v_samp_factor * 3; if (m_this_row_group >= buf_height) { m_this_row_group = 0; } if (m_next_buf_row >= buf_height) { m_next_buf_row = 0; } m_next_buf_stop = m_next_buf_row + m_cinfo.m_max_v_samp_factor; } } }
/// <summary> /// Routines to calculate various quantities related to the size of the image. /// Called once, when first SOS marker is reached /// </summary> private void initial_setup() { /* Make sure image isn't bigger than I can handle */ if (m_cinfo.m_image_height > JpegConstants.JPEG_MAX_DIMENSION || m_cinfo.m_image_width > JpegConstants.JPEG_MAX_DIMENSION) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, (int)JpegConstants.JPEG_MAX_DIMENSION); } /* Only 8 to 12 bits data precision are supported for DCT based JPEG */ if (m_cinfo.m_data_precision < 8 || m_cinfo.m_data_precision > 12) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_PRECISION, m_cinfo.m_data_precision); } /* Check that number of components won't exceed internal array sizes */ if (m_cinfo.m_num_components > JpegConstants.MAX_COMPONENTS) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_num_components, JpegConstants.MAX_COMPONENTS); } /* Compute maximum sampling factors; check factor validity */ m_cinfo.m_max_h_samp_factor = 1; m_cinfo.m_max_v_samp_factor = 1; for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { if (m_cinfo.Comp_info[ci].H_samp_factor <= 0 || m_cinfo.Comp_info[ci].H_samp_factor > JpegConstants.MAX_SAMP_FACTOR || m_cinfo.Comp_info[ci].V_samp_factor <= 0 || m_cinfo.Comp_info[ci].V_samp_factor > JpegConstants.MAX_SAMP_FACTOR) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_SAMPLING); } m_cinfo.m_max_h_samp_factor = Math.Max(m_cinfo.m_max_h_samp_factor, m_cinfo.Comp_info[ci].H_samp_factor); m_cinfo.m_max_v_samp_factor = Math.Max(m_cinfo.m_max_v_samp_factor, m_cinfo.Comp_info[ci].V_samp_factor); } /* Derive block_size, natural_order, and lim_Se */ if (m_cinfo.is_baseline || (m_cinfo.m_progressive_mode && m_cinfo.m_comps_in_scan != 0)) { /* no pseudo SOS marker */ m_cinfo.block_size = JpegConstants.DCTSIZE; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; } else { switch (m_cinfo.m_Se) { case (1 * 1 - 1): m_cinfo.block_size = 1; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; /* not needed */ m_cinfo.lim_Se = m_cinfo.m_Se; break; case (2 * 2 - 1): m_cinfo.block_size = 2; m_cinfo.natural_order = JpegUtils.jpeg_natural_order2; m_cinfo.lim_Se = m_cinfo.m_Se; break; case (3 * 3 - 1): m_cinfo.block_size = 3; m_cinfo.natural_order = JpegUtils.jpeg_natural_order3; m_cinfo.lim_Se = m_cinfo.m_Se; break; case (4 * 4 - 1): m_cinfo.block_size = 4; m_cinfo.natural_order = JpegUtils.jpeg_natural_order4; m_cinfo.lim_Se = m_cinfo.m_Se; break; case (5 * 5 - 1): m_cinfo.block_size = 5; m_cinfo.natural_order = JpegUtils.jpeg_natural_order5; m_cinfo.lim_Se = m_cinfo.m_Se; break; case (6 * 6 - 1): m_cinfo.block_size = 6; m_cinfo.natural_order = JpegUtils.jpeg_natural_order6; m_cinfo.lim_Se = m_cinfo.m_Se; break; case (7 * 7 - 1): m_cinfo.block_size = 7; m_cinfo.natural_order = JpegUtils.jpeg_natural_order7; m_cinfo.lim_Se = m_cinfo.m_Se; break; case (8 * 8 - 1): m_cinfo.block_size = 8; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; break; case (9 * 9 - 1): m_cinfo.block_size = 9; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; break; case (10 * 10 - 1): m_cinfo.block_size = 10; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; break; case (11 * 11 - 1): m_cinfo.block_size = 11; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; break; case (12 * 12 - 1): m_cinfo.block_size = 12; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; break; case (13 * 13 - 1): m_cinfo.block_size = 13; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; break; case (14 * 14 - 1): m_cinfo.block_size = 14; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; break; case (15 * 15 - 1): m_cinfo.block_size = 15; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; break; case (16 * 16 - 1): m_cinfo.block_size = 16; m_cinfo.natural_order = JpegUtils.jpeg_natural_order; m_cinfo.lim_Se = JpegConstants.DCTSIZE2 - 1; break; default: m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROGRESSION, m_cinfo.m_Ss, m_cinfo.m_Se, m_cinfo.m_Ah, m_cinfo.m_Al); break; } } /* We initialize DCT_scaled_size and min_DCT_scaled_size to block_size. * In the full decompressor, * this will be overridden by jpeg_calc_output_dimensions in jdmaster.c; * but in the transcoder, * jpeg_calc_output_dimensions is not used, so we must do it here. */ m_cinfo.min_DCT_h_scaled_size = m_cinfo.block_size; m_cinfo.min_DCT_v_scaled_size = m_cinfo.block_size; /* Compute dimensions of components */ for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { jpeg_component_info compptr = m_cinfo.Comp_info[ci]; compptr.DCT_h_scaled_size = m_cinfo.block_size; compptr.DCT_v_scaled_size = m_cinfo.block_size; /* Size in DCT blocks */ compptr.Width_in_blocks = (int)JpegUtils.jdiv_round_up( m_cinfo.m_image_width * compptr.H_samp_factor, m_cinfo.m_max_h_samp_factor * m_cinfo.block_size); compptr.height_in_blocks = (int)JpegUtils.jdiv_round_up( m_cinfo.m_image_height * compptr.V_samp_factor, m_cinfo.m_max_v_samp_factor * m_cinfo.block_size); /* downsampled_width and downsampled_height will also be overridden by * jpeg_decomp_master if we are doing full decompression. The transcoder library * doesn't use these values, but the calling application might. */ /* Size in samples */ compptr.downsampled_width = (int)JpegUtils.jdiv_round_up( m_cinfo.m_image_width * compptr.H_samp_factor, m_cinfo.m_max_h_samp_factor); compptr.downsampled_height = (int)JpegUtils.jdiv_round_up( m_cinfo.m_image_height * compptr.V_samp_factor, m_cinfo.m_max_v_samp_factor); /* Mark component needed, until color conversion says otherwise */ compptr.component_needed = true; /* Mark no quantization table yet saved for component */ compptr.quant_table = null; } /* Compute number of fully interleaved MCU rows. */ m_cinfo.m_total_iMCU_rows = (int)JpegUtils.jdiv_round_up( m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * m_cinfo.block_size); /* Decide whether file contains multiple scans */ if (m_cinfo.m_comps_in_scan < m_cinfo.m_num_components || m_cinfo.m_progressive_mode) { m_cinfo.m_inputctl.m_has_multiple_scans = true; } else { m_cinfo.m_inputctl.m_has_multiple_scans = false; } }
/// <summary> /// Color conversion for grayscale: just copy the data. /// This also works for YCbCr -> grayscale conversion, in which /// we just copy the Y (luminance) component and ignore chrominance. /// </summary> private void grayscale_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) { JpegUtils.jcopy_sample_rows(input_buf[0], input_row + m_perComponentOffsets[0], output_buf, output_row, num_rows, m_cinfo.m_output_width); }
/**************** Cases other than YCbCr -> RGB **************/ /// <summary> /// Adobe-style YCCK->CMYK conversion. /// We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same /// conversion as above, while passing K (black) unchanged. /// We assume build_ycc_rgb_table has been called. /// </summary> private void ycck_cmyk_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) { int component0RowOffset = m_perComponentOffsets[0]; int component1RowOffset = m_perComponentOffsets[1]; int component2RowOffset = m_perComponentOffsets[2]; int component3RowOffset = m_perComponentOffsets[3]; byte[] limit = m_cinfo.m_sample_range_limit; int limitOffset = m_cinfo.m_sampleRangeLimitOffset; int num_cols = m_cinfo.m_output_width; for (int row = 0; row < num_rows; row++) { int columnOffset = 0; for (int col = 0; col < num_cols; col++) { int y = input_buf[0][input_row + component0RowOffset][col]; int cb = input_buf[1][input_row + component1RowOffset][col]; int cr = input_buf[2][input_row + component2RowOffset][col]; /* Range-limiting is essential due to noise introduced by DCT losses. */ output_buf[output_row + row][columnOffset] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + m_Cr_r_tab[cr])]; /* red */ output_buf[output_row + row][columnOffset + 1] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS))]; /* green */ output_buf[output_row + row][columnOffset + 2] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + m_Cb_b_tab[cb])]; /* blue */ /* K passes through unchanged */ /* don't need GETJSAMPLE here */ output_buf[output_row + row][columnOffset + 3] = input_buf[3][input_row + component3RowOffset][col]; columnOffset += 4; } input_row++; } }
private void ycc_rgb_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) { int component0RowOffset = m_perComponentOffsets[0]; int component1RowOffset = m_perComponentOffsets[1]; int component2RowOffset = m_perComponentOffsets[2]; byte[] limit = m_cinfo.m_sample_range_limit; int limitOffset = m_cinfo.m_sampleRangeLimitOffset; for (int row = 0; row < num_rows; row++) { int columnOffset = 0; for (int col = 0; col < m_cinfo.m_output_width; col++) { int y = input_buf[0][input_row + component0RowOffset][col]; int cb = input_buf[1][input_row + component1RowOffset][col]; int cr = input_buf[2][input_row + component2RowOffset][col]; /* Range-limiting is essential due to noise introduced by DCT losses. */ output_buf[output_row + row][columnOffset + JpegConstants.RGB_RED] = limit[limitOffset + y + m_Cr_r_tab[cr]]; output_buf[output_row + row][columnOffset + JpegConstants.RGB_GREEN] = limit[limitOffset + y + JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS)]; output_buf[output_row + row][columnOffset + JpegConstants.RGB_BLUE] = limit[limitOffset + y + m_Cb_b_tab[cb]]; columnOffset += JpegConstants.RGB_PIXELSIZE; } input_row++; } }
/// <summary> /// Map some rows of pixels to the output colormapped representation. /// General case, with Floyd-Steinberg dithering /// </summary> private void quantize_fs_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) { int nc = m_cinfo.m_out_color_components; int width = m_cinfo.m_output_width; byte[] limit = m_cinfo.m_sample_range_limit; int limitOffset = m_cinfo.m_sampleRangeLimitOffset; for (int row = 0; row < num_rows; row++) { /* Initialize output values to 0 so can process components separately */ Array.Clear(output_buf[out_row + row], 0, width); for (int ci = 0; ci < nc; ci++) { int inRow = in_row + row; int inIndex = ci; int outIndex = 0; int outRow = out_row + row; int errorIndex = 0; int dir; /* 1 for left-to-right, -1 for right-to-left */ if (m_on_odd_row) { /* work right to left in this row */ inIndex += (width - 1) * nc; /* so point to rightmost pixel */ outIndex += width - 1; dir = -1; errorIndex = width + 1; /* => entry after last column */ } else { /* work left to right in this row */ dir = 1; errorIndex = 0; /* => entry before first column */ } int dirnc = dir * nc; /* Preset error values: no error propagated to first pixel from left */ int cur = 0; /* and no error propagated to row below yet */ int belowerr = 0; int bpreverr = 0; for (int col = width; col > 0; col--) { /* cur holds the error propagated from the previous pixel on the * current line. Add the error propagated from the previous line * to form the complete error correction term for this pixel, and * round the error term (which is expressed * 16) to an integer. * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct * for either sign of the error value. * Note: errorIndex is for *previous* column's array entry. */ cur = JpegUtils.RIGHT_SHIFT(cur + m_fserrors[ci][errorIndex + dir] + 8, 4); /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. * The maximum error is +- MAXJSAMPLE; this sets the required size * of the range_limit array. */ cur += input_buf[inRow][inIndex]; cur = limit[limitOffset + cur]; /* Select output value, accumulate into output code for this pixel */ int pixcode = m_colorindex[ci][m_colorindexOffset[ci] + cur]; output_buf[outRow][outIndex] += (byte)pixcode; /* Compute actual representation error at this pixel */ /* Note: we can do this even though we don't have the final */ /* pixel code, because the colormap is orthogonal. */ cur -= m_sv_colormap[ci][pixcode]; /* Compute error fractions to be propagated to adjacent pixels. * Add these into the running sums, and simultaneously shift the * next-line error sums left by 1 column. */ int bnexterr = cur; int delta = cur * 2; cur += delta; /* form error * 3 */ m_fserrors[ci][errorIndex + 0] = (short)(bpreverr + cur); cur += delta; /* form error * 5 */ bpreverr = belowerr + cur; belowerr = bnexterr; cur += delta; /* form error * 7 */ /* At this point cur contains the 7/16 error value to be propagated * to the next pixel on the current line, and all the errors for the * next line have been shifted over. We are therefore ready to move on. */ inIndex += dirnc; /* advance input to next column */ outIndex += dir; /* advance output to next column */ errorIndex += dir; /* advance errorIndex to current column */ } /* Post-loop cleanup: we must unload the final error value into the * final fserrors[] entry. Note we need not unload belowerr because * it is for the dummy column before or after the actual array. */ m_fserrors[ci][errorIndex + 0] = (short)bpreverr; /* unload prev err into array */ } m_on_odd_row = (m_on_odd_row ? false : true); } }
/// <summary> /// Do computations that are needed before processing a JPEG scan /// cinfo.comps_in_scan and cinfo.cur_comp_info[] were set from SOS marker /// </summary> private void per_scan_setup() { if (m_cinfo.m_comps_in_scan == 1) { /* Noninterleaved (single-component) scan */ jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]]; /* Overall image size in MCUs */ m_cinfo.m_MCUs_per_row = componentInfo.Width_in_blocks; m_cinfo.m_MCU_rows_in_scan = componentInfo.height_in_blocks; /* For noninterleaved scan, always one block per MCU */ componentInfo.MCU_width = 1; componentInfo.MCU_height = 1; componentInfo.MCU_blocks = 1; componentInfo.MCU_sample_width = componentInfo.DCT_scaled_size; componentInfo.last_col_width = 1; /* For noninterleaved scans, it is convenient to define last_row_height * as the number of block rows present in the last iMCU row. */ int tmp = componentInfo.height_in_blocks % componentInfo.V_samp_factor; if (tmp == 0) { tmp = componentInfo.V_samp_factor; } componentInfo.last_row_height = tmp; m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]] = componentInfo; /* Prepare array describing MCU composition */ m_cinfo.m_blocks_in_MCU = 1; m_cinfo.m_MCU_membership[0] = 0; } else { /* Interleaved (multi-component) scan */ if (m_cinfo.m_comps_in_scan <= 0 || m_cinfo.m_comps_in_scan > JpegConstants.MAX_COMPS_IN_SCAN) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_comps_in_scan, JpegConstants.MAX_COMPS_IN_SCAN); } /* Overall image size in MCUs */ m_cinfo.m_MCUs_per_row = JpegUtils.jdiv_round_up( m_cinfo.m_image_width, m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); m_cinfo.m_MCU_rows_in_scan = JpegUtils.jdiv_round_up( m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); m_cinfo.m_blocks_in_MCU = 0; for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) { jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; /* Sampling factors give # of blocks of component in each MCU */ componentInfo.MCU_width = componentInfo.H_samp_factor; componentInfo.MCU_height = componentInfo.V_samp_factor; componentInfo.MCU_blocks = componentInfo.MCU_width * componentInfo.MCU_height; componentInfo.MCU_sample_width = componentInfo.MCU_width * componentInfo.DCT_scaled_size; /* Figure number of non-dummy blocks in last MCU column & row */ int tmp = componentInfo.Width_in_blocks % componentInfo.MCU_width; if (tmp == 0) { tmp = componentInfo.MCU_width; } componentInfo.last_col_width = tmp; tmp = componentInfo.height_in_blocks % componentInfo.MCU_height; if (tmp == 0) { tmp = componentInfo.MCU_height; } componentInfo.last_row_height = tmp; /* Prepare array describing MCU composition */ int mcublks = componentInfo.MCU_blocks; if (m_cinfo.m_blocks_in_MCU + mcublks > JpegConstants.D_MAX_BLOCKS_IN_MCU) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_MCU_SIZE); } m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]] = componentInfo; while (mcublks-- > 0) { m_cinfo.m_MCU_membership[m_cinfo.m_blocks_in_MCU++] = ci; } } } }
/* * Compute output image dimensions and related values. * NOTE: this is exported for possible use by application. * Hence it mustn't do anything that can't be done twice. */ /* Do computations that are needed before master selection phase. * This function is used for transcoding and full decompression. */ public void jpeg_core_output_dimensions() { /* Compute actual output image dimensions and DCT scaling choices. */ if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom) { /* Provide 1/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 1; m_cinfo.min_DCT_v_scaled_size = 1; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 2) { /* Provide 2/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 2L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 2L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 2; m_cinfo.min_DCT_v_scaled_size = 2; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 3) { /* Provide 3/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 3L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 3L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 3; m_cinfo.min_DCT_v_scaled_size = 3; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 4) { /* Provide 4/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 4L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 4L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 4; m_cinfo.min_DCT_v_scaled_size = 4; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 5) { /* Provide 5/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 5L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 5L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 5; m_cinfo.min_DCT_v_scaled_size = 5; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 6) { /* Provide 6/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 6L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 6L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 6; m_cinfo.min_DCT_v_scaled_size = 6; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 7) { /* Provide 7/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 7L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 7L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 7; m_cinfo.min_DCT_v_scaled_size = 7; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 8) { /* Provide 8/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 8L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 8L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 8; m_cinfo.min_DCT_v_scaled_size = 8; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 9) { /* Provide 9/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 9L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 9L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 9; m_cinfo.min_DCT_v_scaled_size = 9; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 10) { /* Provide 10/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 10L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 10L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 10; m_cinfo.min_DCT_v_scaled_size = 10; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 11) { /* Provide 11/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 11L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 11L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 11; m_cinfo.min_DCT_v_scaled_size = 11; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 12) { /* Provide 12/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 12L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 12L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 12; m_cinfo.min_DCT_v_scaled_size = 12; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 13) { /* Provide 13/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 13L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 13L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 13; m_cinfo.min_DCT_v_scaled_size = 13; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 14) { /* Provide 14/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 14L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 14L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 14; m_cinfo.min_DCT_v_scaled_size = 14; } else if (m_cinfo.m_scale_num * m_cinfo.block_size <= m_cinfo.m_scale_denom * 15) { /* Provide 15/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 15L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 15L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 15; m_cinfo.min_DCT_v_scaled_size = 15; } else { /* Provide 16/block_size scaling */ m_cinfo.m_output_width = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_width * 16L, (long)m_cinfo.block_size); m_cinfo.m_output_height = (int) JpegUtils.jdiv_round_up((long)m_cinfo.m_image_height * 16L, (long)m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 16; m_cinfo.min_DCT_v_scaled_size = 16; } /* Recompute dimensions of components */ for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { jpeg_component_info compptr = m_cinfo.Comp_info[ci]; compptr.DCT_h_scaled_size = m_cinfo.min_DCT_h_scaled_size; compptr.DCT_v_scaled_size = m_cinfo.min_DCT_v_scaled_size; } }
/// <summary> /// Perform the forward DCT on one block of samples. /// NOTE: this code only copes with 8x8 DCTs. /// /// A slow-but-accurate integer implementation of the /// forward DCT (Discrete Cosine Transform). /// /// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT /// on each column. Direct algorithms are also available, but they are /// much more complex and seem not to be any faster when reduced to code. /// /// This implementation is based on an algorithm described in /// C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT /// Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, /// Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. /// The primary algorithm described there uses 11 multiplies and 29 adds. /// We use their alternate method with 12 multiplies and 32 adds. /// The advantage of this method is that no data path contains more than one /// multiplication; this allows a very simple and accurate implementation in /// scaled fixed-point arithmetic, with a minimal number of shifts. /// /// The poop on this scaling stuff is as follows: /// /// Each 1-D DCT step produces outputs which are a factor of sqrt(N) /// larger than the true DCT outputs. The final outputs are therefore /// a factor of N larger than desired; since N=8 this can be cured by /// a simple right shift at the end of the algorithm. The advantage of /// this arrangement is that we save two multiplications per 1-D DCT, /// because the y0 and y4 outputs need not be divided by sqrt(N). /// In the IJG code, this factor of 8 is removed by the quantization /// step, NOT here. /// /// We have to do addition and subtraction of the integer inputs, which /// is no problem, and multiplication by fractional constants, which is /// a problem to do in integer arithmetic. We multiply all the constants /// by CONST_SCALE and convert them to integer constants (thus retaining /// SLOW_INTEGER_CONST_BITS bits of precision in the constants). After doing a /// multiplication we have to divide the product by CONST_SCALE, with proper /// rounding, to produce the correct output. This division can be done /// cheaply as a right shift of SLOW_INTEGER_CONST_BITS bits. We postpone shifting /// as long as possible so that partial sums can be added together with /// full fractional precision. /// /// The outputs of the first pass are scaled up by SLOW_INTEGER_PASS1_BITS bits so that /// they are represented to better-than-integral precision. These outputs /// require BITS_IN_JSAMPLE + SLOW_INTEGER_PASS1_BITS + 3 bits; this fits in a 16-bit word /// with the recommended scaling. (For 12-bit sample data, the intermediate /// array is int anyway.) /// /// To avoid overflow of the 32-bit intermediate results in pass 2, we must /// have BITS_IN_JSAMPLE + SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS <= 26. Error analysis /// shows that the values given below are the most effective. /// </summary> private static void jpeg_fdct_islow(int[] data) { /* Pass 1: process rows. */ /* Note results are scaled up by sqrt(8) compared to a true DCT; */ /* furthermore, we scale the results by 2**SLOW_INTEGER_PASS1_BITS. */ int dataIndex = 0; for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) { int tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; int tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; int tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; int tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; int tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; int tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; int tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; int tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; /* Even part per LL&M figure 1 --- note that published figure is faulty; * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". */ int tmp10 = tmp0 + tmp3; int tmp13 = tmp0 - tmp3; int tmp11 = tmp1 + tmp2; int tmp12 = tmp1 - tmp2; data[dataIndex + 0] = (tmp10 + tmp11) << SLOW_INTEGER_PASS1_BITS; data[dataIndex + 4] = (tmp10 - tmp11) << SLOW_INTEGER_PASS1_BITS; int z1 = (tmp12 + tmp13) * SLOW_INTEGER_FIX_0_541196100; data[dataIndex + 2] = JpegUtils.DESCALE(z1 + tmp13 * SLOW_INTEGER_FIX_0_765366865, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); data[dataIndex + 6] = JpegUtils.DESCALE(z1 + tmp12 * (-SLOW_INTEGER_FIX_1_847759065), SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); /* Odd part per figure 8 --- note paper omits factor of sqrt(2). * cK represents cos(K*pi/16). * i0..i3 in the paper are tmp4..tmp7 here. */ z1 = tmp4 + tmp7; int z2 = tmp5 + tmp6; int z3 = tmp4 + tmp6; int z4 = tmp5 + tmp7; int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ tmp4 = tmp4 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ tmp5 = tmp5 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ tmp6 = tmp6 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ tmp7 = tmp7 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ z3 += z5; z4 += z5; data[dataIndex + 7] = JpegUtils.DESCALE(tmp4 + z1 + z3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); data[dataIndex + 5] = JpegUtils.DESCALE(tmp5 + z2 + z4, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); data[dataIndex + 3] = JpegUtils.DESCALE(tmp6 + z2 + z3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); data[dataIndex + 1] = JpegUtils.DESCALE(tmp7 + z1 + z4, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ } /* Pass 2: process columns. * We remove the SLOW_INTEGER_PASS1_BITS scaling, but leave the results scaled up * by an overall factor of 8. */ dataIndex = 0; for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) { int tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; int tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; int tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; int tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; int tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; int tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; int tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; int tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; /* Even part per LL&M figure 1 --- note that published figure is faulty; * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". */ int tmp10 = tmp0 + tmp3; int tmp13 = tmp0 - tmp3; int tmp11 = tmp1 + tmp2; int tmp12 = tmp1 - tmp2; data[dataIndex + JpegConstants.DCTSIZE * 0] = JpegUtils.DESCALE(tmp10 + tmp11, SLOW_INTEGER_PASS1_BITS); data[dataIndex + JpegConstants.DCTSIZE * 4] = JpegUtils.DESCALE(tmp10 - tmp11, SLOW_INTEGER_PASS1_BITS); int z1 = (tmp12 + tmp13) * SLOW_INTEGER_FIX_0_541196100; data[dataIndex + JpegConstants.DCTSIZE * 2] = JpegUtils.DESCALE(z1 + tmp13 * SLOW_INTEGER_FIX_0_765366865, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); data[dataIndex + JpegConstants.DCTSIZE * 6] = JpegUtils.DESCALE(z1 + tmp12 * (-SLOW_INTEGER_FIX_1_847759065), SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); /* Odd part per figure 8 --- note paper omits factor of sqrt(2). * cK represents cos(K*pi/16). * i0..i3 in the paper are tmp4..tmp7 here. */ z1 = tmp4 + tmp7; int z2 = tmp5 + tmp6; int z3 = tmp4 + tmp6; int z4 = tmp5 + tmp7; int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ tmp4 = tmp4 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ tmp5 = tmp5 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ tmp6 = tmp6 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ tmp7 = tmp7 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ z3 += z5; z4 += z5; data[dataIndex + JpegConstants.DCTSIZE * 7] = JpegUtils.DESCALE(tmp4 + z1 + z3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); data[dataIndex + JpegConstants.DCTSIZE * 5] = JpegUtils.DESCALE(tmp5 + z2 + z4, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); data[dataIndex + JpegConstants.DCTSIZE * 3] = JpegUtils.DESCALE(tmp6 + z2 + z3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); data[dataIndex + JpegConstants.DCTSIZE * 1] = JpegUtils.DESCALE(tmp7 + z1 + z4, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); dataIndex++; /* advance pointer to next column */ } }
/// <summary> /// Initialize for a processing pass. /// Verify that all referenced Q-tables are present, and set up /// the divisor table for each one. /// In the current implementation, DCT of all components is done during /// the first pass, even if only some components will be output in the /// first scan. Hence all components should be examined here. /// </summary> public virtual void start_pass() { for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { int qtblno = m_cinfo.Component_info[ci].Quant_tbl_no; /* Make sure specified quantization table is present */ if (qtblno < 0 || qtblno >= JpegConstants.NUM_QUANT_TBLS || m_cinfo.m_quant_tbl_ptrs[qtblno] == null) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); } JQUANT_TBL qtbl = m_cinfo.m_quant_tbl_ptrs[qtblno]; /* Compute divisors for this quant table */ /* We may do this more than once for same table, but it's not a big deal */ int i = 0; switch (m_cinfo.m_dct_method) { case J_DCT_METHOD.JDCT_ISLOW: /* For LL&M IDCT method, divisors are equal to raw quantization * coefficients multiplied by 8 (to counteract scaling). */ if (m_divisors[qtblno] == null) { m_divisors[qtblno] = new int [JpegConstants.DCTSIZE2]; } for (i = 0; i < JpegConstants.DCTSIZE2; i++) { m_divisors[qtblno][i] = ((int)qtbl.quantval[i]) << 3; } break; case J_DCT_METHOD.JDCT_IFAST: if (m_divisors[qtblno] == null) { m_divisors[qtblno] = new int [JpegConstants.DCTSIZE2]; } for (i = 0; i < JpegConstants.DCTSIZE2; i++) { m_divisors[qtblno][i] = JpegUtils.DESCALE((int)qtbl.quantval[i] * (int)aanscales[i], CONST_BITS - 3); } break; case J_DCT_METHOD.JDCT_FLOAT: if (m_float_divisors[qtblno] == null) { m_float_divisors[qtblno] = new float [JpegConstants.DCTSIZE2]; } float[] fdtbl = m_float_divisors[qtblno]; i = 0; for (int row = 0; row < JpegConstants.DCTSIZE; row++) { for (int col = 0; col < JpegConstants.DCTSIZE; col++) { fdtbl[i] = (float)(1.0 / (((double)qtbl.quantval[i] * aanscalefactor[row] * aanscalefactor[col] * 8.0))); i++; } } break; default: m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); break; } } }
/// <summary> /// Do computations that are needed before processing a JPEG scan /// cinfo.comps_in_scan and cinfo.cur_comp_info[] are already set /// </summary> private void per_scan_setup() { if (m_cinfo.m_comps_in_scan == 1) { /* Noninterleaved (single-component) scan */ int compIndex = m_cinfo.m_cur_comp_info[0]; /* Overall image size in MCUs */ m_cinfo.m_MCUs_per_row = m_cinfo.Component_info[compIndex].Width_in_blocks; m_cinfo.m_MCU_rows_in_scan = m_cinfo.Component_info[compIndex].height_in_blocks; /* For noninterleaved scan, always one block per MCU */ m_cinfo.Component_info[compIndex].MCU_width = 1; m_cinfo.Component_info[compIndex].MCU_height = 1; m_cinfo.Component_info[compIndex].MCU_blocks = 1; m_cinfo.Component_info[compIndex].MCU_sample_width = JpegConstants.DCTSIZE; m_cinfo.Component_info[compIndex].last_col_width = 1; /* For noninterleaved scans, it is convenient to define last_row_height * as the number of block rows present in the last iMCU row. */ int tmp = m_cinfo.Component_info[compIndex].height_in_blocks % m_cinfo.Component_info[compIndex].V_samp_factor; if (tmp == 0) { tmp = m_cinfo.Component_info[compIndex].V_samp_factor; } m_cinfo.Component_info[compIndex].last_row_height = tmp; /* Prepare array describing MCU composition */ m_cinfo.m_blocks_in_MCU = 1; m_cinfo.m_MCU_membership[0] = 0; } else { /* Interleaved (multi-component) scan */ if (m_cinfo.m_comps_in_scan <= 0 || m_cinfo.m_comps_in_scan > JpegConstants.MAX_COMPS_IN_SCAN) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_comps_in_scan, JpegConstants.MAX_COMPS_IN_SCAN); } /* Overall image size in MCUs */ m_cinfo.m_MCUs_per_row = JpegUtils.jdiv_round_up( m_cinfo.m_image_width, m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); m_cinfo.m_MCU_rows_in_scan = JpegUtils.jdiv_round_up(m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); m_cinfo.m_blocks_in_MCU = 0; for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) { int compIndex = m_cinfo.m_cur_comp_info[ci]; /* Sampling factors give # of blocks of component in each MCU */ m_cinfo.Component_info[compIndex].MCU_width = m_cinfo.Component_info[compIndex].H_samp_factor; m_cinfo.Component_info[compIndex].MCU_height = m_cinfo.Component_info[compIndex].V_samp_factor; m_cinfo.Component_info[compIndex].MCU_blocks = m_cinfo.Component_info[compIndex].MCU_width * m_cinfo.Component_info[compIndex].MCU_height; m_cinfo.Component_info[compIndex].MCU_sample_width = m_cinfo.Component_info[compIndex].MCU_width * JpegConstants.DCTSIZE; /* Figure number of non-dummy blocks in last MCU column & row */ int tmp = m_cinfo.Component_info[compIndex].Width_in_blocks % m_cinfo.Component_info[compIndex].MCU_width; if (tmp == 0) { tmp = m_cinfo.Component_info[compIndex].MCU_width; } m_cinfo.Component_info[compIndex].last_col_width = tmp; tmp = m_cinfo.Component_info[compIndex].height_in_blocks % m_cinfo.Component_info[compIndex].MCU_height; if (tmp == 0) { tmp = m_cinfo.Component_info[compIndex].MCU_height; } m_cinfo.Component_info[compIndex].last_row_height = tmp; /* Prepare array describing MCU composition */ int mcublks = m_cinfo.Component_info[compIndex].MCU_blocks; if (m_cinfo.m_blocks_in_MCU + mcublks > JpegConstants.C_MAX_BLOCKS_IN_MCU) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_MCU_SIZE); } while (mcublks-- > 0) { m_cinfo.m_MCU_membership[m_cinfo.m_blocks_in_MCU++] = ci; } } } /* Convert restart specified in rows to actual MCU count. */ /* Note that count must fit in 16 bits, so we provide limiting. */ if (m_cinfo.m_restart_in_rows > 0) { int nominal = m_cinfo.m_restart_in_rows * m_cinfo.m_MCUs_per_row; m_cinfo.m_restart_interval = Math.Min(nominal, 65535); } }
/// <summary> /// Process some data in the context case. /// </summary> private void PreProcessContext(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) { while (out_row_group_ctr < out_row_groups_avail) { if (in_row_ctr < in_rows_avail) { /* Do color conversion to fill the conversion buffer. */ var inrows = in_rows_avail - in_row_ctr; var numrows = nextBufStop - nextBufRow; numrows = Math.Min(numrows, inrows); cinfo.m_cconvert.color_convert(input_buf, in_row_ctr, colorBuf, colorBufRowsOffset + nextBufRow, numrows); /* Pad at top of image, if first time through */ if (rowsToGo == cinfo.m_image_height) { for (var ci = 0; ci < cinfo.m_num_components; ci++) { for (var row = 1; row <= cinfo.m_max_v_samp_factor; row++) { JpegUtils.jcopy_sample_rows(colorBuf[ci], colorBufRowsOffset, colorBuf[ci], colorBufRowsOffset - row, 1, cinfo.m_image_width); } } } in_row_ctr += numrows; nextBufRow += numrows; rowsToGo -= numrows; } else { /* Return for more data, unless we are at the bottom of the image. */ if (rowsToGo != 0) { break; } /* When at bottom of image, pad to fill the conversion buffer. */ if (nextBufRow < nextBufStop) { for (var ci = 0; ci < cinfo.m_num_components; ci++) { ExpandBottomEdge(colorBuf[ci], colorBufRowsOffset, cinfo.m_image_width, nextBufRow, nextBufStop); } nextBufRow = nextBufStop; } } /* If we've gotten enough data, downsample a row group. */ if (nextBufRow == nextBufStop) { cinfo.m_downsample.Downsample(colorBuf, colorBufRowsOffset + thisRowGroup, output_buf, out_row_group_ctr); out_row_group_ctr++; /* Advance pointers with wraparound as necessary. */ thisRowGroup += cinfo.m_max_v_samp_factor; var buf_height = cinfo.m_max_v_samp_factor * 3; if (thisRowGroup >= buf_height) { thisRowGroup = 0; } if (nextBufRow >= buf_height) { nextBufRow = 0; } nextBufStop = nextBufRow + cinfo.m_max_v_samp_factor; } } }
/// <summary> /// Routines to calculate various quantities related to the size of the image. /// Called once, when first SOS marker is reached /// </summary> private void initial_setup() { /* Make sure image isn't bigger than I can handle */ if (m_cinfo.m_image_height > JpegConstants.JPEG_MAX_DIMENSION || m_cinfo.m_image_width > JpegConstants.JPEG_MAX_DIMENSION) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, (int)JpegConstants.JPEG_MAX_DIMENSION); } /* For now, precision must match compiled-in value... */ if (m_cinfo.m_data_precision != JpegConstants.BITS_IN_JSAMPLE) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_PRECISION, m_cinfo.m_data_precision); } /* Check that number of components won't exceed internal array sizes */ if (m_cinfo.m_num_components > JpegConstants.MAX_COMPONENTS) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_num_components, JpegConstants.MAX_COMPONENTS); } /* Compute maximum sampling factors; check factor validity */ m_cinfo.m_max_h_samp_factor = 1; m_cinfo.m_max_v_samp_factor = 1; for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { if (m_cinfo.Comp_info[ci].H_samp_factor <= 0 || m_cinfo.Comp_info[ci].H_samp_factor > JpegConstants.MAX_SAMP_FACTOR || m_cinfo.Comp_info[ci].V_samp_factor <= 0 || m_cinfo.Comp_info[ci].V_samp_factor > JpegConstants.MAX_SAMP_FACTOR) { m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_SAMPLING); } m_cinfo.m_max_h_samp_factor = Math.Max(m_cinfo.m_max_h_samp_factor, m_cinfo.Comp_info[ci].H_samp_factor); m_cinfo.m_max_v_samp_factor = Math.Max(m_cinfo.m_max_v_samp_factor, m_cinfo.Comp_info[ci].V_samp_factor); } /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. * In the full decompressor, this will be overridden jpeg_decomp_master; * but in the transcoder, jpeg_decomp_master is not used, so we must do it here. */ m_cinfo.m_min_DCT_scaled_size = JpegConstants.DCTSIZE; /* Compute dimensions of components */ for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { m_cinfo.Comp_info[ci].DCT_scaled_size = JpegConstants.DCTSIZE; /* Size in DCT blocks */ m_cinfo.Comp_info[ci].Width_in_blocks = JpegUtils.jdiv_round_up( m_cinfo.m_image_width * m_cinfo.Comp_info[ci].H_samp_factor, m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); m_cinfo.Comp_info[ci].height_in_blocks = JpegUtils.jdiv_round_up( m_cinfo.m_image_height * m_cinfo.Comp_info[ci].V_samp_factor, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); /* downsampled_width and downsampled_height will also be overridden by * jpeg_decomp_master if we are doing full decompression. The transcoder library * doesn't use these values, but the calling application might. */ /* Size in samples */ m_cinfo.Comp_info[ci].downsampled_width = JpegUtils.jdiv_round_up( m_cinfo.m_image_width * m_cinfo.Comp_info[ci].H_samp_factor, m_cinfo.m_max_h_samp_factor); m_cinfo.Comp_info[ci].downsampled_height = JpegUtils.jdiv_round_up( m_cinfo.m_image_height * m_cinfo.Comp_info[ci].V_samp_factor, m_cinfo.m_max_v_samp_factor); /* Mark component needed, until color conversion says otherwise */ m_cinfo.Comp_info[ci].component_needed = true; /* Mark no quantization table yet saved for component */ m_cinfo.Comp_info[ci].quant_table = null; } /* Compute number of fully interleaved MCU rows. */ m_cinfo.m_total_iMCU_rows = JpegUtils.jdiv_round_up( m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); /* Decide whether file contains multiple scans */ if (m_cinfo.m_comps_in_scan < m_cinfo.m_num_components || m_cinfo.m_progressive_mode) { m_cinfo.m_inputctl.m_has_multiple_scans = true; } else { m_cinfo.m_inputctl.m_has_multiple_scans = false; } }
public my_upsampler(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; m_need_context_rows = false; /* until we find out differently */ if (cinfo.m_CCIR601_sampling) /* this isn't supported */ { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); } /* jpeg_d_main_controller doesn't support context rows when min_DCT_scaled_size = 1, * so don't ask for it. */ bool do_fancy = cinfo.m_do_fancy_upsampling && cinfo.m_min_DCT_scaled_size > 1; /* Verify we can handle the sampling factors, select per-component methods, * and create storage as needed. */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { jpeg_component_info componentInfo = cinfo.Comp_info[ci]; /* Compute size of an "input group" after IDCT scaling. This many samples * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. */ int h_in_group = (componentInfo.H_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; int v_in_group = (componentInfo.V_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; int h_out_group = cinfo.m_max_h_samp_factor; int v_out_group = cinfo.m_max_v_samp_factor; /* save for use later */ m_rowgroup_height[ci] = v_in_group; bool need_buffer = true; if (!componentInfo.component_needed) { /* Don't bother to upsample an uninteresting component. */ m_upsampleMethods[ci] = ComponentUpsampler.noop_upsampler; need_buffer = false; } else if (h_in_group == h_out_group && v_in_group == v_out_group) { /* Fullsize components can be processed without any work. */ m_upsampleMethods[ci] = ComponentUpsampler.fullsize_upsampler; need_buffer = false; } else if (h_in_group * 2 == h_out_group && v_in_group == v_out_group) { /* Special cases for 2h1v upsampling */ if (do_fancy && componentInfo.downsampled_width > 2) { m_upsampleMethods[ci] = ComponentUpsampler.h2v1_fancy_upsampler; } else { m_upsampleMethods[ci] = ComponentUpsampler.h2v1_upsampler; } } else if (h_in_group * 2 == h_out_group && v_in_group * 2 == v_out_group) { /* Special cases for 2h2v upsampling */ if (do_fancy && componentInfo.downsampled_width > 2) { m_upsampleMethods[ci] = ComponentUpsampler.h2v2_fancy_upsampler; m_need_context_rows = true; } else { m_upsampleMethods[ci] = ComponentUpsampler.h2v2_upsampler; } } else if ((h_out_group % h_in_group) == 0 && (v_out_group % v_in_group) == 0) { /* Generic integral-factors upsampling method */ m_upsampleMethods[ci] = ComponentUpsampler.int_upsampler; m_h_expand[ci] = (byte)(h_out_group / h_in_group); m_v_expand[ci] = (byte)(v_out_group / v_in_group); } else { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); } if (need_buffer) { ComponentBuffer cb = new ComponentBuffer(); cb.SetBuffer(jpeg_common_struct.AllocJpegSamples(JpegUtils.jround_up(cinfo.m_output_width, cinfo.m_max_h_samp_factor), cinfo.m_max_v_samp_factor), null, 0); m_color_buf[ci] = cb; } } }
/* * Compute output image dimensions and related values. * NOTE: this is exported for possible use by application. * Hence it mustn't do anything that can't be done twice. */ /* Do computations that are needed before master selection phase. * This function is used for transcoding and full decompression. */ public void JpegCoreOutputDimensions() { /* Compute actual output image dimensions and DCT scaling choices. */ if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom) { /* Provide 1/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 1; m_cinfo.min_DCT_v_scaled_size = 1; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 2) { /* Provide 2/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 2L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 2L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 2; m_cinfo.min_DCT_v_scaled_size = 2; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 3) { /* Provide 3/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 3L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 3L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 3; m_cinfo.min_DCT_v_scaled_size = 3; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 4) { /* Provide 4/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 4L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 4L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 4; m_cinfo.min_DCT_v_scaled_size = 4; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 5) { /* Provide 5/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 5L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 5L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 5; m_cinfo.min_DCT_v_scaled_size = 5; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 6) { /* Provide 6/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 6L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 6L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 6; m_cinfo.min_DCT_v_scaled_size = 6; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 7) { /* Provide 7/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 7L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 7L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 7; m_cinfo.min_DCT_v_scaled_size = 7; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 8) { /* Provide 8/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 8L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 8L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 8; m_cinfo.min_DCT_v_scaled_size = 8; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 9) { /* Provide 9/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 9L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 9L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 9; m_cinfo.min_DCT_v_scaled_size = 9; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 10) { /* Provide 10/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 10L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 10L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 10; m_cinfo.min_DCT_v_scaled_size = 10; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 11) { /* Provide 11/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 11L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 11L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 11; m_cinfo.min_DCT_v_scaled_size = 11; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 12) { /* Provide 12/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 12L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 12L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 12; m_cinfo.min_DCT_v_scaled_size = 12; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 13) { /* Provide 13/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 13L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 13L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 13; m_cinfo.min_DCT_v_scaled_size = 13; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 14) { /* Provide 14/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 14L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 14L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 14; m_cinfo.min_DCT_v_scaled_size = 14; } else if (m_cinfo.scaleNum * m_cinfo.block_size <= m_cinfo.scaleDenom * 15) { /* Provide 15/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 15L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 15L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 15; m_cinfo.min_DCT_v_scaled_size = 15; } else { /* Provide 16/block_size scaling */ m_cinfo.outputWidth = (int) JpegUtils.jdiv_round_up(m_cinfo.imageWidth * 16L, m_cinfo.block_size); m_cinfo.outputHeight = (int) JpegUtils.jdiv_round_up(m_cinfo.imageHeight * 16L, m_cinfo.block_size); m_cinfo.min_DCT_h_scaled_size = 16; m_cinfo.min_DCT_v_scaled_size = 16; } /* Recompute dimensions of components */ for (var ci = 0; ci < m_cinfo.numComponents; ci++) { var compptr = m_cinfo.CompInfo[ci]; compptr.DCT_h_scaled_size = m_cinfo.min_DCT_h_scaled_size; compptr.DCT_v_scaled_size = m_cinfo.min_DCT_v_scaled_size; } }