1 /*
2 * Copyright (c) 2000, 2018, 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
261 * This routine is called by the native code when it has already
262 * formatted a string for output.
263 * XXX For truly complete localization of all warning messages,
264 * the sun_jpeg_output_message routine in the native code should
265 * send only the codes and parameters to a method here in Java,
266 * which will then format and send the warnings, using localized
267 * strings. This method will have to deal with all the parameters
268 * and formats (%u with possibly large numbers, %02d, %02x, etc.)
269 * that actually occur in the JPEG library. For now, this prevents
270 * library warnings from being printed to stderr.
271 */
272 protected void warningWithMessage(String msg) {
273 cbLock.lock();
274 try {
275 processWarningOccurred(msg);
276 } finally {
277 cbLock.unlock();
278 }
279 }
280
281 public void setInput(Object input,
282 boolean seekForwardOnly,
283 boolean ignoreMetadata)
284 {
285 setThreadLock();
286 try {
287 cbLock.check();
288
289 super.setInput(input, seekForwardOnly, ignoreMetadata);
290 this.ignoreMetadata = ignoreMetadata;
291 resetInternalState();
292 iis = (ImageInputStream) input; // Always works
293 setSource(structPointer);
294 } finally {
295 clearThreadLock();
296 }
297 }
298
299 /**
300 * This method is called from native code in order to fill
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(iis.getStreamPosition());
371 }
372 } else { // Not tables only, so add original pos to the list
373 imagePositions.add(savePos);
374 // And set current image since we've read it now
375 currentImage = 0;
376 }
377 // If the image positions list is empty as in the case of a tables-only
378 // stream, then attempting to access the element at index
379 // imagePositions.size() - 1 will cause an IndexOutOfBoundsException.
380 if (seekForwardOnly && !imagePositions.isEmpty()) {
381 Long pos = imagePositions.get(imagePositions.size()-1);
382 iis.flushBefore(pos.longValue());
383 }
384 tablesOnlyChecked = true;
385 }
386
387 public int getNumImages(boolean allowSearch) throws IOException {
388 setThreadLock();
389 try { // locked thread
390 cbLock.check();
391
392 return getNumImagesOnThread(allowSearch);
393 } finally {
394 clearThreadLock();
395 }
396 }
397
398 private void skipPastImage(int imageIndex) {
399 cbLock.lock();
400 try {
401 gotoImage(imageIndex);
402 skipImage();
403 } catch (IOException | IndexOutOfBoundsException e) {
404 } finally {
405 cbLock.unlock();
406 }
813 iccCS = new ICC_ColorSpace(newProfile);
814 // verify new color space
815 try {
816 float[] colors = iccCS.fromRGB(new float[] {1f, 0f, 0f});
817 } catch (CMMException e) {
818 /*
819 * Embedded profile seems to be corrupted.
820 * Ignore this profile.
821 */
822 iccCS = null;
823 cbLock.lock();
824 try {
825 warningOccurred(WARNING_IGNORE_INVALID_ICC);
826 } finally {
827 cbLock.unlock();
828 }
829 }
830 }
831 }
832
833 public int getWidth(int imageIndex) throws IOException {
834 setThreadLock();
835 try {
836 if (currentImage != imageIndex) {
837 cbLock.check();
838 readHeader(imageIndex, true);
839 }
840 return width;
841 } finally {
842 clearThreadLock();
843 }
844 }
845
846 public int getHeight(int imageIndex) throws IOException {
847 setThreadLock();
848 try {
849 if (currentImage != imageIndex) {
850 cbLock.check();
851 readHeader(imageIndex, true);
852 }
853 return height;
854 } finally {
855 clearThreadLock();
856 }
857 }
858
859 /////////// Color Conversion and Image Types
860
861 /**
862 * Return an ImageTypeSpecifier corresponding to the given
863 * color space code, or null if the color space is unsupported.
864 */
865 private ImageTypeProducer getImageType(int code) {
866 ImageTypeProducer ret = null;
867
868 if ((code > 0) && (code < JPEG.NUM_JCS_CODES)) {
869 ret = ImageTypeProducer.getTypeProducer(code);
870 }
871 return ret;
872 }
873
874 public ImageTypeSpecifier getRawImageType(int imageIndex)
875 throws IOException {
876 setThreadLock();
877 try {
878 if (currentImage != imageIndex) {
879 cbLock.check();
880
881 readHeader(imageIndex, true);
882 }
883
884 // Returns null if it can't be represented
885 return getImageType(colorSpaceCode).getType();
886 } finally {
887 clearThreadLock();
888 }
889 }
890
891 public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
892 throws IOException {
893 setThreadLock();
894 try {
895 return getImageTypesOnThread(imageIndex);
896 } finally {
897 clearThreadLock();
898 }
899 }
900
901 private Iterator<ImageTypeSpecifier> getImageTypesOnThread(int imageIndex)
902 throws IOException {
903 if (currentImage != imageIndex) {
904 cbLock.check();
905 readHeader(imageIndex, true);
906 }
907
908 // We return an iterator containing the default, any
909 // conversions that the library provides, and
910 // all the other default types with the same number
1041 // Target isn't sRGB, so convert from sRGB to the target
1042 convert = new ColorConvertOp(JPEG.JCS.sRGB, cs, null);
1043 } else if (csType != ColorSpace.TYPE_RGB) {
1044 throw new IIOException("Incompatible color conversion");
1045 }
1046 break;
1047 default:
1048 // Anything else we can't handle at all
1049 throw new IIOException("Incompatible color conversion");
1050 }
1051 }
1052
1053 /**
1054 * Set the IJG output space to the given value. The library will
1055 * perform the appropriate colorspace conversions.
1056 */
1057 private native void setOutColorSpace(long structPointer, int id);
1058
1059 /////// End of Color Conversion & Image Types
1060
1061 public ImageReadParam getDefaultReadParam() {
1062 return new JPEGImageReadParam();
1063 }
1064
1065 public IIOMetadata getStreamMetadata() throws IOException {
1066 setThreadLock();
1067 try {
1068 if (!tablesOnlyChecked) {
1069 cbLock.check();
1070 checkTablesOnly();
1071 }
1072 return streamMetadata;
1073 } finally {
1074 clearThreadLock();
1075 }
1076 }
1077
1078 public IIOMetadata getImageMetadata(int imageIndex)
1079 throws IOException {
1080 setThreadLock();
1081 try {
1082 // imageMetadataIndex will always be either a valid index or
1083 // -1, in which case imageMetadata will not be null.
1084 // So we can leave checking imageIndex for gotoImage.
1085 if ((imageMetadataIndex == imageIndex)
1086 && (imageMetadata != null)) {
1087 return imageMetadata;
1088 }
1089
1090 cbLock.check();
1091
1092 gotoImage(imageIndex);
1093
1094 imageMetadata = new JPEGMetadata(false, false, iis, this);
1095
1096 imageMetadataIndex = imageIndex;
1097
1098 return imageMetadata;
1099 } finally {
1100 clearThreadLock();
1101 }
1102 }
1103
1104 public BufferedImage read(int imageIndex, ImageReadParam param)
1105 throws IOException {
1106 setThreadLock();
1107 try {
1108 cbLock.check();
1109 try {
1110 readInternal(imageIndex, param, false);
1111 } catch (RuntimeException e) {
1112 resetLibraryState(structPointer);
1113 throw e;
1114 } catch (IOException e) {
1115 resetLibraryState(structPointer);
1116 throw e;
1117 }
1118
1119 BufferedImage ret = image;
1120 image = null; // don't keep a reference here
1121 return ret;
1122 } finally {
1123 clearThreadLock();
1476 byte [] buffer,
1477 int numRasterBands,
1478 int [] srcBands,
1479 int [] bandSizes,
1480 int sourceXOffset, int sourceYOffset,
1481 int sourceWidth, int sourceHeight,
1482 int periodX, int periodY,
1483 JPEGQTable [] abbrevQTables,
1484 JPEGHuffmanTable [] abbrevDCHuffmanTables,
1485 JPEGHuffmanTable [] abbrevACHuffmanTables,
1486 int minProgressivePass,
1487 int maxProgressivePass,
1488 boolean wantUpdates);
1489
1490 /*
1491 * We should call clearNativeReadAbortFlag() before we start reading
1492 * jpeg image as image processing happens at native side.
1493 */
1494 private native void clearNativeReadAbortFlag(long structPointer);
1495
1496 public void abort() {
1497 setThreadLock();
1498 try {
1499 /**
1500 * NB: we do not check the call back lock here,
1501 * we allow to abort the reader any time.
1502 */
1503
1504 super.abort();
1505 abortRead(structPointer);
1506 } finally {
1507 clearThreadLock();
1508 }
1509 }
1510
1511 /** Set the C level abort flag. Keep it atomic for thread safety. */
1512 private native void abortRead(long structPointer);
1513
1514 /** Resets library state when an exception occurred during a read. */
1515 private native void resetLibraryState(long structPointer);
1516
1517 public boolean canReadRaster() {
1518 return true;
1519 }
1520
1521 public Raster readRaster(int imageIndex, ImageReadParam param)
1522 throws IOException {
1523 setThreadLock();
1524 Raster retval = null;
1525 try {
1526 cbLock.check();
1527 /*
1528 * This could be further optimized by not resetting the dest.
1529 * offset and creating a translated raster in readInternal()
1530 * (see bug 4994702 for more info).
1531 */
1532
1533 // For Rasters, destination offset is logical, not physical, so
1534 // set it to 0 before calling computeRegions, so that the destination
1535 // region is not clipped.
1536 Point saveDestOffset = null;
1537 if (param != null) {
1538 saveDestOffset = param.getDestinationOffset();
1539 param.setDestinationOffset(new Point(0, 0));
1540 }
1541 retval = readInternal(imageIndex, param, true);
1542 // Apply the destination offset, if any, as a logical offset
1543 if (saveDestOffset != null) {
1544 target = target.createWritableTranslatedChild(saveDestOffset.x,
1545 saveDestOffset.y);
1546 }
1547 } catch (RuntimeException e) {
1548 resetLibraryState(structPointer);
1549 throw e;
1550 } catch (IOException e) {
1551 resetLibraryState(structPointer);
1552 throw e;
1553 } finally {
1554 clearThreadLock();
1555 }
1556 return retval;
1557 }
1558
1559 public boolean readerSupportsThumbnails() {
1560 return true;
1561 }
1562
1563 public int getNumThumbnails(int imageIndex) throws IOException {
1564 setThreadLock();
1565 try {
1566 cbLock.check();
1567
1568 getImageMetadata(imageIndex); // checks iis state for us
1569 // Now check the jfif segments
1570 JFIFMarkerSegment jfif =
1571 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1572 (JFIFMarkerSegment.class, true);
1573 int retval = 0;
1574 if (jfif != null) {
1575 retval = (jfif.thumb == null) ? 0 : 1;
1576 retval += jfif.extSegments.size();
1577 }
1578 return retval;
1579 } finally {
1580 clearThreadLock();
1581 }
1582 }
1583
1584 public int getThumbnailWidth(int imageIndex, int thumbnailIndex)
1585 throws IOException {
1586 setThreadLock();
1587 try {
1588 cbLock.check();
1589
1590 if ((thumbnailIndex < 0)
1591 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1592 throw new IndexOutOfBoundsException("No such thumbnail");
1593 }
1594 // Now we know that there is a jfif segment
1595 JFIFMarkerSegment jfif =
1596 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1597 (JFIFMarkerSegment.class, true);
1598 return jfif.getThumbnailWidth(thumbnailIndex);
1599 } finally {
1600 clearThreadLock();
1601 }
1602 }
1603
1604 public int getThumbnailHeight(int imageIndex, int thumbnailIndex)
1605 throws IOException {
1606 setThreadLock();
1607 try {
1608 cbLock.check();
1609
1610 if ((thumbnailIndex < 0)
1611 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1612 throw new IndexOutOfBoundsException("No such thumbnail");
1613 }
1614 // Now we know that there is a jfif segment
1615 JFIFMarkerSegment jfif =
1616 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1617 (JFIFMarkerSegment.class, true);
1618 return jfif.getThumbnailHeight(thumbnailIndex);
1619 } finally {
1620 clearThreadLock();
1621 }
1622 }
1623
1624 public BufferedImage readThumbnail(int imageIndex,
1625 int thumbnailIndex)
1626 throws IOException {
1627 setThreadLock();
1628 try {
1629 cbLock.check();
1630
1631 if ((thumbnailIndex < 0)
1632 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1633 throw new IndexOutOfBoundsException("No such thumbnail");
1634 }
1635 // Now we know that there is a jfif segment and that iis is good
1636 JFIFMarkerSegment jfif =
1637 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1638 (JFIFMarkerSegment.class, true);
1639 return jfif.getThumbnail(iis, thumbnailIndex, this);
1640 } finally {
1641 clearThreadLock();
1642 }
1643 }
1648
1649 // reset local Java structures
1650 numImages = 0;
1651 imagePositions = new ArrayList<>();
1652 currentImage = -1;
1653 image = null;
1654 raster = null;
1655 target = null;
1656 buffer = null;
1657 destROI = null;
1658 destinationBands = null;
1659 streamMetadata = null;
1660 imageMetadata = null;
1661 imageMetadataIndex = -1;
1662 haveSeeked = false;
1663 tablesOnlyChecked = false;
1664 iccCS = null;
1665 initProgressData();
1666 }
1667
1668 public void reset() {
1669 setThreadLock();
1670 try {
1671 cbLock.check();
1672 super.reset();
1673 } finally {
1674 clearThreadLock();
1675 }
1676 }
1677
1678 private native void resetReader(long structPointer);
1679
1680 public void dispose() {
1681 setThreadLock();
1682 try {
1683 cbLock.check();
1684
1685 if (structPointer != 0) {
1686 disposerRecord.dispose();
1687 structPointer = 0;
1688 }
1689 } finally {
1690 clearThreadLock();
1691 }
1692 }
1693
1694 private static native void disposeReader(long structPointer);
1695
1696 private static class JPEGReaderDisposerRecord implements DisposerRecord {
1697 private long pData;
1698
1699 public JPEGReaderDisposerRecord(long pData) {
1700 this.pData = pData;
1701 }
1702
1703 public synchronized void dispose() {
1704 if (pData != 0) {
1705 disposeReader(pData);
1706 pData = 0;
1707 }
1708 }
1709 }
1710
1711 private Thread theThread = null;
1712 private int theLockCount = 0;
1713
1714 private synchronized void setThreadLock() {
1715 Thread currThread = Thread.currentThread();
1716 if (theThread != null) {
1717 if (theThread != currThread) {
1718 // it looks like that this reader instance is used
1719 // by multiple threads.
1720 throw new IllegalStateException("Attempt to use instance of " +
1721 this + " locked on thread " +
1722 theThread + " from thread " +
|
1 /*
2 * Copyright (c) 2000, 2020, 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
261 * This routine is called by the native code when it has already
262 * formatted a string for output.
263 * XXX For truly complete localization of all warning messages,
264 * the sun_jpeg_output_message routine in the native code should
265 * send only the codes and parameters to a method here in Java,
266 * which will then format and send the warnings, using localized
267 * strings. This method will have to deal with all the parameters
268 * and formats (%u with possibly large numbers, %02d, %02x, etc.)
269 * that actually occur in the JPEG library. For now, this prevents
270 * library warnings from being printed to stderr.
271 */
272 protected void warningWithMessage(String msg) {
273 cbLock.lock();
274 try {
275 processWarningOccurred(msg);
276 } finally {
277 cbLock.unlock();
278 }
279 }
280
281 @Override
282 public void setInput(Object input,
283 boolean seekForwardOnly,
284 boolean ignoreMetadata)
285 {
286 setThreadLock();
287 try {
288 cbLock.check();
289
290 super.setInput(input, seekForwardOnly, ignoreMetadata);
291 this.ignoreMetadata = ignoreMetadata;
292 resetInternalState();
293 iis = (ImageInputStream) input; // Always works
294 setSource(structPointer);
295 } finally {
296 clearThreadLock();
297 }
298 }
299
300 /**
301 * This method is called from native code in order to fill
368 // Now we are at the first image if there are any, so add it
369 // to the list
370 if (hasNextImage()) {
371 imagePositions.add(iis.getStreamPosition());
372 }
373 } else { // Not tables only, so add original pos to the list
374 imagePositions.add(savePos);
375 // And set current image since we've read it now
376 currentImage = 0;
377 }
378 // If the image positions list is empty as in the case of a tables-only
379 // stream, then attempting to access the element at index
380 // imagePositions.size() - 1 will cause an IndexOutOfBoundsException.
381 if (seekForwardOnly && !imagePositions.isEmpty()) {
382 Long pos = imagePositions.get(imagePositions.size()-1);
383 iis.flushBefore(pos.longValue());
384 }
385 tablesOnlyChecked = true;
386 }
387
388 @Override
389 public int getNumImages(boolean allowSearch) throws IOException {
390 setThreadLock();
391 try { // locked thread
392 cbLock.check();
393
394 return getNumImagesOnThread(allowSearch);
395 } finally {
396 clearThreadLock();
397 }
398 }
399
400 private void skipPastImage(int imageIndex) {
401 cbLock.lock();
402 try {
403 gotoImage(imageIndex);
404 skipImage();
405 } catch (IOException | IndexOutOfBoundsException e) {
406 } finally {
407 cbLock.unlock();
408 }
815 iccCS = new ICC_ColorSpace(newProfile);
816 // verify new color space
817 try {
818 float[] colors = iccCS.fromRGB(new float[] {1f, 0f, 0f});
819 } catch (CMMException e) {
820 /*
821 * Embedded profile seems to be corrupted.
822 * Ignore this profile.
823 */
824 iccCS = null;
825 cbLock.lock();
826 try {
827 warningOccurred(WARNING_IGNORE_INVALID_ICC);
828 } finally {
829 cbLock.unlock();
830 }
831 }
832 }
833 }
834
835 @Override
836 public int getWidth(int imageIndex) throws IOException {
837 setThreadLock();
838 try {
839 if (currentImage != imageIndex) {
840 cbLock.check();
841 readHeader(imageIndex, true);
842 }
843 return width;
844 } finally {
845 clearThreadLock();
846 }
847 }
848
849 @Override
850 public int getHeight(int imageIndex) throws IOException {
851 setThreadLock();
852 try {
853 if (currentImage != imageIndex) {
854 cbLock.check();
855 readHeader(imageIndex, true);
856 }
857 return height;
858 } finally {
859 clearThreadLock();
860 }
861 }
862
863 /////////// Color Conversion and Image Types
864
865 /**
866 * Return an ImageTypeSpecifier corresponding to the given
867 * color space code, or null if the color space is unsupported.
868 */
869 private ImageTypeProducer getImageType(int code) {
870 ImageTypeProducer ret = null;
871
872 if ((code > 0) && (code < JPEG.NUM_JCS_CODES)) {
873 ret = ImageTypeProducer.getTypeProducer(code);
874 }
875 return ret;
876 }
877
878 @Override
879 public ImageTypeSpecifier getRawImageType(int imageIndex)
880 throws IOException {
881 setThreadLock();
882 try {
883 if (currentImage != imageIndex) {
884 cbLock.check();
885
886 readHeader(imageIndex, true);
887 }
888
889 // Returns null if it can't be represented
890 return getImageType(colorSpaceCode).getType();
891 } finally {
892 clearThreadLock();
893 }
894 }
895
896 @Override
897 public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
898 throws IOException {
899 setThreadLock();
900 try {
901 return getImageTypesOnThread(imageIndex);
902 } finally {
903 clearThreadLock();
904 }
905 }
906
907 private Iterator<ImageTypeSpecifier> getImageTypesOnThread(int imageIndex)
908 throws IOException {
909 if (currentImage != imageIndex) {
910 cbLock.check();
911 readHeader(imageIndex, true);
912 }
913
914 // We return an iterator containing the default, any
915 // conversions that the library provides, and
916 // all the other default types with the same number
1047 // Target isn't sRGB, so convert from sRGB to the target
1048 convert = new ColorConvertOp(JPEG.JCS.sRGB, cs, null);
1049 } else if (csType != ColorSpace.TYPE_RGB) {
1050 throw new IIOException("Incompatible color conversion");
1051 }
1052 break;
1053 default:
1054 // Anything else we can't handle at all
1055 throw new IIOException("Incompatible color conversion");
1056 }
1057 }
1058
1059 /**
1060 * Set the IJG output space to the given value. The library will
1061 * perform the appropriate colorspace conversions.
1062 */
1063 private native void setOutColorSpace(long structPointer, int id);
1064
1065 /////// End of Color Conversion & Image Types
1066
1067 @Override
1068 public ImageReadParam getDefaultReadParam() {
1069 return new JPEGImageReadParam();
1070 }
1071
1072 @Override
1073 public IIOMetadata getStreamMetadata() throws IOException {
1074 setThreadLock();
1075 try {
1076 if (!tablesOnlyChecked) {
1077 cbLock.check();
1078 checkTablesOnly();
1079 }
1080 return streamMetadata;
1081 } finally {
1082 clearThreadLock();
1083 }
1084 }
1085
1086 @Override
1087 public IIOMetadata getImageMetadata(int imageIndex)
1088 throws IOException {
1089 setThreadLock();
1090 try {
1091 // imageMetadataIndex will always be either a valid index or
1092 // -1, in which case imageMetadata will not be null.
1093 // So we can leave checking imageIndex for gotoImage.
1094 if ((imageMetadataIndex == imageIndex)
1095 && (imageMetadata != null)) {
1096 return imageMetadata;
1097 }
1098
1099 cbLock.check();
1100
1101 gotoImage(imageIndex);
1102
1103 imageMetadata = new JPEGMetadata(false, false, iis, this);
1104
1105 imageMetadataIndex = imageIndex;
1106
1107 return imageMetadata;
1108 } finally {
1109 clearThreadLock();
1110 }
1111 }
1112
1113 @Override
1114 public BufferedImage read(int imageIndex, ImageReadParam param)
1115 throws IOException {
1116 setThreadLock();
1117 try {
1118 cbLock.check();
1119 try {
1120 readInternal(imageIndex, param, false);
1121 } catch (RuntimeException e) {
1122 resetLibraryState(structPointer);
1123 throw e;
1124 } catch (IOException e) {
1125 resetLibraryState(structPointer);
1126 throw e;
1127 }
1128
1129 BufferedImage ret = image;
1130 image = null; // don't keep a reference here
1131 return ret;
1132 } finally {
1133 clearThreadLock();
1486 byte [] buffer,
1487 int numRasterBands,
1488 int [] srcBands,
1489 int [] bandSizes,
1490 int sourceXOffset, int sourceYOffset,
1491 int sourceWidth, int sourceHeight,
1492 int periodX, int periodY,
1493 JPEGQTable [] abbrevQTables,
1494 JPEGHuffmanTable [] abbrevDCHuffmanTables,
1495 JPEGHuffmanTable [] abbrevACHuffmanTables,
1496 int minProgressivePass,
1497 int maxProgressivePass,
1498 boolean wantUpdates);
1499
1500 /*
1501 * We should call clearNativeReadAbortFlag() before we start reading
1502 * jpeg image as image processing happens at native side.
1503 */
1504 private native void clearNativeReadAbortFlag(long structPointer);
1505
1506 @Override
1507 public void abort() {
1508 setThreadLock();
1509 try {
1510 /**
1511 * NB: we do not check the call back lock here,
1512 * we allow to abort the reader any time.
1513 */
1514
1515 super.abort();
1516 abortRead(structPointer);
1517 } finally {
1518 clearThreadLock();
1519 }
1520 }
1521
1522 /** Set the C level abort flag. Keep it atomic for thread safety. */
1523 private native void abortRead(long structPointer);
1524
1525 /** Resets library state when an exception occurred during a read. */
1526 private native void resetLibraryState(long structPointer);
1527
1528 @Override
1529 public boolean canReadRaster() {
1530 return true;
1531 }
1532
1533 @Override
1534 public Raster readRaster(int imageIndex, ImageReadParam param)
1535 throws IOException {
1536 setThreadLock();
1537 Raster retval = null;
1538 try {
1539 cbLock.check();
1540 /*
1541 * This could be further optimized by not resetting the dest.
1542 * offset and creating a translated raster in readInternal()
1543 * (see bug 4994702 for more info).
1544 */
1545
1546 // For Rasters, destination offset is logical, not physical, so
1547 // set it to 0 before calling computeRegions, so that the destination
1548 // region is not clipped.
1549 Point saveDestOffset = null;
1550 if (param != null) {
1551 saveDestOffset = param.getDestinationOffset();
1552 param.setDestinationOffset(new Point(0, 0));
1553 }
1554 retval = readInternal(imageIndex, param, true);
1555 // Apply the destination offset, if any, as a logical offset
1556 if (saveDestOffset != null) {
1557 target = target.createWritableTranslatedChild(saveDestOffset.x,
1558 saveDestOffset.y);
1559 }
1560 } catch (RuntimeException e) {
1561 resetLibraryState(structPointer);
1562 throw e;
1563 } catch (IOException e) {
1564 resetLibraryState(structPointer);
1565 throw e;
1566 } finally {
1567 clearThreadLock();
1568 }
1569 return retval;
1570 }
1571
1572 @Override
1573 public boolean readerSupportsThumbnails() {
1574 return true;
1575 }
1576
1577 @Override
1578 public int getNumThumbnails(int imageIndex) throws IOException {
1579 setThreadLock();
1580 try {
1581 cbLock.check();
1582
1583 getImageMetadata(imageIndex); // checks iis state for us
1584 // Now check the jfif segments
1585 JFIFMarkerSegment jfif =
1586 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1587 (JFIFMarkerSegment.class, true);
1588 int retval = 0;
1589 if (jfif != null) {
1590 retval = (jfif.thumb == null) ? 0 : 1;
1591 retval += jfif.extSegments.size();
1592 }
1593 return retval;
1594 } finally {
1595 clearThreadLock();
1596 }
1597 }
1598
1599 @Override
1600 public int getThumbnailWidth(int imageIndex, int thumbnailIndex)
1601 throws IOException {
1602 setThreadLock();
1603 try {
1604 cbLock.check();
1605
1606 if ((thumbnailIndex < 0)
1607 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1608 throw new IndexOutOfBoundsException("No such thumbnail");
1609 }
1610 // Now we know that there is a jfif segment
1611 JFIFMarkerSegment jfif =
1612 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1613 (JFIFMarkerSegment.class, true);
1614 return jfif.getThumbnailWidth(thumbnailIndex);
1615 } finally {
1616 clearThreadLock();
1617 }
1618 }
1619
1620 @Override
1621 public int getThumbnailHeight(int imageIndex, int thumbnailIndex)
1622 throws IOException {
1623 setThreadLock();
1624 try {
1625 cbLock.check();
1626
1627 if ((thumbnailIndex < 0)
1628 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1629 throw new IndexOutOfBoundsException("No such thumbnail");
1630 }
1631 // Now we know that there is a jfif segment
1632 JFIFMarkerSegment jfif =
1633 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1634 (JFIFMarkerSegment.class, true);
1635 return jfif.getThumbnailHeight(thumbnailIndex);
1636 } finally {
1637 clearThreadLock();
1638 }
1639 }
1640
1641 @Override
1642 public BufferedImage readThumbnail(int imageIndex,
1643 int thumbnailIndex)
1644 throws IOException {
1645 setThreadLock();
1646 try {
1647 cbLock.check();
1648
1649 if ((thumbnailIndex < 0)
1650 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1651 throw new IndexOutOfBoundsException("No such thumbnail");
1652 }
1653 // Now we know that there is a jfif segment and that iis is good
1654 JFIFMarkerSegment jfif =
1655 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1656 (JFIFMarkerSegment.class, true);
1657 return jfif.getThumbnail(iis, thumbnailIndex, this);
1658 } finally {
1659 clearThreadLock();
1660 }
1661 }
1666
1667 // reset local Java structures
1668 numImages = 0;
1669 imagePositions = new ArrayList<>();
1670 currentImage = -1;
1671 image = null;
1672 raster = null;
1673 target = null;
1674 buffer = null;
1675 destROI = null;
1676 destinationBands = null;
1677 streamMetadata = null;
1678 imageMetadata = null;
1679 imageMetadataIndex = -1;
1680 haveSeeked = false;
1681 tablesOnlyChecked = false;
1682 iccCS = null;
1683 initProgressData();
1684 }
1685
1686 @Override
1687 public void reset() {
1688 setThreadLock();
1689 try {
1690 cbLock.check();
1691 super.reset();
1692 } finally {
1693 clearThreadLock();
1694 }
1695 }
1696
1697 private native void resetReader(long structPointer);
1698
1699 @Override
1700 public void dispose() {
1701 setThreadLock();
1702 try {
1703 cbLock.check();
1704
1705 if (structPointer != 0) {
1706 disposerRecord.dispose();
1707 structPointer = 0;
1708 }
1709 } finally {
1710 clearThreadLock();
1711 }
1712 }
1713
1714 private static native void disposeReader(long structPointer);
1715
1716 private static class JPEGReaderDisposerRecord implements DisposerRecord {
1717 private long pData;
1718
1719 public JPEGReaderDisposerRecord(long pData) {
1720 this.pData = pData;
1721 }
1722
1723 @Override
1724 public synchronized void dispose() {
1725 if (pData != 0) {
1726 disposeReader(pData);
1727 pData = 0;
1728 }
1729 }
1730 }
1731
1732 private Thread theThread = null;
1733 private int theLockCount = 0;
1734
1735 private synchronized void setThreadLock() {
1736 Thread currThread = Thread.currentThread();
1737 if (theThread != null) {
1738 if (theThread != currThread) {
1739 // it looks like that this reader instance is used
1740 // by multiple threads.
1741 throw new IllegalStateException("Attempt to use instance of " +
1742 this + " locked on thread " +
1743 theThread + " from thread " +
|