using BitMiracle.LibJpeg.Classic.Internal;

#pragma warning disable 0419 // Ambiguous cref in third-party XML documentation.
#pragma warning disable 1572 // Mismatched <param> tag in third-party XML documentation.
#pragma warning disable 1574 // Invalid cref in third-party XML documentation.
#pragma warning disable 1587 // Misplaced XML documentation in third-party source.

namespace BitMiracle.LibJpeg.Classic;

/// <summary>
/// JPEG decompression routine.
/// </summary>
internal class jpeg_decompress_struct : jpeg_common_struct
{
    /* Source of compressed data */
    internal jpeg_source_mgr m_src;

    internal int m_image_width; /* nominal image width (from SOF marker) */
    internal int m_image_height;    /* nominal image height */
    internal int m_num_components;     /* # of color components in JPEG image */
    internal J_COLOR_SPACE m_jpeg_color_space; /* colorspace of JPEG image */

    internal J_COLOR_SPACE m_out_color_space; /* colorspace for output */
    internal int m_scale_num;
    internal int m_scale_denom; /* fraction by which to scale image */
    internal bool m_buffered_image;    /* true=multiple output passes */
    internal bool m_raw_data_out;      /* true=downsampled data wanted */
    internal J_DCT_METHOD m_dct_method;    /* IDCT algorithm selector */
    internal bool m_do_fancy_upsampling;   /* true=apply fancy upsampling */
    internal bool m_do_block_smoothing;    /* true=apply interblock smoothing */
    internal bool m_quantize_colors;   /* true=colormapped output wanted */
    internal J_DITHER_MODE m_dither_mode;  /* type of color dithering to use */
    internal bool m_two_pass_quantize; /* true=use two-pass color quantization */
    internal int m_desired_number_of_colors;   /* max # colors to use in created colormap */
    internal bool m_enable_1pass_quant;    /* enable future use of 1-pass quantizer */
    internal bool m_enable_external_quant;/* enable future use of external colormap */
    internal bool m_enable_2pass_quant;    /* enable future use of 2-pass quantizer */

    internal int m_output_width;    /* scaled image width */
    internal int m_output_height;   /* scaled image height */
    internal int m_out_color_components;   /* # of color components in out_color_space */
    /* # of color components returned
     * output_components is 1 (a colormap index) when quantizing colors;
     * otherwise it equals out_color_components.
     */
    internal int m_output_components;

    internal int m_rec_outbuf_height;  /* min recommended height of scanline buffer */

    internal int m_actual_number_of_colors;    /* number of entries in use */
    internal byte[][]? m_colormap;     /* The color map as a 2-D pixel array */

    internal int m_output_scanline; /* 0 .. output_height-1  */

    internal int m_input_scan_number;  /* Number of SOS markers seen so far */
    internal int m_input_iMCU_row;  /* Number of iMCU rows completed */

    internal int m_output_scan_number; /* Nominal scan number being displayed */
    internal int m_output_iMCU_row; /* Number of iMCU rows read */

    internal int[][]? m_coef_bits; /* -1 or current Al value for each coef */

    /* Internal JPEG parameters --- the application usually need not look at
     * these fields.  Note that the decompressor output side may not use
     * any parameters that can change between scans.
     */

    /* Quantization and Huffman tables are carried forward across input
     * datastreams when processing abbreviated JPEG datastreams.
     */

    internal JQUANT_TBL?[] m_quant_tbl_ptrs = new JQUANT_TBL?[JpegConstants.NUM_QUANT_TBLS];
    /* ptrs to coefficient quantization tables, or null if not defined */

    internal JHUFF_TBL?[] m_dc_huff_tbl_ptrs = new JHUFF_TBL?[JpegConstants.NUM_HUFF_TBLS];
    internal JHUFF_TBL?[] m_ac_huff_tbl_ptrs = new JHUFF_TBL?[JpegConstants.NUM_HUFF_TBLS];
    /* ptrs to Huffman coding tables, or null if not defined */

    /* These parameters are never carried across datastreams, since they
     * are given in SOF/SOS markers or defined to be reset by SOI.
     */

    internal int m_data_precision;     /* bits of precision in image data */

    /* m_comp_info[i] describes component that appears i'th in SOF */

    internal bool is_baseline;		/* TRUE if Baseline SOF0 encountered */
    internal bool m_progressive_mode;  /* true if SOFn specifies progressive mode */

    internal int m_restart_interval; /* MCUs per restart interval, or 0 for no restart */

    /* These fields record data obtained from optional markers recognized by
     * the JPEG library.
     */
    internal bool m_saw_JFIF_marker;   /* true iff a JFIF APP0 marker was found */
    /* Data copied from JFIF marker; only valid if saw_JFIF_marker is true: */
    internal byte m_JFIF_major_version;   /* JFIF version number */
    internal byte m_JFIF_minor_version;

    internal DensityUnit m_density_unit;     /* JFIF code for pixel size units */
    internal short m_X_density;       /* Horizontal pixel density */
    internal short m_Y_density;       /* Vertical pixel density */

    internal bool m_saw_Adobe_marker;  /* true iff an Adobe APP14 marker was found */
    internal byte m_Adobe_transform;  /* Color transform code from Adobe marker */

    internal J_COLOR_TRANSFORM color_transform;
    /* Color transform identifier derived from LSE marker, otherwise zero */

    internal bool m_CCIR601_sampling;  /* true=first samples are cosited */

    /* Remaining fields are known throughout decompressor, but generally
     * should not be touched by a surrounding application.
     */

    /*
     * These fields are computed during decompression startup
     */
    internal int m_max_h_samp_factor;  /* largest h_samp_factor */
    internal int m_max_v_samp_factor;  /* largest v_samp_factor */

    internal int min_DCT_h_scaled_size;  /* smallest DCT_h_scaled_size of any component */
    internal int min_DCT_v_scaled_size;	/* smallest DCT_v_scaled_size of any component */

    internal int m_total_iMCU_rows; /* # of iMCU rows in image */
    /* The coefficient controller's input and output progress is measured in
     * units of "iMCU" (interleaved MCU) rows.  These are the same as MCU rows
     * in fully interleaved JPEG scans, but are used whether the scan is
     * interleaved or not.  We define an iMCU row as v_samp_factor DCT block
     * rows of each component.  Therefore, the IDCT output contains
     * v_samp_factor*DCT_v_scaled_size sample rows of a component per iMCU row.
     */

    internal byte[] m_sample_range_limit = Array.Empty<byte>(); /* table for fast range-limiting */
    internal int m_sampleRangeLimitOffset;

    /*
     * These fields are valid during any one scan.
     * They describe the components and MCUs actually appearing in the scan.
     * Note that the decompressor output side must not use these fields.
     */
    internal int m_comps_in_scan;      /* # of JPEG components in this scan */
    internal int[] m_cur_comp_info = new int[JpegConstants.MAX_COMPS_IN_SCAN];
    /* *cur_comp_info[i] describes component that appears i'th in SOS */

    internal int m_MCUs_per_row;    /* # of MCUs across the image */
    internal int m_MCU_rows_in_scan;    /* # of MCU rows in the image */

    internal int m_blocks_in_MCU;      /* # of DCT blocks per MCU */
    internal int[] m_MCU_membership = new int[JpegConstants.D_MAX_BLOCKS_IN_MCU];
    /* MCU_membership[i] is index in cur_comp_info of component owning */
    /* i'th block in an MCU */

    /* progressive JPEG parameters for scan */
    internal int m_Ss;
    internal int m_Se;
    internal int m_Ah;
    internal int m_Al;

    /* These fields are derived from Se of first SOS marker. */
    internal int block_size;     /* the basic DCT block size: 1..16 */
    internal int[] natural_order = Array.Empty<int>(); /* natural-order position array for entropy decode */
    internal int lim_Se;			/* min( Se, DCTSIZE2-1 ) for entropy decode */


    /* This field is shared between entropy decoder and marker parser.
     * It is either zero or the code of a JPEG marker that has been
     * read from the data source, but has not yet been processed.
     */
    internal int m_unread_marker;

    /*
     * Links to decompression subobjects (methods, private variables of modules)
     */
    internal jpeg_decomp_master m_master;
    internal jpeg_d_main_controller m_main;
    internal jpeg_d_coef_controller m_coef;
    internal jpeg_d_post_controller m_post;
    internal jpeg_input_controller m_inputctl;
    internal jpeg_marker_reader m_marker = null!;
    internal jpeg_entropy_decoder_union m_entropy;
    internal jpeg_inverse_dct m_idct = null!;
    internal jpeg_upsampler_union m_upsample;
    internal jpeg_color_deconverter m_cconvert = null!;
    internal jpeg_color_quantizer m_cquantize = null!;

