/// <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(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; var inputBuffer = input_data[row]; var outputBuffer = output_data[outrow]; for (int col = 0; outIndex < m_cinfo.m_output_width; col++) { byte invalue = inputBuffer[col]; /* don't need GETJSAMPLE() here */ outputBuffer[outIndex++] = invalue; outputBuffer[outIndex++] = invalue; } JpegUtils.jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, 1, m_cinfo.m_output_width); inrow++; outrow += 2; } }
/// <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; } }
/// <summary> /// Fast processing for the common case of 2:1 horizontal and 2:1 vertical. /// It's still a box filter. /// </summary> private void H2V2UpSample(ref ComponentBuffer input_data) { var output_data = m_color_buf[m_currentComponent]; var inrow = 0; var outrow = 0; while (outrow < m_cinfo.m_maxVSampleFactor) { var row = m_upsampleRowOffset + inrow; var outIndex = 0; for (var col = 0; outIndex < m_cinfo.outputWidth; col++) { var 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.outputWidth); inrow++; outrow += 2; } }
/// <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++; } }
/// <summary> /// Downsample pixel values of a single component. /// This version handles the special case of a full-size component, /// without smoothing. /// </summary> private void FullsizeDownsample(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 */ var compptr = m_cinfo.Component_info[componentIndex]; ExpandRightEdge(output_data, startOutRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, compptr.Width_in_blocks * compptr.DCT_h_scaled_size); }
/// <summary> /// Color conversion for grayscale: just copy the data. /// This also works for YCC -> grayscale conversion, in which /// we just copy the Y (luminance) component and ignore chrominance. /// </summary> private void GrayscaleConvert(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.outputWidth); }