1. /*
  2. * @(#)JPEGImageReader.java 1.44 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.imageio.plugins.jpeg;
  8. import javax.imageio.IIOException;
  9. import javax.imageio.ImageReader;
  10. import javax.imageio.ImageReadParam;
  11. import javax.imageio.ImageTypeSpecifier;
  12. import javax.imageio.metadata.IIOMetadata;
  13. import javax.imageio.spi.ImageReaderSpi;
  14. import javax.imageio.stream.ImageInputStream;
  15. import javax.imageio.plugins.jpeg.JPEGImageReadParam;
  16. import javax.imageio.plugins.jpeg.JPEGQTable;
  17. import javax.imageio.plugins.jpeg.JPEGHuffmanTable;
  18. import java.awt.Point;
  19. import java.awt.Rectangle;
  20. import java.awt.color.ColorSpace;
  21. import java.awt.color.ICC_Profile;
  22. import java.awt.color.ICC_ColorSpace;
  23. import java.awt.image.BufferedImage;
  24. import java.awt.image.Raster;
  25. import java.awt.image.WritableRaster;
  26. import java.awt.image.DataBuffer;
  27. import java.awt.image.DataBufferByte;
  28. import java.awt.image.ColorModel;
  29. import java.awt.image.IndexColorModel;
  30. import java.awt.image.ColorConvertOp;
  31. import java.io.IOException;
  32. import java.util.List;
  33. import java.util.Iterator;
  34. import java.util.ArrayList;
  35. public class JPEGImageReader extends ImageReader {
  36. private boolean debug = false;
  37. /**
  38. * The following variable contains a pointer to the IJG library
  39. * structure for this reader. It is assigned in the constructor
  40. * and then is passed in to every native call. It is set to 0
  41. * by dispose to avoid disposing twice.
  42. */
  43. private long structPointer = 0;
  44. /** The input stream we read from */
  45. private ImageInputStream iis = null;
  46. /**
  47. * List of stream positions for images, reinitialized every time
  48. * a new input source is set.
  49. */
  50. private List imagePositions = null;
  51. /**
  52. * The number of images in the stream, or 0.
  53. */
  54. private int numImages = 0;
  55. static {
  56. java.security.AccessController.doPrivileged(
  57. new sun.security.action.LoadLibraryAction("jpeg"));
  58. initReaderIDs(ImageInputStream.class);
  59. }
  60. // The following warnings are converted to strings when used
  61. // as keys to get localized resources from JPEGImageReaderResources
  62. // and its children.
  63. /**
  64. * Warning code to be passed to warningOccurred to indicate
  65. * that the EOI marker is missing from the end of the stream.
  66. * This usually signals that the stream is corrupted, but
  67. * everything up to the last MCU should be usable.
  68. */
  69. protected static final int WARNING_NO_EOI = 0;
  70. /**
  71. * Warning code to be passed to warningOccurred to indicate
  72. * that a JFIF segment was encountered inside a JFXX JPEG
  73. * thumbnail and is being ignored.
  74. */
  75. protected static final int WARNING_NO_JFIF_IN_THUMB = 1;
  76. private static final int MAX_WARNING = WARNING_NO_JFIF_IN_THUMB;
  77. /**
  78. * Image index of image for which header information
  79. * is available.
  80. */
  81. private int currentImage = -1;
  82. // The following is copied out from C after reading the header.
  83. // Unlike metadata, which may never be retrieved, we need this
  84. // if we are to read an image at all.
  85. /** Set by setImageData native code callback */
  86. private int width;
  87. /** Set by setImageData native code callback */
  88. private int height;
  89. /**
  90. * Set by setImageData native code callback. A modified
  91. * IJG+NIFTY colorspace code.
  92. */
  93. private int colorSpaceCode;
  94. /**
  95. * Set by setImageData native code callback. A modified
  96. * IJG+NIFTY colorspace code.
  97. */
  98. private int outColorSpaceCode;
  99. /** Set by setImageData native code callback */
  100. private int numComponents;
  101. /** Set by setImageData native code callback */
  102. private ColorSpace iccCS = null;
  103. /** If we need to post-convert in Java, convert with this op */
  104. private ColorConvertOp convert = null;
  105. /** The image we are going to fill */
  106. private BufferedImage image = null;
  107. /** An intermediate Raster to hold decoded data */
  108. private WritableRaster raster = null;
  109. /** A view of our target Raster that we can setRect to */
  110. private WritableRaster target = null;
  111. /** The databuffer for the above Raster */
  112. private DataBufferByte buffer = null;
  113. /** The region in the destination where we will write pixels */
  114. private Rectangle destROI = null;
  115. /** The list of destination bands, if any */
  116. private int [] destinationBands = null;
  117. /** Stream metadata, cached, even when the stream is changed. */
  118. private JPEGMetadata streamMetadata = null;
  119. /** Image metadata, valid for the imageMetadataIndex only. */
  120. private JPEGMetadata imageMetadata = null;
  121. private int imageMetadataIndex = -1;
  122. /**
  123. * Set to true every time we seek in the stream; used to
  124. * invalidate the native buffer contents in C.
  125. */
  126. private boolean haveSeeked = false;
  127. /**
  128. * Tables that have been read from a tables-only image at the
  129. * beginning of a stream.
  130. */
  131. private JPEGQTable [] abbrevQTables = null;
  132. private JPEGHuffmanTable[] abbrevDCHuffmanTables = null;
  133. private JPEGHuffmanTable[] abbrevACHuffmanTables = null;
  134. private int minProgressivePass = 0;
  135. private int maxProgressivePass = Integer.MAX_VALUE;
  136. /**
  137. * Variables used by progress monitoring.
  138. */
  139. private static final int UNKNOWN = -1; // Number of passes
  140. private static final int MIN_ESTIMATED_PASSES = 10; // IJG default
  141. private int knownPassCount = UNKNOWN;
  142. private int pass = 0;
  143. private float percentToDate = 0.0F;
  144. private float previousPassPercentage = 0.0F;
  145. private int progInterval = 0;
  146. /**
  147. * Set to true once stream has been checked for stream metadata
  148. */
  149. private boolean tablesOnlyChecked = false;
  150. /**
  151. * Maintain an array of the default image types corresponding to the
  152. * various supported IJG colorspace codes.
  153. */
  154. private static final ImageTypeSpecifier [] defaultTypes =
  155. new ImageTypeSpecifier [JPEG.NUM_JCS_CODES];
  156. static {
  157. defaultTypes[JPEG.JCS_GRAYSCALE] =
  158. ImageTypeSpecifier.createFromBufferedImageType
  159. (BufferedImage.TYPE_BYTE_GRAY);
  160. defaultTypes[JPEG.JCS_RGB] =
  161. ImageTypeSpecifier.createInterleaved
  162. (JPEG.sRGB,
  163. JPEG.bOffsRGB,
  164. DataBuffer.TYPE_BYTE,
  165. false,
  166. false);
  167. defaultTypes[JPEG.JCS_RGBA] =
  168. ImageTypeSpecifier.createPacked
  169. (JPEG.sRGB,
  170. 0xff000000,
  171. 0x00ff0000,
  172. 0x0000ff00,
  173. 0x000000ff,
  174. DataBuffer.TYPE_INT,
  175. false);
  176. if (JPEG.YCC != null) {
  177. defaultTypes[JPEG.JCS_YCC] =
  178. ImageTypeSpecifier.createInterleaved
  179. (JPEG.YCC,
  180. JPEG.bandOffsets[2],
  181. DataBuffer.TYPE_BYTE,
  182. false,
  183. false);
  184. defaultTypes[JPEG.JCS_YCCA] =
  185. ImageTypeSpecifier.createInterleaved
  186. (JPEG.YCC,
  187. JPEG.bandOffsets[3],
  188. DataBuffer.TYPE_BYTE,
  189. true,
  190. false);
  191. }
  192. }
  193. /** Sets up static C structures. */
  194. private static native void initReaderIDs(Class iisClass);
  195. public JPEGImageReader(ImageReaderSpi originator) {
  196. super(originator);
  197. structPointer = initJPEGImageReader();
  198. }
  199. /** Sets up per-reader C structure and returns a pointer to it. */
  200. private native long initJPEGImageReader();
  201. /**
  202. * Called by the native code or other classes to signal a warning.
  203. * The code is used to lookup a localized message to be used when
  204. * sending warnings to listeners.
  205. */
  206. protected void warningOccurred(int code) {
  207. if ((code < 0) || (code > MAX_WARNING)){
  208. throw new InternalError("Invalid warning index");
  209. }
  210. processWarningOccurred
  211. ("com.sun.imageio.plugins.jpeg.JPEGImageReaderResources",
  212. Integer.toString(code));
  213. }
  214. /**
  215. * The library has it's own error facility that emits warning messages.
  216. * This routine is called by the native code when it has already
  217. * formatted a string for output.
  218. * XXX For truly complete localization of all warning messages,
  219. * the sun_jpeg_output_message routine in the native code should
  220. * send only the codes and parameters to a method here in Java,
  221. * which will then format and send the warnings, using localized
  222. * strings. This method will have to deal with all the parameters
  223. * and formats (%u with possibly large numbers, %02d, %02x, etc.)
  224. * that actually occur in the JPEG library. For now, this prevents
  225. * library warnings from being printed to stderr.
  226. */
  227. protected void warningWithMessage(String msg) {
  228. processWarningOccurred(msg);
  229. }
  230. public void setInput(Object input,
  231. boolean seekForwardOnly,
  232. boolean ignoreMetadata) {
  233. super.setInput(input, seekForwardOnly, ignoreMetadata);
  234. this.ignoreMetadata = ignoreMetadata;
  235. iis = (ImageInputStream) input; // Always works
  236. numImages = 0;
  237. imagePositions = new ArrayList();
  238. setSource(structPointer, iis);
  239. imageMetadata = null;
  240. imageMetadataIndex = -1;
  241. tablesOnlyChecked = false;
  242. haveSeeked = false;
  243. }
  244. private native void setSource(long structPointer,
  245. ImageInputStream source);
  246. private void checkTablesOnly() throws IOException {
  247. if (debug) {
  248. System.out.println("Checking for tables-only image");
  249. }
  250. long savePos = iis.getStreamPosition();
  251. if (debug) {
  252. System.out.println("saved pos is " + savePos);
  253. System.out.println("length is " + iis.length());
  254. }
  255. // Read the first header
  256. boolean tablesOnly = readNativeHeader(true);
  257. if (tablesOnly) {
  258. if (debug) {
  259. System.out.println("tables-only image found");
  260. long pos = iis.getStreamPosition();
  261. System.out.println("pos after return from native is " + pos);
  262. }
  263. // This reads the tables-only image twice, once from C
  264. // and once from Java, but only if ignoreMetadata is false
  265. if (ignoreMetadata == false) {
  266. iis.seek(savePos);
  267. haveSeeked = true;
  268. streamMetadata = new JPEGMetadata(true, false,
  269. iis, this);
  270. long pos = iis.getStreamPosition();
  271. if (debug) {
  272. System.out.println
  273. ("pos after constructing stream metadata is " + pos);
  274. }
  275. }
  276. // Now we are at the first image if there are any, so add it
  277. // to the list
  278. if (hasNextImage()) {
  279. imagePositions.add(new Long(iis.getStreamPosition()));
  280. }
  281. } else { // Not tables only, so add original pos to the list
  282. imagePositions.add(new Long(savePos));
  283. // And set current image since we've read it now
  284. currentImage = 0;
  285. }
  286. if (seekForwardOnly) {
  287. Long pos = (Long) imagePositions.get(imagePositions.size()-1);
  288. iis.flushBefore(pos.longValue());
  289. }
  290. tablesOnlyChecked = true;
  291. }
  292. public int getNumImages(boolean allowSearch) throws IOException {
  293. if (numImages != 0) {
  294. return numImages;
  295. }
  296. if (iis == null) {
  297. throw new IllegalStateException("Input not set");
  298. }
  299. if (allowSearch == true) {
  300. if (seekForwardOnly) {
  301. throw new IllegalStateException(
  302. "seekForwardOnly and allowSearch can't both be true!");
  303. }
  304. // Otherwise we have to read the entire stream
  305. if (!tablesOnlyChecked) {
  306. checkTablesOnly();
  307. }
  308. iis.mark();
  309. gotoImage(0);
  310. JPEGBuffer buffer = new JPEGBuffer(iis);
  311. buffer.loadBuf(0);
  312. boolean done = false;
  313. while (!done) {
  314. done = buffer.scanForFF(this);
  315. switch (buffer.buf[buffer.bufPtr] & 0xff) {
  316. case JPEG.SOI:
  317. numImages++;
  318. // FALL THROUGH to decrement buffer vars
  319. // This first set doesn't have a length
  320. case 0: // not a marker, just a data 0xff
  321. case JPEG.RST0:
  322. case JPEG.RST1:
  323. case JPEG.RST2:
  324. case JPEG.RST3:
  325. case JPEG.RST4:
  326. case JPEG.RST5:
  327. case JPEG.RST6:
  328. case JPEG.RST7:
  329. case JPEG.EOI:
  330. buffer.bufAvail--;
  331. buffer.bufPtr++;
  332. break;
  333. // All the others have a length
  334. default:
  335. buffer.bufAvail--;
  336. buffer.bufPtr++;
  337. buffer.loadBuf(2);
  338. int length = ((buffer.buf[buffer.bufPtr++] & 0xff) << 8) |
  339. (buffer.buf[buffer.bufPtr++] & 0xff);
  340. buffer.bufAvail -= 2;
  341. length -= 2; // length includes itself
  342. buffer.skipData(length);
  343. }
  344. }
  345. iis.reset();
  346. return numImages;
  347. }
  348. return -1; // Search is necessary for JPEG
  349. }
  350. /**
  351. * Sets the input stream to the start of the requested image.
  352. * <pre>
  353. * @exception IllegalStateException if the input source has not been
  354. * set.
  355. * @exception IndexOutOfBoundsException if the supplied index is
  356. * out of bounds.
  357. * </pre>
  358. */
  359. private void gotoImage(int imageIndex) throws IOException {
  360. if (iis == null) {
  361. throw new IllegalStateException("Input not set");
  362. }
  363. if (imageIndex < minIndex) {
  364. throw new IndexOutOfBoundsException();
  365. }
  366. if (!tablesOnlyChecked) {
  367. checkTablesOnly();
  368. }
  369. if (imageIndex < imagePositions.size()) {
  370. iis.seek(((Long)(imagePositions.get(imageIndex))).longValue());
  371. } else {
  372. // read to start of image, saving positions
  373. // First seek to the last position we already have, and skip the
  374. // entire image
  375. Long pos = (Long) imagePositions.get(imagePositions.size()-1);
  376. iis.seek(pos.longValue());
  377. skipImage();
  378. // Now add all intervening positions, skipping images
  379. for (int index = imagePositions.size();
  380. index <= imageIndex;
  381. index++) {
  382. // Is there an image?
  383. if (!hasNextImage()) {
  384. throw new IndexOutOfBoundsException();
  385. }
  386. pos = new Long(iis.getStreamPosition());
  387. imagePositions.add(pos);
  388. if (seekForwardOnly) {
  389. iis.flushBefore(pos.longValue());
  390. }
  391. if (index < imageIndex) {
  392. skipImage();
  393. } // Otherwise we are where we want to be
  394. }
  395. }
  396. if (seekForwardOnly) {
  397. minIndex = imageIndex;
  398. }
  399. haveSeeked = true; // No way is native buffer still valid
  400. }
  401. /**
  402. * Skip over a complete image in the stream, leaving the stream
  403. * positioned such that the next byte to be read is the first
  404. * byte of the next image. For JPEG, this means that we read
  405. * until we encounter an EOI marker or until the end of the stream.
  406. * If the stream ends before an EOI marker is encountered, an
  407. * IndexOutOfBoundsException is thrown.
  408. */
  409. private void skipImage() throws IOException {
  410. if (debug) {
  411. System.out.println("skipImage called");
  412. }
  413. boolean foundFF = false;
  414. for (int byteval = iis.read();
  415. byteval != -1;
  416. byteval = iis.read()) {
  417. if (foundFF == true) {
  418. if (byteval == JPEG.EOI) {
  419. return;
  420. }
  421. }
  422. foundFF = (byteval == 0xff) ? true : false;
  423. }
  424. throw new IndexOutOfBoundsException();
  425. }
  426. /**
  427. * Returns <code>true</code> if there is an image beyond
  428. * the current stream position. Does not disturb the
  429. * stream position.
  430. */
  431. private boolean hasNextImage() throws IOException {
  432. if (debug) {
  433. System.out.print("hasNextImage called; returning ");
  434. }
  435. iis.mark();
  436. boolean foundFF = false;
  437. for (int byteval = iis.read();
  438. byteval != -1;
  439. byteval = iis.read()) {
  440. if (foundFF == true) {
  441. if (byteval == JPEG.SOI) {
  442. iis.reset();
  443. if (debug) {
  444. System.out.println("true");
  445. }
  446. return true;
  447. }
  448. }
  449. foundFF = (byteval == 0xff) ? true : false;
  450. }
  451. // We hit the end of the stream before we hit an SOI, so no image
  452. iis.reset();
  453. if (debug) {
  454. System.out.println("false");
  455. }
  456. return false;
  457. }
  458. /**
  459. * Push back the given number of bytes to the input stream.
  460. * Called by the native code at the end of each image so
  461. * that the next one can be identified from Java.
  462. */
  463. private void pushBack(int num) throws IOException {
  464. if (debug) {
  465. System.out.println("pushing back " + num + " bytes");
  466. }
  467. iis.seek(iis.getStreamPosition()-num);
  468. // The buffer is clear after this, so no need to set haveSeeked.
  469. }
  470. /**
  471. * Reads header information for the given image, if possible.
  472. */
  473. private void readHeader(int imageIndex, boolean reset)
  474. throws IOException {
  475. gotoImage(imageIndex);
  476. readNativeHeader(reset); // Ignore return
  477. currentImage = imageIndex;
  478. }
  479. private boolean readNativeHeader(boolean reset) throws IOException {
  480. boolean retval = false;
  481. retval = readImageHeader(structPointer, haveSeeked, reset);
  482. haveSeeked = false;
  483. return retval;
  484. }
  485. /**
  486. * Read in the header information starting from the current
  487. * stream position, returning <code>true</code> if the
  488. * header was a tables-only image. After this call, the
  489. * native IJG decompression struct will contain the image
  490. * information required by most query calls below
  491. * (e.g. getWidth, getHeight, etc.), if the header was not
  492. * a tables-only image.
  493. * If reset is <code>true</code>, the state of the IJG
  494. * object is reset so that it can read a header again.
  495. * This happens automatically if the header was a tables-only
  496. * image.
  497. */
  498. private native boolean readImageHeader(long structPointer,
  499. boolean clearBuffer,
  500. boolean reset)
  501. throws IOException;
  502. /*
  503. * Called by the native code whenever an image header has been
  504. * read. Whether we read metadata or not, we always need this
  505. * information, so it is passed back independently of
  506. * metadata, which may never be read.
  507. */
  508. private void setImageData(int width,
  509. int height,
  510. int colorSpaceCode,
  511. int outColorSpaceCode,
  512. int numComponents,
  513. byte [] iccData) {
  514. this.width = width;
  515. this.height = height;
  516. this.colorSpaceCode = colorSpaceCode;
  517. this.outColorSpaceCode = outColorSpaceCode;
  518. this.numComponents = numComponents;
  519. iccCS = null;
  520. if (iccData != null) {
  521. iccCS = new ICC_ColorSpace(ICC_Profile.getInstance(iccData));
  522. }
  523. }
  524. public int getWidth(int imageIndex) throws IOException {
  525. if (currentImage != imageIndex) {
  526. readHeader(imageIndex, true);
  527. }
  528. return width;
  529. }
  530. public int getHeight(int imageIndex) throws IOException {
  531. if (currentImage != imageIndex) {
  532. readHeader(imageIndex, true);
  533. }
  534. return height;
  535. }
  536. /////////// Color Conversion and Image Types
  537. /**
  538. * Return an ImageTypeSpecifier corresponding to the given
  539. * color space code, or null if the color space is unsupported.
  540. */
  541. private ImageTypeSpecifier getImageType(int code) {
  542. ImageTypeSpecifier ret = null;
  543. if ((code > 0) && (code < JPEG.NUM_JCS_CODES)) {
  544. ret = defaultTypes[code];
  545. }
  546. return ret;
  547. }
  548. public ImageTypeSpecifier getRawImageType(int imageIndex)
  549. throws IOException {
  550. if (currentImage != imageIndex) {
  551. readHeader(imageIndex, true);
  552. }
  553. // Returns null if it can't be represented
  554. return getImageType(colorSpaceCode);
  555. }
  556. public Iterator getImageTypes(int imageIndex)
  557. throws IOException {
  558. if (currentImage != imageIndex) {
  559. readHeader(imageIndex, true);
  560. }
  561. // We return an iterator containing the default, any
  562. // conversions that the library provides, and
  563. // all the other default types with the same number
  564. // of components, as we can do these as a post-process.
  565. // As we convert Rasters rather than images, images
  566. // with alpha cannot be converted in a post-process.
  567. // If this image can't be interpreted, this method
  568. // returns an empty Iterator.
  569. // Get the raw ITS, if there is one. Note that this
  570. // won't always be the same as the default.
  571. ImageTypeSpecifier raw = getImageType(colorSpaceCode);
  572. // Given the encoded colorspace, build a list of ITS's
  573. // representing outputs you could handle starting
  574. // with the default.
  575. ArrayList list = new ArrayList(1);
  576. switch (colorSpaceCode) {
  577. case JPEG.JCS_GRAYSCALE:
  578. list.add(raw);
  579. list.add(getImageType(JPEG.JCS_RGB));
  580. break;
  581. case JPEG.JCS_RGB:
  582. list.add(raw);
  583. list.add(getImageType(JPEG.JCS_GRAYSCALE));
  584. if (JPEG.YCC != null) {
  585. list.add(getImageType(JPEG.JCS_YCC));
  586. }
  587. break;
  588. case JPEG.JCS_RGBA:
  589. list.add(raw);
  590. break;
  591. case JPEG.JCS_YCC:
  592. if (raw != null) { // Might be null if PYCC.pf not installed
  593. list.add(raw);
  594. list.add(getImageType(JPEG.JCS_RGB));
  595. }
  596. break;
  597. case JPEG.JCS_YCCA:
  598. if (raw != null) { // Might be null if PYCC.pf not installed
  599. list.add(raw);
  600. }
  601. break;
  602. case JPEG.JCS_YCbCr:
  603. // As there is no YCbCr ColorSpace, we can't support
  604. // the raw type.
  605. // If there is an ICC Profile, use that as the default
  606. if (iccCS != null) {
  607. list.add(ImageTypeSpecifier.createInterleaved
  608. (iccCS,
  609. JPEG.bOffsRGB, // Assume it's for RGB
  610. DataBuffer.TYPE_BYTE,
  611. false,
  612. false));
  613. }
  614. list.add(getImageType(JPEG.JCS_RGB));
  615. list.add(getImageType(JPEG.JCS_GRAYSCALE));
  616. if (JPEG.YCC != null) { // Might be null if PYCC.pf not installed
  617. list.add(getImageType(JPEG.JCS_YCC));
  618. }
  619. break;
  620. case JPEG.JCS_YCbCrA: // Default is to convert to RGBA
  621. // As there is no YCbCr ColorSpace, we can't support
  622. // the raw type.
  623. list.add(getImageType(JPEG.JCS_RGBA));
  624. break;
  625. }
  626. return list.iterator();
  627. }
  628. /**
  629. * Checks the implied color conversion between the stream and
  630. * the target image, altering the IJG output color space if necessary.
  631. * If a java color conversion is required, then this sets up
  632. * <code>convert</code>.
  633. * If bands are being rearranged at all (either source or destination
  634. * bands are specified in the param), then the default color
  635. * conversions are assumed to be correct.
  636. * Throws an IIOException if there is no conversion available.
  637. */
  638. private void checkColorConversion(BufferedImage image,
  639. ImageReadParam param)
  640. throws IIOException {
  641. // If we are rearranging channels at all, the default
  642. // conversions remain in place. If the user wants
  643. // raw channels then he should do this while reading
  644. // a Raster.
  645. if (param != null) {
  646. if ((param.getSourceBands() != null) ||
  647. (param.getDestinationBands() != null)) {
  648. // Accept default conversions out of decoder, silently
  649. return;
  650. }
  651. }
  652. // XXX - We do not currently support any indexed color models,
  653. // though we could, as IJG will quantize for us.
  654. // This is a performance and memory-use issue, as
  655. // users can read RGB and then convert to indexed in Java.
  656. ColorModel cm = image.getColorModel();
  657. if (cm instanceof IndexColorModel) {
  658. throw new IIOException("IndexColorModel not supported");
  659. }
  660. // Now check the ColorSpace type against outColorSpaceCode
  661. // We may want to tweak the default
  662. ColorSpace cs = cm.getColorSpace();
  663. int csType = cs.getType();
  664. convert = null;
  665. switch (outColorSpaceCode) {
  666. case JPEG.JCS_GRAYSCALE: // Its gray in the file
  667. if (csType == ColorSpace.TYPE_RGB) { // We want RGB
  668. // IJG can do this for us more efficiently
  669. setOutColorSpace(structPointer, JPEG.JCS_RGB);
  670. } else if (csType != ColorSpace.TYPE_GRAY) {
  671. throw new IIOException("Incompatible color conversion");
  672. }
  673. break;
  674. case JPEG.JCS_RGB: // IJG wants to go to RGB
  675. if (csType == ColorSpace.TYPE_GRAY) { // We want gray
  676. if (colorSpaceCode == JPEG.JCS_YCbCr) {
  677. // If the jpeg space is YCbCr, IJG can do it
  678. setOutColorSpace(structPointer, JPEG.JCS_GRAYSCALE);
  679. }
  680. } else if ((iccCS != null) &&
  681. (cm.getNumComponents() == numComponents) &&
  682. (cs != iccCS)) {
  683. // We have an ICC profile but it isn't used in the dest
  684. // image. So convert from the profile cs to the target cs
  685. convert = new ColorConvertOp(iccCS, cs, null);
  686. // Leave IJG conversion in place; we still need it
  687. } else if ((!cs.isCS_sRGB()) &&
  688. (cm.getNumComponents() == numComponents)) {
  689. // Target isn't sRGB, so convert from sRGB to the target
  690. convert = new ColorConvertOp(JPEG.sRGB, cs, null);
  691. } else if (csType != ColorSpace.TYPE_RGB) {
  692. throw new IIOException("Incompatible color conversion");
  693. }
  694. break;
  695. case JPEG.JCS_RGBA:
  696. // No conversions available; image must be RGBA
  697. if ((csType != ColorSpace.TYPE_RGB) ||
  698. (cm.getNumComponents() != numComponents)) {
  699. throw new IIOException("Incompatible color conversion");
  700. }
  701. break;
  702. case JPEG.JCS_YCC:
  703. if (JPEG.YCC == null) { // We can't do YCC at all
  704. throw new IIOException("Incompatible color conversion");
  705. }
  706. if ((cs != JPEG.YCC) &&
  707. (cm.getNumComponents() == numComponents)) {
  708. convert = new ColorConvertOp(JPEG.YCC, cs, null);
  709. }
  710. break;
  711. case JPEG.JCS_YCCA:
  712. // No conversions available; image must be YCCA
  713. if ((JPEG.YCC == null) || // We can't do YCC at all
  714. (cs != JPEG.YCC) ||
  715. (cm.getNumComponents() != numComponents)) {
  716. throw new IIOException("Incompatible color conversion");
  717. }
  718. break;
  719. default:
  720. // Anything else we can't handle at all
  721. throw new IIOException("Incompatible color conversion");
  722. }
  723. }
  724. /**
  725. * Set the IJG output space to the given value. The library will
  726. * perform the appropriate colorspace conversions.
  727. */
  728. private native void setOutColorSpace(long structPointer, int id);
  729. /////// End of Color Conversion & Image Types
  730. public ImageReadParam getDefaultReadParam() {
  731. return new JPEGImageReadParam();
  732. }
  733. public IIOMetadata getStreamMetadata() throws IOException {
  734. if (!tablesOnlyChecked) {
  735. checkTablesOnly();
  736. }
  737. return streamMetadata;
  738. }
  739. public IIOMetadata getImageMetadata(int imageIndex)
  740. throws IOException {
  741. // imageMetadataIndex will always be either a valid index or
  742. // -1, in which case imageMetadata will not be null.
  743. // So we can leave checking imageIndex for gotoImage.
  744. if ((imageMetadataIndex == imageIndex)
  745. && (imageMetadata != null)) {
  746. return imageMetadata;
  747. }
  748. gotoImage(imageIndex);
  749. imageMetadata = new JPEGMetadata(false, false, iis, this);
  750. imageMetadataIndex = imageIndex;
  751. return imageMetadata;
  752. }
  753. public BufferedImage read(int imageIndex, ImageReadParam param)
  754. throws IOException {
  755. try {
  756. readInternal(imageIndex, param, false);
  757. } catch (RuntimeException e) {
  758. resetLibraryState(structPointer);
  759. throw e;
  760. } catch (IOException e) {
  761. resetLibraryState(structPointer);
  762. throw e;
  763. }
  764. BufferedImage ret = image;
  765. image = null; // don't keep a reference here
  766. return ret;
  767. }
  768. private Raster readInternal(int imageIndex,
  769. ImageReadParam param,
  770. boolean wantRaster) throws IOException {
  771. readHeader(imageIndex, false);
  772. WritableRaster imRas = null;
  773. int numImageBands = 0;
  774. if (!wantRaster){
  775. // Can we read this image?
  776. Iterator imageTypes = getImageTypes(imageIndex);
  777. if (imageTypes.hasNext() == false) {
  778. throw new IIOException("Unsupported Image Type");
  779. }
  780. image = getDestination(param, imageTypes, width, height);
  781. imRas = image.getRaster();
  782. // The destination may still be incompatible.
  783. numImageBands = image.getSampleModel().getNumBands();
  784. // Check whether we can handle any implied color conversion
  785. // Throws IIOException if the stream and the image are
  786. // incompatible, and sets convert if a java conversion
  787. // is necessary
  788. checkColorConversion(image, param);
  789. // Check the source and destination bands in the param
  790. checkReadParamBandSettings(param, numComponents, numImageBands);
  791. } else {
  792. // Set the output color space equal to the input colorspace
  793. // This disables all conversions
  794. setOutColorSpace(structPointer, colorSpaceCode);
  795. image = null;
  796. }
  797. // Create an intermediate 1-line Raster that will hold the decoded,
  798. // subsampled, clipped, band-selected image data in a single
  799. // byte-interleaved buffer. The above transformations
  800. // will occur in C for performance. Every time this Raster
  801. // is filled we will call back to acceptPixels below to copy
  802. // this to whatever kind of buffer our image has.
  803. int [] srcBands = JPEG.bandOffsets[numComponents-1];
  804. int numRasterBands = (wantRaster ? numComponents : numImageBands);
  805. destinationBands = null;
  806. Rectangle srcROI = new Rectangle(0, 0, 0, 0);
  807. destROI = new Rectangle(0, 0, 0, 0);
  808. // For Rasters, destination offset is logical, not physical, so
  809. // set it to 0 before calling computeRegions, so that the destination
  810. // region is not clipped.
  811. Point saveDestOffset = null;
  812. if (wantRaster) {
  813. if (param != null) {
  814. saveDestOffset = param.getDestinationOffset();
  815. param.setDestinationOffset(new Point(0, 0));
  816. }
  817. }
  818. computeRegions(param, width, height, image, srcROI, destROI);
  819. int periodX = 1;
  820. int periodY = 1;
  821. minProgressivePass = 0;
  822. maxProgressivePass = Integer.MAX_VALUE;
  823. if (param != null) {
  824. periodX = param.getSourceXSubsampling();
  825. periodY = param.getSourceYSubsampling();
  826. int[] sBands = param.getSourceBands();
  827. if (sBands != null) {
  828. srcBands = sBands;
  829. numRasterBands = srcBands.length;
  830. }
  831. if (!wantRaster) { // ignore dest bands for Raster
  832. destinationBands = param.getDestinationBands();
  833. }
  834. minProgressivePass = param.getSourceMinProgressivePass();
  835. maxProgressivePass = param.getSourceMaxProgressivePass();
  836. if (param instanceof JPEGImageReadParam) {
  837. JPEGImageReadParam jparam = (JPEGImageReadParam) param;
  838. if (jparam.areTablesSet()) {
  839. abbrevQTables = jparam.getQTables();
  840. abbrevDCHuffmanTables = jparam.getDCHuffmanTables();
  841. abbrevACHuffmanTables = jparam.getACHuffmanTables();
  842. }
  843. }
  844. }
  845. int lineSize = destROI.width*numRasterBands;
  846. buffer = new DataBufferByte(lineSize);
  847. int [] bandOffs = JPEG.bandOffsets[numRasterBands-1];
  848. raster = Raster.createInterleavedRaster(buffer,
  849. destROI.width, 1,
  850. lineSize,
  851. numRasterBands,
  852. bandOffs,
  853. null);
  854. // Now that we have the Raster we'll decode to, get a view of the
  855. // target Raster that will permit a simple setRect for each scanline
  856. if (wantRaster) {
  857. target = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
  858. destROI.width,
  859. destROI.height,
  860. lineSize,
  861. numRasterBands,
  862. bandOffs,
  863. null);
  864. } else {
  865. target = imRas.createWritableChild(destROI.x,
  866. destROI.y,
  867. destROI.width,
  868. destROI.height,
  869. 0, 0,
  870. destinationBands);
  871. }
  872. int [] bandSizes = target.getSampleModel().getSampleSize();
  873. /*
  874. * If the process is sequential, and we have restart markers,
  875. * we could skip to the correct restart marker, if the library
  876. * lets us. That's an optimization to investigate later.
  877. */
  878. // Check for update listeners (don't call back if none)
  879. boolean callbackUpdates = ((updateListeners != null)
  880. || (progressListeners != null));
  881. // Set up progression data
  882. initProgressData();
  883. // if we have a metadata object, we can count the scans
  884. // and set knownPassCount
  885. if (imageIndex == imageMetadataIndex) { // We have metadata
  886. knownPassCount = 0;
  887. for (Iterator iter = imageMetadata.markerSequence.iterator();
  888. iter.hasNext();) {
  889. if (iter.next() instanceof SOSMarkerSegment) {
  890. knownPassCount++;
  891. }
  892. }
  893. }
  894. progInterval = Math.max((target.getHeight()-1) / 20, 1);
  895. if (knownPassCount > 0) {
  896. progInterval *= knownPassCount;
  897. } else if (maxProgressivePass != Integer.MAX_VALUE) {
  898. progInterval *= (maxProgressivePass - minProgressivePass + 1);
  899. }
  900. if (debug) {
  901. System.out.println("**** Read Data *****");
  902. System.out.println("numRasterBands is " + numRasterBands);
  903. System.out.print("srcBands:");
  904. for (int i = 0; i<srcBands.length;i++)
  905. System.out.print(" " + srcBands[i]);
  906. System.out.println();
  907. System.out.println("destination bands is " + destinationBands);
  908. if (destinationBands != null) {
  909. for (int i = 0; i < destinationBands.length; i++) {
  910. System.out.print(" " + destinationBands[i]);
  911. }
  912. System.out.println();
  913. }
  914. System.out.println("sourceROI is " + srcROI);
  915. System.out.println("destROI is " + destROI);
  916. System.out.println("periodX is " + periodX);
  917. System.out.println("periodY is " + periodY);
  918. System.out.println("minProgressivePass is " + minProgressivePass);
  919. System.out.println("maxProgressivePass is " + maxProgressivePass);
  920. System.out.println("callbackUpdates is " + callbackUpdates);
  921. }
  922. // Finally, we are ready to read
  923. processImageStarted(currentImage);
  924. boolean aborted = false;
  925. aborted = readImage(structPointer,
  926. buffer.getData(),
  927. numRasterBands,
  928. srcBands,
  929. bandSizes,
  930. srcROI.x, srcROI.y,
  931. srcROI.width, srcROI.height,
  932. periodX, periodY,
  933. abbrevQTables,
  934. abbrevDCHuffmanTables,
  935. abbrevACHuffmanTables,
  936. minProgressivePass, maxProgressivePass,
  937. callbackUpdates);
  938. if (aborted) {
  939. processReadAborted();
  940. } else {
  941. processImageComplete();
  942. }
  943. // Apply the destination offset, if any, as a logical offset
  944. // This applies only to Rasters
  945. if (saveDestOffset != null) {
  946. target = target.createWritableTranslatedChild(saveDestOffset.x,
  947. saveDestOffset.y);
  948. }
  949. return target;
  950. }
  951. /**
  952. * This method is called back from C when the intermediate Raster
  953. * is full. The parameter indicates the scanline in the target
  954. * Raster to which the intermediate Raster should be copied.
  955. * After the copy, we notify update listeners.
  956. */
  957. private void acceptPixels(int y, boolean progressive) {
  958. if (convert != null) {
  959. convert.filter(raster, raster);
  960. }
  961. target.setRect(0, y, raster);
  962. processImageUpdate(image,
  963. destROI.x, destROI.y+y,
  964. raster.getWidth(), 1,
  965. 1, 1,
  966. destinationBands);
  967. if ((y > 0) && (y%progInterval == 0)) {
  968. int height = target.getHeight()-1;
  969. float percentOfPass = ((float)y)/height;
  970. if (progressive) {
  971. if (knownPassCount != UNKNOWN) {
  972. processImageProgress((pass + percentOfPass)*100.0F
  973. / knownPassCount);
  974. } else if (maxProgressivePass != Integer.MAX_VALUE) {
  975. // Use the range of allowed progressive passes
  976. processImageProgress((pass + percentOfPass)*100.0F
  977. / (maxProgressivePass - minProgressivePass + 1));
  978. } else {
  979. // Assume there are a minimum of MIN_ESTIMATED_PASSES
  980. // and that there is always one more pass
  981. // Compute the percentage as the percentage at the end
  982. // of the previous pass, plus the percentage of this
  983. // pass scaled to be the percentage of the total remaining,
  984. // assuming a minimum of MIN_ESTIMATED_PASSES passes and
  985. // that there is always one more pass. This is monotonic
  986. // and asymptotic to 1.0, which is what we need.
  987. int remainingPasses = // including this one
  988. Math.max(2, MIN_ESTIMATED_PASSES-pass);
  989. int totalPasses = pass + remainingPasses-1;
  990. progInterval = Math.max(height20*totalPasses,
  991. totalPasses);
  992. if (y%progInterval == 0) {
  993. percentToDate = previousPassPercentage +
  994. (1.0F - previousPassPercentage)
  995. * (percentOfPass)/remainingPasses;
  996. if (debug) {
  997. System.out.print("pass= " + pass);
  998. System.out.print(", y= " + y);
  999. System.out.print(", progInt= " + progInterval);
  1000. System.out.print(", % of pass: " + percentOfPass);
  1001. System.out.print(", rem. passes: "
  1002. + remainingPasses);
  1003. System.out.print(", prev%: "
  1004. + previousPassPercentage);
  1005. System.out.print(", %ToDate: " + percentToDate);
  1006. System.out.print(" ");
  1007. }
  1008. processImageProgress(percentToDate*100.0F);
  1009. }
  1010. }
  1011. } else {
  1012. processImageProgress(percentOfPass * 100.0F);
  1013. }
  1014. }
  1015. }
  1016. private void initProgressData() {
  1017. knownPassCount = UNKNOWN;
  1018. pass = 0;
  1019. percentToDate = 0.0F;
  1020. previousPassPercentage = 0.0F;
  1021. progInterval = 0;
  1022. }
  1023. private void passStarted (int pass) {
  1024. this.pass = pass;
  1025. previousPassPercentage = percentToDate;
  1026. processPassStarted(image,
  1027. pass,
  1028. minProgressivePass,
  1029. maxProgressivePass,
  1030. 0, 0,
  1031. 1,1,
  1032. destinationBands);
  1033. }
  1034. private void passComplete () {
  1035. processPassComplete(image);
  1036. }
  1037. void thumbnailStarted(int thumbnailIndex) {
  1038. processThumbnailStarted(currentImage, thumbnailIndex);
  1039. }
  1040. // Provide access to protected superclass method
  1041. void thumbnailProgress(float percentageDone) {
  1042. processThumbnailProgress(percentageDone);
  1043. }
  1044. // Provide access to protected superclass method
  1045. void thumbnailComplete() {
  1046. processThumbnailComplete();
  1047. }
  1048. /**
  1049. * Returns <code>true</code> if the read was aborted.
  1050. */
  1051. private native boolean readImage(long structPointer,
  1052. byte [] buffer,
  1053. int numRasterBands,
  1054. int [] srcBands,
  1055. int [] bandSizes,
  1056. int sourceXOffset, int sourceYOffset,
  1057. int sourceWidth, int sourceHeight,
  1058. int periodX, int periodY,
  1059. JPEGQTable [] abbrevQTables,
  1060. JPEGHuffmanTable [] abbrevDCHuffmanTables,
  1061. JPEGHuffmanTable [] abbrevACHuffmanTables,
  1062. int minProgressivePass,
  1063. int maxProgressivePass,
  1064. boolean wantUpdates);
  1065. public void abort() {
  1066. super.abort();
  1067. abortRead(structPointer);
  1068. }
  1069. /** Set the C level abort flag. Keep it atomic for thread safety. */
  1070. private native void abortRead(long structPointer);
  1071. /** Resets library state when an exception occurred during a read. */
  1072. private native void resetLibraryState(long structPointer);
  1073. public boolean canReadRaster() {
  1074. return true;
  1075. }
  1076. public Raster readRaster(int imageIndex, ImageReadParam param)
  1077. throws IOException {
  1078. Raster retval = null;
  1079. try {
  1080. retval = readInternal(imageIndex, param, true);
  1081. } catch (RuntimeException e) {
  1082. resetLibraryState(structPointer);
  1083. throw e;
  1084. } catch (IOException e) {
  1085. resetLibraryState(structPointer);
  1086. throw e;
  1087. }
  1088. return retval;
  1089. }
  1090. public boolean readerSupportsThumbnails() {
  1091. return true;
  1092. }
  1093. public int getNumThumbnails(int imageIndex) throws IOException {
  1094. getImageMetadata(imageIndex); // checks iis state for us
  1095. // Now check the jfif segments
  1096. JFIFMarkerSegment jfif =
  1097. (JFIFMarkerSegment) imageMetadata.findMarkerSegment
  1098. (JFIFMarkerSegment.class, true);
  1099. int retval = 0;
  1100. if (jfif != null) {
  1101. retval = (jfif.thumb == null) ? 0 : 1;
  1102. retval += jfif.extSegments.size();
  1103. }
  1104. return retval;
  1105. }
  1106. public int getThumbnailWidth(int imageIndex, int thumbnailIndex)
  1107. throws IOException {
  1108. if ((thumbnailIndex < 0)
  1109. || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
  1110. throw new IndexOutOfBoundsException("No such thumbnail");
  1111. }
  1112. // Now we know that there is a jfif segment
  1113. JFIFMarkerSegment jfif =
  1114. (JFIFMarkerSegment) imageMetadata.findMarkerSegment
  1115. (JFIFMarkerSegment.class, true);
  1116. return jfif.getThumbnailWidth(thumbnailIndex);
  1117. }
  1118. public int getThumbnailHeight(int imageIndex, int thumbnailIndex)
  1119. throws IOException {
  1120. if ((thumbnailIndex < 0)
  1121. || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
  1122. throw new IndexOutOfBoundsException("No such thumbnail");
  1123. }
  1124. // Now we know that there is a jfif segment
  1125. JFIFMarkerSegment jfif =
  1126. (JFIFMarkerSegment) imageMetadata.findMarkerSegment
  1127. (JFIFMarkerSegment.class, true);
  1128. return jfif.getThumbnailHeight(thumbnailIndex);
  1129. }
  1130. public BufferedImage readThumbnail(int imageIndex,
  1131. int thumbnailIndex)
  1132. throws IOException {
  1133. if ((thumbnailIndex < 0)
  1134. || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
  1135. throw new IndexOutOfBoundsException("No such thumbnail");
  1136. }
  1137. // Now we know that there is a jfif segment and that iis is good
  1138. JFIFMarkerSegment jfif =
  1139. (JFIFMarkerSegment) imageMetadata.findMarkerSegment
  1140. (JFIFMarkerSegment.class, true);
  1141. return jfif.getThumbnail(iis, thumbnailIndex, this);
  1142. }
  1143. public void reset() {
  1144. // reset C structures
  1145. resetReader(structPointer);
  1146. // reset local Java structures
  1147. super.reset();
  1148. iis = null;
  1149. numImages = 0;
  1150. imagePositions = new ArrayList();
  1151. currentImage = -1;
  1152. image = null;
  1153. raster = null;
  1154. target = null;
  1155. buffer = null;
  1156. destROI = null;
  1157. destinationBands = null;
  1158. streamMetadata = null;
  1159. imageMetadata = null;
  1160. imageMetadataIndex = -1;
  1161. haveSeeked = false;
  1162. tablesOnlyChecked = false;
  1163. iccCS = null;
  1164. initProgressData();
  1165. System.gc();
  1166. }
  1167. private native void resetReader(long structPointer);
  1168. public void dispose() {
  1169. if (structPointer != 0) {
  1170. disposeReader(structPointer);
  1171. structPointer = 0;
  1172. }
  1173. }
  1174. private native void disposeReader(long structPointer);
  1175. public void finalize() {
  1176. dispose();
  1177. }
  1178. }