    /// <summary>
    /// Initializes a new instance of the <see cref="jpeg_decompress_struct"/> class.
    /// </summary>
    public jpeg_decompress_struct()
    {
        initialize();
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="jpeg_decompress_struct"/> class.
    /// </summary>
    /// <param name="errorManager">The error manager.</param>
    public jpeg_decompress_struct(jpeg_error_mgr errorManager)
        : base(errorManager)
    {
        initialize();
    }

    /// <summary>
    /// Retrieves <c>true</c> because this is a decompressor.
    /// </summary>
    /// <value><c>true</c></value>
    public override bool IsDecompressor
    {
        get { return true; }
    }

    /// <summary>
    /// Gets or sets the source for decompression.
    /// </summary>
    /// <value>The source for decompression.</value>
    public jpeg_source_mgr Src
    { get => m_src; internal set => m_src = value;
    }

    /* Basic description of image --- filled in by jpeg_read_header(). */
    /* Application may inspect these values to decide how to process image. */

    /// <summary>
    /// Gets the width of image, set by <see cref="jpeg_read_header"/>
    /// </summary>
    /// <value>The width of image.</value>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Image_width
    {
        get { return m_image_width; }
    }

    /// <summary>
    /// Gets the height of image, set by <see cref="jpeg_read_header"/>
    /// </summary>
    /// <value>The height of image.</value>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Image_height
    {
        get { return m_image_height; }
    }

    /// <summary>
    /// Gets the number of color components in JPEG image.
    /// </summary>
    /// <value>The number of color components.</value>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Num_components
    {
        get { return m_num_components; }
    }

    /// <summary>
    /// Gets or sets the colorspace of JPEG image.
    /// </summary>
    /// <value>The colorspace of JPEG image.</value>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public J_COLOR_SPACE Jpeg_color_space
    { get => m_jpeg_color_space; set => m_jpeg_color_space = value;
    }

    /* Decompression processing parameters --- these fields must be set before
     * calling jpeg_start_decompress().  Note that jpeg_read_header() initializes
     * them to default values.
     */

    /// <summary>
    /// Gets or sets the output color space.
    /// </summary>
    /// <value>The output color space.</value>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public J_COLOR_SPACE Out_color_space
    { get => m_out_color_space; set => m_out_color_space = value;
    }

    /// <summary>
    /// Gets or sets the numerator of the fraction of image scaling.
    /// </summary>
    /// <value>Scale the image by the fraction Scale_num/<see cref="Scale_denom">Scale_denom</see>. 
    /// Default is 1/1, or no scaling. Currently, the only supported scaling ratios are 1/1, 1/2, 1/4, and 1/8.
    /// (The library design allows for arbitrary scaling ratios but this is not likely to be implemented any time soon.)
    /// </value>
    /// <remarks>Smaller scaling ratios permit significantly faster decoding since fewer pixels 
    /// need to be processed and a simpler <see cref="J_DCT_METHOD">DCT method</see> can be used.</remarks>
    /// <seealso cref="Scale_denom"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Scale_num
    { get => m_scale_num; set => m_scale_num = value;
    }

    /// <summary>
    /// Gets or sets the denominator of the fraction of image scaling.
    /// </summary>
    /// <value>Scale the image by the fraction <see cref="Scale_num">Scale_num</see>/Scale_denom. 
    /// Default is 1/1, or no scaling. Currently, the only supported scaling ratios are 1/1, 1/2, 1/4, and 1/8.
    /// (The library design allows for arbitrary scaling ratios but this is not likely to be implemented any time soon.)
    /// </value>
    /// <remarks>Smaller scaling ratios permit significantly faster decoding since fewer pixels 
    /// need to be processed and a simpler <see cref="J_DCT_METHOD">DCT method</see> can be used.</remarks>
    /// <seealso cref="Scale_num"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Scale_denom
    { get => m_scale_denom; set => m_scale_denom = value;
    }

    /// <summary>
    /// Gets or sets a value indicating whether to use buffered-image mode.
    /// </summary>
    /// <value><c>true</c> if buffered-image mode is turned on; otherwise, <c>false</c>.</value>
    /// <seealso href="../articles/KB/buffered-image-mode.html">Buffered-image mode</seealso>
    public bool Buffered_image
    { get => m_buffered_image; set => m_buffered_image = value;
    }

    /// <summary>
    /// Enable or disable raw data output.
    /// </summary>
    /// <value><c>true</c> if raw data output is enabled; otherwise, <c>false</c>.</value>
    /// <remarks>Default value: <c>false</c><br/>
    /// Set this to true before <see cref="jpeg_start_decompress"/> 
    /// if you need to obtain raw data output.
    /// </remarks>
    /// <seealso cref="jpeg_read_raw_data"/>
    public bool Raw_data_out
    { get => m_raw_data_out; set => m_raw_data_out = value;
    }

    /// <summary>
    /// Gets or sets the algorithm used for the DCT step.
    /// </summary>
    /// <value>The algorithm used for the DCT step.</value>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public J_DCT_METHOD Dct_method
    { get => m_dct_method; set => m_dct_method = value;
    }

    /// <summary>
    /// Enable or disable upsampling of chroma components.
    /// </summary>
    /// <value>If <c>true</c>, do careful upsampling of chroma components. 
    /// If <c>false</c>, a faster but sloppier method is used. 
    /// The visual impact of the sloppier method is often very small.
    /// </value>
    /// <remarks>Default value: <c>true</c></remarks>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public bool Do_fancy_upsampling
    { get => m_do_fancy_upsampling; set => m_do_fancy_upsampling = value;
    }

    /// <summary>
    /// Apply interblock smoothing in early stages of decoding progressive JPEG files.
    /// </summary>
    /// <value>If <c>true</c>, interblock smoothing is applied in early stages of decoding progressive JPEG files; 
    /// if <c>false</c>, not. Early progression stages look "fuzzy" with smoothing, "blocky" without.</value>
    /// <remarks>Default value: <c>true</c><br/>
    /// In any case, block smoothing ceases to be applied after the first few AC coefficients are 
    /// known to full accuracy, so it is relevant only when using 
    /// <see href="../articles/KB/buffered-image-mode.html">buffered-image mode</see> for progressive images.
    /// </remarks>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public bool Do_block_smoothing
    { get => m_do_block_smoothing; set => m_do_block_smoothing = value;
    }

    /// <summary>
    /// Colors quantization.
    /// </summary>
    /// <value>If set <c>true</c>, colormapped output will be delivered.<br/>
    /// Default value: <c>false</c>, meaning that full-color output will be delivered.
    /// </value>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public bool Quantize_colors
    { get => m_quantize_colors; set => m_quantize_colors = value;
    }

    /* the following are ignored if not quantize_colors: */

    /// <summary>
    /// Selects color dithering method.
    /// </summary>
    /// <value>Default value: <see cref="J_DITHER_MODE.JDITHER_FS"/>.</value>
    /// <remarks>Ignored if <see cref="Quantize_colors"/> is <c>false</c>.<br/>
    /// At present, ordered dither is implemented only in the single-pass, standard-colormap case. 
    /// If you ask for ordered dither when <see cref="Two_pass_quantize"/> is <c>true</c>
    /// or when you supply an external color map, you'll get F-S dithering.
    /// </remarks>
    /// <seealso cref="Quantize_colors"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public J_DITHER_MODE Dither_mode
    { get => m_dither_mode; set => m_dither_mode = value;
    }

    /// <summary>
    /// Gets or sets a value indicating whether to use two-pass color quantization.
    /// </summary>
    /// <value>If <c>true</c>, an extra pass over the image is made to select a custom color map for the image.
    /// This usually looks a lot better than the one-size-fits-all colormap that is used otherwise.
    /// Ignored when the application supplies its own color map.<br/>
    /// 
    /// Default value: <c>true</c>
    /// </value>
    /// <remarks>Ignored if <see cref="Quantize_colors"/> is <c>false</c>.<br/>
    /// </remarks>
    /// <seealso cref="Quantize_colors"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public bool Two_pass_quantize
    { get => m_two_pass_quantize; set => m_two_pass_quantize = value;
    }

    /// <summary>
    /// Maximum number of colors to use in generating a library-supplied color map.
    /// </summary>
    /// <value>Default value: 256.</value>
    /// <remarks>Ignored if <see cref="Quantize_colors"/> is <c>false</c>.<br/>
    /// The actual number of colors is returned in a <see cref="Actual_number_of_colors"/>.
    /// </remarks>
    /// <seealso cref="Quantize_colors"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Desired_number_of_colors
    { get => m_desired_number_of_colors; set => m_desired_number_of_colors = value;
    }

    /* these are significant only in buffered-image mode: */

    /// <summary>
    /// Enable future use of 1-pass quantizer.
    /// </summary>
    /// <value>Default value: <c>false</c></value>
    /// <remarks>Significant only in buffered-image mode.</remarks>
    /// <seealso href="../articles/KB/buffered-image-mode.html">Buffered-image mode</seealso>
    public bool Enable_1pass_quant
    { get => m_enable_1pass_quant; set => m_enable_1pass_quant = value;
    }

    /// <summary>
    /// Enable future use of external colormap.
    /// </summary>
    /// <value>Default value: <c>false</c></value>
    /// <remarks>Significant only in buffered-image mode.</remarks>
    /// <seealso href="../articles/KB/buffered-image-mode.html">Buffered-image mode</seealso>
    public bool Enable_external_quant
    { get => m_enable_external_quant; set => m_enable_external_quant = value;
    }

    /// <summary>
    /// Enable future use of 2-pass quantizer.
    /// </summary>
    /// <value>Default value: <c>false</c></value>
    /// <remarks>Significant only in buffered-image mode.</remarks>
    /// <seealso href="../articles/KB/buffered-image-mode.html">Buffered-image mode</seealso>
    public bool Enable_2pass_quant
    { get => m_enable_2pass_quant; set => m_enable_2pass_quant = value;
    }

    /* Description of actual output image that will be returned to application.
     * These fields are computed by jpeg_start_decompress().
     * You can also use jpeg_calc_output_dimensions() to determine these values
     * in advance of calling jpeg_start_decompress().
     */

    /// <summary>
    /// Gets the actual width of output image.
    /// </summary>
    /// <value>The width of output image.</value>
    /// <remarks>Computed by <see cref="jpeg_start_decompress"/>.
    /// You can also use <see cref="jpeg_calc_output_dimensions"/> to determine this value
    /// in advance of calling <see cref="jpeg_start_decompress"/>.</remarks>
    /// <seealso cref="Output_height"/>
    public int Output_width
    {
        get { return m_output_width; }
    }

    /// <summary>
    /// Gets the actual height of output image.
    /// </summary>
    /// <value>The height of output image.</value>
    /// <remarks>Computed by <see cref="jpeg_start_decompress"/>.
    /// You can also use <see cref="jpeg_calc_output_dimensions"/> to determine this value
    /// in advance of calling <see cref="jpeg_start_decompress"/>.</remarks>
    /// <seealso cref="Output_width"/>
    public int Output_height
    {
        get { return m_output_height; }
    }

    /// <summary>
    /// Gets the number of color components in <see cref="Out_color_space"/>.
    /// </summary>
    /// <remarks>Computed by <see cref="jpeg_start_decompress"/>.
    /// You can also use <see cref="jpeg_calc_output_dimensions"/> to determine this value
    /// in advance of calling <see cref="jpeg_start_decompress"/>.</remarks>
    /// <value>The number of color components.</value>
    /// <seealso cref="Out_color_space"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Out_color_components
    {
        get { return m_out_color_components; }
    }

    /// <summary>
    /// Gets the number of color components returned.
    /// </summary>
    /// <remarks>Computed by <see cref="jpeg_start_decompress"/>.
    /// You can also use <see cref="jpeg_calc_output_dimensions"/> to determine this value
    /// in advance of calling <see cref="jpeg_start_decompress"/>.</remarks>
    /// <value>When <see cref="Quantize_colors">quantizing colors</see>, 
    /// <c>Output_components</c> is 1, indicating a single color map index per pixel. 
    /// Otherwise it equals to <see cref="Out_color_components"/>.
    /// </value>
    /// <seealso cref="Out_color_space"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Output_components
    {
        get { return m_output_components; }
    }

    /// <summary>
    /// Gets the recommended height of scanline buffer.
    /// </summary>
    /// <value>In high-quality modes, <c>Rec_outbuf_height</c> is always 1, but some faster, 
    /// lower-quality modes set it to larger values (typically 2 to 4).</value>
    /// <remarks>Computed by <see cref="jpeg_start_decompress"/>.
    /// You can also use <see cref="jpeg_calc_output_dimensions"/> to determine this value
    /// in advance of calling <see cref="jpeg_start_decompress"/>.<br/>
    /// 
    /// <c>Rec_outbuf_height</c> is the recommended minimum height (in scanlines) 
    /// of the buffer passed to <see cref="jpeg_decompress_struct.jpeg_read_scanlines"/>.
    /// If the buffer is smaller, the library will still work, but time will be wasted due 
    /// to unnecessary data copying. If you are going to ask for a high-speed processing mode, 
    /// you may as well go to the trouble of honoring <c>Rec_outbuf_height</c> so as to avoid data copying.
    /// (An output buffer larger than <c>Rec_outbuf_height</c> lines is OK, but won't provide 
    /// any material speed improvement over that height.)
    /// </remarks>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Rec_outbuf_height
    {
        get { return m_rec_outbuf_height; }
    }

    /* When quantizing colors, the output colormap is described by these fields.
     * The application can supply a colormap by setting colormap non-null before
     * calling jpeg_start_decompress; otherwise a colormap is created during
     * jpeg_start_decompress or jpeg_start_output.
     * The map has out_color_components rows and actual_number_of_colors columns.
     */

    /// <summary>
    /// The number of colors in the color map.
    /// </summary>
    /// <value>The number of colors in the color map.</value>
    /// <seealso cref="Colormap"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public int Actual_number_of_colors
    { get => m_actual_number_of_colors; set => m_actual_number_of_colors = value;
    }

    /// <summary>
    /// The color map, represented as a 2-D pixel array of <see cref="Out_color_components"/> rows 
    /// and <see cref="Actual_number_of_colors"/> columns.
    /// </summary>
    /// <value>Colormap is set to <c>null</c> by <see cref="jpeg_read_header"/>.
    /// The application can supply a color map by setting <c>Colormap</c> non-null and setting 
    /// <see cref="Actual_number_of_colors"/> to the map size.
    /// </value>
    /// <remarks>Ignored if not quantizing.<br/>
    /// Implementation restriction: at present, an externally supplied <c>Colormap</c>
    /// is only accepted for 3-component output color spaces.
    /// </remarks>
    /// <seealso cref="Actual_number_of_colors"/>
    /// <seealso cref="Quantize_colors"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public byte[][]? Colormap
    { get => m_colormap; set => m_colormap = value;
    }

    /* State variables: these variables indicate the progress of decompression.
     * The application may examine these but must not modify them.
     */

    /* Row index of next scanline to be read from jpeg_read_scanlines().
     * Application may use this to control its processing loop, e.g.,
     * "while (output_scanline < output_height)".
     */

    /// <summary>
    /// Gets the number of scanlines returned so far.
    /// </summary>
    /// <value>The output_scanline.</value>
    /// <remarks>Usually you can just use this variable as the loop counter, 
    /// so that the loop test looks like 
    /// <c>while (cinfo.Output_scanline &lt; cinfo.Output_height)</c></remarks>
    /// <seealso href="../articles/KB/decompression-details.html">Decompression details</seealso>
    public int Output_scanline
    {
        get { return m_output_scanline; }
    }

    /* Current input scan number and number of iMCU rows completed in scan.
     * These indicate the progress of the decompressor input side.
     */

    /// <summary>
    /// Gets the number of SOS markers seen so far.
    /// </summary>
    /// <value>The number of SOS markers seen so far.</value>
    /// <remarks>Indicates the progress of the decompressor input side.</remarks>
    public int Input_scan_number
    {
        get { return m_input_scan_number; }
    }

    /// <summary>
    /// Gets the number of iMCU rows completed.
    /// </summary>
    /// <value>The number of iMCU rows completed.</value>
    /// <remarks>Indicates the progress of the decompressor input side.</remarks>
    public int Input_iMCU_row
    {
        get { return m_input_iMCU_row; }
    }

    /* The "output scan number" is the notional scan being displayed by the
     * output side.  The decompressor will not allow output scan/row number
     * to get ahead of input scan/row, but it can fall arbitrarily far behind.
     */

    /// <summary>
    /// Gets the nominal scan number being displayed.
    /// </summary>
    /// <value>The nominal scan number being displayed.</value>
    public int Output_scan_number
    {
        get { return m_output_scan_number; }
    }

    /// <summary>
    /// Gets the number of iMCU rows read.
    /// </summary>
    /// <value>The number of iMCU rows read.</value>
    public int Output_iMCU_row
    {
        get { return m_output_iMCU_row; }
    }

    /* Current progression status.  coef_bits[c][i] indicates the precision
     * with which component c's DCT coefficient i (in zigzag order) is known.
     * It is -1 when no data has yet been received, otherwise it is the point
     * transform (shift) value for the most recent scan of the coefficient
     * (thus, 0 at completion of the progression).
     * This is null when reading a non-progressive file.
     */

    /// <summary>
    /// Gets the current progression status..
    /// </summary>
    /// <value><c>Coef_bits[c][i]</c> indicates the precision with 
    /// which component c's DCT coefficient i (in zigzag order) is known. 
    /// It is <c>-1</c> when no data has yet been received, otherwise 
    /// it is the point transform (shift) value for the most recent scan of the coefficient 
    /// (thus, 0 at completion of the progression). This is null when reading a non-progressive file.
    /// </value>
    /// <seealso href="../articles/KB/progressive-jpeg.html">Progressive JPEG support</seealso>
    public int[][]? Coef_bits
    {
        get { return m_coef_bits; }
    }

    // These fields record data obtained from optional markers 
    // recognized by the JPEG library.

    /// <summary>
    /// Gets the resolution information from JFIF marker.
    /// </summary>
    /// <value>The information from JFIF marker.</value>
    /// <seealso cref="X_density"/>
    /// <seealso cref="Y_density"/>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public DensityUnit Density_unit
    {
        get { return m_density_unit; }
    }

    /// <summary>
    /// Gets the horizontal component of pixel ratio.
    /// </summary>
    /// <value>The horizontal component of pixel ratio.</value>
    /// <seealso cref="Y_density"/>
    /// <seealso cref="Density_unit"/>
    public short X_density
    {
        get { return m_X_density; }
    }

    /// <summary>
    /// Gets the vertical component of pixel ratio.
    /// </summary>
    /// <value>The vertical component of pixel ratio.</value>
    /// <seealso cref="X_density"/>
    /// <seealso cref="Density_unit"/>
    public short Y_density
    {
        get { return m_Y_density; }
    }

    /// <summary>
    /// Gets the data precision.
    /// </summary>
    /// <value>The data precision.</value>
    public int Data_precision
    {
        get { return m_data_precision; }
        //set { m_data_precision = value; }
    }

    /// <summary>
    /// Gets the largest vertical sample factor.
    /// </summary>
    /// <value>The largest vertical sample factor.</value>
    public int Max_v_samp_factor
    {
        get { return m_max_v_samp_factor; }
        //set { m_max_v_samp_factor = value; }
    }

    /// <summary>
    /// Gets the last read and unprocessed JPEG marker.
    /// </summary>
    /// <value>It is either zero or the code of a JPEG marker that has been
    /// read from the data source, but has not yet been processed.
    /// </value>
    /// <seealso cref="jpeg_decompress_struct.jpeg_set_marker_processor"/>
    /// <seealso href="../articles/KB/special-markers.html">Special markers</seealso>
    public int Unread_marker
    {
        get { return m_unread_marker; }
    }

    /// <summary>
    /// Comp_info[i] describes component that appears i'th in SOF
    /// </summary>
    /// <value>The components in SOF.</value>
    /// <seealso cref="jpeg_component_info"/>
    public jpeg_component_info[]? Comp_info { get; internal set; }

    /// <summary>
    /// Sets input stream.
    /// </summary>
    /// <param name="infile">The input stream.</param>
    /// <remarks>
    /// The caller must have already opened the stream, and is responsible
    /// for closing it after finishing decompression.
    /// </remarks>
    /// <seealso href="../articles/KB/decompression-details.html">Decompression details</seealso>
    public void jpeg_stdio_src(Stream infile)
    {
        /* The source object and input buffer are made permanent so that a series
        * of JPEG images can be read from the same file by calling jpeg_stdio_src
        * only before the first one.  (If we discarded the buffer at the end of
        * one image, we'd likely lose the start of the next one.)
        * This makes it unsafe to use this manager and a different source
        * manager serially with the same JPEG object.  Caveat programmer.
        */
        m_src.Attach(this, infile);
    }

    /// <summary>
    /// Decompression startup: this will read the source datastream header markers, up to the beginning of the compressed data proper.
    /// </summary>
    /// <param name="require_image">Read a description of <b>Return Value</b>.</param>
    /// <returns>
    /// If you pass <c>require_image=true</c> (normal case), you need not check for a
    /// <see cref="ReadResult.JPEG_HEADER_TABLES_ONLY"/> return code; an abbreviated file will cause
    /// an error exit. <see cref="ReadResult.JPEG_SUSPENDED"/> is only possible if you use a data source
    /// module that can give a suspension return.<br/><br/>
    /// 
    /// This method will read as far as the first SOS marker (ie, actual start of compressed data),
    /// and will save all tables and parameters in the JPEG object. It will also initialize the
    /// decompression parameters to default values, and finally return <see cref="ReadResult.JPEG_HEADER_OK"/>.
    /// On return, the application may adjust the decompression parameters and then call
    /// <see cref="jpeg_start_decompress"/>. (Or, if the application only wanted to
    /// determine the image parameters, the data need not be decompressed. In that case, call
    /// <see cref="jpeg_common_struct.jpeg_abort"/> to release any temporary space.)<br/><br/>
    /// 
    /// If an abbreviated (tables only) datastream is presented, the routine will return
    /// <see cref="ReadResult.JPEG_HEADER_TABLES_ONLY"/> upon reaching EOI. The application may then re-use
    /// the JPEG object to read the abbreviated image datastream(s). It is unnecessary (but OK) to call
    /// <see cref="jpeg_common_struct.jpeg_abort">jpeg_abort</see> in this case.
    /// The <see cref="ReadResult.JPEG_SUSPENDED"/> return code only occurs if the data source module
    /// requests suspension of the decompressor. In this case the application should load more source
    /// data and then re-call <c>jpeg_read_header</c> to resume processing.<br/><br/>
    /// 
    /// If a non-suspending data source is used and <c>require_image</c> is <c>true</c>,
    /// then the return code need not be inspected since only <see cref="ReadResult.JPEG_HEADER_OK"/> is possible.
    /// </returns>
    /// <remarks>Need only initialize JPEG object and supply a data source before calling.<br/>
    /// On return, the image dimensions and other info have been stored in the JPEG object.
    /// The application may wish to consult this information before selecting decompression parameters.<br/>
    /// This routine is now just a front end to <see cref="jpeg_consume_input"/>, with some extra error checking.
    /// </remarks>
    /// <seealso href="../articles/KB/decompression-details.html">Decompression details</seealso>
    /// <seealso href="../articles/KB/decompression-parameter-selection.html">Decompression parameter selection</seealso>
    public ReadResult jpeg_read_header(bool require_image)
    {
        if (m_global_state != JpegState.DSTATE_START && m_global_state != JpegState.DSTATE_INHEADER)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        ReadResult retcode = jpeg_consume_input();

        switch (retcode)
        {
            case ReadResult.JPEG_REACHED_SOS:
                return ReadResult.JPEG_HEADER_OK;
            case ReadResult.JPEG_REACHED_EOI:
                if (require_image)      /* Complain if application wanted an image */
                {
                    ERREXIT(J_MESSAGE_CODE.JERR_NO_IMAGE);
                }
                /* Reset to start state; it would be safer to require the application to
* call jpeg_abort, but we can't change it now for compatibility reasons.
* A side effect is to free any temporary memory (there shouldn't be any).
*/
                jpeg_abort(); /* sets state = DSTATE_START */
                return ReadResult.JPEG_HEADER_TABLES_ONLY;

            case ReadResult.JPEG_SUSPENDED:
                /* no work */
                break;
        }

        return ReadResult.JPEG_SUSPENDED;
    }

    //////////////////////////////////////////////////////////////////////////
    // Main entry points for decompression

    /// <summary>
    /// Decompression initialization.
    /// </summary>
    /// <returns>Returns <c>false</c> if suspended. The return value need be inspected 
    /// only if a suspending data source is used.
    /// </returns>
    /// <remarks><see cref="jpeg_read_header">jpeg_read_header</see> must be completed before calling this.<br/>
    /// 
    /// If a multipass operating mode was selected, this will do all but the last pass, and thus may take a great deal of time.
    /// </remarks>
    /// <seealso cref="jpeg_finish_decompress"/>
    /// <seealso href="../articles/KB/decompression-details.html">Decompression details</seealso>
    public bool jpeg_start_decompress()
    {
        if (m_global_state == JpegState.DSTATE_READY)
        {
            /* First call: initialize master control, select active modules */
            m_master = new jpeg_decomp_master(this);
            if (m_buffered_image)
            {
                /* No more work here; expecting jpeg_start_output next */
                m_global_state = JpegState.DSTATE_BUFIMAGE;
                return true;
            }
            m_global_state = JpegState.DSTATE_PRELOAD;
        }

        if (m_global_state == JpegState.DSTATE_PRELOAD)
        {
            /* If file has multiple scans, absorb them all into the coef buffer */
            if (m_inputctl.HasMultipleScans())
            {
                for (; ; )
                {
                    ReadResult retcode;
                    /* Call progress monitor hook if present */
                    if (m_progress != null)
                    {
                        m_progress.Updated();
                    }

                    /* Absorb some more input */
                    retcode = m_inputctl.consume_input();
                    if (retcode == ReadResult.JPEG_SUSPENDED)
                    {
                        return false;
                    }

                    if (retcode == ReadResult.JPEG_REACHED_EOI)
                    {
                        break;
                    }

                    /* Advance progress counter if appropriate */
                    if (m_progress != null && (retcode == ReadResult.JPEG_ROW_COMPLETED || retcode == ReadResult.JPEG_REACHED_SOS))
                    {
                        m_progress.Pass_counter++;
                        if (m_progress.Pass_counter >= m_progress.Pass_limit)
                        {
                            /* underestimated number of scans; ratchet up one scan */
                            m_progress.Pass_limit += m_total_iMCU_rows;
                        }
                    }
                }
            }

            m_output_scan_number = m_input_scan_number;
        }
        else if (m_global_state != JpegState.DSTATE_PRESCAN)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        /* Perform any dummy output passes, and set up for the final pass */
        return output_pass_setup();
    }

    /// <summary>
    /// Read some scanlines of data from the JPEG decompressor.
    /// </summary>
    /// <param name="scanlines">Buffer for filling.</param>
    /// <param name="max_lines">Required number of lines.</param>
    /// <returns>The return value will be the number of lines actually read. 
    /// This may be less than the number requested in several cases, including 
    /// bottom of image, data source suspension, and operating modes that emit multiple scanlines at a time.
    /// </returns>
    /// <remarks>We warn about excess calls to <c>jpeg_read_scanlines</c> since this likely signals an 
    /// application programmer error. However, an oversize buffer <c>(max_lines > scanlines remaining)</c> 
    /// is not an error.
    /// </remarks>
    /// <seealso href="../articles/KB/decompression-details.html">Decompression details</seealso>
    public int jpeg_read_scanlines(byte[][] scanlines, int max_lines)
    {
        if (m_global_state != JpegState.DSTATE_SCANNING)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        if (m_output_scanline >= m_output_height)
        {
            WARNMS(J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA);
            return 0;
        }

        /* Call progress monitor hook if present */
        if (m_progress != null)
        {
            m_progress.Pass_counter = m_output_scanline;
            m_progress.Pass_limit = m_output_height;
            m_progress.Updated();
        }

        /* Process some data */
        int row_ctr = 0;
        m_main.process_data(scanlines, ref row_ctr, max_lines);
        m_output_scanline += row_ctr;
        return row_ctr;
    }

    /// <summary>
    /// Read scanlines directly into a contiguous Buffer2D for better cache performance.
    /// </summary>
    /// <param name="buffer">Contiguous buffer to fill (width must match Output_width * Output_components).</param>
    /// <param name="startRow">Starting row index in the buffer.</param>
    /// <param name="max_lines">Maximum number of lines to read.</param>
    /// <returns>The number of lines actually read.</returns>
    public int jpeg_read_scanlines(Buffer2D<byte> buffer, int startRow, int max_lines)
    {
        if (m_global_state != JpegState.DSTATE_SCANNING)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        if (m_output_scanline >= m_output_height)
        {
            WARNMS(J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA);
            return 0;
        }

        /* Call progress monitor hook if present */
        if (m_progress != null)
        {
            m_progress.Pass_counter = m_output_scanline;
            m_progress.Pass_limit = m_output_height;
            m_progress.Updated();
        }

        /* Process some data using Buffer2D */
        int row_ctr = 0;
        m_main.process_data(buffer, startRow, ref row_ctr, max_lines);
        m_output_scanline += row_ctr;
        return row_ctr;
    }

    /// <summary>
    /// Finish JPEG decompression.
    /// </summary>
    /// <returns>Returns <c>false</c> if suspended. The return value need be inspected 
    /// only if a suspending data source is used.
    /// </returns>
    /// <remarks>This will normally just verify the file trailer and release temp storage.</remarks>
    /// <seealso cref="jpeg_start_decompress"/>
    /// <seealso href="../articles/KB/decompression-details.html">Decompression details</seealso>
    public bool jpeg_finish_decompress()
    {
        if ((m_global_state == JpegState.DSTATE_SCANNING || m_global_state == JpegState.DSTATE_RAW_OK) && !m_buffered_image)
        {
            /* Terminate final pass of non-buffered mode */
            if (m_output_scanline < m_output_height)
            {
                ERREXIT(J_MESSAGE_CODE.JERR_TOO_LITTLE_DATA);
            }

            m_master.finish_output_pass();
            m_global_state = JpegState.DSTATE_STOPPING;
        }
        else if (m_global_state == JpegState.DSTATE_BUFIMAGE)
        {
            /* Finishing after a buffered-image operation */
            m_global_state = JpegState.DSTATE_STOPPING;
        }
        else if (m_global_state != JpegState.DSTATE_STOPPING)
        {
            /* STOPPING = repeat call after a suspension, anything else is error */
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        /* Read until EOI */
        while (!m_inputctl.EOIReached())
        {
            if (m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED)
            {
                /* Suspend, come back later */
                return false;
            }
        }

        /* Do final cleanup */
        m_src.term_source();

        /* We can use jpeg_abort to release memory and reset global_state */
        jpeg_abort();
        return true;
    }

    /// <summary>
    /// Alternate entry point to read raw data.
    /// </summary>
    /// <param name="data">The raw data.</param>
    /// <param name="max_lines">The number of scanlines for reading.</param>
    /// <returns>The number of lines actually read.</returns>
    /// <remarks>Replaces <see cref="jpeg_decompress_struct.jpeg_read_scanlines">jpeg_read_scanlines</see> 
    /// when reading raw downsampled data. Processes exactly one iMCU row per call, unless suspended.
    /// </remarks>
    public int jpeg_read_raw_data(byte[][][] data, int max_lines)
    {
        if (m_global_state != JpegState.DSTATE_RAW_OK)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        if (m_output_scanline >= m_output_height)
        {
            WARNMS(J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA);
            return 0;
        }

        /* Call progress monitor hook if present */
        if (m_progress != null)
        {
            m_progress.Pass_counter = m_output_scanline;
            m_progress.Pass_limit = m_output_height;
            m_progress.Updated();
        }

        /* Verify that at least one iMCU row can be returned. */
        int lines_per_iMCU_row = m_max_v_samp_factor * min_DCT_v_scaled_size;
        if (max_lines < lines_per_iMCU_row)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BUFFER_SIZE);
        }

        int componentCount = data.Length; // maybe we should use max_lines here
        ComponentBuffer[] cb = new ComponentBuffer[componentCount];
        for (int i = 0; i < componentCount; i++)
        {
            cb[i].SetBuffer(data[i]);
        }

        /* Decompress directly into user's buffer. */
        if (m_coef.decompress_data(cb) == ReadResult.JPEG_SUSPENDED)
        {
            /* suspension forced, can do nothing more */
            return 0;
        }

        /* OK, we processed one iMCU row. */
        m_output_scanline += lines_per_iMCU_row;
        return lines_per_iMCU_row;
    }

