/* * 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]; } }
/**************** 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); } }