src/share/classes/com/sun/imageio/plugins/jpeg/JFIFMarkerSegment.java

Print this page
rev 9343 : 8033716: Fix raw and unchecked lint warnings in com.sun.imageio
Reviewed-by: darcy, prr, bae


  59 import org.w3c.dom.Node;
  60 import org.w3c.dom.NodeList;
  61 import org.w3c.dom.NamedNodeMap;
  62 
  63 /**
  64  * A JFIF (JPEG File Interchange Format) APP0 (Application-Specific)
  65  * marker segment.  Inner classes are included for JFXX extension
  66  * marker segments, for different varieties of thumbnails, and for
  67  * ICC Profile APP2 marker segments.  Any of these secondary types
  68  * that occur are kept as members of a single JFIFMarkerSegment object.
  69  */
  70 class JFIFMarkerSegment extends MarkerSegment {
  71     int majorVersion;
  72     int minorVersion;
  73     int resUnits;
  74     int Xdensity;
  75     int Ydensity;
  76     int thumbWidth;
  77     int thumbHeight;
  78     JFIFThumbRGB thumb = null;  // If present
  79     ArrayList extSegments = new ArrayList();
  80     ICCMarkerSegment iccSegment = null; // optional ICC
  81     private static final int THUMB_JPEG = 0x10;
  82     private static final int THUMB_PALETTE = 0x11;
  83     private static final int THUMB_UNASSIGNED = 0x12;
  84     private static final int THUMB_RGB = 0x13;
  85     private static final int DATA_SIZE = 14;
  86     private static final int ID_SIZE = 5;
  87     private final int MAX_THUMB_WIDTH = 255;
  88     private final int MAX_THUMB_HEIGHT = 255;
  89 
  90     private final boolean debug = false;
  91 
  92     /**
  93      * Set to <code>true</code> when reading the chunks of an
  94      * ICC profile.  All chunks are consolidated to create a single
  95      * "segment" containing all the chunks.  This flag is a state
  96      * variable identifying whether to construct a new segment or
  97      * append to an old one.
  98      */
  99     private boolean inICC = false;


 140         buffer.bufAvail -= DATA_SIZE;
 141         if (thumbWidth > 0) {
 142             thumb = new JFIFThumbRGB(buffer, thumbWidth, thumbHeight);
 143         }
 144     }
 145 
 146     /**
 147      * Constructs a JFIF header from a DOM Node.
 148      */
 149     JFIFMarkerSegment(Node node) throws IIOInvalidTreeException {
 150         this();
 151         updateFromNativeNode(node, true);
 152     }
 153 
 154     /**
 155      * Returns a deep-copy clone of this object.
 156      */
 157     protected Object clone() {
 158         JFIFMarkerSegment newGuy = (JFIFMarkerSegment) super.clone();
 159         if (!extSegments.isEmpty()) { // Clone the list with a deep copy
 160             newGuy.extSegments = new ArrayList();
 161             for (Iterator iter = extSegments.iterator(); iter.hasNext();) {
 162                 JFIFExtensionMarkerSegment jfxx =
 163                     (JFIFExtensionMarkerSegment) iter.next();
 164                 newGuy.extSegments.add(jfxx.clone());
 165             }
 166         }
 167         if (iccSegment != null) {
 168             newGuy.iccSegment = (ICCMarkerSegment) iccSegment.clone();
 169         }
 170         return newGuy;
 171     }
 172 
 173     /**
 174      * Add an JFXX extension marker segment from the stream wrapped
 175      * in the JPEGBuffer to the list of extension segments.
 176      */
 177     void addJFXX(JPEGBuffer buffer, JPEGImageReader reader)
 178         throws IOException {
 179         extSegments.add(new JFIFExtensionMarkerSegment(buffer, reader));
 180     }
 181 
 182     /**
 183      * Adds an ICC Profile APP2 segment from the stream wrapped
 184      * in the JPEGBuffer.


 213         }
 214         iccSegment = new ICCMarkerSegment(cs);
 215     }
 216 
 217     /**
 218      * Returns a tree of DOM nodes representing this object and any
 219      * subordinate JFXX extension or ICC Profile segments.
 220      */
 221     IIOMetadataNode getNativeNode() {
 222         IIOMetadataNode node = new IIOMetadataNode("app0JFIF");
 223         node.setAttribute("majorVersion", Integer.toString(majorVersion));
 224         node.setAttribute("minorVersion", Integer.toString(minorVersion));
 225         node.setAttribute("resUnits", Integer.toString(resUnits));
 226         node.setAttribute("Xdensity", Integer.toString(Xdensity));
 227         node.setAttribute("Ydensity", Integer.toString(Ydensity));
 228         node.setAttribute("thumbWidth", Integer.toString(thumbWidth));
 229         node.setAttribute("thumbHeight", Integer.toString(thumbHeight));
 230         if (!extSegments.isEmpty()) {
 231             IIOMetadataNode JFXXnode = new IIOMetadataNode("JFXX");
 232             node.appendChild(JFXXnode);
 233             for (Iterator iter = extSegments.iterator(); iter.hasNext();) {
 234                 JFIFExtensionMarkerSegment seg =
 235                     (JFIFExtensionMarkerSegment) iter.next();
 236                 JFXXnode.appendChild(seg.getNativeNode());
 237             }
 238         }
 239         if (iccSegment != null) {
 240             node.appendChild(iccSegment.getNativeNode());
 241         }
 242 
 243         return node;
 244     }
 245 
 246     /**
 247      * Updates the data in this object from the given DOM Node tree.
 248      * If fromScratch is true, this object is being constructed.
 249      * Otherwise an existing object is being modified.
 250      * Throws an IIOInvalidTreeException if the tree is invalid in
 251      * any way.
 252      */
 253     void updateFromNativeNode(Node node, boolean fromScratch)
 254         throws IIOInvalidTreeException {
 255         // none of the attributes are required


 295                     }
 296                 }
 297                 if (name.equals("app2ICC")) {
 298                     if ((iccSegment != null) && fromScratch) {
 299                         throw new IIOInvalidTreeException
 300                             ("> 1 ICC APP2 Marker Segment not supported", node);
 301                     }
 302                     iccSegment = new ICCMarkerSegment(child);
 303                 }
 304             }
 305         }
 306     }
 307 
 308     int getThumbnailWidth(int index) {
 309         if (thumb != null) {
 310             if (index == 0) {
 311                 return thumb.getWidth();
 312             }
 313             index--;
 314         }
 315         JFIFExtensionMarkerSegment jfxx =
 316             (JFIFExtensionMarkerSegment) extSegments.get(index);
 317         return jfxx.thumb.getWidth();
 318     }
 319 
 320     int getThumbnailHeight(int index) {
 321         if (thumb != null) {
 322             if (index == 0) {
 323                 return thumb.getHeight();
 324             }
 325             index--;
 326         }
 327         JFIFExtensionMarkerSegment jfxx =
 328             (JFIFExtensionMarkerSegment) extSegments.get(index);
 329         return jfxx.thumb.getHeight();
 330     }
 331 
 332     BufferedImage getThumbnail(ImageInputStream iis,
 333                                int index,
 334                                JPEGImageReader reader) throws IOException {
 335         reader.thumbnailStarted(index);
 336         BufferedImage ret = null;
 337         if ((thumb != null) && (index == 0)) {
 338                 ret = thumb.getThumbnail(iis, reader);
 339         } else {
 340             if (thumb != null) {
 341                 index--;
 342             }
 343             JFIFExtensionMarkerSegment jfxx =
 344                 (JFIFExtensionMarkerSegment) extSegments.get(index);
 345             ret = jfxx.thumb.getThumbnail(iis, reader);
 346         }
 347         reader.thumbnailComplete();
 348         return ret;
 349     }
 350 
 351 
 352     /**
 353      * Writes the data for this segment to the stream in
 354      * valid JPEG format.  Assumes that there will be no thumbnail.
 355      */
 356     void write(ImageOutputStream ios,
 357                JPEGImageWriter writer) throws IOException {
 358         // No thumbnail
 359         write(ios, null, writer);
 360     }
 361 
 362     /**
 363      * Writes the data for this segment to the stream in
 364      * valid JPEG format.  The length written takes the thumbnail


 419         }
 420         for (int i = 0; i < thumbData.length; i++) {
 421             ios.write(thumbData[i]);
 422             if ((i > progInterval) && (i % progInterval == 0)) {
 423                 writer.thumbnailProgress
 424                     (((float) i * 100) / ((float) thumbData.length));
 425             }
 426         }
 427     }
 428 
 429     /**
 430      * Write out this JFIF Marker Segment, including a thumbnail or
 431      * appending a series of JFXX Marker Segments, as appropriate.
 432      * Warnings and progress reports are sent to the writer argument.
 433      * The list of thumbnails is matched to the list of JFXX extension
 434      * segments, if any, in order to determine how to encode the
 435      * thumbnails.  If there are more thumbnails than metadata segments,
 436      * default encoding is used for the extra thumbnails.
 437      */
 438     void writeWithThumbs(ImageOutputStream ios,
 439                          List thumbnails,
 440                          JPEGImageWriter writer) throws IOException {
 441         if (thumbnails != null) {
 442             JFIFExtensionMarkerSegment jfxx = null;
 443             if (thumbnails.size() == 1) {
 444                 if (!extSegments.isEmpty()) {
 445                     jfxx = (JFIFExtensionMarkerSegment) extSegments.get(0);
 446                 }
 447                 writeThumb(ios,
 448                            (BufferedImage) thumbnails.get(0),
 449                            jfxx,
 450                            0,
 451                            true,
 452                            writer);
 453             } else {
 454                 // All others write as separate JFXX segments
 455                 write(ios, writer);  // Just the header without any thumbnail
 456                 for (int i = 0; i < thumbnails.size(); i++) {
 457                     jfxx = null;
 458                     if (i < extSegments.size()) {
 459                         jfxx = (JFIFExtensionMarkerSegment) extSegments.get(i);
 460                     }
 461                     writeThumb(ios,
 462                                (BufferedImage) thumbnails.get(i),
 463                                jfxx,
 464                                i,
 465                                false,
 466                                writer);
 467                 }
 468             }
 469         } else {  // No thumbnails
 470             write(ios, writer);
 471         }
 472 
 473     }
 474 
 475     private void writeThumb(ImageOutputStream ios,
 476                             BufferedImage thumb,
 477                             JFIFExtensionMarkerSegment jfxx,
 478                             int index,
 479                             boolean onlyOne,


 588      */
 589     private static BufferedImage expandGrayThumb(BufferedImage thumb) {
 590         BufferedImage ret = new BufferedImage(thumb.getWidth(),
 591                                               thumb.getHeight(),
 592                                               BufferedImage.TYPE_INT_RGB);
 593         Graphics g = ret.getGraphics();
 594         g.drawImage(thumb, 0, 0, null);
 595         return ret;
 596     }
 597 
 598     /**
 599      * Writes out a default JFIF marker segment to the given
 600      * output stream.  If <code>thumbnails</code> is not <code>null</code>,
 601      * writes out the set of thumbnail images as JFXX marker segments, or
 602      * incorporated into the JFIF segment if appropriate.
 603      * If <code>iccProfile</code> is not <code>null</code>,
 604      * writes out the profile after the JFIF segment using as many APP2
 605      * marker segments as necessary.
 606      */
 607     static void writeDefaultJFIF(ImageOutputStream ios,
 608                                  List thumbnails,
 609                                  ICC_Profile iccProfile,
 610                                  JPEGImageWriter writer)
 611         throws IOException {
 612 
 613         JFIFMarkerSegment jfif = new JFIFMarkerSegment();
 614         jfif.writeWithThumbs(ios, thumbnails, writer);
 615         if (iccProfile != null) {
 616             writeICC(iccProfile, ios);
 617         }
 618     }
 619 
 620     /**
 621      * Prints out the contents of this object to System.out for debugging.
 622      */
 623     void print() {
 624         printTag("JFIF");
 625         System.out.print("Version ");
 626         System.out.print(majorVersion);
 627         System.out.println(".0"
 628                            + Integer.toString(minorVersion));
 629         System.out.print("Resolution units: ");
 630         System.out.println(resUnits);
 631         System.out.print("X density: ");
 632         System.out.println(Xdensity);
 633         System.out.print("Y density: ");
 634         System.out.println(Ydensity);
 635         System.out.print("Thumbnail Width: ");
 636         System.out.println(thumbWidth);
 637         System.out.print("Thumbnail Height: ");
 638         System.out.println(thumbHeight);
 639         if (!extSegments.isEmpty()) {
 640             for (Iterator iter = extSegments.iterator(); iter.hasNext();) {
 641                 JFIFExtensionMarkerSegment extSegment =
 642                     (JFIFExtensionMarkerSegment) iter.next();
 643                 extSegment.print();
 644             }
 645         }
 646         if (iccSegment != null) {
 647             iccSegment.print();
 648         }
 649     }
 650 
 651     /**
 652      * A JFIF extension APP0 marker segment.
 653      */
 654     class JFIFExtensionMarkerSegment extends MarkerSegment {
 655         int code;
 656         JFIFThumb thumb;
 657         private static final int DATA_SIZE = 6;
 658         private static final int ID_SIZE = 5;
 659 
 660         JFIFExtensionMarkerSegment(JPEGBuffer buffer, JPEGImageReader reader)
 661             throws IOException {
 662 


1356             ios.write(0xff);
1357             ios.write(JPEG.APP2);
1358             MarkerSegment.write2bytes(ios, segLength);
1359             byte [] id = ID.getBytes("US-ASCII");
1360             ios.write(id);
1361             ios.write(0); // Null-terminate the string
1362             ios.write(chunkNum++);
1363             ios.write(numChunks);
1364             ios.write(data, offset, dataLength);
1365             offset += dataLength;
1366         }
1367     }
1368 
1369     /**
1370      * An APP2 marker segment containing an ICC profile.  In the stream
1371      * a profile larger than 64K is broken up into a series of chunks.
1372      * This inner class represents the complete profile as a single object,
1373      * combining chunks as necessary.
1374      */
1375     class ICCMarkerSegment extends MarkerSegment {
1376         ArrayList chunks = null;
1377         byte [] profile = null; // The complete profile when it's fully read
1378                          // May remain null when writing
1379         private static final int ID_SIZE = 12;
1380         int chunksRead;
1381         int numChunks;
1382 
1383         ICCMarkerSegment(ICC_ColorSpace cs) {
1384             super(JPEG.APP2);
1385             chunks = null;
1386             chunksRead = 0;
1387             numChunks = 0;
1388             profile = cs.getProfile().getData();
1389         }
1390 
1391         ICCMarkerSegment(JPEGBuffer buffer) throws IOException {
1392             super(buffer);  // gets whole segment or fills the buffer
1393             if (debug) {
1394                 System.out.println("Creating new ICC segment");
1395             }
1396             buffer.bufPtr += ID_SIZE; // Skip the id


1411                 throw new IIOException
1412                     ("Image format Error; chunk num > num chunks");
1413             }
1414 
1415             // if there are no more chunks, set up the data
1416             if (numChunks == 1) {
1417                 // reduce the stored length by the two chunk numbering bytes
1418                 length -= 2;
1419                 profile = new byte[length];
1420                 buffer.bufPtr += 2;
1421                 buffer.bufAvail-=2;
1422                 buffer.readData(profile);
1423                 inICC = false;
1424             } else {
1425                 // If we store them away, include the chunk numbering bytes
1426                 byte [] profileData = new byte[length];
1427                 // Now reduce the stored length by the
1428                 // two chunk numbering bytes
1429                 length -= 2;
1430                 buffer.readData(profileData);
1431                 chunks = new ArrayList();
1432                 chunks.add(profileData);
1433                 chunksRead = 1;
1434                 inICC = true;
1435             }
1436         }
1437 
1438         ICCMarkerSegment(Node node) throws IIOInvalidTreeException {
1439             super(JPEG.APP2);
1440             if (node instanceof IIOMetadataNode) {
1441                 IIOMetadataNode ourNode = (IIOMetadataNode) node;
1442                 ICC_Profile prof = (ICC_Profile) ourNode.getUserObject();
1443                 if (prof != null) {  // May be null
1444                     profile = prof.getData();
1445                 }
1446             }
1447         }
1448 
1449         protected Object clone () {
1450             ICCMarkerSegment newGuy = (ICCMarkerSegment) super.clone();
1451             if (profile != null) {


1501             buffer.readData(profileData);
1502             chunks.add(profileData);
1503             length += dataLen;
1504             chunksRead++;
1505             if (chunksRead < numChunks) {
1506                 inICC = true;
1507             } else {
1508                 if (debug) {
1509                     System.out.println("Completing profile; total length is "
1510                                        + length);
1511                 }
1512                 // create an array for the whole thing
1513                 profile = new byte[length];
1514                 // copy the existing chunks, releasing them
1515                 // Note that they may be out of order
1516 
1517                 int index = 0;
1518                 for (int i = 1; i <= numChunks; i++) {
1519                     boolean foundIt = false;
1520                     for (int chunk = 0; chunk < chunks.size(); chunk++) {
1521                         byte [] chunkData = (byte []) chunks.get(chunk);
1522                         if (chunkData[0] == i) { // Right one
1523                             System.arraycopy(chunkData, 2,
1524                                              profile, index,
1525                                              chunkData.length-2);
1526                             index += chunkData.length-2;
1527                             foundIt = true;
1528                         }
1529                     }
1530                     if (foundIt == false) {
1531                         throw new IIOException
1532                             ("Image Format Error: Missing ICC chunk num " + i);
1533                     }
1534                 }
1535 
1536                 chunks = null;
1537                 chunksRead = 0;
1538                 numChunks = 0;
1539                 inICC = false;
1540                 retval = true;
1541             }




  59 import org.w3c.dom.Node;
  60 import org.w3c.dom.NodeList;
  61 import org.w3c.dom.NamedNodeMap;
  62 
  63 /**
  64  * A JFIF (JPEG File Interchange Format) APP0 (Application-Specific)
  65  * marker segment.  Inner classes are included for JFXX extension
  66  * marker segments, for different varieties of thumbnails, and for
  67  * ICC Profile APP2 marker segments.  Any of these secondary types
  68  * that occur are kept as members of a single JFIFMarkerSegment object.
  69  */
  70 class JFIFMarkerSegment extends MarkerSegment {
  71     int majorVersion;
  72     int minorVersion;
  73     int resUnits;
  74     int Xdensity;
  75     int Ydensity;
  76     int thumbWidth;
  77     int thumbHeight;
  78     JFIFThumbRGB thumb = null;  // If present
  79     ArrayList<JFIFExtensionMarkerSegment> extSegments = new ArrayList<>();
  80     ICCMarkerSegment iccSegment = null; // optional ICC
  81     private static final int THUMB_JPEG = 0x10;
  82     private static final int THUMB_PALETTE = 0x11;
  83     private static final int THUMB_UNASSIGNED = 0x12;
  84     private static final int THUMB_RGB = 0x13;
  85     private static final int DATA_SIZE = 14;
  86     private static final int ID_SIZE = 5;
  87     private final int MAX_THUMB_WIDTH = 255;
  88     private final int MAX_THUMB_HEIGHT = 255;
  89 
  90     private final boolean debug = false;
  91 
  92     /**
  93      * Set to <code>true</code> when reading the chunks of an
  94      * ICC profile.  All chunks are consolidated to create a single
  95      * "segment" containing all the chunks.  This flag is a state
  96      * variable identifying whether to construct a new segment or
  97      * append to an old one.
  98      */
  99     private boolean inICC = false;


 140         buffer.bufAvail -= DATA_SIZE;
 141         if (thumbWidth > 0) {
 142             thumb = new JFIFThumbRGB(buffer, thumbWidth, thumbHeight);
 143         }
 144     }
 145 
 146     /**
 147      * Constructs a JFIF header from a DOM Node.
 148      */
 149     JFIFMarkerSegment(Node node) throws IIOInvalidTreeException {
 150         this();
 151         updateFromNativeNode(node, true);
 152     }
 153 
 154     /**
 155      * Returns a deep-copy clone of this object.
 156      */
 157     protected Object clone() {
 158         JFIFMarkerSegment newGuy = (JFIFMarkerSegment) super.clone();
 159         if (!extSegments.isEmpty()) { // Clone the list with a deep copy
 160             newGuy.extSegments = new ArrayList<>();
 161             for (Iterator<JFIFExtensionMarkerSegment> iter =
 162                     extSegments.iterator(); iter.hasNext();) {
 163                 JFIFExtensionMarkerSegment jfxx = iter.next();
 164                 newGuy.extSegments.add((JFIFExtensionMarkerSegment) jfxx.clone());
 165             }
 166         }
 167         if (iccSegment != null) {
 168             newGuy.iccSegment = (ICCMarkerSegment) iccSegment.clone();
 169         }
 170         return newGuy;
 171     }
 172 
 173     /**
 174      * Add an JFXX extension marker segment from the stream wrapped
 175      * in the JPEGBuffer to the list of extension segments.
 176      */
 177     void addJFXX(JPEGBuffer buffer, JPEGImageReader reader)
 178         throws IOException {
 179         extSegments.add(new JFIFExtensionMarkerSegment(buffer, reader));
 180     }
 181 
 182     /**
 183      * Adds an ICC Profile APP2 segment from the stream wrapped
 184      * in the JPEGBuffer.


 213         }
 214         iccSegment = new ICCMarkerSegment(cs);
 215     }
 216 
 217     /**
 218      * Returns a tree of DOM nodes representing this object and any
 219      * subordinate JFXX extension or ICC Profile segments.
 220      */
 221     IIOMetadataNode getNativeNode() {
 222         IIOMetadataNode node = new IIOMetadataNode("app0JFIF");
 223         node.setAttribute("majorVersion", Integer.toString(majorVersion));
 224         node.setAttribute("minorVersion", Integer.toString(minorVersion));
 225         node.setAttribute("resUnits", Integer.toString(resUnits));
 226         node.setAttribute("Xdensity", Integer.toString(Xdensity));
 227         node.setAttribute("Ydensity", Integer.toString(Ydensity));
 228         node.setAttribute("thumbWidth", Integer.toString(thumbWidth));
 229         node.setAttribute("thumbHeight", Integer.toString(thumbHeight));
 230         if (!extSegments.isEmpty()) {
 231             IIOMetadataNode JFXXnode = new IIOMetadataNode("JFXX");
 232             node.appendChild(JFXXnode);
 233             for (Iterator<JFIFExtensionMarkerSegment> iter =
 234                     extSegments.iterator(); iter.hasNext();) {
 235                 JFIFExtensionMarkerSegment seg = iter.next();
 236                 JFXXnode.appendChild(seg.getNativeNode());
 237             }
 238         }
 239         if (iccSegment != null) {
 240             node.appendChild(iccSegment.getNativeNode());
 241         }
 242 
 243         return node;
 244     }
 245 
 246     /**
 247      * Updates the data in this object from the given DOM Node tree.
 248      * If fromScratch is true, this object is being constructed.
 249      * Otherwise an existing object is being modified.
 250      * Throws an IIOInvalidTreeException if the tree is invalid in
 251      * any way.
 252      */
 253     void updateFromNativeNode(Node node, boolean fromScratch)
 254         throws IIOInvalidTreeException {
 255         // none of the attributes are required


 295                     }
 296                 }
 297                 if (name.equals("app2ICC")) {
 298                     if ((iccSegment != null) && fromScratch) {
 299                         throw new IIOInvalidTreeException
 300                             ("> 1 ICC APP2 Marker Segment not supported", node);
 301                     }
 302                     iccSegment = new ICCMarkerSegment(child);
 303                 }
 304             }
 305         }
 306     }
 307 
 308     int getThumbnailWidth(int index) {
 309         if (thumb != null) {
 310             if (index == 0) {
 311                 return thumb.getWidth();
 312             }
 313             index--;
 314         }
 315         JFIFExtensionMarkerSegment jfxx = extSegments.get(index);

 316         return jfxx.thumb.getWidth();
 317     }
 318 
 319     int getThumbnailHeight(int index) {
 320         if (thumb != null) {
 321             if (index == 0) {
 322                 return thumb.getHeight();
 323             }
 324             index--;
 325         }
 326         JFIFExtensionMarkerSegment jfxx = extSegments.get(index);

 327         return jfxx.thumb.getHeight();
 328     }
 329 
 330     BufferedImage getThumbnail(ImageInputStream iis,
 331                                int index,
 332                                JPEGImageReader reader) throws IOException {
 333         reader.thumbnailStarted(index);
 334         BufferedImage ret = null;
 335         if ((thumb != null) && (index == 0)) {
 336                 ret = thumb.getThumbnail(iis, reader);
 337         } else {
 338             if (thumb != null) {
 339                 index--;
 340             }
 341             JFIFExtensionMarkerSegment jfxx = extSegments.get(index);

 342             ret = jfxx.thumb.getThumbnail(iis, reader);
 343         }
 344         reader.thumbnailComplete();
 345         return ret;
 346     }
 347 
 348 
 349     /**
 350      * Writes the data for this segment to the stream in
 351      * valid JPEG format.  Assumes that there will be no thumbnail.
 352      */
 353     void write(ImageOutputStream ios,
 354                JPEGImageWriter writer) throws IOException {
 355         // No thumbnail
 356         write(ios, null, writer);
 357     }
 358 
 359     /**
 360      * Writes the data for this segment to the stream in
 361      * valid JPEG format.  The length written takes the thumbnail


 416         }
 417         for (int i = 0; i < thumbData.length; i++) {
 418             ios.write(thumbData[i]);
 419             if ((i > progInterval) && (i % progInterval == 0)) {
 420                 writer.thumbnailProgress
 421                     (((float) i * 100) / ((float) thumbData.length));
 422             }
 423         }
 424     }
 425 
 426     /**
 427      * Write out this JFIF Marker Segment, including a thumbnail or
 428      * appending a series of JFXX Marker Segments, as appropriate.
 429      * Warnings and progress reports are sent to the writer argument.
 430      * The list of thumbnails is matched to the list of JFXX extension
 431      * segments, if any, in order to determine how to encode the
 432      * thumbnails.  If there are more thumbnails than metadata segments,
 433      * default encoding is used for the extra thumbnails.
 434      */
 435     void writeWithThumbs(ImageOutputStream ios,
 436                          List<? extends BufferedImage> thumbnails,
 437                          JPEGImageWriter writer) throws IOException {
 438         if (thumbnails != null) {
 439             JFIFExtensionMarkerSegment jfxx = null;
 440             if (thumbnails.size() == 1) {
 441                 if (!extSegments.isEmpty()) {
 442                     jfxx = extSegments.get(0);
 443                 }
 444                 writeThumb(ios,
 445                            (BufferedImage) thumbnails.get(0),
 446                            jfxx,
 447                            0,
 448                            true,
 449                            writer);
 450             } else {
 451                 // All others write as separate JFXX segments
 452                 write(ios, writer);  // Just the header without any thumbnail
 453                 for (int i = 0; i < thumbnails.size(); i++) {
 454                     jfxx = null;
 455                     if (i < extSegments.size()) {
 456                         jfxx = extSegments.get(i);
 457                     }
 458                     writeThumb(ios,
 459                                (BufferedImage) thumbnails.get(i),
 460                                jfxx,
 461                                i,
 462                                false,
 463                                writer);
 464                 }
 465             }
 466         } else {  // No thumbnails
 467             write(ios, writer);
 468         }
 469 
 470     }
 471 
 472     private void writeThumb(ImageOutputStream ios,
 473                             BufferedImage thumb,
 474                             JFIFExtensionMarkerSegment jfxx,
 475                             int index,
 476                             boolean onlyOne,


 585      */
 586     private static BufferedImage expandGrayThumb(BufferedImage thumb) {
 587         BufferedImage ret = new BufferedImage(thumb.getWidth(),
 588                                               thumb.getHeight(),
 589                                               BufferedImage.TYPE_INT_RGB);
 590         Graphics g = ret.getGraphics();
 591         g.drawImage(thumb, 0, 0, null);
 592         return ret;
 593     }
 594 
 595     /**
 596      * Writes out a default JFIF marker segment to the given
 597      * output stream.  If <code>thumbnails</code> is not <code>null</code>,
 598      * writes out the set of thumbnail images as JFXX marker segments, or
 599      * incorporated into the JFIF segment if appropriate.
 600      * If <code>iccProfile</code> is not <code>null</code>,
 601      * writes out the profile after the JFIF segment using as many APP2
 602      * marker segments as necessary.
 603      */
 604     static void writeDefaultJFIF(ImageOutputStream ios,
 605                                  List<? extends BufferedImage> thumbnails,
 606                                  ICC_Profile iccProfile,
 607                                  JPEGImageWriter writer)
 608         throws IOException {
 609 
 610         JFIFMarkerSegment jfif = new JFIFMarkerSegment();
 611         jfif.writeWithThumbs(ios, thumbnails, writer);
 612         if (iccProfile != null) {
 613             writeICC(iccProfile, ios);
 614         }
 615     }
 616 
 617     /**
 618      * Prints out the contents of this object to System.out for debugging.
 619      */
 620     void print() {
 621         printTag("JFIF");
 622         System.out.print("Version ");
 623         System.out.print(majorVersion);
 624         System.out.println(".0"
 625                            + Integer.toString(minorVersion));
 626         System.out.print("Resolution units: ");
 627         System.out.println(resUnits);
 628         System.out.print("X density: ");
 629         System.out.println(Xdensity);
 630         System.out.print("Y density: ");
 631         System.out.println(Ydensity);
 632         System.out.print("Thumbnail Width: ");
 633         System.out.println(thumbWidth);
 634         System.out.print("Thumbnail Height: ");
 635         System.out.println(thumbHeight);
 636         if (!extSegments.isEmpty()) {
 637             for (Iterator<JFIFExtensionMarkerSegment> iter =
 638                     extSegments.iterator(); iter.hasNext();) {
 639                 JFIFExtensionMarkerSegment extSegment = iter.next();
 640                 extSegment.print();
 641             }
 642         }
 643         if (iccSegment != null) {
 644             iccSegment.print();
 645         }
 646     }
 647 
 648     /**
 649      * A JFIF extension APP0 marker segment.
 650      */
 651     class JFIFExtensionMarkerSegment extends MarkerSegment {
 652         int code;
 653         JFIFThumb thumb;
 654         private static final int DATA_SIZE = 6;
 655         private static final int ID_SIZE = 5;
 656 
 657         JFIFExtensionMarkerSegment(JPEGBuffer buffer, JPEGImageReader reader)
 658             throws IOException {
 659 


1353             ios.write(0xff);
1354             ios.write(JPEG.APP2);
1355             MarkerSegment.write2bytes(ios, segLength);
1356             byte [] id = ID.getBytes("US-ASCII");
1357             ios.write(id);
1358             ios.write(0); // Null-terminate the string
1359             ios.write(chunkNum++);
1360             ios.write(numChunks);
1361             ios.write(data, offset, dataLength);
1362             offset += dataLength;
1363         }
1364     }
1365 
1366     /**
1367      * An APP2 marker segment containing an ICC profile.  In the stream
1368      * a profile larger than 64K is broken up into a series of chunks.
1369      * This inner class represents the complete profile as a single object,
1370      * combining chunks as necessary.
1371      */
1372     class ICCMarkerSegment extends MarkerSegment {
1373         ArrayList<byte[]> chunks = null;
1374         byte [] profile = null; // The complete profile when it's fully read
1375                          // May remain null when writing
1376         private static final int ID_SIZE = 12;
1377         int chunksRead;
1378         int numChunks;
1379 
1380         ICCMarkerSegment(ICC_ColorSpace cs) {
1381             super(JPEG.APP2);
1382             chunks = null;
1383             chunksRead = 0;
1384             numChunks = 0;
1385             profile = cs.getProfile().getData();
1386         }
1387 
1388         ICCMarkerSegment(JPEGBuffer buffer) throws IOException {
1389             super(buffer);  // gets whole segment or fills the buffer
1390             if (debug) {
1391                 System.out.println("Creating new ICC segment");
1392             }
1393             buffer.bufPtr += ID_SIZE; // Skip the id


1408                 throw new IIOException
1409                     ("Image format Error; chunk num > num chunks");
1410             }
1411 
1412             // if there are no more chunks, set up the data
1413             if (numChunks == 1) {
1414                 // reduce the stored length by the two chunk numbering bytes
1415                 length -= 2;
1416                 profile = new byte[length];
1417                 buffer.bufPtr += 2;
1418                 buffer.bufAvail-=2;
1419                 buffer.readData(profile);
1420                 inICC = false;
1421             } else {
1422                 // If we store them away, include the chunk numbering bytes
1423                 byte [] profileData = new byte[length];
1424                 // Now reduce the stored length by the
1425                 // two chunk numbering bytes
1426                 length -= 2;
1427                 buffer.readData(profileData);
1428                 chunks = new ArrayList<>();
1429                 chunks.add(profileData);
1430                 chunksRead = 1;
1431                 inICC = true;
1432             }
1433         }
1434 
1435         ICCMarkerSegment(Node node) throws IIOInvalidTreeException {
1436             super(JPEG.APP2);
1437             if (node instanceof IIOMetadataNode) {
1438                 IIOMetadataNode ourNode = (IIOMetadataNode) node;
1439                 ICC_Profile prof = (ICC_Profile) ourNode.getUserObject();
1440                 if (prof != null) {  // May be null
1441                     profile = prof.getData();
1442                 }
1443             }
1444         }
1445 
1446         protected Object clone () {
1447             ICCMarkerSegment newGuy = (ICCMarkerSegment) super.clone();
1448             if (profile != null) {


1498             buffer.readData(profileData);
1499             chunks.add(profileData);
1500             length += dataLen;
1501             chunksRead++;
1502             if (chunksRead < numChunks) {
1503                 inICC = true;
1504             } else {
1505                 if (debug) {
1506                     System.out.println("Completing profile; total length is "
1507                                        + length);
1508                 }
1509                 // create an array for the whole thing
1510                 profile = new byte[length];
1511                 // copy the existing chunks, releasing them
1512                 // Note that they may be out of order
1513 
1514                 int index = 0;
1515                 for (int i = 1; i <= numChunks; i++) {
1516                     boolean foundIt = false;
1517                     for (int chunk = 0; chunk < chunks.size(); chunk++) {
1518                         byte [] chunkData = chunks.get(chunk);
1519                         if (chunkData[0] == i) { // Right one
1520                             System.arraycopy(chunkData, 2,
1521                                              profile, index,
1522                                              chunkData.length-2);
1523                             index += chunkData.length-2;
1524                             foundIt = true;
1525                         }
1526                     }
1527                     if (foundIt == false) {
1528                         throw new IIOException
1529                             ("Image Format Error: Missing ICC chunk num " + i);
1530                     }
1531                 }
1532 
1533                 chunks = null;
1534                 chunksRead = 0;
1535                 numChunks = 0;
1536                 inICC = false;
1537                 retval = true;
1538             }