- /*
- * @(#)JFIFMarkerSegment.java 1.9 03/12/19
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package com.sun.imageio.plugins.jpeg;
-
- import javax.imageio.IIOException;
- import javax.imageio.IIOImage;
- import javax.imageio.ImageTypeSpecifier;
- import javax.imageio.ImageReader;
- import javax.imageio.metadata.IIOInvalidTreeException;
- import javax.imageio.metadata.IIOMetadataNode;
- import javax.imageio.metadata.IIOMetadata;
- import javax.imageio.stream.ImageInputStream;
- import javax.imageio.stream.ImageOutputStream;
- import javax.imageio.stream.MemoryCacheImageOutputStream;
- import javax.imageio.event.IIOReadProgressListener;
-
- import java.awt.Graphics;
- import java.awt.color.ICC_Profile;
- import java.awt.color.ICC_ColorSpace;
- import java.awt.color.ColorSpace;
- import java.awt.image.ColorModel;
- import java.awt.image.SampleModel;
- import java.awt.image.IndexColorModel;
- import java.awt.image.ComponentColorModel;
- import java.awt.image.BufferedImage;
- import java.awt.image.DataBuffer;
- import java.awt.image.DataBufferByte;
- import java.awt.image.Raster;
- import java.awt.image.WritableRaster;
- import java.io.IOException;
- import java.io.ByteArrayOutputStream;
- import java.util.List;
- import java.util.ArrayList;
- import java.util.Iterator;
-
- import org.w3c.dom.Node;
- import org.w3c.dom.NodeList;
- import org.w3c.dom.NamedNodeMap;
-
- /**
- * 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.
- */
- class JFIFMarkerSegment extends MarkerSegment {
- int majorVersion;
- int minorVersion;
- int resUnits;
- int Xdensity;
- int Ydensity;
- int thumbWidth;
- int thumbHeight;
- JFIFThumbRGB thumb = null; // If present
- ArrayList extSegments = new ArrayList();
- ICCMarkerSegment iccSegment = null; // optional ICC
- private static final int THUMB_JPEG = 0x10;
- private static final int THUMB_PALETTE = 0x11;
- private static final int THUMB_UNASSIGNED = 0x12;
- private static final int THUMB_RGB = 0x13;
- private static final int DATA_SIZE = 14;
- private static final int ID_SIZE = 5;
- private final int MAX_THUMB_WIDTH = 255;
- private final int MAX_THUMB_HEIGHT = 255;
-
- private final boolean debug = false;
-
- /**
- * Set to <code>true</code> 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 boolean inICC = false;
-
- /**
- * A placeholder for an ICC profile marker segment under
- * construction. The segment is not added to the list
- * until all chunks have been read.
- */
- private ICCMarkerSegment tempICCSegment = null;
-
-
- /**
- * Default constructor. Used to create a default JFIF header
- */
- JFIFMarkerSegment() {
- super(JPEG.APP0);
- majorVersion = 1;
- minorVersion = 2;
- resUnits = JPEG.DENSITY_UNIT_ASPECT_RATIO;
- Xdensity = 1;
- Ydensity = 1;
- thumbWidth = 0;
- thumbHeight = 0;
- }
-
- /**
- * Constructs a JFIF header by reading from a stream wrapped
- * in a JPEGBuffer.
- */
- JFIFMarkerSegment(JPEGBuffer buffer) throws IOException {
- 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);
- }
- }
-
- /**
- * Constructs a JFIF header from a DOM Node.
- */
- JFIFMarkerSegment(Node node) throws IIOInvalidTreeException {
- this();
- updateFromNativeNode(node, true);
- }
-
- /**
- * Returns a deep-copy clone of this object.
- */
- protected Object clone() {
- 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;
- }
-
- /**
- * Add an JFXX extension marker segment from the stream wrapped
- * in the JPEGBuffer to the list of extension segments.
- */
- void addJFXX(JPEGBuffer buffer, JPEGImageReader reader)
- throws IOException {
- extSegments.add(new JFIFExtensionMarkerSegment(buffer, reader));
- }
-
- /**
- * Adds an ICC Profile APP2 segment from the stream wrapped
- * in the JPEGBuffer.
- */
- void addICC(JPEGBuffer buffer) throws IOException {
- 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;
- }
- }
- }
-
- /**
- * Add an ICC Profile APP2 segment by constructing it from
- * the given ICC_ColorSpace object.
- */
- void addICC(ICC_ColorSpace cs) throws IOException {
- if (iccSegment != null) {
- throw new IIOException
- ("> 1 ICC APP2 Marker Segment not supported");
- }
- iccSegment = new ICCMarkerSegment(cs);
- }
-
- /**
- * Returns a tree of DOM nodes representing this object and any
- * subordinate JFXX extension or ICC Profile segments.
- */
- IIOMetadataNode getNativeNode() {
- 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;
- }
-
- /**
- * 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.
- */
- void updateFromNativeNode(Node node, boolean fromScratch)
- throws IIOInvalidTreeException {
- // 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);
- }
- }
- }
- }
-
- int getThumbnailWidth(int index) {
- if (thumb != null) {
- if (index == 0) {
- return thumb.getWidth();
- }
- index--;
- }
- JFIFExtensionMarkerSegment jfxx =
- (JFIFExtensionMarkerSegment) extSegments.get(index);
- return jfxx.thumb.getWidth();
- }
-
- int getThumbnailHeight(int index) {
- if (thumb != null) {
- if (index == 0) {
- return thumb.getHeight();
- }
- index--;
- }
- JFIFExtensionMarkerSegment jfxx =
- (JFIFExtensionMarkerSegment) extSegments.get(index);
- return jfxx.thumb.getHeight();
- }
-
- BufferedImage getThumbnail(ImageInputStream iis,
- int index,
- JPEGImageReader reader) throws IOException {
- 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;
- }
-
-
- /**
- * Writes the data for this segment to the stream in
- * valid JPEG format. Assumes that there will be no thumbnail.
- */
- void write(ImageOutputStream ios,
- JPEGImageWriter writer) throws IOException {
- // No thumbnail
- write(ios, null, 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.
- */
- void write(ImageOutputStream ios,
- BufferedImage thumb,
- JPEGImageWriter writer) throws IOException {
- 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();
- }
- }
-
- /*
- * Write out the values in the integer array as a sequence of bytes,
- * reporting progress to the writer argument.
- */
- void writeThumbnailData(ImageOutputStream ios,
- int [] thumbData,
- JPEGImageWriter writer) throws IOException {
- 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));
- }
- }
- }
-
- /**
- * 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.
- */
- void writeWithThumbs(ImageOutputStream ios,
- List thumbnails,
- JPEGImageWriter writer) throws IOException {
- 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);
- }
-
- }
-
- private void writeThumb(ImageOutputStream ios,
- BufferedImage thumb,
- JFIFExtensionMarkerSegment jfxx,
- int index,
- boolean onlyOne,
- JPEGImageWriter writer) throws IOException {
- 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);
- }
- }
-
- // Could put reason codes in here to be parsed in writeJFXXSegment
- // in order to provide more meaningful warnings.
- private class IllegalThumbException extends Exception {}
-
- /**
- * Writes out a new JFXX extension segment, without saving it.
- */
- private void writeJFXXSegment(int index,
- BufferedImage thumbnail,
- ImageOutputStream ios,
- JPEGImageWriter writer) throws IOException {
- 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();
- }
-
-
- /**
- * Return an RGB image that is the expansion of the given grayscale
- * image.
- */
- private static BufferedImage expandGrayThumb(BufferedImage thumb) {
- BufferedImage ret = new BufferedImage(thumb.getWidth(),
- thumb.getHeight(),
- BufferedImage.TYPE_INT_RGB);
- Graphics g = ret.getGraphics();
- g.drawImage(thumb, 0, 0, null);
- return ret;
- }
-
- /**
- * Writes out a default JFIF marker segment to the given
- * output stream. If <code>thumbnails</code> is not <code>null</code>,
- * writes out the set of thumbnail images as JFXX marker segments, or
- * incorporated into the JFIF segment if appropriate.
- * If <code>iccProfile</code> is not <code>null</code>,
- * writes out the profile after the JFIF segment using as many APP2
- * marker segments as necessary.
- */
- static void writeDefaultJFIF(ImageOutputStream ios,
- List thumbnails,
- ICC_Profile iccProfile,
- JPEGImageWriter writer)
- throws IOException {
-
- JFIFMarkerSegment jfif = new JFIFMarkerSegment();
- jfif.writeWithThumbs(ios, thumbnails, writer);
- if (iccProfile != null) {
- writeICC(iccProfile, ios);
- }
- }
-
- /**
- * Prints out the contents of this object to System.out for debugging.
- */
- void print() {
- 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();
- }
- }
-
- /**
- * A JFIF extension APP0 marker segment.
- */
- class JFIFExtensionMarkerSegment extends MarkerSegment {
- int code;
- JFIFThumb thumb;
- private static final int DATA_SIZE = 6;
- private static final int ID_SIZE = 5;
-
- JFIFExtensionMarkerSegment(JPEGBuffer buffer, JPEGImageReader reader)
- throws IOException {
-
- super(buffer);
- buffer.bufPtr += ID_SIZE; // skip the id, we already checked it
-
- code = buffer.buf[buffer.bufPtr++] & 0xff;
- buffer.bufAvail -= DATA_SIZE;
- if (code == THUMB_JPEG) {
- thumb = new JFIFThumbJPEG(buffer, length, reader);
- } else {
- buffer.loadBuf(2);
- int thumbX = buffer.buf[buffer.bufPtr++] & 0xff;
- int thumbY = buffer.buf[buffer.bufPtr++] & 0xff;
- buffer.bufAvail -= 2;
- // following constructors handle bufAvail
- if (code == THUMB_PALETTE) {
- thumb = new JFIFThumbPalette(buffer, thumbX, thumbY);
- } else {
- thumb = new JFIFThumbRGB(buffer, thumbX, thumbY);
- }
- }
- }
-
- JFIFExtensionMarkerSegment(Node node) throws IIOInvalidTreeException {
- super(JPEG.APP0);
- NamedNodeMap attrs = node.getAttributes();
- if (attrs.getLength() > 0) {
- code = getAttributeValue(node,
- attrs,
- "extensionCode",
- THUMB_JPEG,
- THUMB_RGB,
- false);
- if (code == THUMB_UNASSIGNED) {
- throw new IIOInvalidTreeException
- ("invalid extensionCode attribute value", node);
- }
- } else {
- code = THUMB_UNASSIGNED;
- }
- // Now the child
- if (node.getChildNodes().getLength() != 1) {
- throw new IIOInvalidTreeException
- ("app0JFXX node must have exactly 1 child", node);
- }
- Node child = node.getFirstChild();
- String name = child.getNodeName();
- if (name.equals("JFIFthumbJPEG")) {
- if (code == THUMB_UNASSIGNED) {
- code = THUMB_JPEG;
- }
- thumb = new JFIFThumbJPEG(child);
- } else if (name.equals("JFIFthumbPalette")) {
- if (code == THUMB_UNASSIGNED) {
- code = THUMB_PALETTE;
- }
- thumb = new JFIFThumbPalette(child);
- } else if (name.equals("JFIFthumbRGB")) {
- if (code == THUMB_UNASSIGNED) {
- code = THUMB_RGB;
- }
- thumb = new JFIFThumbRGB(child);
- } else {
- throw new IIOInvalidTreeException
- ("unrecognized app0JFXX child node", node);
- }
- }
-
- JFIFExtensionMarkerSegment(BufferedImage thumbnail)
- throws IllegalThumbException {
-
- super(JPEG.APP0);
- ColorModel cm = thumbnail.getColorModel();
- int csType = cm.getColorSpace().getType();
- if (cm.hasAlpha()) {
- throw new IllegalThumbException();
- }
- if (cm instanceof IndexColorModel) {
- code = THUMB_PALETTE;
- thumb = new JFIFThumbPalette(thumbnail);
- } else if (csType == ColorSpace.TYPE_RGB) {
- code = THUMB_RGB;
- thumb = new JFIFThumbRGB(thumbnail);
- } else if (csType == ColorSpace.TYPE_GRAY) {
- code = THUMB_JPEG;
- thumb = new JFIFThumbJPEG(thumbnail);
- } else {
- throw new IllegalThumbException();
- }
- }
-
- void setThumbnail(BufferedImage thumbnail) {
- try {
- switch (code) {
- case THUMB_PALETTE:
- thumb = new JFIFThumbPalette(thumbnail);
- break;
- case THUMB_RGB:
- thumb = new JFIFThumbRGB(thumbnail);
- break;
- case THUMB_JPEG:
- thumb = new JFIFThumbJPEG(thumbnail);
- break;
- }
- } catch (IllegalThumbException e) {
- // Should never happen
- throw new InternalError("Illegal thumb in setThumbnail!");
- }
- }
-
- protected Object clone() {
- JFIFExtensionMarkerSegment newGuy =
- (JFIFExtensionMarkerSegment) super.clone();
- if (thumb != null) {
- newGuy.thumb = (JFIFThumb) thumb.clone();
- }
- return newGuy;
- }
-
- IIOMetadataNode getNativeNode() {
- IIOMetadataNode node = new IIOMetadataNode("app0JFXX");
- node.setAttribute("extensionCode", Integer.toString(code));
- node.appendChild(thumb.getNativeNode());
- return node;
- }
-
- void write(ImageOutputStream ios,
- JPEGImageWriter writer) throws IOException {
- length = LENGTH_SIZE + DATA_SIZE + thumb.getLength();
- writeTag(ios);
- byte [] id = {0x4A, 0x46, 0x58, 0x58, 0x00};
- ios.write(id);
- ios.write(code);
- thumb.write(ios, writer);
- }
-
- void print() {
- printTag("JFXX");
- thumb.print();
- }
- }
-
- /**
- * A superclass for the varieties of thumbnails that can
- * be stored in a JFIF extension marker segment.
- */
- abstract class JFIFThumb implements Cloneable {
- long streamPos = -1L; // Save the thumbnail pos when reading
- abstract int getLength(); // When writing
- abstract int getWidth();
- abstract int getHeight();
- abstract BufferedImage getThumbnail(ImageInputStream iis,
- JPEGImageReader reader)
- throws IOException;
-
- protected JFIFThumb() {}
-
- protected JFIFThumb(JPEGBuffer buffer) throws IOException{
- // Save the stream position for reading the thumbnail later
- streamPos = buffer.getStreamPosition();
- }
-
- abstract void print();
-
- abstract IIOMetadataNode getNativeNode();
-
- abstract void write(ImageOutputStream ios,
- JPEGImageWriter writer) throws IOException;
-
- protected Object clone() {
- try {
- return super.clone();
- } catch (CloneNotSupportedException e) {} // won't happen
- return null;
- }
-
- }
-
- abstract class JFIFThumbUncompressed extends JFIFThumb {
- BufferedImage thumbnail = null;
- int thumbWidth;
- int thumbHeight;
- String name;
-
- JFIFThumbUncompressed(JPEGBuffer buffer,
- int width,
- int height,
- int skip,
- String name)
- throws IOException {
- super(buffer);
- thumbWidth = width;
- thumbHeight = height;
- // Now skip the thumbnail data
- buffer.skipData(skip);
- this.name = name;
- }
-
- JFIFThumbUncompressed(Node node, String name)
- throws IIOInvalidTreeException {
-
- thumbWidth = 0;
- thumbHeight = 0;
- this.name = name;
- NamedNodeMap attrs = node.getAttributes();
- int count = attrs.getLength();
- if (count > 2) {
- throw new IIOInvalidTreeException
- (name +" node cannot have > 2 attributes", node);
- }
- if (count != 0) {
- int 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;
- }
- }
-
- JFIFThumbUncompressed(BufferedImage thumb) {
- thumbnail = thumb;
- thumbWidth = thumb.getWidth();
- thumbHeight = thumb.getHeight();
- name = null; // not used when writing
- }
-
- void readByteBuffer(ImageInputStream iis,
- byte [] data,
- JPEGImageReader reader,
- float workPortion,
- float workOffset) throws IOException {
- int progInterval = Math.max((int)(data.length20workPortion),
- 1);
- for (int offset = 0;
- offset < data.length;) {
- int len = Math.min(progInterval, data.length-offset);
- iis.read(data, offset, len);
- offset += progInterval;
- float percentDone = ((float) offset* 100)
- / data.length
- * workPortion + workOffset;
- if (percentDone > 100.0F) {
- percentDone = 100.0F;
- }
- reader.thumbnailProgress (percentDone);
- }
- }
-
-
- int getWidth() {
- return thumbWidth;
- }
-
- int getHeight() {
- return thumbHeight;
- }
-
- IIOMetadataNode getNativeNode() {
- IIOMetadataNode node = new IIOMetadataNode(name);
- node.setAttribute("thumbWidth", Integer.toString(thumbWidth));
- node.setAttribute("thumbHeight", Integer.toString(thumbHeight));
- return node;
- }
-
- void write(ImageOutputStream ios,
- JPEGImageWriter writer) throws IOException {
- 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);
- ios.write(thumbWidth);
- ios.write(thumbHeight);
- }
-
- void writePixels(ImageOutputStream ios,
- JPEGImageWriter writer) throws IOException {
- 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);
- int [] data = thumbnail.getRaster().getPixels(0, 0,
- thumbWidth,
- thumbHeight,
- (int []) null);
- writeThumbnailData(ios, data, writer);
- }
-
- void print() {
- System.out.print(name + " width: ");
- System.out.println(thumbWidth);
- System.out.print(name + " height: ");
- System.out.println(thumbHeight);
- }
-
- }
-
- /**
- * A JFIF thumbnail stored as RGB, one byte per channel,
- * interleaved.
- */
- class JFIFThumbRGB extends JFIFThumbUncompressed {
-
- JFIFThumbRGB(JPEGBuffer buffer, int width, int height)
- throws IOException {
-
- super(buffer, width, height, width*height*3, "JFIFthumbRGB");
- }
-
- JFIFThumbRGB(Node node) throws IIOInvalidTreeException {
- super(node, "JFIFthumbRGB");
- }
-
- JFIFThumbRGB(BufferedImage thumb) throws IllegalThumbException {
- super(thumb);
- }
-
- int getLength() {
- return (thumbWidth*thumbHeight*3);
- }
-
- BufferedImage getThumbnail(ImageInputStream iis,
- JPEGImageReader reader)
- throws IOException {
- iis.mark();
- iis.seek(streamPos);
- DataBufferByte buffer = new DataBufferByte(getLength());
- readByteBuffer(iis,
- buffer.getData(),
- reader,
- 1.0F,
- 0.0F);
- iis.reset();
-
- WritableRaster raster =
- Raster.createInterleavedRaster(buffer,
- thumbWidth,
- thumbHeight,
- thumbWidth*3,
- 3,
- new int [] {0, 1, 2},
- null);
- ColorModel cm = new ComponentColorModel(JPEG.sRGB,
- false,
- false,
- ColorModel.OPAQUE,
- DataBuffer.TYPE_BYTE);
- return new BufferedImage(cm,
- raster,
- false,
- null);
- }
-
- void write(ImageOutputStream ios,
- JPEGImageWriter writer) throws IOException {
- super.write(ios, writer); // width and height
- writePixels(ios, writer);
- }
-
- }
-
- /**
- * A JFIF thumbnail stored as an indexed palette image
- * using an RGB palette.
- */
- class JFIFThumbPalette extends JFIFThumbUncompressed {
- private static final int PALETTE_SIZE = 768;
-
- JFIFThumbPalette(JPEGBuffer buffer, int width, int height)
- throws IOException {
- super(buffer,
- width,
- height,
- PALETTE_SIZE + width * height,
- "JFIFThumbPalette");
- }
-
- JFIFThumbPalette(Node node) throws IIOInvalidTreeException {
- super(node, "JFIFThumbPalette");
- }
-
- JFIFThumbPalette(BufferedImage thumb) throws IllegalThumbException {
- super(thumb);
- IndexColorModel icm = (IndexColorModel) thumbnail.getColorModel();
- if (icm.getMapSize() > 256) {
- throw new IllegalThumbException();
- }
- }
-
- int getLength() {
- return (thumbWidth*thumbHeight + PALETTE_SIZE);
- }
-
- BufferedImage getThumbnail(ImageInputStream iis,
- JPEGImageReader reader)
- throws IOException {
- iis.mark();
- iis.seek(streamPos);
- // read the palette
- byte [] palette = new byte [PALETTE_SIZE];
- float palettePart = ((float) PALETTE_SIZE) / getLength();
- readByteBuffer(iis,
- palette,
- reader,
- palettePart,
- 0.0F);
- DataBufferByte buffer = new DataBufferByte(thumbWidth*thumbHeight);
- readByteBuffer(iis,
- buffer.getData(),
- reader,
- 1.0F-palettePart,
- palettePart);
- iis.read();
- iis.reset();
-
- IndexColorModel cm = new IndexColorModel(8,
- 256,
- palette,
- 0,
- false);
- SampleModel sm = cm.createCompatibleSampleModel(thumbWidth,
- thumbHeight);
- WritableRaster raster =
- Raster.createWritableRaster(sm, buffer, null);
- return new BufferedImage(cm,
- raster,
- false,
- null);
- }
-
- void write(ImageOutputStream ios,
- JPEGImageWriter writer) throws IOException {
- super.write(ios, writer); // width and height
- // Write the palette (must be 768 bytes)
- byte [] palette = new byte[768];
- IndexColorModel icm = (IndexColorModel) thumbnail.getColorModel();
- byte [] reds = new byte [256];
- byte [] greens = new byte [256];
- byte [] blues = new byte [256];
- icm.getReds(reds);
- icm.getGreens(greens);
- icm.getBlues(blues);
- for (int i = 0; i < 256; i++) {
- palette[i*3] = reds[i];
- palette[i*3+1] = greens[i];
- palette[i*3+2] = blues[i];
- }
- ios.write(palette);
- writePixels(ios, writer);
- }
- }
-
-
- /**
- * A JFIF thumbnail stored as a JPEG stream. No JFIF or
- * JFIF extension markers are permitted. There is no need
- * to clip these, but the entire image must fit into a
- * single JFXX marker segment.
- */
- class JFIFThumbJPEG extends JFIFThumb {
- JPEGMetadata thumbMetadata = null;
- byte [] data = null; // Compressed image data, for writing
- private static final int PREAMBLE_SIZE = 6;
-
- JFIFThumbJPEG(JPEGBuffer buffer,
- int length,
- JPEGImageReader reader) throws IOException {
- super(buffer);
- // Compute the final stream position
- long finalPos = streamPos + (length - PREAMBLE_SIZE);
- // Set the stream back to the start of the thumbnail
- // and read its metadata (but don't decode the image)
- buffer.iis.seek(streamPos);
- thumbMetadata = new JPEGMetadata(false, true, buffer.iis, reader);
- // Set the stream to the computed final position
- buffer.iis.seek(finalPos);
- // Clear the now invalid buffer
- buffer.bufAvail = 0;
- buffer.bufPtr = 0;
- }
-
- JFIFThumbJPEG(Node node) throws IIOInvalidTreeException {
- if (node.getChildNodes().getLength() > 1) {
- throw new IIOInvalidTreeException
- ("JFIFThumbJPEG node must have 0 or 1 child", node);
- }
- Node child = node.getFirstChild();
- if (child != null) {
- String name = child.getNodeName();
- if (!name.equals("markerSequence")) {
- throw new IIOInvalidTreeException
- ("JFIFThumbJPEG child must be a markerSequence node",
- node);
- }
- thumbMetadata = new JPEGMetadata(false, true);
- thumbMetadata.setFromMarkerSequenceNode(child);
- }
- }
-
- JFIFThumbJPEG(BufferedImage thumb) throws IllegalThumbException {
- int INITIAL_BUFSIZE = 4096;
- int MAZ_BUFSIZE = 65535 - 2 - PREAMBLE_SIZE;
- try {
- ByteArrayOutputStream baos =
- new ByteArrayOutputStream(INITIAL_BUFSIZE);
- MemoryCacheImageOutputStream mos =
- new MemoryCacheImageOutputStream(baos);
-
- JPEGImageWriter thumbWriter = new JPEGImageWriter(null);
-
- thumbWriter.setOutput(mos);
-
- // get default metadata for the thumb
- JPEGMetadata metadata =
- (JPEGMetadata) thumbWriter.getDefaultImageMetadata
- (new ImageTypeSpecifier(thumb), null);
-
- // Remove the jfif segment, which should be there.
- MarkerSegment jfif = metadata.findMarkerSegment
- (JFIFMarkerSegment.class, true);
- if (jfif == null) {
- throw new IllegalThumbException();
- }
-
- metadata.markerSequence.remove(jfif);
-
- /* Use this if removing leaves a hole and causes trouble
-
- // Get the tree
- String format = metadata.getNativeMetadataFormatName();
- IIOMetadataNode tree =
- (IIOMetadataNode) metadata.getAsTree(format);
-
- // If there is no app0jfif node, the image is bad
- NodeList jfifs = tree.getElementsByTagName("app0JFIF");
- if (jfifs.getLength() == 0) {
- throw new IllegalThumbException();
- }
-
- // remove the app0jfif node
- Node jfif = jfifs.item(0);
- Node parent = jfif.getParentNode();
- parent.removeChild(jfif);
-
- metadata.setFromTree(format, tree);
- */
-
- thumbWriter.write(new IIOImage(thumb, null, metadata));
-
- thumbWriter.dispose();
- // Now check that the size is OK
- if (baos.size() > MAZ_BUFSIZE) {
- throw new IllegalThumbException();
- }
- data = baos.toByteArray();
- } catch (IOException e) {
- throw new IllegalThumbException();
- }
- }
-
- int getWidth() {
- int retval = 0;
- SOFMarkerSegment sof =
- (SOFMarkerSegment) thumbMetadata.findMarkerSegment
- (SOFMarkerSegment.class, true);
- if (sof != null) {
- retval = sof.samplesPerLine;
- }
- return retval;
- }
-
- int getHeight() {
- int retval = 0;
- SOFMarkerSegment sof =
- (SOFMarkerSegment) thumbMetadata.findMarkerSegment
- (SOFMarkerSegment.class, true);
- if (sof != null) {
- retval = sof.numLines;
- }
- return retval;
- }
-
- private class ThumbnailReadListener
- implements IIOReadProgressListener {
- JPEGImageReader reader = null;
- ThumbnailReadListener (JPEGImageReader reader) {
- this.reader = reader;
- }
- public void sequenceStarted(ImageReader source, int minIndex) {}
- public void sequenceComplete(ImageReader source) {}
- public void imageStarted(ImageReader source, int imageIndex) {}
- public void imageProgress(ImageReader source,
- float percentageDone) {
- reader.thumbnailProgress(percentageDone);
- }
- public void imageComplete(ImageReader source) {}
- public void thumbnailStarted(ImageReader source,
- int imageIndex, int thumbnailIndex) {}
- public void thumbnailProgress(ImageReader source, float percentageDone) {}
- public void thumbnailComplete(ImageReader source) {}
- public void readAborted(ImageReader source) {}
- }
-
- BufferedImage getThumbnail(ImageInputStream iis,
- JPEGImageReader reader)
- throws IOException {
- iis.mark();
- iis.seek(streamPos);
- JPEGImageReader thumbReader = new JPEGImageReader(null);
- thumbReader.setInput(iis);
- thumbReader.addIIOReadProgressListener
- (new ThumbnailReadListener(reader));
- BufferedImage ret = thumbReader.read(0, null);
- thumbReader.dispose();
- iis.reset();
- return ret;
- }
-
- protected Object clone() {
- JFIFThumbJPEG newGuy = (JFIFThumbJPEG) super.clone();
- if (thumbMetadata != null) {
- newGuy.thumbMetadata = (JPEGMetadata) thumbMetadata.clone();
- }
- return newGuy;
- }
-
- IIOMetadataNode getNativeNode() {
- IIOMetadataNode node = new IIOMetadataNode("JFIFthumbJPEG");
- if (thumbMetadata != null) {
- node.appendChild(thumbMetadata.getNativeTree());
- }
- return node;
- }
-
- int getLength() {
- if (data == null) {
- return 0;
- } else {
- return data.length;
- }
- }
-
- void write(ImageOutputStream ios,
- JPEGImageWriter writer) throws IOException {
- int progInterval = data.length / 20; // approx. every 5%
- if (progInterval == 0) {
- progInterval = 1;
- }
- for (int offset = 0;
- offset < data.length;) {
- int len = Math.min(progInterval, data.length-offset);
- ios.write(data, offset, len);
- offset += progInterval;
- float percentDone = ((float) offset * 100) / data.length;
- if (percentDone > 100.0F) {
- percentDone = 100.0F;
- }
- writer.thumbnailProgress (percentDone);
- }
- }
-
- void print () {
- System.out.println("JFIF thumbnail stored as JPEG");
- }
- }
-
- /**
- * 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.
- */
- static void writeICC(ICC_Profile profile, ImageOutputStream ios)
- throws IOException {
- 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;
- }
- }
-
- /**
- * An APP2 marker segment containing an ICC profile. In the stream
- * a profile larger than 64K is broken up into a series of chunks.
- * This inner class represents the complete profile as a single objec,
- * combining chunks as necessary.
- */
- class ICCMarkerSegment extends MarkerSegment {
- ArrayList chunks = null;
- byte [] profile = null; // The complete profile when it's fully read
- // May remain null when writing
- private static final int ID_SIZE = 12;
- int chunksRead;
- int numChunks;
-
- ICCMarkerSegment(ICC_ColorSpace cs) {
- super(JPEG.APP2);
- chunks = null;
- chunksRead = 0;
- numChunks = 0;
- profile = cs.getProfile().getData();
- }
-
- ICCMarkerSegment(JPEGBuffer buffer) throws IOException {
- super(buffer); // gets whole segment or fills the buffer
- if (debug) {
- System.out.println("Creating new ICC segment");
- }
- buffer.bufPtr += ID_SIZE; // Skip the id
- buffer.bufAvail -= ID_SIZE;
- /*
- * Reduce the stored length by the id size. The stored
- * length is used to store the length of the profile
- * data only.
- */
- length -= ID_SIZE;
-
- // get the chunk number
- int chunkNum = buffer.buf[buffer.bufPtr] & 0xff;
- // get the total number of chunks
- numChunks = buffer.buf[buffer.bufPtr+1] & 0xff;
-
- if (chunkNum > numChunks) {
- throw new IIOException
- ("Image format Error; chunk num > num chunks");
- }
-
- // if there are no more chunks, set up the data
- if (numChunks == 1) {
- // reduce the stored length by the two chunk numbering bytes
- length -= 2;
- profile = new byte[length];
- buffer.bufPtr += 2;
- buffer.bufAvail-=2;
- buffer.readData(profile);
- inICC = false;
- } else {
- // If we store them away, include the chunk numbering bytes
- byte [] profileData = new byte[length];
- // Now reduce the stored length by the
- // two chunk numbering bytes
- length -= 2;
- buffer.readData(profileData);
- chunks = new ArrayList();
- chunks.add(profileData);
- chunksRead = 1;
- inICC = true;
- }
- }
-
- ICCMarkerSegment(Node node) throws IIOInvalidTreeException {
- super(JPEG.APP2);
- if (node instanceof IIOMetadataNode) {
- IIOMetadataNode ourNode = (IIOMetadataNode) node;
- ICC_Profile prof = (ICC_Profile) ourNode.getUserObject();
- if (prof != null) { // May be null
- profile = prof.getData();
- }
- }
- }
-
- protected Object clone () {
- ICCMarkerSegment newGuy = (ICCMarkerSegment) super.clone();
- if (profile != null) {
- newGuy.profile = (byte[]) profile.clone();
- }
- return newGuy;
- }
-
- boolean addData(JPEGBuffer buffer) throws IOException {
- if (debug) {
- System.out.println("Adding to ICC segment");
- }
- // skip the tag
- buffer.bufPtr++;
- buffer.bufAvail--;
- // Get the length, but not in length
- int dataLen = (buffer.buf[buffer.bufPtr++] & 0xff) << 8;
- dataLen |= buffer.buf[buffer.bufPtr++] & 0xff;
- buffer.bufAvail -= 2;
- // Don't include length itself
- dataLen -= 2;
- // skip the id
- buffer.bufPtr += ID_SIZE; // Skip the id
- buffer.bufAvail -= ID_SIZE;
- /*
- * Reduce the stored length by the id size. The stored
- * length is used to store the length of the profile
- * data only.
- */
- dataLen -= ID_SIZE;
-
- // get the chunk number
- int chunkNum = buffer.buf[buffer.bufPtr] & 0xff;
- if (chunkNum > numChunks) {
- throw new IIOException
- ("Image format Error; chunk num > num chunks");
- }
-
- // get the number of chunks, which should match
- int newNumChunks = buffer.buf[buffer.bufPtr+1] & 0xff;
- if (numChunks != newNumChunks) {
- throw new IIOException
- ("Image format Error; icc num chunks mismatch");
- }
- dataLen -= 2;
- if (debug) {
- System.out.println("chunkNum: " + chunkNum
- + ", numChunks: " + numChunks
- + ", dataLen: " + dataLen);
- }
- boolean retval = false;
- byte [] profileData = new byte[dataLen];
- buffer.readData(profileData);
- chunks.add(profileData);
- length += dataLen;
- chunksRead++;
- if (chunksRead < numChunks) {
- inICC = true;
- } else {
- if (debug) {
- System.out.println("Completing profile; total length is "
- + length);
- }
- // create an array for the whole thing
- profile = new byte[length];
- // copy the existing chunks, releasing them
- // Note that they may be out of order
-
- int index = 0;
- for (int i = 1; i <= numChunks; i++) {
- boolean foundIt = false;
- for (int chunk = 0; chunk < chunks.size(); chunk++) {
- byte [] chunkData = (byte []) chunks.get(chunk);
- if (chunkData[0] == i) { // Right one
- System.arraycopy(chunkData, 2,
- profile, index,
- chunkData.length-2);
- index += chunkData.length-2;
- foundIt = true;
- }
- }
- if (foundIt == false) {
- throw new IIOException
- ("Image Format Error: Missing ICC chunk num " + i);
- }
- }
-
- chunks = null;
- chunksRead = 0;
- numChunks = 0;
- inICC = false;
- retval = true;
- }
- return retval;
- }
-
- IIOMetadataNode getNativeNode() {
- IIOMetadataNode node = new IIOMetadataNode("app2ICC");
- if (profile != null) {
- node.setUserObject(ICC_Profile.getInstance(profile));
- }
- return node;
- }
-
- /**
- * No-op. Profiles are never written from metadata.
- * They are written from the ColorSpace of the image.
- */
- void write(ImageOutputStream ios) throws IOException {
- // No-op
- }
-
- void print () {
- printTag("ICC Profile APP2");
- }
- }
- }
-