예제 #1
0
        /// <summary>
        /// Find the next JPEG marker, save it in cinfo.unread_marker.
        /// Returns false if had to suspend before reaching a marker;
        /// in that case cinfo.unread_marker is unchanged.
        ///
        /// Note that the result might not be a valid marker code,
        /// but it will never be 0 or FF.
        /// </summary>
        public bool next_marker()
        {
            int c;

            for ( ; ;)
            {
                if (!m_cinfo.m_src.GetByte(out c))
                {
                    return(false);
                }

                /* Skip any non-FF bytes.
                 * This may look a bit inefficient, but it will not occur in a valid file.
                 * We sync after each discarded byte so that a suspending data source
                 * can discard the byte from its buffer.
                 */
                while (c != 0xFF)
                {
                    m_cinfo.m_marker.m_discarded_bytes++;
                    if (!m_cinfo.m_src.GetByte(out c))
                    {
                        return(false);
                    }
                }

                /* This loop swallows any duplicate FF bytes.  Extra FFs are legal as
                 * pad bytes, so don't count them in discarded_bytes.  We assume there
                 * will not be so many consecutive FF bytes as to overflow a suspending
                 * data source's input buffer.
                 */
                do
                {
                    if (!m_cinfo.m_src.GetByte(out c))
                    {
                        return(false);
                    }
                }while (c == 0xFF);

                if (c != 0)
                {
                    /* found a valid marker, exit loop */
                    break;
                }

                /* Reach here if we found a stuffed-zero data sequence (FF/00).
                 * Discard it and loop back to try again.
                 */
                m_cinfo.m_marker.m_discarded_bytes += 2;
            }

            if (m_cinfo.m_marker.m_discarded_bytes != 0)
            {
                m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_EXTRANEOUS_DATA, m_cinfo.m_marker.m_discarded_bytes, c);
                m_cinfo.m_marker.m_discarded_bytes = 0;
            }

            m_cinfo.m_unread_marker = c;
            return(true);
        }
예제 #2
0
        /// <summary>
        /// Fill the input buffer - called whenever buffer is emptied.
        ///
        /// 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.
        ///
        /// 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.
        ///
        /// 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 scan lines 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.
        ///
        /// 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.
        /// </summary>
        public override bool fill_input_buffer()
        {
            int nbytes = m_infile.Read(m_buffer, 0, InputBufferSize);

            if (nbytes <= 0)
            {
                if (m_start_of_file) /* Treat empty input file as fatal error */
                {
                    m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_INPUT_EMPTY);
                }

                m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_JPEG_EOF);
                /* Insert a fake EOI marker */
                m_buffer[0] = 0xFF;
                m_buffer[1] = (byte)JPEG_MARKER.EOI;
                nbytes      = 2;
            }

            initInternalBuffer(m_buffer, nbytes);
            m_start_of_file = false;

            return(true);
        }
