JFIFMarkerSegmentpublic class JFIFMarkerSegment extends MarkerSegment A JFIF (JPEG File Interchange Format) APP0 (Application-Specific)
marker segment. Inner classes are included for JFXX extension
marker segments, for different varieties of thumbnails, and for
ICC Profile APP2 marker segments. Any of these secondary types
that occur are kept as members of a single JFIFMarkerSegment object. |
Fields Summary |
---|
int | majorVersion | int | minorVersion | int | resUnits | int | Xdensity | int | Ydensity | int | thumbWidth | int | thumbHeight | JFIFThumbRGB | thumb | ArrayList | extSegments | ICCMarkerSegment | iccSegment | private static final int | THUMB_JPEG | private static final int | THUMB_PALETTE | private static final int | THUMB_UNASSIGNED | private static final int | THUMB_RGB | private static final int | DATA_SIZE | private static final int | ID_SIZE | private final int | MAX_THUMB_WIDTH | private final int | MAX_THUMB_HEIGHT | private final boolean | debug | private boolean | inICCSet to true when reading the chunks of an
ICC profile. All chunks are consolidated to create a single
"segment" containing all the chunks. This flag is a state
variable identifying whether to construct a new segment or
append to an old one. | private ICCMarkerSegment | tempICCSegmentA placeholder for an ICC profile marker segment under
construction. The segment is not added to the list
until all chunks have been read. |
Constructors Summary |
---|
JFIFMarkerSegment()Default constructor. Used to create a default JFIF header
super(JPEG.APP0);
majorVersion = 1;
minorVersion = 2;
resUnits = JPEG.DENSITY_UNIT_ASPECT_RATIO;
Xdensity = 1;
Ydensity = 1;
thumbWidth = 0;
thumbHeight = 0;
| JFIFMarkerSegment(JPEGBuffer buffer)Constructs a JFIF header by reading from a stream wrapped
in a JPEGBuffer.
super(buffer);
buffer.bufPtr += ID_SIZE; // skip the id, we already checked it
majorVersion = buffer.buf[buffer.bufPtr++];
minorVersion = buffer.buf[buffer.bufPtr++];
resUnits = buffer.buf[buffer.bufPtr++];
Xdensity = (buffer.buf[buffer.bufPtr++] & 0xff) << 8;
Xdensity |= buffer.buf[buffer.bufPtr++] & 0xff;
Ydensity = (buffer.buf[buffer.bufPtr++] & 0xff) << 8;
Ydensity |= buffer.buf[buffer.bufPtr++] & 0xff;
thumbWidth = buffer.buf[buffer.bufPtr++] & 0xff;
thumbHeight = buffer.buf[buffer.bufPtr++] & 0xff;
buffer.bufAvail -= DATA_SIZE;
if (thumbWidth > 0) {
thumb = new JFIFThumbRGB(buffer, thumbWidth, thumbHeight);
}
| JFIFMarkerSegment(Node node)Constructs a JFIF header from a DOM Node.
this();
updateFromNativeNode(node, true);
|
Methods Summary |
---|
void | addICC(com.sun.imageio.plugins.jpeg.JPEGBuffer buffer)Adds an ICC Profile APP2 segment from the stream wrapped
in the JPEGBuffer.
if (inICC == false) {
if (iccSegment != null) {
throw new IIOException
("> 1 ICC APP2 Marker Segment not supported");
}
tempICCSegment = new ICCMarkerSegment(buffer);
if (inICC == false) { // Just one chunk
iccSegment = tempICCSegment;
tempICCSegment = null;
}
} else {
if (tempICCSegment.addData(buffer) == true) {
iccSegment = tempICCSegment;
tempICCSegment = null;
}
}
| void | addICC(java.awt.color.ICC_ColorSpace cs)Add an ICC Profile APP2 segment by constructing it from
the given ICC_ColorSpace object.
if (iccSegment != null) {
throw new IIOException
("> 1 ICC APP2 Marker Segment not supported");
}
iccSegment = new ICCMarkerSegment(cs);
| void | addJFXX(com.sun.imageio.plugins.jpeg.JPEGBuffer buffer, com.sun.imageio.plugins.jpeg.JPEGImageReader reader)Add an JFXX extension marker segment from the stream wrapped
in the JPEGBuffer to the list of extension segments.
extSegments.add(new JFIFExtensionMarkerSegment(buffer, reader));
| protected java.lang.Object | clone()Returns a deep-copy clone of this object.
JFIFMarkerSegment newGuy = (JFIFMarkerSegment) super.clone();
if (!extSegments.isEmpty()) { // Clone the list with a deep copy
newGuy.extSegments = new ArrayList();
for (Iterator iter = extSegments.iterator(); iter.hasNext();) {
JFIFExtensionMarkerSegment jfxx =
(JFIFExtensionMarkerSegment) iter.next();
newGuy.extSegments.add(jfxx.clone());
}
}
if (iccSegment != null) {
newGuy.iccSegment = (ICCMarkerSegment) iccSegment.clone();
}
return newGuy;
| private static java.awt.image.BufferedImage | expandGrayThumb(java.awt.image.BufferedImage thumb)Return an RGB image that is the expansion of the given grayscale
image.
BufferedImage ret = new BufferedImage(thumb.getWidth(),
thumb.getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics g = ret.getGraphics();
g.drawImage(thumb, 0, 0, null);
return ret;
| javax.imageio.metadata.IIOMetadataNode | getNativeNode()Returns a tree of DOM nodes representing this object and any
subordinate JFXX extension or ICC Profile segments.
IIOMetadataNode node = new IIOMetadataNode("app0JFIF");
node.setAttribute("majorVersion", Integer.toString(majorVersion));
node.setAttribute("minorVersion", Integer.toString(minorVersion));
node.setAttribute("resUnits", Integer.toString(resUnits));
node.setAttribute("Xdensity", Integer.toString(Xdensity));
node.setAttribute("Ydensity", Integer.toString(Ydensity));
node.setAttribute("thumbWidth", Integer.toString(thumbWidth));
node.setAttribute("thumbHeight", Integer.toString(thumbHeight));
if (!extSegments.isEmpty()) {
IIOMetadataNode JFXXnode = new IIOMetadataNode("JFXX");
node.appendChild(JFXXnode);
for (Iterator iter = extSegments.iterator(); iter.hasNext();) {
JFIFExtensionMarkerSegment seg =
(JFIFExtensionMarkerSegment) iter.next();
JFXXnode.appendChild(seg.getNativeNode());
}
}
if (iccSegment != null) {
node.appendChild(iccSegment.getNativeNode());
}
return node;
| java.awt.image.BufferedImage | getThumbnail(javax.imageio.stream.ImageInputStream iis, int index, com.sun.imageio.plugins.jpeg.JPEGImageReader reader)
reader.thumbnailStarted(index);
BufferedImage ret = null;
if ((thumb != null) && (index == 0)) {
ret = thumb.getThumbnail(iis, reader);
} else {
if (thumb != null) {
index--;
}
JFIFExtensionMarkerSegment jfxx =
(JFIFExtensionMarkerSegment) extSegments.get(index);
ret = jfxx.thumb.getThumbnail(iis, reader);
}
reader.thumbnailComplete();
return ret;
| int | getThumbnailHeight(int index)
if (thumb != null) {
if (index == 0) {
return thumb.getHeight();
}
index--;
}
JFIFExtensionMarkerSegment jfxx =
(JFIFExtensionMarkerSegment) extSegments.get(index);
return jfxx.thumb.getHeight();
| int | getThumbnailWidth(int index)
if (thumb != null) {
if (index == 0) {
return thumb.getWidth();
}
index--;
}
JFIFExtensionMarkerSegment jfxx =
(JFIFExtensionMarkerSegment) extSegments.get(index);
return jfxx.thumb.getWidth();
| void | print()Prints out the contents of this object to System.out for debugging.
printTag("JFIF");
System.out.print("Version ");
System.out.print(majorVersion);
System.out.println(".0"
+ Integer.toString(minorVersion));
System.out.print("Resolution units: ");
System.out.println(resUnits);
System.out.print("X density: ");
System.out.println(Xdensity);
System.out.print("Y density: ");
System.out.println(Ydensity);
System.out.print("Thumbnail Width: ");
System.out.println(thumbWidth);
System.out.print("Thumbnail Height: ");
System.out.println(thumbHeight);
if (!extSegments.isEmpty()) {
for (Iterator iter = extSegments.iterator(); iter.hasNext();) {
JFIFExtensionMarkerSegment extSegment =
(JFIFExtensionMarkerSegment) iter.next();
extSegment.print();
}
}
if (iccSegment != null) {
iccSegment.print();
}
| void | updateFromNativeNode(org.w3c.dom.Node node, boolean fromScratch)Updates the data in this object from the given DOM Node tree.
If fromScratch is true, this object is being constructed.
Otherwise an existing object is being modified.
Throws an IIOInvalidTreeException if the tree is invalid in
any way.
// none of the attributes are required
NamedNodeMap attrs = node.getAttributes();
if (attrs.getLength() > 0) {
int value = getAttributeValue(node, attrs, "majorVersion",
0, 255, false);
majorVersion = (value != -1) ? value : majorVersion;
value = getAttributeValue(node, attrs, "minorVersion",
0, 255, false);
minorVersion = (value != -1) ? value : minorVersion;
value = getAttributeValue(node, attrs, "resUnits", 0, 2, false);
resUnits = (value != -1) ? value : resUnits;
value = getAttributeValue(node, attrs, "Xdensity", 1, 65535, false);
Xdensity = (value != -1) ? value : Xdensity;
value = getAttributeValue(node, attrs, "Ydensity", 1, 65535, false);
Ydensity = (value != -1) ? value : Ydensity;
value = getAttributeValue(node, attrs, "thumbWidth", 0, 255, false);
thumbWidth = (value != -1) ? value : thumbWidth;
value = getAttributeValue(node, attrs, "thumbHeight", 0, 255, false);
thumbHeight = (value != -1) ? value : thumbHeight;
}
if (node.hasChildNodes()) {
NodeList children = node.getChildNodes();
int count = children.getLength();
if (count > 2) {
throw new IIOInvalidTreeException
("app0JFIF node cannot have > 2 children", node);
}
for (int i = 0; i < count; i++) {
Node child = children.item(i);
String name = child.getNodeName();
if (name.equals("JFXX")) {
if ((!extSegments.isEmpty()) && fromScratch) {
throw new IIOInvalidTreeException
("app0JFIF node cannot have > 1 JFXX node", node);
}
NodeList exts = child.getChildNodes();
int extCount = exts.getLength();
for (int j = 0; j < extCount; j++) {
Node ext = exts.item(j);
extSegments.add(new JFIFExtensionMarkerSegment(ext));
}
}
if (name.equals("app2ICC")) {
if ((iccSegment != null) && fromScratch) {
throw new IIOInvalidTreeException
("> 1 ICC APP2 Marker Segment not supported", node);
}
iccSegment = new ICCMarkerSegment(child);
}
}
}
| void | write(javax.imageio.stream.ImageOutputStream ios, com.sun.imageio.plugins.jpeg.JPEGImageWriter writer)Writes the data for this segment to the stream in
valid JPEG format. Assumes that there will be no thumbnail.
// No thumbnail
write(ios, null, writer);
| void | write(javax.imageio.stream.ImageOutputStream ios, java.awt.image.BufferedImage thumb, com.sun.imageio.plugins.jpeg.JPEGImageWriter writer)Writes the data for this segment to the stream in
valid JPEG format. The length written takes the thumbnail
width and height into account. If necessary, the thumbnail
is clipped to 255 x 255 and a warning is sent to the writer
argument. Progress updates are sent to the writer argument.
int thumbWidth = 0;
int thumbHeight = 0;
int thumbLength = 0;
int [] thumbData = null;
if (thumb != null) {
// Clip if necessary and get the data in thumbData
thumbWidth = thumb.getWidth();
thumbHeight = thumb.getHeight();
if ((thumbWidth > MAX_THUMB_WIDTH)
|| (thumbHeight > MAX_THUMB_HEIGHT)) {
writer.warningOccurred(JPEGImageWriter.WARNING_THUMB_CLIPPED);
}
thumbWidth = Math.min(thumbWidth, MAX_THUMB_WIDTH);
thumbHeight = Math.min(thumbHeight, MAX_THUMB_HEIGHT);
thumbData = thumb.getRaster().getPixels(0, 0,
thumbWidth, thumbHeight,
(int []) null);
thumbLength = thumbData.length;
}
length = DATA_SIZE + LENGTH_SIZE + thumbLength;
writeTag(ios);
byte [] id = {0x4A, 0x46, 0x49, 0x46, 0x00};
ios.write(id);
ios.write(majorVersion);
ios.write(minorVersion);
ios.write(resUnits);
write2bytes(ios, Xdensity);
write2bytes(ios, Ydensity);
ios.write(thumbWidth);
ios.write(thumbHeight);
if (thumbData != null) {
writer.thumbnailStarted(0);
writeThumbnailData(ios, thumbData, writer);
writer.thumbnailComplete();
}
| static void | writeDefaultJFIF(javax.imageio.stream.ImageOutputStream ios, java.util.List thumbnails, java.awt.color.ICC_Profile iccProfile, com.sun.imageio.plugins.jpeg.JPEGImageWriter writer)Writes out a default JFIF marker segment to the given
output stream. If thumbnails is not null ,
writes out the set of thumbnail images as JFXX marker segments, or
incorporated into the JFIF segment if appropriate.
If iccProfile is not null ,
writes out the profile after the JFIF segment using as many APP2
marker segments as necessary.
JFIFMarkerSegment jfif = new JFIFMarkerSegment();
jfif.writeWithThumbs(ios, thumbnails, writer);
if (iccProfile != null) {
writeICC(iccProfile, ios);
}
| static void | writeICC(java.awt.color.ICC_Profile profile, javax.imageio.stream.ImageOutputStream ios)Write out the given profile to the stream, embedded in
the necessary number of APP2 segments, per the ICC spec.
This is the only mechanism for writing an ICC profile
to a stream.
int LENGTH_LENGTH = 2;
final String ID = "ICC_PROFILE";
int ID_LENGTH = ID.length()+1; // spec says it's null-terminated
int COUNTS_LENGTH = 2;
int MAX_ICC_CHUNK_SIZE =
65535 - LENGTH_LENGTH - ID_LENGTH - COUNTS_LENGTH;
byte [] data = profile.getData();
int numChunks = data.length / MAX_ICC_CHUNK_SIZE;
if ((data.length % MAX_ICC_CHUNK_SIZE) != 0) {
numChunks++;
}
int chunkNum = 1;
int offset = 0;
for (int i = 0; i < numChunks; i++) {
int dataLength = Math.min(data.length-offset, MAX_ICC_CHUNK_SIZE);
int segLength = dataLength+COUNTS_LENGTH+ID_LENGTH+LENGTH_LENGTH;
ios.write(0xff);
ios.write(JPEG.APP2);
MarkerSegment.write2bytes(ios, segLength);
byte [] id = ID.getBytes("US-ASCII");
ios.write(id);
ios.write(0); // Null-terminate the string
ios.write(chunkNum++);
ios.write(numChunks);
ios.write(data, offset, dataLength);
offset += dataLength;
}
| private void | writeJFXXSegment(int index, java.awt.image.BufferedImage thumbnail, javax.imageio.stream.ImageOutputStream ios, com.sun.imageio.plugins.jpeg.JPEGImageWriter writer)Writes out a new JFXX extension segment, without saving it.
JFIFExtensionMarkerSegment jfxx = null;
try {
jfxx = new JFIFExtensionMarkerSegment(thumbnail);
} catch (IllegalThumbException e) {
writer.warningOccurred
(JPEGImageWriter.WARNING_ILLEGAL_THUMBNAIL);
return;
}
writer.thumbnailStarted(index);
jfxx.write(ios, writer);
writer.thumbnailComplete();
| private void | writeThumb(javax.imageio.stream.ImageOutputStream ios, java.awt.image.BufferedImage thumb, com.sun.imageio.plugins.jpeg.JFIFMarkerSegment$JFIFExtensionMarkerSegment jfxx, int index, boolean onlyOne, com.sun.imageio.plugins.jpeg.JPEGImageWriter writer)
ColorModel cm = thumb.getColorModel();
ColorSpace cs = cm.getColorSpace();
if (cm instanceof IndexColorModel) {
// We never write a palette image into the header
// So if it's the only one, we need to write the header first
if (onlyOne) {
write(ios, writer);
}
if ((jfxx == null)
|| (jfxx.code == THUMB_PALETTE)) {
writeJFXXSegment(index, thumb, ios, writer); // default
} else {
// Expand to RGB
BufferedImage thumbRGB =
((IndexColorModel) cm).convertToIntDiscrete
(thumb.getRaster(), false);
jfxx.setThumbnail(thumbRGB);
writer.thumbnailStarted(index);
jfxx.write(ios, writer); // Handles clipping if needed
writer.thumbnailComplete();
}
} else if (cs.getType() == ColorSpace.TYPE_RGB) {
if (jfxx == null) {
if (onlyOne) {
write(ios, thumb, writer); // As part of the header
} else {
writeJFXXSegment(index, thumb, ios, writer); // default
}
} else {
// If this is the only one, write the header first
if (onlyOne) {
write(ios, writer);
}
if (jfxx.code == THUMB_PALETTE) {
writeJFXXSegment(index, thumb, ios, writer); // default
writer.warningOccurred
(JPEGImageWriter.WARNING_NO_RGB_THUMB_AS_INDEXED);
} else {
jfxx.setThumbnail(thumb);
writer.thumbnailStarted(index);
jfxx.write(ios, writer); // Handles clipping if needed
writer.thumbnailComplete();
}
}
} else if (cs.getType() == ColorSpace.TYPE_GRAY) {
if (jfxx == null) {
if (onlyOne) {
BufferedImage thumbRGB = expandGrayThumb(thumb);
write(ios, thumbRGB, writer); // As part of the header
} else {
writeJFXXSegment(index, thumb, ios, writer); // default
}
} else {
// If this is the only one, write the header first
if (onlyOne) {
write(ios, writer);
}
if (jfxx.code == THUMB_RGB) {
BufferedImage thumbRGB = expandGrayThumb(thumb);
writeJFXXSegment(index, thumbRGB, ios, writer);
} else if (jfxx.code == THUMB_JPEG) {
jfxx.setThumbnail(thumb);
writer.thumbnailStarted(index);
jfxx.write(ios, writer); // Handles clipping if needed
writer.thumbnailComplete();
} else if (jfxx.code == THUMB_PALETTE) {
writeJFXXSegment(index, thumb, ios, writer); // default
writer.warningOccurred
(JPEGImageWriter.WARNING_NO_GRAY_THUMB_AS_INDEXED);
}
}
} else {
writer.warningOccurred
(JPEGImageWriter.WARNING_ILLEGAL_THUMBNAIL);
}
| void | writeThumbnailData(javax.imageio.stream.ImageOutputStream ios, int[] thumbData, com.sun.imageio.plugins.jpeg.JPEGImageWriter writer)
int progInterval = thumbData.length / 20; // approx. every 5%
if (progInterval == 0) {
progInterval = 1;
}
for (int i = 0; i < thumbData.length; i++) {
ios.write(thumbData[i]);
if ((i > progInterval) && (i % progInterval == 0)) {
writer.thumbnailProgress
(((float) i * 100) / ((float) thumbData.length));
}
}
| void | writeWithThumbs(javax.imageio.stream.ImageOutputStream ios, java.util.List thumbnails, com.sun.imageio.plugins.jpeg.JPEGImageWriter writer)Write out this JFIF Marker Segment, including a thumbnail or
appending a series of JFXX Marker Segments, as appropriate.
Warnings and progress reports are sent to the writer argument.
The list of thumbnails is matched to the list of JFXX extension
segments, if any, in order to determine how to encode the
thumbnails. If there are more thumbnails than metadata segments,
default encoding is used for the extra thumbnails.
if (thumbnails != null) {
JFIFExtensionMarkerSegment jfxx = null;
if (thumbnails.size() == 1) {
if (!extSegments.isEmpty()) {
jfxx = (JFIFExtensionMarkerSegment) extSegments.get(0);
}
writeThumb(ios,
(BufferedImage) thumbnails.get(0),
jfxx,
0,
true,
writer);
} else {
// All others write as separate JFXX segments
write(ios, writer); // Just the header without any thumbnail
for (int i = 0; i < thumbnails.size(); i++) {
jfxx = null;
if (i < extSegments.size()) {
jfxx = (JFIFExtensionMarkerSegment) extSegments.get(i);
}
writeThumb(ios,
(BufferedImage) thumbnails.get(i),
jfxx,
i,
false,
writer);
}
}
} else { // No thumbnails
write(ios, writer);
}
|
|