// Downsample pixel values of a single component. // One row group is processed per call. // This version handles arbitrary integral sampling ratios, without smoothing. // Note that this version is not actually used for customary sampling ratios. static void int_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { uint output_cols = compptr.width_in_blocks * cinfo.DCT_size; int h_expand = cinfo.max_h_samp_factor / compptr.h_samp_factor; int v_expand = cinfo.max_v_samp_factor / compptr.v_samp_factor; int numpix = h_expand * v_expand; int numpix2 = numpix / 2; // Expand input data enough to let all the output samples be generated // by the standard loop. Special-casing padded output would be more // efficient. expand_right_edge(input_data, in_row_index, cinfo.max_v_samp_factor, cinfo.image_width, (uint)(output_cols * h_expand)); int inrow = (int)in_row_index; for (int outrow = 0; outrow < compptr.v_samp_factor; outrow++) { byte[] outptr = output_data[out_row_index + outrow]; for (uint outcol = 0, outcol_h = 0; outcol < output_cols; outcol++, outcol_h += (uint)h_expand) { byte[] inptr; int outvalue = 0; for (int v = 0; v < v_expand; v++) { inptr = input_data[inrow + v]; for (int h = 0; h < h_expand; h++) { outvalue += (int)inptr[outcol_h + h]; } } outptr[outcol] = (byte)((outvalue + numpix2) / numpix); } inrow += v_expand; } }
// Downsample pixel values of a single component. // This version handles the standard case of 2:1 horizontal and 2:1 vertical, // without smoothing. static void h2v2_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { uint output_cols = compptr.width_in_blocks * cinfo.DCT_size; // Expand input data enough to let all the output samples be generated // by the standard loop. Special-casing padded output would be more // efficient. expand_right_edge(input_data, in_row_index, cinfo.max_v_samp_factor, cinfo.image_width, output_cols * 2); int inrow = (int)in_row_index; for (int outrow = 0; outrow < compptr.v_samp_factor; outrow++) { byte[] outptr = output_data[out_row_index + outrow]; byte[] inptr0 = input_data[inrow]; byte[] inptr1 = input_data[inrow + 1]; int bias = 1; // bias = 1,2,1,2,... for successive samples for (uint outcol = 0, ind = 0; outcol < output_cols; outcol++, ind += 2) { outptr[outcol] = (byte)((inptr0[ind] + inptr0[ind + 1] + inptr1[ind] + inptr1[ind + 1] + bias) >> 2); bias ^= 3; // 1=>2, 2=>1 } inrow += 2; } }
// These are the routines invoked by sep_upsample to upsample pixel values // of a single component. One row group is processed per call. // For full-size components, we just make color_buf[ci] point at the // input buffer, and thus avoid copying any data. Note that this is // safe only because sep_upsample doesn't declare the input row group // "consumed" until we are done color converting and emitting it. static void fullsize_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { for (int i = 0; i < cinfo.max_v_samp_factor; i++) { output_data_ptr[output_data_offset][i] = input_data[input_data_offset++]; } }
// Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. // // The upsampling algorithm is linear interpolation between pixel centers, // also known as a "triangle filter". This is a good compromise between // speed and visual quality. The centers of the output pixels are 1/4 and 3/4 // of the way between input pixel centers. // // A note about the "bias" calculations: when rounding fractional values to // integer, we do not want to always round 0.5 up to the next integer. // If we did that, we'd introduce a noticeable bias towards larger values. // Instead, this code is arranged so that 0.5 will be rounded up or down at // alternate pixel locations (a simple ordered dither pattern). #if !USE_UNSAFE_STUFF static void h2v1_fancy_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { byte[][] output_data = output_data_ptr[output_data_offset]; for (int inrow = 0; inrow < cinfo.max_v_samp_factor; inrow++) { byte[] inptr = input_data[input_data_offset++]; uint inptr_ind = 0; byte[] outptr = output_data[inrow]; uint outptr_ind = 0; // Special case for first column int invalue = inptr[inptr_ind++]; outptr[outptr_ind++] = (byte)invalue; outptr[outptr_ind++] = (byte)((invalue * 3 + inptr[inptr_ind] + 2) >> 2); for (uint colctr = compptr.downsampled_width - 2; colctr > 0; colctr--) { // General case: 3/4 * nearer pixel + 1/4 * further pixel invalue = inptr[inptr_ind++] * 3; outptr[outptr_ind++] = (byte)((invalue + inptr[inptr_ind - 2] + 1) >> 2); outptr[outptr_ind++] = (byte)((invalue + inptr[inptr_ind] + 2) >> 2); } // Special case for last column invalue = inptr[inptr_ind]; outptr[outptr_ind++] = (byte)((invalue * 3 + inptr[inptr_ind - 1] + 1) >> 2); outptr[outptr_ind] = (byte)invalue; } }
// Fast processing for the common case of 2:1 horizontal and 2:1 vertical. // It's still a box filter. static void h2v2_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { byte[][] output_data = output_data_ptr[output_data_offset]; int outrow = 0; uint outend = cinfo.output_width; while (outrow < cinfo.max_v_samp_factor) { byte[] inptr = input_data[input_data_offset]; uint inptr_ind = 0; byte[] outptr = output_data[outrow]; uint outptr_ind = 0; while (outptr_ind < outend) { byte invalue = inptr[inptr_ind++]; outptr[outptr_ind++] = invalue; outptr[outptr_ind++] = invalue; } Array.Copy(output_data[outrow], output_data[outrow + 1], cinfo.output_width); input_data_offset++; outrow += 2; } }
static void h2v2_fancy_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { unsafe { if (!compptr.notFirst) { input_data[0].CopyTo(input_data[input_data.Length - 1], 0); compptr.notFirst = true; } byte[][] output_data = output_data_ptr[output_data_offset]; int inrow = 0, outrow = 0; while (outrow < cinfo.max_v_samp_factor) { // inptr0 points to nearest input row fixed(byte *inptr0_ = input_data[input_data_offset + inrow]) { // inptr1 points to next nearest int nextnearestrow = input_data_offset == 0?input_data.Length - 1:input_data_offset + inrow - 1; // next nearest is row above for (int v = 0; v < 2; v++) { fixed(byte *inptr1_ = input_data[nextnearestrow], outptr_ = output_data[outrow++]) { byte *inptr0 = inptr0_, inptr1 = inptr1_, outptr = outptr_; // Special case for first column int thiscolsum = *(inptr0++) * 3 + *(inptr1++); int nextcolsum = *(inptr0++) * 3 + *(inptr1++); *(outptr++) = (byte)((thiscolsum * 4 + 8) >> 4); *(outptr++) = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); int lastcolsum = thiscolsum; thiscolsum = nextcolsum; for (uint colctr = compptr.downsampled_width - 2; colctr > 0; colctr--) { // General case: 3/4 * nearer pixel + 1/4 * further pixel in each // dimension, thus 9/16, 3/16, 3/16, 1/16 overall nextcolsum = *(inptr0++) * 3 + *(inptr1++); *(outptr++) = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); *(outptr++) = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); lastcolsum = thiscolsum; thiscolsum = nextcolsum; } // Special case for last column *(outptr++) = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); *(outptr++) = (byte)((thiscolsum * 4 + 7) >> 4); } nextnearestrow = (input_data_offset + inrow + 1) % input_data.Length; // next nearest is row below } } inrow++; } } }
static void h2v1_fancy_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { unsafe { byte[][] output_data = output_data_ptr[output_data_offset]; for (int inrow = 0; inrow < cinfo.max_v_samp_factor; inrow++) { fixed(byte *inptr_ = input_data[input_data_offset++], outptr_ = output_data[inrow]) { byte *inptr = inptr_; byte *outptr = outptr_; // Special case for first column int invalue = *(inptr++); *(outptr++) = (byte)invalue; *(outptr++) = (byte)((invalue * 3 + *inptr + 2) >> 2); for (uint colctr = compptr.downsampled_width - 2; colctr > 0; colctr--) { // General case: 3/4 * nearer pixel + 1/4 * further pixel invalue = *(inptr++) * 3; *(outptr++) = (byte)((invalue + inptr[-2] + 1) >> 2); *(outptr++) = (byte)((invalue + *inptr + 2) >> 2); } // Special case for last column invalue = *inptr; *(outptr++) = (byte)((invalue * 3 + inptr[-1] + 1) >> 2); *outptr = (byte)invalue; } } } }
// Allocate downsampled-data buffers needed for downsampled I/O. // We use values computed in jpeg_start_compress or jpeg_start_decompress. // We use libjpeg's allocator so that buffers will be released automatically // when done with strip/tile. // This is also a handy place to compute samplesperclump, bytesperline. static bool alloc_downsampled_buffers(TIFF tif, jpeg_component_info[] comp_info, int num_components) { JPEGState sp=tif.tif_data as JPEGState; byte[][] buf; int samples_per_clump=0; for(int ci=0; ci<num_components; ci++) { jpeg_component_info compptr=comp_info[ci]; samples_per_clump+=compptr.h_samp_factor*compptr.v_samp_factor; try { buf=TIFFjpeg_alloc_sarray(sp, compptr.width_in_blocks*libjpeg.DCTSIZE, (uint)compptr.v_samp_factor*libjpeg.DCTSIZE); } catch { return false; } sp.ds_buffer[ci]=buf; } sp.samplesperclump=samples_per_clump; return true; }
// Create the wrapped-around downsampling input buffer needed for context mode. static void create_context_buffer(jpeg_compress cinfo) { my_prep_controller prep = (my_prep_controller)cinfo.prep; int rgroup_height = cinfo.max_v_samp_factor; for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; // Grab enough space for fake row pointers; // we need five row groups' worth of pointers for each component. byte[][] fake_buffer = new byte[5 * rgroup_height][]; // Allocate the actual buffer space (3 row groups) for this component. // We make the buffer wide enough to allow the downsampler to edge-expand // horizontally within the buffer, if it so chooses. byte[][] true_buffer = alloc_sarray(cinfo, (uint)(((int)compptr.width_in_blocks * cinfo.DCT_size * cinfo.max_h_samp_factor) / compptr.h_samp_factor), (uint)(3 * rgroup_height)); // Copy true buffer row pointers into the middle of the fake row array Array.Copy(true_buffer, 0, fake_buffer, rgroup_height, 3 * rgroup_height); // Fill in the above and below wraparound pointers for (int i = 0; i < rgroup_height; i++) { fake_buffer[i] = true_buffer[2 * rgroup_height + i]; fake_buffer[4 * rgroup_height + i] = true_buffer[i]; } prep.color_buf[ci] = fake_buffer; } }
// Downsample pixel values of a single component. // This version handles the special case of a full-size component, // without smoothing. static void fullsize_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { // Copy the data jcopy_sample_rows(input_data, (int)in_row_index, output_data, (int)out_row_index, cinfo.max_v_samp_factor, cinfo.image_width); // Edge-expand expand_right_edge(output_data, out_row_index, cinfo.max_v_samp_factor, cinfo.image_width, compptr.width_in_blocks * cinfo.DCT_size); }
// Fast processing for the common case of 2:1 horizontal and 2:1 vertical. // It's still a box filter. #if !USE_UNSAFE_STUFF static void h2v2_fancy_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { if (!compptr.notFirst) { input_data[0].CopyTo(input_data[input_data.Length - 1], 0); compptr.notFirst = true; } byte[][] output_data = output_data_ptr[output_data_offset]; int inrow = 0, outrow = 0; while (outrow < cinfo.max_v_samp_factor) { for (int v = 0; v < 2; v++) { // inptr0 points to nearest input row, inptr1 points to next nearest byte[] inptr0 = input_data[input_data_offset + inrow]; byte[] inptr1 = null; if (v == 0) { inptr1 = input_data[input_data_offset == 0?input_data.Length - 1:input_data_offset + inrow - 1]; // next nearest is row above } else { inptr1 = input_data[(input_data_offset + inrow + 1) % input_data.Length]; // next nearest is row below } uint intptr_ind = 0; byte[] outptr = output_data[outrow++]; uint outptr_ind = 0; // Special case for first column int thiscolsum = inptr0[intptr_ind] * 3 + inptr1[intptr_ind]; intptr_ind++; int nextcolsum = inptr0[intptr_ind] * 3 + inptr1[intptr_ind]; intptr_ind++; outptr[outptr_ind++] = (byte)((thiscolsum * 4 + 8) >> 4); outptr[outptr_ind++] = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); int lastcolsum = thiscolsum; thiscolsum = nextcolsum; for (uint colctr = compptr.downsampled_width - 2; colctr > 0; colctr--) { // General case: 3/4 * nearer pixel + 1/4 * further pixel in each // dimension, thus 9/16, 3/16, 3/16, 1/16 overall nextcolsum = inptr0[intptr_ind] * 3 + inptr1[intptr_ind]; intptr_ind++; outptr[outptr_ind++] = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); outptr[outptr_ind++] = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); lastcolsum = thiscolsum; thiscolsum = nextcolsum; } // Special case for last column outptr[outptr_ind++] = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); outptr[outptr_ind++] = (byte)((thiscolsum * 4 + 7) >> 4); } inrow++; } }
// Downsample pixel values of a single component. // This version handles the special case of a full-size component, // with smoothing. One row of context is required. static void fullsize_smooth_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { uint output_cols = compptr.width_in_blocks * cinfo.DCT_size; // Expand input data enough to let all the output samples be generated // by the standard loop. Special-casing padded output would be more // efficient. expand_right_edge(input_data, in_row_index - 1, cinfo.max_v_samp_factor + 2, cinfo.image_width, output_cols); // Each of the eight neighbor pixels contributes a fraction SF to the // smoothed pixel, while the main pixel contributes (1-8*SF). In order // to use integer arithmetic, these factors are multiplied by 2^16 = 65536. // Also recall that SF = smoothing_factor / 1024. int memberscale = 65536 - cinfo.smoothing_factor * 512; // scaled 1-8*SF int neighscale = cinfo.smoothing_factor * 64; // scaled SF int membersum, neighsum; int colsum, lastcolsum, nextcolsum; for (int outrow = 0; outrow < compptr.v_samp_factor; outrow++) { byte[] outptr = output_data[out_row_index + outrow]; byte[] inptr = input_data[in_row_index + outrow]; byte[] above_ptr = input_data[in_row_index + outrow - 1]; byte[] below_ptr = input_data[in_row_index + outrow + 1]; int iind = 0, aind = 0, bind = 0, oind = 0; // Special case for first column colsum = above_ptr[aind++] + below_ptr[bind++] + inptr[iind]; membersum = inptr[iind++]; nextcolsum = above_ptr[aind] + below_ptr[bind] + inptr[iind]; neighsum = colsum + (colsum - membersum) + nextcolsum; membersum = membersum * memberscale + neighsum * neighscale; outptr[oind++] = (byte)((membersum + 32768) >> 16); lastcolsum = colsum; colsum = nextcolsum; for (uint colctr = output_cols - 2; colctr > 0; colctr--) { membersum = inptr[iind++]; nextcolsum = above_ptr[aind++] + below_ptr[bind++] + inptr[iind]; neighsum = lastcolsum + (colsum - membersum) + nextcolsum; membersum = membersum * memberscale + neighsum * neighscale; outptr[oind++] = (byte)((membersum + 32768) >> 16); lastcolsum = colsum; colsum = nextcolsum; } // Special case for last column membersum = inptr[iind]; neighsum = lastcolsum + (colsum - membersum) + colsum; membersum = membersum * memberscale + neighsum * neighscale; outptr[oind] = (byte)((membersum + 32768) >> 16); } }
static void SET_COMP(jpeg_compress cinfo, int index, int id, int hsamp, int vsamp, int quant, int dctbl, int actbl) { jpeg_component_info compptr = cinfo.comp_info[index]; compptr.component_id = id; compptr.h_samp_factor = hsamp; compptr.v_samp_factor = vsamp; compptr.quant_tbl_no = quant; compptr.dc_tbl_no = dctbl; compptr.ac_tbl_no = actbl; }
// Initialize for a Huffman-compressed scan. static void start_pass_lhuff_decoder(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; lhuff_entropy_decoder entropy = (lhuff_entropy_decoder)losslsd.entropy_private; for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; int dctbl = compptr.dc_tbl_no; // Make sure requested tables are present if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS || cinfo.dc_huff_tbl_ptrs[dctbl] == null) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, dctbl); } // Compute derived values for Huffman tables // We may do this more than once for a table, but it's not expensive jpeg_make_d_derived_tbl(cinfo, true, dctbl, ref entropy.derived_tbls[dctbl]); } // Precalculate decoding info for each sample in an MCU of this scan int ptrn = 0; for (int sampn = 0; sampn < cinfo.blocks_in_MCU;) { jpeg_component_info compptr = cinfo.cur_comp_info[cinfo.MCU_membership[sampn]]; int ci = compptr.component_index; for (int yoffset = 0; yoffset < compptr.MCU_height; yoffset++, ptrn++) { // Precalculate the setup info for each output pointer entropy.output_ptr_info[ptrn].ci = ci; entropy.output_ptr_info[ptrn].yoffset = yoffset; entropy.output_ptr_info[ptrn].MCU_width = compptr.MCU_width; for (int xoffset = 0; xoffset < compptr.MCU_width; xoffset++, sampn++) { // Precalculate the output pointer index for each sample entropy.output_ptr_index[sampn] = ptrn; // Precalculate which table to use for each sample entropy.cur_tbls[sampn] = entropy.derived_tbls[compptr.dc_tbl_no]; } } } entropy.num_output_ptrs = ptrn; // Initialize bitread state variables entropy.bitstate.bits_left = 0; entropy.bitstate.get_buffer = 0; // unnecessary, but keeps Purify quiet entropy.insufficient_data = false; }
// Control routine to do upsampling (and color conversion). // // In this version we upsample each component independently. // We upsample one row group into the conversion buffer, then apply // color conversion a row at a time. static void sep_upsample(jpeg_decompress cinfo, byte[][][] input_buf, ref uint in_row_group_ctr, uint in_row_groups_avail, byte[][] output_buf, uint output_buf_offset, ref uint out_row_ctr, uint out_rows_avail) { my_upsampler upsample = (my_upsampler)cinfo.upsample; // Fill the conversion buffer, if it's empty if (upsample.next_row_out >= cinfo.max_v_samp_factor) { for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; // Invoke per-component upsample method. Notice we pass a POINTER // to color_buf[ci], so that fullsize_upsample can change it. upsample.methods[ci](cinfo, compptr, input_buf[ci], (int)(in_row_group_ctr * upsample.rowgroup_height[ci]), upsample.color_buf, ci); } upsample.next_row_out = 0; } // Color-convert and emit rows // How many we have in the buffer: uint num_rows = (uint)(cinfo.max_v_samp_factor - upsample.next_row_out); // Not more than the distance to the end of the image. Need this test // in case the image height is not a multiple of max_v_samp_factor: if (num_rows > upsample.rows_to_go) { num_rows = upsample.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; } cinfo.cconvert.color_convert(cinfo, upsample.color_buf, (uint)upsample.next_row_out, output_buf, output_buf_offset + out_row_ctr, (int)num_rows); // Adjust counts out_row_ctr += num_rows; upsample.rows_to_go -= num_rows; upsample.next_row_out += (int)num_rows; // When the buffer is emptied, declare this input row group consumed if (upsample.next_row_out >= cinfo.max_v_samp_factor) { in_row_group_ctr++; } }
// Output some data from the full-image buffer sample in the multi-pass case. // Always attempts to emit one fully interleaved MCU row ("iMCU" row). // Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. // // NB: output_buf contains a plane for each component in image. static CONSUME_INPUT output_data_d_diff(jpeg_decompress cinfo, byte[][][] output_buf) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; d_diff_controller diff = (d_diff_controller)losslsd.diff_private; uint last_iMCU_row = cinfo.total_iMCU_rows - 1; // Force some input to be done if we are getting ahead of the input. while (cinfo.input_scan_number < cinfo.output_scan_number || (cinfo.input_scan_number == cinfo.output_scan_number && cinfo.input_iMCU_row <= cinfo.output_iMCU_row)) { if (cinfo.inputctl.consume_input(cinfo) == CONSUME_INPUT.JPEG_SUSPENDED) { return(CONSUME_INPUT.JPEG_SUSPENDED); } } // OK, output from the arrays. for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; // Align the buffer for this component. byte[][] buffer = diff.whole_image[ci]; int samp_rows; if (cinfo.output_iMCU_row < last_iMCU_row) { samp_rows = compptr.v_samp_factor; } else { // NB: can't use last_row_height here; it is input-side-dependent! samp_rows = (int)(compptr.height_in_blocks % compptr.v_samp_factor); if (samp_rows == 0) { samp_rows = compptr.v_samp_factor; } } for (int row = 0; row < samp_rows; row++) { Array.Copy(buffer[cinfo.output_iMCU_row * compptr.v_samp_factor + row], output_buf[ci][row], compptr.width_in_blocks); } } if (++(cinfo.output_iMCU_row) < cinfo.total_iMCU_rows) { return(CONSUME_INPUT.JPEG_ROW_COMPLETED); } return(CONSUME_INPUT.JPEG_SCAN_COMPLETED); }
// Initialize difference buffer controller. static void jinit_d_diff_controller(jpeg_decompress cinfo, bool need_full_buffer) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; d_diff_controller diff = null; try { diff = new d_diff_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } losslsd.diff_private = diff; losslsd.diff_start_input_pass = start_input_pass_d_diff; losslsd.start_output_pass = start_output_pass_d_diff; // Create the [un]difference buffers. for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; diff.diff_buf[ci] = alloc_darray(cinfo, (uint)jround_up(compptr.width_in_blocks, compptr.h_samp_factor), (uint)compptr.v_samp_factor); diff.undiff_buf[ci] = alloc_darray(cinfo, (uint)jround_up(compptr.width_in_blocks, compptr.h_samp_factor), (uint)compptr.v_samp_factor); } if (need_full_buffer) { #if D_MULTISCAN_FILES_SUPPORTED // Allocate a full-image array for each component. for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; diff.whole_image[ci] = alloc_sarray(cinfo, (uint)jround_up(compptr.width_in_blocks, compptr.h_samp_factor), (uint)jround_up(compptr.height_in_blocks, compptr.v_samp_factor)); } losslsd.consume_data = consume_data_d_diff; losslsd.decompress_data = output_data_d_diff; #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif } else { losslsd.consume_data = dummy_consume_data_d_diff; losslsd.decompress_data = decompress_data_d_diff; diff.whole_image[0] = null; // flag for no arrays } }
// Initialize preprocessing controller. static void jinit_c_prep_controller(jpeg_compress cinfo, bool need_full_buffer) { if (need_full_buffer) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); // safety check } my_prep_controller prep = null; try { prep = new my_prep_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.prep = prep; prep.start_pass = start_pass_prep; // Allocate the color conversion buffer. // We make the buffer wide enough to allow the downsampler to edge-expand // horizontally within the buffer, if it so chooses. if (cinfo.downsample.need_context_rows) { // Set up to provide context rows #if CONTEXT_ROWS_SUPPORTED prep.pre_process_data = pre_process_context; create_context_buffer(cinfo); #else ERREXIT(cinfo, J_MESSAGE_CODE.JERR_NOT_COMPILED); #endif } else { // No context, just make it tall enough for one row group prep.pre_process_data = pre_process_data; for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; prep.color_buf[ci] = alloc_sarray(cinfo, (uint)(((int)compptr.width_in_blocks * cinfo.DCT_size * cinfo.max_h_samp_factor) / compptr.h_samp_factor), (uint)cinfo.max_v_samp_factor); } } }
// Process some data in the simple no-context case. // // Preprocessor output data is counted in "row groups". A row group // is defined to be v_samp_factor sample rows of each component. // Downsampling will produce this much data from each max_v_samp_factor input rows. static void pre_process_data(jpeg_compress cinfo, byte[][] input_buf, ref uint in_row_ctr, uint in_rows_avail, byte[][][] output_buf, ref uint out_row_group_ctr, uint out_row_groups_avail) { my_prep_controller prep = (my_prep_controller)cinfo.prep; while (in_row_ctr < in_rows_avail && out_row_group_ctr < out_row_groups_avail) { // Do color conversion to fill the conversion buffer. uint inrows = in_rows_avail - in_row_ctr; int numrows = cinfo.max_v_samp_factor - prep.next_buf_row; numrows = (int)Math.Min((uint)numrows, inrows); cinfo.cconvert.color_convert(cinfo, input_buf, in_row_ctr, prep.color_buf, (uint)prep.next_buf_row, numrows); in_row_ctr += (uint)numrows; prep.next_buf_row += numrows; prep.rows_to_go -= (uint)numrows; // If at bottom of image, pad to fill the conversion buffer. if (prep.rows_to_go == 0 && prep.next_buf_row < cinfo.max_v_samp_factor) { for (int ci = 0; ci < cinfo.num_components; ci++) { expand_bottom_edge(prep.color_buf[ci], cinfo.image_width, prep.next_buf_row, cinfo.max_v_samp_factor); } prep.next_buf_row = cinfo.max_v_samp_factor; } // If we've filled the conversion buffer, empty it. if (prep.next_buf_row == cinfo.max_v_samp_factor) { cinfo.downsample.downsample(cinfo, prep.color_buf, 0, output_buf, out_row_group_ctr); prep.next_buf_row = 0; out_row_group_ctr++; } // If at bottom of image, pad the output to a full iMCU height. // Note we assume the caller is providing a one-iMCU-height output buffer! if (prep.rows_to_go == 0 && out_row_group_ctr < out_row_groups_avail) { for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; expand_bottom_edge(output_buf[ci], compptr.width_in_blocks * cinfo.DCT_size, (int)(out_row_group_ctr * compptr.v_samp_factor), (int)(out_row_groups_avail * compptr.v_samp_factor)); } out_row_group_ctr = out_row_groups_avail; break; // can exit outer loop without test } } // while(...) }
// Process some data in the first pass of a multi-pass case. // We process the equivalent of one fully interleaved MCU row ("iMCU" row) // per call, ie, v_samp_factor rows for each component in the image. // This amount of data is read from the source buffer and saved into the arrays. // // We must also emit the data to the compressor. This is conveniently // done by calling compress_output_diff() after we've loaded the current strip // of the arrays. // // NB: input_buf contains a plane for each component in image. All components // are loaded into the arrays in this pass. However, it may be that // only a subset of the components are emitted to the compressor during // this first pass; be careful about looking at the scan-dependent variables // (MCU dimensions, etc). static bool compress_first_pass_diff(jpeg_compress cinfo, byte[][][] input_buf) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_diff_controller diff = (c_diff_controller)losslsc.diff_private; uint last_iMCU_row = cinfo.total_iMCU_rows - 1; for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; // Count non-dummy sample rows in this iMCU row. int samp_rows; if (diff.iMCU_row_num < last_iMCU_row) { samp_rows = compptr.v_samp_factor; } else { // NB: can't use last_row_height here, since may not be set! samp_rows = (int)(compptr.height_in_blocks % compptr.v_samp_factor); if (samp_rows == 0) { samp_rows = compptr.v_samp_factor; } } uint samps_across = compptr.width_in_blocks; // Perform point transform scaling and prediction/differencing for all // non-dummy rows in this iMCU row. Each call on these functions // process a complete row of samples. for (int samp_row = 0; samp_row < samp_rows; samp_row++) { Array.Copy(input_buf[ci][samp_row], diff.whole_image[ci][samp_row + diff.iMCU_row_num * compptr.v_samp_factor], samps_across); } } // NB: compress_output will increment iMCU_row_num if successful. // A suspension return will result in redoing all the work above next time. // Emit data to the compressor, sharing code with subsequent passes return(compress_output_diff(cinfo, input_buf)); }
// Consume input data and store it in the full-image sample buffer. // We read as much as one fully interleaved MCU row ("iMCU" row) per call, // ie, v_samp_factor rows for each component in the scan. // Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. static CONSUME_INPUT consume_data_d_diff(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; d_diff_controller diff = (d_diff_controller)losslsd.diff_private; uint last_iMCU_row = cinfo.total_iMCU_rows - 1; byte[][][] buffer = new byte[MAX_COMPS_IN_SCAN][][]; int[] buffer_ind = new int[MAX_COMPS_IN_SCAN]; // Align the buffers for the components used in this scan. for (int comp = 0; comp < cinfo.comps_in_scan; comp++) { jpeg_component_info compptr = cinfo.cur_comp_info[comp]; int ci = compptr.component_index; buffer[ci] = diff.whole_image[ci]; buffer_ind[ci] = (int)cinfo.input_iMCU_row * compptr.v_samp_factor; } return(decompress_data_d_diff(cinfo, buffer, buffer_ind)); }
// Fast processing for the common case of 2:1 horizontal and 1:1 vertical. // It's still a box filter. static void h2v1_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { byte[][] output_data = output_data_ptr[output_data_offset]; uint outend = cinfo.output_width; for (int outrow = 0; outrow < cinfo.max_v_samp_factor; outrow++) { byte[] inptr = input_data[input_data_offset++]; uint inptr_ind = 0; byte[] outptr = output_data[outrow]; uint outptr_ind = 0; while (outptr_ind < outend) { byte invalue = inptr[inptr_ind++]; outptr[outptr_ind++] = invalue; outptr[outptr_ind++] = invalue; } } }
// Process some data in subsequent passes of a multi-pass case. // We process the equivalent of one fully interleaved MCU row ("iMCU" row) // per call, ie, v_samp_factor rows for each component in the scan. // The data is obtained from the arrays and fed to the compressor. // Returns true if the iMCU row is completed, false if suspended. // // NB: input_buf is ignored; it is likely to be a null pointer. static bool compress_output_diff(jpeg_compress cinfo, byte[][][] input_buf) { jpeg_lossless_c_codec losslsc = (jpeg_lossless_c_codec)cinfo.coef; c_diff_controller diff = (c_diff_controller)losslsc.diff_private; byte[][][] buffer = new byte[MAX_COMPONENTS][][]; int[] buffer_ind = new int[MAX_COMPONENTS]; // Align the buffers for the components used in this scan. // NB: during first pass, this is safe only because the buffers will // already be aligned properly, so jmemmgr.cs won't need to do any I/O. for (int comp = 0; comp < cinfo.comps_in_scan; comp++) { jpeg_component_info compptr = cinfo.cur_comp_info[comp]; int ci = compptr.component_index; buffer[ci] = diff.whole_image[ci]; buffer_ind[ci] = (int)diff.iMCU_row_num * compptr.v_samp_factor; } return(compress_data_diff(cinfo, buffer, buffer_ind)); }
// 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. static void int_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { my_upsampler upsample = (my_upsampler)cinfo.upsample; byte[][] output_data = output_data_ptr[output_data_offset]; int h_expand = upsample.h_expand[compptr.component_index]; int v_expand = upsample.v_expand[compptr.component_index]; int outrow = 0; uint outend = cinfo.output_width; while (outrow < cinfo.max_v_samp_factor) { // Generate one output row with proper horizontal expansion byte[] inptr = input_data[input_data_offset]; uint inptr_ind = 0; byte[] outptr = output_data[outrow]; uint outptr_ind = 0; while (outptr_ind < outend) { byte invalue = inptr[inptr_ind++]; for (int h = h_expand; h > 0; h--) { outptr[outptr_ind++] = invalue; } } // Generate any additional output rows by duplicating the first one if (v_expand > 1) { jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, v_expand - 1, cinfo.output_width); } input_data_offset++; outrow += v_expand; } }
// Downsample pixel values of a single component. // One row group is processed per call. // This version handles arbitrary integral sampling ratios, without smoothing. // Note that this version is not actually used for customary sampling ratios. static void int_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { uint output_cols=compptr.width_in_blocks*cinfo.DCT_size; int h_expand=cinfo.max_h_samp_factor/compptr.h_samp_factor; int v_expand=cinfo.max_v_samp_factor/compptr.v_samp_factor; int numpix=h_expand*v_expand; int numpix2=numpix/2; // Expand input data enough to let all the output samples be generated // by the standard loop. Special-casing padded output would be more // efficient. expand_right_edge(input_data, in_row_index, cinfo.max_v_samp_factor, cinfo.image_width, (uint)(output_cols*h_expand)); int inrow=(int)in_row_index; for(int outrow=0; outrow<compptr.v_samp_factor; outrow++) { byte[] outptr=output_data[out_row_index+outrow]; for(uint outcol=0, outcol_h=0; outcol<output_cols; outcol++, outcol_h+=(uint)h_expand) { byte[] inptr; int outvalue=0; for(int v=0; v<v_expand; v++) { inptr=input_data[inrow+v]; for(int h=0; h<h_expand; h++) outvalue+=(int)inptr[outcol_h+h]; } outptr[outcol]=(byte)((outvalue+numpix2)/numpix); } inrow+=v_expand; } }
// Routines to calculate various quantities related to the size of the image. // Called once, when first SOS marker is reached static void initial_setup_d_input(jpeg_decompress cinfo) { // Make sure image isn't bigger than I can handle if ((int)cinfo.image_height > JPEG_MAX_DIMENSION || (int)cinfo.image_width > JPEG_MAX_DIMENSION) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, JPEG_MAX_DIMENSION); } if (cinfo.process == J_CODEC_PROCESS.JPROC_LOSSLESS) { // If precision > compiled-in value, we must downscale if (cinfo.data_precision > BITS_IN_JSAMPLE) { WARNMS2(cinfo, J_MESSAGE_CODE.JWRN_MUST_DOWNSCALE, cinfo.data_precision, BITS_IN_JSAMPLE); } } else { // Lossy processes // For now, precision must match compiled-in value... if (cinfo.data_precision != BITS_IN_JSAMPLE) { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_BAD_PRECISION, cinfo.data_precision); } } // Check that number of components won't exceed internal array sizes if (cinfo.num_components > MAX_COMPONENTS) { ERREXIT2(cinfo, J_MESSAGE_CODE.JERR_COMPONENT_COUNT, cinfo.num_components, MAX_COMPONENTS); } // Compute maximum sampling factors; check factor validity cinfo.max_h_samp_factor = 1; cinfo.max_v_samp_factor = 1; for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; if (compptr.h_samp_factor <= 0 || compptr.h_samp_factor > MAX_SAMP_FACTOR || compptr.v_samp_factor <= 0 || compptr.v_samp_factor > MAX_SAMP_FACTOR) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_SAMPLING); } cinfo.max_h_samp_factor = Math.Max(cinfo.max_h_samp_factor, compptr.h_samp_factor); cinfo.max_v_samp_factor = Math.Max(cinfo.max_v_samp_factor, compptr.v_samp_factor); } // We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. // In the full decompressor, this will be overridden by jdmaster.cs; // but in the transcoder, jdmaster.cs is not used, so we must do it here. cinfo.min_DCT_scaled_size = cinfo.DCT_size; // Compute dimensions of components for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; compptr.DCT_scaled_size = (uint)cinfo.DCT_size; // Size in data units compptr.width_in_blocks = (uint)jdiv_round_up(cinfo.image_width * compptr.h_samp_factor, cinfo.max_h_samp_factor * cinfo.DCT_size); compptr.height_in_blocks = (uint)jdiv_round_up(cinfo.image_height * compptr.v_samp_factor, cinfo.max_v_samp_factor * cinfo.DCT_size); // downsampled_width and downsampled_height will also be overridden by // jdmaster.cs 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 = (uint)jdiv_round_up(cinfo.image_width * compptr.h_samp_factor, cinfo.max_h_samp_factor); compptr.downsampled_height = (uint)jdiv_round_up(cinfo.image_height * compptr.v_samp_factor, cinfo.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. cinfo.total_iMCU_rows = (uint)jdiv_round_up(cinfo.image_height, cinfo.max_v_samp_factor * cinfo.DCT_size); // Decide whether file contains multiple scans if (cinfo.comps_in_scan < cinfo.num_components || cinfo.process == J_CODEC_PROCESS.JPROC_PROGRESSIVE) { cinfo.inputctl.has_multiple_scans = true; } else { cinfo.inputctl.has_multiple_scans = false; } }
// Downsample pixel values of a single component. // This version handles the standard case of 2:1 horizontal and 2:1 vertical, // without smoothing. static void h2v2_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { uint output_cols=compptr.width_in_blocks*cinfo.DCT_size; // Expand input data enough to let all the output samples be generated // by the standard loop. Special-casing padded output would be more // efficient. expand_right_edge(input_data, in_row_index, cinfo.max_v_samp_factor, cinfo.image_width, output_cols*2); int inrow=(int)in_row_index; for(int outrow=0; outrow<compptr.v_samp_factor; outrow++) { byte[] outptr=output_data[out_row_index+outrow]; byte[] inptr0=input_data[inrow]; byte[] inptr1=input_data[inrow+1]; int bias=1; // bias = 1,2,1,2,... for successive samples for(uint outcol=0, ind=0; outcol<output_cols; outcol++, ind+=2) { outptr[outcol]=(byte)((inptr0[ind]+inptr0[ind+1]+inptr1[ind]+inptr1[ind+1]+bias)>>2); bias^=3; // 1=>2, 2=>1 } inrow+=2; } }
// Downsample pixel values of a single component. // This version handles the special case of a full-size component, // without smoothing. static void fullsize_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { // Copy the data jcopy_sample_rows(input_data, (int)in_row_index, output_data, (int)out_row_index, cinfo.max_v_samp_factor, cinfo.image_width); // Edge-expand expand_right_edge(output_data, out_row_index, cinfo.max_v_samp_factor, cinfo.image_width, compptr.width_in_blocks*cinfo.DCT_size); }
// Fast processing for the common case of 2:1 horizontal and 2:1 vertical. // It's still a box filter. #if! USE_UNSAFE_STUFF static void h2v2_fancy_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { if(!compptr.notFirst) { input_data[0].CopyTo(input_data[input_data.Length-1], 0); compptr.notFirst=true; } byte[][] output_data=output_data_ptr[output_data_offset]; int inrow=0, outrow=0; while(outrow<cinfo.max_v_samp_factor) { for(int v=0; v<2; v++) { // inptr0 points to nearest input row, inptr1 points to next nearest byte[] inptr0=input_data[input_data_offset+inrow]; byte[] inptr1=null; if(v==0) { inptr1=input_data[input_data_offset==0?input_data.Length-1:input_data_offset+inrow-1]; // next nearest is row above } else inptr1=input_data[(input_data_offset+inrow+1)%input_data.Length]; // next nearest is row below uint intptr_ind=0; byte[] outptr=output_data[outrow++]; uint outptr_ind=0; // Special case for first column int thiscolsum=inptr0[intptr_ind]*3+inptr1[intptr_ind]; intptr_ind++; int nextcolsum=inptr0[intptr_ind]*3+inptr1[intptr_ind]; intptr_ind++; outptr[outptr_ind++]=(byte)((thiscolsum*4+8)>>4); outptr[outptr_ind++]=(byte)((thiscolsum*3+nextcolsum+7)>>4); int lastcolsum=thiscolsum; thiscolsum=nextcolsum; for(uint colctr=compptr.downsampled_width-2; colctr>0; colctr--) { // General case: 3/4 * nearer pixel + 1/4 * further pixel in each // dimension, thus 9/16, 3/16, 3/16, 1/16 overall nextcolsum=inptr0[intptr_ind]*3+inptr1[intptr_ind]; intptr_ind++; outptr[outptr_ind++]=(byte)((thiscolsum*3+lastcolsum+8)>>4); outptr[outptr_ind++]=(byte)((thiscolsum*3+nextcolsum+7)>>4); lastcolsum=thiscolsum; thiscolsum=nextcolsum; } // Special case for last column outptr[outptr_ind++]=(byte)((thiscolsum*3+lastcolsum+8)>>4); outptr[outptr_ind++]=(byte)((thiscolsum*4+7)>>4); } inrow++; } }
// Downsample pixel values of a single component. // This version handles the standard case of 2:1 horizontal and 2:1 vertical, // with smoothing. One row of context is required. static void h2v2_smooth_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { uint output_cols=compptr.width_in_blocks*cinfo.DCT_size; // Expand input data enough to let all the output samples be generated // by the standard loop. Special-casing padded output would be more efficient. expand_right_edge(input_data, in_row_index-1, cinfo.max_v_samp_factor+2, cinfo.image_width, output_cols*2); // We don't bother to form the individual "smoothed" input pixel values; // we can directly compute the output which is the average of the four // smoothed values. Each of the four member pixels contributes a fraction // (1-8*SF) to its own smoothed image and a fraction SF to each of the three // other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final // output. The four corner-adjacent neighbor pixels contribute a fraction // SF to just one smoothed pixel, or SF/4 to the final output; while the // eight edge-adjacent neighbors contribute SF to each of two smoothed // pixels, or SF/2 overall. In order to use integer arithmetic, these // factors are scaled by 2^16 = 65536. // Also recall that SF = smoothing_factor / 1024. int memberscale=16384-cinfo.smoothing_factor*80; // scaled (1-5*SF)/4 int neighscale=cinfo.smoothing_factor*16; // scaled SF/4 int inrow=(int)in_row_index; for(int outrow=0; outrow<compptr.v_samp_factor; outrow++) { byte[] outptr=output_data[out_row_index+outrow]; byte[] inptr0=input_data[inrow]; byte[] inptr1=input_data[inrow+1]; byte[] above_ptr=input_data[inrow-1]; byte[] below_ptr=input_data[inrow+2]; // Special case for first column: pretend column -1 is same as column 0 int membersum=inptr0[0]+inptr0[1]+inptr1[0]+inptr1[1]; int neighsum=above_ptr[0]+above_ptr[1]+below_ptr[0]+below_ptr[1]+inptr0[0]+inptr0[2]+inptr1[0]+inptr1[2]; neighsum+=neighsum; neighsum+=above_ptr[0]+above_ptr[2]+below_ptr[0]+below_ptr[2]; membersum=membersum*memberscale+neighsum*neighscale; outptr[0]=(byte)((membersum+32768)>>16); int iind=2, oind=1; for(uint colctr=output_cols-2; colctr>0; colctr--) { // sum of pixels directly mapped to this output element membersum=inptr0[iind]+inptr0[iind+1]+inptr1[iind]+inptr1[iind+1]; // sum of edge-neighbor pixels neighsum=above_ptr[iind]+above_ptr[iind+1]+below_ptr[iind]+below_ptr[iind+1]+inptr0[iind-1]+inptr0[iind+2]+inptr1[iind-1]+inptr1[iind+2]; // The edge-neighbors count twice as much as corner-neighbors neighsum+=neighsum; // Add in the corner-neighbors neighsum+=above_ptr[iind-1]+above_ptr[iind+2]+below_ptr[iind-1]+below_ptr[iind+2]; // form final output scaled up by 2^16 membersum=membersum*memberscale+neighsum*neighscale; // round, descale and output it outptr[oind]=(byte)((membersum+32768)>>16); iind+=2; oind++; } // Special case for last column membersum=inptr0[iind]+inptr0[iind+1]+inptr1[iind]+inptr1[iind+1]; neighsum=above_ptr[iind]+above_ptr[iind+1]+below_ptr[iind]+below_ptr[iind+1]+inptr0[iind-1]+inptr0[iind+1]+inptr1[iind-1]+inptr1[iind+1]; neighsum+=neighsum; neighsum+=above_ptr[iind-1]+above_ptr[iind+1]+below_ptr[iind-1]+below_ptr[iind+1]; membersum=membersum*memberscale+neighsum*neighscale; outptr[oind]=(byte)((membersum+32768)>>16); inrow+=2; } }
static CONSUME_INPUT decompress_data_d_diff(jpeg_decompress cinfo, byte[][][] output_buf, int[] output_buf_ind) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; d_diff_controller diff = (d_diff_controller)losslsd.diff_private; // Loop to process as much as one whole iMCU row for (uint yoffset = diff.MCU_vert_offset; yoffset < diff.MCU_rows_per_iMCU_row; yoffset++) { // Process restart marker if needed; may have to suspend if (cinfo.restart_interval != 0) { if (diff.restart_rows_to_go == 0) { if (!process_restart_d_diff(cinfo)) { return(CONSUME_INPUT.JPEG_SUSPENDED); } } } uint MCU_col_num = diff.MCU_ctr; // index of current MCU within row // Try to fetch an MCU-row (or remaining portion of suspended MCU-row). uint MCU_count = losslsd.entropy_decode_mcus(cinfo, diff.diff_buf, yoffset, MCU_col_num, cinfo.MCUs_per_row - MCU_col_num); if (MCU_count != cinfo.MCUs_per_row - MCU_col_num) { // Suspension forced; update state counters and exit diff.MCU_vert_offset = yoffset; diff.MCU_ctr += MCU_count; return(CONSUME_INPUT.JPEG_SUSPENDED); } // Account for restart interval (no-op if not using restarts) diff.restart_rows_to_go--; // Completed an MCU row, but perhaps not an iMCU row diff.MCU_ctr = 0; } uint last_iMCU_row = cinfo.total_iMCU_rows - 1; // Undifference and scale each scanline of the disassembled MCU-row // separately. We do not process dummy samples at the end of a scanline // or dummy rows at the end of the image. for (int comp = 0; comp < cinfo.comps_in_scan; comp++) { jpeg_component_info compptr = cinfo.cur_comp_info[comp]; int ci = compptr.component_index; int stop = cinfo.input_iMCU_row == last_iMCU_row?compptr.last_row_height:compptr.v_samp_factor; for (int row = 0, prev_row = compptr.v_samp_factor - 1; row < stop; prev_row = row, row++) { losslsd.predict_undifference[ci](cinfo, ci, diff.diff_buf[ci][row], diff.undiff_buf[ci][prev_row], diff.undiff_buf[ci][row], compptr.width_in_blocks); losslsd.scaler_scale(cinfo, diff.undiff_buf[ci][row], output_buf[ci][output_buf_ind[ci] + row], compptr.width_in_blocks); } } // Completed the iMCU row, advance counters for next one. // // NB: output_data will increment output_iMCU_row. // This counter is not needed for the single-pass case // or the input side of the multi-pass case. if (++(cinfo.input_iMCU_row) < cinfo.total_iMCU_rows) { start_iMCU_row_d_diff(cinfo); return(CONSUME_INPUT.JPEG_ROW_COMPLETED); } // Completed the scan cinfo.inputctl.finish_input_pass(cinfo); return(CONSUME_INPUT.JPEG_SCAN_COMPLETED); }
static void h2v2_fancy_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { unsafe { if(!compptr.notFirst) { input_data[0].CopyTo(input_data[input_data.Length-1], 0); compptr.notFirst=true; } byte[][] output_data=output_data_ptr[output_data_offset]; int inrow=0, outrow=0; while(outrow<cinfo.max_v_samp_factor) { // inptr0 points to nearest input row fixed(byte* inptr0_=input_data[input_data_offset+inrow]) { // inptr1 points to next nearest int nextnearestrow=input_data_offset==0?input_data.Length-1:input_data_offset+inrow-1; // next nearest is row above for(int v=0; v<2; v++) { fixed(byte* inptr1_=input_data[nextnearestrow], outptr_=output_data[outrow++]) { byte* inptr0=inptr0_, inptr1=inptr1_, outptr=outptr_; // Special case for first column int thiscolsum=*(inptr0++)*3+*(inptr1++); int nextcolsum=*(inptr0++)*3+*(inptr1++); *(outptr++)=(byte)((thiscolsum*4+8)>>4); *(outptr++)=(byte)((thiscolsum*3+nextcolsum+7)>>4); int lastcolsum=thiscolsum; thiscolsum=nextcolsum; for(uint colctr=compptr.downsampled_width-2; colctr>0; colctr--) { // General case: 3/4 * nearer pixel + 1/4 * further pixel in each // dimension, thus 9/16, 3/16, 3/16, 1/16 overall nextcolsum=*(inptr0++)*3+*(inptr1++); *(outptr++)=(byte)((thiscolsum*3+lastcolsum+8)>>4); *(outptr++)=(byte)((thiscolsum*3+nextcolsum+7)>>4); lastcolsum=thiscolsum; thiscolsum=nextcolsum; } // Special case for last column *(outptr++)=(byte)((thiscolsum*3+lastcolsum+8)>>4); *(outptr++)=(byte)((thiscolsum*4+7)>>4); } nextnearestrow=(input_data_offset+inrow+1)%input_data.Length; // next nearest is row below } } inrow++; } } }
// Module initialization routine for downsampling. // Note that we must select a routine for each component. static void jinit_downsampler(jpeg_compress cinfo) { my_downsampler downsample = null; try { downsample = new my_downsampler(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.downsample = downsample; downsample.start_pass = start_pass_downsample; downsample.downsample = sep_downsample; downsample.need_context_rows = false; if (cinfo.CCIR601_sampling) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); } bool smoothok = true; // Verify we can handle the sampling factors, and set up method pointers for (int ci = 0; ci < cinfo.num_components; ci++) { jpeg_component_info compptr = cinfo.comp_info[ci]; if (compptr.h_samp_factor == cinfo.max_h_samp_factor && compptr.v_samp_factor == cinfo.max_v_samp_factor) { #if INPUT_SMOOTHING_SUPPORTED if (cinfo.smoothing_factor != 0) { downsample.methods[ci] = fullsize_smooth_downsample; downsample.need_context_rows = true; } else #endif downsample.methods[ci] = fullsize_downsample; } else if (compptr.h_samp_factor * 2 == cinfo.max_h_samp_factor && compptr.v_samp_factor == cinfo.max_v_samp_factor) { smoothok = false; downsample.methods[ci] = h2v1_downsample; } else if (compptr.h_samp_factor * 2 == cinfo.max_h_samp_factor && compptr.v_samp_factor * 2 == cinfo.max_v_samp_factor) { #if INPUT_SMOOTHING_SUPPORTED if (cinfo.smoothing_factor != 0) { downsample.methods[ci] = h2v2_smooth_downsample; downsample.need_context_rows = true; } else #endif downsample.methods[ci] = h2v2_downsample; } else if ((cinfo.max_h_samp_factor % compptr.h_samp_factor) == 0 && (cinfo.max_v_samp_factor % compptr.v_samp_factor) == 0) { smoothok = false; downsample.methods[ci] = int_downsample; } else { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); } } #if INPUT_SMOOTHING_SUPPORTED if (cinfo.smoothing_factor != 0 && !smoothok) { TRACEMS(cinfo, 0, J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL); } #endif }
// Module initialization routine for upsampling. public static void jinit_upsampler(jpeg_decompress cinfo) { my_upsampler upsample = null; bool need_buffer; int h_in_group, v_in_group, h_out_group, v_out_group; try { upsample = new my_upsampler(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.upsample = upsample; upsample.start_pass = start_pass_upsample; upsample.upsample = sep_upsample; #if UPSCALING_CONTEXT upsample.need_context_rows = false; // until we find out differently #endif if (cinfo.CCIR601_sampling) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); // this isn't supported } // jdmainct.cs doesn't support context rows when min_DCT_scaled_size = 1, // so don't ask for it. bool do_fancy = cinfo.do_fancy_upsampling && cinfo.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.num_components; ci++) { jpeg_component_info compptr = 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. h_in_group = (int)(compptr.h_samp_factor * compptr.DCT_scaled_size) / cinfo.min_DCT_scaled_size; v_in_group = (int)(compptr.v_samp_factor * compptr.DCT_scaled_size) / cinfo.min_DCT_scaled_size; h_out_group = cinfo.max_h_samp_factor; v_out_group = cinfo.max_v_samp_factor; upsample.rowgroup_height[ci] = v_in_group; // save for use later need_buffer = true; if (!compptr.component_needed) { // Don't bother to upsample an uninteresting component. upsample.methods[ci] = noop_upsample; 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. upsample.methods[ci] = fullsize_upsample; need_buffer = false; upsample.color_buf[ci] = new byte[cinfo.max_v_samp_factor][]; } else if (h_in_group * 2 == h_out_group && v_in_group == v_out_group) { // Special cases for 2h1v upsampling if (do_fancy && compptr.downsampled_width > 2) { upsample.methods[ci] = h2v1_fancy_upsample; } else { upsample.methods[ci] = h2v1_upsample; } } else if (h_in_group * 2 == h_out_group && v_in_group * 2 == v_out_group) { // Special cases for 2h2v upsampling #if UPSCALING_CONTEXT if (do_fancy && compptr.downsampled_width > 2) { upsample.methods[ci] = h2v2_fancy_upsample; upsample.need_context_rows = true; compptr.doContext = true; } else #endif upsample.methods[ci] = h2v2_upsample; } else if ((h_out_group % h_in_group) == 0 && (v_out_group % v_in_group) == 0) { // Generic integral-factors upsampling method upsample.methods[ci] = int_upsample; upsample.h_expand[ci] = (byte)(h_out_group / h_in_group); upsample.v_expand[ci] = (byte)(v_out_group / v_in_group); } else { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); } if (need_buffer) { upsample.color_buf[ci] = alloc_sarray(cinfo, (uint)jround_up((int)cinfo.output_width, (int)cinfo.max_h_samp_factor), (uint)cinfo.max_v_samp_factor); } } }
// These are the routines invoked by sep_upsample to upsample pixel values // of a single component. One row group is processed per call. // For full-size components, we just make color_buf[ci] point at the // input buffer, and thus avoid copying any data. Note that this is // safe only because sep_upsample doesn't declare the input row group // "consumed" until we are done color converting and emitting it. static void fullsize_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { for(int i=0; i<cinfo.max_v_samp_factor; i++) output_data_ptr[output_data_offset][i]=input_data[input_data_offset++]; }
// Downsample pixel values of a single component. // This version handles the standard case of 2:1 horizontal and 2:1 vertical, // with smoothing. One row of context is required. static void h2v2_smooth_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { uint output_cols = compptr.width_in_blocks * cinfo.DCT_size; // Expand input data enough to let all the output samples be generated // by the standard loop. Special-casing padded output would be more efficient. expand_right_edge(input_data, in_row_index - 1, cinfo.max_v_samp_factor + 2, cinfo.image_width, output_cols * 2); // We don't bother to form the individual "smoothed" input pixel values; // we can directly compute the output which is the average of the four // smoothed values. Each of the four member pixels contributes a fraction // (1-8*SF) to its own smoothed image and a fraction SF to each of the three // other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final // output. The four corner-adjacent neighbor pixels contribute a fraction // SF to just one smoothed pixel, or SF/4 to the final output; while the // eight edge-adjacent neighbors contribute SF to each of two smoothed // pixels, or SF/2 overall. In order to use integer arithmetic, these // factors are scaled by 2^16 = 65536. // Also recall that SF = smoothing_factor / 1024. int memberscale = 16384 - cinfo.smoothing_factor * 80; // scaled (1-5*SF)/4 int neighscale = cinfo.smoothing_factor * 16; // scaled SF/4 int inrow = (int)in_row_index; for (int outrow = 0; outrow < compptr.v_samp_factor; outrow++) { byte[] outptr = output_data[out_row_index + outrow]; byte[] inptr0 = input_data[inrow]; byte[] inptr1 = input_data[inrow + 1]; byte[] above_ptr = input_data[inrow - 1]; byte[] below_ptr = input_data[inrow + 2]; // Special case for first column: pretend column -1 is same as column 0 int membersum = inptr0[0] + inptr0[1] + inptr1[0] + inptr1[1]; int neighsum = above_ptr[0] + above_ptr[1] + below_ptr[0] + below_ptr[1] + inptr0[0] + inptr0[2] + inptr1[0] + inptr1[2]; neighsum += neighsum; neighsum += above_ptr[0] + above_ptr[2] + below_ptr[0] + below_ptr[2]; membersum = membersum * memberscale + neighsum * neighscale; outptr[0] = (byte)((membersum + 32768) >> 16); int iind = 2, oind = 1; for (uint colctr = output_cols - 2; colctr > 0; colctr--) { // sum of pixels directly mapped to this output element membersum = inptr0[iind] + inptr0[iind + 1] + inptr1[iind] + inptr1[iind + 1]; // sum of edge-neighbor pixels neighsum = above_ptr[iind] + above_ptr[iind + 1] + below_ptr[iind] + below_ptr[iind + 1] + inptr0[iind - 1] + inptr0[iind + 2] + inptr1[iind - 1] + inptr1[iind + 2]; // The edge-neighbors count twice as much as corner-neighbors neighsum += neighsum; // Add in the corner-neighbors neighsum += above_ptr[iind - 1] + above_ptr[iind + 2] + below_ptr[iind - 1] + below_ptr[iind + 2]; // form final output scaled up by 2^16 membersum = membersum * memberscale + neighsum * neighscale; // round, descale and output it outptr[oind] = (byte)((membersum + 32768) >> 16); iind += 2; oind++; } // Special case for last column membersum = inptr0[iind] + inptr0[iind + 1] + inptr1[iind] + inptr1[iind + 1]; neighsum = above_ptr[iind] + above_ptr[iind + 1] + below_ptr[iind] + below_ptr[iind + 1] + inptr0[iind - 1] + inptr0[iind + 1] + inptr1[iind - 1] + inptr1[iind + 1]; neighsum += neighsum; neighsum += above_ptr[iind - 1] + above_ptr[iind + 1] + below_ptr[iind - 1] + below_ptr[iind + 1]; membersum = membersum * memberscale + neighsum * neighscale; outptr[oind] = (byte)((membersum + 32768) >> 16); inrow += 2; } }
// This is a no-op version used for "uninteresting" components. // These components will not be referenced by color conversion. static void noop_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { output_data_ptr[output_data_offset]=null; // safety check }
// 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. static void int_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { my_upsampler upsample=(my_upsampler)cinfo.upsample; byte[][] output_data=output_data_ptr[output_data_offset]; int h_expand=upsample.h_expand[compptr.component_index]; int v_expand=upsample.v_expand[compptr.component_index]; int outrow=0; uint outend=cinfo.output_width; while(outrow<cinfo.max_v_samp_factor) { // Generate one output row with proper horizontal expansion byte[] inptr=input_data[input_data_offset]; uint inptr_ind=0; byte[] outptr=output_data[outrow]; uint outptr_ind=0; while(outptr_ind<outend) { byte invalue=inptr[inptr_ind++]; for(int h=h_expand; h>0; h--) outptr[outptr_ind++]=invalue; } // Generate any additional output rows by duplicating the first one if(v_expand>1) jcopy_sample_rows(output_data, outrow, output_data, outrow+1, v_expand-1, cinfo.output_width); input_data_offset++; outrow+=v_expand; } }
// Fast processing for the common case of 2:1 horizontal and 1:1 vertical. // It's still a box filter. static void h2v1_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { byte[][] output_data=output_data_ptr[output_data_offset]; uint outend=cinfo.output_width; for(int outrow=0; outrow<cinfo.max_v_samp_factor; outrow++) { byte[] inptr=input_data[input_data_offset++]; uint inptr_ind=0; byte[] outptr=output_data[outrow]; uint outptr_ind=0; while(outptr_ind<outend) { byte invalue=inptr[inptr_ind++]; outptr[outptr_ind++]=invalue; outptr[outptr_ind++]=invalue; } } }
// Do computations that are needed before processing a JPEG scan // cinfo.comps_in_scan and cinfo.cur_comp_info[] were set from SOS marker static void per_scan_setup_d_input(jpeg_decompress cinfo) { if (cinfo.comps_in_scan == 1) { // Noninterleaved (single-component) scan jpeg_component_info compptr = cinfo.cur_comp_info[0]; // Overall image size in MCUs cinfo.MCUs_per_row = compptr.width_in_blocks; cinfo.MCU_rows_in_scan = compptr.height_in_blocks; // For noninterleaved scan, always one block per MCU compptr.MCU_width = 1; compptr.MCU_height = 1; compptr.MCU_blocks = 1; compptr.MCU_sample_width = (int)compptr.DCT_scaled_size; compptr.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 = (int)(compptr.height_in_blocks % compptr.v_samp_factor); if (tmp == 0) { tmp = compptr.v_samp_factor; } compptr.last_row_height = tmp; // Prepare array describing MCU composition cinfo.blocks_in_MCU = 1; cinfo.MCU_membership[0] = 0; } else { // Interleaved (multi-component) scan if (cinfo.comps_in_scan <= 0 || cinfo.comps_in_scan > MAX_COMPS_IN_SCAN) { ERREXIT2(cinfo, J_MESSAGE_CODE.JERR_COMPONENT_COUNT, cinfo.comps_in_scan, MAX_COMPS_IN_SCAN); } // Overall image size in MCUs cinfo.MCUs_per_row = (uint)jdiv_round_up(cinfo.image_width, cinfo.max_h_samp_factor * cinfo.DCT_size); cinfo.MCU_rows_in_scan = (uint)jdiv_round_up(cinfo.image_height, cinfo.max_v_samp_factor * cinfo.DCT_size); cinfo.blocks_in_MCU = 0; for (int ci = 0; ci < cinfo.comps_in_scan; ci++) { jpeg_component_info compptr = cinfo.cur_comp_info[ci]; // Sampling factors give # of blocks of component in each MCU compptr.MCU_width = compptr.h_samp_factor; compptr.MCU_height = compptr.v_samp_factor; compptr.MCU_blocks = (uint)(compptr.MCU_width * compptr.MCU_height); compptr.MCU_sample_width = (int)(compptr.MCU_width * compptr.DCT_scaled_size); // Figure number of non-dummy blocks in last MCU column & row int tmp = (int)(compptr.width_in_blocks % compptr.MCU_width); if (tmp == 0) { tmp = compptr.MCU_width; } compptr.last_col_width = tmp; tmp = (int)(compptr.height_in_blocks % compptr.MCU_height); if (tmp == 0) { tmp = compptr.MCU_height; } compptr.last_row_height = tmp; // Prepare array describing MCU composition int mcublks = (int)compptr.MCU_blocks; if (cinfo.blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_BAD_MCU_SIZE); } while ((mcublks--) > 0) { cinfo.MCU_membership[cinfo.blocks_in_MCU++] = ci; } } } }
static void h2v1_fancy_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { unsafe { byte[][] output_data=output_data_ptr[output_data_offset]; for(int inrow=0; inrow<cinfo.max_v_samp_factor; inrow++) { fixed(byte* inptr_=input_data[input_data_offset++], outptr_=output_data[inrow]) { byte* inptr=inptr_; byte* outptr=outptr_; // Special case for first column int invalue=*(inptr++); *(outptr++)=(byte)invalue; *(outptr++)=(byte)((invalue*3+*inptr+2)>>2); for(uint colctr=compptr.downsampled_width-2; colctr>0; colctr--) { // General case: 3/4 * nearer pixel + 1/4 * further pixel invalue=*(inptr++)*3; *(outptr++)=(byte)((invalue+inptr[-2]+1)>>2); *(outptr++)=(byte)((invalue+*inptr+2)>>2); } // Special case for last column invalue=*inptr; *(outptr++)=(byte)((invalue*3+inptr[-1]+1)>>2); *outptr=(byte)invalue; } } } }
// Downsample pixel values of a single component. // This version handles the special case of a full-size component, // with smoothing. One row of context is required. static void fullsize_smooth_downsample(jpeg_compress cinfo, jpeg_component_info compptr, byte[][] input_data, uint in_row_index, byte[][] output_data, uint out_row_index) { uint output_cols=compptr.width_in_blocks*cinfo.DCT_size; // Expand input data enough to let all the output samples be generated // by the standard loop. Special-casing padded output would be more // efficient. expand_right_edge(input_data, in_row_index-1, cinfo.max_v_samp_factor+2, cinfo.image_width, output_cols); // Each of the eight neighbor pixels contributes a fraction SF to the // smoothed pixel, while the main pixel contributes (1-8*SF). In order // to use integer arithmetic, these factors are multiplied by 2^16 = 65536. // Also recall that SF = smoothing_factor / 1024. int memberscale=65536-cinfo.smoothing_factor*512; // scaled 1-8*SF int neighscale=cinfo.smoothing_factor*64; // scaled SF int membersum, neighsum; int colsum, lastcolsum, nextcolsum; for(int outrow=0; outrow<compptr.v_samp_factor; outrow++) { byte[] outptr=output_data[out_row_index+outrow]; byte[] inptr=input_data[in_row_index+outrow]; byte[] above_ptr=input_data[in_row_index+outrow-1]; byte[] below_ptr=input_data[in_row_index+outrow+1]; int iind=0, aind=0, bind=0, oind=0; // Special case for first column colsum=above_ptr[aind++]+below_ptr[bind++]+inptr[iind]; membersum=inptr[iind++]; nextcolsum=above_ptr[aind]+below_ptr[bind]+inptr[iind]; neighsum=colsum+(colsum-membersum)+nextcolsum; membersum=membersum*memberscale+neighsum*neighscale; outptr[oind++]=(byte)((membersum+32768)>>16); lastcolsum=colsum; colsum=nextcolsum; for(uint colctr=output_cols-2; colctr>0; colctr--) { membersum=inptr[iind++]; nextcolsum=above_ptr[aind++]+below_ptr[bind++]+inptr[iind]; neighsum=lastcolsum+(colsum-membersum)+nextcolsum; membersum=membersum*memberscale+neighsum*neighscale; outptr[oind++]=(byte)((membersum+32768)>>16); lastcolsum=colsum; colsum=nextcolsum; } // Special case for last column membersum=inptr[iind]; neighsum=lastcolsum+(colsum-membersum)+colsum; membersum=membersum*memberscale+neighsum*neighscale; outptr[oind]=(byte)((membersum+32768)>>16); } }
// Fast processing for the common case of 2:1 horizontal and 2:1 vertical. // It's still a box filter. static void h2v2_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { byte[][] output_data=output_data_ptr[output_data_offset]; int outrow=0; uint outend=cinfo.output_width; while(outrow<cinfo.max_v_samp_factor) { byte[] inptr=input_data[input_data_offset]; uint inptr_ind=0; byte[] outptr=output_data[outrow]; uint outptr_ind=0; while(outptr_ind<outend) { byte invalue=inptr[inptr_ind++]; outptr[outptr_ind++]=invalue; outptr[outptr_ind++]=invalue; } Array.Copy(output_data[outrow], output_data[outrow+1], cinfo.output_width); input_data_offset++; outrow+=2; } }
// This is a no-op version used for "uninteresting" components. // These components will not be referenced by color conversion. static void noop_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { output_data_ptr[output_data_offset] = null; // safety check }
// Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. // // The upsampling algorithm is linear interpolation between pixel centers, // also known as a "triangle filter". This is a good compromise between // speed and visual quality. The centers of the output pixels are 1/4 and 3/4 // of the way between input pixel centers. // // A note about the "bias" calculations: when rounding fractional values to // integer, we do not want to always round 0.5 up to the next integer. // If we did that, we'd introduce a noticeable bias towards larger values. // Instead, this code is arranged so that 0.5 will be rounded up or down at // alternate pixel locations (a simple ordered dither pattern). #if! USE_UNSAFE_STUFF static void h2v1_fancy_upsample(jpeg_decompress cinfo, jpeg_component_info compptr, byte[][] input_data, int input_data_offset, byte[][][] output_data_ptr, int output_data_offset) { byte[][] output_data=output_data_ptr[output_data_offset]; for(int inrow=0; inrow<cinfo.max_v_samp_factor; inrow++) { byte[] inptr=input_data[input_data_offset++]; uint inptr_ind=0; byte[] outptr=output_data[inrow]; uint outptr_ind=0; // Special case for first column int invalue=inptr[inptr_ind++]; outptr[outptr_ind++]=(byte)invalue; outptr[outptr_ind++]=(byte)((invalue*3+inptr[inptr_ind]+2)>>2); for(uint colctr=compptr.downsampled_width-2; colctr>0; colctr--) { // General case: 3/4 * nearer pixel + 1/4 * further pixel invalue=inptr[inptr_ind++]*3; outptr[outptr_ind++]=(byte)((invalue+inptr[inptr_ind-2]+1)>>2); outptr[outptr_ind++]=(byte)((invalue+inptr[inptr_ind]+2)>>2); } // Special case for last column invalue=inptr[inptr_ind]; outptr[outptr_ind++]=(byte)((invalue*3+inptr[inptr_ind-1]+1)>>2); outptr[outptr_ind]=(byte)invalue; } }