예제 #3
0
        /// <summary>
        /// This is the default resync_to_restart method for data source 
        /// managers to use if they don't have any better approach.
        /// </summary>
        /// <param name="cinfo">An instance of <see cref="jpeg_decompress_struct"/></param>
        /// <param name="desired">The desired</param>
        /// <returns><c>false</c> if suspension is required.</returns>
        /// <remarks>That method assumes that no backtracking is possible. 
        /// Some data source managers may be able to back up, or may have 
        /// additional knowledge about the data which permits a more 
        /// intelligent recovery strategy; such managers would
        /// presumably supply their own resync method.<br/><br/>
        /// 
        /// read_restart_marker calls resync_to_restart if it finds a marker other than
        /// the restart marker it was expecting.  (This code is *not* used unless
        /// a nonzero restart interval has been declared.)  cinfo.unread_marker is
        /// the marker code actually found (might be anything, except 0 or FF).
        /// The desired restart marker number (0..7) is passed as a parameter.<br/><br/>
        /// 
        /// This routine is supposed to apply whatever error recovery strategy seems
        /// appropriate in order to position the input stream to the next data segment.
        /// Note that cinfo.unread_marker is treated as a marker appearing before
        /// the current data-source input point; usually it should be reset to zero
        /// before returning.<br/><br/>
        /// 
        /// This implementation is substantially constrained by wanting to treat the
        /// input as a data stream; this means we can't back up.  Therefore, we have
        /// only the following actions to work with:<br/>
        /// 1. Simply discard the marker and let the entropy decoder resume at next
        /// byte of file.<br/>
        /// 2. Read forward until we find another marker, discarding intervening
        /// data.  (In theory we could look ahead within the current bufferload,
        /// without having to discard data if we don't find the desired marker.
        /// This idea is not implemented here, in part because it makes behavior
        /// dependent on buffer size and chance buffer-boundary positions.)<br/>
        /// 3. Leave the marker unread (by failing to zero cinfo.unread_marker).
        /// This will cause the entropy decoder to process an empty data segment,
        /// inserting dummy zeroes, and then we will reprocess the marker.<br/>
        /// 
        /// #2 is appropriate if we think the desired marker lies ahead, while #3 is
        /// appropriate if the found marker is a future restart marker (indicating
        /// that we have missed the desired restart marker, probably because it got
        /// corrupted).<br/>
        /// We apply #2 or #3 if the found marker is a restart marker no more than
        /// two counts behind or ahead of the expected one.  We also apply #2 if the
        /// found marker is not a legal JPEG marker code (it's certainly bogus data).
        /// If the found marker is a restart marker more than 2 counts away, we do #1
        /// (too much risk that the marker is erroneous; with luck we will be able to
        /// resync at some future point).<br/>
        /// For any valid non-restart JPEG marker, we apply #3.  This keeps us from
        /// overrunning the end of a scan.  An implementation limited to single-scan
        /// files might find it better to apply #2 for markers other than EOI, since
        /// any other marker would have to be bogus data in that case.</remarks>
        public virtual bool resync_to_restart(jpeg_decompress_struct cinfo, int desired)
        {
            /* Always put up a warning. */
            cinfo.WARNMS(J_MESSAGE_CODE.JWRN_MUST_RESYNC, cinfo.m_unread_marker, desired);

            /* Outer loop handles repeated decision after scanning forward. */
            int action = 1;
            for ( ; ; )
            {
                if (cinfo.m_unread_marker < (int)JPEG_MARKER.SOF0)
                {
                    /* invalid marker */
                    action = 2;
                }
                else if (cinfo.m_unread_marker < (int)JPEG_MARKER.RST0 ||
                    cinfo.m_unread_marker > (int)JPEG_MARKER.RST7)
                {
                    /* valid non-restart marker */
                    action = 3;
                }
                else
                {
                    if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 1) & 7))
                        || cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 2) & 7)))
                    {
                        /* one of the next two expected restarts */
                        action = 3;
                    }
                    else if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 1) & 7)) ||
                        cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 2) & 7)))
                    {
                        /* a prior restart, so advance */
                        action = 2;
                    }
                    else
                    {
                        /* desired restart or too far away */
                        action = 1;
                    }
                }

                cinfo.TRACEMS(4, J_MESSAGE_CODE.JTRC_RECOVERY_ACTION, cinfo.m_unread_marker, action);

                switch (action)
                {
                    case 1:
                        /* Discard marker and let entropy decoder resume processing. */
                        cinfo.m_unread_marker = 0;
                        return true;
                    case 2:
                        /* Scan to the next marker, and repeat the decision loop. */
                        if (!cinfo.m_marker.next_marker())
                            return false;
                        break;
                    case 3:
                        /* Return without advancing past this marker. */
                        /* Entropy decoder will be forced to process an empty segment. */
                        return true;
                }
            }
        }
        /*
         * Routines for processing APPn and COM markers.
         * These are either saved in memory or discarded, per application request.
         * APP0 and APP14 are specially checked to see if they are
         * JFIF and Adobe markers, respectively.
         */

        /// <summary>
        /// Examine first few bytes from an APP0.
        /// Take appropriate action if it is a JFIF marker.
        /// datalen is # of bytes at data[], remaining is length of rest of marker data.
        /// </summary>
        private static void examine_app0(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining)
        {
            int totallen = datalen + remaining;

            if (datalen >= APP0_DATA_LEN &&
                data[0] == 0x4A &&
                data[1] == 0x46 &&
                data[2] == 0x49 &&
                data[3] == 0x46 &&
                data[4] == 0)
            {
                /* Found JFIF APP0 marker: save info */
                cinfo.m_saw_JFIF_marker = true;
                cinfo.m_JFIF_major_version = data[5];
                cinfo.m_JFIF_minor_version = data[6];
                cinfo.m_density_unit = (DensityUnit)data[7];
                cinfo.m_X_density = (short)((data[8] << 8) + data[9]);
                cinfo.m_Y_density = (short)((data[10] << 8) + data[11]);

                /* Check version.
                 * Major version must be 1, anything else signals an incompatible change.
                 * (We used to treat this as an error, but now it's a nonfatal warning,
                 * because some bozo at Hijaak couldn't read the spec.)
                 * Minor version should be 0..2, but process anyway if newer.
                 */
                if (cinfo.m_JFIF_major_version != 1)
                    cinfo.WARNMS(J_MESSAGE_CODE.JWRN_JFIF_MAJOR, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version);

                /* Generate trace messages */
                cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version, cinfo.m_X_density,
                                cinfo.m_Y_density, cinfo.m_density_unit);

                /* Validate thumbnail dimensions and issue appropriate messages */
                if ((data[12] | data[13]) != 0)
                    cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_THUMBNAIL, data[12], data[13]);

                totallen -= APP0_DATA_LEN;
                if (totallen != ((int)data[12] * (int)data[13] * 3))
                    cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_BADTHUMBNAILSIZE, totallen);
            }
            else if (datalen >= 6 && data[0] == 0x4A && data[1] == 0x46 && data[2] == 0x58 && data[3] == 0x58 && data[4] == 0)
            {
                /* Found JFIF "JFXX" extension APP0 marker */
                /* The library doesn't actually do anything with these,
                 * but we try to produce a helpful trace message.
                 */
                switch (data[5])
                {
                    case 0x10:
                        cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_JPEG, totallen);
                        break;
                    case 0x11:
                        cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_PALETTE, totallen);
                        break;
                    case 0x13:
                        cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_RGB, totallen);
                        break;
                    default:
                        cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_EXTENSION, data[5], totallen);
                        break;
                }
            }
            else
            {
                /* Start of APP0 does not match "JFIF" or "JFXX", or too short */
                cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP0, totallen);
            }
        }
