public static jcopy_sample_rows ( |
||
input_array | ||
source_row | int | |
output_array | ||
dest_row | int | |
num_rows | int | |
num_cols | int | |
return | void |
/// <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> /// 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> /// 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++; } }
/// <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> /// 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); }
/// <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; } } }