this.sampleFormat = (int)sampleFormatValue;
char[] sampleFormatArray = new char[bitsPerSample.length];
Arrays.fill(sampleFormatArray, sampleFormatValue);
// Update the metadata.
TIFFTag sampleFormatTag =
base.getTag(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
TIFFField sampleFormatField =
new TIFFField(sampleFormatTag, TIFFTag.TIFF_SHORT,
sampleFormatArray.length, sampleFormatArray);
rootIFD.addTIFFField(sampleFormatField);
} else if(f != null) {
// Get whatever was provided.
sampleFormat = f.getAsInt(0);
} else {
// Set default value for internal use only.
sampleFormat = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED;
}
if (extraSamples != null) {
TIFFField extraSamplesField =
new TIFFField(
base.getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES),
TIFFTag.TIFF_SHORT,
extraSamples.length,
extraSamples);
rootIFD.addTIFFField(extraSamplesField);
} else {
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
}
TIFFField samplesPerPixelField =
new TIFFField(
base.getTag(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL),
bitsPerSample.length);
rootIFD.addTIFFField(samplesPerPixelField);
// Emit ColorMap if image is of palette color type
if (photometricInterpretation ==
BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR &&
cm instanceof IndexColorModel) {
char[] colorMap = new char[3*(1 << bitsPerSample[0])];
IndexColorModel icm = (IndexColorModel)cm;
// mapSize is determined by BitsPerSample, not by incoming ICM.
int mapSize = 1 << bitsPerSample[0];
int indexBound = Math.min(mapSize, icm.getMapSize());
for (int i = 0; i < indexBound; i++) {
colorMap[i] = (char)((icm.getRed(i)*65535)/255);
colorMap[mapSize + i] = (char)((icm.getGreen(i)*65535)/255);
colorMap[2*mapSize + i] = (char)((icm.getBlue(i)*65535)/255);
}
TIFFField colorMapField =
new TIFFField(
base.getTag(BaselineTIFFTagSet.TAG_COLOR_MAP),
TIFFTag.TIFF_SHORT,
colorMap.length,
colorMap);
rootIFD.addTIFFField(colorMapField);
} else {
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP);
}
// Emit ICCProfile if there is no ICCProfile field already in the
// metadata and the ColorSpace is non-standard ICC.
if(cm != null &&
rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_ICC_PROFILE) == null &&
ImageUtil.isNonStandardICCColorSpace(cm.getColorSpace())) {
ICC_ColorSpace iccColorSpace = (ICC_ColorSpace)cm.getColorSpace();
byte[] iccProfileData = iccColorSpace.getProfile().getData();
TIFFField iccProfileField =
new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_ICC_PROFILE),
TIFFTag.TIFF_UNDEFINED,
iccProfileData.length,
iccProfileData);
rootIFD.addTIFFField(iccProfileField);
}
// Always emit XResolution and YResolution.
TIFFField XResolutionField =
rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_X_RESOLUTION);
TIFFField YResolutionField =
rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
if(XResolutionField == null && YResolutionField == null) {
long[][] resRational = new long[1][2];
resRational[0] = new long[2];
TIFFField ResolutionUnitField =
rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT);
// Don't force dimensionless if one of the other dimensional
// quantities is present.
if(ResolutionUnitField == null &&
rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_X_POSITION) == null &&
rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_Y_POSITION) == null) {
// Set resolution to unit and units to dimensionless.
resRational[0][0] = 1;
resRational[0][1] = 1;
ResolutionUnitField =
new TIFFField(rootIFD.getTag
(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
BaselineTIFFTagSet.RESOLUTION_UNIT_NONE);
rootIFD.addTIFFField(ResolutionUnitField);
} else {
// Set resolution to a value which would make the maximum
// image dimension equal to 4 inches as arbitrarily stated
// in the description of ResolutionUnit in the TIFF 6.0
// specification. If the ResolutionUnit field specifies
// "none" then set the resolution to unity (1/1).
int resolutionUnit = ResolutionUnitField != null ?
ResolutionUnitField.getAsInt(0) :
BaselineTIFFTagSet.RESOLUTION_UNIT_INCH;
int maxDimension = Math.max(destWidth, destHeight);
switch(resolutionUnit) {
case BaselineTIFFTagSet.RESOLUTION_UNIT_INCH:
resRational[0][0] = maxDimension;
resRational[0][1] = 4;
break;
case BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER:
resRational[0][0] = 100L*maxDimension; // divide out 100
resRational[0][1] = 4*254; // 2.54 cm/inch * 100
break;
default:
resRational[0][0] = 1;
resRational[0][1] = 1;
}
}
XResolutionField =
new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION),
TIFFTag.TIFF_RATIONAL,
1,
resRational);
rootIFD.addTIFFField(XResolutionField);
YResolutionField =
new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION),
TIFFTag.TIFF_RATIONAL,
1,
resRational);
rootIFD.addTIFFField(YResolutionField);
} else if(XResolutionField == null && YResolutionField != null) {
// Set XResolution to YResolution.
long[] yResolution =
(long[])YResolutionField.getAsRational(0).clone();
XResolutionField =
new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION),
TIFFTag.TIFF_RATIONAL,
1,
yResolution);
rootIFD.addTIFFField(XResolutionField);
} else if(XResolutionField != null && YResolutionField == null) {
// Set YResolution to XResolution.
long[] xResolution =
(long[])XResolutionField.getAsRational(0).clone();
YResolutionField =
new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION),
TIFFTag.TIFF_RATIONAL,
1,
xResolution);
rootIFD.addTIFFField(YResolutionField);
}
// Set mandatory fields, overriding metadata passed in
int width = destWidth;
TIFFField imageWidthField =
new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_IMAGE_WIDTH),
width);
rootIFD.addTIFFField(imageWidthField);
int height = destHeight;
TIFFField imageLengthField =
new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_IMAGE_LENGTH),
height);
rootIFD.addTIFFField(imageLengthField);
// Determine rowsPerStrip
int rowsPerStrip;
TIFFField rowsPerStripField =
rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
if (rowsPerStripField != null) {
rowsPerStrip = rowsPerStripField.getAsInt(0);
if(rowsPerStrip < 0) {
rowsPerStrip = height;
}
} else {
int bitsPerPixel = bitDepth*(numBands + numExtraSamples);
int bytesPerRow = (bitsPerPixel*width + 7)/8;
rowsPerStrip =
Math.max(Math.max(DEFAULT_BYTES_PER_STRIP/bytesPerRow, 1), 8);
}
rowsPerStrip = Math.min(rowsPerStrip, height);
// Tiling flag.
boolean useTiling = false;
// Analyze tiling parameters
int tilingMode = param instanceof TIFFImageWriteParam ?
param.getTilingMode() : ImageWriteParam.MODE_DEFAULT;
if (tilingMode == ImageWriteParam.MODE_DISABLED ||
tilingMode == ImageWriteParam.MODE_DEFAULT) {
this.tileWidth = width;
this.tileLength = rowsPerStrip;
useTiling = false;
} else if (tilingMode == ImageWriteParam.MODE_EXPLICIT) {
tileWidth = param.getTileWidth();
tileLength = param.getTileHeight();
useTiling = true;
} else if (tilingMode == ImageWriteParam.MODE_COPY_FROM_METADATA) {
f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
if (f == null) {
tileWidth = width;
useTiling = false;
} else {
tileWidth = f.getAsInt(0);
useTiling = true;
}
f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_TILE_LENGTH);
if (f == null) {
tileLength = rowsPerStrip;
} else {
tileLength = f.getAsInt(0);
useTiling = true;
}
} else {
throw new IIOException("Illegal value of tilingMode!");
}
if(compression == BaselineTIFFTagSet.COMPRESSION_JPEG) {
// Reset tile size per TTN2 spec for JPEG compression.
int subX;
int subY;
if(numBands == 1) {
subX = subY = 1;
} else {
subX = subY = TIFFJPEGCompressor.CHROMA_SUBSAMPLING;
}
if(useTiling) {
int MCUMultipleX = 8*subX;
int MCUMultipleY = 8*subY;
tileWidth =
Math.max(MCUMultipleX*((tileWidth +
MCUMultipleX/2)/MCUMultipleX),
MCUMultipleX);
tileLength =
Math.max(MCUMultipleY*((tileLength +
MCUMultipleY/2)/MCUMultipleY),
MCUMultipleY);
} else if(rowsPerStrip < height) {
int MCUMultiple = 8*Math.max(subX, subY);
rowsPerStrip = tileLength =
Math.max(MCUMultiple*((tileLength +
MCUMultiple/2)/MCUMultiple),
MCUMultiple);
}
} else if(isJPEGInterchange) {
// Force tile size to equal image size.
tileWidth = width;
tileLength = height;
} else if(useTiling) {
// Round tile size to multiple of 16 per TIFF 6.0 specification
// (see pages 67-68 of version 6.0.1 from Adobe).
int tileWidthRemainder = tileWidth % 16;
if(tileWidthRemainder != 0) {
// Round to nearest multiple of 16 not less than 16.
tileWidth = Math.max(16*((tileWidth + 8)/16), 16);
// XXX insert processWarningOccurred(int,String);
}
int tileLengthRemainder = tileLength % 16;
if(tileLengthRemainder != 0) {
// Round to nearest multiple of 16 not less than 16.
tileLength = Math.max(16*((tileLength + 8)/16), 16);
// XXX insert processWarningOccurred(int,String);
}
}
this.tilesAcross = (width + tileWidth - 1)/tileWidth;
this.tilesDown = (height + tileLength - 1)/tileLength;
if (!useTiling) {
this.isTiled = false;
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_LENGTH);
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
rowsPerStripField =
new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP),
rowsPerStrip);
rootIFD.addTIFFField(rowsPerStripField);
TIFFField stripOffsetsField =
new TIFFField(
base.getTag(BaselineTIFFTagSet.TAG_STRIP_OFFSETS),
TIFFTag.TIFF_LONG,
tilesDown);
rootIFD.addTIFFField(stripOffsetsField);
TIFFField stripByteCountsField =
new TIFFField(
base.getTag(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS),
TIFFTag.TIFF_LONG,
tilesDown);
rootIFD.addTIFFField(stripByteCountsField);
} else {
this.isTiled = true;
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
TIFFField tileWidthField =
new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_TILE_WIDTH),
tileWidth);
rootIFD.addTIFFField(tileWidthField);
TIFFField tileLengthField =
new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_TILE_LENGTH),
tileLength);
rootIFD.addTIFFField(tileLengthField);
TIFFField tileOffsetsField =
new TIFFField(
base.getTag(BaselineTIFFTagSet.TAG_TILE_OFFSETS),
TIFFTag.TIFF_LONG,
tilesDown*tilesAcross);
rootIFD.addTIFFField(tileOffsetsField);
TIFFField tileByteCountsField =
new TIFFField(
base.getTag(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS),
TIFFTag.TIFF_LONG,
tilesDown*tilesAcross);
rootIFD.addTIFFField(tileByteCountsField);
}
if(isEXIF) {
//
// Ensure presence of mandatory fields and absence of prohibited
// fields and those that duplicate information in JPEG marker
// segments per tables 14-18 of the EXIF 2.2 specification.
//
// If an empty image is being written or inserted then infer
// that the primary IFD is being set up.
boolean isPrimaryIFD = isEncodingEmpty();
// Handle TIFF fields in order of increasing tag number.
if(compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
// ImageWidth
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
// ImageLength
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
// BitsPerSample
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
// Compression
if(isPrimaryIFD) {
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_COMPRESSION);
}
// PhotometricInterpretation
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
// StripOffsets
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
// SamplesPerPixel
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
// RowsPerStrip
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
// StripByteCounts
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
// XResolution and YResolution are handled above for all TIFFs.
// PlanarConfiguration
rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
// ResolutionUnit
if(rootIFD.getTIFFField
(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT) == null) {
f = new TIFFField(base.getTag
(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
BaselineTIFFTagSet.RESOLUTION_UNIT_INCH);
rootIFD.addTIFFField(f);
}
if(isPrimaryIFD) {
// JPEGInterchangeFormat
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
// JPEGInterchangeFormatLength
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
// YCbCrSubsampling
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
// YCbCrPositioning
if(rootIFD.getTIFFField
(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING) == null) {
f = new TIFFField
(base.getTag
(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING),
TIFFTag.TIFF_SHORT,
1,
new char[] {
(char)BaselineTIFFTagSet.Y_CB_CR_POSITIONING_CENTERED
});
rootIFD.addTIFFField(f);
}
} else { // Thumbnail IFD
// JPEGInterchangeFormat
f = new TIFFField
(base.getTag
(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT),
TIFFTag.TIFF_LONG,
1);
rootIFD.addTIFFField(f);
// JPEGInterchangeFormatLength
f = new TIFFField
(base.getTag
(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH),
TIFFTag.TIFF_LONG,
1);
rootIFD.addTIFFField(f);
// YCbCrSubsampling
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
}
} else { // Uncompressed
// ImageWidth through PlanarConfiguration are set above.
// XResolution and YResolution are handled above for all TIFFs.
// ResolutionUnit
if(rootIFD.getTIFFField
(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT) == null) {
f = new TIFFField(base.getTag
(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
BaselineTIFFTagSet.RESOLUTION_UNIT_INCH);
rootIFD.addTIFFField(f);
}
// JPEGInterchangeFormat
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
// JPEGInterchangeFormatLength
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
if(photometricInterpretation ==
BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB) {
// YCbCrCoefficients
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_Y_CB_CR_COEFFICIENTS);
// YCbCrSubsampling
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
// YCbCrPositioning
rootIFD.removeTIFFField
(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING);
}
}
// Get EXIF tags.
TIFFTagSet exifTags = EXIFTIFFTagSet.getInstance();
// Retrieve or create the EXIF IFD.
TIFFIFD exifIFD = null;
f = rootIFD.getTIFFField
(EXIFParentTIFFTagSet.TAG_EXIF_IFD_POINTER);
if(f != null) {
// Retrieve the EXIF IFD.
exifIFD = (TIFFIFD)f.getData();
} else if(isPrimaryIFD) {
// Create the EXIF IFD.
List exifTagSets = new ArrayList(1);
exifTagSets.add(exifTags);
exifIFD = new TIFFIFD(exifTagSets);
// Add it to the root IFD.
TIFFTagSet tagSet = EXIFParentTIFFTagSet.getInstance();
TIFFTag exifIFDTag =
tagSet.getTag(EXIFParentTIFFTagSet.TAG_EXIF_IFD_POINTER);
rootIFD.addTIFFField(new TIFFField(exifIFDTag,
TIFFTag.TIFF_LONG,
1,
exifIFD));