    //////////////////////////////////////////////////////////////////////////
    // Additional entry points for buffered-image mode.

    /// <summary>
    /// Is there more than one scan?
    /// </summary>
    /// <returns><c>true</c> if image has more than one scan; otherwise, <c>false</c></returns>
    /// <remarks>If you are concerned about maximum performance on baseline JPEG files,
    /// you should use <see href="../articles/KB/buffered-image-mode.html">buffered-image mode</see> only
    /// when the incoming file actually has multiple scans. This can be tested by calling this method.
    /// </remarks>
    public bool jpeg_has_multiple_scans()
    {
        /* Only valid after jpeg_read_header completes */
        if (m_global_state < JpegState.DSTATE_READY || m_global_state > JpegState.DSTATE_STOPPING)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        return m_inputctl.HasMultipleScans();
    }

    /// <summary>
    /// Initialize for an output pass in <see href="../articles/KB/buffered-image-mode.html">buffered-image mode</see>.
    /// </summary>
    /// <param name="scan_number">Indicates which scan of the input file is to be displayed; 
    /// the scans are numbered starting at 1 for this purpose.</param>
    /// <returns><c>true</c> if done; <c>false</c> if suspended</returns>
    /// <seealso cref="jpeg_finish_output"/>
    /// <seealso href="../articles/KB/buffered-image-mode.html">Buffered-image mode</seealso>
    public bool jpeg_start_output(int scan_number)
    {
        if (m_global_state != JpegState.DSTATE_BUFIMAGE && m_global_state != JpegState.DSTATE_PRESCAN)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        /* Limit scan number to valid range */
        if (scan_number <= 0)
        {
            scan_number = 1;
        }

        if (m_inputctl.EOIReached() && scan_number > m_input_scan_number)
        {
            scan_number = m_input_scan_number;
        }

        m_output_scan_number = scan_number;
        /* Perform any dummy output passes, and set up for the real pass */
        return output_pass_setup();
    }

