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"); }
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)); } }
static bool set_sample_factors(jpeg_compress_struct cinfo, string arg) { // not implemented yet return false; }
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, int scale_factor, bool force_baseline) { // not implemented yet return false; }
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; }
/// <summary> /// Initializes the compression object with default parameters, then copy from the source object /// all parameters needed for lossless transcoding. /// </summary> /// <param name="dstinfo">Target JPEG compression object.</param> /// <remarks>Parameters that can be varied without loss (such as scan script and /// Huffman optimization) are left in their default states.</remarks> public void jpeg_copy_critical_parameters(jpeg_compress_struct dstinfo) { /* Safety check to ensure start_compress not called yet. */ if (dstinfo.m_global_state != JpegState.CSTATE_START) ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)dstinfo.m_global_state); /* Copy fundamental image dimensions */ dstinfo.m_image_width = m_image_width; dstinfo.m_image_height = m_image_height; dstinfo.m_input_components = m_num_components; dstinfo.m_in_color_space = m_jpeg_color_space; /* Initialize all parameters to default values */ dstinfo.jpeg_set_defaults(); /* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB. * Fix it to get the right header markers for the image colorspace. */ dstinfo.jpeg_set_colorspace(m_jpeg_color_space); dstinfo.m_data_precision = m_data_precision; dstinfo.m_CCIR601_sampling = m_CCIR601_sampling; /* Copy the source's quantization tables. */ for (int tblno = 0; tblno < JpegConstants.NUM_QUANT_TBLS; tblno++) { if (m_quant_tbl_ptrs[tblno] != null) { if (dstinfo.m_quant_tbl_ptrs[tblno] == null) dstinfo.m_quant_tbl_ptrs[tblno] = new JQUANT_TBL(); Buffer.BlockCopy(m_quant_tbl_ptrs[tblno].quantval, 0, dstinfo.m_quant_tbl_ptrs[tblno].quantval, 0, dstinfo.m_quant_tbl_ptrs[tblno].quantval.Length * sizeof(short)); dstinfo.m_quant_tbl_ptrs[tblno].Sent_table = false; } } /* Copy the source's per-component info. * Note we assume jpeg_set_defaults has allocated the dest comp_info array. */ dstinfo.m_num_components = m_num_components; if (dstinfo.m_num_components < 1 || dstinfo.m_num_components> JpegConstants.MAX_COMPONENTS) ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, dstinfo.m_num_components, JpegConstants.MAX_COMPONENTS); for (int ci = 0; ci < dstinfo.m_num_components; ci++) { dstinfo.Component_info[ci].Component_id = m_comp_info[ci].Component_id; dstinfo.Component_info[ci].H_samp_factor = m_comp_info[ci].H_samp_factor; dstinfo.Component_info[ci].V_samp_factor = m_comp_info[ci].V_samp_factor; dstinfo.Component_info[ci].Quant_tbl_no = m_comp_info[ci].Quant_tbl_no; /* Make sure saved quantization table for component matches the qtable * slot. If not, the input file re-used this qtable slot. * IJG encoder currently cannot duplicate this. */ int tblno = dstinfo.Component_info[ci].Quant_tbl_no; if (tblno < 0 || tblno >= JpegConstants.NUM_QUANT_TBLS || m_quant_tbl_ptrs[tblno] == null) ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, tblno); JQUANT_TBL c_quant = m_comp_info[ci].quant_table; if (c_quant != null) { JQUANT_TBL slot_quant = m_quant_tbl_ptrs[tblno]; for (int coefi = 0; coefi < JpegConstants.DCTSIZE2; coefi++) { if (c_quant.quantval[coefi] != slot_quant.quantval[coefi]) ERREXIT(J_MESSAGE_CODE.JERR_MISMATCHED_QUANT_TABLE, tblno); } } /* Note: we do not copy the source's Huffman table assignments; * instead we rely on jpeg_set_colorspace to have made a suitable choice. */ } /* Also copy JFIF version and resolution information, if available. * Strictly speaking this isn't "critical" info, but it's nearly * always appropriate to copy it if available. In particular, * if the application chooses to copy JFIF 1.02 extension markers from * the source file, we need to copy the version to make sure we don't * emit a file that has 1.02 extensions but a claimed version of 1.01. * We will *not*, however, copy version info from mislabeled "2.01" files. */ if (m_saw_JFIF_marker) { if (m_JFIF_major_version == 1) { dstinfo.m_JFIF_major_version = m_JFIF_major_version; dstinfo.m_JFIF_minor_version = m_JFIF_minor_version; } dstinfo.m_density_unit = m_density_unit; dstinfo.m_X_density = (short)m_X_density; dstinfo.m_Y_density = (short)m_Y_density; } }
private void cleanState() { m_compression = null; m_decompression = null; m_common = null; m_h_sampling = 0; m_v_sampling = 0; m_jpegtables = null; m_jpegtables_length = 0; m_jpegquality = 0; m_jpegcolormode = 0; m_jpegtablesmode = 0; m_ycbcrsampling_fetched = false; m_recvparams = 0; m_subaddress = null; m_recvtime = 0; m_faxdcs = null; m_rawDecode = false; m_rawEncode = false; m_cinfo_initialized = false; m_err = null; m_photometric = 0; m_bytesperline = 0; m_ds_buffer = new byte[JpegConstants.MAX_COMPONENTS][][]; m_scancount = 0; m_samplesperclump = 0; }
/* * Interface routines. This layer of routines exists * primarily to limit side-effects from LibJpeg.Net exceptions. * Also, normal/error returns are converted into return * values per LibTiff.Net practice. */ private bool TIFFjpeg_create_compress() { /* initialize JPEG error handling */ try { m_compression = new jpeg_compress_struct(new JpegErrorManager(this)); m_common = m_compression; } catch (Exception) { return false; } return true; }
public bmp_source_struct(jpeg_compress_struct cinfo) { this.cinfo = cinfo; }
public static string IncImageDCT(string fullPath) { string suffix = "testDCT"; var img = new Bitmap(fullPath); var width = img.Width; var height = img.Height; int hd = height / 8; int wd = width / 8; img.Dispose(); BitMiracle.LibJpeg.Classic.jpeg_decompress_struct oJpegDecompress = new BitMiracle.LibJpeg.Classic.jpeg_decompress_struct(); System.IO.FileStream oFileStreamImage = new System.IO.FileStream(fullPath, System.IO.FileMode.Open, System.IO.FileAccess.Read); oJpegDecompress.jpeg_stdio_src(oFileStreamImage); oJpegDecompress.jpeg_read_header(true); BitMiracle.LibJpeg.Classic.jvirt_array<BitMiracle.LibJpeg.Classic.JBLOCK>[] JBlock = oJpegDecompress.jpeg_read_coefficients(); var block = JBlock[0].Access(0, hd); // accessing the element for (int i = 0; i < hd; i++) { for (int j = 0; j < wd; j++) { short t = block[i][j].data[0]; if ((t >= 0 && t % 2 == 1) || (t < 0 && t % 2 == 0)) { t--; } else if ((t >= 0 && t % 2 == 0) || (t < 0 && t % 2 == 1)) { t++; } block[i][j].data[0] = t; } } oJpegDecompress.jpeg_finish_decompress(); oFileStreamImage.Close(); //// string filenameNew = MyHelper.AppendFileName(fullPath, suffix); System.IO.FileStream objFileStreamMegaMap = System.IO.File.Create(filenameNew); BitMiracle.LibJpeg.Classic.jpeg_compress_struct oJpegCompress = new BitMiracle.LibJpeg.Classic.jpeg_compress_struct(); oJpegCompress.jpeg_stdio_dest(objFileStreamMegaMap); oJpegDecompress.jpeg_copy_critical_parameters(oJpegCompress); oJpegCompress.Image_height = height; oJpegCompress.Image_width = width; oJpegCompress.jpeg_write_coefficients(JBlock); oJpegCompress.jpeg_finish_compress(); objFileStreamMegaMap.Close(); oJpegDecompress.jpeg_abort_decompress(); return filenameNew; }
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"); }
/// <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. */ /* Note that default -quality level need not, and does not, * match the default scaling for an explicit -qtables argument. */ int quality = 75; /* default -quality value */ int q_scale_factor = 100; /* default to no scaling for -qtables */ bool force_baseline = false; /* by default, allow 16-bit quantizers */ bool simple_progressive = false; 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", 1)) { /* Force baseline-compatible output (8-bit quantizer values). */ force_baseline = true; } 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, "optimize", 1) || cdjpeg_utils.keymatch(arg, "optimise", 1)) { /* Enable entropy parm optimization. */ cinfo.Optimize_coding = true; } 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 factor (quantization table scaling factor). */ argn++;/* advance to next argument */ if (argn >= argv.Length) return false; try { quality = int.Parse(argv[argn]); /* Change scale factor in case -qtables is present. */ q_scale_factor = jpeg_compress_struct.jpeg_quality_scaling(quality); } catch (Exception e) { Console.WriteLine(e.Message); return false; } } 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, "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. */ cinfo.jpeg_set_quality(quality, force_baseline); if (qtablefile != null) /* process -qtables if it was present */ { if (!read_quant_tables(cinfo, qtablefile, q_scale_factor, 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; }