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 My1PassCQuantizer(JpegDecompressStruct 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.outColorComponents > MAX_Q_COMPS) { cinfo.ErrExit(JMessageCode.JERR_QUANT_COMPONENTS, MAX_Q_COMPS); } /* Make sure colormap indexes can be represented by JSAMPLEs */ if (cinfo.desiredNumberOfColors > (JpegConstants.MAXJSAMPLE + 1)) { cinfo.ErrExit(JMessageCode.JERR_QUANT_MANY_COLORS, JpegConstants.MAXJSAMPLE + 1); } /* Create the colormap and color index table. */ CreateColormap(); CreateColorIndex(); /* 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.ditherMode == JDitherMode.JDITHER_FS) { AllocFsWorkspace(); } }
/// <summary> /// Initialize for a processing pass. /// </summary> public void StartPass(JBufMode pass_mode) { switch (pass_mode) { case JBufMode.PassThrough: if (m_cinfo.m_upsample.NeedContextRows()) { m_dataProcessor = DataProcessor.context_main; MakeFunnyPointers(); /* Create the xbuffer[] lists */ m_whichFunny = 0; /* Read first iMCU row into xbuffer[0] */ m_context_state = CTX_PREPARE_FOR_IMCU; m_iMCU_row_ctr = 0; } else { /* Simple case with no context needed */ m_dataProcessor = DataProcessor.simple_main; } m_buffer_full = false; /* Mark buffer empty */ m_rowgroup_ctr = 0; break; case JBufMode.CrankDest: /* For last pass of 2-pass quantization, just crank the postprocessor */ m_dataProcessor = DataProcessor.crank_post; break; default: m_cinfo.ErrExit(JMessageCode.JERR_BAD_BUFFER_MODE); break; } }
/// <summary> /// Initialize for a processing pass. /// </summary> public void StartPass(JBufMode pass_mode) { switch (pass_mode) { case JBufMode.PassThrough: if (m_cinfo.quantizeColors) { /* Single-pass processing with color quantization. */ m_processor = ProcessorType.OnePass; /* We could be doing buffered-image output before starting a 2-pass * color quantization; in that case, jinit_d_post_controller did not * allocate a strip buffer. Use the virtual-array buffer as workspace. */ if (m_buffer is null) { m_buffer = m_whole_image.Access(0, m_strip_height); } } else { /* For single-pass processing without color quantization, * I have no work to do; just call the upsampler directly. */ m_processor = ProcessorType.Upsample; } break; case JBufMode.SaveAndPass: /* First pass of 2-pass quantization */ if (m_whole_image is null) { m_cinfo.ErrExit(JMessageCode.JERR_BAD_BUFFER_MODE); } m_processor = ProcessorType.PrePass; break; case JBufMode.CrankDest: /* Second pass of 2-pass quantization */ if (m_whole_image is null) { m_cinfo.ErrExit(JMessageCode.JERR_BAD_BUFFER_MODE); } m_processor = ProcessorType.SecondPass; break; default: m_cinfo.ErrExit(JMessageCode.JERR_BAD_BUFFER_MODE); break; } m_starting_row = m_next_row = 0; }
private int m_iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ public JpegDMainController(JpegDecompressStruct cinfo) { m_cinfo = cinfo; /* Allocate the workspace. * ngroups is the number of row groups we need. */ var ngroups = cinfo.min_DCT_v_scaled_size; if (cinfo.m_upsample.NeedContextRows()) { if (cinfo.min_DCT_v_scaled_size < 2) /* unsupported, see comments above */ { cinfo.ErrExit(JMessageCode.JERR_NOTIMPL); } AllocFunnyPointers(); /* Alloc space for xbuffer[] lists */ ngroups = cinfo.min_DCT_v_scaled_size + 2; } for (var ci = 0; ci < cinfo.numComponents; ci++) { /* height of a row group of component */ var rgroup = (cinfo.CompInfo[ci].V_samp_factor * cinfo.CompInfo[ci].DCT_v_scaled_size) / cinfo.min_DCT_v_scaled_size; m_buffer[ci] = JpegCommonStruct.AllocJpegSamples( cinfo.CompInfo[ci].Width_in_blocks * cinfo.CompInfo[ci].DCT_h_scaled_size, rgroup * ngroups); } }
private void UpSampleComponent(ref ComponentBuffer input_data) { switch (m_upsampleMethods[m_currentComponent]) { case ComponentUpsampler.noop_upsampler: NoOpUpSample(); break; case ComponentUpsampler.fullsize_upsampler: FullSizeUpSample(ref input_data); break; case ComponentUpsampler.h2v1_upsampler: H2V1UpSample(ref input_data); break; case ComponentUpsampler.h2v2_upsampler: H2V2UpSample(ref input_data); break; case ComponentUpsampler.int_upsampler: IntUpSample(ref input_data); break; default: m_cinfo.ErrExit(JMessageCode.JERR_NOTIMPL); break; } }
public ReadResult DecompressData(ComponentBuffer[] output_buf) { switch (m_decompressor) { case DecompressorType.Ordinary: return(DecompressDataOrdinary(output_buf)); case DecompressorType.Smooth: return(DecompressSmoothData(output_buf)); case DecompressorType.OnePass: return(DecompressOnePass(output_buf)); } m_cinfo.ErrExit(JMessageCode.JERR_NOTIMPL); return(0); }
/// <summary> /// <para>Fill the input buffer - called whenever buffer is emptied.</para> /// <para> /// In typical applications, this should read fresh data into the buffer /// (ignoring the current state of next_input_byte and bytes_in_buffer), /// reset the pointer and count to the start of the buffer, and return true /// indicating that the buffer has been reloaded. It is not necessary to /// fill the buffer entirely, only to obtain at least one more byte. /// </para> /// <para> /// There is no such thing as an EOF return. If the end of the file has been /// reached, the routine has a choice of ERREXIT() or inserting fake data into /// the buffer. In most cases, generating a warning message and inserting a /// fake EOI marker is the best course of action --- this will allow the /// decompressor to output however much of the image is there. However, /// the resulting error message is misleading if the real problem is an empty /// input file, so we handle that case specially. /// </para> /// <para> /// In applications that need to be able to suspend compression due to input /// not being available yet, a false return indicates that no more data can be /// obtained right now, but more may be forthcoming later. In this situation, /// the decompressor will return to its caller (with an indication of the /// number of scanlines it has read, if any). The application should resume /// decompression after it has loaded more data into the input buffer. Note /// that there are substantial restrictions on the use of suspension --- see /// the documentation. /// </para> /// <para> /// When suspending, the decompressor will back up to a convenient restart point /// (typically the start of the current MCU). next_input_byte and bytes_in_buffer /// indicate where the restart point will be if the current call returns false. /// Data beyond this point must be rescanned after resumption, so move it to /// the front of the buffer rather than discarding it. /// </para> /// </summary> public override bool FillInputBuffer() { var nbytes = m_infile.Read(m_buffer, 0, INPUT_BUF_SIZE); if (nbytes <= 0) { if (m_start_of_file) /* Treat empty input file as fatal error */ { m_cinfo.ErrExit(JMessageCode.JERR_INPUT_EMPTY); } m_cinfo.WarnMS(JMessageCode.JWRN_JPEG_EOF); /* Insert a fake EOI marker */ m_buffer[0] = 0xFF; m_buffer[1] = (byte)JpegMarker.EOI; nbytes = 2; } InitInternalBuffer(m_buffer, nbytes); m_start_of_file = false; return(true); }
public ArithEntropyDecoder(JpegDecompressStruct cinfo) { cinfo.ErrExit(JMessageCode.JERR_NOTIMPL); }
/// <summary> /// Per-pass setup. /// This is called at the beginning of each output pass. We determine which /// modules will be active during this pass and give them appropriate /// start_pass calls. We also set is_dummy_pass to indicate whether this /// is a "real" output pass or a dummy pass for color quantization. /// (In the latter case, we will crank the pass to completion.) /// </summary> public void PrepareForOutputPass() { if (m_is_dummy_pass) { /* Final pass of 2-pass quantization */ m_is_dummy_pass = false; m_cinfo.m_cquantize.StartPass(false); m_cinfo.m_post.StartPass(JBufMode.CrankDest); m_cinfo.m_main.StartPass(JBufMode.CrankDest); } else { if (m_cinfo.quantizeColors && m_cinfo.m_colormap is null) { /* Select new quantization method */ if (m_cinfo.twoPassQuantize && m_cinfo.enable2PassQuant) { m_cinfo.m_cquantize = m_quantizer_2pass; m_is_dummy_pass = true; } else if (m_cinfo.enable1PassQuant) { m_cinfo.m_cquantize = m_quantizer_1pass; } else { m_cinfo.ErrExit(JMessageCode.JERR_MODE_CHANGE); } } m_cinfo.m_idct.StartPass(); m_cinfo.m_coef.StartOutputPass(); if (!m_cinfo.rawDataOut) { m_cinfo.m_upsample.StartPass(); if (m_cinfo.quantizeColors) { m_cinfo.m_cquantize.StartPass(m_is_dummy_pass); } m_cinfo.m_post.StartPass(m_is_dummy_pass ? JBufMode.SaveAndPass : JBufMode.PassThrough); m_cinfo.m_main.StartPass(JBufMode.PassThrough); } } /* Set up progress monitor's pass info if present */ if (m_cinfo.prog is object) { m_cinfo.prog.CompletedPasses = m_pass_number; m_cinfo.prog.TotalPasses = m_pass_number + (m_is_dummy_pass ? 2 : 1); /* In buffered-image mode, we assume one more output pass if EOI not * yet reached, but no more passes if EOI has been reached. */ if (m_cinfo.bufferedImage && !m_cinfo.m_inputctl.EOIReached()) { m_cinfo.prog.TotalPasses += (m_cinfo.enable2PassQuant ? 2 : 1); } } }
private int[] rgb_y_tab; /* => table for RGB to Y conversion */ /// <summary> /// Module initialization routine for output colorspace conversion. /// </summary> public JpegColorDeconverter(JpegDecompressStruct cinfo) { m_cinfo = cinfo; /* Make sure num_components agrees with jpeg_color_space */ switch (cinfo.jpegColorSpace) { case JColorSpace.JCS_GRAYSCALE: if (cinfo.numComponents != 1) { cinfo.ErrExit(JMessageCode.JERR_BAD_J_COLORSPACE); } break; case JColorSpace.JCS_RGB: case JColorSpace.JCS_YCbCr: case JColorSpace.JCS_BG_RGB: case JColorSpace.JCS_BG_YCC: if (cinfo.numComponents != 3) { cinfo.ErrExit(JMessageCode.JERR_BAD_J_COLORSPACE); } break; case JColorSpace.JCS_CMYK: case JColorSpace.JCS_YCCK: if (cinfo.numComponents != 4) { cinfo.ErrExit(JMessageCode.JERR_BAD_J_COLORSPACE); } break; case JColorSpace.JCS_NCHANNEL: if (cinfo.numComponents < 1) { cinfo.ErrExit(JMessageCode.JERR_BAD_J_COLORSPACE); } break; default: /* JCS_UNKNOWN can be anything */ if (cinfo.numComponents < 1) { cinfo.ErrExit(JMessageCode.JERR_BAD_J_COLORSPACE); } break; } /* Support color transform only for RGB colorspaces */ if (cinfo.color_transform != JColorTransform.JCT_NONE && cinfo.jpegColorSpace != JColorSpace.JCS_RGB && cinfo.jpegColorSpace != JColorSpace.JCS_BG_RGB) { cinfo.ErrExit(JMessageCode.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.outColorSpace) { case JColorSpace.JCS_GRAYSCALE: cinfo.outColorComponents = 1; switch (cinfo.jpegColorSpace) { case JColorSpace.JCS_GRAYSCALE: case JColorSpace.JCS_YCbCr: case JColorSpace.JCS_BG_YCC: m_converter = GrayscaleConvert; /* For color->grayscale conversion, only the Y (0) component is needed */ for (var ci = 1; ci < cinfo.numComponents; ci++) { cinfo.CompInfo[ci].component_needed = false; } break; case JColorSpace.JCS_RGB: switch (cinfo.color_transform) { case JColorTransform.JCT_NONE: m_converter = RgbGrayConvert; break; case JColorTransform.JCT_SUBTRACT_GREEN: m_converter = Rgb1GrayConvert; break; default: cinfo.ErrExit(JMessageCode.JERR_CONVERSION_NOTIMPL); break; } BuildRgbYTable(); break; default: cinfo.ErrExit(JMessageCode.JERR_CONVERSION_NOTIMPL); break; } break; case JColorSpace.JCS_RGB: cinfo.outColorComponents = JpegConstants.RGB_PIXELSIZE; switch (cinfo.jpegColorSpace) { case JColorSpace.JCS_GRAYSCALE: m_converter = GrayRgbConvert; break; case JColorSpace.JCS_YCbCr: m_converter = YccRgbConvert; BuildYccRgbTable(); break; case JColorSpace.JCS_BG_YCC: m_converter = YccRgbConvert; BuildBgYccRgbTable(); break; case JColorSpace.JCS_RGB: switch (cinfo.color_transform) { case JColorTransform.JCT_NONE: m_converter = RgbConvert; break; case JColorTransform.JCT_SUBTRACT_GREEN: m_converter = Rgb1RgbConvert; break; default: cinfo.ErrExit(JMessageCode.JERR_CONVERSION_NOTIMPL); break; } break; case JColorSpace.JCS_CMYK: m_converter = CmykRgbConvert; break; case JColorSpace.JCS_YCCK: m_converter = YcckRgbConvert; BuildYccRgbTable(); break; default: cinfo.ErrExit(JMessageCode.JERR_CONVERSION_NOTIMPL); break; } break; case JColorSpace.JCS_BG_RGB: cinfo.outColorComponents = JpegConstants.RGB_PIXELSIZE; if (cinfo.jpegColorSpace == JColorSpace.JCS_BG_RGB) { switch (cinfo.color_transform) { case JColorTransform.JCT_NONE: m_converter = RgbConvert; break; case JColorTransform.JCT_SUBTRACT_GREEN: m_converter = Rgb1RgbConvert; break; default: cinfo.ErrExit(JMessageCode.JERR_CONVERSION_NOTIMPL); break; } } else { cinfo.ErrExit(JMessageCode.JERR_CONVERSION_NOTIMPL); } break; case JColorSpace.JCS_CMYK: cinfo.outColorComponents = 4; switch (cinfo.jpegColorSpace) { case JColorSpace.JCS_YCCK: m_converter = YcckCmykConvert; BuildYccRgbTable(); break; case JColorSpace.JCS_CMYK: m_converter = NullConvert; break; default: cinfo.ErrExit(JMessageCode.JERR_CONVERSION_NOTIMPL); break; } break; case JColorSpace.JCS_NCHANNEL: if (cinfo.jpegColorSpace == JColorSpace.JCS_NCHANNEL) { m_converter = NullConvert; } else { cinfo.ErrExit(JMessageCode.JERR_CONVERSION_NOTIMPL); } break; default: /* Permit null conversion to same output space */ if (cinfo.outColorSpace == cinfo.jpegColorSpace) { cinfo.outColorComponents = cinfo.numComponents; m_converter = NullConvert; } else { /* unsupported non-null conversion */ cinfo.ErrExit(JMessageCode.JERR_CONVERSION_NOTIMPL); } break; } if (cinfo.quantizeColors) { cinfo.outputComponents = 1; /* single colormapped output component */ } else { cinfo.outputComponents = cinfo.outColorComponents; } }
public MyUpSampler(JpegDecompressStruct 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(JMessageCode.JERR_CCIR601_NOTIMPL); } /* Verify we can handle the sampling factors, select per-component methods, * and create storage as needed. */ for (var ci = 0; ci < cinfo.numComponents; ci++) { var componentInfo = cinfo.CompInfo[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. */ var h_in_group = (componentInfo.H_samp_factor * componentInfo.DCT_h_scaled_size) / cinfo.min_DCT_h_scaled_size; var v_in_group = (componentInfo.V_samp_factor * componentInfo.DCT_v_scaled_size) / cinfo.min_DCT_v_scaled_size; var h_out_group = cinfo.m_max_h_samp_factor; var v_out_group = cinfo.m_maxVSampleFactor; /* save for use later */ m_rowgroup_height[ci] = v_in_group; if (!componentInfo.component_needed) { /* Don't bother to upsample an uninteresting component. */ m_upsampleMethods[ci] = ComponentUpsampler.noop_upsampler; continue; /* don't need to allocate buffer */ } 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; continue; /* don't need to allocate buffer */ } if (h_in_group * 2 == h_out_group && v_in_group == v_out_group) { /* Special case for 2h1v upsampling */ m_upsampleMethods[ci] = ComponentUpsampler.h2v1_upsampler; } else if (h_in_group * 2 == h_out_group && v_in_group * 2 == v_out_group) { /* Special case for 2h2v upsampling */ 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(JMessageCode.JERR_FRACT_SAMPLE_NOTIMPL); } var cb = new ComponentBuffer(); cb.SetBuffer(JpegCommonStruct.AllocJpegSamples(JpegUtils.jround_up(cinfo.outputWidth, cinfo.m_max_h_samp_factor), cinfo.m_maxVSampleFactor), null, 0); m_color_buf[ci] = cb; } }
/// <summary> /// Initialize for one-pass color quantization. /// </summary> public virtual void StartPass(bool is_pre_scan) { /* Install my colormap. */ m_cinfo.m_colormap = m_sv_colormap; m_cinfo.actualColorCount = m_sv_actual; /* Initialize for desired dithering mode. */ switch (m_cinfo.ditherMode) { case JDitherMode.JDITHER_NONE: if (m_cinfo.outColorComponents == 3) { m_quantizer = QuantizerType.color_quantizer3; } else { m_quantizer = QuantizerType.color_quantizer; } break; case JDitherMode.JDITHER_ORDERED: if (m_cinfo.outColorComponents == 3) { m_quantizer = QuantizerType.quantize3_ord_dither_quantizer; } else { m_quantizer = QuantizerType.quantize_ord_dither_quantizer; } /* initialize state for ordered dither */ m_row_index = 0; /* If user changed to ordered dither from another mode, * we must recreate the color index table with padding. * This will cost extra space, but probably isn't very likely. */ if (!m_is_padded) { CreateColorIndex(); } /* Create ordered-dither tables if we didn't already. */ if (m_odither[0] is null) { CreateODitherTable(); } break; case JDitherMode.JDITHER_FS: m_quantizer = QuantizerType.quantize_fs_dither_quantizer; /* initialize state for F-S dither */ m_on_odd_row = false; /* Allocate Floyd-Steinberg workspace if didn't already. */ if (m_fserrors[0] is null) { AllocFsWorkspace(); } /* Initialize the propagated errors to zero. */ var arraysize = m_cinfo.outputWidth + 2; for (var i = 0; i < m_cinfo.outColorComponents; i++) { Array.Clear(m_fserrors[i], 0, arraysize); } break; default: m_cinfo.ErrExit(JMessageCode.JERR_NOT_COMPILED); break; } }
/// <summary> /// <para> /// Read JPEG markers before, between, or after compressed-data scans. /// Change state as necessary when a new scan is reached. /// Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. /// </para> /// <para> /// The consume_input method pointer points either here or to the /// coefficient controller's consume_data routine, depending on whether /// we are reading a compressed data segment or inter-segment markers. /// </para> /// <para> /// Note: This function should NOT return a pseudo SOS marker(with zero /// component number) to the caller.A pseudo marker received by /// read_markers is processed and then skipped for other markers. /// </para> /// </summary> private ReadResult ConsumeMarkers() { ReadResult val; if (m_eoi_reached) /* After hitting EOI, read no further */ { return(ReadResult.JPEG_REACHED_EOI); } /* Loop to pass pseudo SOS marker */ for (; ;) { val = m_cinfo.m_marker.ReadMarkers(); switch (val) { case ReadResult.JPEG_REACHED_SOS: /* Found SOS */ if (m_inheaders != 0) { /* 1st SOS */ if (m_inheaders == 1) { InitialSetup(); } if (m_cinfo.m_comps_in_scan == 0) { /* pseudo SOS marker */ m_inheaders = 2; break; } m_inheaders = 0; /* Note: start_input_pass must be called by jpeg_decomp_master * before any more input can be consumed. */ } else { /* 2nd or later SOS marker */ if (!m_has_multiple_scans) { /* Oops, I wasn't expecting this! */ m_cinfo.ErrExit(JMessageCode.JERR_EOI_EXPECTED); } if (m_cinfo.m_comps_in_scan == 0) { /* unexpected pseudo SOS marker */ break; } m_cinfo.m_inputctl.StartInputPass(); } return(val); case ReadResult.JPEG_REACHED_EOI: /* Found EOI */ m_eoi_reached = true; if (m_inheaders != 0) { /* Tables-only datastream, apparently */ if (m_cinfo.m_marker.SawSOF()) { m_cinfo.ErrExit(JMessageCode.JERR_SOF_NO_SOS); } } else { /* Prevent infinite loop in coef ctlr's decompress_data routine * if user set output_scan_number larger than number of scans. */ if (m_cinfo.outputScanNumber > m_cinfo.inputScanNumber) { m_cinfo.outputScanNumber = m_cinfo.inputScanNumber; } } return(val); case ReadResult.JPEG_SUSPENDED: default: return(val); } } }