    /// <summary>
    /// Finish up after an output pass in <see href="../articles/KB/buffered-image-mode.html">buffered-image mode</see>.
    /// </summary>
    /// <returns>Returns <c>false</c> if suspended. The return value need be inspected only if a suspending data source is used.</returns>
    /// <seealso cref="jpeg_start_output"/>
    /// <seealso href="../articles/KB/buffered-image-mode.html">Buffered-image mode</seealso>
    public bool jpeg_finish_output()
    {
        if ((m_global_state == JpegState.DSTATE_SCANNING || m_global_state == JpegState.DSTATE_RAW_OK) && m_buffered_image)
        {
            /* Terminate this pass. */
            /* We do not require the whole pass to have been completed. */
            m_master.finish_output_pass();
            m_global_state = JpegState.DSTATE_BUFPOST;
        }
        else if (m_global_state != JpegState.DSTATE_BUFPOST)
        {
            /* BUFPOST = repeat call after a suspension, anything else is error */
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        /* Read markers looking for SOS or EOI */
        while (m_input_scan_number <= m_output_scan_number && !m_inputctl.EOIReached())
        {
            if (m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED)
            {
                /* Suspend, come back later */
                return false;
            }
        }

        m_global_state = JpegState.DSTATE_BUFIMAGE;
        return true;
    }

    /// <summary>
    /// Indicates if we have finished reading the input file.
    /// </summary>
    /// <returns><c>true</c> if we have finished reading the input file.</returns>
    /// <seealso href="../articles/KB/buffered-image-mode.html">Buffered-image mode</seealso>
    public bool jpeg_input_complete()
    {
        /* Check for valid jpeg object */
        if (m_global_state < JpegState.DSTATE_START || m_global_state > JpegState.DSTATE_STOPPING)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        return m_inputctl.EOIReached();
    }

    /// <summary>
    /// Consume data in advance of what the decompressor requires.
    /// </summary>
    /// <returns>The result of data consumption.</returns>
    /// <remarks>This routine can be called at any time after initializing the JPEG object.
    /// It reads some additional data and returns when one of the indicated significant events
    /// occurs. If called after the EOI marker is reached, it will immediately return
    /// <see cref="ReadResult.JPEG_REACHED_EOI"/> without attempting to read more data.</remarks>
    public ReadResult jpeg_consume_input()
    {
        ReadResult retcode = ReadResult.JPEG_SUSPENDED;

        /* NB: every possible DSTATE value should be listed in this switch */
        switch (m_global_state)
        {
            case JpegState.DSTATE_START:
                jpeg_consume_input_start();
                retcode = jpeg_consume_input_inHeader();
                break;
            case JpegState.DSTATE_INHEADER:
                retcode = jpeg_consume_input_inHeader();
                break;
            case JpegState.DSTATE_READY:
                /* Can't advance past first SOS until start_decompress is called */
                retcode = ReadResult.JPEG_REACHED_SOS;
                break;
            case JpegState.DSTATE_PRELOAD:
            case JpegState.DSTATE_PRESCAN:
            case JpegState.DSTATE_SCANNING:
            case JpegState.DSTATE_RAW_OK:
            case JpegState.DSTATE_BUFIMAGE:
            case JpegState.DSTATE_BUFPOST:
            case JpegState.DSTATE_STOPPING:
                retcode = m_inputctl.consume_input();
                break;
            default:
                ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
                break;
        }
        return retcode;
    }

    /// <summary>
    /// Pre-calculate output image dimensions and related values for current decompression parameters.
    /// </summary>
    /// <remarks>This is allowed for possible use by application. Hence it mustn't do anything 
    /// that can't be done twice. Also note that it may be called before the master module is initialized!
    /// </remarks>
    public void jpeg_calc_output_dimensions()
    {
        // Do computations that are needed before master selection phase
        // This function is used for full decompression.
        /* Prevent application from calling me at wrong times */
        if (m_global_state != JpegState.DSTATE_READY)
        {
            ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        }

        /* Compute core output image dimensions and DCT scaling choices. */
        m_inputctl.jpeg_core_output_dimensions();

        /* In selecting the actual DCT scaling for each component, we try to
        * scale up the chroma components via IDCT scaling rather than upsampling.
        * This saves time if the upsampler gets to use 1:1 scaling.
        * Note this code adapts subsampling ratios which are powers of 2.
        */
        for (int ci = 0; ci < m_num_components; ci++)
        {
            int ssize = 1;

            jpeg_component_info compptr = Comp_info![ci];
            while (min_DCT_h_scaled_size * ssize <=
                (m_do_fancy_upsampling ? JpegConstants.DCTSIZE : JpegConstants.DCTSIZE / 2) &&
                (m_max_h_samp_factor % (compptr.H_samp_factor * ssize * 2)) == 0)
            {
                ssize = ssize * 2;
            }

            compptr.DCT_h_scaled_size = min_DCT_h_scaled_size * ssize;

            ssize = 1;
            while (min_DCT_v_scaled_size * ssize <=
               (m_do_fancy_upsampling ? JpegConstants.DCTSIZE : JpegConstants.DCTSIZE / 2) &&
               (m_max_v_samp_factor % (compptr.V_samp_factor * ssize * 2)) == 0)
            {
                ssize = ssize * 2;
            }
            compptr.DCT_v_scaled_size = min_DCT_v_scaled_size * ssize;

            /* We don't support IDCT ratios larger than 2. */
            if (compptr.DCT_h_scaled_size > compptr.DCT_v_scaled_size * 2)
            {
                compptr.DCT_h_scaled_size = compptr.DCT_v_scaled_size * 2;
            }
            else if (compptr.DCT_v_scaled_size > compptr.DCT_h_scaled_size * 2)
            {
                compptr.DCT_v_scaled_size = compptr.DCT_h_scaled_size * 2;
            }
        }

        /* Recompute downsampled dimensions of components;
        * application needs to know these if using raw downsampled data.
        */
        for (int ci = 0; ci < m_num_components; ci++)
        {
            /* Size in samples, after IDCT scaling */
            Comp_info![ci].downsampled_width = (int)JpegUtils.jdiv_round_up(
                m_image_width * Comp_info![ci].H_samp_factor * Comp_info![ci].DCT_h_scaled_size,
                m_max_h_samp_factor * block_size);

            Comp_info![ci].downsampled_height = (int)JpegUtils.jdiv_round_up(
                m_image_height * Comp_info![ci].V_samp_factor * Comp_info![ci].DCT_v_scaled_size,
                m_max_v_samp_factor * block_size);
        }

        /* Report number of components in selected colorspace. */
        /* Probably this should be in the color conversion module... */
        switch (m_out_color_space)
        {
            case J_COLOR_SPACE.JCS_GRAYSCALE:
                m_out_color_components = 1;
                break;

            case J_COLOR_SPACE.JCS_RGB:
            case J_COLOR_SPACE.JCS_BG_RGB:
                m_out_color_components = JpegConstants.RGB_PIXELSIZE;
                break;

            case J_COLOR_SPACE.JCS_YCbCr:
            case J_COLOR_SPACE.JCS_BG_YCC:
                m_out_color_components = 3;
                break;

            case J_COLOR_SPACE.JCS_CMYK:
            case J_COLOR_SPACE.JCS_YCCK:
                m_out_color_components = 4;
                break;

            default:
                /* else must be same colorspace as in file */
                m_out_color_components = m_num_components;
                break;
        }

        m_output_components = (m_quantize_colors ? 1 : m_out_color_components);

        /* See if upsampler will want to emit more than one row at a time */
        if (use_merged_upsample())
        {
            m_rec_outbuf_height = m_max_v_samp_factor;
        }
        else
        {
            m_rec_outbuf_height = 1;
        }
    }

    /// <summary>
    /// Read or write the raw DCT coefficient arrays from a JPEG file (useful for lossless transcoding).
    /// </summary>
    /// <returns>Returns <c>null</c> if suspended. This case need be checked only 
    /// if a suspending data source is used.
    /// </returns>
    /// <remarks>
    /// <see cref="jpeg_read_header">jpeg_read_header</see> must be completed before calling this.<br/>
    /// 
    /// The entire image is read into a set of virtual coefficient-block arrays, one per component.
    /// The return value is an array of virtual-array descriptors.<br/>
    /// 
    /// An alternative usage is to simply obtain access to the coefficient arrays during a 
    /// <see href="../articles/KB/buffered-image-mode.html">buffered-image mode</see> decompression operation. This is allowed after any 
    /// <see cref="jpeg_finish_output">jpeg_finish_output</see> call. The arrays can be accessed 
    /// until <see cref="jpeg_finish_decompress">jpeg_finish_decompress</see> is called. 
    /// Note that any call to the library may reposition the arrays, 
    /// so don't rely on <see cref="jvirt_array{T}.Access"/> results to stay valid across library calls.
    /// </remarks>
    public jvirt_array<JBLOCK>[]? jpeg_read_coefficients()
    {
        if (m_global_state == JpegState.DSTATE_READY)
        {
            /* First call: initialize active modules */
            transdecode_master_selection();
            m_global_state = JpegState.DSTATE_RDCOEFS;
        }

        if (m_global_state == JpegState.DSTATE_RDCOEFS)
        {
            /* Absorb whole file into the coef buffer */
            for (; ; )
            {
                ReadResult retcode;
                /* Call progress monitor hook if present */
                if (m_progress != null)
                {
                    m_progress.Updated();
                }

                /* Absorb some more input */
                retcode = m_inputctl.consume_input();
                if (retcode == ReadResult.JPEG_SUSPENDED)
                {
                    return null;
                }

                if (retcode == ReadResult.JPEG_REACHED_EOI)
                {
                    break;
                }

                /* Advance progress counter if appropriate */
                if (m_progress != null && (retcode == ReadResult.JPEG_ROW_COMPLETED || retcode == ReadResult.JPEG_REACHED_SOS))
                {
                    m_progress.Pass_counter++;
                    if (m_progress.Pass_counter >= m_progress.Pass_limit)
                    {
                        /* startup underestimated number of scans; ratchet up one scan */
                        m_progress.Pass_limit += m_total_iMCU_rows;
                    }
                }
            }

            /* Set state so that jpeg_finish_decompress does the right thing */
            m_global_state = JpegState.DSTATE_STOPPING;
        }

        /* At this point we should be in state DSTATE_STOPPING if being used
        * standalone, or in state DSTATE_BUFIMAGE if being invoked to get access
        * to the coefficients during a full buffered-image-mode decompression.
        */
        if ((m_global_state == JpegState.DSTATE_STOPPING || m_global_state == JpegState.DSTATE_BUFIMAGE) && m_buffered_image)
        {
            return m_coef.GetCoefArrays();
        }

        /* Oops, improper usage */
        ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state);
        /* keep compiler happy */
        return null;
    }

