// An additional method that can be provided by data source modules is the // resync_to_restart method for error recovery in the presence of RST markers. // For the moment, this source module just uses the default resync method // provided by the JPEG library. That method assumes that no backtracking // is possible. // Terminate source --- called by jpeg_finish_decompress // after all data has been read. Often a no-op. // // NB: *not* called by jpeg_abort or jpeg_destroy; surrounding // application must deal with any cleanup that should happen even // for error exit. static void term_source(jpeg_decompress cinfo, bool readExtraBytes, out byte[] data) { if (!readExtraBytes) { data = null; // no work necessary here return; } my_source_mgr src = (my_source_mgr)cinfo.src; MemoryStream mem = new MemoryStream(); mem.Write(src.input_bytes, (int)src.next_input_byte, (int)src.bytes_in_buffer); src.next_input_byte += (int)src.bytes_in_buffer; src.bytes_in_buffer = 0; int nbytes = 0; while ((nbytes = src.infile.Read(src.buffer, 0, INPUT_BUF_SIZE)) != 0) { mem.Write(src.buffer, 0, nbytes); } data = mem.ToArray(); }
// 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++]; } }
// Prepare for input from a stdio stream. // The caller must have already opened the stream, and is responsible // for closing it after finishing decompression. public static void jpeg_stdio_src(jpeg_decompress cinfo, Stream infile) { my_source_mgr src = null; // The source object and input buffer are made permanent so that a series // of JPEG images can be read from the same file by calling jpeg_stdio_src // only before the first one. (If we discarded the buffer at the end of // one image, we'd likely lose the start of the next one.) // This makes it unsafe to use this manager and a different source // manager serially with the same JPEG object. Caveat programmer. if (cinfo.src == null) { // first time for this JPEG object? try { src = new my_source_mgr(); src.buffer = new byte[INPUT_BUF_SIZE]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.src = src; } src = (my_source_mgr)cinfo.src; src.init_source = init_source; src.fill_input_buffer = fill_input_buffer; src.skip_input_data = skip_input_data; src.resync_to_restart = jpeg_resync_to_restart; // use default method src.term_source = term_source; src.infile = infile; src.bytes_in_buffer = 0; // forces fill_input_buffer on first read src.input_bytes = null; src.next_input_byte = 0; // until buffer loaded }
// Create the color index table. static void create_colorindex(jpeg_decompress cinfo) { my_cquantizer1 cquantize = (my_cquantizer1)cinfo.cquantize; cquantize.colorindex = alloc_sarray(cinfo, (uint)(MAXJSAMPLE + 1), (uint)cinfo.out_color_components); // blksize is number of adjacent repeated entries for a component int blksize = cquantize.sv_actual; for (int i = 0; i < cinfo.out_color_components; i++) { // fill in colorindex entries for i'th color component int nci = cquantize.Ncolors[i]; // # of distinct values for this color blksize = blksize / nci; // in loop, val = index of current output value, // and k = largest j that maps to current val byte[] indexptr = cquantize.colorindex[i]; int val = 0; int k = largest_input_value(cinfo, i, 0, nci - 1); for (int j = 0; j <= MAXJSAMPLE; j++) { while (j > k) { k = largest_input_value(cinfo, i, ++val, nci - 1); // advance val if past boundary } // premultiply so that no multiplication needed in main processing indexptr[j] = (byte)(val * blksize); } } }
// Create an ordered-dither array for a component having ncolors // distinct output values. static int[,] make_odither_array(jpeg_decompress cinfo, int ncolors) { int[,] odither = null; try { odither = new int[ODITHER_SIZE, ODITHER_SIZE]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } // The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). // Hence the dither value for the matrix cell with fill order f // (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). // On 16-bit-int machine, be careful to avoid overflow. int den = 2 * ODITHER_CELLS * ((int)(ncolors - 1)); for (int j = 0; j < ODITHER_SIZE; j++) { for (int k = 0; k < ODITHER_SIZE; k++) { int num = ((int)(ODITHER_CELLS - 1 - 2 * ((int)base_dither_matrix[j, k]))) * MAXJSAMPLE; // Ensure round towards zero despite C's lack of consistency // about rounding negative values in integer division... odither[j, k] = (int)(num < 0?-((-num) / den):num / den); } } return(odither); }
// 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; } }
// Initialize the input controller module. // This is called only once, when the decompression object is created. public static void jinit_input_controller(jpeg_decompress cinfo) { my_input_controller inputctl = null; // Create subobject in pool try { inputctl = new my_input_controller(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.inputctl = inputctl; // Initialize method pointers inputctl.consume_input = consume_markers_d_input; inputctl.reset_input_controller = reset_input_controller_d_input; inputctl.start_input_pass = start_input_pass_d_input; inputctl.finish_input_pass = finish_input_pass_d_input; // Initialize state: can't use reset_input_controller since we don't // want to try to reset other modules yet. inputctl.has_multiple_scans = false; // "unknown" would be better inputctl.eoi_reached = false; inputctl.inheaders = 1; }
//#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) // Check for a restart marker & resynchronize decoder. // Returns false if must suspend. static bool process_restart_dlhuff(jpeg_decompress cinfo) { jpeg_lossless_d_codec losslsd = (jpeg_lossless_d_codec)cinfo.coef; lhuff_entropy_decoder entropy = (lhuff_entropy_decoder)losslsd.entropy_private; // Throw away any unused bits remaining in bit buffer; // include any full bytes in next_marker's count of discarded bytes cinfo.marker.discarded_bytes += (uint)(entropy.bitstate.bits_left / 8); entropy.bitstate.bits_left = 0; // Advance past the RSTn marker if (!cinfo.marker.read_restart_marker(cinfo)) { return(false); } // Reset out-of-data flag, unless read_restart_marker left us smack up // against a marker. In that case we will end up treating the next data // segment as empty, and we can avoid producing bogus output pixels by // leaving the flag set. if (cinfo.unread_marker == 0) { entropy.insufficient_data = false; } return(true); }
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; } } } }
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++; } } }
// Initialize tables for YCC->RGB colorspace conversion. // This is taken directly from jdcolor.cs; see that file for more info. static void build_ycc_rgb_table_upsample(jpeg_decompress cinfo) { my_upsampler_merge upsample = (my_upsampler_merge)cinfo.upsample; try { upsample.Cr_r_tab = new int[MAXJSAMPLE + 1]; upsample.Cb_b_tab = new int[MAXJSAMPLE + 1]; upsample.Cr_g_tab = new int[MAXJSAMPLE + 1]; upsample.Cb_g_tab = new int[MAXJSAMPLE + 1]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { // i is the actual input pixel value, in the range 0..MAXJSAMPLE // The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE // Cr=>R value is nearest int to 1.40200 * x upsample.Cr_r_tab[i] = (int)((FIX_140200 * x + ONE_HALF) >> SCALEBITS); // Cb=>B value is nearest int to 1.77200 * x upsample.Cb_b_tab[i] = (int)((FIX_177200 * x + ONE_HALF) >> SCALEBITS); // Cr=>G value is scaled-up -0.71414 * x upsample.Cr_g_tab[i] = (-FIX_071414) * x; // Cb=>G value is scaled-up -0.34414 * x // We also add in ONE_HALF so that need not do it in inner loop upsample.Cb_g_tab[i] = (-FIX_034414) * x + ONE_HALF; } }
// Reset within-iMCU-row counters for a new row (input side) static void start_iMCU_row_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; // In an interleaved scan, an MCU row is the same as an iMCU row. // In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. // But at the bottom of the image, process only what's left. if (cinfo.comps_in_scan > 1) { diff.MCU_rows_per_iMCU_row = 1; } else { if (cinfo.input_iMCU_row < (cinfo.total_iMCU_rows - 1)) { diff.MCU_rows_per_iMCU_row = (uint)cinfo.cur_comp_info[0].v_samp_factor; } else { diff.MCU_rows_per_iMCU_row = (uint)cinfo.cur_comp_info[0].last_row_height; } } diff.MCU_ctr = 0; diff.MCU_vert_offset = 0; }
// Initialize tables for YCC->RGB colorspace conversion. // This is taken directly from jdcolor.cs; see that file for more info. static void build_ycc_rgb_table_upsample(jpeg_decompress cinfo) { my_upsampler_merge upsample=(my_upsampler_merge)cinfo.upsample; try { upsample.Cr_r_tab=new int[MAXJSAMPLE+1]; upsample.Cb_b_tab=new int[MAXJSAMPLE+1]; upsample.Cr_g_tab=new int[MAXJSAMPLE+1]; upsample.Cb_g_tab=new int[MAXJSAMPLE+1]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } for(int i=0, x=-CENTERJSAMPLE; i<=MAXJSAMPLE; i++, x++) { // i is the actual input pixel value, in the range 0..MAXJSAMPLE // The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE // Cr=>R value is nearest int to 1.40200 * x upsample.Cr_r_tab[i]=(int)((FIX_140200*x+ONE_HALF)>>SCALEBITS); // Cb=>B value is nearest int to 1.77200 * x upsample.Cb_b_tab[i]=(int)((FIX_177200*x+ONE_HALF)>>SCALEBITS); // Cr=>G value is scaled-up -0.71414 * x upsample.Cr_g_tab[i]=(-FIX_071414)*x; // Cb=>G value is scaled-up -0.34414 * x // We also add in ONE_HALF so that need not do it in inner loop upsample.Cb_g_tab[i]=(-FIX_034414)*x+ONE_HALF; } }
// Fill the input buffer --- called whenever buffer is emptied. // // In typical applications, this should read fresh data into the buffer // (ignoring the current state of next_input_byte & bytes_in_buffer), // reset the pointer & count to the start of the buffer, and return true // indicating that the buffer has been reloaded. It is not necessary to // fill the buffer entirely, only to obtain at least one more byte. // // There is no such thing as an EOF return. If the end of the file has been // reached, the routine has a choice of ERREXIT() or inserting fake data into // the buffer. In most cases, generating a warning message and inserting a // fake EOI marker is the best course of action --- this will allow the // decompressor to output however much of the image is there. However, // the resulting error message is misleading if the real problem is an empty // input file, so we handle that case specially. // // In applications that need to be able to suspend compression due to input // not being available yet, a false return indicates that no more data can be // obtained right now, but more may be forthcoming later. In this situation, // the decompressor will return to its caller (with an indication of the // number of scanlines it has read, if any). The application should resume // decompression after it has loaded more data into the input buffer. Note // that there are substantial restrictions on the use of suspension --- see // the documentation. // // When suspending, the decompressor will back up to a convenient restart point // (typically the start of the current MCU). next_input_byte & bytes_in_buffer // indicate where the restart point will be if the current call returns false. // Data beyond this point must be rescanned after resumption, so move it to // the front of the buffer rather than discarding it. static bool fill_input_buffer(jpeg_decompress cinfo) { my_source_mgr src = (my_source_mgr)cinfo.src; uint nbytes = (uint)src.infile.Read(src.buffer, 0, INPUT_BUF_SIZE); if (nbytes <= 0) { if (src.start_of_file) { ERREXIT(cinfo, J_MESSAGE_CODE.JERR_INPUT_EMPTY); // Treat empty input file as fatal error } WARNMS(cinfo, J_MESSAGE_CODE.JWRN_JPEG_EOF); // Insert a fake EOI marker src.buffer[0] = (byte)0xFF; src.buffer[1] = (byte)JPEG_EOI; nbytes = 2; } src.input_bytes = src.buffer; src.next_input_byte = 0; src.bytes_in_buffer = nbytes; src.start_of_file = false; return(true); }
// Return j'th output value, where j will range from 0 to maxj // The output values must fall in 0..MAXJSAMPLE in increasing order static int output_value(jpeg_decompress cinfo, int ci, int j, int maxj) { // We always provide values 0 and MAXJSAMPLE for each component; // any additional values are equally spaced between these limits. // (Forcing the upper and lower values to the limits ensures that // dithering can't produce a color outside the selected gamut.) return((int)(((int)j * MAXJSAMPLE + maxj / 2) / maxj)); }
// 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. static void merged_2v_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_merge upsample = (my_upsampler_merge)cinfo.upsample; byte[][] work_ptrs = new byte[2][]; uint num_rows; // number of rows returned to caller if (upsample.spare_full) { // If we have a spare row saved from a previous cycle, just return it. Array.Copy(upsample.spare_row, output_buf[output_buf_offset + out_row_ctr], upsample.out_row_width); num_rows = 1; upsample.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 > 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; } // Create output pointer array for upsampler. work_ptrs[0] = output_buf[output_buf_offset + out_row_ctr]; if (num_rows > 1) { work_ptrs[1] = output_buf[output_buf_offset + out_row_ctr + 1]; } else { work_ptrs[1] = upsample.spare_row; upsample.spare_full = true; } // Now do the upsampling. upsample.upmethod(cinfo, input_buf, in_row_group_ctr, work_ptrs, 0); } // Adjust counts out_row_ctr += num_rows; upsample.rows_to_go -= num_rows; // When the buffer is emptied, declare this input row group consumed if (!upsample.spare_full) { in_row_group_ctr++; } }
const int INPUT_BUF_SIZE=4096; // choose an efficiently Read'able size // Initialize source --- called by jpeg_read_header // before any data is actually read. static void init_source(jpeg_decompress cinfo) { my_source_mgr src=(my_source_mgr)cinfo.src; // We reset the empty-input-file flag for each image, // but we don't clear the input buffer. // This is correct behavior for reading a series of images from one source. src.start_of_file=true; }
// 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; }
// 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++; } }
const int INPUT_BUF_SIZE = 4096; // choose an efficiently Read'able size // Initialize source --- called by jpeg_read_header // before any data is actually read. static void init_source(jpeg_decompress cinfo) { my_source_mgr src = (my_source_mgr)cinfo.src; // We reset the empty-input-file flag for each image, // but we don't clear the input buffer. // This is correct behavior for reading a series of images from one source. src.start_of_file = true; }
// 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; }
// Initialize for an upsampling pass. static void start_pass_merged_upsample(jpeg_decompress cinfo) { my_upsampler_merge upsample = (my_upsampler_merge)cinfo.upsample; // Mark the spare buffer empty upsample.spare_full = false; // Initialize total-height counter for detecting bottom of image upsample.rows_to_go = cinfo.output_height; }
// 1:1 vertical sampling case: much easier, never need a spare row. static void merged_1v_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_merge upsample = (my_upsampler_merge)cinfo.upsample; // Just do the upsampling. upsample.upmethod(cinfo, input_buf, in_row_group_ctr, output_buf, (int)(output_buf_offset + out_row_ctr)); // Adjust counts out_row_ctr++; in_row_group_ctr++; }
// 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. // Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. static void h2v1_merged_upsample(jpeg_decompress cinfo, byte[][][] input_buf, uint in_row_group_ctr, byte[][] output_buf, int output_buf_offset) { my_upsampler_merge upsample = (my_upsampler_merge)cinfo.upsample; // copy these pointers into registers if possible int[] Crrtab = upsample.Cr_r_tab; int[] Cbbtab = upsample.Cb_b_tab; int[] Crgtab = upsample.Cr_g_tab; int[] Cbgtab = upsample.Cb_g_tab; byte[] inptr0 = input_buf[0][in_row_group_ctr]; byte[] inptr1 = input_buf[1][in_row_group_ctr]; byte[] inptr2 = input_buf[2][in_row_group_ctr]; byte[] outptr = output_buf[output_buf_offset]; int inptr0_ind = 0, inptrX_ind = 0, outptr_ind = 0; // Loop for each pair of output pixels for (uint col = cinfo.output_width >> 1; col > 0; col--) { // Do the chroma part of the calculation int cb = inptr1[inptrX_ind]; int cr = inptr2[inptrX_ind]; inptrX_ind++; int cred = Crrtab[cr]; int cgreen = (int)((Cbgtab[cb] + Crgtab[cr]) >> SCALEBITS); int cblue = Cbbtab[cb]; // Fetch 2 Y values and emit 2 pixels int y = inptr0[inptr0_ind++]; int tmp = y + cred; outptr[outptr_ind + RGB_RED] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); tmp = y + cgreen; outptr[outptr_ind + RGB_GREEN] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); tmp = y + cblue; outptr[outptr_ind + RGB_BLUE] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); outptr_ind += RGB_PIXELSIZE; y = inptr0[inptr0_ind++]; tmp = y + cred; outptr[outptr_ind + RGB_RED] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); tmp = y + cgreen; outptr[outptr_ind + RGB_GREEN] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); tmp = y + cblue; outptr[outptr_ind + RGB_BLUE] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); outptr_ind = RGB_PIXELSIZE; } // If image width is odd, do the last output column separately if ((cinfo.output_width & 1) != 0) { int cb = inptr1[inptrX_ind]; int cr = inptr2[inptrX_ind]; int cred = Crrtab[cr]; int cgreen = (int)((Cbgtab[cb] + Crgtab[cr]) >> SCALEBITS); int cblue = Cbbtab[cb]; int y = inptr0[inptr0_ind]; int tmp = y + cred; outptr[outptr_ind + RGB_RED] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); tmp = y + cgreen; outptr[outptr_ind + RGB_GREEN] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); tmp = y + cblue; outptr[outptr_ind + RGB_BLUE] = (byte)(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp)); } }
// Create the colormap. static void create_colormap(jpeg_decompress cinfo) { my_cquantizer1 cquantize = (my_cquantizer1)cinfo.cquantize; // Select number of colors for each component int total_colors = select_ncolors(cinfo, cquantize.Ncolors); // Number of distinct output colors // Report selected color counts if (cinfo.out_color_components == 3) { TRACEMS4(cinfo, 1, J_MESSAGE_CODE.JTRC_QUANT_3_NCOLORS, total_colors, cquantize.Ncolors[0], cquantize.Ncolors[1], cquantize.Ncolors[2]); } else { TRACEMS1(cinfo, 1, J_MESSAGE_CODE.JTRC_QUANT_NCOLORS, total_colors); } // Allocate and fill in the colormap. // The colors are ordered in the map in standard row-major order, // i.e. rightmost (highest-indexed) color changes most rapidly. byte[][] colormap = alloc_sarray(cinfo, (uint)total_colors, (uint)cinfo.out_color_components); // blksize is number of adjacent repeated entries for a component // blkdist is distance between groups of identical entries for a component int blkdist = total_colors; for (int i = 0; i < cinfo.out_color_components; i++) { // fill in colormap entries for i'th color component int nci = cquantize.Ncolors[i]; // # of distinct values for this color int blksize = blkdist / nci; for (int j = 0; j < nci; j++) { // Compute j'th output value (out of nci) for component int val = output_value(cinfo, i, j, nci - 1); // Fill in all colormap entries that have this value of this component for (int ptr = j * blksize; ptr < total_colors; ptr += blkdist) { // fill in blksize entries beginning at ptr for (int k = 0; k < blksize; k++) { colormap[i][ptr + k] = (byte)val; } } } blkdist = blksize; // blksize of this color is blkdist of next } // Save the colormap in private storage, // where it will survive color quantization mode changes. cquantize.sv_colormap = colormap; cquantize.sv_actual = total_colors; }
// 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); }
// Check for a restart marker & resynchronize decoder, undifferencer. // Returns false if must suspend. static bool process_restart_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; if(!losslsd.entropy_process_restart(cinfo)) return false; losslsd.predict_process_restart(cinfo); // Reset restart counter diff.restart_rows_to_go=cinfo.restart_interval/cinfo.MCUs_per_row; return true; }
// Initialize for an input processing pass. static void start_input_pass_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; // Check that the restart interval is an integer multiple of the number // of MCU in an MCU-row. if(cinfo.restart_interval%cinfo.MCUs_per_row!=0) ERREXIT2(cinfo, J_MESSAGE_CODE.JERR_BAD_RESTART, (int)cinfo.restart_interval, (int)cinfo.MCUs_per_row); // Initialize restart counter diff.restart_rows_to_go=cinfo.restart_interval/cinfo.MCUs_per_row; cinfo.input_iMCU_row=0; start_iMCU_row_d_diff(cinfo); }
// Reset state to begin a fresh datastream. static void reset_input_controller_d_input(jpeg_decompress cinfo) { my_input_controller inputctl = (my_input_controller)cinfo.inputctl; inputctl.consume_input = consume_markers_d_input; inputctl.has_multiple_scans = false; // "unknown" would be better inputctl.eoi_reached = false; inputctl.inheaders = 1; // Reset other modules cinfo.err.reset_error_mgr(cinfo); cinfo.marker.reset_marker_reader(cinfo); // Reset progression state -- would be cleaner if entropy decoder did this cinfo.coef_bits = null; }
// 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 } }
// General case, with ordered dithering static void quantize_ord_dither(jpeg_decompress cinfo, byte[][] input_buf, uint input_row, byte[][] output_buf, uint output_row, int num_rows) { my_cquantizer1 cquantize = (my_cquantizer1)cinfo.cquantize; int row_index; // current indexes into dither matrix int nc = cinfo.out_color_components; uint width = cinfo.output_width; for (int row = 0; row < num_rows; row++) { // Initialize output values to 0 so can process components separately for (int i = 0; i < width; i++) { output_buf[output_row + row][i] = 0; } row_index = cquantize.row_index; for (int ci = 0; ci < nc; ci++) { byte[] input_ptr = input_buf[input_row + row]; int iind = ci; byte[] output_ptr = output_buf[output_row + row]; uint oind = 0; byte[] colorindex_ci = cquantize.colorindex[ci]; int[,] dither = cquantize.odither[ci]; // points to active row of dither matrix int col_index = 0; for (uint col = width; col > 0; col--) { // Form pixel value + dither, range-limit to 0..MAXJSAMPLE, // select output value, accumulate into output code for this pixel. // Range-limiting need not be done explicitly, as we have extended // the colorindex table to produce the right answers for out-of-range // inputs. The maximum dither is +- MAXJSAMPLE; this sets the // required amount of padding. int tmp = input_ptr[iind] + dither[row_index, col_index]; output_ptr[oind] += colorindex_ci[(tmp >= MAXJSAMPLE?MAXJSAMPLE:(tmp < 0?0:tmp))]; iind += nc; oind++; col_index = (col_index + 1) & ODITHER_MASK; } } // Advance row index for next row row_index = (row_index + 1) & ODITHER_MASK; cquantize.row_index = row_index; } }
// Check for a restart marker & resynchronize decoder, undifferencer. // Returns false if must suspend. static bool process_restart_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; if (!losslsd.entropy_process_restart(cinfo)) { return(false); } losslsd.predict_process_restart(cinfo); // Reset restart counter diff.restart_rows_to_go = cinfo.restart_interval / cinfo.MCUs_per_row; return(true); }
// Reset within-iMCU-row counters for a new row (input side) static void start_iMCU_row_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; // In an interleaved scan, an MCU row is the same as an iMCU row. // In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. // But at the bottom of the image, process only what's left. if(cinfo.comps_in_scan>1) diff.MCU_rows_per_iMCU_row=1; else { if(cinfo.input_iMCU_row<(cinfo.total_iMCU_rows-1)) diff.MCU_rows_per_iMCU_row=(uint)cinfo.cur_comp_info[0].v_samp_factor; else diff.MCU_rows_per_iMCU_row=(uint)cinfo.cur_comp_info[0].last_row_height; } diff.MCU_ctr=0; diff.MCU_vert_offset=0; }
// 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++; }
// Fill the input buffer --- called whenever buffer is emptied. // // In typical applications, this should read fresh data into the buffer // (ignoring the current state of next_input_byte & bytes_in_buffer), // reset the pointer & count to the start of the buffer, and return true // indicating that the buffer has been reloaded. It is not necessary to // fill the buffer entirely, only to obtain at least one more byte. // // There is no such thing as an EOF return. If the end of the file has been // reached, the routine has a choice of ERREXIT() or inserting fake data into // the buffer. In most cases, generating a warning message and inserting a // fake EOI marker is the best course of action --- this will allow the // decompressor to output however much of the image is there. However, // the resulting error message is misleading if the real problem is an empty // input file, so we handle that case specially. // // In applications that need to be able to suspend compression due to input // not being available yet, a false return indicates that no more data can be // obtained right now, but more may be forthcoming later. In this situation, // the decompressor will return to its caller (with an indication of the // number of scanlines it has read, if any). The application should resume // decompression after it has loaded more data into the input buffer. Note // that there are substantial restrictions on the use of suspension --- see // the documentation. // // When suspending, the decompressor will back up to a convenient restart point // (typically the start of the current MCU). next_input_byte & bytes_in_buffer // indicate where the restart point will be if the current call returns false. // Data beyond this point must be rescanned after resumption, so move it to // the front of the buffer rather than discarding it. static bool fill_input_buffer(jpeg_decompress cinfo) { my_source_mgr src=(my_source_mgr)cinfo.src; uint nbytes=(uint)src.infile.Read(src.buffer, 0, INPUT_BUF_SIZE); if(nbytes<=0) { if(src.start_of_file) ERREXIT(cinfo, J_MESSAGE_CODE.JERR_INPUT_EMPTY); // Treat empty input file as fatal error WARNMS(cinfo, J_MESSAGE_CODE.JWRN_JPEG_EOF); // Insert a fake EOI marker src.buffer[0]=(byte)0xFF; src.buffer[1]=(byte)JPEG_EOI; nbytes=2; } src.input_bytes=src.buffer; src.next_input_byte=0; src.bytes_in_buffer=nbytes; src.start_of_file=false; return true; }
// Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. static void h2v2_merged_upsample(jpeg_decompress cinfo, byte[][][] input_buf, uint in_row_group_ctr, byte[][] output_buf, int output_buf_offset) { my_upsampler_merge upsample=(my_upsampler_merge)cinfo.upsample; // copy these pointers into registers if possible int[] Crrtab=upsample.Cr_r_tab; int[] Cbbtab=upsample.Cb_b_tab; int[] Crgtab=upsample.Cr_g_tab; int[] Cbgtab=upsample.Cb_g_tab; byte[] inptr00=input_buf[0][in_row_group_ctr*2]; byte[] inptr01=input_buf[0][in_row_group_ctr*2+1]; byte[] inptr1=input_buf[1][in_row_group_ctr]; byte[] inptr2=input_buf[2][in_row_group_ctr]; byte[] outptr0=output_buf[output_buf_offset]; byte[] outptr1=output_buf[output_buf_offset+1]; int inptr0_ind=0, inptrX_ind=0, outptr_ind=0; // Loop for each group of output pixels for(uint col=cinfo.output_width>>1; col>0; col--) { // Do the chroma part of the calculation int cb=inptr1[inptrX_ind]; int cr=inptr2[inptrX_ind]; inptrX_ind++; int cred=Crrtab[cr]; int cgreen=(int)((Cbgtab[cb]+Crgtab[cr])>>SCALEBITS); int cblue=Cbbtab[cb]; // Fetch 4 Y values and emit 4 pixels int y=inptr00[inptr0_ind]; int tmp=y+cred; outptr0[outptr_ind+RGB_RED]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cgreen; outptr0[outptr_ind+RGB_GREEN]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cblue; outptr0[outptr_ind+RGB_BLUE]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); y=inptr01[inptr0_ind]; tmp=y+cred; outptr1[outptr_ind+RGB_RED]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cgreen; outptr1[outptr_ind+RGB_GREEN]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cblue; outptr1[outptr_ind+RGB_BLUE]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); inptr0_ind++; outptr_ind+=RGB_PIXELSIZE; y=inptr00[inptr0_ind]; tmp=y+cred; outptr0[outptr_ind+RGB_RED]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cgreen; outptr0[outptr_ind+RGB_GREEN]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cblue; outptr0[outptr_ind+RGB_BLUE]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); y=inptr01[inptr0_ind]; tmp=y+cred; outptr1[outptr_ind+RGB_RED]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cgreen; outptr1[outptr_ind+RGB_GREEN]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cblue; outptr1[outptr_ind+RGB_BLUE]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); inptr0_ind++; outptr_ind+=RGB_PIXELSIZE; } // If image width is odd, do the last output column separately if((cinfo.output_width&1)!=0) { int cb=inptr1[inptrX_ind]; int cr=inptr2[inptrX_ind]; int cred=Crrtab[cr]; int cgreen=(int)((Cbgtab[cb]+Crgtab[cr])>>SCALEBITS); int cblue=Cbbtab[cb]; int y=inptr00[inptr0_ind]; int tmp=y+cred; outptr0[outptr_ind+RGB_RED]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cgreen; outptr0[outptr_ind+RGB_GREEN]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cblue; outptr0[outptr_ind+RGB_BLUE]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); y=inptr01[inptr0_ind]; tmp=y+cred; outptr1[outptr_ind+RGB_RED]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cgreen; outptr1[outptr_ind+RGB_GREEN]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); tmp=y+cblue; outptr1[outptr_ind+RGB_BLUE]=(byte)(tmp>=MAXJSAMPLE?MAXJSAMPLE:(tmp<0?0:tmp)); } }
// 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; } }
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; }
// 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; }
// 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); } } }
// 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; } }
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++; } } }
// Dummy consume-input routine for single-pass operation. static CONSUME_INPUT dummy_consume_data_d_diff(jpeg_decompress cinfo) { return CONSUME_INPUT.JPEG_SUSPENDED; // Always indicate nothing was done }
// 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; } } }
// Module initialization routine for merged upsampling/color conversion. // // NB: this is called under the conditions determined by use_merged_upsample() // in jdmaster.cs. That routine MUST correspond to the actual capabilities // of this module; no safety checks are made here. public static void jinit_merged_upsampler(jpeg_decompress cinfo) { my_upsampler_merge upsample=null; try { upsample=new my_upsampler_merge(); } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.upsample=upsample; upsample.start_pass=start_pass_merged_upsample; #if UPSCALING_CONTEXT upsample.need_context_rows=false; #endif upsample.out_row_width=(uint)(cinfo.output_width*cinfo.out_color_components); if(cinfo.max_v_samp_factor==2) { upsample.upsample=merged_2v_upsample; upsample.upmethod=h2v2_merged_upsample; // Allocate a spare row buffer try { upsample.spare_row=new byte[upsample.out_row_width]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } } else { upsample.upsample=merged_1v_upsample; upsample.upmethod=h2v1_merged_upsample; // No spare row needed upsample.spare_row=null; } build_ycc_rgb_table_upsample(cinfo); }
// Skip data --- used to skip over a potentially large amount of // uninteresting data (such as an APPn marker). // // Writers of suspendable-input applications must note that skip_input_data // is not granted the right to give a suspension return. If the skip extends // beyond the data currently in the buffer, the buffer can be marked empty so // that the next read will cause a fill_input_buffer call that can suspend. // Arranging for additional bytes to be discarded before reloading the input // buffer is the application writer's problem. static void skip_input_data(jpeg_decompress cinfo, int num_bytes) { my_source_mgr src=(my_source_mgr)cinfo.src; // Just a dumb implementation for now. Could use Seek() except // it doesn't work on pipes. Not clear that being smart is worth // any trouble anyway --- large skips are infrequent. if(num_bytes>0) { while(num_bytes>(int)src.bytes_in_buffer) { num_bytes-=(int)src.bytes_in_buffer; fill_input_buffer(cinfo); // note we assume that fill_input_buffer will never return false, // so suspension need not be handled. } src.next_input_byte+=num_bytes; src.bytes_in_buffer-=(uint)num_bytes; } }
// Initialize for an output processing pass. static void start_output_pass_d_diff(jpeg_decompress cinfo) { cinfo.output_iMCU_row=0; }
// An additional method that can be provided by data source modules is the // resync_to_restart method for error recovery in the presence of RST markers. // For the moment, this source module just uses the default resync method // provided by the JPEG library. That method assumes that no backtracking // is possible. // Terminate source --- called by jpeg_finish_decompress // after all data has been read. Often a no-op. // // NB: *not* called by jpeg_abort or jpeg_destroy; surrounding // application must deal with any cleanup that should happen even // for error exit. static void term_source(jpeg_decompress cinfo, bool readExtraBytes, out byte[] data) { if(!readExtraBytes) { data=null; // no work necessary here return; } my_source_mgr src=(my_source_mgr)cinfo.src; MemoryStream mem=new MemoryStream(); mem.Write(src.input_bytes, (int)src.next_input_byte, (int)src.bytes_in_buffer); src.next_input_byte+=(int)src.bytes_in_buffer; src.bytes_in_buffer=0; int nbytes=0; while((nbytes=src.infile.Read(src.buffer, 0, INPUT_BUF_SIZE))!=0) mem.Write(src.buffer, 0, nbytes); data=mem.ToArray(); }
// 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 } }
// Prepare for input from a stdio stream. // The caller must have already opened the stream, and is responsible // for closing it after finishing decompression. public static void jpeg_stdio_src(jpeg_decompress cinfo, Stream infile) { my_source_mgr src=null; // The source object and input buffer are made permanent so that a series // of JPEG images can be read from the same file by calling jpeg_stdio_src // only before the first one. (If we discarded the buffer at the end of // one image, we'd likely lose the start of the next one.) // This makes it unsafe to use this manager and a different source // manager serially with the same JPEG object. Caveat programmer. if(cinfo.src==null) { // first time for this JPEG object? try { src=new my_source_mgr(); src.buffer=new byte[INPUT_BUF_SIZE]; } catch { ERREXIT1(cinfo, J_MESSAGE_CODE.JERR_OUT_OF_MEMORY, 4); } cinfo.src=src; } src=(my_source_mgr)cinfo.src; src.init_source=init_source; src.fill_input_buffer=fill_input_buffer; src.skip_input_data=skip_input_data; src.resync_to_restart=jpeg_resync_to_restart; // use default method src.term_source=term_source; src.infile=infile; src.bytes_in_buffer=0; // forces fill_input_buffer on first read src.input_bytes=null; src.next_input_byte=0; // until buffer loaded }
// 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); }
// 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 }
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; } } } }
// 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; } }
// 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++; } }
// Initialize for an upsampling pass. static void start_pass_merged_upsample(jpeg_decompress cinfo) { my_upsampler_merge upsample=(my_upsampler_merge)cinfo.upsample; // Mark the spare buffer empty upsample.spare_full=false; // Initialize total-height counter for detecting bottom of image upsample.rows_to_go=cinfo.output_height; }