// Convert a stream into a destination manager. public static void StreamToDestinationManager (ref jpeg_compress_struct cinfo, Stream stream) { // 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)); // Create the managed version of "jpeg_destination_mgr". jpeg_destination_mgr mgr = new jpeg_destination_mgr(); mgr.next_output_byte = buf; mgr.free_in_buffer = (size_t)4096; mgr.init_destination = new init_destination_type(init_destination); mgr.empty_output_buffer = new empty_output_buffer_type(empty_output_buffer); mgr.term_destination = new term_destination_type(term_destination); // Convert it into the unmanaged version and store it. #if __CSCC__ IntPtr umgr = Marshal.AllocHGlobal (sizeof(jpeg_destination_mgr)); Marshal.StructureToPtr(mgr, umgr, false); cinfo.dest = (jpeg_destination_mgr *)umgr; #endif }
/* Process a quality-ratings parameter string, of the form * N[,N,...] * If there are more q-table slots than parameters, the last value is replicated. */ static bool set_quality_ratings(jpeg_compress_struct cinfo, string arg, bool force_baseline) { int val = 75; /* default value */ string[] factors = arg.Split(new char[','], StringSplitOptions.RemoveEmptyEntries); for (int tblno = 0; tblno < JpegConstants.NUM_QUANT_TBLS; tblno++) { if (factors.Length > tblno) { bool parsed = int.TryParse(factors[tblno], out val); if (!parsed) { return(false); } /* Convert user 0-100 rating to percentage scaling */ cinfo.q_scale_factor[tblno] = jpeg_compress_struct.jpeg_quality_scaling(val); } else { /* reached end of parameter, set remaining factors to last value */ cinfo.q_scale_factor[tblno] = jpeg_compress_struct.jpeg_quality_scaling(val); } } cinfo.jpeg_default_qtables(force_baseline); return(true); }
/// <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); }
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(); } } }
private int m_next_buf_stop; /* downsample when we reach this index */ public jpeg_c_prep_controller(jpeg_compress_struct cinfo) { m_cinfo = cinfo; /* Allocate the color conversion buffer. * We make the buffer wide enough to allow the downsampler to edge-expand * horizontally within the buffer, if it so chooses. */ if (cinfo.m_downsample.NeedContextRows()) { /* Set up to provide context rows */ create_context_buffer(); } else { /* No context, just make it tall enough for one row group */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { m_colorBufRowsOffset = 0; m_color_buf[ci] = jpeg_compress_struct.AllocJpegSamples( (cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE * cinfo.m_max_h_samp_factor) / cinfo.Component_info[ci].H_samp_factor, cinfo.m_max_v_samp_factor); } } }
private int m_scan_number; /* current index in scan_info[] */ public jpeg_comp_master(jpeg_compress_struct cinfo, bool transcode_only) { m_cinfo = cinfo; if (transcode_only) { /* no main pass in transcoding */ if (cinfo.m_optimize_coding) { m_pass_type = c_pass_type.huff_opt_pass; } else { m_pass_type = c_pass_type.output_pass; } } else { /* for normal compression, first pass is always this type: */ m_pass_type = c_pass_type.main_pass; } if (cinfo.m_optimize_coding) { m_total_passes = cinfo.m_num_scans * 2; } else { m_total_passes = cinfo.m_num_scans; } }
public my_c_coef_controller(jpeg_compress_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. */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( JpegUtils.jround_up(cinfo.Component_info[ci].Width_in_blocks, cinfo.Component_info[ci].H_samp_factor), JpegUtils.jround_up(cinfo.Component_info[ci].height_in_blocks, cinfo.Component_info[ci].V_samp_factor)); m_whole_image[ci].ErrorProcessor = cinfo; } } else { /* We only need a single-MCU buffer. */ JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) buffer[i] = new JBLOCK(); for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) { m_MCU_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) m_MCU_buffer[i][j - i] = buffer[j]; } /* flag for no virtual arrays */ m_whole_image[0] = null; } }
public jpeg_forward_dct(jpeg_compress_struct cinfo) { m_cinfo = cinfo; switch (cinfo.m_dct_method) { case J_DCT_METHOD.JDCT_ISLOW: m_useFloatMethod = false; m_useSlowMethod = true; break; case J_DCT_METHOD.JDCT_IFAST: m_useFloatMethod = false; m_useSlowMethod = false; break; case J_DCT_METHOD.JDCT_FLOAT: m_useFloatMethod = true; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); break; } /* Mark divisor tables unallocated */ for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) { m_divisors[i] = null; m_float_divisors[i] = null; } }
/// <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); }
private bool m_need_context_rows; /* true if need rows above & below */ public jpeg_downsampler(jpeg_compress_struct cinfo) { m_cinfo = cinfo; m_need_context_rows = false; if (cinfo.m_CCIR601_sampling) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); /* Verify we can handle the sampling factors, and set up method pointers */ bool smoothok = true; for (int ci = 0; ci < cinfo.m_num_components; ci++) { jpeg_component_info componentInfo = cinfo.Component_info[ci]; if (componentInfo.H_samp_factor == cinfo.m_max_h_samp_factor && componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) { if (cinfo.m_smoothing_factor != 0) { m_downSamplers[ci] = downSampleMethod.fullsize_smooth_downsampler; m_need_context_rows = true; } else { m_downSamplers[ci] = downSampleMethod.fullsize_downsampler; } } else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) { smoothok = false; m_downSamplers[ci] = downSampleMethod.h2v1_downsampler; } else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && componentInfo.V_samp_factor * 2 == cinfo.m_max_v_samp_factor) { if (cinfo.m_smoothing_factor != 0) { m_downSamplers[ci] = downSampleMethod.h2v2_smooth_downsampler; m_need_context_rows = true; } else { m_downSamplers[ci] = downSampleMethod.h2v2_downsampler; } } else if ((cinfo.m_max_h_samp_factor % componentInfo.H_samp_factor) == 0 && (cinfo.m_max_v_samp_factor % componentInfo.V_samp_factor) == 0) { smoothok = false; m_downSamplers[ci] = downSampleMethod.int_downsampler; } else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); } if (cinfo.m_smoothing_factor != 0 && !smoothok) cinfo.TRACEMS(0, J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL); }
private static void compress(Stream input, CompressOptions options, Stream output) { Debug.Assert(input != null); Debug.Assert(options != null); Debug.Assert(output != null); jpeg_compress_struct cinfo = new jpeg_compress_struct(new cd_jpeg_error_mgr()); /* Initialize JPEG parameters. * Much of this may be overridden later. * In particular, we don't yet know the input file's color space, * but we need to provide some value for jpeg_set_defaults() to work. */ cinfo.In_color_space = J_COLOR_SPACE.JCS_RGB; /* arbitrary guess */ cinfo.jpeg_set_defaults(); /* Figure out the input file format, and set up to read it. */ cjpeg_source_struct src_mgr = new bmp_source_struct(cinfo); src_mgr.input_file = input; /* Read the input file header to obtain file size & colorspace. */ src_mgr.start_input(); /* Now that we know input colorspace, fix colorspace-dependent defaults */ cinfo.jpeg_default_colorspace(); /* Adjust default compression parameters */ if (!applyOptions(cinfo, options)) { return; } /* Specify data destination for compression */ cinfo.jpeg_stdio_dest(output); /* Start compressor */ cinfo.jpeg_start_compress(true); /* Process data */ while (cinfo.Next_scanline < cinfo.Image_height) { int num_scanlines = src_mgr.get_pixel_rows(); cinfo.jpeg_write_scanlines(src_mgr.buffer, num_scanlines); } /* Finish compression and release memory */ src_mgr.finish_input(); cinfo.jpeg_finish_compress(); /* All done. */ if (cinfo.Err.Num_warnings != 0) { Console.WriteLine("Corrupt-data warning count is not zero"); } }
private int m_restarts_to_go; /* MCUs left in this restart interval */ #endregion Fields #region Constructors public phuff_entropy_encoder(jpeg_compress_struct cinfo) { m_cinfo = cinfo; /* Mark tables unallocated */ for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) { m_derived_tbls[i] = null; m_count_ptrs[i] = null; } }
public phuff_entropy_encoder(jpeg_compress_struct cinfo) { m_cinfo = cinfo; /* Mark tables unallocated */ for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) { m_derived_tbls[i] = null; m_count_ptrs[i] = null; } }
// Free a destination manager. public static void FreeDestinationManager(ref jpeg_compress_struct cinfo) { GCHandle handle = (GCHandle)(cinfo.client_data); StreamState state = (StreamState)(handle.Target); Marshal.FreeHGlobal(state.buf); handle.Free(); Marshal.FreeHGlobal((IntPtr)(cinfo.dest)); cinfo.client_data = IntPtr.Zero; cinfo.dest = null; }
public jpeg_c_main_controller(jpeg_compress_struct cinfo) { m_cinfo = cinfo; /* Allocate a strip buffer for each component */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE, cinfo.Component_info[ci].V_samp_factor * JpegConstants.DCTSIZE); } }
// Empty an output buffer to a stream. private static Int empty_output_buffer(ref jpeg_compress_struct cinfo) { int len; StreamState state = GetStreamState(ref cinfo); len = state.buffer.Length; Marshal.Copy(state.buf, state.buffer, 0, len); state.stream.Write(state.buffer, 0, len); cinfo.dest->next_output_byte = state.buf; cinfo.dest->free_in_buffer = (size_t)len; return((Int)1); }
public jpeg_c_main_controller(jpeg_compress_struct cinfo) { m_cinfo = cinfo; /* Allocate a strip buffer for each component */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { jpeg_component_info compptr = cinfo.Component_info[ci]; m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( compptr.Width_in_blocks * compptr.DCT_h_scaled_size, compptr.V_samp_factor * compptr.DCT_v_scaled_size); } }
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(); }
/// <summary> /// Initialize coefficient buffer controller. /// /// Each passed coefficient array must be the right size for that /// coefficient: width_in_blocks wide and height_in_blocks high, /// with unit height at least v_samp_factor. /// </summary> public my_trans_c_coef_controller(jpeg_compress_struct cinfo, jvirt_array<JBLOCK>[] coef_arrays) { m_cinfo = cinfo; /* Save pointer to virtual arrays */ m_whole_image = coef_arrays; /* Allocate and pre-zero space for dummy DCT blocks. */ JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) buffer[i] = new JBLOCK(); for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) { m_dummy_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) m_dummy_buffer[i][j - i] = buffer[j]; } }
public void TestCompressorWithContextRows() { using (MemoryStream stream = new MemoryStream()) { jpeg_compress_struct compressor = new jpeg_compress_struct(new jpeg_error_mgr()); compressor.Image_height = 100; compressor.Image_width = 100; compressor.In_color_space = J_COLOR_SPACE.JCS_GRAYSCALE; compressor.Input_components = 1; compressor.jpeg_set_defaults(); compressor.Dct_method = J_DCT_METHOD.JDCT_IFAST; compressor.Smoothing_factor = 94; compressor.jpeg_set_quality(75, true); compressor.jpeg_simple_progression(); compressor.Density_unit = DensityUnit.Unknown; compressor.X_density = (short)96; compressor.Y_density = (short)96; compressor.jpeg_stdio_dest(stream); compressor.jpeg_start_compress(true); byte[][] rowForDecompressor = new byte[1][]; int bytesPerPixel = 1; while (compressor.Next_scanline < compressor.Image_height) { byte[] row = new byte[100 * bytesPerPixel]; // wasteful, but gets you 0 bytes every time - content is immaterial. rowForDecompressor[0] = row; compressor.jpeg_write_scanlines(rowForDecompressor, 1); } compressor.jpeg_finish_compress(); byte[] bytes = stream.ToArray(); string filename = "TestCompressorWithContextRows.jpg"; File.WriteAllBytes(Tester.MapOutputPath(filename), bytes); FileAssert.AreEqual(Tester.MapExpectedPath(filename), Tester.MapOutputPath(filename)); } }
public my_c_coef_controller(jpeg_compress_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. */ for (int ci = 0; ci < cinfo.m_num_components; ci++) { m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( JpegUtils.jround_up(cinfo.Component_info[ci].Width_in_blocks, cinfo.Component_info[ci].H_samp_factor), JpegUtils.jround_up(cinfo.Component_info[ci].height_in_blocks, cinfo.Component_info[ci].V_samp_factor)); m_whole_image[ci].ErrorProcessor = cinfo; } } else { /* We only need a single-MCU buffer. */ JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) { buffer[i] = new JBLOCK(); } for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) { m_MCU_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) { m_MCU_buffer[i][j - i] = buffer[j]; } } /* flag for no virtual arrays */ m_whole_image[0] = null; } }
/// <summary> /// Initialize coefficient buffer controller. /// /// Each passed coefficient array must be the right size for that /// coefficient: width_in_blocks wide and height_in_blocks high, /// with unit height at least v_samp_factor. /// </summary> public my_trans_c_coef_controller(jpeg_compress_struct cinfo, jvirt_array <JBLOCK>[] coef_arrays) { m_cinfo = cinfo; /* Save pointer to virtual arrays */ m_whole_image = coef_arrays; /* Allocate and pre-zero space for dummy DCT blocks. */ JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) { buffer[i] = new JBLOCK(); } for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) { m_dummy_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) { m_dummy_buffer[i][j - i] = buffer[j]; } } }
private int m_scan_number; /* current index in scan_info[] */ public jpeg_comp_master(jpeg_compress_struct cinfo, bool transcode_only) { m_cinfo = cinfo; if (transcode_only) { /* no main pass in transcoding */ if (cinfo.m_optimize_coding) m_pass_type = c_pass_type.huff_opt_pass; else m_pass_type = c_pass_type.output_pass; } else { /* for normal compression, first pass is always this type: */ m_pass_type = c_pass_type.main_pass; } if (cinfo.m_optimize_coding) m_total_passes = cinfo.m_num_scans * 2; else m_total_passes = cinfo.m_num_scans; }
public static byte[] CompressJPEG(byte[] uncompressedFileData, int vWidth, int vHeight) { using (MemoryStream stream = new MemoryStream()) { jpeg_compress_struct compressor = new jpeg_compress_struct(new jpeg_error_mgr()); compressor.Image_height = vHeight; compressor.Image_width = vWidth; compressor.In_color_space = J_COLOR_SPACE.JCS_RGB; compressor.Input_components = 3; compressor.jpeg_set_defaults(); compressor.Dct_method = J_DCT_METHOD.JDCT_IFAST; compressor.Smoothing_factor = 0; compressor.jpeg_set_quality(80, true); compressor.jpeg_simple_progression(); compressor.Density_unit = DensityUnit.Unknown; compressor.X_density = (short)192; compressor.Y_density = (short)192; compressor.jpeg_stdio_dest(stream); compressor.jpeg_start_compress(true); byte[][] rowForDecompressor = new byte[1][]; int bytesPerPixel = 3; byte[] row = new byte[vWidth * bytesPerPixel]; // wasteful, but gets you 0 bytes every time - content is immaterial. while (compressor.Next_scanline < compressor.Image_height) { Buffer.BlockCopy(uncompressedFileData, compressor.Next_scanline * vWidth * bytesPerPixel, row, 0, vWidth * bytesPerPixel); rowForDecompressor[0] = row; compressor.jpeg_write_scanlines(rowForDecompressor, 1); } compressor.jpeg_finish_compress(); return(stream.ToArray()); } }
private byte[] m_buffer; /* start of buffer */ public my_destination_mgr(jpeg_compress_struct cinfo, Stream alreadyOpenFile) { m_cinfo = cinfo; m_outfile = alreadyOpenFile; }
private bool m_need_context_rows; /* true if need rows above & below */ public jpeg_downsampler(jpeg_compress_struct cinfo) { m_cinfo = cinfo; m_need_context_rows = false; if (cinfo.m_CCIR601_sampling) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); } /* Verify we can handle the sampling factors, and set up method pointers */ bool smoothok = true; for (int ci = 0; ci < cinfo.m_num_components; ci++) { jpeg_component_info componentInfo = cinfo.Component_info[ci]; if (componentInfo.H_samp_factor == cinfo.m_max_h_samp_factor && componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) { if (cinfo.m_smoothing_factor != 0) { m_downSamplers[ci] = downSampleMethod.fullsize_smooth_downsampler; m_need_context_rows = true; } else { m_downSamplers[ci] = downSampleMethod.fullsize_downsampler; } } else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) { smoothok = false; m_downSamplers[ci] = downSampleMethod.h2v1_downsampler; } else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && componentInfo.V_samp_factor * 2 == cinfo.m_max_v_samp_factor) { if (cinfo.m_smoothing_factor != 0) { m_downSamplers[ci] = downSampleMethod.h2v2_smooth_downsampler; m_need_context_rows = true; } else { m_downSamplers[ci] = downSampleMethod.h2v2_downsampler; } } else if ((cinfo.m_max_h_samp_factor % componentInfo.H_samp_factor) == 0 && (cinfo.m_max_v_samp_factor % componentInfo.V_samp_factor) == 0) { smoothok = false; m_downSamplers[ci] = downSampleMethod.int_downsampler; } else { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); } } if (cinfo.m_smoothing_factor != 0 && !smoothok) { cinfo.TRACEMS(0, J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL); } }
static bool set_quant_slots(jpeg_compress_struct cinfo, string arg) { // not implemented yet return(false); }
static bool read_quant_tables(jpeg_compress_struct cinfo, string filename, bool force_baseline) { // not implemented yet return(false); }
/// <summary> /// Parse optional switches. /// Returns true if switches were parsed successfully; false otherwise. /// fileIndex receives index of first file-name argument (== -1 if none). /// for_real is false on the first (dummy) pass; we may skip any expensive /// processing. /// </summary> static bool parse_switches(jpeg_compress_struct cinfo, string[] argv, bool for_real, out int fileIndex) { /* Set up default JPEG parameters. */ bool force_baseline = false; /* by default, allow 16-bit quantizers */ bool simple_progressive = false; string qualityarg = null; /* saves -quality parm if any */ string qtablefile = null; /* saves -qtables filename if any */ string qslotsarg = null; /* saves -qslots parm if any */ string samplearg = null; /* saves -sample parm if any */ outfilename = null; fileIndex = -1; cinfo.Err.Trace_level = 0; /* Scan command line options, adjust parameters */ int argn = 0; for (; argn < argv.Length; argn++) { string arg = argv[argn]; if (string.IsNullOrEmpty(arg) || arg[0] != '-') { /* Not a switch, must be a file name argument */ fileIndex = argn; break; } arg = arg.Substring(1); if (cdjpeg_utils.keymatch(arg, "baseline", 2)) { /* Force baseline-compatible output (8-bit quantizer values). */ force_baseline = true; } else if (cdjpeg_utils.keymatch(arg, "block", 2)) { /* Set DCT block size. */ argn++; /* advance to next argument */ if (argn >= argv.Length) { return(false); } int val; if (!int.TryParse(argv[argn], out val)) { return(false); } if (val < 1 || val > 16) { return(false); } cinfo.block_size = val; } else if (cdjpeg_utils.keymatch(arg, "dct", 2)) { /* Select DCT algorithm. */ argn++; /* advance to next argument */ if (argn >= argv.Length) { return(false); } if (cdjpeg_utils.keymatch(argv[argn], "int", 1)) { cinfo.Dct_method = J_DCT_METHOD.JDCT_ISLOW; } else if (cdjpeg_utils.keymatch(argv[argn], "fast", 2)) { cinfo.Dct_method = J_DCT_METHOD.JDCT_IFAST; } else if (cdjpeg_utils.keymatch(argv[argn], "float", 2)) { cinfo.Dct_method = J_DCT_METHOD.JDCT_FLOAT; } else { return(false); } } else if (cdjpeg_utils.keymatch(arg, "debug", 1) || cdjpeg_utils.keymatch(arg, "verbose", 1)) { /* Enable debug printouts. */ /* On first -d, print version identification */ if (!printed_version) { Console.Write(string.Format("Bit Miracle's CJPEG, version {0}\n{1}\n", jpeg_common_struct.Version, jpeg_common_struct.Copyright)); printed_version = true; } cinfo.Err.Trace_level++; } else if (cdjpeg_utils.keymatch(arg, "grayscale", 2) || cdjpeg_utils.keymatch(arg, "greyscale", 2)) { /* Force a monochrome JPEG file to be generated. */ cinfo.jpeg_set_colorspace(J_COLOR_SPACE.JCS_GRAYSCALE); } else if (cdjpeg_utils.keymatch(arg, "rgb", 3) || cdjpeg_utils.keymatch(arg, "rgb1", 4)) { /* Force an RGB JPEG file to be generated. */ /* Note: Entropy table assignment in Jpeg_color_space depends * on color_transform. */ cinfo.color_transform = (arg == "rgb") ? J_COLOR_TRANSFORM.JCT_SUBTRACT_GREEN : J_COLOR_TRANSFORM.JCT_NONE; cinfo.Jpeg_color_space = J_COLOR_SPACE.JCS_RGB; } else if (cdjpeg_utils.keymatch(arg, "bgycc", 5)) { /* Force a big gamut YCC JPEG file to be generated. */ cinfo.Jpeg_color_space = J_COLOR_SPACE.JCS_BG_YCC; } else if (cdjpeg_utils.keymatch(arg, "optimize", 1) || cdjpeg_utils.keymatch(arg, "optimise", 1)) { /* Enable entropy parm optimization. */ cinfo.Optimize_coding = true; } else if (cdjpeg_utils.keymatch(arg, "nosmooth", 3)) { /* Suppress fancy downsampling. */ cinfo.do_fancy_downsampling = false; } else if (cdjpeg_utils.keymatch(arg, "outfile", 4)) { /* Set output file name. */ argn++;/* advance to next argument */ if (argn >= argv.Length) { return(false); } outfilename = argv[argn]; /* save it away for later use */ } else if (cdjpeg_utils.keymatch(arg, "progressive", 1)) { /* Select simple progressive mode. */ simple_progressive = true; /* We must postpone execution until num_components is known. */ } else if (cdjpeg_utils.keymatch(arg, "quality", 1)) { /* Quality ratings (quantization table scaling factors). */ argn++;/* advance to next argument */ if (argn >= argv.Length) { return(false); } qualityarg = argv[argn]; } else if (cdjpeg_utils.keymatch(arg, "qslots", 2)) { /* Quantization table slot numbers. */ argn++; /* advance to next argument */ if (argn >= argv.Length) { return(false); } qslotsarg = argv[argn]; /* Must delay setting qslots until after we have processed any * colorspace-determining switches, since jpeg_set_colorspace sets * default quant table numbers. */ } else if (cdjpeg_utils.keymatch(arg, "qtables", 2)) { /* Quantization tables fetched from file. */ argn++; /* advance to next argument */ if (argn >= argv.Length) { return(false); } qtablefile = argv[argn]; /* We postpone actually reading the file in case -quality comes later. */ } else if (cdjpeg_utils.keymatch(arg, "restart", 1)) { /* Restart interval in MCU rows (or in MCUs with 'b'). */ argn++; /* advance to next argument */ if (argn >= argv.Length) { return(false); } bool inBlocks = false; if (argv[argn].EndsWith("b") || argv[argn].EndsWith("B")) { inBlocks = true; } string parsee = argv[argn]; if (inBlocks) { parsee = parsee.Remove(parsee.Length - 1); } try { int val = int.Parse(parsee); if (val < 0 || val > 65535) { return(false); } if (inBlocks) { cinfo.Restart_interval = val; cinfo.Restart_in_rows = 0; /* else prior '-restart n' overrides me */ } else { cinfo.Restart_in_rows = val; /* restart_interval will be computed during startup */ } } catch (Exception e) { Console.WriteLine(e.Message); return(false); } } else if (cdjpeg_utils.keymatch(arg, "sample", 2)) { /* Set sampling factors. */ argn++; /* advance to next argument */ if (argn >= argv.Length) { return(false); } samplearg = argv[argn]; /* Must delay setting sample factors until after we have processed any * colorspace-determining switches, since jpeg_set_colorspace sets * default sampling factors. */ } else if (cdjpeg_utils.keymatch(arg, "scale", 4)) { /* Scale the image by a fraction M/N. */ argn++; /* advance to next argument */ if (argn >= argv.Length) { return(false); } string[] parts = argv[argn].Split(','); if (parts.Length != 2) { return(false); } if (!int.TryParse(parts[0], out cinfo.scale_num)) { return(false); } if (!int.TryParse(parts[1], out cinfo.scale_denom)) { return(false); } } else if (cdjpeg_utils.keymatch(arg, "smooth", 2)) { /* Set input smoothing factor. */ argn++; /* advance to next argument */ if (argn >= argv.Length) { return(false); } try { int val = int.Parse(argv[argn]); if (val < 0 || val > 100) { return(false); } cinfo.Smoothing_factor = val; } catch (Exception e) { Console.WriteLine(e.Message); return(false); } } else { /* bogus switch */ return(false); } } /* Post-switch-scanning cleanup */ if (for_real) { /* Set quantization tables for selected quality. */ /* Some or all may be overridden if -qtables is present. */ if (qualityarg != null) { if (!set_quality_ratings(cinfo, qualityarg, force_baseline)) { return(false); } } if (qtablefile != null) /* process -qtables if it was present */ { if (!read_quant_tables(cinfo, qtablefile, force_baseline)) { return(false); } } if (qslotsarg != null) /* process -qslots if it was present */ { if (!set_quant_slots(cinfo, qslotsarg)) { return(false); } } if (samplearg != null) /* process -sample if it was present */ { if (!set_sample_factors(cinfo, samplearg)) { return(false); } } if (simple_progressive) /* process -progressive; -scans can override */ { cinfo.jpeg_simple_progression(); } } return(true); }
extern public static void jpeg_simple_progression (ref jpeg_compress_struct cinfo);
// Empty an output buffer to a stream. private static Int empty_output_buffer(ref jpeg_compress_struct cinfo) { int len; StreamState state = GetStreamState(ref cinfo); len = state.buffer.Length; Marshal.Copy(state.buf, state.buffer, 0, len); state.stream.Write(state.buffer, 0, len); cinfo.dest->next_output_byte = state.buf; cinfo.dest->free_in_buffer = (size_t)len; return (Int)1; }
// Terminate an output destination. private static void term_destination(ref jpeg_compress_struct cinfo) { // Nothing to do here. }
public jpeg_color_converter(jpeg_compress_struct cinfo) { m_cinfo = cinfo; /* set start_pass to null method until we find out differently */ m_useNullStart = true; /* Make sure input_components agrees with in_color_space */ switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if (cinfo.m_input_components != 1) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_BG_RGB: if (cinfo.m_input_components != JpegConstants.RGB_PIXELSIZE) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; case J_COLOR_SPACE.JCS_YCbCr: case J_COLOR_SPACE.JCS_BG_YCC: if (cinfo.m_input_components != 3) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; case J_COLOR_SPACE.JCS_CMYK: case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_input_components != 4) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; default: /* JCS_UNKNOWN can be anything */ if (cinfo.m_input_components < 1) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; } /* Support color transform only for RGB colorspaces */ if (cinfo.color_transform != J_COLOR_TRANSFORM.JCT_NONE && cinfo.Jpeg_color_space != J_COLOR_SPACE.JCS_RGB && cinfo.Jpeg_color_space != J_COLOR_SPACE.JCS_BG_RGB) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } /* Check num_components, set conversion method based on requested 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); } switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: case J_COLOR_SPACE.JCS_YCbCr: case J_COLOR_SPACE.JCS_BG_YCC: color_convert = grayscale_convert; break; case J_COLOR_SPACE.JCS_RGB: m_useNullStart = false; // use rgb_ycc_start color_convert = rgb_gray_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_BG_RGB: if (cinfo.m_num_components != 3) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.m_in_color_space == cinfo.Jpeg_color_space) { switch (cinfo.color_transform) { case J_COLOR_TRANSFORM.JCT_NONE: color_convert = rgb_convert; break; case J_COLOR_TRANSFORM.JCT_SUBTRACT_GREEN: color_convert = rgb_rgb1_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_YCbCr: if (cinfo.m_num_components != 3) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_RGB: m_useNullStart = false; // use rgb_ycc_start color_convert = rgb_ycc_convert; break; case J_COLOR_SPACE.JCS_YCbCr: color_convert = null_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_BG_YCC: if (cinfo.m_num_components != 3) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_RGB: /* For conversion from normal RGB input to BG_YCC representation, * the Cb/Cr values are first computed as usual, and then * quantized further after DCT processing by a factor of * 2 in reference to the nominal quantization factor. */ /* need quantization scale by factor of 2 after DCT */ cinfo.Component_info[1].component_needed = true; cinfo.Component_info[2].component_needed = true; /* compute normal YCC first */ m_useNullStart = false; // use rgb_ycc_start color_convert = rgb_ycc_convert; break; case J_COLOR_SPACE.JCS_YCbCr: /* need quantization scale by factor of 2 after DCT */ cinfo.Component_info[1].component_needed = true; cinfo.Component_info[2].component_needed = true; color_convert = null_convert; break; case J_COLOR_SPACE.JCS_BG_YCC: /* Pass through for BG_YCC input */ color_convert = null_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; } break; case J_COLOR_SPACE.JCS_CMYK: if (cinfo.m_num_components != 4) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) { color_convert = null_convert; } else { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_num_components != 4) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_CMYK: m_useNullStart = false; // use rgb_ycc_start color_convert = cmyk_ycck_convert; break; case J_COLOR_SPACE.JCS_YCCK: color_convert = null_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; default: /* allow null conversion of JCS_UNKNOWN */ if (cinfo.m_jpeg_color_space != cinfo.m_in_color_space || cinfo.m_num_components != cinfo.m_input_components) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } color_convert = null_convert; break; } }
private bool m_need_context_rows; /* true if need rows above & below */ public jpeg_downsampler(jpeg_compress_struct cinfo) { m_cinfo = cinfo; m_need_context_rows = false; if (cinfo.m_CCIR601_sampling) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); /* Verify we can handle the sampling factors, and set up method pointers */ bool smoothok = true; for (int ci = 0; ci < cinfo.m_num_components; ci++) { jpeg_component_info componentInfo = cinfo.Component_info[ci]; /* Compute size of an "output group" for DCT scaling. This many samples * are to be converted from max_h_samp_factor * max_v_samp_factor pixels. */ int h_out_group = (componentInfo.H_samp_factor * componentInfo.DCT_h_scaled_size) / m_cinfo.min_DCT_h_scaled_size; int v_out_group = (componentInfo.V_samp_factor * componentInfo.DCT_v_scaled_size) / m_cinfo.min_DCT_v_scaled_size; int h_in_group = m_cinfo.m_max_h_samp_factor; int v_in_group = m_cinfo.m_max_v_samp_factor; rowgroup_height[ci] = v_out_group; /* save for use later */ if (h_in_group == h_out_group && v_in_group == v_out_group) { if (cinfo.m_smoothing_factor != 0) { m_downSamplers[ci] = downSampleMethod.fullsize_smooth_downsampler; m_need_context_rows = true; } else { m_downSamplers[ci] = downSampleMethod.fullsize_downsampler; } } else if (h_in_group == h_out_group * 2 && v_in_group == v_out_group) { smoothok = false; m_downSamplers[ci] = downSampleMethod.h2v1_downsampler; } else if (h_in_group == h_out_group * 2 && v_in_group == v_out_group * 2) { if (cinfo.m_smoothing_factor != 0) { m_downSamplers[ci] = downSampleMethod.h2v2_smooth_downsampler; m_need_context_rows = true; } else { m_downSamplers[ci] = downSampleMethod.h2v2_downsampler; } } else if ((h_in_group % h_out_group) == 0 && (v_in_group % v_out_group) == 0) { smoothok = false; m_downSamplers[ci] = downSampleMethod.int_downsampler; h_expand[ci] = (byte)(h_in_group / h_out_group); v_expand[ci] = (byte)(v_in_group / v_out_group); } else { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); } } if (cinfo.m_smoothing_factor != 0 && !smoothok) cinfo.TRACEMS(0, J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL); }
static string outfilename; /* for -outfile switch */ public static void Main(string[] args) { progname = Path.GetFileName(Environment.GetCommandLineArgs()[0]); cd_jpeg_error_mgr err = new cd_jpeg_error_mgr(); jpeg_compress_struct cinfo = new jpeg_compress_struct(err); /* Initialize JPEG parameters. * Much of this may be overridden later. * In particular, we don't yet know the input file's color space, * but we need to provide some value for jpeg_set_defaults() to work. */ cinfo.In_color_space = J_COLOR_SPACE.JCS_RGB; /* arbitrary guess */ cinfo.jpeg_set_defaults(); /* Scan command line to find file names. * It is convenient to use just one switch-parsing routine, but the switch * values read here are ignored; we will rescan the switches after opening * the input file. */ int file_index; if (!parse_switches(cinfo, args, false, out file_index)) { usage(); return; } /* Must have either -outfile switch or explicit output file name */ if (outfilename == null) { // file_index should point to input file if (file_index != args.Length - 2) { Console.WriteLine(string.Format("{0}: must name one input and one output file.", progname)); usage(); return; } // output file comes right after input one outfilename = args[file_index + 1]; } else { // file_index should point to input file if (file_index != args.Length - 1) { Console.WriteLine(string.Format("{0}: must name one input and one output file.", progname)); usage(); return; } } /* Open the input file. */ FileStream input_file = null; if (file_index < args.Length) { try { input_file = new FileStream(args[file_index], FileMode.Open); } catch (Exception e) { Console.WriteLine(string.Format("{0}: can't open {1}", progname, args[file_index])); Console.WriteLine(e.Message); return; } } else { Console.WriteLine(string.Format("{0}: sorry, can't read file from console")); return; } /* Open the output file. */ FileStream output_file = null; if (outfilename != null) { try { output_file = new FileStream(outfilename, FileMode.Create); } catch (Exception e) { Console.WriteLine(string.Format("{0}: can't open {1}", progname, args[file_index])); Console.WriteLine(e.Message); return; } } else { Console.WriteLine(string.Format("{0}: sorry, can't write file to console")); return; } /* Figure out the input file format, and set up to read it. */ cjpeg_source_struct src_mgr = new bmp_source_struct(cinfo); src_mgr.input_file = input_file; /* Read the input file header to obtain file size & colorspace. */ src_mgr.start_input(); /* Now that we know input colorspace, fix colorspace-dependent defaults */ cinfo.jpeg_default_colorspace(); /* Adjust default compression parameters by re-parsing the options */ parse_switches(cinfo, args, true, out file_index); /* Specify data destination for compression */ cinfo.jpeg_stdio_dest(output_file); /* Start compressor */ cinfo.jpeg_start_compress(true); /* Process data */ while (cinfo.Next_scanline < cinfo.Image_height) { int num_scanlines = src_mgr.get_pixel_rows(); cinfo.jpeg_write_scanlines(src_mgr.buffer, num_scanlines); } /* Finish compression and release memory */ src_mgr.finish_input(); cinfo.jpeg_finish_compress(); /* Close files, if we opened them */ input_file.Close(); input_file.Dispose(); output_file.Close(); output_file.Dispose(); /* All done. */ if (cinfo.Err.Num_warnings != 0) { Console.WriteLine("Corrupt-data warning count is not zero"); } }
// Initialize a stream data destination. private static void init_destination(ref jpeg_compress_struct cinfo) { // Nothing to do here: already initialized. }
extern public static UInt jpeg_write_scanlines (ref jpeg_compress_struct cinfo, ref IntPtr scanline, UInt num_lines);
public jpeg_forward_dct(jpeg_compress_struct cinfo) { m_cinfo = cinfo; m_dctTables = new divisor_table[m_cinfo.m_num_components]; for (int ci = 0; ci < m_cinfo.m_num_components; ci++) { /* Allocate a divisor table for each component */ m_dctTables[ci] = new divisor_table(); } }
static bool set_sample_factors(jpeg_compress_struct cinfo, string arg) { // not implemented yet return(false); }
// Get the stream state for a compress structure. private static StreamState GetStreamState(ref jpeg_compress_struct cinfo) { GCHandle handle = (GCHandle)(cinfo.client_data); return (StreamState)(handle.Target); }
extern public static void jpeg_start_compress (ref jpeg_compress_struct cinfo, Int write_all_tables);
extern public static void jpeg_abort_compress (ref jpeg_compress_struct cinfo);
private int m_last_restart_interval; /* last DRI value emitted; 0 after SOI */ #endregion Fields #region Constructors public jpeg_marker_writer(jpeg_compress_struct cinfo) { m_cinfo = cinfo; }
private static bool applyOptions(jpeg_compress_struct compressor, CompressOptions options) { compressor.jpeg_set_quality(options.Quality, options.ForceBaseline); compressor.Dct_method = options.DCTMethod; if (options.Debug) { compressor.Err.Trace_level = 1; } if (options.Grayscale) { compressor.jpeg_set_colorspace(J_COLOR_SPACE.JCS_GRAYSCALE); } if (options.Optimize) { compressor.Optimize_coding = true; } compressor.Restart_interval = options.RestartInterval; compressor.Restart_in_rows = options.RestartInRows; compressor.Smoothing_factor = options.SmoothingFactor; int q_scale_factor = 100; if (options.Quality != 75) { q_scale_factor = jpeg_compress_struct.jpeg_quality_scaling(options.Quality); } /* Set quantization tables for selected quality. */ /* Some or all may be overridden if -qtables is present. */ if (options.Qtables != "") /* process -qtables if it was present */ { if (!read_quant_tables(compressor, options.Qtables, q_scale_factor, options.ForceBaseline)) { return(false); } } if (options.Qslots != "") /* process -qslots if it was present */ { if (!set_quant_slots(compressor, options.Qslots)) { return(false); } } if (options.Sample != "") /* process -sample if it was present */ { if (!set_sample_factors(compressor, options.Sample)) { return(false); } } if (options.Progressive) /* process -progressive; -scans can override */ { compressor.jpeg_simple_progression(); } return(true); }
extern public static void *jpeg_alloc_huff_table (ref jpeg_compress_struct cinfo);
extern public static void jpeg_write_tables (ref jpeg_compress_struct cinfo);
extern public static void jpeg_suppress_tables (ref jpeg_compress_struct cinfo, Int suppress);
private int[] m_rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ public jpeg_color_converter(jpeg_compress_struct cinfo) { m_cinfo = cinfo; /* set start_pass to null method until we find out differently */ m_useNullStart = true; /* Make sure input_components agrees with in_color_space */ switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if (cinfo.m_input_components != 1) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_YCbCr: if (cinfo.m_input_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; case J_COLOR_SPACE.JCS_CMYK: case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_input_components != 4) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; default: /* JCS_UNKNOWN can be anything */ if (cinfo.m_input_components < 1) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; } /* Check num_components, set conversion method based on requested space */ clearConvertFlags(); 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); switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: m_useGrayscaleConvert = true; break; case J_COLOR_SPACE.JCS_RGB: m_useNullStart = false; // use rgb_ycc_start m_useRgbGrayConvert = true; break; case J_COLOR_SPACE.JCS_YCbCr: m_useGrayscaleConvert = true; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_RGB: if (cinfo.m_num_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) m_useNullConvert = true; else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_YCbCr: if (cinfo.m_num_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) { m_useNullStart = false; // use rgb_ycc_start m_useRgbYccConvert = true; } else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCbCr) m_useNullConvert = true; else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_CMYK: if (cinfo.m_num_components != 4) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) m_useNullConvert = true; else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_num_components != 4) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) { m_useNullStart = false; // use rgb_ycc_start m_useCmykYcckConvert = true; } else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCCK) m_useNullConvert = true; else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; default: /* allow null conversion of JCS_UNKNOWN */ if (cinfo.m_jpeg_color_space != cinfo.m_in_color_space || cinfo.m_num_components != cinfo.m_input_components) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); m_useNullConvert = true; break; } }
extern public static void jpeg_add_quant_table (ref jpeg_compress_struct cinfo, Int which_tbl, void *basic_table, Int scale_factor, Int force_baseline);
public jpeg_color_converter(jpeg_compress_struct cinfo) { m_cinfo = cinfo; /* set start_pass to null method until we find out differently */ m_useNullStart = true; /* Make sure input_components agrees with in_color_space */ switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if (cinfo.m_input_components != 1) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_BG_RGB: if (cinfo.m_input_components != JpegConstants.RGB_PIXELSIZE) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; case J_COLOR_SPACE.JCS_YCbCr: case J_COLOR_SPACE.JCS_BG_YCC: if (cinfo.m_input_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; case J_COLOR_SPACE.JCS_CMYK: case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_input_components != 4) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; default: /* JCS_UNKNOWN can be anything */ if (cinfo.m_input_components < 1) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); break; } /* Support color transform only for RGB colorspaces */ if (cinfo.color_transform != J_COLOR_TRANSFORM.JCT_NONE && cinfo.Jpeg_color_space != J_COLOR_SPACE.JCS_RGB && cinfo.Jpeg_color_space != J_COLOR_SPACE.JCS_BG_RGB) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } /* Check num_components, set conversion method based on requested 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); switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: case J_COLOR_SPACE.JCS_YCbCr: case J_COLOR_SPACE.JCS_BG_YCC: color_convert = grayscale_convert; break; case J_COLOR_SPACE.JCS_RGB: m_useNullStart = false; // use rgb_ycc_start color_convert = rgb_gray_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_BG_RGB: if (cinfo.m_num_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if (cinfo.m_in_color_space == cinfo.Jpeg_color_space) { switch (cinfo.color_transform) { case J_COLOR_TRANSFORM.JCT_NONE: color_convert = rgb_convert; break; case J_COLOR_TRANSFORM.JCT_SUBTRACT_GREEN: color_convert = rgb_rgb1_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_YCbCr: if (cinfo.m_num_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_RGB: m_useNullStart = false; // use rgb_ycc_start color_convert = rgb_ycc_convert; break; case J_COLOR_SPACE.JCS_YCbCr: color_convert = null_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_BG_YCC: if (cinfo.m_num_components != 3) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_RGB: /* For conversion from normal RGB input to BG_YCC representation, * the Cb/Cr values are first computed as usual, and then * quantized further after DCT processing by a factor of * 2 in reference to the nominal quantization factor. */ /* need quantization scale by factor of 2 after DCT */ cinfo.Component_info[1].component_needed = true; cinfo.Component_info[2].component_needed = true; /* compute normal YCC first */ m_useNullStart = false; // use rgb_ycc_start color_convert = rgb_ycc_convert; break; case J_COLOR_SPACE.JCS_YCbCr: /* need quantization scale by factor of 2 after DCT */ cinfo.Component_info[1].component_needed = true; cinfo.Component_info[2].component_needed = true; color_convert = null_convert; break; case J_COLOR_SPACE.JCS_BG_YCC: /* Pass through for BG_YCC input */ color_convert = null_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); break; } break; case J_COLOR_SPACE.JCS_CMYK: if (cinfo.m_num_components != 4) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) color_convert = null_convert; else cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_num_components != 4) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_CMYK: m_useNullStart = false; // use rgb_ycc_start color_convert = cmyk_ycck_convert; break; case J_COLOR_SPACE.JCS_YCCK: color_convert = null_convert; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; default: /* allow null conversion of JCS_UNKNOWN */ if (cinfo.m_jpeg_color_space != cinfo.m_in_color_space || cinfo.m_num_components != cinfo.m_input_components) cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); color_convert = null_convert; break; } }
private int[] m_rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ public jpeg_color_converter(jpeg_compress_struct cinfo) { m_cinfo = cinfo; /* set start_pass to null method until we find out differently */ m_useNullStart = true; /* Make sure input_components agrees with in_color_space */ switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: if (cinfo.m_input_components != 1) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; case J_COLOR_SPACE.JCS_RGB: case J_COLOR_SPACE.JCS_YCbCr: if (cinfo.m_input_components != 3) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; case J_COLOR_SPACE.JCS_CMYK: case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_input_components != 4) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; default: /* JCS_UNKNOWN can be anything */ if (cinfo.m_input_components < 1) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); } break; } /* Check num_components, set conversion method based on requested space */ clearConvertFlags(); 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); } switch (cinfo.m_in_color_space) { case J_COLOR_SPACE.JCS_GRAYSCALE: m_useGrayscaleConvert = true; break; case J_COLOR_SPACE.JCS_RGB: m_useNullStart = false; // use rgb_ycc_start m_useRgbGrayConvert = true; break; case J_COLOR_SPACE.JCS_YCbCr: m_useGrayscaleConvert = true; break; default: cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); break; } break; case J_COLOR_SPACE.JCS_RGB: if (cinfo.m_num_components != 3) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) { m_useNullConvert = true; } else { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; case J_COLOR_SPACE.JCS_YCbCr: if (cinfo.m_num_components != 3) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) { m_useNullStart = false; // use rgb_ycc_start m_useRgbYccConvert = true; } else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCbCr) { m_useNullConvert = true; } else { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; case J_COLOR_SPACE.JCS_CMYK: if (cinfo.m_num_components != 4) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) { m_useNullConvert = true; } else { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; case J_COLOR_SPACE.JCS_YCCK: if (cinfo.m_num_components != 4) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); } if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) { m_useNullStart = false; // use rgb_ycc_start m_useCmykYcckConvert = true; } else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCCK) { m_useNullConvert = true; } else { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } break; default: /* allow null conversion of JCS_UNKNOWN */ if (cinfo.m_jpeg_color_space != cinfo.m_in_color_space || cinfo.m_num_components != cinfo.m_input_components) { cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); } m_useNullConvert = true; break; } }
// the algorithm flow is based on: // https://bitmiracle.github.io/libjpeg.net/help/articles/KB/compression-details.html private static byte[] CompressRawToJpeg(byte[] input, int width, int height, int scale, int quality, Tuple <int, int> crop) { jpeg_error_mgr errorManager = new jpeg_error_mgr(); jpeg_compress_struct cinfo = new jpeg_compress_struct(errorManager); var memoryStream = new MemoryStream(); cinfo.jpeg_stdio_dest(memoryStream); var widthToSkip = 0; var widthToSkipFront = 0; var widthToSkipBack = 0; if (crop != null && width > crop.Item1) { widthToSkip = (width - crop.Item1); widthToSkipFront = widthToSkip / 2; widthToSkipBack = widthToSkip - widthToSkipFront; // to handle odd 'widthToSkip' values } var heightToSkip = 0; var heightToSkipTop = 0; if (crop != null && height > crop.Item2) { heightToSkip = (height - crop.Item2); heightToSkipTop = heightToSkip / 2; } cinfo.Image_width = (width - widthToSkip) / scale; cinfo.Image_height = (height - heightToSkip) / scale; cinfo.Input_components = 3; cinfo.In_color_space = J_COLOR_SPACE.JCS_RGB; cinfo.jpeg_set_defaults(); if (quality != -1) { cinfo.jpeg_set_quality(quality, true); } cinfo.jpeg_start_compress(true); int row_stride = cinfo.Image_width * 3; // physical row width in buffer byte[][] rowData = new byte[1][]; // single row rowData[0] = new byte[row_stride]; int inputOffset = heightToSkipTop * width; while (cinfo.Next_scanline < cinfo.Image_height) { // crop pixels at the beginning of the line inputOffset += 3 * widthToSkipFront; for (int i = 0; i < rowData[0].Length - 2; i += 3) { rowData[0][i] = input[inputOffset]; rowData[0][i + 1] = input[inputOffset + 1]; rowData[0][i + 2] = input[inputOffset + 2]; inputOffset += 3 * scale; } // crop pixels at the end of the line inputOffset += 3 * widthToSkipBack; // drop some lines due to scaling inputOffset += 3 * (scale - 1) * width; cinfo.jpeg_write_scanlines(rowData, 1); } cinfo.jpeg_finish_compress(); var result = memoryStream.ToArray(); memoryStream.Close(); return(result); }
extern public static void jpeg_finish_compress (ref jpeg_compress_struct cinfo);