Source Code Cross Referenced for CLibJPEGMetadata.java in  » 6.0-JDK-Modules » Java-Advanced-Imaging » com » sun » media » imageioimpl » plugins » jpeg » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » Java Advanced Imaging » com.sun.media.imageioimpl.plugins.jpeg 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $RCSfile: CLibJPEGMetadata.java,v $
0003:         *
0004:         * 
0005:         * Copyright (c) 2006 Sun Microsystems, Inc. All  Rights Reserved.
0006:         * 
0007:         * Redistribution and use in source and binary forms, with or without
0008:         * modification, are permitted provided that the following conditions
0009:         * are met: 
0010:         * 
0011:         * - Redistribution of source code must retain the above copyright 
0012:         *   notice, this  list of conditions and the following disclaimer.
0013:         * 
0014:         * - Redistribution in binary form must reproduce the above copyright
0015:         *   notice, this list of conditions and the following disclaimer in 
0016:         *   the documentation and/or other materials provided with the
0017:         *   distribution.
0018:         * 
0019:         * Neither the name of Sun Microsystems, Inc. or the names of 
0020:         * contributors may be used to endorse or promote products derived 
0021:         * from this software without specific prior written permission.
0022:         * 
0023:         * This software is provided "AS IS," without a warranty of any 
0024:         * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
0025:         * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
0026:         * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0027:         * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
0028:         * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
0029:         * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0030:         * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
0031:         * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0032:         * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0033:         * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0034:         * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0035:         * POSSIBILITY OF SUCH DAMAGES. 
0036:         * 
0037:         * You acknowledge that this software is not designed or intended for 
0038:         * use in the design, construction, operation or maintenance of any 
0039:         * nuclear facility. 
0040:         *
0041:         * $Revision: 1.7 $
0042:         * $Date: 2007/08/28 18:45:53 $
0043:         * $State: Exp $
0044:         */
0045:
0046:        package com.sun.media.imageioimpl.plugins.jpeg;
0047:
0048:        import java.awt.Dimension;
0049:        import java.awt.Transparency;
0050:        import java.awt.color.ColorSpace;
0051:        import java.awt.color.ICC_Profile;
0052:        import java.awt.image.BufferedImage;
0053:        import java.awt.image.ColorModel;
0054:        import java.awt.image.ComponentColorModel;
0055:        import java.awt.image.DataBuffer;
0056:        import java.awt.image.DataBufferByte;
0057:        import java.awt.image.IndexColorModel;
0058:        import java.awt.image.Raster;
0059:        import java.awt.image.RenderedImage;
0060:        import java.awt.image.SampleModel;
0061:        import java.awt.image.WritableRaster;
0062:        import java.io.ByteArrayInputStream;
0063:        import java.io.EOFException;
0064:        import java.io.IOException;
0065:        import java.io.UnsupportedEncodingException;
0066:        import java.nio.ByteOrder;
0067:        import java.util.ArrayList;
0068:        import java.util.Arrays;
0069:        import java.util.Iterator;
0070:        import java.util.List;
0071:        import java.util.Map;
0072:        import java.util.SortedMap;
0073:        import java.util.TreeMap;
0074:        import javax.imageio.IIOException;
0075:        import javax.imageio.IIOImage;
0076:        import javax.imageio.ImageIO;
0077:        import javax.imageio.ImageReader;
0078:        import javax.imageio.ImageTypeSpecifier;
0079:        import javax.imageio.metadata.IIOMetadata;
0080:        import javax.imageio.metadata.IIOMetadataFormatImpl;
0081:        import javax.imageio.metadata.IIOMetadataNode;
0082:        import javax.imageio.metadata.IIOInvalidTreeException;
0083:        import javax.imageio.plugins.jpeg.JPEGHuffmanTable;
0084:        import javax.imageio.plugins.jpeg.JPEGQTable;
0085:        import javax.imageio.stream.ImageInputStream;
0086:        import javax.imageio.stream.MemoryCacheImageInputStream;
0087:        import org.w3c.dom.Node;
0088:        import org.w3c.dom.NodeList;
0089:        import com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet;
0090:        import com.sun.media.imageio.plugins.tiff.EXIFGPSTagSet;
0091:        import com.sun.media.imageio.plugins.tiff.EXIFInteroperabilityTagSet;
0092:        import com.sun.media.imageio.plugins.tiff.EXIFParentTIFFTagSet;
0093:        import com.sun.media.imageio.plugins.tiff.EXIFTIFFTagSet;
0094:        import com.sun.media.imageio.plugins.tiff.TIFFDirectory;
0095:        import com.sun.media.imageio.plugins.tiff.TIFFField;
0096:        import com.sun.media.imageio.plugins.tiff.TIFFTag;
0097:        import com.sun.media.imageio.plugins.tiff.TIFFTagSet;
0098:
0099:        public class CLibJPEGMetadata extends IIOMetadata {
0100:            // --- Constants ---
0101:
0102:            static final String NATIVE_FORMAT = "javax_imageio_jpeg_image_1.0";
0103:            // XXX Reference to a non-API J2SE class:
0104:            static final String NATIVE_FORMAT_CLASS = "com.sun.imageio.plugins.jpeg.JPEGImageMetadataFormat";
0105:
0106:            static final String TIFF_FORMAT = "com_sun_media_imageio_plugins_tiff_image_1.0";
0107:            static final String TIFF_FORMAT_CLASS = "com.sun.media.imageioimpl.plugins.tiff.TIFFImageMetadataFormat";
0108:
0109:            // Marker codes from J2SE in numerically increasing order.
0110:
0111:            /** For temporary use in arithmetic coding */
0112:            static final int TEM = 0x01;
0113:
0114:            // Codes 0x02 - 0xBF are reserved
0115:
0116:            // SOF markers for Nondifferential Huffman coding
0117:            /** Baseline DCT */
0118:            static final int SOF0 = 0xC0;
0119:            /** Extended Sequential DCT */
0120:            static final int SOF1 = 0xC1;
0121:            /** Progressive DCT */
0122:            static final int SOF2 = 0xC2;
0123:            /** Lossless Sequential */
0124:            static final int SOF3 = 0xC3;
0125:
0126:            /** Define Huffman Tables */
0127:            static final int DHT = 0xC4;
0128:
0129:            // SOF markers for Differential Huffman coding
0130:            /** Differential Sequential DCT */
0131:            static final int SOF5 = 0xC5;
0132:            /** Differential Progressive DCT */
0133:            static final int SOF6 = 0xC6;
0134:            /** Differential Lossless */
0135:            static final int SOF7 = 0xC7;
0136:
0137:            /** Reserved for JPEG extensions */
0138:            static final int JPG = 0xC8;
0139:
0140:            // SOF markers for Nondifferential arithmetic coding
0141:            /** Extended Sequential DCT, Arithmetic coding */
0142:            static final int SOF9 = 0xC9;
0143:            /** Progressive DCT, Arithmetic coding */
0144:            static final int SOF10 = 0xCA;
0145:            /** Lossless Sequential, Arithmetic coding */
0146:            static final int SOF11 = 0xCB;
0147:
0148:            /** Define Arithmetic conditioning tables */
0149:            static final int DAC = 0xCC;
0150:
0151:            // SOF markers for Differential arithmetic coding
0152:            /** Differential Sequential DCT, Arithmetic coding */
0153:            static final int SOF13 = 0xCD;
0154:            /** Differential Progressive DCT, Arithmetic coding */
0155:            static final int SOF14 = 0xCE;
0156:            /** Differential Lossless, Arithmetic coding */
0157:            static final int SOF15 = 0xCF;
0158:
0159:            // Restart Markers
0160:            static final int RST0 = 0xD0;
0161:            static final int RST1 = 0xD1;
0162:            static final int RST2 = 0xD2;
0163:            static final int RST3 = 0xD3;
0164:            static final int RST4 = 0xD4;
0165:            static final int RST5 = 0xD5;
0166:            static final int RST6 = 0xD6;
0167:            static final int RST7 = 0xD7;
0168:            /** Number of restart markers */
0169:            static final int RESTART_RANGE = 8;
0170:
0171:            /** Start of Image */
0172:            static final int SOI = 0xD8;
0173:            /** End of Image */
0174:            static final int EOI = 0xD9;
0175:            /** Start of Scan */
0176:            static final int SOS = 0xDA;
0177:
0178:            /** Define Quantisation Tables */
0179:            static final int DQT = 0xDB;
0180:
0181:            /** Define Number of lines */
0182:            static final int DNL = 0xDC;
0183:
0184:            /** Define Restart Interval */
0185:            static final int DRI = 0xDD;
0186:
0187:            /** Define Heirarchical progression */
0188:            static final int DHP = 0xDE;
0189:
0190:            /** Expand reference image(s) */
0191:            static final int EXP = 0xDF;
0192:
0193:            // Application markers
0194:            /** APP0 used by JFIF */
0195:            static final int APP0 = 0xE0;
0196:            static final int APP1 = 0xE1;
0197:            static final int APP2 = 0xE2;
0198:            static final int APP3 = 0xE3;
0199:            static final int APP4 = 0xE4;
0200:            static final int APP5 = 0xE5;
0201:            static final int APP6 = 0xE6;
0202:            static final int APP7 = 0xE7;
0203:            static final int APP8 = 0xE8;
0204:            static final int APP9 = 0xE9;
0205:            static final int APP10 = 0xEA;
0206:            static final int APP11 = 0xEB;
0207:            static final int APP12 = 0xEC;
0208:            static final int APP13 = 0xED;
0209:            /** APP14 used by Adobe */
0210:            static final int APP14 = 0xEE;
0211:            static final int APP15 = 0xEF;
0212:
0213:            // codes 0xF0 to 0xFD are reserved
0214:
0215:            /** Comment marker */
0216:            static final int COM = 0xFE;
0217:
0218:            // Marker codes for JPEG-LS
0219:
0220:            /** JPEG-LS SOF marker */
0221:            // This was SOF48 in an earlier revision of the JPEG-LS specification.
0222:            // "55" is the numerical value of SOF55 - SOF0 (= 247 - 192).
0223:            static final int SOF55 = 0xF7;
0224:
0225:            /** JPEG-LS parameters */
0226:            static final int LSE = 0xF2;
0227:
0228:            // Min and max APPn codes.
0229:            static final int APPN_MIN = APP0;
0230:            static final int APPN_MAX = APP15;
0231:
0232:            // Min and max contiguous SOFn codes.
0233:            static final int SOFN_MIN = SOF0;
0234:            static final int SOFN_MAX = SOF15;
0235:
0236:            // Min and Max RSTn codes.
0237:            static final int RST_MIN = RST0;
0238:            static final int RST_MAX = RST7;
0239:
0240:            // Specific segment types defined as (code << 8) | X.
0241:            static final int APP0_JFIF = (APP0 << 8) | 0;
0242:            static final int APP0_JFXX = (APP0 << 8) | 1;
0243:            static final int APP1_EXIF = (APP1 << 8) | 0;
0244:            static final int APP2_ICC = (APP2 << 8) | 0;
0245:            static final int APP14_ADOBE = (APP14 << 8) | 0;
0246:            static final int UNKNOWN_MARKER = 0xffff;
0247:            static final int SOF_MARKER = (SOF0 << 8) | 0;
0248:
0249:            // Resolution unit types.
0250:            static final int JFIF_RESUNITS_ASPECT = 0;
0251:            static final int JFIF_RESUNITS_DPI = 1;
0252:            static final int JFIF_RESUNITS_DPC = 2;
0253:
0254:            // Thumbnail types
0255:            static final int THUMBNAIL_JPEG = 0x10;
0256:            static final int THUMBNAIL_PALETTE = 0x11;
0257:            static final int THUMBNAIL_RGB = 0x12;
0258:
0259:            // Adobe transform type.
0260:            static final int ADOBE_TRANSFORM_UNKNOWN = 0;
0261:            static final int ADOBE_TRANSFORM_YCC = 1;
0262:            static final int ADOBE_TRANSFORM_YCCK = 2;
0263:
0264:            // Zig-zag to natural re-ordering array.
0265:            static final int[] zigzag = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7,
0266:                    13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11,
0267:                    18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20,
0268:                    22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61,
0269:                    35, 36, 48, 49, 57, 58, 62, 63 };
0270:
0271:            // --- Static methods ---
0272:
0273:            private static IIOImage getThumbnail(ImageInputStream stream,
0274:                    int len, int thumbnailType, int w, int h)
0275:                    throws IOException {
0276:
0277:                IIOImage result;
0278:
0279:                long startPos = stream.getStreamPosition();
0280:
0281:                if (thumbnailType == THUMBNAIL_JPEG) {
0282:                    Iterator readers = ImageIO.getImageReaders(stream);
0283:                    if (readers == null || !readers.hasNext())
0284:                        return null;
0285:                    ImageReader reader = (ImageReader) readers.next();
0286:                    reader.setInput(stream);
0287:                    BufferedImage image = reader.read(0, null);
0288:                    IIOMetadata metadata = null;
0289:                    try {
0290:                        metadata = reader.getImageMetadata(0);
0291:                    } catch (Exception e) {
0292:                        // Ignore it
0293:                    }
0294:                    result = new IIOImage(image, null, metadata);
0295:                } else {
0296:                    int numBands;
0297:                    ColorModel cm;
0298:                    if (thumbnailType == THUMBNAIL_PALETTE) {
0299:                        if (len < 768 + w * h) {
0300:                            return null;
0301:                        }
0302:
0303:                        numBands = 1;
0304:
0305:                        byte[] palette = new byte[768];
0306:                        stream.readFully(palette);
0307:                        byte[] r = new byte[256];
0308:                        byte[] g = new byte[256];
0309:                        byte[] b = new byte[256];
0310:                        for (int i = 0, off = 0; i < 256; i++) {
0311:                            r[i] = palette[off++];
0312:                            g[i] = palette[off++];
0313:                            b[i] = palette[off++];
0314:                        }
0315:
0316:                        cm = new IndexColorModel(8, 256, r, g, b);
0317:                    } else {
0318:                        if (len < 3 * w * h) {
0319:                            return null;
0320:                        }
0321:
0322:                        numBands = 3;
0323:
0324:                        ColorSpace cs = ColorSpace
0325:                                .getInstance(ColorSpace.CS_sRGB);
0326:                        cm = new ComponentColorModel(cs, false, false,
0327:                                Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
0328:                    }
0329:
0330:                    byte[] data = new byte[w * h * numBands];
0331:                    stream.readFully(data);
0332:                    DataBufferByte db = new DataBufferByte(data, data.length);
0333:                    WritableRaster wr = Raster.createInterleavedRaster(db, w,
0334:                            h, w * numBands, numBands, new int[] { 0, 1, 2 },
0335:                            null);
0336:                    BufferedImage image = new BufferedImage(cm, wr, false, null);
0337:                    result = new IIOImage(image, null, null);
0338:                }
0339:
0340:                stream.seek(startPos + len);
0341:
0342:                return result;
0343:            }
0344:
0345:            // --- Instance variables ---
0346:
0347:            /** Whether the object may be edited. */
0348:            private boolean isReadOnly = true;
0349:
0350:            // APP0 JFIF marker segment parameters.
0351:            boolean app0JFIFPresent;
0352:            int majorVersion = 1;
0353:            int minorVersion = 2;
0354:            int resUnits; // (0 = aspect ratio; 1 = dots/inch; 2 = dots/cm)
0355:            int Xdensity = 1;
0356:            int Ydensity = 1;
0357:            int thumbWidth = 0;
0358:            int thumbHeight = 0;
0359:            BufferedImage jfifThumbnail;
0360:
0361:            // APP0 JFIF thumbnail(s).
0362:            boolean app0JFXXPresent;
0363:            List extensionCodes; // Integers 0x10, 0x11, 0x12
0364:            List jfxxThumbnails; // IIOImages
0365:
0366:            // APP2 ICC_PROFILE marker segment parameters.
0367:            boolean app2ICCPresent;
0368:            ICC_Profile profile = null;
0369:
0370:            // DQT marker segment parameters.
0371:            boolean dqtPresent;
0372:            List qtables; // Each element is a List of QTables
0373:
0374:            // DHT marker segment parameters.
0375:            boolean dhtPresent;
0376:            List htables; // Each element is a List of HuffmanTables
0377:
0378:            // DRI marker segment parameters.
0379:            boolean driPresent;
0380:            int driInterval;
0381:
0382:            // COM marker segment parameters.
0383:            boolean comPresent;
0384:            List comments; // byte[]s
0385:
0386:            // Unknown marker segment parameters.
0387:            boolean unknownPresent;
0388:            List markerTags; // Integers
0389:            List unknownData; // byte[] (NB: 'length' parameter is array length)
0390:
0391:            // APP14 Adobe marker segment parameters.
0392:            boolean app14AdobePresent;
0393:            int version = 100;
0394:            int flags0 = 0;
0395:            int flags1 = 0;
0396:            int transform; // 0 = Unknown, 1 = YCbCr, 2 = YCCK
0397:
0398:            // SOF marker segment parameters.
0399:            boolean sofPresent;
0400:            int sofProcess;
0401:            int samplePrecision = 8;
0402:            int numLines;
0403:            int samplesPerLine;
0404:            int numFrameComponents;
0405:            int[] componentId;
0406:            int[] hSamplingFactor;
0407:            int[] vSamplingFactor;
0408:            int[] qtableSelector;
0409:
0410:            // SOS marker segment parameters.
0411:            boolean sosPresent;
0412:            int numScanComponents;
0413:            int[] componentSelector;
0414:            int[] dcHuffTable;
0415:            int[] acHuffTable;
0416:            int startSpectralSelection;
0417:            int endSpectralSelection;
0418:            int approxHigh;
0419:            int approxLow;
0420:
0421:            // Embedded TIFF stream from EXIF segment.
0422:            byte[] exifData = null;
0423:
0424:            /** Marker codes in the order encountered. */
0425:            private List markers = null; // List of Integer
0426:
0427:            // Standard metadata variables.
0428:            private boolean hasAlpha = false;
0429:
0430:            // Agregated list of thumbnails: JFIF > JFXX > EXIF.
0431:            private boolean thumbnailsInitialized = false;
0432:            private List thumbnails = new ArrayList();
0433:
0434:            CLibJPEGMetadata() {
0435:                super (true, NATIVE_FORMAT, NATIVE_FORMAT_CLASS,
0436:                        new String[] { TIFF_FORMAT },
0437:                        new String[] { TIFF_FORMAT_CLASS });
0438:
0439:                this .isReadOnly = isReadOnly;
0440:            }
0441:
0442:            CLibJPEGMetadata(ImageInputStream stream) throws IIOException {
0443:                this ();
0444:
0445:                try {
0446:                    initializeFromStream(stream);
0447:                } catch (IOException e) {
0448:                    throw new IIOException("Cannot initialize JPEG metadata!",
0449:                            e);
0450:                }
0451:            }
0452:
0453:            private class QTable {
0454:                private static final int QTABLE_SIZE = 64;
0455:
0456:                int elementPrecision;
0457:                int tableID;
0458:                JPEGQTable table;
0459:
0460:                int length;
0461:
0462:                QTable(ImageInputStream stream) throws IOException {
0463:                    elementPrecision = (int) stream.readBits(4);
0464:                    tableID = (int) stream.readBits(4);
0465:                    byte[] tmp = new byte[QTABLE_SIZE];
0466:                    stream.readFully(tmp);
0467:                    int[] data = new int[QTABLE_SIZE];
0468:                    for (int i = 0; i < QTABLE_SIZE; i++) {
0469:                        data[i] = tmp[zigzag[i]] & 0xff;
0470:                    }
0471:                    table = new JPEGQTable(data);
0472:                    length = data.length + 1;
0473:                }
0474:            }
0475:
0476:            private class HuffmanTable {
0477:                private static final int NUM_LENGTHS = 16;
0478:
0479:                int tableClass;
0480:                int tableID;
0481:                JPEGHuffmanTable table;
0482:
0483:                int length;
0484:
0485:                HuffmanTable(ImageInputStream stream) throws IOException {
0486:                    tableClass = (int) stream.readBits(4);
0487:                    tableID = (int) stream.readBits(4);
0488:                    short[] lengths = new short[NUM_LENGTHS];
0489:                    for (int i = 0; i < NUM_LENGTHS; i++) {
0490:                        lengths[i] = (short) stream.read();
0491:                    }
0492:                    int numValues = 0;
0493:                    for (int i = 0; i < NUM_LENGTHS; i++) {
0494:                        numValues += lengths[i];
0495:                    }
0496:                    short[] values = new short[numValues];
0497:                    for (int i = 0; i < numValues; i++) {
0498:                        values[i] = (short) stream.read();
0499:                    }
0500:                    table = new JPEGHuffmanTable(lengths, values);
0501:
0502:                    length = 1 + NUM_LENGTHS + values.length;
0503:                }
0504:            }
0505:
0506:            private synchronized void initializeFromStream(ImageInputStream iis)
0507:                    throws IOException {
0508:                iis.mark();
0509:                iis.setByteOrder(ByteOrder.BIG_ENDIAN);
0510:
0511:                markers = new ArrayList();
0512:
0513:                boolean isICCProfileValid = true;
0514:                int numICCProfileChunks = 0;
0515:                long[] iccProfileChunkOffsets = null;
0516:                int[] iccProfileChunkLengths = null;
0517:
0518:                while (true) {
0519:                    try {
0520:                        // 0xff denotes a potential marker.
0521:                        if (iis.read() == 0xff) {
0522:                            // Get next byte.
0523:                            int code = iis.read();
0524:
0525:                            // Is a marker if and only if code not in {0x00, 0xff}.
0526:                            // Continue to next marker if this is not a marker or if
0527:                            // it is an empty marker.
0528:                            if (code == 0x00 || code == 0xff || code == SOI
0529:                                    || code == TEM
0530:                                    || (code >= RST_MIN && code <= RST_MAX)) {
0531:                                continue;
0532:                            }
0533:
0534:                            // If at the end, quit.
0535:                            if (code == EOI) {
0536:                                break;
0537:                            }
0538:
0539:                            // Get the content length.
0540:                            int dataLength = iis.readUnsignedShort() - 2;
0541:
0542:                            if (APPN_MIN <= code && code <= APPN_MAX) {
0543:                                long pos = iis.getStreamPosition();
0544:                                boolean appnAdded = false;
0545:
0546:                                switch (code) {
0547:                                case APP0:
0548:                                    if (dataLength >= 5) {
0549:                                        byte[] b = new byte[5];
0550:                                        iis.readFully(b);
0551:                                        String id = new String(b);
0552:                                        if (id.startsWith("JFIF")
0553:                                                && !app0JFIFPresent) {
0554:                                            app0JFIFPresent = true;
0555:                                            markers.add(new Integer(APP0_JFIF));
0556:                                            majorVersion = iis.read();
0557:                                            minorVersion = iis.read();
0558:                                            resUnits = iis.read();
0559:                                            Xdensity = iis.readUnsignedShort();
0560:                                            Ydensity = iis.readUnsignedShort();
0561:                                            thumbWidth = iis.read();
0562:                                            thumbHeight = iis.read();
0563:                                            if (thumbWidth > 0
0564:                                                    && thumbHeight > 0) {
0565:                                                IIOImage imiio = getThumbnail(
0566:                                                        iis, dataLength - 14,
0567:                                                        THUMBNAIL_RGB,
0568:                                                        thumbWidth, thumbHeight);
0569:                                                if (imiio != null) {
0570:                                                    jfifThumbnail = (BufferedImage) imiio
0571:                                                            .getRenderedImage();
0572:                                                }
0573:                                            }
0574:                                            appnAdded = true;
0575:                                        } else if (id.startsWith("JFXX")) {
0576:                                            if (!app0JFXXPresent) {
0577:                                                extensionCodes = new ArrayList(
0578:                                                        1);
0579:                                                jfxxThumbnails = new ArrayList(
0580:                                                        1);
0581:                                                app0JFXXPresent = true;
0582:                                            }
0583:                                            markers.add(new Integer(APP0_JFXX));
0584:                                            int extCode = iis.read();
0585:                                            extensionCodes.add(new Integer(
0586:                                                    extCode));
0587:                                            int w = 0, h = 0, offset = 6;
0588:                                            if (extCode != THUMBNAIL_JPEG) {
0589:                                                w = iis.read();
0590:                                                h = iis.read();
0591:                                                offset += 2;
0592:                                            }
0593:                                            IIOImage imiio = getThumbnail(iis,
0594:                                                    dataLength - offset,
0595:                                                    extCode, w, h);
0596:                                            if (imiio != null) {
0597:                                                jfxxThumbnails.add(imiio);
0598:                                            }
0599:                                            appnAdded = true;
0600:                                        }
0601:                                    }
0602:                                    break;
0603:                                case APP1:
0604:                                    if (dataLength >= 6) {
0605:                                        byte[] b = new byte[6];
0606:                                        iis.readFully(b);
0607:                                        if (b[0] == (byte) 'E'
0608:                                                && b[1] == (byte) 'x'
0609:                                                && b[2] == (byte) 'i'
0610:                                                && b[3] == (byte) 'f'
0611:                                                && b[4] == (byte) 0
0612:                                                && b[5] == (byte) 0) {
0613:                                            exifData = new byte[dataLength - 6];
0614:                                            iis.readFully(exifData);
0615:                                        }
0616:                                    }
0617:                                case APP2:
0618:                                    if (dataLength >= 12) {
0619:                                        byte[] b = new byte[12];
0620:                                        iis.readFully(b);
0621:                                        String id = new String(b);
0622:                                        if (id.startsWith("ICC_PROFILE")) {
0623:                                            if (!isICCProfileValid) {
0624:                                                iis.skipBytes(dataLength - 12);
0625:                                                continue;
0626:                                            }
0627:
0628:                                            int chunkNum = iis.read();
0629:                                            int numChunks = iis.read();
0630:                                            if (numChunks == 0
0631:                                                    || chunkNum == 0
0632:                                                    || chunkNum > numChunks
0633:                                                    || (app2ICCPresent && (numChunks != numICCProfileChunks || iccProfileChunkOffsets[chunkNum] != 0L))) {
0634:                                                isICCProfileValid = false;
0635:                                                iis.skipBytes(dataLength - 14);
0636:                                                continue;
0637:                                            }
0638:
0639:                                            if (!app2ICCPresent) {
0640:                                                app2ICCPresent = true;
0641:                                                // Only flag one marker even though
0642:                                                // multiple may be present.
0643:                                                markers.add(new Integer(
0644:                                                        APP2_ICC));
0645:
0646:                                                numICCProfileChunks = numChunks;
0647:
0648:                                                if (numChunks == 1) {
0649:                                                    b = new byte[dataLength - 14];
0650:                                                    iis.readFully(b);
0651:                                                    profile = ICC_Profile
0652:                                                            .getInstance(b);
0653:                                                } else {
0654:                                                    iccProfileChunkOffsets = new long[numChunks + 1];
0655:                                                    iccProfileChunkLengths = new int[numChunks + 1];
0656:                                                    iccProfileChunkOffsets[chunkNum] = iis
0657:                                                            .getStreamPosition();
0658:                                                    iccProfileChunkLengths[chunkNum] = dataLength - 14;
0659:                                                    iis
0660:                                                            .skipBytes(dataLength - 14);
0661:                                                }
0662:                                            } else {
0663:                                                iccProfileChunkOffsets[chunkNum] = iis
0664:                                                        .getStreamPosition();
0665:                                                iccProfileChunkLengths[chunkNum] = dataLength - 14;
0666:                                                iis.skipBytes(dataLength - 14);
0667:                                            }
0668:
0669:                                            appnAdded = true;
0670:                                        }
0671:                                    }
0672:                                    break;
0673:                                case APP14:
0674:                                    if (dataLength >= 5) {
0675:                                        byte[] b = new byte[5];
0676:                                        iis.readFully(b);
0677:                                        String id = new String(b);
0678:                                        if (id.startsWith("Adobe")
0679:                                                && !app14AdobePresent) { // Adobe segment
0680:                                            app14AdobePresent = true;
0681:                                            markers
0682:                                                    .add(new Integer(
0683:                                                            APP14_ADOBE));
0684:                                            version = iis.readUnsignedShort();
0685:                                            flags0 = iis.readUnsignedShort();
0686:                                            flags1 = iis.readUnsignedShort();
0687:                                            transform = iis.read();
0688:                                            iis.skipBytes(dataLength - 12);
0689:                                            appnAdded = true;
0690:                                        }
0691:                                    }
0692:                                    break;
0693:                                default:
0694:                                    appnAdded = false;
0695:                                    break;
0696:                                }
0697:
0698:                                if (!appnAdded) {
0699:                                    iis.seek(pos);
0700:                                    addUnknownMarkerSegment(iis, code,
0701:                                            dataLength);
0702:                                }
0703:                            } else if (code == DQT) {
0704:                                if (!dqtPresent) {
0705:                                    dqtPresent = true;
0706:                                    qtables = new ArrayList(1);
0707:                                }
0708:                                markers.add(new Integer(DQT));
0709:                                List l = new ArrayList(1);
0710:                                do {
0711:                                    QTable t = new QTable(iis);
0712:                                    l.add(t);
0713:                                    dataLength -= t.length;
0714:                                } while (dataLength > 0);
0715:                                qtables.add(l);
0716:                            } else if (code == DHT) {
0717:                                if (!dhtPresent) {
0718:                                    dhtPresent = true;
0719:                                    htables = new ArrayList(1);
0720:                                }
0721:                                markers.add(new Integer(DHT));
0722:                                List l = new ArrayList(1);
0723:                                do {
0724:                                    HuffmanTable t = new HuffmanTable(iis);
0725:                                    l.add(t);
0726:                                    dataLength -= t.length;
0727:                                } while (dataLength > 0);
0728:                                htables.add(l);
0729:                            } else if (code == DRI) {
0730:                                if (!driPresent) {
0731:                                    driPresent = true;
0732:                                }
0733:                                markers.add(new Integer(DRI));
0734:                                driInterval = iis.readUnsignedShort();
0735:                            } else if (code == COM) {
0736:                                if (!comPresent) {
0737:                                    comPresent = true;
0738:                                    comments = new ArrayList(1);
0739:                                }
0740:                                markers.add(new Integer(COM));
0741:                                byte[] b = new byte[dataLength];
0742:                                iis.readFully(b);
0743:                                comments.add(b);
0744:                            } else if ((code >= SOFN_MIN && code <= SOFN_MAX)
0745:                                    || code == SOF55) { // SOFn
0746:                                if (!sofPresent) {
0747:                                    sofPresent = true;
0748:                                    sofProcess = code - SOFN_MIN;
0749:                                    samplePrecision = iis.read();
0750:                                    numLines = iis.readUnsignedShort();
0751:                                    samplesPerLine = iis.readUnsignedShort();
0752:                                    numFrameComponents = iis.read();
0753:                                    componentId = new int[numFrameComponents];
0754:                                    hSamplingFactor = new int[numFrameComponents];
0755:                                    vSamplingFactor = new int[numFrameComponents];
0756:                                    qtableSelector = new int[numFrameComponents];
0757:                                    for (int i = 0; i < numFrameComponents; i++) {
0758:                                        componentId[i] = iis.read();
0759:                                        hSamplingFactor[i] = (int) iis
0760:                                                .readBits(4);
0761:                                        vSamplingFactor[i] = (int) iis
0762:                                                .readBits(4);
0763:                                        qtableSelector[i] = iis.read();
0764:                                    }
0765:                                    markers.add(new Integer(SOF_MARKER));
0766:                                }
0767:                            } else if (code == SOS) {
0768:                                if (!sosPresent) {
0769:                                    sosPresent = true;
0770:                                    numScanComponents = iis.read();
0771:                                    componentSelector = new int[numScanComponents];
0772:                                    dcHuffTable = new int[numScanComponents];
0773:                                    acHuffTable = new int[numScanComponents];
0774:                                    for (int i = 0; i < numScanComponents; i++) {
0775:                                        componentSelector[i] = iis.read();
0776:                                        dcHuffTable[i] = (int) iis.readBits(4);
0777:                                        acHuffTable[i] = (int) iis.readBits(4);
0778:                                    }
0779:                                    startSpectralSelection = iis.read();
0780:                                    endSpectralSelection = iis.read();
0781:                                    approxHigh = (int) iis.readBits(4);
0782:                                    approxLow = (int) iis.readBits(4);
0783:                                    markers.add(new Integer(SOS));
0784:                                }
0785:                                break;
0786:                            } else { // Any other marker
0787:                                addUnknownMarkerSegment(iis, code, dataLength);
0788:                            }
0789:                        }
0790:                    } catch (EOFException eofe) {
0791:                        // XXX Should this be caught?
0792:                        break;
0793:                    }
0794:                }
0795:
0796:                if (app2ICCPresent && isICCProfileValid && profile == null) {
0797:                    int profileDataLength = 0;
0798:                    for (int i = 1; i <= numICCProfileChunks; i++) {
0799:                        if (iccProfileChunkOffsets[i] == 0L) {
0800:                            isICCProfileValid = false;
0801:                            break;
0802:                        }
0803:                        profileDataLength += iccProfileChunkLengths[i];
0804:                    }
0805:
0806:                    if (isICCProfileValid) {
0807:                        byte[] b = new byte[profileDataLength];
0808:                        int off = 0;
0809:                        for (int i = 1; i <= numICCProfileChunks; i++) {
0810:                            iis.seek(iccProfileChunkOffsets[i]);
0811:                            iis.read(b, off, iccProfileChunkLengths[i]);
0812:                            off += iccProfileChunkLengths[i];
0813:                        }
0814:
0815:                        profile = ICC_Profile.getInstance(b);
0816:                    }
0817:                }
0818:
0819:                iis.reset();
0820:            }
0821:
0822:            private void addUnknownMarkerSegment(ImageInputStream stream,
0823:                    int code, int len) throws IOException {
0824:                if (!unknownPresent) {
0825:                    unknownPresent = true;
0826:                    markerTags = new ArrayList(1);
0827:                    unknownData = new ArrayList(1);
0828:                }
0829:                markerTags.add(new Integer(code));
0830:                byte[] b = new byte[len];
0831:                stream.readFully(b);
0832:                unknownData.add(b);
0833:                markers.add(new Integer(UNKNOWN_MARKER));
0834:            }
0835:
0836:            public boolean isReadOnly() {
0837:                return isReadOnly;
0838:            }
0839:
0840:            public Node getAsTree(String formatName) {
0841:                if (formatName.equals(nativeMetadataFormatName)) {
0842:                    return getNativeTree();
0843:                } else if (formatName
0844:                        .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
0845:                    return getStandardTree();
0846:                } else if (formatName.equals(TIFF_FORMAT)) {
0847:                    return getTIFFTree();
0848:                } else {
0849:                    throw new IllegalArgumentException(
0850:                            "Not a recognized format!");
0851:                }
0852:            }
0853:
0854:            public void mergeTree(String formatName, Node root)
0855:                    throws IIOInvalidTreeException {
0856:                if (isReadOnly) {
0857:                    throw new IllegalStateException("isReadOnly() == true!");
0858:                }
0859:            }
0860:
0861:            public void reset() {
0862:                if (isReadOnly) {
0863:                    throw new IllegalStateException("isReadOnly() == true!");
0864:                }
0865:            }
0866:
0867:            // Native tree method.
0868:
0869:            private Node getNativeTree() {
0870:                int jfxxIndex = 0;
0871:                int dqtIndex = 0;
0872:                int dhtIndex = 0;
0873:                int comIndex = 0;
0874:                int unknownIndex = 0;
0875:
0876:                IIOMetadataNode root = new IIOMetadataNode(
0877:                        nativeMetadataFormatName);
0878:
0879:                IIOMetadataNode JPEGvariety = new IIOMetadataNode("JPEGvariety");
0880:                root.appendChild(JPEGvariety);
0881:
0882:                IIOMetadataNode markerSequence = new IIOMetadataNode(
0883:                        "markerSequence");
0884:                root.appendChild(markerSequence);
0885:
0886:                IIOMetadataNode app0JFIF = null;
0887:                if (app0JFIFPresent || app0JFXXPresent || app2ICCPresent) {
0888:                    app0JFIF = new IIOMetadataNode("app0JFIF");
0889:                    app0JFIF.setAttribute("majorVersion", Integer
0890:                            .toString(majorVersion));
0891:                    app0JFIF.setAttribute("minorVersion", Integer
0892:                            .toString(minorVersion));
0893:                    app0JFIF.setAttribute("resUnits", Integer
0894:                            .toString(resUnits));
0895:                    app0JFIF.setAttribute("Xdensity", Integer
0896:                            .toString(Xdensity));
0897:                    app0JFIF.setAttribute("Ydensity", Integer
0898:                            .toString(Ydensity));
0899:                    app0JFIF.setAttribute("thumbWidth", Integer
0900:                            .toString(thumbWidth));
0901:                    app0JFIF.setAttribute("thumbHeight", Integer
0902:                            .toString(thumbHeight));
0903:                    JPEGvariety.appendChild(app0JFIF);
0904:                }
0905:
0906:                IIOMetadataNode JFXX = null;
0907:                if (app0JFXXPresent) {
0908:                    JFXX = new IIOMetadataNode("JFXX");
0909:                    app0JFIF.appendChild(JFXX);
0910:                }
0911:
0912:                Iterator markerIter = markers.iterator();
0913:                while (markerIter.hasNext()) {
0914:                    int marker = ((Integer) markerIter.next()).intValue();
0915:                    switch (marker) {
0916:                    case APP0_JFIF:
0917:                        // Do nothing: already handled above.
0918:                        break;
0919:                    case APP0_JFXX:
0920:                        IIOMetadataNode app0JFXX = new IIOMetadataNode(
0921:                                "app0JFXX");
0922:                        Integer extensionCode = (Integer) extensionCodes
0923:                                .get(jfxxIndex);
0924:                        app0JFXX.setAttribute("extensionCode", extensionCode
0925:                                .toString());
0926:                        IIOMetadataNode JFIFthumb = null;
0927:                        switch (extensionCode.intValue()) {
0928:                        case THUMBNAIL_JPEG:
0929:                            JFIFthumb = new IIOMetadataNode("JFIFthumbJPEG");
0930:                            break;
0931:                        case THUMBNAIL_PALETTE:
0932:                            JFIFthumb = new IIOMetadataNode("JFIFthumbPalette");
0933:                            break;
0934:                        case THUMBNAIL_RGB:
0935:                            JFIFthumb = new IIOMetadataNode("JFIFthumbRGB");
0936:                            break;
0937:                        default:
0938:                            // No JFIFthumb node will be appended.
0939:                        }
0940:                        if (JFIFthumb != null) {
0941:                            IIOImage img = (IIOImage) jfxxThumbnails
0942:                                    .get(jfxxIndex++);
0943:                            if (extensionCode.intValue() == THUMBNAIL_JPEG) {
0944:                                IIOMetadata thumbMetadata = img.getMetadata();
0945:                                if (thumbMetadata != null) {
0946:                                    Node thumbTree = thumbMetadata
0947:                                            .getAsTree(nativeMetadataFormatName);
0948:                                    if (thumbTree instanceof  IIOMetadataNode) {
0949:                                        IIOMetadataNode elt = (IIOMetadataNode) thumbTree;
0950:                                        NodeList elts = elt
0951:                                                .getElementsByTagName("markerSequence");
0952:                                        if (elts.getLength() > 0) {
0953:                                            JFIFthumb.appendChild(elts.item(0));
0954:                                        }
0955:                                    }
0956:                                }
0957:                            } else {
0958:                                BufferedImage thumb = (BufferedImage) img
0959:                                        .getRenderedImage();
0960:                                JFIFthumb.setAttribute("thumbWidth", Integer
0961:                                        .toString(thumb.getWidth()));
0962:                                JFIFthumb.setAttribute("thumbHeight", Integer
0963:                                        .toString(thumb.getHeight()));
0964:                            }
0965:                            // Add thumbnail as a user object even though not in
0966:                            // metadata specification.
0967:                            JFIFthumb.setUserObject(img);
0968:                            app0JFXX.appendChild(JFIFthumb);
0969:                        }
0970:                        JFXX.appendChild(app0JFXX);
0971:                        break;
0972:                    case APP2_ICC:
0973:                        IIOMetadataNode app2ICC = new IIOMetadataNode("app2ICC");
0974:                        app2ICC.setUserObject(profile);
0975:                        app0JFIF.appendChild(app2ICC);
0976:                        break;
0977:                    case DQT:
0978:                        IIOMetadataNode dqt = new IIOMetadataNode("dqt");
0979:                        List tables = (List) qtables.get(dqtIndex++);
0980:                        int numTables = tables.size();
0981:                        for (int j = 0; j < numTables; j++) {
0982:                            IIOMetadataNode dqtable = new IIOMetadataNode(
0983:                                    "dqtable");
0984:                            QTable t = (QTable) tables.get(j);
0985:                            dqtable.setAttribute("elementPrecision", Integer
0986:                                    .toString(t.elementPrecision));
0987:                            dqtable.setAttribute("qtableId", Integer
0988:                                    .toString(t.tableID));
0989:                            dqtable.setUserObject(t.table);
0990:                            dqt.appendChild(dqtable);
0991:                        }
0992:                        markerSequence.appendChild(dqt);
0993:                        break;
0994:                    case DHT:
0995:                        IIOMetadataNode dht = new IIOMetadataNode("dht");
0996:                        tables = (List) htables.get(dhtIndex++);
0997:                        numTables = tables.size();
0998:                        for (int j = 0; j < numTables; j++) {
0999:                            IIOMetadataNode dhtable = new IIOMetadataNode(
1000:                                    "dhtable");
1001:                            HuffmanTable t = (HuffmanTable) tables.get(j);
1002:                            dhtable.setAttribute("class", Integer
1003:                                    .toString(t.tableClass));
1004:                            dhtable.setAttribute("htableId", Integer
1005:                                    .toString(t.tableID));
1006:                            dhtable.setUserObject(t.table);
1007:                            dht.appendChild(dhtable);
1008:                        }
1009:                        markerSequence.appendChild(dht);
1010:                        break;
1011:                    case DRI:
1012:                        IIOMetadataNode dri = new IIOMetadataNode("dri");
1013:                        dri.setAttribute("interval", Integer
1014:                                .toString(driInterval));
1015:                        markerSequence.appendChild(dri);
1016:                        break;
1017:                    case COM:
1018:                        IIOMetadataNode com = new IIOMetadataNode("com");
1019:                        com.setUserObject(comments.get(comIndex++));
1020:                        markerSequence.appendChild(com);
1021:                        break;
1022:                    case UNKNOWN_MARKER:
1023:                        IIOMetadataNode unknown = new IIOMetadataNode("unknown");
1024:                        Integer markerTag = (Integer) markerTags
1025:                                .get(unknownIndex);
1026:                        unknown.setAttribute("MarkerTag", markerTag.toString());
1027:                        unknown.setUserObject(unknownData.get(unknownIndex++));
1028:                        markerSequence.appendChild(unknown);
1029:                        break;
1030:                    case APP14_ADOBE:
1031:                        IIOMetadataNode app14Adobe = new IIOMetadataNode(
1032:                                "app14Adobe");
1033:                        app14Adobe.setAttribute("version", Integer
1034:                                .toString(version));
1035:                        app14Adobe.setAttribute("flags0", Integer
1036:                                .toString(flags0));
1037:                        app14Adobe.setAttribute("flags1", Integer
1038:                                .toString(flags1));
1039:                        app14Adobe.setAttribute("transform", Integer
1040:                                .toString(transform));
1041:                        markerSequence.appendChild(app14Adobe);
1042:                        break;
1043:                    case SOF_MARKER:
1044:                        IIOMetadataNode sof = new IIOMetadataNode("sof");
1045:                        sof.setAttribute("process", Integer
1046:                                .toString(sofProcess));
1047:                        sof.setAttribute("samplePrecision", Integer
1048:                                .toString(samplePrecision));
1049:                        sof
1050:                                .setAttribute("numLines", Integer
1051:                                        .toString(numLines));
1052:                        sof.setAttribute("samplesPerLine", Integer
1053:                                .toString(samplesPerLine));
1054:                        sof.setAttribute("numFrameComponents", Integer
1055:                                .toString(numFrameComponents));
1056:                        for (int i = 0; i < numFrameComponents; i++) {
1057:                            IIOMetadataNode componentSpec = new IIOMetadataNode(
1058:                                    "componentSpec");
1059:                            componentSpec.setAttribute("componentId", Integer
1060:                                    .toString(componentId[i]));
1061:                            componentSpec.setAttribute("HsamplingFactor",
1062:                                    Integer.toString(hSamplingFactor[i]));
1063:                            componentSpec.setAttribute("VsamplingFactor",
1064:                                    Integer.toString(vSamplingFactor[i]));
1065:                            componentSpec.setAttribute("QtableSelector",
1066:                                    Integer.toString(qtableSelector[i]));
1067:                            sof.appendChild(componentSpec);
1068:                        }
1069:                        markerSequence.appendChild(sof);
1070:                        break;
1071:                    case SOS:
1072:                        IIOMetadataNode sos = new IIOMetadataNode("sos");
1073:                        sos.setAttribute("numScanComponents", Integer
1074:                                .toString(numScanComponents));
1075:                        sos.setAttribute("startSpectralSelection", Integer
1076:                                .toString(startSpectralSelection));
1077:                        sos.setAttribute("endSpectralSelection", Integer
1078:                                .toString(endSpectralSelection));
1079:                        sos.setAttribute("approxHigh", Integer
1080:                                .toString(approxHigh));
1081:                        sos.setAttribute("approxLow", Integer
1082:                                .toString(approxLow));
1083:                        for (int i = 0; i < numScanComponents; i++) {
1084:                            IIOMetadataNode scanComponentSpec = new IIOMetadataNode(
1085:                                    "scanComponentSpec");
1086:                            scanComponentSpec.setAttribute("componentSelector",
1087:                                    Integer.toString(componentSelector[i]));
1088:                            scanComponentSpec.setAttribute("dcHuffTable",
1089:                                    Integer.toString(dcHuffTable[i]));
1090:                            scanComponentSpec.setAttribute("acHuffTable",
1091:                                    Integer.toString(acHuffTable[i]));
1092:                            sos.appendChild(scanComponentSpec);
1093:                        }
1094:                        markerSequence.appendChild(sos);
1095:                        break;
1096:                    }
1097:                }
1098:
1099:                return root;
1100:            }
1101:
1102:            // Standard tree node methods
1103:
1104:            protected IIOMetadataNode getStandardChromaNode() {
1105:                if (!sofPresent) {
1106:                    // No image, so no chroma
1107:                    return null;
1108:                }
1109:
1110:                IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
1111:                IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
1112:                chroma.appendChild(csType);
1113:
1114:                IIOMetadataNode numChanNode = new IIOMetadataNode("NumChannels");
1115:                chroma.appendChild(numChanNode);
1116:                numChanNode.setAttribute("value", Integer
1117:                        .toString(numFrameComponents));
1118:
1119:                // Check JFIF presence.
1120:                if (app0JFIFPresent) {
1121:                    if (numFrameComponents == 1) {
1122:                        csType.setAttribute("name", "GRAY");
1123:                    } else {
1124:                        csType.setAttribute("name", "YCbCr");
1125:                    }
1126:                    return chroma;
1127:                }
1128:
1129:                // How about an Adobe marker segment?
1130:                if (app14AdobePresent) {
1131:                    switch (transform) {
1132:                    case ADOBE_TRANSFORM_YCCK: // YCCK
1133:                        csType.setAttribute("name", "YCCK");
1134:                        break;
1135:                    case ADOBE_TRANSFORM_YCC: // YCC
1136:                        csType.setAttribute("name", "YCbCr");
1137:                        break;
1138:                    case ADOBE_TRANSFORM_UNKNOWN: // Unknown
1139:                        if (numFrameComponents == 3) {
1140:                            csType.setAttribute("name", "RGB");
1141:                        } else if (numFrameComponents == 4) {
1142:                            csType.setAttribute("name", "CMYK");
1143:                        }
1144:                        break;
1145:                    }
1146:                    return chroma;
1147:                }
1148:
1149:                // Initially assume no opacity.
1150:                hasAlpha = false;
1151:
1152:                // Neither marker.  Check components
1153:                if (numFrameComponents < 3) {
1154:                    csType.setAttribute("name", "GRAY");
1155:                    if (numFrameComponents == 2) {
1156:                        hasAlpha = true;
1157:                    }
1158:                    return chroma;
1159:                }
1160:
1161:                boolean idsAreJFIF = true;
1162:
1163:                for (int i = 0; i < componentId.length; i++) {
1164:                    int id = componentId[i];
1165:                    if ((id < 1) || (id >= componentId.length)) {
1166:                        idsAreJFIF = false;
1167:                    }
1168:                }
1169:
1170:                if (idsAreJFIF) {
1171:                    csType.setAttribute("name", "YCbCr");
1172:                    if (numFrameComponents == 4) {
1173:                        hasAlpha = true;
1174:                    }
1175:                    return chroma;
1176:                }
1177:
1178:                // Check against the letters
1179:                if (componentId[0] == 'R' && componentId[1] == 'G'
1180:                        && componentId[2] == 'B') {
1181:                    csType.setAttribute("name", "RGB");
1182:                    if (numFrameComponents == 4 && componentId[3] == 'A') {
1183:                        hasAlpha = true;
1184:                    }
1185:                    return chroma;
1186:                }
1187:
1188:                if (componentId[0] == 'Y' && componentId[1] == 'C'
1189:                        && componentId[2] == 'c') {
1190:                    csType.setAttribute("name", "PhotoYCC");
1191:                    if (numFrameComponents == 4 && componentId[3] == 'A') {
1192:                        hasAlpha = true;
1193:                    }
1194:                    return chroma;
1195:                }
1196:
1197:                // Finally, 3-channel subsampled are YCbCr, unsubsampled are RGB
1198:                // 4-channel subsampled are YCbCrA, unsubsampled are CMYK
1199:
1200:                boolean subsampled = false;
1201:
1202:                int hfactor = hSamplingFactor[0];
1203:                int vfactor = vSamplingFactor[0];
1204:
1205:                for (int i = 1; i < componentId.length; i++) {
1206:                    if (hSamplingFactor[i] != hfactor
1207:                            || vSamplingFactor[i] != vfactor) {
1208:                        subsampled = true;
1209:                        break;
1210:                    }
1211:                }
1212:
1213:                if (subsampled) {
1214:                    csType.setAttribute("name", "YCbCr");
1215:                    if (numFrameComponents == 4) {
1216:                        hasAlpha = true;
1217:                    }
1218:                    return chroma;
1219:                }
1220:
1221:                // Not subsampled.  numFrameComponents < 3 is taken care of above
1222:                if (numFrameComponents == 3) {
1223:                    csType.setAttribute("name", "RGB");
1224:                } else {
1225:                    csType.setAttribute("name", "CMYK");
1226:                }
1227:
1228:                return chroma;
1229:            }
1230:
1231:            protected IIOMetadataNode getStandardCompressionNode() {
1232:                IIOMetadataNode compression = null;
1233:
1234:                if (sofPresent || sosPresent) {
1235:                    compression = new IIOMetadataNode("Compression");
1236:
1237:                    if (sofPresent) {
1238:                        // Process 55 is JPEG-LS, others are lossless JPEG.
1239:                        boolean isLossless = sofProcess == 3 || sofProcess == 7
1240:                                || sofProcess == 11 || sofProcess == 15
1241:                                || sofProcess == 55;
1242:
1243:                        // CompressionTypeName
1244:                        IIOMetadataNode name = new IIOMetadataNode(
1245:                                "CompressionTypeName");
1246:                        String compressionType = isLossless ? (sofProcess == 55 ? "JPEG-LS"
1247:                                : "JPEG-LOSSLESS")
1248:                                : "JPEG";
1249:                        name.setAttribute("value", compressionType);
1250:                        compression.appendChild(name);
1251:
1252:                        // Lossless - false
1253:                        IIOMetadataNode lossless = new IIOMetadataNode(
1254:                                "Lossless");
1255:                        lossless.setAttribute("value", isLossless ? "true"
1256:                                : "false");
1257:                        compression.appendChild(lossless);
1258:                    }
1259:
1260:                    if (sosPresent) {
1261:                        IIOMetadataNode prog = new IIOMetadataNode(
1262:                                "NumProgressiveScans");
1263:                        prog.setAttribute("value", "1");
1264:                        compression.appendChild(prog);
1265:                    }
1266:                }
1267:
1268:                return compression;
1269:            }
1270:
1271:            protected IIOMetadataNode getStandardDimensionNode() {
1272:                IIOMetadataNode dim = new IIOMetadataNode("Dimension");
1273:                IIOMetadataNode orient = new IIOMetadataNode("ImageOrientation");
1274:                orient.setAttribute("value", "normal");
1275:                dim.appendChild(orient);
1276:
1277:                if (app0JFIFPresent) {
1278:                    float aspectRatio;
1279:                    if (resUnits == JFIF_RESUNITS_ASPECT) {
1280:                        // Aspect ratio.
1281:                        aspectRatio = (float) Xdensity / (float) Ydensity;
1282:                    } else {
1283:                        // Density.
1284:                        aspectRatio = (float) Ydensity / (float) Xdensity;
1285:                    }
1286:                    IIOMetadataNode aspect = new IIOMetadataNode(
1287:                            "PixelAspectRatio");
1288:                    aspect.setAttribute("value", Float.toString(aspectRatio));
1289:                    dim.insertBefore(aspect, orient);
1290:
1291:                    if (resUnits != JFIF_RESUNITS_ASPECT) {
1292:                        // 1 == dpi, 2 == dpc
1293:                        float scale = (resUnits == JFIF_RESUNITS_DPI) ? 25.4F
1294:                                : 10.0F;
1295:
1296:                        IIOMetadataNode horiz = new IIOMetadataNode(
1297:                                "HorizontalPixelSize");
1298:                        horiz.setAttribute("value", Float.toString(scale
1299:                                / Xdensity));
1300:                        dim.appendChild(horiz);
1301:
1302:                        IIOMetadataNode vert = new IIOMetadataNode(
1303:                                "VerticalPixelSize");
1304:                        vert.setAttribute("value", Float.toString(scale
1305:                                / Ydensity));
1306:                        dim.appendChild(vert);
1307:                    }
1308:                }
1309:                return dim;
1310:            }
1311:
1312:            protected IIOMetadataNode getStandardTextNode() {
1313:                IIOMetadataNode text = null;
1314:                if (comPresent) {
1315:                    text = new IIOMetadataNode("Text");
1316:                    Iterator iter = comments.iterator();
1317:                    while (iter.hasNext()) {
1318:                        IIOMetadataNode entry = new IIOMetadataNode("TextEntry");
1319:                        entry.setAttribute("keyword", "comment");
1320:                        byte[] data = (byte[]) iter.next();
1321:                        try {
1322:                            entry.setAttribute("value", new String(data,
1323:                                    "ISO-8859-1"));
1324:                        } catch (UnsupportedEncodingException e) {
1325:                            entry.setAttribute("value", new String(data));
1326:                        }
1327:                        text.appendChild(entry);
1328:                    }
1329:                }
1330:                return text;
1331:            }
1332:
1333:            // This method assumes that getStandardChromaNode() has already been
1334:            // called to initialize hasAlpha.
1335:            protected IIOMetadataNode getStandardTransparencyNode() {
1336:                IIOMetadataNode trans = null;
1337:                if (hasAlpha == true) {
1338:                    trans = new IIOMetadataNode("Transparency");
1339:                    IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
1340:                    alpha.setAttribute("value", "nonpremultiplied"); // Always assume
1341:                    trans.appendChild(alpha);
1342:                }
1343:                return trans;
1344:            }
1345:
1346:            // TIFF tree method
1347:
1348:            private Node getTIFFTree() {
1349:                String metadataName = TIFF_FORMAT;
1350:
1351:                BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
1352:
1353:                TIFFDirectory dir = new TIFFDirectory(new TIFFTagSet[] { base,
1354:                        EXIFParentTIFFTagSet.getInstance() }, null);
1355:
1356:                if (sofPresent) {
1357:                    // sofProcess -> Compression ?
1358:                    int compression = BaselineTIFFTagSet.COMPRESSION_JPEG;
1359:                    TIFFField compressionField = new TIFFField(base
1360:                            .getTag(BaselineTIFFTagSet.TAG_COMPRESSION),
1361:                            compression);
1362:                    dir.addTIFFField(compressionField);
1363:
1364:                    // samplePrecision -> BitsPerSample
1365:                    char[] bitsPerSample = new char[numFrameComponents];
1366:                    Arrays.fill(bitsPerSample, (char) (samplePrecision & 0xff));
1367:                    TIFFField bitsPerSampleField = new TIFFField(base
1368:                            .getTag(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE),
1369:                            TIFFTag.TIFF_SHORT, bitsPerSample.length,
1370:                            bitsPerSample);
1371:                    dir.addTIFFField(bitsPerSampleField);
1372:
1373:                    // numLines -> ImageLength
1374:                    TIFFField imageLengthField = new TIFFField(base
1375:                            .getTag(BaselineTIFFTagSet.TAG_IMAGE_LENGTH),
1376:                            numLines);
1377:                    dir.addTIFFField(imageLengthField);
1378:
1379:                    // samplesPerLine -> ImageWidth
1380:                    TIFFField imageWidthField = new TIFFField(base
1381:                            .getTag(BaselineTIFFTagSet.TAG_IMAGE_WIDTH),
1382:                            samplesPerLine);
1383:                    dir.addTIFFField(imageWidthField);
1384:
1385:                    // numFrameComponents -> SamplesPerPixel
1386:                    TIFFField samplesPerPixelField = new TIFFField(base
1387:                            .getTag(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL),
1388:                            numFrameComponents);
1389:                    dir.addTIFFField(samplesPerPixelField);
1390:
1391:                    // componentId -> PhotometricInterpretation + ExtraSamples
1392:                    IIOMetadataNode chroma = getStandardChromaNode();
1393:                    if (chroma != null) {
1394:                        IIOMetadataNode csType = (IIOMetadataNode) chroma
1395:                                .getElementsByTagName("ColorSpaceType").item(0);
1396:                        String name = csType.getAttribute("name");
1397:                        int photometricInterpretation = -1;
1398:                        if (name.equals("GRAY")) {
1399:                            photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
1400:                        } else if (name.equals("YCbCr")
1401:                                || name.equals("PhotoYCC")) {
1402:                            // NOTE: PhotoYCC -> YCbCr
1403:                            photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR;
1404:                        } else if (name.equals("RGB")) {
1405:                            photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
1406:                        } else if (name.equals("CMYK") || name.equals("YCCK")) {
1407:                            // NOTE: YCCK -> CMYK
1408:                            photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK;
1409:                        }
1410:
1411:                        if (photometricInterpretation != -1) {
1412:                            TIFFField photometricInterpretationField = new TIFFField(
1413:                                    base
1414:                                            .getTag(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION),
1415:                                    photometricInterpretation);
1416:                            dir.addTIFFField(photometricInterpretationField);
1417:                        }
1418:
1419:                        if (hasAlpha) {
1420:                            char[] extraSamples = new char[] { BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA };
1421:                            TIFFField extraSamplesField = new TIFFField(
1422:                                    base
1423:                                            .getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES),
1424:                                    TIFFTag.TIFF_SHORT, extraSamples.length,
1425:                                    extraSamples);
1426:                            dir.addTIFFField(extraSamplesField);
1427:                        }
1428:                    } // chroma != null
1429:                } // sofPresent
1430:
1431:                // JFIF APP0 -> Resolution fields.
1432:                if (app0JFIFPresent) {
1433:                    long[][] xResolution = new long[][] { { Xdensity, 1 } };
1434:                    TIFFField XResolutionField = new TIFFField(base
1435:                            .getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION),
1436:                            TIFFTag.TIFF_RATIONAL, 1, xResolution);
1437:                    dir.addTIFFField(XResolutionField);
1438:
1439:                    long[][] yResolution = new long[][] { { Ydensity, 1 } };
1440:                    TIFFField YResolutionField = new TIFFField(base
1441:                            .getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION),
1442:                            TIFFTag.TIFF_RATIONAL, 1, yResolution);
1443:                    dir.addTIFFField(YResolutionField);
1444:
1445:                    int resolutionUnit = BaselineTIFFTagSet.RESOLUTION_UNIT_NONE;
1446:                    switch (resUnits) {
1447:                    case JFIF_RESUNITS_ASPECT:
1448:                        resolutionUnit = BaselineTIFFTagSet.RESOLUTION_UNIT_NONE;
1449:                    case JFIF_RESUNITS_DPI:
1450:                        resolutionUnit = BaselineTIFFTagSet.RESOLUTION_UNIT_INCH;
1451:                        break;
1452:                    case JFIF_RESUNITS_DPC:
1453:                        resolutionUnit = BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER;
1454:                        break;
1455:                    }
1456:                    TIFFField ResolutionUnitField = new TIFFField(base
1457:                            .getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
1458:                            resolutionUnit);
1459:                    dir.addTIFFField(ResolutionUnitField);
1460:                }
1461:
1462:                // DQT + DHT -> JPEGTables.
1463:                byte[] jpegTablesData = null;
1464:                if (dqtPresent || dqtPresent) {
1465:                    // Determine length of JPEGTables data.
1466:                    int jpegTablesLength = 2; // SOI
1467:                    if (dqtPresent) {
1468:                        Iterator dqts = qtables.iterator();
1469:                        while (dqts.hasNext()) {
1470:                            Iterator qtiter = ((List) dqts.next()).iterator();
1471:                            while (qtiter.hasNext()) {
1472:                                QTable qt = (QTable) qtiter.next();
1473:                                jpegTablesLength += 4 + qt.length;
1474:                            }
1475:                        }
1476:                    }
1477:                    if (dhtPresent) {
1478:                        Iterator dhts = htables.iterator();
1479:                        while (dhts.hasNext()) {
1480:                            Iterator htiter = ((List) dhts.next()).iterator();
1481:                            while (htiter.hasNext()) {
1482:                                HuffmanTable ht = (HuffmanTable) htiter.next();
1483:                                jpegTablesLength += 4 + ht.length;
1484:                            }
1485:                        }
1486:                    }
1487:                    jpegTablesLength += 2; // EOI
1488:
1489:                    // Allocate space.
1490:                    jpegTablesData = new byte[jpegTablesLength];
1491:
1492:                    // SOI
1493:                    jpegTablesData[0] = (byte) 0xff;
1494:                    jpegTablesData[1] = (byte) SOI;
1495:                    int jpoff = 2;
1496:
1497:                    if (dqtPresent) {
1498:                        Iterator dqts = qtables.iterator();
1499:                        while (dqts.hasNext()) {
1500:                            Iterator qtiter = ((List) dqts.next()).iterator();
1501:                            while (qtiter.hasNext()) {
1502:                                jpegTablesData[jpoff++] = (byte) 0xff;
1503:                                jpegTablesData[jpoff++] = (byte) DQT;
1504:                                QTable qt = (QTable) qtiter.next();
1505:                                int qtlength = qt.length + 2;
1506:                                jpegTablesData[jpoff++] = (byte) ((qtlength & 0xff00) >> 8);
1507:                                jpegTablesData[jpoff++] = (byte) (qtlength & 0xff);
1508:                                jpegTablesData[jpoff++] = (byte) (((qt.elementPrecision & 0xf0) << 4) | (qt.tableID & 0x0f));
1509:                                int[] table = qt.table.getTable();
1510:                                int qlen = table.length;
1511:                                for (int i = 0; i < qlen; i++) {
1512:                                    jpegTablesData[jpoff + zigzag[i]] = (byte) table[i];
1513:                                }
1514:                                jpoff += qlen;
1515:                            }
1516:                        }
1517:                    }
1518:
1519:                    if (dhtPresent) {
1520:                        Iterator dhts = htables.iterator();
1521:                        while (dhts.hasNext()) {
1522:                            Iterator htiter = ((List) dhts.next()).iterator();
1523:                            while (htiter.hasNext()) {
1524:                                jpegTablesData[jpoff++] = (byte) 0xff;
1525:                                jpegTablesData[jpoff++] = (byte) DHT;
1526:                                HuffmanTable ht = (HuffmanTable) htiter.next();
1527:                                int htlength = ht.length + 2;
1528:                                jpegTablesData[jpoff++] = (byte) ((htlength & 0xff00) >> 8);
1529:                                jpegTablesData[jpoff++] = (byte) (htlength & 0xff);
1530:                                jpegTablesData[jpoff++] = (byte) (((ht.tableClass & 0x0f) << 4) | (ht.tableID & 0x0f));
1531:                                short[] lengths = ht.table.getLengths();
1532:                                int numLengths = lengths.length;
1533:                                for (int i = 0; i < numLengths; i++) {
1534:                                    jpegTablesData[jpoff++] = (byte) lengths[i];
1535:                                }
1536:                                short[] values = ht.table.getValues();
1537:                                int numValues = values.length;
1538:                                for (int i = 0; i < numValues; i++) {
1539:                                    jpegTablesData[jpoff++] = (byte) values[i];
1540:                                }
1541:                            }
1542:                        }
1543:                    }
1544:
1545:                    jpegTablesData[jpoff++] = (byte) 0xff;
1546:                    jpegTablesData[jpoff] = (byte) EOI;
1547:                }
1548:                if (jpegTablesData != null) {
1549:                    TIFFField JPEGTablesField = new TIFFField(base
1550:                            .getTag(BaselineTIFFTagSet.TAG_JPEG_TABLES),
1551:                            TIFFTag.TIFF_UNDEFINED, jpegTablesData.length,
1552:                            jpegTablesData);
1553:                    dir.addTIFFField(JPEGTablesField);
1554:                }
1555:
1556:                IIOMetadata tiffMetadata = dir.getAsMetadata();
1557:
1558:                if (exifData != null) {
1559:                    try {
1560:                        Iterator tiffReaders = ImageIO
1561:                                .getImageReadersByFormatName("TIFF");
1562:                        if (tiffReaders != null && tiffReaders.hasNext()) {
1563:                            ImageReader tiffReader = (ImageReader) tiffReaders
1564:                                    .next();
1565:                            ByteArrayInputStream bais = new ByteArrayInputStream(
1566:                                    exifData);
1567:                            ImageInputStream exifStream = new MemoryCacheImageInputStream(
1568:                                    bais);
1569:                            tiffReader.setInput(exifStream);
1570:                            IIOMetadata exifMetadata = tiffReader
1571:                                    .getImageMetadata(0);
1572:                            tiffMetadata.mergeTree(metadataName, exifMetadata
1573:                                    .getAsTree(metadataName));
1574:                            tiffReader.reset();
1575:                        }
1576:                    } catch (IOException ioe) {
1577:                        // Ignore it.
1578:                    }
1579:                }
1580:
1581:                return tiffMetadata.getAsTree(metadataName);
1582:            }
1583:
1584:            // Thumbnail methods
1585:
1586:            private void initializeThumbnails() {
1587:                synchronized (thumbnails) {
1588:                    if (!thumbnailsInitialized) {
1589:                        // JFIF/JFXX are not supposed to coexist in the same
1590:                        // JPEG stream but in reality sometimes they do.
1591:
1592:                        // JFIF thumbnail
1593:                        if (app0JFIFPresent && jfifThumbnail != null) {
1594:                            thumbnails.add(jfifThumbnail);
1595:                        }
1596:
1597:                        // JFXX thumbnail(s)
1598:                        if (app0JFXXPresent && jfxxThumbnails != null) {
1599:                            int numJFXX = jfxxThumbnails.size();
1600:                            for (int i = 0; i < numJFXX; i++) {
1601:                                IIOImage img = (IIOImage) jfxxThumbnails.get(i);
1602:                                BufferedImage jfxxThumbnail = (BufferedImage) img
1603:                                        .getRenderedImage();
1604:                                thumbnails.add(jfxxThumbnail);
1605:                            }
1606:                        }
1607:
1608:                        // EXIF thumbnail
1609:                        if (exifData != null) {
1610:                            try {
1611:                                Iterator tiffReaders = ImageIO
1612:                                        .getImageReadersByFormatName("TIFF");
1613:                                if (tiffReaders != null
1614:                                        && tiffReaders.hasNext()) {
1615:                                    ImageReader tiffReader = (ImageReader) tiffReaders
1616:                                            .next();
1617:                                    ByteArrayInputStream bais = new ByteArrayInputStream(
1618:                                            exifData);
1619:                                    ImageInputStream exifStream = new MemoryCacheImageInputStream(
1620:                                            bais);
1621:                                    tiffReader.setInput(exifStream);
1622:                                    if (tiffReader.getNumImages(true) > 1) {
1623:                                        BufferedImage exifThumbnail = tiffReader
1624:                                                .read(1, null);
1625:                                        thumbnails.add(exifThumbnail);
1626:                                    }
1627:                                    tiffReader.reset();
1628:                                }
1629:                            } catch (IOException ioe) {
1630:                                // Ignore it.
1631:                            }
1632:                        }
1633:
1634:                        thumbnailsInitialized = true;
1635:                    } // if(!thumbnailsInitialized)
1636:                } // sychronized
1637:            }
1638:
1639:            int getNumThumbnails() throws IOException {
1640:                initializeThumbnails();
1641:                return thumbnails.size();
1642:            }
1643:
1644:            BufferedImage getThumbnail(int thumbnailIndex) throws IOException {
1645:                if (thumbnailIndex < 0) {
1646:                    throw new IndexOutOfBoundsException("thumbnailIndex < 0!");
1647:                }
1648:
1649:                initializeThumbnails();
1650:
1651:                if (thumbnailIndex >= thumbnails.size()) {
1652:                    throw new IndexOutOfBoundsException(
1653:                            "thumbnailIndex > getNumThumbnails()");
1654:                }
1655:
1656:                return (BufferedImage) thumbnails.get(thumbnailIndex);
1657:            }
1658:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.