private int m_next_row; /* index of next row to fill/empty in strip */ /// <summary> /// Initialize postprocessing controller. /// </summary> public jpeg_d_post_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) { m_cinfo = cinfo; /* Create the quantization buffer, if needed */ if (cinfo.m_quantize_colors) { /* The buffer strip height is max_v_samp_factor, which is typically * an efficient number of rows for upsampling to return. * (In the presence of output rescaling, we might want to be smarter?) */ m_strip_height = cinfo.m_max_v_samp_factor; if (need_full_buffer) { /* Two-pass color quantization: need full-image storage. */ /* We round up the number of rows to a multiple of the strip height. */ m_whole_image = jpeg_common_struct.CreateSamplesArray( cinfo.m_output_width * cinfo.m_out_color_components, JpegUtils.jround_up(cinfo.m_output_height, m_strip_height)); m_whole_image.ErrorProcessor = cinfo; } else { /* One-pass color quantization: just make a strip buffer. */ m_buffer = jpeg_common_struct.AllocJpegSamples( cinfo.m_output_width * cinfo.m_out_color_components, m_strip_height); } } }
public jpeg_d_coef_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) { m_cinfo = cinfo; /* Create the coefficient buffer. */ if (need_full_buffer) { /* Allocate a full-image virtual array for each component, */ /* padded to a multiple of samp_factor DCT blocks in each direction. */ /* Note we ask for a pre-zeroed array. */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( JpegUtils.jround_up(cinfo.Comp_info[ci].Width_in_blocks, cinfo.Comp_info[ci].H_samp_factor), JpegUtils.jround_up(cinfo.Comp_info[ci].height_in_blocks, cinfo.Comp_info[ci].V_samp_factor)); m_whole_image[ci].ErrorProcessor = cinfo; } m_useDummyConsumeData = false; m_decompressor = DecompressorType.Ordinary; m_coef_arrays = m_whole_image; /* link to virtual arrays */ } else { /* We only need a single-MCU buffer. */ for (int i = 0; i < JpegConstants.D_MAX_BLOCKS_IN_MCU; i++) { m_MCU_buffer[i] = new JBLOCK(); } m_useDummyConsumeData = true; m_decompressor = DecompressorType.OnePass; m_coef_arrays = null; /* flag for no virtual arrays */ } }
// the algorithm flow is based on: // https://bitmiracle.github.io/libjpeg.net/help/articles/KB/decompression-details.html private DecompressionResult DecompressJpgToRaw(byte[] image) { var cinfo = new jpeg_decompress_struct(new jpeg_error_mgr()); using (var memoryStream = new MemoryStream(image)) { cinfo.jpeg_stdio_src(memoryStream); cinfo.jpeg_read_header(true); cinfo.Out_color_space = J_COLOR_SPACE.JCS_RGB; cinfo.jpeg_start_decompress(); // there are 3 components: R, G, B var rowStride = 3 * cinfo.Output_width; var result = new byte[rowStride * cinfo.Output_height]; var resultOffset = 0; var buffer = new byte[1][]; buffer[0] = new byte[rowStride]; while (cinfo.Output_scanline < cinfo.Output_height) { var ct = cinfo.jpeg_read_scanlines(buffer, 1); if (ct > 0) { Array.Copy(buffer[0], 0, result, resultOffset, buffer[0].Length); resultOffset += buffer[0].Length; } } cinfo.jpeg_finish_decompress(); return(new DecompressionResult(result, cinfo.Output_width, cinfo.Output_height)); } }
private int m_rows_to_go; /* counts rows remaining in image */ public my_merged_upsampler(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; m_need_context_rows = false; m_out_row_width = cinfo.m_output_width * cinfo.m_out_color_components; if (cinfo.m_max_v_samp_factor == 2) { m_use_2v_upsample = true; /* Allocate a spare row buffer */ m_spare_row = new byte[m_out_row_width]; } else { m_use_2v_upsample = false; } if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_BG_YCC) { build_bg_ycc_rgb_table(); } else { build_ycc_rgb_table(); } }
// Fill an input buffer from a stream. private static Int fill_input_buffer(ref jpeg_decompress_struct cinfo) { int len; StreamState state = GetStreamState(ref cinfo); if (!(state.sawEOF)) { len = state.stream.Read (state.buffer, 0, state.buffer.Length); if (len > 0) { Marshal.Copy(state.buffer, 0, state.buf, len); cinfo.src->next_input_byte = state.buf; cinfo.src->bytes_in_buffer = (size_t)len; return((Int)1); } state.sawEOF = true; } // Insert an EOI marker to indicate end of stream to "libjpeg". Marshal.WriteByte(state.buf, 0, (byte)0xFF); Marshal.WriteByte(state.buf, 1, (byte)0xD9); cinfo.src->next_input_byte = state.buf; cinfo.src->bytes_in_buffer = (size_t)2; return((Int)1); }
private int m_iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ public jpeg_d_main_controller(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Allocate the workspace. * ngroups is the number of row groups we need. */ int ngroups = cinfo.m_min_DCT_scaled_size; if (cinfo.m_upsample.NeedContextRows()) { if (cinfo.m_min_DCT_scaled_size < 2) /* unsupported, see comments above */ { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); } alloc_funny_pointers(); /* Alloc space for xbuffer[] lists */ ngroups = cinfo.m_min_DCT_scaled_size + 2; } for (int ci = 0; ci < cinfo.m_num_components; ci++) { /* height of a row group of component */ int rgroup = (cinfo.Comp_info[ci].V_samp_factor * cinfo.Comp_info[ci].DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( cinfo.Comp_info[ci].Width_in_blocks * cinfo.Comp_info[ci].DCT_scaled_size, rgroup * ngroups); } }
private d_derived_tbl m_ac_derived_tbl; /* active table during an AC scan */ public phuff_entropy_decoder(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Mark derived tables unallocated */ for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) { m_derived_tbls[i] = null; } /* Create progression status table */ cinfo.m_coef_bits = new int[cinfo.m_num_components][]; for (int i = 0; i < cinfo.m_num_components; i++) { cinfo.m_coef_bits[i] = new int[JpegConstants.DCTSIZE2]; } for (int ci = 0; ci < cinfo.m_num_components; ci++) { for (int i = 0; i < JpegConstants.DCTSIZE2; i++) { cinfo.m_coef_bits[ci][i] = -1; } } }
public jpeg_d_coef_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) { m_cinfo = cinfo; /* Create the coefficient buffer. */ if (need_full_buffer) { /* Allocate a full-image virtual array for each component, */ /* padded to a multiple of samp_factor DCT blocks in each direction. */ /* Note we ask for a pre-zeroed array. */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( JpegUtils.jround_up(cinfo.Comp_info[ci].Width_in_blocks, cinfo.Comp_info[ci].H_samp_factor), JpegUtils.jround_up(cinfo.Comp_info[ci].height_in_blocks, cinfo.Comp_info[ci].V_samp_factor)); m_whole_image[ci].ErrorProcessor = cinfo; } m_useDummyConsumeData = false; m_decompressor = DecompressorType.Ordinary; m_coef_arrays = m_whole_image; /* link to virtual arrays */ } else { /* We only need a single-MCU buffer. */ for (int i = 0; i < JpegConstants.D_MAX_BLOCKS_IN_MCU; i++) m_MCU_buffer[i] = new JBLOCK(); m_useDummyConsumeData = true; m_decompressor = DecompressorType.OnePass; m_coef_arrays = null; /* flag for no virtual arrays */ } }
private bool m_on_odd_row; /* flag to remember which row we are on */ /// <summary> /// Module initialization routine for 1-pass color quantization. /// </summary> /// <param name="cinfo">The cinfo.</param> public my_1pass_cquantizer(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; m_fserrors[0] = null; /* Flag FS workspace not allocated */ m_odither[0] = null; /* Also flag odither arrays not allocated */ /* Make sure my internal arrays won't overflow */ if (cinfo.m_out_color_components > MAX_Q_COMPS) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_COMPONENTS, MAX_Q_COMPS); } /* Make sure colormap indexes can be represented by JSAMPLEs */ if (cinfo.m_desired_number_of_colors > (JpegConstants.MAXJSAMPLE + 1)) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, JpegConstants.MAXJSAMPLE + 1); } /* Create the colormap and color index table. */ create_colormap(); create_colorindex(); /* Allocate Floyd-Steinberg workspace now if requested. * We do this now since it is FAR storage and may affect the memory * manager's space calculations. If the user changes to FS dither * mode in a later pass, we will allocate the space then, and will * possibly overrun the max_memory_to_use setting. */ if (cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) { alloc_fs_workspace(); } }
private void Enc(string inputPath, string outputPath, byte[] data) { var input = new jpeg_decompress_struct(); using (var fileStream = new FileStream(inputPath, FileMode.Open)) { input.jpeg_stdio_src(fileStream); input.jpeg_read_header(false); var coefficients = input.jpeg_read_coefficients(); var channels = coefficients.Take(3).Select(JpegHelper.GetBuffer).ToArray(); Encrypt(channels, data); var output = new jpeg_compress_struct(); using (var outfile = new FileStream(outputPath, FileMode.Create)) { output.jpeg_stdio_dest(outfile); input.jpeg_copy_critical_parameters(output); output.jpeg_write_coefficients(coefficients); output.jpeg_finish_compress(); } } }
/// <summary> /// Get the Huffman tables using reflection for a specific component (AC, DC) /// </summary> /// <returns></returns> public static HuffmanTable[] GetHuffmanTables(string JpegFilePath) { FileStream objFileStreamMegaMap = File.Create(JpegFilePath); jpeg_decompress_struct jpds = new jpeg_decompress_struct(); jpeg_compress_struct jpcs = new jpeg_compress_struct(); jpcs.jpeg_stdio_dest(objFileStreamMegaMap); jpds.jpeg_copy_critical_parameters(jpcs); jpds.jpeg_finish_decompress(); objFileStreamMegaMap.Close(); // DC Huffman tables JHUFF_TBL[] jpeg_dc_huffman_tables = jpcs.Dc_huff_tbl_ptrs; var comp = HuffmanTable.EnumComponent.DC; var htdc = getHTables(jpeg_dc_huffman_tables, comp); // AC Huffman tables JHUFF_TBL[] jpeg_ac_huffman_tables = jpcs.Ac_huff_tbl_ptrs; comp = HuffmanTable.EnumComponent.AC; var htac = getHTables(jpeg_ac_huffman_tables, comp); HuffmanTable[] hts = new HuffmanTable[htdc.Length + htac.Length]; Array.Copy(htdc, hts, htdc.Length); Array.Copy(htac, 0, hts, htdc.Length, htac.Length); return(hts); }
/// <summary> /// The DCT coefficients are returned in a structure where the different components are separated in 2D arrays od shorts[][]. /// The first dimension of the array contains the Blocks and the second dimension the 64 values for that block. /// </summary> /// <returns></returns> public static DCTCoefficients GetDctCoefficients(string JpegFilePath) { jpeg_decompress_struct jpds = new jpeg_decompress_struct(); FileStream fileStreamImg = new FileStream(JpegFilePath, FileMode.Open, FileAccess.Read); jpds.jpeg_stdio_src(fileStreamImg); jpds.jpeg_read_header(true); // DCT coefficients var jBlock = jpds.jpeg_read_coefficients(); // Initialize the list for all the dct var dctFull = new List <short>(); var dctFullDc = new List <short>(); var dctFullAc = new List <short>(); // Get coeffs and fill the list _dctFull List <short[]> dctCr = readDctCoeffs(EnumStegoChannel.Cr, jBlock, dctFull, dctFullDc, dctFullAc); List <short[]> dctCb = readDctCoeffs(EnumStegoChannel.Cb, jBlock, dctFull, dctFullDc, dctFullAc); List <short[]> dctY = readDctCoeffs(EnumStegoChannel.Y, jBlock, dctFull, dctFullDc, dctFullAc); // Close decompress and filestream jpds.jpeg_finish_decompress(); fileStreamImg.Close(); // Return DCT coefficients DCTCoefficients dctCoeffs = new DCTCoefficients { DctY = dctY.ToArray(), DctCb = dctCb.ToArray(), DctCr = dctCr.ToArray(), DctFull = dctFull.ToArray(), DctFullDc = dctFullDc.ToArray(), DctFullAc = dctFullAc.ToArray() }; return(dctCoeffs); }
/// <summary> /// Get the Quantization tables using reflection per component /// </summary> /// <returns></returns> public static short[][] GetQuantizationTables(string JpegFilePath) { FileStream objFileStreamMegaMap = File.Create(JpegFilePath); jpeg_decompress_struct jpds = new jpeg_decompress_struct(); jpeg_compress_struct jpcs = new jpeg_compress_struct(); jpcs.jpeg_stdio_dest(objFileStreamMegaMap); jpds.jpeg_copy_critical_parameters(jpcs); jpds.jpeg_finish_decompress(); objFileStreamMegaMap.Close(); JQUANT_TBL[] jpeg_quant_tables = jpcs.Quant_tbl_ptrs; int qCount = jpeg_quant_tables.Length; short[][] qt = new short[qCount][]; BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; int idx = 0; while (null != jpeg_quant_tables[idx]) { Type type = jpeg_quant_tables[idx].GetType(); object instance = jpeg_quant_tables[idx]; FieldInfo field = type.GetField("quantval", bindFlags); qt[idx] = field.GetValue(instance) as short[]; ++idx; } var qTables = qt.Where(tab => null != tab).ToArray(); return(qTables); }
/// <summary> /// This is the default resync_to_restart method for data source /// managers to use if they don't have any better approach. /// </summary> /// <param name="cinfo">An instance of <see cref="jpeg_decompress_struct"/></param> /// <param name="desired">The desired</param> /// <returns><c>false</c> if suspension is required.</returns> /// <remarks>That method assumes that no backtracking is possible. /// Some data source managers may be able to back up, or may have /// additional knowledge about the data which permits a more /// intelligent recovery strategy; such managers would /// presumably supply their own resync method.<br/><br/> /// read_restart_marker calls resync_to_restart if it finds a marker other than /// the restart marker it was expecting. (This code is *not* used unless /// a nonzero restart interval has been declared.) cinfo.unread_marker is /// the marker code actually found (might be anything, except 0 or FF). /// The desired restart marker number (0..7) is passed as a parameter.<br/><br/> /// This routine is supposed to apply whatever error recovery strategy seems /// appropriate in order to position the input stream to the next data segment. /// Note that cinfo.unread_marker is treated as a marker appearing before /// the current data-source input point; usually it should be reset to zero /// before returning.<br/><br/> /// This implementation is substantially constrained by wanting to treat the /// input as a data stream; this means we can't back up. Therefore, we have /// only the following actions to work with:<br/> /// 1. Simply discard the marker and let the entropy decoder resume at next /// byte of file.<br/> /// 2. Read forward until we find another marker, discarding intervening /// data. (In theory we could look ahead within the current bufferload, /// without having to discard data if we don't find the desired marker. /// This idea is not implemented here, in part because it makes behavior /// dependent on buffer size and chance buffer-boundary positions.)<br/> /// 3. Leave the marker unread (by failing to zero cinfo.unread_marker). /// This will cause the entropy decoder to process an empty data segment, /// inserting dummy zeroes, and then we will reprocess the marker.<br/> /// #2 is appropriate if we think the desired marker lies ahead, while #3 is /// appropriate if the found marker is a future restart marker (indicating /// that we have missed the desired restart marker, probably because it got /// corrupted).<br/> /// We apply #2 or #3 if the found marker is a restart marker no more than /// two counts behind or ahead of the expected one. We also apply #2 if the /// found marker is not a legal JPEG marker code (it's certainly bogus data). /// If the found marker is a restart marker more than 2 counts away, we do #1 /// (too much risk that the marker is erroneous; with luck we will be able to /// resync at some future point).<br/> /// For any valid non-restart JPEG marker, we apply #3. This keeps us from /// overrunning the end of a scan. An implementation limited to single-scan /// files might find it better to apply #2 for markers other than EOI, since /// any other marker would have to be bogus data in that case.</remarks> public override bool resync_to_restart(jpeg_decompress_struct cinfo, int desired) { Tiff tif = m_sp.GetTiff(); Tiff.ErrorExt(tif, tif.m_clientdata, "LibJpeg", "Unexpected error"); return(false); }
public void TestMarkerList() { jpeg_decompress_struct cinfo = new jpeg_decompress_struct(); using (FileStream input = new FileStream(Tester.MapOpenPath("PARROTS.JPG"), FileMode.Open)) { /* Specify data source for decompression */ cinfo.jpeg_stdio_src(input); const int markerDataLengthLimit = 1000; cinfo.jpeg_save_markers((int)JPEG_MARKER.COM, markerDataLengthLimit); cinfo.jpeg_save_markers((int)JPEG_MARKER.APP0, markerDataLengthLimit); /* Read file header, set default decompression parameters */ cinfo.jpeg_read_header(true); Assert.AreEqual(cinfo.Marker_list.Count, 3); int[] expectedMarkerType = { (int)JPEG_MARKER.APP0, (int)JPEG_MARKER.APP0, (int)JPEG_MARKER.COM }; int[] expectedMarkerOriginalLength = { 14, 3072, 10 }; for (int i = 0; i < cinfo.Marker_list.Count; ++i) { jpeg_marker_struct marker = cinfo.Marker_list[i]; Assert.IsNotNull(marker); Assert.AreEqual(marker.Marker, expectedMarkerType[i]); Assert.AreEqual(marker.OriginalLength, expectedMarkerOriginalLength[i]); Assert.LessOrEqual(marker.Data.Length, markerDataLengthLimit); } } }
private savable_state m_saved = new savable_state(); /* Other state at start of MCU */ #endregion Fields #region Constructors public huff_entropy_decoder(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Mark tables unallocated */ for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) m_dc_derived_tbls[i] = m_ac_derived_tbls[i] = null; }
private bool m_eoi_reached; /* True when EOI has been consumed */ /// <summary> /// Initialize the input controller module. /// This is called only once, when the decompression object is created. /// </summary> public jpeg_input_controller(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Initialize state: can't use reset_input_controller since we don't * want to try to reset other modules yet. */ m_inheaders = true; }
private int cur_output_row; /* next row# to write to virtual array */ public bmp_dest_struct(jpeg_decompress_struct cinfo, bool is_os2) { this.cinfo = cinfo; this.is_os2 = is_os2; if (cinfo.Out_color_space == J_COLOR_SPACE.JCS_GRAYSCALE) { m_putGrayRows = true; } else if (cinfo.Out_color_space == J_COLOR_SPACE.JCS_RGB) { if (cinfo.Quantize_colors) { m_putGrayRows = true; } else { m_putGrayRows = false; } } else { cinfo.ERREXIT((int)ADDON_MESSAGE_CODE.JERR_BMP_COLORSPACE); } /* Calculate output image dimensions so we can allocate space */ cinfo.jpeg_calc_output_dimensions(); /* Determine width of rows in the BMP file (padded to 4-byte boundary). */ row_width = cinfo.Output_width * cinfo.Output_components; data_width = row_width; while ((row_width & 3) != 0) { row_width++; } pad_bytes = row_width - data_width; /* Allocate space for inversion array, prepare for write pass */ whole_image = jpeg_common_struct.CreateSamplesArray(row_width, cinfo.Output_height); whole_image.ErrorProcessor = cinfo; cur_output_row = 0; if (cinfo.Progress != null) { cdjpeg_progress_mgr progress = cinfo.Progress as cdjpeg_progress_mgr; if (progress != null) { /* count file input as separate pass */ progress.total_extra_passes++; } } /* Create decompressor output buffer. */ buffer = jpeg_common_struct.AllocJpegSamples(row_width, 1); buffer_height = 1; }
private bool m_start_of_file; /* have we gotten any data yet? */ public VirtualStreamSourceManager(jpeg_decompress_struct cinfo, Stream src) { m_cinfo = cinfo; m_buffer = new byte[InputBufferSize]; m_infile = src; m_infile.Seek(0, SeekOrigin.Begin); initInternalBuffer(null, 0); }
public huff_entropy_decoder(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Mark tables unallocated */ for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) { m_dc_derived_tbls[i] = m_ac_derived_tbls[i] = null; } }
// Free a source manager. public static void FreeSourceManager(ref jpeg_decompress_struct cinfo) { GCHandle handle = (GCHandle)(cinfo.client_data); StreamState state = (StreamState)(handle.Target); Marshal.FreeHGlobal(state.buf); handle.Free(); Marshal.FreeHGlobal((IntPtr)(cinfo.src)); cinfo.client_data = IntPtr.Zero; cinfo.src = null; }
/// <summary> /// Read next byte /// </summary> static int jpeg_getc(jpeg_decompress_struct decompressor) { int v; if (!decompressor.Src.GetByte(out v)) { decompressor.ERREXIT(J_MESSAGE_CODE.JERR_CANT_SUSPEND); } return(v); }
private byte[] Dec(string inputPath, int len) { var input = new jpeg_decompress_struct(); input.jpeg_stdio_src(new FileStream(inputPath, FileMode.Open)); input.jpeg_read_header(false); var coefficients = input.jpeg_read_coefficients(); var channels = coefficients.Take(3).Select(JpegHelper.GetBuffer).ToArray(); return(Decrypt(channels, len)); }
static void applyOptions(jpeg_decompress_struct decompressor, DecompressOptions options) { Debug.Assert(decompressor != null); Debug.Assert(options != null); if (options.QuantizeColors) { decompressor.Quantize_colors = true; decompressor.Desired_number_of_colors = options.DesiredNumberOfColors; } decompressor.Dct_method = options.DCTMethod; decompressor.Dither_mode = options.DitherMode; if (options.Debug) { decompressor.Err.Trace_level = 1; } if (options.Fast) { /* Select recommended processing options for quick-and-dirty output. */ decompressor.Two_pass_quantize = false; decompressor.Dither_mode = J_DITHER_MODE.JDITHER_ORDERED; if (!decompressor.Quantize_colors) /* don't override an earlier -colors */ { decompressor.Desired_number_of_colors = 216; } decompressor.Dct_method = JpegConstants.JDCT_FASTEST; decompressor.Do_fancy_upsampling = false; } if (options.Grayscale) { decompressor.Out_color_space = J_COLOR_SPACE.JCS_GRAYSCALE; } if (options.NoSmooth) { decompressor.Do_fancy_upsampling = false; } if (options.OnePass) { decompressor.Two_pass_quantize = false; } if (options.Scaled) { decompressor.Scale_num = options.ScaleNumerator; decompressor.Scale_denom = options.ScaleDenominator; } }
// Convert a stream into a source manager. public static void StreamToSourceManager (ref jpeg_decompress_struct cinfo, Stream stream, byte[] prime, int primeLen) { // Allocate a state structure and store it in "cinfo". IntPtr buf = Marshal.AllocHGlobal(4096); StreamState state = new StreamState(); state.buf = buf; state.buffer = new byte [4096]; state.stream = stream; cinfo.client_data = (IntPtr)(GCHandle.Alloc(state)); // We prime the input buffer with the JPEG magic number // if some higher-level process has already read it. int len; if (prime != null) { len = primeLen; Marshal.Copy(prime, 0, buf, len); } else { len = 0; } // Create the managed version of "jpeg_source_mgr". jpeg_source_mgr mgr = new jpeg_source_mgr(); mgr.next_input_byte = buf; mgr.bytes_in_buffer = (size_t)len; mgr.init_source = new init_source_type(init_source); mgr.fill_input_buffer = new fill_input_buffer_type(fill_input_buffer); mgr.skip_input_data = new skip_input_data_type(skip_input_data); mgr.resync_to_restart = new resync_to_restart_type(jpeg_resync_to_restart); mgr.term_source = new term_source_type(term_source); // Convert it into the unmanaged version and store it. #if __CSCC__ IntPtr umgr = Marshal.AllocHGlobal(sizeof(jpeg_source_mgr)); Marshal.StructureToPtr(mgr, umgr, false); cinfo.src = (jpeg_source_mgr *)umgr; #endif }
public byte[] DecodeJpeg(string inputPath, int len) { var input = new jpeg_decompress_struct(); using (var fs = new FileStream(inputPath, FileMode.Open)) { input.jpeg_stdio_src(fs); input.jpeg_read_header(false); var coefficients = input.jpeg_read_coefficients(); var channels = coefficients.Take(3).Select(JpegHelper.GetBuffer).ToArray(); input.jpeg_finish_decompress(); return(DecodeBlocks(channels, len)); } }
public static void ApplyQuantizationTable(string JpegFilePath, EnumStegoChannel Component = EnumStegoChannel.Y, int ScaleFactor = 50, int[] QuantizationTable = null) { var jpds = new jpeg_decompress_struct(); FileStream fileStreamImg = new FileStream(JpegFilePath, FileMode.Open, FileAccess.Read); jpds.jpeg_stdio_src(fileStreamImg); jpds.jpeg_read_header(true); // DCT coefficients var jBlock = jpds.jpeg_read_coefficients(); jpds.jpeg_finish_decompress(); fileStreamImg.Close(); if (null == QuantizationTable) { switch (Component) { case EnumStegoChannel.Y: QuantizationTable = _qLuminance; break; case EnumStegoChannel.Cb: case EnumStegoChannel.Cr: QuantizationTable = _qChromiance; break; } } // Get fle info FileInfo fInfo = new FileInfo(JpegFilePath); string dir = fInfo.DirectoryName; string fNane = fInfo.Name; string stegFName = $"steg_{DateTime.Now.ToString("yyyyMMddHHmm")}_{fNane}"; string stegJpegFilePath = Path.Combine(dir, stegFName); // Compress process FileStream fileStream = File.Create(stegJpegFilePath); jpeg_compress_struct jpcs = new jpeg_compress_struct(); jpcs.jpeg_stdio_dest(fileStream); jpds.jpeg_copy_critical_parameters(jpcs); jpcs.jpeg_write_coefficients(jBlock); jpcs.jpeg_finish_compress(); fileStream.Close(); jpds.jpeg_abort_decompress(); }
// Generic versions of jpeg_abort and jpeg_destroy that work on either // flavor of JPEG object. These may be more convenient in some places. /// <summary> /// Abort processing of a JPEG compression or decompression operation, /// but don't destroy the object itself. /// /// Closing a data source or destination, if necessary, is the /// application's responsibility. /// </summary> public void jpeg_abort() { /* Reset overall state for possible reuse of object */ if (IsDecompressor) { m_global_state = JpegState.DSTATE_START; /* Try to keep application from accessing now-deleted marker list. * A bit kludgy to do it here, but this is the most central place. */ jpeg_decompress_struct s = this as jpeg_decompress_struct; if (s != null) s.m_marker_list = null; } else { m_global_state = JpegState.CSTATE_START; } }
private savable_state m_saved = new savable_state(); /* Other state at start of MCU */ #endregion Fields #region Constructors public phuff_entropy_decoder(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Mark derived tables unallocated */ for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) m_derived_tbls[i] = null; /* Create progression status table */ cinfo.m_coef_bits = new int[cinfo.m_num_components][]; for (int i = 0; i < cinfo.m_num_components; i++) cinfo.m_coef_bits[i] = new int[JpegConstants.DCTSIZE2]; for (int ci = 0; ci < cinfo.m_num_components; ci++) { for (int i = 0; i < JpegConstants.DCTSIZE2; i++) cinfo.m_coef_bits[ci][i] = -1; } }
// Skip data in an input stream. private static void skip_input_data (ref jpeg_decompress_struct cinfo, Long num_bytes) { #if __CSCC__ jpeg_source_mgr *src = cinfo.src; int num = (int)num_bytes; if (num > 0) { while (num > (int)(src->bytes_in_buffer)) { num -= (int)(src->bytes_in_buffer); fill_input_buffer(ref cinfo); } src->next_input_byte = new IntPtr((src->next_input_byte.ToInt64()) + num); src->bytes_in_buffer = (size_t)(((int)(src->bytes_in_buffer)) - num); } #endif }
private int m_rows_to_go; /* counts rows remaining in image */ public my_merged_upsampler(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; m_need_context_rows = false; m_out_row_width = cinfo.m_output_width * cinfo.m_out_color_components; if (cinfo.m_max_v_samp_factor == 2) { m_use_2v_upsample = true; /* Allocate a spare row buffer */ m_spare_row = new byte[m_out_row_width]; } else { m_use_2v_upsample = false; } build_ycc_rgb_table(); }
/// <summary> /// Skip over an unknown or uninteresting variable-length marker /// </summary> private static bool skip_variable(jpeg_decompress_struct cinfo) { int length; if (!cinfo.m_src.GetTwoBytes(out length)) { return(false); } length -= 2; cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, length); if (length > 0) { cinfo.m_src.skip_input_data(length); } return(true); }
private int m_bytes_read; /* data bytes read so far in marker */ /* Note: cur_marker is not linked into marker_list until it's all read. */ /// <summary> /// Initialize the marker reader module. /// This is called only once, when the decompression object is created. /// </summary> public jpeg_marker_reader(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Initialize COM/APPn processing. * By default, we examine and then discard APP0 and APP14, * but simply discard COM and all other APPn. */ m_process_COM = skip_variable; for (int i = 0; i < 16; i++) { m_process_APPn[i] = skip_variable; m_length_limit_APPn[i] = 0; } m_process_APPn[0] = get_interesting_appn; m_process_APPn[14] = get_interesting_appn; /* Reset marker processing state */ reset_marker_reader(); }
/// <summary> /// Examine first few bytes from an APP14. /// Take appropriate action if it is an Adobe marker. /// datalen is # of bytes at data[], remaining is length of rest of marker data. /// </summary> private static void examine_app14(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining) { if (datalen >= APP14_DATA_LEN && data[0] == 0x41 && data[1] == 0x64 && data[2] == 0x6F && data[3] == 0x62 && data[4] == 0x65) { /* Found Adobe APP14 marker */ int version = (data[5] << 8) + data[6]; int flags0 = (data[7] << 8) + data[8]; int flags1 = (data[9] << 8) + data[10]; int transform = data[11]; cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_ADOBE, version, flags0, flags1, transform); cinfo.m_saw_Adobe_marker = true; cinfo.m_Adobe_transform = (byte)transform; } else { /* Start of APP14 does not match "Adobe", or too short */ cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP14, datalen + remaining); } }
/// <summary> /// Save an APPn or COM marker into the marker list /// </summary> private static bool save_marker(jpeg_decompress_struct cinfo) { jpeg_marker_struct cur_marker = cinfo.m_marker.m_cur_marker; byte[] data = null; int length = 0; int bytes_read; int data_length; int dataOffset = 0; if (cur_marker == null) { /* begin reading a marker */ if (!cinfo.m_src.GetTwoBytes(out length)) return false; length -= 2; if (length >= 0) { /* watch out for bogus length word */ /* figure out how much we want to save */ int limit; if (cinfo.m_unread_marker == (int)JPEG_MARKER.COM) limit = cinfo.m_marker.m_length_limit_COM; else limit = cinfo.m_marker.m_length_limit_APPn[cinfo.m_unread_marker - (int)JPEG_MARKER.APP0]; if (length < limit) limit = length; /* allocate and initialize the marker item */ cur_marker = new jpeg_marker_struct((byte)cinfo.m_unread_marker, length, limit); /* data area is just beyond the jpeg_marker_struct */ data = cur_marker.Data; cinfo.m_marker.m_cur_marker = cur_marker; cinfo.m_marker.m_bytes_read = 0; bytes_read = 0; data_length = limit; } else { /* deal with bogus length word */ bytes_read = data_length = 0; data = null; } } else { /* resume reading a marker */ bytes_read = cinfo.m_marker.m_bytes_read; data_length = cur_marker.Data.Length; data = cur_marker.Data; dataOffset = bytes_read; } byte[] tempData = null; if (data_length != 0) tempData = new byte[data.Length]; while (bytes_read < data_length) { /* move the restart point to here */ cinfo.m_marker.m_bytes_read = bytes_read; /* If there's not at least one byte in buffer, suspend */ if (!cinfo.m_src.MakeByteAvailable()) return false; /* Copy bytes with reasonable rapidity */ int read = cinfo.m_src.GetBytes(tempData, data_length - bytes_read); Buffer.BlockCopy(tempData, 0, data, dataOffset, data_length - bytes_read); bytes_read += read; } /* Done reading what we want to read */ if (cur_marker != null) { /* will be null if bogus length word */ /* Add new marker to end of list */ cinfo.m_marker_list.Add(cur_marker); /* Reset pointer & calc remaining data length */ data = cur_marker.Data; dataOffset = 0; length = cur_marker.OriginalLength - data_length; } /* Reset to initial state for next marker */ cinfo.m_marker.m_cur_marker = null; JPEG_MARKER currentMarker = (JPEG_MARKER)cinfo.m_unread_marker; if (data_length != 0 && (currentMarker == JPEG_MARKER.APP0 || currentMarker == JPEG_MARKER.APP14)) { tempData = new byte[data.Length]; Buffer.BlockCopy(data, dataOffset, tempData, 0, data.Length - dataOffset); } /* Process the marker if interesting; else just make a generic trace msg */ switch ((JPEG_MARKER)cinfo.m_unread_marker) { case JPEG_MARKER.APP0: examine_app0(cinfo, tempData, data_length, length); break; case JPEG_MARKER.APP14: examine_app14(cinfo, tempData, data_length, length); break; default: cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, data_length + length); break; } /* skip any remaining data -- could be lots */ if (length > 0) cinfo.m_src.skip_input_data(length); return true; }
private bool m_on_odd_row; /* flag to remember which row we are on */ /// <summary> /// Module initialization routine for 1-pass color quantization. /// </summary> /// <param name="cinfo">The cinfo.</param> public my_1pass_cquantizer(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; m_fserrors[0] = null; /* Flag FS workspace not allocated */ m_odither[0] = null; /* Also flag odither arrays not allocated */ /* Make sure my internal arrays won't overflow */ if (cinfo.m_out_color_components > MAX_Q_COMPS) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_COMPONENTS, MAX_Q_COMPS); /* Make sure colormap indexes can be represented by JSAMPLEs */ if (cinfo.m_desired_number_of_colors > (JpegConstants.MAXJSAMPLE + 1)) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, JpegConstants.MAXJSAMPLE + 1); /* Create the colormap and color index table. */ create_colormap(); create_colorindex(); /* Allocate Floyd-Steinberg workspace now if requested. * We do this now since it is FAR storage and may affect the memory * manager's space calculations. If the user changes to FS dither * mode in a later pass, we will allocate the space then, and will * possibly overrun the max_memory_to_use setting. */ if (cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) alloc_fs_workspace(); }
private bool m_start_of_file; /* have we gotten any data yet? */ /// <summary> /// Initialize source - called by jpeg_read_header /// before any data is actually read. /// </summary> public my_source_mgr(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; m_buffer = new byte[INPUT_BUF_SIZE]; }
/// <summary> /// Install a special processing method for COM or APPn markers. /// </summary> public void jpeg_set_marker_processor(int marker_code, jpeg_decompress_struct.jpeg_marker_parser_method routine) { if (marker_code == (int)JPEG_MARKER.COM) m_process_COM = routine; else if (marker_code >= (int)JPEG_MARKER.APP0 && marker_code <= (int)JPEG_MARKER.APP15) m_process_APPn[marker_code - (int)JPEG_MARKER.APP0] = routine; else m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, marker_code); }
private int m_whichFunny; /* indicates which funny indices set is now in use */ #endregion Fields #region Constructors public jpeg_d_main_controller(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Allocate the workspace. * ngroups is the number of row groups we need. */ int ngroups = cinfo.m_min_DCT_scaled_size; if (cinfo.m_upsample.NeedContextRows()) { if (cinfo.m_min_DCT_scaled_size < 2) /* unsupported, see comments above */ cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); alloc_funny_pointers(); /* Alloc space for xbuffer[] lists */ ngroups = cinfo.m_min_DCT_scaled_size + 2; } for (int ci = 0; ci < cinfo.m_num_components; ci++) { /* height of a row group of component */ int rgroup = (cinfo.Comp_info[ci].V_samp_factor * cinfo.Comp_info[ci].DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( cinfo.Comp_info[ci].Width_in_blocks * cinfo.Comp_info[ci].DCT_scaled_size, rgroup * ngroups); } }
// Skip data in an input stream. private static void skip_input_data (ref jpeg_decompress_struct cinfo, Long num_bytes) { #if __CSCC__ jpeg_source_mgr *src = cinfo.src; int num = (int)num_bytes; if(num > 0) { while(num > (int)(src->bytes_in_buffer)) { num -= (int)(src->bytes_in_buffer); fill_input_buffer(ref cinfo); } src->next_input_byte = new IntPtr((src->next_input_byte.ToInt64()) + num); src->bytes_in_buffer = (size_t)(((int)(src->bytes_in_buffer)) - num); } #endif }
/// <summary> /// This is the default resync_to_restart method for data source /// managers to use if they don't have any better approach. /// </summary> /// <param name="cinfo">An instance of <see cref="jpeg_decompress_struct"/></param> /// <param name="desired">The desired</param> /// <returns><c>false</c> if suspension is required.</returns> /// <remarks>That method assumes that no backtracking is possible. /// Some data source managers may be able to back up, or may have /// additional knowledge about the data which permits a more /// intelligent recovery strategy; such managers would /// presumably supply their own resync method.<br/><br/> /// /// read_restart_marker calls resync_to_restart if it finds a marker other than /// the restart marker it was expecting. (This code is *not* used unless /// a nonzero restart interval has been declared.) cinfo.unread_marker is /// the marker code actually found (might be anything, except 0 or FF). /// The desired restart marker number (0..7) is passed as a parameter.<br/><br/> /// /// This routine is supposed to apply whatever error recovery strategy seems /// appropriate in order to position the input stream to the next data segment. /// Note that cinfo.unread_marker is treated as a marker appearing before /// the current data-source input point; usually it should be reset to zero /// before returning.<br/><br/> /// /// This implementation is substantially constrained by wanting to treat the /// input as a data stream; this means we can't back up. Therefore, we have /// only the following actions to work with:<br/> /// 1. Simply discard the marker and let the entropy decoder resume at next /// byte of file.<br/> /// 2. Read forward until we find another marker, discarding intervening /// data. (In theory we could look ahead within the current bufferload, /// without having to discard data if we don't find the desired marker. /// This idea is not implemented here, in part because it makes behavior /// dependent on buffer size and chance buffer-boundary positions.)<br/> /// 3. Leave the marker unread (by failing to zero cinfo.unread_marker). /// This will cause the entropy decoder to process an empty data segment, /// inserting dummy zeroes, and then we will reprocess the marker.<br/> /// /// #2 is appropriate if we think the desired marker lies ahead, while #3 is /// appropriate if the found marker is a future restart marker (indicating /// that we have missed the desired restart marker, probably because it got /// corrupted).<br/> /// We apply #2 or #3 if the found marker is a restart marker no more than /// two counts behind or ahead of the expected one. We also apply #2 if the /// found marker is not a legal JPEG marker code (it's certainly bogus data). /// If the found marker is a restart marker more than 2 counts away, we do #1 /// (too much risk that the marker is erroneous; with luck we will be able to /// resync at some future point).<br/> /// For any valid non-restart JPEG marker, we apply #3. This keeps us from /// overrunning the end of a scan. An implementation limited to single-scan /// files might find it better to apply #2 for markers other than EOI, since /// any other marker would have to be bogus data in that case.</remarks> public virtual bool resync_to_restart(jpeg_decompress_struct cinfo, int desired) { /* Always put up a warning. */ cinfo.WARNMS(J_MESSAGE_CODE.JWRN_MUST_RESYNC, cinfo.m_unread_marker, desired); /* Outer loop handles repeated decision after scanning forward. */ int action = 1; for ( ; ; ) { if (cinfo.m_unread_marker < (int)JPEG_MARKER.SOF0) { /* invalid marker */ action = 2; } else if (cinfo.m_unread_marker < (int)JPEG_MARKER.RST0 || cinfo.m_unread_marker > (int)JPEG_MARKER.RST7) { /* valid non-restart marker */ action = 3; } else { if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 1) & 7)) || cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 2) & 7))) { /* one of the next two expected restarts */ action = 3; } else if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 1) & 7)) || cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 2) & 7))) { /* a prior restart, so advance */ action = 2; } else { /* desired restart or too far away */ action = 1; } } cinfo.TRACEMS(4, J_MESSAGE_CODE.JTRC_RECOVERY_ACTION, cinfo.m_unread_marker, action); switch (action) { case 1: /* Discard marker and let entropy decoder resume processing. */ cinfo.m_unread_marker = 0; return true; case 2: /* Scan to the next marker, and repeat the decision loop. */ if (!cinfo.m_marker.next_marker()) return false; break; case 3: /* Return without advancing past this marker. */ /* Entropy decoder will be forced to process an empty segment. */ return true; } } }
public jpeg_decomp_master(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; master_selection(); }
private int[] rgb_y_tab; /* => table for RGB to Y conversion */ /// <summary> /// Module initialization routine for output colorspace conversion. /// </summary> public jpeg_color_deconverter(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Make sure num_components agrees with jpeg_color_space */ switch (cinfo.m_jpeg_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if (cinfo.m_num_components != 1) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_YCbCr: case J_COLOR_SPACE.JCS_BG_RGB: case J_COLOR_SPACE.JCS_BG_YCC: if (cinfo.m_num_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; case J_COLOR_SPACE.JCS_CMYK: case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_num_components != 4) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; case J_COLOR_SPACE.JCS_NCHANNEL: if (cinfo.m_num_components < 1) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; default: /* JCS_UNKNOWN can be anything */ if (cinfo.m_num_components < 1) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; } /* Support color transform only for RGB colorspaces */ if (cinfo.color_transform != J_COLOR_TRANSFORM.JCT_NONE && cinfo.m_jpeg_color_space != J_COLOR_SPACE.JCS_RGB && cinfo.m_jpeg_color_space != J_COLOR_SPACE.JCS_BG_RGB) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } /* Set out_color_components and conversion method based on requested space. * Also clear the component_needed flags for any unused components, * so that earlier pipeline stages can avoid useless computation. */ switch (cinfo.m_out_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: cinfo.m_out_color_components = 1; switch (cinfo.m_jpeg_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: case J_COLOR_SPACE.JCS_YCbCr: case J_COLOR_SPACE.JCS_BG_YCC: m_converter = grayscale_convert; /* For color->grayscale conversion, only the Y (0) component is needed */ for (int ci = 1; ci < cinfo.m_num_components; ci++) cinfo.Comp_info[ci].component_needed = false; break; case J_COLOR_SPACE.JCS_RGB: switch (cinfo.color_transform) { case J_COLOR_TRANSFORM.JCT_NONE: m_converter = rgb_gray_convert; break; case J_COLOR_TRANSFORM.JCT_SUBTRACT_GREEN: m_converter = rgb1_gray_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } build_rgb_y_table(); break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_RGB: cinfo.m_out_color_components = JpegConstants.RGB_PIXELSIZE; switch (cinfo.m_jpeg_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: m_converter = gray_rgb_convert; break; case J_COLOR_SPACE.JCS_YCbCr: m_converter = ycc_rgb_convert; build_ycc_rgb_table(); break; case J_COLOR_SPACE.JCS_BG_YCC: m_converter = ycc_rgb_convert; build_bg_ycc_rgb_table(); break; case J_COLOR_SPACE.JCS_RGB: switch (cinfo.color_transform) { case J_COLOR_TRANSFORM.JCT_NONE: m_converter = rgb_convert; break; case J_COLOR_TRANSFORM.JCT_SUBTRACT_GREEN: m_converter = rgb1_rgb_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_CMYK: m_converter = cmyk_rgb_convert; break; case J_COLOR_SPACE.JCS_YCCK: m_converter = ycck_rgb_convert; build_ycc_rgb_table(); break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_BG_RGB: cinfo.m_out_color_components = JpegConstants.RGB_PIXELSIZE; if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_BG_RGB) { switch (cinfo.color_transform) { case J_COLOR_TRANSFORM.JCT_NONE: m_converter = rgb_convert; break; case J_COLOR_TRANSFORM.JCT_SUBTRACT_GREEN: m_converter = rgb1_rgb_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } } else { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; case J_COLOR_SPACE.JCS_CMYK: cinfo.m_out_color_components = 4; switch (cinfo.m_jpeg_color_space) { case J_COLOR_SPACE.JCS_YCCK: m_converter = ycck_cmyk_convert; build_ycc_rgb_table(); break; case J_COLOR_SPACE.JCS_CMYK: m_converter = null_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_NCHANNEL: if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_NCHANNEL) m_converter = null_convert; else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; default: /* Permit null conversion to same output space */ if (cinfo.m_out_color_space == cinfo.m_jpeg_color_space) { cinfo.m_out_color_components = cinfo.m_num_components; m_converter = null_convert; } else { /* unsupported non-null conversion */ cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; } if (cinfo.m_quantize_colors) cinfo.m_output_components = 1; /* single colormapped output component */ else cinfo.m_output_components = cinfo.m_out_color_components; }
/* * Routines for processing APPn and COM markers. * These are either saved in memory or discarded, per application request. * APP0 and APP14 are specially checked to see if they are * JFIF and Adobe markers, respectively. */ /// <summary> /// Examine first few bytes from an APP0. /// Take appropriate action if it is a JFIF marker. /// datalen is # of bytes at data[], remaining is length of rest of marker data. /// </summary> private static void examine_app0(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining) { int totallen = datalen + remaining; if (datalen >= APP0_DATA_LEN && data[0] == 0x4A && data[1] == 0x46 && data[2] == 0x49 && data[3] == 0x46 && data[4] == 0) { /* Found JFIF APP0 marker: save info */ cinfo.m_saw_JFIF_marker = true; cinfo.m_JFIF_major_version = data[5]; cinfo.m_JFIF_minor_version = data[6]; cinfo.m_density_unit = (DensityUnit)data[7]; cinfo.m_X_density = (short)((data[8] << 8) + data[9]); cinfo.m_Y_density = (short)((data[10] << 8) + data[11]); /* Check version. * Major version must be 1, anything else signals an incompatible change. * (We used to treat this as an error, but now it's a nonfatal warning, * because some bozo at Hijaak couldn't read the spec.) * Minor version should be 0..2, but process anyway if newer. */ if (cinfo.m_JFIF_major_version != 1) cinfo.WARNMS(J_MESSAGE_CODE.JWRN_JFIF_MAJOR, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version); /* Generate trace messages */ cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version, cinfo.m_X_density, cinfo.m_Y_density, cinfo.m_density_unit); /* Validate thumbnail dimensions and issue appropriate messages */ if ((data[12] | data[13]) != 0) cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_THUMBNAIL, data[12], data[13]); totallen -= APP0_DATA_LEN; if (totallen != ((int)data[12] * (int)data[13] * 3)) cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_BADTHUMBNAILSIZE, totallen); } else if (datalen >= 6 && data[0] == 0x4A && data[1] == 0x46 && data[2] == 0x58 && data[3] == 0x58 && data[4] == 0) { /* Found JFIF "JFXX" extension APP0 marker */ /* The library doesn't actually do anything with these, * but we try to produce a helpful trace message. */ switch (data[5]) { case 0x10: cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_JPEG, totallen); break; case 0x11: cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_PALETTE, totallen); break; case 0x13: cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_RGB, totallen); break; default: cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_EXTENSION, data[5], totallen); break; } } else { /* Start of APP0 does not match "JFIF" or "JFXX", or too short */ cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP0, totallen); } }
// Convert a stream into a source manager. public static void StreamToSourceManager (ref jpeg_decompress_struct cinfo, Stream stream, byte[] prime, int primeLen) { // Allocate a state structure and store it in "cinfo". IntPtr buf = Marshal.AllocHGlobal(4096); StreamState state = new StreamState(); state.buf = buf; state.buffer = new byte [4096]; state.stream = stream; cinfo.client_data = (IntPtr)(GCHandle.Alloc(state)); // We prime the input buffer with the JPEG magic number // if some higher-level process has already read it. int len; if(prime != null) { len = primeLen; Marshal.Copy(prime, 0, buf, len); } else { len = 0; } // Create the managed version of "jpeg_source_mgr". jpeg_source_mgr mgr = new jpeg_source_mgr(); mgr.next_input_byte = buf; mgr.bytes_in_buffer = (size_t)len; mgr.init_source = new init_source_type(init_source); mgr.fill_input_buffer = new fill_input_buffer_type(fill_input_buffer); mgr.skip_input_data = new skip_input_data_type(skip_input_data); mgr.resync_to_restart = new resync_to_restart_type(jpeg_resync_to_restart); mgr.term_source = new term_source_type(term_source); // Convert it into the unmanaged version and store it. #if __CSCC__ IntPtr umgr = Marshal.AllocHGlobal(sizeof(jpeg_source_mgr)); Marshal.StructureToPtr(mgr, umgr, false); cinfo.src = (jpeg_source_mgr *)umgr; #endif }
public huff_entropy_decoder(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; finish_pass = finish_pass_huff; if (m_cinfo.m_progressive_mode) { /* Create progression status table */ cinfo.m_coef_bits = new int[cinfo.m_num_components][]; for (int i = 0; i < cinfo.m_num_components; i++) cinfo.m_coef_bits[i] = new int[JpegConstants.DCTSIZE2]; for (int ci = 0; ci < cinfo.m_num_components; ci++) { for (int i = 0; i < JpegConstants.DCTSIZE2; i++) cinfo.m_coef_bits[ci][i] = -1; } /* Mark derived tables unallocated */ for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) { derived_tbls[i] = null; } } else { /* Mark tables unallocated */ for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) { m_dc_derived_tbls[i] = null; m_ac_derived_tbls[i] = null; } } }
/// <summary> /// Skip over an unknown or uninteresting variable-length marker /// </summary> private static bool skip_variable(jpeg_decompress_struct cinfo) { int length; if (!cinfo.m_src.GetTwoBytes(out length)) return false; length -= 2; cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, length); if (length > 0) cinfo.m_src.skip_input_data(length); return true; }
public my_upsampler(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; m_need_context_rows = false; /* until we find out differently */ if (cinfo.m_CCIR601_sampling) /* this isn't supported */ cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); /* jpeg_d_main_controller doesn't support context rows when min_DCT_scaled_size = 1, * so don't ask for it. */ bool do_fancy = cinfo.m_do_fancy_upsampling && cinfo.m_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.m_num_components; ci++) { jpeg_component_info componentInfo = 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. */ int h_in_group = (componentInfo.H_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; int v_in_group = (componentInfo.V_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; int h_out_group = cinfo.m_max_h_samp_factor; int v_out_group = cinfo.m_max_v_samp_factor; /* save for use later */ m_rowgroup_height[ci] = v_in_group; bool need_buffer = true; if (!componentInfo.component_needed) { /* Don't bother to upsample an uninteresting component. */ m_upsampleMethods[ci] = ComponentUpsampler.noop_upsampler; 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. */ m_upsampleMethods[ci] = ComponentUpsampler.fullsize_upsampler; need_buffer = false; } else if (h_in_group * 2 == h_out_group && v_in_group == v_out_group) { /* Special cases for 2h1v upsampling */ if (do_fancy && componentInfo.downsampled_width > 2) m_upsampleMethods[ci] = ComponentUpsampler.h2v1_fancy_upsampler; else m_upsampleMethods[ci] = ComponentUpsampler.h2v1_upsampler; } else if (h_in_group * 2 == h_out_group && v_in_group * 2 == v_out_group) { /* Special cases for 2h2v upsampling */ if (do_fancy && componentInfo.downsampled_width > 2) { m_upsampleMethods[ci] = ComponentUpsampler.h2v2_fancy_upsampler; m_need_context_rows = true; } else { m_upsampleMethods[ci] = ComponentUpsampler.h2v2_upsampler; } } else if ((h_out_group % h_in_group) == 0 && (v_out_group % v_in_group) == 0) { /* Generic integral-factors upsampling method */ m_upsampleMethods[ci] = ComponentUpsampler.int_upsampler; m_h_expand[ci] = (byte) (h_out_group / h_in_group); m_v_expand[ci] = (byte) (v_out_group / v_in_group); } else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); if (need_buffer) { ComponentBuffer cb = new ComponentBuffer(); cb.SetBuffer(jpeg_common_struct.AllocJpegSamples(JpegUtils.jround_up(cinfo.m_output_width, cinfo.m_max_h_samp_factor), cinfo.m_max_v_samp_factor), null, 0); m_color_buf[ci] = cb; } } }
/// <summary> /// Process an APP0 or APP14 marker without saving it /// </summary> private static bool get_interesting_appn(jpeg_decompress_struct cinfo) { int length; if (!cinfo.m_src.GetTwoBytes(out length)) return false; length -= 2; /* get the interesting part of the marker data */ int numtoread = 0; if (length >= APPN_DATA_LEN) numtoread = APPN_DATA_LEN; else if (length > 0) numtoread = length; byte[] b = new byte[APPN_DATA_LEN]; for (int i = 0; i < numtoread; i++) { int temp = 0; if (!cinfo.m_src.GetByte(out temp)) return false; b[i] = (byte) temp; } length -= numtoread; /* process it */ switch ((JPEG_MARKER)cinfo.m_unread_marker) { case JPEG_MARKER.APP0: examine_app0(cinfo, b, numtoread, length); break; case JPEG_MARKER.APP14: examine_app14(cinfo, b, numtoread, length); break; default: /* can't get here unless jpeg_save_markers chooses wrong processor */ cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, cinfo.m_unread_marker); break; } /* skip any remaining data -- could be lots */ if (length > 0) cinfo.m_src.skip_input_data(length); return true; }
private int[] m_Cb_g_tab; /* => table for Cb to G conversion */ /// <summary> /// Module initialization routine for output colorspace conversion. /// </summary> public jpeg_color_deconverter(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Make sure num_components agrees with jpeg_color_space */ switch (cinfo.m_jpeg_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if (cinfo.m_num_components != 1) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_YCbCr: if (cinfo.m_num_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; case J_COLOR_SPACE.JCS_CMYK: case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_num_components != 4) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; default: /* JCS_UNKNOWN can be anything */ if (cinfo.m_num_components < 1) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; } /* Set out_color_components and conversion method based on requested space. * Also clear the component_needed flags for any unused components, * so that earlier pipeline stages can avoid useless computation. */ switch (cinfo.m_out_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: cinfo.m_out_color_components = 1; if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_GRAYSCALE || cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) { m_converter = ColorConverter.grayscale_converter; /* For color->grayscale conversion, only the Y (0) component is needed */ for (int ci = 1; ci < cinfo.m_num_components; ci++) cinfo.Comp_info[ci].component_needed = false; } else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_RGB: cinfo.m_out_color_components = JpegConstants.RGB_PIXELSIZE; if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) { m_converter = ColorConverter.ycc_rgb_converter; build_ycc_rgb_table(); } else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_GRAYSCALE) m_converter = ColorConverter.gray_rgb_converter; else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_RGB) m_converter = ColorConverter.null_converter; else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_CMYK: cinfo.m_out_color_components = 4; if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCCK) { m_converter = ColorConverter.ycck_cmyk_converter; build_ycc_rgb_table(); } else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_CMYK) m_converter = ColorConverter.null_converter; else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; default: /* Permit null conversion to same output space */ if (cinfo.m_out_color_space == cinfo.m_jpeg_color_space) { cinfo.m_out_color_components = cinfo.m_num_components; m_converter = ColorConverter.null_converter; } else { /* unsupported non-null conversion */ cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; } if (cinfo.m_quantize_colors) cinfo.m_output_components = 1; /* single colormapped output component */ else cinfo.m_output_components = cinfo.m_out_color_components; }
/// <summary> /// Examine first few bytes from an APP14. /// Take appropriate action if it is an Adobe marker. /// datalen is # of bytes at data[], remaining is length of rest of marker data. /// </summary> private static void examine_app14(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining) { if (datalen >= APP14_DATA_LEN && data[0] == 0x41 && data[1] == 0x64 && data[2] == 0x6F && data[3] == 0x62 && data[4] == 0x65) { /* Found Adobe APP14 marker */ int version = (data[5] << 8) + data[6]; int flags0 = (data[7] << 8) + data[8]; int flags1 = (data[9] << 8) + data[10]; int transform = data[11]; cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_ADOBE, version, flags0, flags1, transform); cinfo.m_saw_Adobe_marker = true; cinfo.m_Adobe_transform = (byte) transform; } else { /* Start of APP14 does not match "Adobe", or too short */ cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP14, datalen + remaining); } }
// Terminate an input source. private static void term_source(ref jpeg_decompress_struct cinfo) { // Nothing to do here. }
// Fill an input buffer from a stream. private static Int fill_input_buffer(ref jpeg_decompress_struct cinfo) { int len; StreamState state = GetStreamState(ref cinfo); if(!(state.sawEOF)) { len = state.stream.Read (state.buffer, 0, state.buffer.Length); if(len > 0) { Marshal.Copy(state.buffer, 0, state.buf, len); cinfo.src->next_input_byte = state.buf; cinfo.src->bytes_in_buffer = (size_t)len; return (Int)1; } state.sawEOF = true; } // Insert an EOI marker to indicate end of stream to "libjpeg". Marshal.WriteByte(state.buf, 0, (byte)0xFF); Marshal.WriteByte(state.buf, 1, (byte)0xD9); cinfo.src->next_input_byte = state.buf; cinfo.src->bytes_in_buffer = (size_t)2; return (Int)1; }
private int[] m_error_limiter; /* table for clamping the applied error */ /// <summary> /// Module initialization routine for 2-pass color quantization. /// </summary> public my_2pass_cquantizer(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; /* Make sure jdmaster didn't give me a case I can't handle */ if (cinfo.m_out_color_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); /* Allocate the histogram/inverse colormap storage */ m_histogram = new ushort[HIST_C0_ELEMS][]; for (int i = 0; i < HIST_C0_ELEMS; i++) m_histogram[i] = new ushort[HIST_C1_ELEMS * HIST_C2_ELEMS]; m_needs_zeroed = true; /* histogram is garbage now */ /* Allocate storage for the completed colormap, if required. * We do this now since it is FAR storage and may affect * the memory manager's space calculations. */ if (cinfo.m_enable_2pass_quant) { /* Make sure color count is acceptable */ int desired_local = cinfo.m_desired_number_of_colors; /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */ if (desired_local < 8) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS, 8); /* Make sure colormap indexes can be represented by JSAMPLEs */ if (desired_local > MAXNUMCOLORS) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); m_sv_colormap = jpeg_common_struct.AllocJpegSamples(desired_local, 3); m_desired = desired_local; } /* Only F-S dithering or no dithering is supported. */ /* If user asks for ordered dither, give him F-S. */ if (cinfo.m_dither_mode != J_DITHER_MODE.JDITHER_NONE) cinfo.m_dither_mode = J_DITHER_MODE.JDITHER_FS; /* Allocate Floyd-Steinberg workspace if necessary. * This isn't really needed until pass 2, but again it is FAR storage. * Although we will cope with a later change in dither_mode, * we do not promise to honor max_memory_to_use if dither_mode changes. */ if (cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) { m_fserrors = new short[(cinfo.m_output_width + 2) * 3]; /* Might as well create the error-limiting table too. */ init_error_limit(); } }
public arith_entropy_decoder(jpeg_decompress_struct cinfo) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); }
public jpeg_inverse_dct(jpeg_decompress_struct cinfo) { m_cinfo = cinfo; m_dctTables = new multiplier_table[cinfo.m_num_components]; for (int ci = 0; ci < cinfo.m_num_components; ci++) { /* Allocate and pre-zero a multiplier table for each component */ m_dctTables[ci] = new multiplier_table(); /* Mark multiplier table not yet set up for any method */ m_cur_method[ci] = -1; } }