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