// Initialize for an upsampling pass. static void start_pass_upsample(jpeg_decompress cinfo) { my_upsampler upsample = (my_upsampler)cinfo.upsample; // Mark the conversion buffer empty upsample.next_row_out = cinfo.max_v_samp_factor; // Initialize total-height counter for detecting bottom of image upsample.rows_to_go = cinfo.output_height; }
// 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++; } }
// 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; } }
// 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); } } }
// 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); } } }