/// <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); }
/// <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); }
/// <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); } }
/// <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); } }