// Get the TIFF metadata object.
TIFFImageMetadata tim = (TIFFImageMetadata)metadata;
// Get the JPEGInterchangeFormat field.
TIFFField JPEGInterchangeFormatField =
tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
// Get the tile or strip offsets.
TIFFField segmentOffsetField =
tim.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
if(segmentOffsetField == null) {
segmentOffsetField =
tim.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
if(segmentOffsetField == null) {
segmentOffsetField = JPEGInterchangeFormatField;
}
}
long[] segmentOffsets = segmentOffsetField.getAsLongs();
// Determine whether the image has more than one strip or tile.
boolean isTiled = segmentOffsets.length > 1;
if(!isTiled) {
//
// If the image has only a single strip or tile and it looks
// as if a complete JPEG stream is present then set the value
// of JPEGStreamOffset to the offset of the JPEG stream;
// otherwise leave JPEGStreamOffset set to null.
//
stream.seek(offset);
stream.mark();
if(stream.read() == 0xff && stream.read() == SOI) {
// Tile or strip offset points to SOI.
JPEGStreamOffset = new Long(offset);
// Set initialization flag and return.
if(DEBUG) System.out.println("OLD JPEG CASE 1");
((TIFFImageReader)reader).forwardWarningMessage("SOI marker detected at start of strip or tile.");
isInitialized = true;
stream.reset();
return;
}
stream.reset();
if(JPEGInterchangeFormatField != null) {
// Get the value of JPEGInterchangeFormat.
long jpegInterchangeOffset =
JPEGInterchangeFormatField.getAsLong(0);
// Check that the value of JPEGInterchangeFormat points to SOI.
stream.mark();
stream.seek(jpegInterchangeOffset);
if(stream.read() == 0xff && stream.read() == SOI)
// JPEGInterchangeFormat offset points to SOI.
JPEGStreamOffset = new Long(jpegInterchangeOffset);
else
((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormat does not point to SOI");
stream.reset();
// Get the JPEGInterchangeFormatLength field.
TIFFField JPEGInterchangeFormatLengthField =
tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
if(JPEGInterchangeFormatLengthField == null) {
if(DEBUG) System.out.println("OLD JPEG CASE 2");
((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormatLength field is missing");
} else {
// Get the JPEGInterchangeFormatLength field's value.
long jpegInterchangeLength =
JPEGInterchangeFormatLengthField.getAsLong(0);
if(jpegInterchangeOffset < segmentOffsets[0] &&
(jpegInterchangeOffset + jpegInterchangeLength) >
segmentOffsets[0]) {
if(DEBUG) System.out.println("OLD JPEG CASE 3");
} else {
if(DEBUG) System.out.println("OLD JPEG CASE 3A");
((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormatLength field value is invalid");
}
}
// Return if JPEGInterchangeFormat pointed to SOI.
if(JPEGStreamOffset != null) {
isInitialized = true;
return;
}
}
}
// Get the subsampling factors.
TIFFField YCbCrSubsamplingField =
tim.getTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
if(YCbCrSubsamplingField != null) {
subsamplingX = YCbCrSubsamplingField.getAsChars()[0];
subsamplingY = YCbCrSubsamplingField.getAsChars()[1];
}
//
// Initialize the 'tables' instance variable either for later
// use in prepending to individual abbreviated strips or tiles.
//
if(JPEGInterchangeFormatField != null) {
// Get the value of JPEGInterchangeFormat.
long jpegInterchangeOffset =
JPEGInterchangeFormatField.getAsLong(0);
// Get the JPEGInterchangeFormatLength field.
TIFFField JPEGInterchangeFormatLengthField =
tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
if(JPEGInterchangeFormatLengthField != null) {
// Get the JPEGInterchangeFormatLength field's value.
long jpegInterchangeLength =
JPEGInterchangeFormatLengthField.getAsLong(0);
if(jpegInterchangeLength >= 2 &&
jpegInterchangeOffset + jpegInterchangeLength <=
segmentOffsets[0]) {
// Determine the length excluding any terminal EOI marker
// and allocate table memory.
stream.mark();
stream.seek(jpegInterchangeOffset+jpegInterchangeLength-2);
if(stream.read() == 0xff && stream.read() == EOI) {
this.tables = new byte[(int)(jpegInterchangeLength-2)];
} else {
this.tables = new byte[(int)jpegInterchangeLength];
}
stream.reset();
// Read the tables.
stream.mark();
stream.seek(jpegInterchangeOffset);
stream.readFully(tables);
stream.reset();
if(DEBUG) System.out.println("OLD JPEG CASE 4");
((TIFFImageReader)reader).forwardWarningMessage("Incorrect JPEG interchange format: using JPEGInterchangeFormat offset to derive tables.");
} else {
((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormat+JPEGInterchangeFormatLength > offset to first strip or tile.");
}
}
}
if(this.tables == null) {
//
// Create tables-only stream in tables[] consisting of
// SOI+DQTs+DHTs
//
ByteArrayOutputStream baos =
new ByteArrayOutputStream();//XXX length
// Save stream length;
long streamLength = stream.length();
// SOI
baos.write(0xff);
baos.write(SOI);
// Quantization Tables
TIFFField f =
tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES);
if(f == null) {
throw new IIOException("JPEGQTables field missing!");
}
long[] off = f.getAsLongs();
for(int i = 0; i < off.length; i++) {
baos.write(0xff); // Marker ID
baos.write(DQT);
char markerLength = (char)67;
baos.write((markerLength >>> 8) & 0xff); // Length
baos.write(markerLength & 0xff);
baos.write(i); // Table ID and precision
byte[] qtable = new byte[64];
if(streamLength != -1 && off[i] > streamLength) {
throw new IIOException("JPEGQTables offset for index "+
i+" is not in the stream!");
}
stream.seek(off[i]);
stream.readFully(qtable);
baos.write(qtable); // Table data
}
// Huffman Tables (k == 0 ? DC : AC).
for(int k = 0; k < 2; k++) {
int tableTagNumber = k == 0 ?
BaselineTIFFTagSet.TAG_JPEG_DC_TABLES :
BaselineTIFFTagSet.TAG_JPEG_AC_TABLES;
f = tim.getTIFFField(tableTagNumber);
String fieldName =
tableTagNumber ==
BaselineTIFFTagSet.TAG_JPEG_DC_TABLES ?
"JPEGDCTables" : "JPEGACTables";
if(f == null) {
throw new IIOException(fieldName+" field missing!");
}
off = f.getAsLongs();
for(int i = 0; i < off.length; i++) {
baos.write(0xff); // Marker ID
baos.write(DHT);
byte[] blengths = new byte[16];
if(streamLength != -1 && off[i] > streamLength) {
throw new IIOException(fieldName+" offset for index "+
i+" is not in the stream!");
}
stream.seek(off[i]);
stream.readFully(blengths);
int numCodes = 0;
for(int j = 0; j < 16; j++) {
numCodes += blengths[j]&0xff;
}
char markerLength = (char)(19 + numCodes);
baos.write((markerLength >>> 8) & 0xff); // Length
baos.write(markerLength & 0xff);
baos.write(i | (k << 4)); // Table ID and type
baos.write(blengths); // Number of codes
byte[] bcodes = new byte[numCodes];
stream.readFully(bcodes);
baos.write(bcodes); // Codes
}
}
// SOF0
baos.write((byte)0xff); // Marker identifier
baos.write((byte)SOF0);
short sval = (short)(8 + 3*samplesPerPixel); // Length
baos.write((byte)((sval >>> 8) & 0xff));
baos.write((byte)(sval & 0xff));
baos.write((byte)8); // Data precision
sval = (short)srcHeight; // Tile/strip height
baos.write((byte)((sval >>> 8) & 0xff));
baos.write((byte)(sval & 0xff));
sval = (short)srcWidth; // Tile/strip width
baos.write((byte)((sval >>> 8) & 0xff));
baos.write((byte)(sval & 0xff));
baos.write((byte)samplesPerPixel); // Number of components
if(samplesPerPixel == 1) {
baos.write((byte)1); // Component ID
baos.write((byte)0x11); // Subsampling factor
baos.write((byte)0); // Quantization table ID
} else { // 3
for(int i = 0; i < 3; i++) {
baos.write((byte)(i + 1)); // Component ID
baos.write((i != 0) ?
(byte)0x11 :
(byte)(((subsamplingX & 0x0f) << 4) |
(subsamplingY & 0x0f)));
baos.write((byte)i); // Quantization table ID
}
};
// DRI (optional).
f = tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_RESTART_INTERVAL);
if(f != null) {
char restartInterval = f.getAsChars()[0];
if(restartInterval != 0) {
baos.write((byte)0xff); // Marker identifier
baos.write((byte)DRI);