/// <summary>
        /// Process some data in the first pass of a multi-pass case.
        /// We process the equivalent of one fully interleaved MCU row ("iMCU" row)
        /// per call, ie, v_samp_factor block rows for each component in the image.
        /// This amount of data is read from the source buffer, DCT'd and quantized,
        /// and saved into the virtual arrays.  We also generate suitable dummy blocks
        /// as needed at the right and lower edges.  (The dummy blocks are constructed
        /// in the virtual arrays, which have been padded appropriately.)  This makes
        /// it possible for subsequent passes not to worry about real vs. dummy blocks.
        ///
        /// We must also emit the data to the entropy encoder.  This is conveniently
        /// done by calling compress_output() after we've loaded the current strip
        /// of the virtual arrays.
        ///
        /// NB: input_buf contains a plane for each component in image.  All
        /// components are DCT'd and loaded into the virtual arrays in this pass.
        /// However, it may be that only a subset of the components are emitted to
        /// the entropy encoder during this first pass; be careful about looking
        /// at the scan-dependent variables (MCU dimensions, etc).
        /// </summary>
        private bool compressFirstPass(byte[][][] input_buf)
        {
            int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1;

            for (int ci = 0; ci < m_cinfo.m_num_components; ci++)
            {
                jpeg_component_info componentInfo = m_cinfo.Component_info[ci];

                /* Align the virtual buffer for this component. */
                JBLOCK[][] buffer = m_whole_image[ci].Access(m_iMCU_row_num * componentInfo.V_samp_factor,
                                                             componentInfo.V_samp_factor);

                /* Count non-dummy DCT block rows in this iMCU row. */
                int block_rows;
                if (m_iMCU_row_num < last_iMCU_row)
                {
                    block_rows = componentInfo.V_samp_factor;
                }
                else
                {
                    /* NB: can't use last_row_height here, since may not be set! */
                    block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor;
                    if (block_rows == 0)
                    {
                        block_rows = componentInfo.V_samp_factor;
                    }
                }

                int blocks_across = componentInfo.Width_in_blocks;
                int h_samp_factor = componentInfo.H_samp_factor;

                /* Count number of dummy blocks to be added at the right margin. */
                int ndummy = blocks_across % h_samp_factor;
                if (ndummy > 0)
                {
                    ndummy = h_samp_factor - ndummy;
                }

                jpeg_forward_dct.forward_DCT_ptr forward_DCT = m_cinfo.m_fdct.forward_DCT[ci];

                /* Perform DCT for all non-dummy blocks in this iMCU row.  Each call
                 * on forward_DCT processes a complete horizontal row of DCT blocks.
                 */
                for (int block_row = 0; block_row < block_rows; block_row++)
                {
                    forward_DCT(componentInfo, input_buf[ci],
                                buffer[block_row], block_row * componentInfo.DCT_v_scaled_size, 0, blocks_across);

                    if (ndummy > 0)
                    {
                        /* Create dummy blocks at the right edge of the image. */
                        Array.Clear(buffer[block_row][blocks_across].data, 0, buffer[block_row][blocks_across].data.Length);

                        short lastDC = buffer[block_row][blocks_across - 1][0];
                        for (int bi = 0; bi < ndummy; bi++)
                        {
                            buffer[block_row][blocks_across + bi][0] = lastDC;
                        }
                    }
                }

                /* If at end of image, create dummy block rows as needed.
                 * The tricky part here is that within each MCU, we want the DC values
                 * of the dummy blocks to match the last real block's DC value.
                 * This squeezes a few more bytes out of the resulting file...
                 */
                if (m_iMCU_row_num == last_iMCU_row)
                {
                    blocks_across += ndummy;    /* include lower right corner */
                    int MCUs_across = blocks_across / h_samp_factor;
                    for (int block_row = block_rows; block_row < componentInfo.V_samp_factor; block_row++)
                    {
                        for (int i = 0; i < blocks_across; i++)
                        {
                            Array.Clear(buffer[block_row][i].data, 0, buffer[block_row][i].data.Length);
                        }

                        int thisOffset = 0;
                        int lastOffset = 0;
                        for (int MCUindex = 0; MCUindex < MCUs_across; MCUindex++)
                        {
                            short lastDC = buffer[block_row - 1][lastOffset + h_samp_factor - 1][0];
                            for (int bi = 0; bi < h_samp_factor; bi++)
                            {
                                buffer[block_row][thisOffset + bi][0] = lastDC;
                            }

                            thisOffset += h_samp_factor; /* advance to next MCU in row */
                            lastOffset += h_samp_factor;
                        }
                    }
                }
            }

            /* NB: compress_output will increment iMCU_row_num if successful.
             * A suspension return will result in redoing all the work above next time.
             */

            /* Emit data to the entropy encoder, sharing code with subsequent passes */
            return(compressOutput());
        }
        /// <summary>
        /// Process some data in the single-pass case.
        /// We process the equivalent of one fully interleaved MCU row ("iMCU" row)
        /// per call, ie, v_samp_factor block rows for each component in the image.
        /// Returns true if the iMCU row is completed, false if suspended.
        ///
        /// NB: input_buf contains a plane for each component in image,
        /// which we index according to the component's SOF position.
        /// </summary>
        private bool compressDataImpl(byte[][][] input_buf)
        {
            int last_MCU_col  = m_cinfo.m_MCUs_per_row - 1;
            int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1;

            /* Loop to write as much as one whole iMCU row */
            for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++)
            {
                for (int MCU_col_num = m_mcu_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++)
                {
                    /* Determine where data comes from in input_buf and do the DCT thing.
                     * Each call on forward_DCT processes a horizontal row of DCT blocks
                     * as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks
                     * sequentially.  Dummy blocks at the right or bottom edge are filled in
                     * specially.  The data in them does not matter for image reconstruction,
                     * so we fill them with values that will encode to the smallest amount of
                     * data, viz: all zeroes in the AC entries, DC entries equal to previous
                     * block's DC value.  (Thanks to Thomas Kinsman for this idea.)
                     */
                    int blkn = 0;
                    for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++)
                    {
                        jpeg_component_info componentInfo            = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]];
                        jpeg_forward_dct.forward_DCT_ptr forward_DCT = m_cinfo.m_fdct.forward_DCT[componentInfo.Component_index];

                        int blockcnt = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width;
                        int xpos     = MCU_col_num * componentInfo.MCU_sample_width;
                        int ypos     = yoffset * componentInfo.DCT_v_scaled_size;

                        for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++)
                        {
                            if (m_iMCU_row_num < last_iMCU_row || yoffset + yindex < componentInfo.last_row_height)
                            {
                                forward_DCT(componentInfo, input_buf[componentInfo.Component_index],
                                            m_MCU_buffer[blkn], ypos, xpos, blockcnt);

                                if (blockcnt < componentInfo.MCU_width)
                                {
                                    /* Create some dummy blocks at the right edge of the image. */
                                    for (int i = 0; i < (componentInfo.MCU_width - blockcnt); i++)
                                    {
                                        Array.Clear(m_MCU_buffer[blkn + blockcnt][i].data, 0, m_MCU_buffer[blkn + blockcnt][i].data.Length);
                                    }

                                    for (int bi = blockcnt; bi < componentInfo.MCU_width; bi++)
                                    {
                                        m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn + bi - 1][0][0];
                                    }
                                }
                            }
                            else
                            {
                                /* Create a row of dummy blocks at the bottom of the image. */
                                for (int i = 0; i < componentInfo.MCU_width; i++)
                                {
                                    Array.Clear(m_MCU_buffer[blkn][i].data, 0, m_MCU_buffer[blkn][i].data.Length);
                                }

                                for (int bi = 0; bi < componentInfo.MCU_width; bi++)
                                {
                                    m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn - 1][0][0];
                                }
                            }

                            blkn += componentInfo.MCU_width;
                            ypos += componentInfo.DCT_v_scaled_size;
                        }
                    }

                    /* Try to write the MCU.  In event of a suspension failure, we will
                     * re-DCT the MCU on restart (a bit inefficient, could be fixed...)
                     */
                    if (!m_cinfo.m_entropy.encode_mcu(m_MCU_buffer))
                    {
                        /* Suspension forced; update state counters and exit */
                        m_MCU_vert_offset = yoffset;
                        m_mcu_ctr         = MCU_col_num;
                        return(false);
                    }
                }

                /* Completed an MCU row, but perhaps not an iMCU row */
                m_mcu_ctr = 0;
            }

            /* Completed the iMCU row, advance counters for next one */
            m_iMCU_row_num++;
            start_iMCU_row();
            return(true);
        }