1 /*
2 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
61
62 public class JPEGImageReader extends ImageReader {
63
64 private boolean debug = false;
65
66 /**
67 * The following variable contains a pointer to the IJG library
68 * structure for this reader. It is assigned in the constructor
69 * and then is passed in to every native call. It is set to 0
70 * by dispose to avoid disposing twice.
71 */
72 private long structPointer = 0;
73
74 /** The input stream we read from */
75 private ImageInputStream iis = null;
76
77 /**
78 * List of stream positions for images, reinitialized every time
79 * a new input source is set.
80 */
81 private List imagePositions = null;
82
83 /**
84 * The number of images in the stream, or 0.
85 */
86 private int numImages = 0;
87
88 static {
89 java.security.AccessController.doPrivileged(
90 new java.security.PrivilegedAction<Void>() {
91 public Void run() {
92 System.loadLibrary("jpeg");
93 return null;
94 }
95 });
96 initReaderIDs(ImageInputStream.class,
97 JPEGQTable.class,
98 JPEGHuffmanTable.class);
99 }
100
101 // The following warnings are converted to strings when used
206 private static final int UNKNOWN = -1; // Number of passes
207 private static final int MIN_ESTIMATED_PASSES = 10; // IJG default
208 private int knownPassCount = UNKNOWN;
209 private int pass = 0;
210 private float percentToDate = 0.0F;
211 private float previousPassPercentage = 0.0F;
212 private int progInterval = 0;
213
214 /**
215 * Set to true once stream has been checked for stream metadata
216 */
217 private boolean tablesOnlyChecked = false;
218
219 /** The referent to be registered with the Disposer. */
220 private Object disposerReferent = new Object();
221
222 /** The DisposerRecord that handles the actual disposal of this reader. */
223 private DisposerRecord disposerRecord;
224
225 /** Sets up static C structures. */
226 private static native void initReaderIDs(Class iisClass,
227 Class qTableClass,
228 Class huffClass);
229
230 public JPEGImageReader(ImageReaderSpi originator) {
231 super(originator);
232 structPointer = initJPEGImageReader();
233 disposerRecord = new JPEGReaderDisposerRecord(structPointer);
234 Disposer.addRecord(disposerReferent, disposerRecord);
235 }
236
237 /** Sets up per-reader C structure and returns a pointer to it. */
238 private native long initJPEGImageReader();
239
240 /**
241 * Called by the native code or other classes to signal a warning.
242 * The code is used to lookup a localized message to be used when
243 * sending warnings to listeners.
244 */
245 protected void warningOccurred(int code) {
246 cbLock.lock();
247 try {
248 if ((code < 0) || (code > MAX_WARNING)){
358 haveSeeked = true;
359 streamMetadata = new JPEGMetadata(true, false,
360 iis, this);
361 long pos = iis.getStreamPosition();
362 if (debug) {
363 System.out.println
364 ("pos after constructing stream metadata is " + pos);
365 }
366 }
367 // Now we are at the first image if there are any, so add it
368 // to the list
369 if (hasNextImage()) {
370 imagePositions.add(new Long(iis.getStreamPosition()));
371 }
372 } else { // Not tables only, so add original pos to the list
373 imagePositions.add(new Long(savePos));
374 // And set current image since we've read it now
375 currentImage = 0;
376 }
377 if (seekForwardOnly) {
378 Long pos = (Long) imagePositions.get(imagePositions.size()-1);
379 iis.flushBefore(pos.longValue());
380 }
381 tablesOnlyChecked = true;
382 }
383
384 public int getNumImages(boolean allowSearch) throws IOException {
385 setThreadLock();
386 try { // locked thread
387 cbLock.check();
388
389 return getNumImagesOnThread(allowSearch);
390 } finally {
391 clearThreadLock();
392 }
393 }
394
395 private int getNumImagesOnThread(boolean allowSearch)
396 throws IOException {
397 if (numImages != 0) {
398 return numImages;
464 /**
465 * Sets the input stream to the start of the requested image.
466 * <pre>
467 * @exception IllegalStateException if the input source has not been
468 * set.
469 * @exception IndexOutOfBoundsException if the supplied index is
470 * out of bounds.
471 * </pre>
472 */
473 private void gotoImage(int imageIndex) throws IOException {
474 if (iis == null) {
475 throw new IllegalStateException("Input not set");
476 }
477 if (imageIndex < minIndex) {
478 throw new IndexOutOfBoundsException();
479 }
480 if (!tablesOnlyChecked) {
481 checkTablesOnly();
482 }
483 if (imageIndex < imagePositions.size()) {
484 iis.seek(((Long)(imagePositions.get(imageIndex))).longValue());
485 } else {
486 // read to start of image, saving positions
487 // First seek to the last position we already have, and skip the
488 // entire image
489 Long pos = (Long) imagePositions.get(imagePositions.size()-1);
490 iis.seek(pos.longValue());
491 skipImage();
492 // Now add all intervening positions, skipping images
493 for (int index = imagePositions.size();
494 index <= imageIndex;
495 index++) {
496 // Is there an image?
497 if (!hasNextImage()) {
498 throw new IndexOutOfBoundsException();
499 }
500 pos = new Long(iis.getStreamPosition());
501 imagePositions.add(pos);
502 if (seekForwardOnly) {
503 iis.flushBefore(pos.longValue());
504 }
505 if (index < imageIndex) {
506 skipImage();
507 } // Otherwise we are where we want to be
508 }
509 }
749 return ret;
750 }
751
752 public ImageTypeSpecifier getRawImageType(int imageIndex)
753 throws IOException {
754 setThreadLock();
755 try {
756 if (currentImage != imageIndex) {
757 cbLock.check();
758
759 readHeader(imageIndex, true);
760 }
761
762 // Returns null if it can't be represented
763 return getImageType(colorSpaceCode).getType();
764 } finally {
765 clearThreadLock();
766 }
767 }
768
769 public Iterator getImageTypes(int imageIndex)
770 throws IOException {
771 setThreadLock();
772 try {
773 return getImageTypesOnThread(imageIndex);
774 } finally {
775 clearThreadLock();
776 }
777 }
778
779 private Iterator getImageTypesOnThread(int imageIndex)
780 throws IOException {
781 if (currentImage != imageIndex) {
782 cbLock.check();
783 readHeader(imageIndex, true);
784 }
785
786 // We return an iterator containing the default, any
787 // conversions that the library provides, and
788 // all the other default types with the same number
789 // of components, as we can do these as a post-process.
790 // As we convert Rasters rather than images, images
791 // with alpha cannot be converted in a post-process.
792
793 // If this image can't be interpreted, this method
794 // returns an empty Iterator.
795
796 // Get the raw ITS, if there is one. Note that this
797 // won't always be the same as the default.
798 ImageTypeProducer raw = getImageType(colorSpaceCode);
799
1046 }
1047
1048 BufferedImage ret = image;
1049 image = null; // don't keep a reference here
1050 return ret;
1051 } finally {
1052 clearThreadLock();
1053 }
1054 }
1055
1056 private Raster readInternal(int imageIndex,
1057 ImageReadParam param,
1058 boolean wantRaster) throws IOException {
1059 readHeader(imageIndex, false);
1060
1061 WritableRaster imRas = null;
1062 int numImageBands = 0;
1063
1064 if (!wantRaster){
1065 // Can we read this image?
1066 Iterator imageTypes = getImageTypes(imageIndex);
1067 if (imageTypes.hasNext() == false) {
1068 throw new IIOException("Unsupported Image Type");
1069 }
1070
1071 image = getDestination(param, imageTypes, width, height);
1072 imRas = image.getRaster();
1073
1074 // The destination may still be incompatible.
1075
1076 numImageBands = image.getSampleModel().getNumBands();
1077
1078 // Check whether we can handle any implied color conversion
1079
1080 // Throws IIOException if the stream and the image are
1081 // incompatible, and sets convert if a java conversion
1082 // is necessary
1083 checkColorConversion(image, param);
1084
1085 // Check the source and destination bands in the param
1086 checkReadParamBandSettings(param, numComponents, numImageBands);
1170 throw new IIOException("Illegal band size: should be 0 < size <= 8");
1171 }
1172 }
1173
1174 /*
1175 * If the process is sequential, and we have restart markers,
1176 * we could skip to the correct restart marker, if the library
1177 * lets us. That's an optimization to investigate later.
1178 */
1179
1180 // Check for update listeners (don't call back if none)
1181 boolean callbackUpdates = ((updateListeners != null)
1182 || (progressListeners != null));
1183
1184 // Set up progression data
1185 initProgressData();
1186 // if we have a metadata object, we can count the scans
1187 // and set knownPassCount
1188 if (imageIndex == imageMetadataIndex) { // We have metadata
1189 knownPassCount = 0;
1190 for (Iterator iter = imageMetadata.markerSequence.iterator();
1191 iter.hasNext();) {
1192 if (iter.next() instanceof SOSMarkerSegment) {
1193 knownPassCount++;
1194 }
1195 }
1196 }
1197 progInterval = Math.max((target.getHeight()-1) / 20, 1);
1198 if (knownPassCount > 0) {
1199 progInterval *= knownPassCount;
1200 } else if (maxProgressivePass != Integer.MAX_VALUE) {
1201 progInterval *= (maxProgressivePass - minProgressivePass + 1);
1202 }
1203
1204 if (debug) {
1205 System.out.println("**** Read Data *****");
1206 System.out.println("numRasterBands is " + numRasterBands);
1207 System.out.print("srcBands:");
1208 for (int i = 0; i<srcBands.length;i++)
1209 System.out.print(" " + srcBands[i]);
1210 System.out.println();
1548 if ((thumbnailIndex < 0)
1549 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1550 throw new IndexOutOfBoundsException("No such thumbnail");
1551 }
1552 // Now we know that there is a jfif segment and that iis is good
1553 JFIFMarkerSegment jfif =
1554 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1555 (JFIFMarkerSegment.class, true);
1556 return jfif.getThumbnail(iis, thumbnailIndex, this);
1557 } finally {
1558 clearThreadLock();
1559 }
1560 }
1561
1562 private void resetInternalState() {
1563 // reset C structures
1564 resetReader(structPointer);
1565
1566 // reset local Java structures
1567 numImages = 0;
1568 imagePositions = new ArrayList();
1569 currentImage = -1;
1570 image = null;
1571 raster = null;
1572 target = null;
1573 buffer = null;
1574 destROI = null;
1575 destinationBands = null;
1576 streamMetadata = null;
1577 imageMetadata = null;
1578 imageMetadataIndex = -1;
1579 haveSeeked = false;
1580 tablesOnlyChecked = false;
1581 iccCS = null;
1582 initProgressData();
1583 }
1584
1585 public void reset() {
1586 setThreadLock();
1587 try {
1588 cbLock.check();
|
1 /*
2 * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
61
62 public class JPEGImageReader extends ImageReader {
63
64 private boolean debug = false;
65
66 /**
67 * The following variable contains a pointer to the IJG library
68 * structure for this reader. It is assigned in the constructor
69 * and then is passed in to every native call. It is set to 0
70 * by dispose to avoid disposing twice.
71 */
72 private long structPointer = 0;
73
74 /** The input stream we read from */
75 private ImageInputStream iis = null;
76
77 /**
78 * List of stream positions for images, reinitialized every time
79 * a new input source is set.
80 */
81 private List<Long> imagePositions = null;
82
83 /**
84 * The number of images in the stream, or 0.
85 */
86 private int numImages = 0;
87
88 static {
89 java.security.AccessController.doPrivileged(
90 new java.security.PrivilegedAction<Void>() {
91 public Void run() {
92 System.loadLibrary("jpeg");
93 return null;
94 }
95 });
96 initReaderIDs(ImageInputStream.class,
97 JPEGQTable.class,
98 JPEGHuffmanTable.class);
99 }
100
101 // The following warnings are converted to strings when used
206 private static final int UNKNOWN = -1; // Number of passes
207 private static final int MIN_ESTIMATED_PASSES = 10; // IJG default
208 private int knownPassCount = UNKNOWN;
209 private int pass = 0;
210 private float percentToDate = 0.0F;
211 private float previousPassPercentage = 0.0F;
212 private int progInterval = 0;
213
214 /**
215 * Set to true once stream has been checked for stream metadata
216 */
217 private boolean tablesOnlyChecked = false;
218
219 /** The referent to be registered with the Disposer. */
220 private Object disposerReferent = new Object();
221
222 /** The DisposerRecord that handles the actual disposal of this reader. */
223 private DisposerRecord disposerRecord;
224
225 /** Sets up static C structures. */
226 private static native void initReaderIDs(Class<?> iisClass,
227 Class<?> qTableClass,
228 Class<?> huffClass);
229
230 public JPEGImageReader(ImageReaderSpi originator) {
231 super(originator);
232 structPointer = initJPEGImageReader();
233 disposerRecord = new JPEGReaderDisposerRecord(structPointer);
234 Disposer.addRecord(disposerReferent, disposerRecord);
235 }
236
237 /** Sets up per-reader C structure and returns a pointer to it. */
238 private native long initJPEGImageReader();
239
240 /**
241 * Called by the native code or other classes to signal a warning.
242 * The code is used to lookup a localized message to be used when
243 * sending warnings to listeners.
244 */
245 protected void warningOccurred(int code) {
246 cbLock.lock();
247 try {
248 if ((code < 0) || (code > MAX_WARNING)){
358 haveSeeked = true;
359 streamMetadata = new JPEGMetadata(true, false,
360 iis, this);
361 long pos = iis.getStreamPosition();
362 if (debug) {
363 System.out.println
364 ("pos after constructing stream metadata is " + pos);
365 }
366 }
367 // Now we are at the first image if there are any, so add it
368 // to the list
369 if (hasNextImage()) {
370 imagePositions.add(new Long(iis.getStreamPosition()));
371 }
372 } else { // Not tables only, so add original pos to the list
373 imagePositions.add(new Long(savePos));
374 // And set current image since we've read it now
375 currentImage = 0;
376 }
377 if (seekForwardOnly) {
378 Long pos = imagePositions.get(imagePositions.size()-1);
379 iis.flushBefore(pos.longValue());
380 }
381 tablesOnlyChecked = true;
382 }
383
384 public int getNumImages(boolean allowSearch) throws IOException {
385 setThreadLock();
386 try { // locked thread
387 cbLock.check();
388
389 return getNumImagesOnThread(allowSearch);
390 } finally {
391 clearThreadLock();
392 }
393 }
394
395 private int getNumImagesOnThread(boolean allowSearch)
396 throws IOException {
397 if (numImages != 0) {
398 return numImages;
464 /**
465 * Sets the input stream to the start of the requested image.
466 * <pre>
467 * @exception IllegalStateException if the input source has not been
468 * set.
469 * @exception IndexOutOfBoundsException if the supplied index is
470 * out of bounds.
471 * </pre>
472 */
473 private void gotoImage(int imageIndex) throws IOException {
474 if (iis == null) {
475 throw new IllegalStateException("Input not set");
476 }
477 if (imageIndex < minIndex) {
478 throw new IndexOutOfBoundsException();
479 }
480 if (!tablesOnlyChecked) {
481 checkTablesOnly();
482 }
483 if (imageIndex < imagePositions.size()) {
484 iis.seek(imagePositions.get(imageIndex).longValue());
485 } else {
486 // read to start of image, saving positions
487 // First seek to the last position we already have, and skip the
488 // entire image
489 Long pos = imagePositions.get(imagePositions.size()-1);
490 iis.seek(pos.longValue());
491 skipImage();
492 // Now add all intervening positions, skipping images
493 for (int index = imagePositions.size();
494 index <= imageIndex;
495 index++) {
496 // Is there an image?
497 if (!hasNextImage()) {
498 throw new IndexOutOfBoundsException();
499 }
500 pos = new Long(iis.getStreamPosition());
501 imagePositions.add(pos);
502 if (seekForwardOnly) {
503 iis.flushBefore(pos.longValue());
504 }
505 if (index < imageIndex) {
506 skipImage();
507 } // Otherwise we are where we want to be
508 }
509 }
749 return ret;
750 }
751
752 public ImageTypeSpecifier getRawImageType(int imageIndex)
753 throws IOException {
754 setThreadLock();
755 try {
756 if (currentImage != imageIndex) {
757 cbLock.check();
758
759 readHeader(imageIndex, true);
760 }
761
762 // Returns null if it can't be represented
763 return getImageType(colorSpaceCode).getType();
764 } finally {
765 clearThreadLock();
766 }
767 }
768
769 public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
770 throws IOException {
771 setThreadLock();
772 try {
773 return getImageTypesOnThread(imageIndex);
774 } finally {
775 clearThreadLock();
776 }
777 }
778
779 private Iterator<ImageTypeSpecifier> getImageTypesOnThread(int imageIndex)
780 throws IOException {
781 if (currentImage != imageIndex) {
782 cbLock.check();
783 readHeader(imageIndex, true);
784 }
785
786 // We return an iterator containing the default, any
787 // conversions that the library provides, and
788 // all the other default types with the same number
789 // of components, as we can do these as a post-process.
790 // As we convert Rasters rather than images, images
791 // with alpha cannot be converted in a post-process.
792
793 // If this image can't be interpreted, this method
794 // returns an empty Iterator.
795
796 // Get the raw ITS, if there is one. Note that this
797 // won't always be the same as the default.
798 ImageTypeProducer raw = getImageType(colorSpaceCode);
799
1046 }
1047
1048 BufferedImage ret = image;
1049 image = null; // don't keep a reference here
1050 return ret;
1051 } finally {
1052 clearThreadLock();
1053 }
1054 }
1055
1056 private Raster readInternal(int imageIndex,
1057 ImageReadParam param,
1058 boolean wantRaster) throws IOException {
1059 readHeader(imageIndex, false);
1060
1061 WritableRaster imRas = null;
1062 int numImageBands = 0;
1063
1064 if (!wantRaster){
1065 // Can we read this image?
1066 Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
1067 if (imageTypes.hasNext() == false) {
1068 throw new IIOException("Unsupported Image Type");
1069 }
1070
1071 image = getDestination(param, imageTypes, width, height);
1072 imRas = image.getRaster();
1073
1074 // The destination may still be incompatible.
1075
1076 numImageBands = image.getSampleModel().getNumBands();
1077
1078 // Check whether we can handle any implied color conversion
1079
1080 // Throws IIOException if the stream and the image are
1081 // incompatible, and sets convert if a java conversion
1082 // is necessary
1083 checkColorConversion(image, param);
1084
1085 // Check the source and destination bands in the param
1086 checkReadParamBandSettings(param, numComponents, numImageBands);
1170 throw new IIOException("Illegal band size: should be 0 < size <= 8");
1171 }
1172 }
1173
1174 /*
1175 * If the process is sequential, and we have restart markers,
1176 * we could skip to the correct restart marker, if the library
1177 * lets us. That's an optimization to investigate later.
1178 */
1179
1180 // Check for update listeners (don't call back if none)
1181 boolean callbackUpdates = ((updateListeners != null)
1182 || (progressListeners != null));
1183
1184 // Set up progression data
1185 initProgressData();
1186 // if we have a metadata object, we can count the scans
1187 // and set knownPassCount
1188 if (imageIndex == imageMetadataIndex) { // We have metadata
1189 knownPassCount = 0;
1190 for (Iterator<MarkerSegment> iter = imageMetadata.markerSequence.iterator();
1191 iter.hasNext();) {
1192 if (iter.next() instanceof SOSMarkerSegment) {
1193 knownPassCount++;
1194 }
1195 }
1196 }
1197 progInterval = Math.max((target.getHeight()-1) / 20, 1);
1198 if (knownPassCount > 0) {
1199 progInterval *= knownPassCount;
1200 } else if (maxProgressivePass != Integer.MAX_VALUE) {
1201 progInterval *= (maxProgressivePass - minProgressivePass + 1);
1202 }
1203
1204 if (debug) {
1205 System.out.println("**** Read Data *****");
1206 System.out.println("numRasterBands is " + numRasterBands);
1207 System.out.print("srcBands:");
1208 for (int i = 0; i<srcBands.length;i++)
1209 System.out.print(" " + srcBands[i]);
1210 System.out.println();
1548 if ((thumbnailIndex < 0)
1549 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1550 throw new IndexOutOfBoundsException("No such thumbnail");
1551 }
1552 // Now we know that there is a jfif segment and that iis is good
1553 JFIFMarkerSegment jfif =
1554 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1555 (JFIFMarkerSegment.class, true);
1556 return jfif.getThumbnail(iis, thumbnailIndex, this);
1557 } finally {
1558 clearThreadLock();
1559 }
1560 }
1561
1562 private void resetInternalState() {
1563 // reset C structures
1564 resetReader(structPointer);
1565
1566 // reset local Java structures
1567 numImages = 0;
1568 imagePositions = new ArrayList<>();
1569 currentImage = -1;
1570 image = null;
1571 raster = null;
1572 target = null;
1573 buffer = null;
1574 destROI = null;
1575 destinationBands = null;
1576 streamMetadata = null;
1577 imageMetadata = null;
1578 imageMetadataIndex = -1;
1579 haveSeeked = false;
1580 tablesOnlyChecked = false;
1581 iccCS = null;
1582 initProgressData();
1583 }
1584
1585 public void reset() {
1586 setThreadLock();
1587 try {
1588 cbLock.check();
|