int gridY = 0;
JPEGQTable [] qTables = null;
JPEGHuffmanTable[] DCHuffmanTables = null;
JPEGHuffmanTable[] ACHuffmanTables = null;
boolean optimizeHuffman = false;
JPEGImageWriteParam jparam = null;
int progressiveMode = ImageWriteParam.MODE_DISABLED;
if (param != null) {
Rectangle sourceRegion = param.getSourceRegion();
if (sourceRegion != null) {
Rectangle imageBounds = new Rectangle(sourceXOffset,
sourceYOffset,
sourceWidth,
sourceHeight);
sourceRegion = sourceRegion.intersection(imageBounds);
sourceXOffset = sourceRegion.x;
sourceYOffset = sourceRegion.y;
sourceWidth = sourceRegion.width;
sourceHeight = sourceRegion.height;
}
if (sourceWidth + sourceXOffset > imageWidth) {
sourceWidth = imageWidth - sourceXOffset;
}
if (sourceHeight + sourceYOffset > imageHeight) {
sourceHeight = imageHeight - sourceYOffset;
}
periodX = param.getSourceXSubsampling();
periodY = param.getSourceYSubsampling();
gridX = param.getSubsamplingXOffset();
gridY = param.getSubsamplingYOffset();
switch(param.getCompressionMode()) {
case ImageWriteParam.MODE_DISABLED:
throw new IIOException("JPEG compression cannot be disabled");
case ImageWriteParam.MODE_EXPLICIT:
float quality = param.getCompressionQuality();
quality = JPEG.convertToLinearQuality(quality);
qTables = new JPEGQTable[2];
qTables[0] = JPEGQTable.K1Luminance.getScaledInstance
(quality, true);
qTables[1] = JPEGQTable.K2Chrominance.getScaledInstance
(quality, true);
break;
case ImageWriteParam.MODE_DEFAULT:
qTables = new JPEGQTable[2];
qTables[0] = JPEGQTable.K1Div2Luminance;
qTables[1] = JPEGQTable.K2Div2Chrominance;
break;
// We'll handle the metadata case later
}
progressiveMode = param.getProgressiveMode();
if (param instanceof JPEGImageWriteParam) {
jparam = (JPEGImageWriteParam)param;
optimizeHuffman = jparam.getOptimizeHuffmanTables();
}
}
// Now examine the metadata
IIOMetadata mdata = image.getMetadata();
if (mdata != null) {
if (mdata instanceof JPEGMetadata) {
metadata = (JPEGMetadata) mdata;
if (debug) {
System.out.println
("We have metadata, and it's JPEG metadata");
}
} else {
if (!rasterOnly) {
ImageTypeSpecifier type = destType;
if (type == null) {
type = new ImageTypeSpecifier(rimage);
}
metadata = (JPEGMetadata) convertImageMetadata(mdata,
type,
param);
} else {
warningOccurred(WARNING_METADATA_NOT_JPEG_FOR_RASTER);
}
}
}
// First set a default state
ignoreJFIF = false; // If it's there, use it
ignoreAdobe = false; // If it's there, use it
newAdobeTransform = JPEG.ADOBE_IMPOSSIBLE; // Change if needed
writeDefaultJFIF = false;
writeAdobe = false;
// By default we'll do no conversion:
int inCsType = JPEG.JCS_UNKNOWN;
int outCsType = JPEG.JCS_UNKNOWN;
JFIFMarkerSegment jfif = null;
AdobeMarkerSegment adobe = null;
SOFMarkerSegment sof = null;
if (metadata != null) {
jfif = (JFIFMarkerSegment) metadata.findMarkerSegment
(JFIFMarkerSegment.class, true);
adobe = (AdobeMarkerSegment) metadata.findMarkerSegment
(AdobeMarkerSegment.class, true);
sof = (SOFMarkerSegment) metadata.findMarkerSegment
(SOFMarkerSegment.class, true);
}
iccProfile = null; // By default don't write one
convertTosRGB = false; // PhotoYCC does this
converted = null;
if (destType != null) {
if (numBandsUsed != destType.getNumBands()) {
throw new IIOException
("Number of source bands != number of destination bands");
}
cs = destType.getColorModel().getColorSpace();
// Check the metadata against the destination type
if (metadata != null) {
checkSOFBands(sof, numBandsUsed);
checkJFIF(jfif, destType, false);
// Do we want to write an ICC profile?
if ((jfif != null) && (ignoreJFIF == false)) {
if (JPEG.isNonStandardICC(cs)) {
iccProfile = ((ICC_ColorSpace) cs).getProfile();
}
}
checkAdobe(adobe, destType, false);
} else { // no metadata, but there is a dest type
// If we can add a JFIF or an Adobe marker segment, do so
if (JPEG.isJFIFcompliant(destType, false)) {
writeDefaultJFIF = true;
// Do we want to write an ICC profile?
if (JPEG.isNonStandardICC(cs)) {
iccProfile = ((ICC_ColorSpace) cs).getProfile();
}
} else {
int transform = JPEG.transformForType(destType, false);
if (transform != JPEG.ADOBE_IMPOSSIBLE) {
writeAdobe = true;
newAdobeTransform = transform;
}
}
// re-create the metadata
metadata = new JPEGMetadata(destType, null, this);
}
inCsType = getSrcCSType(destType);
outCsType = getDefaultDestCSType(destType);
} else { // no destination type
if (metadata == null) {
if (fullImage) { // no dest, no metadata, full image
// Use default metadata matching the image and param
metadata = new JPEGMetadata(new ImageTypeSpecifier(rimage),
param, this);
if (metadata.findMarkerSegment
(JFIFMarkerSegment.class, true) != null) {
cs = rimage.getColorModel().getColorSpace();
if (JPEG.isNonStandardICC(cs)) {
iccProfile = ((ICC_ColorSpace) cs).getProfile();
}
}
inCsType = getSrcCSType(rimage);
outCsType = getDefaultDestCSType(rimage);
}
// else no dest, no metadata, not an image,
// so no special headers, no color conversion
} else { // no dest type, but there is metadata
checkSOFBands(sof, numBandsUsed);
if (fullImage) { // no dest, metadata, image
// Check that the metadata and the image match
ImageTypeSpecifier inputType =
new ImageTypeSpecifier(rimage);
inCsType = getSrcCSType(rimage);
if (cm != null) {
boolean alpha = cm.hasAlpha();
switch (cs.getType()) {
case ColorSpace.TYPE_GRAY:
if (!alpha) {
outCsType = JPEG.JCS_GRAYSCALE;
} else {
if (jfif != null) {
ignoreJFIF = true;
warningOccurred
(WARNING_IMAGE_METADATA_JFIF_MISMATCH);
}
// out colorspace remains unknown
}
if ((adobe != null)
&& (adobe.transform != JPEG.ADOBE_UNKNOWN)) {
newAdobeTransform = JPEG.ADOBE_UNKNOWN;
warningOccurred
(WARNING_IMAGE_METADATA_ADOBE_MISMATCH);
}
break;
case ColorSpace.TYPE_RGB:
if (!alpha) {
if (jfif != null) {
outCsType = JPEG.JCS_YCbCr;
if (JPEG.isNonStandardICC(cs)
|| ((cs instanceof ICC_ColorSpace)
&& (jfif.iccSegment != null))) {
iccProfile =
((ICC_ColorSpace) cs).getProfile();
}
} else if (adobe != null) {
switch (adobe.transform) {
case JPEG.ADOBE_UNKNOWN:
outCsType = JPEG.JCS_RGB;
break;
case JPEG.ADOBE_YCC:
outCsType = JPEG.JCS_YCbCr;
break;
default:
warningOccurred
(WARNING_IMAGE_METADATA_ADOBE_MISMATCH);
newAdobeTransform = JPEG.ADOBE_UNKNOWN;
outCsType = JPEG.JCS_RGB;
break;
}
} else {
// consult the ids
int outCS = sof.getIDencodedCSType();
// if they don't resolve it,
// consult the sampling factors
if (outCS != JPEG.JCS_UNKNOWN) {
outCsType = outCS;
} else {
boolean subsampled =
isSubsampled(sof.componentSpecs);
if (subsampled) {
outCsType = JPEG.JCS_YCbCr;
} else {
outCsType = JPEG.JCS_RGB;
}
}
}
} else { // RGBA
if (jfif != null) {
ignoreJFIF = true;
warningOccurred
(WARNING_IMAGE_METADATA_JFIF_MISMATCH);
}
if (adobe != null) {
if (adobe.transform
!= JPEG.ADOBE_UNKNOWN) {
newAdobeTransform = JPEG.ADOBE_UNKNOWN;
warningOccurred
(WARNING_IMAGE_METADATA_ADOBE_MISMATCH);
}
outCsType = JPEG.JCS_RGBA;
} else {
// consult the ids
int outCS = sof.getIDencodedCSType();
// if they don't resolve it,
// consult the sampling factors
if (outCS != JPEG.JCS_UNKNOWN) {
outCsType = outCS;
} else {
boolean subsampled =
isSubsampled(sof.componentSpecs);
outCsType = subsampled ?
JPEG.JCS_YCbCrA : JPEG.JCS_RGBA;
}
}
}
break;
case ColorSpace.TYPE_3CLR:
if (cs == JPEG.JCS.getYCC()) {
if (!alpha) {
if (jfif != null) {
convertTosRGB = true;
convertOp =
new ColorConvertOp(cs,
JPEG.JCS.sRGB,
null);
outCsType = JPEG.JCS_YCbCr;
} else if (adobe != null) {
if (adobe.transform
!= JPEG.ADOBE_YCC) {
newAdobeTransform = JPEG.ADOBE_YCC;
warningOccurred
(WARNING_IMAGE_METADATA_ADOBE_MISMATCH);
}
outCsType = JPEG.JCS_YCC;
} else {
outCsType = JPEG.JCS_YCC;
}
} else { // PhotoYCCA
if (jfif != null) {
ignoreJFIF = true;
warningOccurred
(WARNING_IMAGE_METADATA_JFIF_MISMATCH);
} else if (adobe != null) {
if (adobe.transform
!= JPEG.ADOBE_UNKNOWN) {
newAdobeTransform
= JPEG.ADOBE_UNKNOWN;
warningOccurred
(WARNING_IMAGE_METADATA_ADOBE_MISMATCH);
}
}
outCsType = JPEG.JCS_YCCA;
}
}
}
}
} // else no dest, metadata, not an image. Defaults ok
}
}
boolean metadataProgressive = false;
int [] scans = null;
if (metadata != null) {
if (sof == null) {
sof = (SOFMarkerSegment) metadata.findMarkerSegment
(SOFMarkerSegment.class, true);
}
if ((sof != null) && (sof.tag == JPEG.SOF2)) {
metadataProgressive = true;
if (progressiveMode == ImageWriteParam.MODE_COPY_FROM_METADATA) {
scans = collectScans(metadata, sof); // Might still be null
} else {
numScans = 0;
}
}
if (jfif == null) {
jfif = (JFIFMarkerSegment) metadata.findMarkerSegment
(JFIFMarkerSegment.class, true);
}
}
thumbnails = image.getThumbnails();
int numThumbs = image.getNumThumbnails();
forceJFIF = false;
// determine if thumbnails can be written
// If we are going to add a default JFIF marker segment,
// then thumbnails can be written
if (!writeDefaultJFIF) {
// If there is no metadata, then we can't write thumbnails
if (metadata == null) {
thumbnails = null;
if (numThumbs != 0) {
warningOccurred(WARNING_IGNORING_THUMBS);
}
} else {
// There is metadata
// If we are writing a raster or subbands,
// then the user must specify JFIF on the metadata
if (fullImage == false) {
if (jfif == null) {
thumbnails = null; // Or we can't include thumbnails
if (numThumbs != 0) {
warningOccurred(WARNING_IGNORING_THUMBS);
}
}
} else { // It is a full image, and there is metadata
if (jfif == null) { // Not JFIF
// Can it have JFIF?
if ((outCsType == JPEG.JCS_GRAYSCALE)
|| (outCsType == JPEG.JCS_YCbCr)) {
if (numThumbs != 0) {
forceJFIF = true;
warningOccurred(WARNING_FORCING_JFIF);
}
} else { // Nope, not JFIF-compatible
thumbnails = null;
if (numThumbs != 0) {
warningOccurred(WARNING_IGNORING_THUMBS);
}
}
}
}
}
}
// Set up a boolean to indicate whether we need to call back to
// write metadata
boolean haveMetadata =
((metadata != null) || writeDefaultJFIF || writeAdobe);
// Now that we have dealt with metadata, finalize our tables set up
// Are we going to write tables? By default, yes.
boolean writeDQT = true;
boolean writeDHT = true;
// But if the metadata has no tables, no.
DQTMarkerSegment dqt = null;
DHTMarkerSegment dht = null;
int restartInterval = 0;
if (metadata != null) {
dqt = (DQTMarkerSegment) metadata.findMarkerSegment
(DQTMarkerSegment.class, true);
dht = (DHTMarkerSegment) metadata.findMarkerSegment
(DHTMarkerSegment.class, true);
DRIMarkerSegment dri =
(DRIMarkerSegment) metadata.findMarkerSegment
(DRIMarkerSegment.class, true);
if (dri != null) {
restartInterval = dri.restartInterval;
}
if (dqt == null) {
writeDQT = false;
}
if (dht == null) {
writeDHT = false; // Ignored if optimizeHuffman is true
}
}
// Whether we write tables or not, we need to figure out which ones
// to use
if (qTables == null) { // Get them from metadata, or use defaults
if (dqt != null) {
qTables = collectQTablesFromMetadata(metadata);
} else if (streamQTables != null) {
qTables = streamQTables;
} else if ((jparam != null) && (jparam.areTablesSet())) {
qTables = jparam.getQTables();
} else {
qTables = JPEG.getDefaultQTables();
}
}
// If we are optimizing, we don't want any tables.
if (optimizeHuffman == false) {
// If they were for progressive scans, we can't use them.
if ((dht != null) && (metadataProgressive == false)) {
DCHuffmanTables = collectHTablesFromMetadata(metadata, true);
ACHuffmanTables = collectHTablesFromMetadata(metadata, false);
} else if (streamDCHuffmanTables != null) {
DCHuffmanTables = streamDCHuffmanTables;
ACHuffmanTables = streamACHuffmanTables;
} else if ((jparam != null) && (jparam.areTablesSet())) {
DCHuffmanTables = jparam.getDCHuffmanTables();
ACHuffmanTables = jparam.getACHuffmanTables();
} else {
DCHuffmanTables = JPEG.getDefaultHuffmanTables(true);
ACHuffmanTables = JPEG.getDefaultHuffmanTables(false);
}
}