    /// <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>
#if LIBJPEG_ENCODER
    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;
        dstinfo.jpeg_width = Output_width;
        dstinfo.jpeg_height = Output_height;
        dstinfo.min_DCT_h_scaled_size = min_DCT_h_scaled_size;
        dstinfo.min_DCT_v_scaled_size = min_DCT_v_scaled_size;

        /* 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.
         * Note: Entropy table assignment in jpeg_set_colorspace depends
         * on color_transform.
         */
        dstinfo.color_transform = color_transform;
        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++)
        {
            JQUANT_TBL? srcQuant = m_quant_tbl_ptrs[tblno];
            if (srcQuant != null)
            {
                JQUANT_TBL? dstQuant = dstinfo.m_quant_tbl_ptrs[tblno];
                if (dstQuant == null)
                {
                    dstQuant = new JQUANT_TBL();
                    dstinfo.m_quant_tbl_ptrs[tblno] = dstQuant;
                }

                Buffer.BlockCopy(srcQuant.quantval, 0,
                    dstQuant.quantval, 0,
                    dstQuant.quantval.Length * sizeof(short));

                dstQuant.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 entropy 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.
        */
        if (m_saw_JFIF_marker)
        {
            if (m_JFIF_major_version == 1 || m_JFIF_major_version == 2)
            {
                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;
        }
    }
#endif

    /// <summary>
    /// Aborts processing of a JPEG decompression operation.
    /// </summary>
    /// <seealso cref="jpeg_common_struct.jpeg_abort"/>
    public void jpeg_abort_decompress()
    {
        jpeg_abort();
    }

    /// <summary>
    /// Sets processor for special marker.
    /// </summary>
    /// <param name="marker_code">The marker code.</param>
    /// <param name="routine">The processor.</param>
    /// <remarks>Allows you to supply your own routine to process 
    /// COM and/or APPn markers on-the-fly as they are read.
    /// </remarks>
    /// <seealso href="../articles/KB/special-markers.html">Special markers</seealso>
    /// <summary>
    /// Determine whether merged upsample/color conversion should be used.
    /// CRUCIAL: this must match the actual capabilities of merged upsampler!
    /// </summary>
    internal bool use_merged_upsample()
    {
        /* Merging is the equivalent of plain box-filter upsampling. */
        /* The following condition is only needed if fancy shall select
         * a different upsampling method.  In our current implementation
         * fancy only affects the DCT scaling, thus we can use fancy
         * upsampling and merged upsampler simultaneously, in particular
         * with scaled DCT sizes larger than the default DCTSIZE.
         */
        if (m_CCIR601_sampling)
        {
            return false;
        }

        /* my_upsampler only supports YCC=>RGB color conversion */
        if ((m_jpeg_color_space != J_COLOR_SPACE.JCS_YCbCr &&
            m_jpeg_color_space != J_COLOR_SPACE.JCS_BG_YCC) ||
            m_num_components != 3 ||
            m_out_color_space != J_COLOR_SPACE.JCS_RGB ||
            m_out_color_components != JpegConstants.RGB_PIXELSIZE ||
            color_transform != J_COLOR_TRANSFORM.JCT_NONE)
        {
            return false;
        }

        /* and it only handles 2h1v or 2h2v sampling ratios */
        if (Comp_info![0].H_samp_factor != 2 || Comp_info![1].H_samp_factor != 1 ||
            Comp_info![2].H_samp_factor != 1 || Comp_info![0].V_samp_factor > 2 ||
            Comp_info![1].V_samp_factor != 1 || Comp_info![2].V_samp_factor != 1)
        {
            return false;
        }

        /* furthermore, it doesn't work if we've scaled the IDCTs differently */
        if (Comp_info![0].DCT_h_scaled_size != min_DCT_h_scaled_size ||
            Comp_info![1].DCT_h_scaled_size != min_DCT_h_scaled_size ||
            Comp_info![2].DCT_h_scaled_size != min_DCT_h_scaled_size ||
            Comp_info![0].DCT_v_scaled_size != min_DCT_v_scaled_size ||
            Comp_info![1].DCT_v_scaled_size != min_DCT_v_scaled_size ||
            Comp_info![2].DCT_v_scaled_size != min_DCT_v_scaled_size)
        {
            return false;
        }

        /* ??? also need to test for upsample-time rescaling, when & if supported */
        /* by golly, it'll work... */
        return true;
    }

    /// <summary>
    /// Initialization of JPEG compression objects.
    /// The error manager must already be set up (in case memory manager fails).
    /// </summary>
    private void initialize()
    {
        /* Zero out pointers to permanent structures. */
        m_progress = null;
        m_src = default;

        for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++)
        {
            m_quant_tbl_ptrs[i] = null;
        }

        for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++)
        {
            m_dc_huff_tbl_ptrs[i] = null;
            m_ac_huff_tbl_ptrs[i] = null;
        }

        /* Initialize marker processor (APP0/APP14 handling, etc.) */
        m_marker = new jpeg_marker_reader(this);

        /* And initialize the overall input controller. */
        m_inputctl = new jpeg_input_controller(this);

        /* OK, I'm ready */
        m_global_state = JpegState.DSTATE_START;
    }

    /// <summary>
    /// Master selection of decompression modules for transcoding (that is, reading 
    /// raw DCT coefficient arrays from an input JPEG file.)
    /// This substitutes for initialization of the full decompressor.
    /// </summary>
    private void transdecode_master_selection()
    {
        /* This is effectively a buffered-image operation. */
        m_buffered_image = true;

        /* Compute output image dimensions and related values. */
        m_inputctl.jpeg_core_output_dimensions();

        m_entropy = jpeg_entropy_decoder_union.CreateHuffman(this);

        /* Always get a full-image coefficient buffer. */
        m_coef = new jpeg_d_coef_controller(this, true);

        /* Initialize input side of decompressor to consume first scan. */
        m_inputctl.start_input_pass();

        /* Initialize progress monitoring. */
        if (m_progress != null)
        {
            int nscans = 1;
            /* Estimate number of scans to set pass_limit. */
            if (m_progressive_mode)
            {
                /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */
                nscans = 2 + 3 * m_num_components;
            }
            else if (m_inputctl.HasMultipleScans())
            {
                /* For a nonprogressive multiscan file, estimate 1 scan per component. */
                nscans = m_num_components;
            }

            m_progress.Pass_counter = 0;
            m_progress.Pass_limit = m_total_iMCU_rows * nscans;
            m_progress.Completed_passes = 0;
            m_progress.Total_passes = 1;
        }
    }

    /// <summary>
    /// Set up for an output pass, and perform any dummy pass(es) needed.
    /// Common subroutine for jpeg_start_decompress and jpeg_start_output.
    /// Entry: global_state = DSTATE_PRESCAN only if previously suspended.
    /// Exit: If done, returns true and sets global_state for proper output mode.
    ///       If suspended, returns false and sets global_state = DSTATE_PRESCAN.
    /// </summary>
    private bool output_pass_setup()
    {
        if (m_global_state != JpegState.DSTATE_PRESCAN)
        {
            /* First call: do pass setup */
            m_master.prepare_for_output_pass();
            m_output_scanline = 0;
            m_global_state = JpegState.DSTATE_PRESCAN;
        }

        /* Loop over any required dummy passes */
        while (m_master.IsDummyPass())
        {
            /* Crank through the dummy pass */
            while (m_output_scanline < m_output_height)
            {
                int last_scanline;
                /* Call progress monitor hook if present */
                if (m_progress != null)
                {
                    m_progress.Pass_counter = m_output_scanline;
                    m_progress.Pass_limit = m_output_height;
                    m_progress.Updated();
                }

                /* Process some data */
                last_scanline = m_output_scanline;
                m_main.process_data(Array.Empty<byte[]>(), ref m_output_scanline, 0);
                if (m_output_scanline == last_scanline)
                {
                    /* No progress made, must suspend */
                    return false;
                }
            }

            /* Finish up dummy pass, and set up for another one */
            m_master.finish_output_pass();
            m_master.prepare_for_output_pass();
            m_output_scanline = 0;
        }

        /* Ready for application to drive output pass through
        * jpeg_read_scanlines or jpeg_read_raw_data.
        */
        m_global_state = m_raw_data_out ? JpegState.DSTATE_RAW_OK : JpegState.DSTATE_SCANNING;
        return true;
    }

    /// <summary>
    /// Set default decompression parameters.
    /// </summary>
    private void default_decompress_parms()
    {
        /* Guess the input colorspace, and set output colorspace accordingly. */
        /* Note application may override our guesses. */
        switch (m_num_components)
        {
            case 1:
                m_jpeg_color_space = J_COLOR_SPACE.JCS_GRAYSCALE;
                m_out_color_space = J_COLOR_SPACE.JCS_GRAYSCALE;
                break;

            case 3:
                int cid0 = Comp_info![0].Component_id;
                int cid1 = Comp_info![1].Component_id;
                int cid2 = Comp_info![2].Component_id;

                // Use Adobe marker info, otherwise try to guess from the component IDs
                if (m_saw_Adobe_marker)
                {
                    switch (m_Adobe_transform)
                    {
                        case 0:
                            m_jpeg_color_space = J_COLOR_SPACE.JCS_RGB;
                            break;
                        case 1:
                            m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr;
                            break;
                        default:
                            WARNMS(J_MESSAGE_CODE.JWRN_ADOBE_XFORM, m_Adobe_transform);
                            /* assume it's YCbCr */
                            m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr;
                            break;
                    }
                }
                else if (cid0 == 0x01 && cid1 == 0x02 && cid2 == 0x03)
                {
                    m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr;
                }
                else if (cid0 == 0x01 && cid1 == 0x22 && cid2 == 0x23)
                {
                    m_jpeg_color_space = J_COLOR_SPACE.JCS_BG_YCC;
                }
                else if (cid0 == 0x52 && cid1 == 0x47 && cid2 == 0x42)
                {
                    /* ASCII 'R', 'G', 'B' */
                    m_jpeg_color_space = J_COLOR_SPACE.JCS_RGB;
                }
                else if (cid0 == 0x72 && cid1 == 0x67 && cid2 == 0x62)
                {
                    /* ASCII 'r', 'g', 'b' */
                    m_jpeg_color_space = J_COLOR_SPACE.JCS_BG_RGB;
                }
                else if (m_saw_JFIF_marker)
                {
                    /* assume it's YCbCr */
                    m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr;
                }
                else
                {
                    TRACEMS(1, J_MESSAGE_CODE.JTRC_UNKNOWN_IDS, cid0, cid1, cid2);
                    /* assume it's YCbCr */
                    m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr;
                }
                /* Always guess RGB is proper output colorspace. */
                m_out_color_space = J_COLOR_SPACE.JCS_RGB;
                break;

            case 4:
                if (m_saw_Adobe_marker)
                {
                    switch (m_Adobe_transform)
                    {
                        case 0:
                            m_jpeg_color_space = J_COLOR_SPACE.JCS_CMYK;
                            break;
                        case 2:
                            m_jpeg_color_space = J_COLOR_SPACE.JCS_YCCK;
                            break;
                        default:
                            WARNMS(J_MESSAGE_CODE.JWRN_ADOBE_XFORM, m_Adobe_transform);
                            /* assume it's YCCK */
                            m_jpeg_color_space = J_COLOR_SPACE.JCS_YCCK;
                            break;
                    }
                }
                else
                {
                    /* No special markers, assume straight CMYK. */
                    m_jpeg_color_space = J_COLOR_SPACE.JCS_CMYK;
                }

                m_out_color_space = J_COLOR_SPACE.JCS_CMYK;
                break;

            default:
                m_jpeg_color_space = J_COLOR_SPACE.JCS_UNKNOWN;
                m_out_color_space = J_COLOR_SPACE.JCS_UNKNOWN;
                break;
        }

        /* Set defaults for other decompression parameters. */
        m_scale_num = block_size;       /* 1:1 scaling */
        m_scale_denom = block_size;
        m_buffered_image = false;
        m_raw_data_out = false;
        m_dct_method = JpegConstants.JDCT_DEFAULT;
        m_do_fancy_upsampling = true;
        m_do_block_smoothing = true;
        m_quantize_colors = false;

        /* We set these in case application only sets quantize_colors. */
        m_dither_mode = J_DITHER_MODE.JDITHER_FS;
        m_two_pass_quantize = true;
        m_desired_number_of_colors = 256;
        m_colormap = null;

        /* Initialize for no mode change in buffered-image mode. */
        m_enable_1pass_quant = false;
        m_enable_external_quant = false;
        m_enable_2pass_quant = false;
    }

    private void jpeg_consume_input_start()
    {
        /* Start-of-datastream actions: reset appropriate modules */
        m_inputctl.reset_input_controller();

        /* Initialize application's data source module */
        m_src.init_source();
        m_global_state = JpegState.DSTATE_INHEADER;
    }

    private ReadResult jpeg_consume_input_inHeader()
    {
        ReadResult retcode = m_inputctl.consume_input();
        if (retcode == ReadResult.JPEG_REACHED_SOS)
        {
            /* Found SOS, prepare to decompress */
            /* Set up default parameters based on header data */
            default_decompress_parms();

            /* Set global state: ready for start_decompress */
            m_global_state = JpegState.DSTATE_READY;
        }

        return retcode;
    }
}

#pragma warning restore 0419
#pragma warning restore 1572
#pragma warning restore 1574
#pragma warning restore 1587