예제 #5
0
        /// <summary>
        /// This is the default resync_to_restart method for data source
        /// managers to use if they don't have any better approach.
        /// </summary>
        /// <param name="cinfo">An instance of <see cref="jpeg_decompress_struct"/></param>
        /// <param name="desired">The desired</param>
        /// <returns><c>false</c> if suspension is required.</returns>
        /// <remarks>That method assumes that no backtracking is possible.
        /// Some data source managers may be able to back up, or may have
        /// additional knowledge about the data which permits a more
        /// intelligent recovery strategy; such managers would
        /// presumably supply their own resync method.<br/><br/>
        ///
        /// read_restart_marker calls resync_to_restart if it finds a marker other than
        /// the restart marker it was expecting.  (This code is *not* used unless
        /// a nonzero restart interval has been declared.)  cinfo.unread_marker is
        /// the marker code actually found (might be anything, except 0 or FF).
        /// The desired restart marker number (0..7) is passed as a parameter.<br/><br/>
        ///
        /// This routine is supposed to apply whatever error recovery strategy seems
        /// appropriate in order to position the input stream to the next data segment.
        /// Note that cinfo.unread_marker is treated as a marker appearing before
        /// the current data-source input point; usually it should be reset to zero
        /// before returning.<br/><br/>
        ///
        /// This implementation is substantially constrained by wanting to treat the
        /// input as a data stream; this means we can't back up.  Therefore, we have
        /// only the following actions to work with:<br/>
        /// 1. Simply discard the marker and let the entropy decoder resume at next
        /// byte of file.<br/>
        /// 2. Read forward until we find another marker, discarding intervening
        /// data.  (In theory we could look ahead within the current bufferload,
        /// without having to discard data if we don't find the desired marker.
        /// This idea is not implemented here, in part because it makes behavior
        /// dependent on buffer size and chance buffer-boundary positions.)<br/>
        /// 3. Leave the marker unread (by failing to zero cinfo.unread_marker).
        /// This will cause the entropy decoder to process an empty data segment,
        /// inserting dummy zeroes, and then we will reprocess the marker.<br/>
        ///
        /// #2 is appropriate if we think the desired marker lies ahead, while #3 is
        /// appropriate if the found marker is a future restart marker (indicating
        /// that we have missed the desired restart marker, probably because it got
        /// corrupted).<br/>
        /// We apply #2 or #3 if the found marker is a restart marker no more than
        /// two counts behind or ahead of the expected one.  We also apply #2 if the
        /// found marker is not a legal JPEG marker code (it's certainly bogus data).
        /// If the found marker is a restart marker more than 2 counts away, we do #1
        /// (too much risk that the marker is erroneous; with luck we will be able to
        /// resync at some future point).<br/>
        /// For any valid non-restart JPEG marker, we apply #3.  This keeps us from
        /// overrunning the end of a scan.  An implementation limited to single-scan
        /// files might find it better to apply #2 for markers other than EOI, since
        /// any other marker would have to be bogus data in that case.</remarks>
        public virtual bool resync_to_restart(jpeg_decompress_struct cinfo, int desired)
        {
            /* Always put up a warning. */
            cinfo.WARNMS(J_MESSAGE_CODE.JWRN_MUST_RESYNC, cinfo.m_unread_marker, desired);

            /* Outer loop handles repeated decision after scanning forward. */
            int action = 1;

            for ( ; ;)
            {
                if (cinfo.m_unread_marker < (int)JPEG_MARKER.SOF0)
                {
                    /* invalid marker */
                    action = 2;
                }
                else if (cinfo.m_unread_marker < (int)JPEG_MARKER.RST0 ||
                         cinfo.m_unread_marker > (int)JPEG_MARKER.RST7)
                {
                    /* valid non-restart marker */
                    action = 3;
                }
                else
                {
                    if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 1) & 7)) ||
                        cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 2) & 7)))
                    {
                        /* one of the next two expected restarts */
                        action = 3;
                    }
                    else if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 1) & 7)) ||
                             cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 2) & 7)))
                    {
                        /* a prior restart, so advance */
                        action = 2;
                    }
                    else
                    {
                        /* desired restart or too far away */
                        action = 1;
                    }
                }

                cinfo.TRACEMS(4, J_MESSAGE_CODE.JTRC_RECOVERY_ACTION, cinfo.m_unread_marker, action);

                switch (action)
                {
                case 1:
                    /* Discard marker and let entropy decoder resume processing. */
                    cinfo.m_unread_marker = 0;
                    return(true);

                case 2:
                    /* Scan to the next marker, and repeat the decision loop. */
                    if (!cinfo.m_marker.next_marker())
                    {
                        return(false);
                    }
                    break;

                case 3:
                    /* Return without advancing past this marker. */
                    /* Entropy decoder will be forced to process an empty segment. */
                    return(true);
                }
            }
        }
