/// <summary> /// Create the colormap. /// </summary> private void create_colormap() { /* Select number of colors for each component */ int total_colors = select_ncolors(m_Ncolors); /* Report selected color counts */ if (m_cinfo.m_out_color_components == 3) { m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_3_NCOLORS, total_colors, m_Ncolors[0], m_Ncolors[1], m_Ncolors[2]); } else { m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_NCOLORS, total_colors); } /* Allocate and fill in the colormap. */ /* The colors are ordered in the map in standard row-major order, */ /* i.e. rightmost (highest-indexed) color changes most rapidly. */ byte[][] colormap = jpeg_common_struct.AllocJpegSamples(total_colors, m_cinfo.m_out_color_components); /* blksize is number of adjacent repeated entries for a component */ /* blkdist is distance between groups of identical entries for a component */ int blkdist = total_colors; for (int i = 0; i < m_cinfo.m_out_color_components; i++) { /* fill in colormap entries for i'th color component */ int nci = m_Ncolors[i]; /* # of distinct values for this color */ int blksize = blkdist / nci; for (int j = 0; j < nci; j++) { /* Compute j'th output value (out of nci) for component */ int val = output_value(j, nci - 1); /* Fill in all colormap entries that have this value of this component */ for (int ptr = j * blksize; ptr < total_colors; ptr += blkdist) { /* fill in blksize entries beginning at ptr */ for (int k = 0; k < blksize; k++) { colormap[i][ptr + k] = (byte)val; } } } /* blksize of this color is blkdist of next */ blkdist = blksize; } /* Save the colormap in private storage, * where it will survive color quantization mode changes. */ m_sv_colormap = colormap; m_sv_actual = total_colors; }
/// <summary> /// Examine first few bytes from an APP14. /// Take appropriate action if it is an Adobe marker. /// datalen is # of bytes at data[], remaining is length of rest of marker data. /// </summary> private static void examine_app14(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining) { if (datalen >= APP14_DATA_LEN && data[0] == 0x41 && data[1] == 0x64 && data[2] == 0x6F && data[3] == 0x62 && data[4] == 0x65) { /* Found Adobe APP14 marker */ int version = (data[5] << 8) + data[6]; int flags0 = (data[7] << 8) + data[8]; int flags1 = (data[9] << 8) + data[10]; int transform = data[11]; cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_ADOBE, version, flags0, flags1, transform); cinfo.m_saw_Adobe_marker = true; cinfo.m_Adobe_transform = (byte)transform; } else { /* Start of APP14 does not match "Adobe", or too short */ cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP14, datalen + remaining); } }
/// <summary> /// Skip over an unknown or uninteresting variable-length marker /// </summary> private static bool skip_variable(jpeg_decompress_struct cinfo) { int length; if (!cinfo.m_src.GetTwoBytes(out length)) { return(false); } length -= 2; cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, length); if (length > 0) { cinfo.m_src.skip_input_data(length); } 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; } } }
/// <summary> /// Examine first few bytes from an APP14. /// Take appropriate action if it is an Adobe marker. /// datalen is # of bytes at data[], remaining is length of rest of marker data. /// </summary> private static void examine_app14(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining) { if (datalen >= APP14_DATA_LEN && data[0] == 0x41 && data[1] == 0x64 && data[2] == 0x6F && data[3] == 0x62 && data[4] == 0x65) { /* Found Adobe APP14 marker */ int version = (data[5] << 8) + data[6]; int flags0 = (data[7] << 8) + data[8]; int flags1 = (data[9] << 8) + data[10]; int transform = data[11]; cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_ADOBE, version, flags0, flags1, transform); cinfo.m_saw_Adobe_marker = true; cinfo.m_Adobe_transform = (byte) transform; } else { /* Start of APP14 does not match "Adobe", or too short */ cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP14, datalen + remaining); } }
/* * 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> /// Skip over an unknown or uninteresting variable-length marker /// </summary> private static bool skip_variable(jpeg_decompress_struct cinfo) { int length; if (!cinfo.m_src.GetTwoBytes(out length)) return false; length -= 2; cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, length); if (length > 0) cinfo.m_src.skip_input_data(length); return true; }
/// <summary> /// Save an APPn or COM marker into the marker list /// </summary> private static bool save_marker(jpeg_decompress_struct cinfo) { jpeg_marker_struct cur_marker = cinfo.m_marker.m_cur_marker; byte[] data = null; int length = 0; int bytes_read; int data_length; int dataOffset = 0; if (cur_marker == null) { /* begin reading a marker */ if (!cinfo.m_src.GetTwoBytes(out length)) return false; length -= 2; if (length >= 0) { /* watch out for bogus length word */ /* figure out how much we want to save */ int limit; if (cinfo.m_unread_marker == (int)JPEG_MARKER.COM) limit = cinfo.m_marker.m_length_limit_COM; else limit = cinfo.m_marker.m_length_limit_APPn[cinfo.m_unread_marker - (int)JPEG_MARKER.APP0]; if (length < limit) limit = length; /* allocate and initialize the marker item */ cur_marker = new jpeg_marker_struct((byte)cinfo.m_unread_marker, length, limit); /* data area is just beyond the jpeg_marker_struct */ data = cur_marker.Data; cinfo.m_marker.m_cur_marker = cur_marker; cinfo.m_marker.m_bytes_read = 0; bytes_read = 0; data_length = limit; } else { /* deal with bogus length word */ bytes_read = data_length = 0; data = null; } } else { /* resume reading a marker */ bytes_read = cinfo.m_marker.m_bytes_read; data_length = cur_marker.Data.Length; data = cur_marker.Data; dataOffset = bytes_read; } byte[] tempData = null; if (data_length != 0) tempData = new byte[data.Length]; while (bytes_read < data_length) { /* move the restart point to here */ cinfo.m_marker.m_bytes_read = bytes_read; /* If there's not at least one byte in buffer, suspend */ if (!cinfo.m_src.MakeByteAvailable()) return false; /* Copy bytes with reasonable rapidity */ int read = cinfo.m_src.GetBytes(tempData, data_length - bytes_read); Buffer.BlockCopy(tempData, 0, data, dataOffset, data_length - bytes_read); bytes_read += read; } /* Done reading what we want to read */ if (cur_marker != null) { /* will be null if bogus length word */ /* Add new marker to end of list */ cinfo.m_marker_list.Add(cur_marker); /* Reset pointer & calc remaining data length */ data = cur_marker.Data; dataOffset = 0; length = cur_marker.OriginalLength - data_length; } /* Reset to initial state for next marker */ cinfo.m_marker.m_cur_marker = null; JPEG_MARKER currentMarker = (JPEG_MARKER)cinfo.m_unread_marker; if (data_length != 0 && (currentMarker == JPEG_MARKER.APP0 || currentMarker == JPEG_MARKER.APP14)) { tempData = new byte[data.Length]; Buffer.BlockCopy(data, dataOffset, tempData, 0, data.Length - dataOffset); } /* Process the marker if interesting; else just make a generic trace msg */ switch ((JPEG_MARKER)cinfo.m_unread_marker) { case JPEG_MARKER.APP0: examine_app0(cinfo, tempData, data_length, length); break; case JPEG_MARKER.APP14: examine_app14(cinfo, tempData, data_length, length); break; default: cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, data_length + length); break; } /* skip any remaining data -- could be lots */ if (length > 0) cinfo.m_src.skip_input_data(length); 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); } } }
/// <summary> /// Read markers until SOS or EOI. /// /// Returns same codes as are defined for jpeg_consume_input: /// JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. /// </summary> public ReadResult read_markers() { /* Outer loop repeats once for each marker. */ for (;;) { /* Collect the marker proper, unless we already did. */ /* NB: first_marker() enforces the requirement that SOI appear first. */ if (m_cinfo.m_unread_marker == 0) { if (!m_cinfo.m_marker.m_saw_SOI) { if (!first_marker()) { return(ReadResult.JPEG_SUSPENDED); } } else { if (!next_marker()) { return(ReadResult.JPEG_SUSPENDED); } } } /* At this point m_cinfo.unread_marker contains the marker code and the * input point is just past the marker proper, but before any parameters. * A suspension will cause us to return with this state still true. */ switch ((JPEG_MARKER)m_cinfo.m_unread_marker) { case JPEG_MARKER.SOI: if (!get_soi()) { return(ReadResult.JPEG_SUSPENDED); } break; case JPEG_MARKER.SOF0: /* Baseline */ case JPEG_MARKER.SOF1: /* Extended sequential, Huffman */ if (!get_sof(false)) { return(ReadResult.JPEG_SUSPENDED); } break; case JPEG_MARKER.SOF2: /* Progressive, Huffman */ if (!get_sof(true)) { return(ReadResult.JPEG_SUSPENDED); } break; /* Currently unsupported SOFn types */ case JPEG_MARKER.SOF3: /* Lossless, Huffman */ case JPEG_MARKER.SOF5: /* Differential sequential, Huffman */ case JPEG_MARKER.SOF6: /* Differential progressive, Huffman */ case JPEG_MARKER.SOF7: /* Differential lossless, Huffman */ case JPEG_MARKER.SOF9: /* Extended sequential, arithmetic */ case JPEG_MARKER.SOF10: /* Progressive, arithmetic */ case JPEG_MARKER.JPG: /* Reserved for JPEG extensions */ case JPEG_MARKER.SOF11: /* Lossless, arithmetic */ case JPEG_MARKER.SOF13: /* Differential sequential, arithmetic */ case JPEG_MARKER.SOF14: /* Differential progressive, arithmetic */ case JPEG_MARKER.SOF15: /* Differential lossless, arithmetic */ m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOF_UNSUPPORTED, m_cinfo.m_unread_marker); break; case JPEG_MARKER.SOS: if (!get_sos()) { return(ReadResult.JPEG_SUSPENDED); } m_cinfo.m_unread_marker = 0; /* processed the marker */ return(ReadResult.JPEG_REACHED_SOS); case JPEG_MARKER.EOI: m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_EOI); m_cinfo.m_unread_marker = 0; /* processed the marker */ return(ReadResult.JPEG_REACHED_EOI); case JPEG_MARKER.DAC: if (!skip_variable(m_cinfo)) { return(ReadResult.JPEG_SUSPENDED); } break; case JPEG_MARKER.DHT: if (!get_dht()) { return(ReadResult.JPEG_SUSPENDED); } break; case JPEG_MARKER.DQT: if (!get_dqt()) { return(ReadResult.JPEG_SUSPENDED); } break; case JPEG_MARKER.DRI: if (!get_dri()) { return(ReadResult.JPEG_SUSPENDED); } break; case JPEG_MARKER.APP0: case JPEG_MARKER.APP1: case JPEG_MARKER.APP2: case JPEG_MARKER.APP3: case JPEG_MARKER.APP4: case JPEG_MARKER.APP5: case JPEG_MARKER.APP6: case JPEG_MARKER.APP7: case JPEG_MARKER.APP8: case JPEG_MARKER.APP9: case JPEG_MARKER.APP10: case JPEG_MARKER.APP11: case JPEG_MARKER.APP12: case JPEG_MARKER.APP13: case JPEG_MARKER.APP14: case JPEG_MARKER.APP15: if (!m_cinfo.m_marker.m_process_APPn[m_cinfo.m_unread_marker - (int)JPEG_MARKER.APP0](m_cinfo)) { return(ReadResult.JPEG_SUSPENDED); } break; case JPEG_MARKER.COM: if (!m_cinfo.m_marker.m_process_COM(m_cinfo)) { return(ReadResult.JPEG_SUSPENDED); } break; /* these are all parameterless */ case JPEG_MARKER.RST0: case JPEG_MARKER.RST1: case JPEG_MARKER.RST2: case JPEG_MARKER.RST3: case JPEG_MARKER.RST4: case JPEG_MARKER.RST5: case JPEG_MARKER.RST6: case JPEG_MARKER.RST7: case JPEG_MARKER.TEM: m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_PARMLESS_MARKER, m_cinfo.m_unread_marker); break; case JPEG_MARKER.DNL: /* Ignore DNL ... perhaps the wrong thing */ if (!skip_variable(m_cinfo)) { return(ReadResult.JPEG_SUSPENDED); } break; default: /* must be DHP, EXP, JPGn, or RESn */ /* For now, we treat the reserved markers as fatal errors since they are * likely to be used to signal incompatible JPEG Part 3 extensions. * Once the JPEG 3 version-number marker is well defined, this code * ought to change! */ m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, m_cinfo.m_unread_marker); break; } /* Successfully processed marker, so reset state variable */ m_cinfo.m_unread_marker = 0; } /* end loop */ }
/* * 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> /// Save an APPn or COM marker into the marker list /// </summary> private static bool save_marker(jpeg_decompress_struct cinfo) { jpeg_marker_struct cur_marker = cinfo.m_marker.m_cur_marker; byte[] data = null; int length = 0; int bytes_read; int data_length; int dataOffset = 0; if (cur_marker == null) { /* begin reading a marker */ if (!cinfo.m_src.GetTwoBytes(out length)) { return(false); } length -= 2; if (length >= 0) { /* watch out for bogus length word */ /* figure out how much we want to save */ int limit; if (cinfo.m_unread_marker == (int)JPEG_MARKER.COM) { limit = cinfo.m_marker.m_length_limit_COM; } else { limit = cinfo.m_marker.m_length_limit_APPn[cinfo.m_unread_marker - (int)JPEG_MARKER.APP0]; } if (length < limit) { limit = length; } /* allocate and initialize the marker item */ cur_marker = new jpeg_marker_struct((byte)cinfo.m_unread_marker, length, limit); /* data area is just beyond the jpeg_marker_struct */ data = cur_marker.Data; cinfo.m_marker.m_cur_marker = cur_marker; cinfo.m_marker.m_bytes_read = 0; bytes_read = 0; data_length = limit; } else { /* deal with bogus length word */ bytes_read = data_length = 0; data = null; } } else { /* resume reading a marker */ bytes_read = cinfo.m_marker.m_bytes_read; data_length = cur_marker.Data.Length; data = cur_marker.Data; dataOffset = bytes_read; } byte[] tempData = null; if (data_length != 0) { tempData = new byte[data.Length]; } while (bytes_read < data_length) { /* move the restart point to here */ cinfo.m_marker.m_bytes_read = bytes_read; /* If there's not at least one byte in buffer, suspend */ if (!cinfo.m_src.MakeByteAvailable()) { return(false); } /* Copy bytes with reasonable rapidity */ int read = cinfo.m_src.GetBytes(tempData, data_length - bytes_read); Buffer.BlockCopy(tempData, 0, data, dataOffset, read); bytes_read += read; dataOffset += read; } /* Done reading what we want to read */ if (cur_marker != null) { /* will be null if bogus length word */ /* Add new marker to end of list */ cinfo.m_marker_list.Add(cur_marker); /* Reset pointer & calc remaining data length */ data = cur_marker.Data; dataOffset = 0; length = cur_marker.OriginalLength - data_length; } /* Reset to initial state for next marker */ cinfo.m_marker.m_cur_marker = null; JPEG_MARKER currentMarker = (JPEG_MARKER)cinfo.m_unread_marker; if (data_length != 0 && (currentMarker == JPEG_MARKER.APP0 || currentMarker == JPEG_MARKER.APP14)) { tempData = new byte[data.Length]; Buffer.BlockCopy(data, dataOffset, tempData, 0, data.Length - dataOffset); } /* Process the marker if interesting; else just make a generic trace msg */ switch ((JPEG_MARKER)cinfo.m_unread_marker) { case JPEG_MARKER.APP0: examine_app0(cinfo, tempData, data_length, length); break; case JPEG_MARKER.APP14: examine_app14(cinfo, tempData, data_length, length); break; default: cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, data_length + length); break; } /* skip any remaining data -- could be lots */ if (length > 0) { cinfo.m_src.skip_input_data(length); } return(true); }
/// <summary> /// Write a Windows-style BMP file header, including colormap if needed /// </summary> private void write_bmp_header() { int bits_per_pixel; int cmap_entries; /* Compute colormap size and total file size */ if (cinfo.Out_color_space == J_COLOR_SPACE.JCS_RGB) { if (cinfo.Quantize_colors) { /* Colormapped RGB */ bits_per_pixel = 8; cmap_entries = 256; } else { /* Unquantized, full color RGB */ bits_per_pixel = 24; cmap_entries = 0; } } else { /* Grayscale output. We need to fake a 256-entry colormap. */ bits_per_pixel = 8; cmap_entries = 256; } /* File size */ int headersize = 14 + 40 + cmap_entries * 4; /* Header and colormap */ int bfSize = headersize + row_width * cinfo.Output_height; /* Set unused fields of header to 0 */ byte[] bmpfileheader = new byte[14]; byte[] bmpinfoheader = new byte[40]; /* Fill the file header */ bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ bmpfileheader[1] = 0x4D; PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */ /* we leave bfReserved1 & bfReserved2 = 0 */ PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */ /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */ PUT_2B(bmpinfoheader, 0, 40); /* biSize */ PUT_4B(bmpinfoheader, 4, cinfo.Output_width); /* biWidth */ PUT_4B(bmpinfoheader, 8, cinfo.Output_height); /* biHeight */ PUT_2B(bmpinfoheader, 12, 1); /* biPlanes - must be 1 */ PUT_2B(bmpinfoheader, 14, bits_per_pixel); /* biBitCount */ /* we leave biCompression = 0, for none */ /* we leave biSizeImage = 0; this is correct for uncompressed data */ if (cinfo.Density_unit == DensityUnit.DotsCm) { /* if have density in dots/cm, then */ PUT_4B(bmpinfoheader, 24, (int)(cinfo.X_density * 100)); /* XPels/M */ PUT_4B(bmpinfoheader, 28, (int)(cinfo.Y_density * 100)); /* XPels/M */ } PUT_2B(bmpinfoheader, 32, cmap_entries); /* biClrUsed */ /* we leave biClrImportant = 0 */ try { output_file.Write(bmpfileheader, 0, 14); } catch (Exception e) { cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); } try { output_file.Write(bmpinfoheader, 0, 40); } catch (Exception e) { cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); } if (cmap_entries > 0) { write_colormap(cmap_entries, 4); } }