예제 #6
0
        /*
         * Routines for processing APPn and COM markers.
         * These are either saved in memory or discarded, per application request.
         * APP0 and APP14 are specially checked to see if they are
         * JFIF and Adobe markers, respectively.
         */

        /// <summary>
        /// Examine first few bytes from an APP0.
        /// Take appropriate action if it is a JFIF marker.
        /// datalen is # of bytes at data[], remaining is length of rest of marker data.
        /// </summary>
        private static void examine_app0(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining)
        {
            int totallen = datalen + remaining;

            if (datalen >= APP0_DATA_LEN &&
                data[0] == 0x4A &&
                data[1] == 0x46 &&
                data[2] == 0x49 &&
                data[3] == 0x46 &&
                data[4] == 0)
            {
                /* Found JFIF APP0 marker: save info */
                cinfo.m_saw_JFIF_marker    = true;
                cinfo.m_JFIF_major_version = data[5];
                cinfo.m_JFIF_minor_version = data[6];
                cinfo.m_density_unit       = (DensityUnit)data[7];
                cinfo.m_X_density          = (short)((data[8] << 8) + data[9]);
                cinfo.m_Y_density          = (short)((data[10] << 8) + data[11]);

                /* Check version.
                 * Major version must be 1, anything else signals an incompatible change.
                 * (We used to treat this as an error, but now it's a nonfatal warning,
                 * because some bozo at Hijaak couldn't read the spec.)
                 * Minor version should be 0..2, but process anyway if newer.
                 */
                if (cinfo.m_JFIF_major_version != 1)
                {
                    cinfo.WARNMS(J_MESSAGE_CODE.JWRN_JFIF_MAJOR, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version);
                }

                /* Generate trace messages */
                cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version, cinfo.m_X_density,
                              cinfo.m_Y_density, cinfo.m_density_unit);

                /* Validate thumbnail dimensions and issue appropriate messages */
                if ((data[12] | data[13]) != 0)
                {
                    cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_THUMBNAIL, data[12], data[13]);
                }

                totallen -= APP0_DATA_LEN;
                if (totallen != ((int)data[12] * (int)data[13] * 3))
                {
                    cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_BADTHUMBNAILSIZE, totallen);
                }
            }
            else if (datalen >= 6 && data[0] == 0x4A && data[1] == 0x46 && data[2] == 0x58 && data[3] == 0x58 && data[4] == 0)
            {
                /* Found JFIF "JFXX" extension APP0 marker */

                /* The library doesn't actually do anything with these,
                 * but we try to produce a helpful trace message.
                 */
                switch (data[5])
                {
                case 0x10:
                    cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_JPEG, totallen);
                    break;

                case 0x11:
                    cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_PALETTE, totallen);
                    break;

                case 0x13:
                    cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_RGB, totallen);
                    break;

                default:
                    cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_EXTENSION, data[5], totallen);
                    break;
                }
            }
            else
            {
                /* Start of APP0 does not match "JFIF" or "JFXX", or too short */
                cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP0, totallen);
            }
        }