JPEGImageReader.java revision 15662:25c0c37cd66a
1/* 2 * Copyright (c) 2000, 2016, 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 23 * questions. 24 */ 25 26package com.sun.imageio.plugins.jpeg; 27 28import javax.imageio.IIOException; 29import javax.imageio.ImageReader; 30import javax.imageio.ImageReadParam; 31import javax.imageio.ImageTypeSpecifier; 32import javax.imageio.metadata.IIOMetadata; 33import javax.imageio.spi.ImageReaderSpi; 34import javax.imageio.stream.ImageInputStream; 35import javax.imageio.plugins.jpeg.JPEGImageReadParam; 36import javax.imageio.plugins.jpeg.JPEGQTable; 37import javax.imageio.plugins.jpeg.JPEGHuffmanTable; 38 39import java.awt.Point; 40import java.awt.Rectangle; 41import java.awt.color.ColorSpace; 42import java.awt.color.ICC_Profile; 43import java.awt.color.ICC_ColorSpace; 44import java.awt.color.CMMException; 45import java.awt.image.BufferedImage; 46import java.awt.image.Raster; 47import java.awt.image.WritableRaster; 48import java.awt.image.DataBuffer; 49import java.awt.image.DataBufferByte; 50import java.awt.image.ColorModel; 51import java.awt.image.IndexColorModel; 52import java.awt.image.ColorConvertOp; 53import java.io.IOException; 54import java.util.List; 55import java.util.Iterator; 56import java.util.ArrayList; 57import java.util.NoSuchElementException; 58 59import sun.java2d.Disposer; 60import sun.java2d.DisposerRecord; 61 62public class JPEGImageReader extends ImageReader { 63 64 private boolean debug = false; 65 66 /** 67 * The following variable contains a pointer to the IJG library 68 * structure for this reader. It is assigned in the constructor 69 * and then is passed in to every native call. It is set to 0 70 * by dispose to avoid disposing twice. 71 */ 72 private long structPointer = 0; 73 74 /** The input stream we read from */ 75 private ImageInputStream iis = null; 76 77 /** 78 * List of stream positions for images, reinitialized every time 79 * a new input source is set. 80 */ 81 private List<Long> imagePositions = null; 82 83 /** 84 * The number of images in the stream, or 0. 85 */ 86 private int numImages = 0; 87 88 static { 89 java.security.AccessController.doPrivileged( 90 new java.security.PrivilegedAction<Void>() { 91 public Void run() { 92 System.loadLibrary("javajpeg"); 93 return null; 94 } 95 }); 96 initReaderIDs(ImageInputStream.class, 97 JPEGQTable.class, 98 JPEGHuffmanTable.class); 99 } 100 101 // The following warnings are converted to strings when used 102 // as keys to get localized resources from JPEGImageReaderResources 103 // and its children. 104 105 /** 106 * Warning code to be passed to warningOccurred to indicate 107 * that the EOI marker is missing from the end of the stream. 108 * This usually signals that the stream is corrupted, but 109 * everything up to the last MCU should be usable. 110 */ 111 protected static final int WARNING_NO_EOI = 0; 112 113 /** 114 * Warning code to be passed to warningOccurred to indicate 115 * that a JFIF segment was encountered inside a JFXX JPEG 116 * thumbnail and is being ignored. 117 */ 118 protected static final int WARNING_NO_JFIF_IN_THUMB = 1; 119 120 /** 121 * Warning code to be passed to warningOccurred to indicate 122 * that embedded ICC profile is invalid and will be ignored. 123 */ 124 protected static final int WARNING_IGNORE_INVALID_ICC = 2; 125 126 private static final int MAX_WARNING = WARNING_IGNORE_INVALID_ICC; 127 128 /** 129 * Image index of image for which header information 130 * is available. 131 */ 132 private int currentImage = -1; 133 134 // The following is copied out from C after reading the header. 135 // Unlike metadata, which may never be retrieved, we need this 136 // if we are to read an image at all. 137 138 /** Set by setImageData native code callback */ 139 private int width; 140 /** Set by setImageData native code callback */ 141 private int height; 142 /** 143 * Set by setImageData native code callback. A modified 144 * IJG+NIFTY colorspace code. 145 */ 146 private int colorSpaceCode; 147 /** 148 * Set by setImageData native code callback. A modified 149 * IJG+NIFTY colorspace code. 150 */ 151 private int outColorSpaceCode; 152 /** Set by setImageData native code callback */ 153 private int numComponents; 154 /** Set by setImageData native code callback */ 155 private ColorSpace iccCS = null; 156 157 158 /** If we need to post-convert in Java, convert with this op */ 159 private ColorConvertOp convert = null; 160 161 /** The image we are going to fill */ 162 private BufferedImage image = null; 163 164 /** An intermediate Raster to hold decoded data */ 165 private WritableRaster raster = null; 166 167 /** A view of our target Raster that we can setRect to */ 168 private WritableRaster target = null; 169 170 /** The databuffer for the above Raster */ 171 private DataBufferByte buffer = null; 172 173 /** The region in the destination where we will write pixels */ 174 private Rectangle destROI = null; 175 176 /** The list of destination bands, if any */ 177 private int [] destinationBands = null; 178 179 /** Stream metadata, cached, even when the stream is changed. */ 180 private JPEGMetadata streamMetadata = null; 181 182 /** Image metadata, valid for the imageMetadataIndex only. */ 183 private JPEGMetadata imageMetadata = null; 184 private int imageMetadataIndex = -1; 185 186 /** 187 * Set to true every time we seek in the stream; used to 188 * invalidate the native buffer contents in C. 189 */ 190 private boolean haveSeeked = false; 191 192 /** 193 * Tables that have been read from a tables-only image at the 194 * beginning of a stream. 195 */ 196 private JPEGQTable [] abbrevQTables = null; 197 private JPEGHuffmanTable[] abbrevDCHuffmanTables = null; 198 private JPEGHuffmanTable[] abbrevACHuffmanTables = null; 199 200 private int minProgressivePass = 0; 201 private int maxProgressivePass = Integer.MAX_VALUE; 202 203 /** 204 * Variables used by progress monitoring. 205 */ 206 private static final int UNKNOWN = -1; // Number of passes 207 private static final int MIN_ESTIMATED_PASSES = 10; // IJG default 208 private int knownPassCount = UNKNOWN; 209 private int pass = 0; 210 private float percentToDate = 0.0F; 211 private float previousPassPercentage = 0.0F; 212 private int progInterval = 0; 213 214 /** 215 * Set to true once stream has been checked for stream metadata 216 */ 217 private boolean tablesOnlyChecked = false; 218 219 /** The referent to be registered with the Disposer. */ 220 private Object disposerReferent = new Object(); 221 222 /** The DisposerRecord that handles the actual disposal of this reader. */ 223 private DisposerRecord disposerRecord; 224 225 /** Sets up static C structures. */ 226 private static native void initReaderIDs(Class<?> iisClass, 227 Class<?> qTableClass, 228 Class<?> huffClass); 229 230 public JPEGImageReader(ImageReaderSpi originator) { 231 super(originator); 232 structPointer = initJPEGImageReader(); 233 disposerRecord = new JPEGReaderDisposerRecord(structPointer); 234 Disposer.addRecord(disposerReferent, disposerRecord); 235 } 236 237 /** Sets up per-reader C structure and returns a pointer to it. */ 238 private native long initJPEGImageReader(); 239 240 /** 241 * Called by the native code or other classes to signal a warning. 242 * The code is used to lookup a localized message to be used when 243 * sending warnings to listeners. 244 */ 245 protected void warningOccurred(int code) { 246 cbLock.lock(); 247 try { 248 if ((code < 0) || (code > MAX_WARNING)){ 249 throw new InternalError("Invalid warning index"); 250 } 251 processWarningOccurred 252 ("com.sun.imageio.plugins.jpeg.JPEGImageReaderResources", 253 Integer.toString(code)); 254 } finally { 255 cbLock.unlock(); 256 } 257 } 258 259 /** 260 * The library has it's own error facility that emits warning messages. 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 301 * native input buffer. 302 * 303 * We block any attempt to change the reading state during this 304 * method, in order to prevent a corruption of the native decoder 305 * state. 306 * 307 * @return number of bytes read from the stream. 308 */ 309 private int readInputData(byte[] buf, int off, int len) throws IOException { 310 cbLock.lock(); 311 try { 312 return iis.read(buf, off, len); 313 } finally { 314 cbLock.unlock(); 315 } 316 } 317 318 /** 319 * This method is called from the native code in order to 320 * skip requested number of bytes in the input stream. 321 * 322 * @param n 323 * @return 324 * @throws IOException 325 */ 326 private long skipInputBytes(long n) throws IOException { 327 cbLock.lock(); 328 try { 329 return iis.skipBytes(n); 330 } finally { 331 cbLock.unlock(); 332 } 333 } 334 335 private native void setSource(long structPointer); 336 337 private void checkTablesOnly() throws IOException { 338 if (debug) { 339 System.out.println("Checking for tables-only image"); 340 } 341 long savePos = iis.getStreamPosition(); 342 if (debug) { 343 System.out.println("saved pos is " + savePos); 344 System.out.println("length is " + iis.length()); 345 } 346 // Read the first header 347 boolean tablesOnly = readNativeHeader(true); 348 if (tablesOnly) { 349 if (debug) { 350 System.out.println("tables-only image found"); 351 long pos = iis.getStreamPosition(); 352 System.out.println("pos after return from native is " + pos); 353 } 354 // This reads the tables-only image twice, once from C 355 // and once from Java, but only if ignoreMetadata is false 356 if (ignoreMetadata == false) { 357 iis.seek(savePos); 358 haveSeeked = true; 359 streamMetadata = new JPEGMetadata(true, false, 360 iis, this); 361 long pos = iis.getStreamPosition(); 362 if (debug) { 363 System.out.println 364 ("pos after constructing stream metadata is " + pos); 365 } 366 } 367 // Now we are at the first image if there are any, so add it 368 // to the list 369 if (hasNextImage()) { 370 imagePositions.add(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 (seekForwardOnly) { 378 Long pos = imagePositions.get(imagePositions.size()-1); 379 iis.flushBefore(pos.longValue()); 380 } 381 tablesOnlyChecked = true; 382 } 383 384 public int getNumImages(boolean allowSearch) throws IOException { 385 setThreadLock(); 386 try { // locked thread 387 cbLock.check(); 388 389 return getNumImagesOnThread(allowSearch); 390 } finally { 391 clearThreadLock(); 392 } 393 } 394 395 @SuppressWarnings("fallthrough") 396 private int getNumImagesOnThread(boolean allowSearch) 397 throws IOException { 398 if (numImages != 0) { 399 return numImages; 400 } 401 if (iis == null) { 402 throw new IllegalStateException("Input not set"); 403 } 404 if (allowSearch == true) { 405 if (seekForwardOnly) { 406 throw new IllegalStateException( 407 "seekForwardOnly and allowSearch can't both be true!"); 408 } 409 // Otherwise we have to read the entire stream 410 411 if (!tablesOnlyChecked) { 412 checkTablesOnly(); 413 } 414 415 iis.mark(); 416 417 gotoImage(0); 418 419 JPEGBuffer buffer = new JPEGBuffer(iis); 420 buffer.loadBuf(0); 421 422 boolean done = false; 423 while (!done) { 424 done = buffer.scanForFF(this); 425 switch (buffer.buf[buffer.bufPtr] & 0xff) { 426 case JPEG.SOI: 427 numImages++; 428 // FALL THROUGH to decrement buffer vars 429 // This first set doesn't have a length 430 case 0: // not a marker, just a data 0xff 431 case JPEG.RST0: 432 case JPEG.RST1: 433 case JPEG.RST2: 434 case JPEG.RST3: 435 case JPEG.RST4: 436 case JPEG.RST5: 437 case JPEG.RST6: 438 case JPEG.RST7: 439 case JPEG.EOI: 440 buffer.bufAvail--; 441 buffer.bufPtr++; 442 break; 443 // All the others have a length 444 default: 445 buffer.bufAvail--; 446 buffer.bufPtr++; 447 buffer.loadBuf(2); 448 int length = ((buffer.buf[buffer.bufPtr++] & 0xff) << 8) | 449 (buffer.buf[buffer.bufPtr++] & 0xff); 450 buffer.bufAvail -= 2; 451 length -= 2; // length includes itself 452 buffer.skipData(length); 453 } 454 } 455 456 457 iis.reset(); 458 459 return numImages; 460 } 461 462 return -1; // Search is necessary for JPEG 463 } 464 465 /** 466 * Sets the input stream to the start of the requested image. 467 * <pre> 468 * @exception IllegalStateException if the input source has not been 469 * set. 470 * @exception IndexOutOfBoundsException if the supplied index is 471 * out of bounds. 472 * </pre> 473 */ 474 private void gotoImage(int imageIndex) throws IOException { 475 if (iis == null) { 476 throw new IllegalStateException("Input not set"); 477 } 478 if (imageIndex < minIndex) { 479 throw new IndexOutOfBoundsException(); 480 } 481 if (!tablesOnlyChecked) { 482 checkTablesOnly(); 483 } 484 if (imageIndex < imagePositions.size()) { 485 iis.seek(imagePositions.get(imageIndex).longValue()); 486 } else { 487 // read to start of image, saving positions 488 // First seek to the last position we already have, and skip the 489 // entire image 490 Long pos = imagePositions.get(imagePositions.size()-1); 491 iis.seek(pos.longValue()); 492 skipImage(); 493 // Now add all intervening positions, skipping images 494 for (int index = imagePositions.size(); 495 index <= imageIndex; 496 index++) { 497 // Is there an image? 498 if (!hasNextImage()) { 499 throw new IndexOutOfBoundsException(); 500 } 501 pos = iis.getStreamPosition(); 502 imagePositions.add(pos); 503 if (seekForwardOnly) { 504 iis.flushBefore(pos.longValue()); 505 } 506 if (index < imageIndex) { 507 skipImage(); 508 } // Otherwise we are where we want to be 509 } 510 } 511 512 if (seekForwardOnly) { 513 minIndex = imageIndex; 514 } 515 516 haveSeeked = true; // No way is native buffer still valid 517 } 518 519 /** 520 * Skip over a complete image in the stream, leaving the stream 521 * positioned such that the next byte to be read is the first 522 * byte of the next image. For JPEG, this means that we read 523 * until we encounter an EOI marker or until the end of the stream. 524 * We can find data same as EOI marker in some headers 525 * or comments, so we have to skip bytes related to these headers. 526 * If the stream ends before an EOI marker is encountered, 527 * an IndexOutOfBoundsException is thrown. 528 */ 529 private void skipImage() throws IOException { 530 if (debug) { 531 System.out.println("skipImage called"); 532 } 533 // verify if image starts with an SOI marker 534 int initialFF = iis.read(); 535 if (initialFF == 0xff) { 536 int soiMarker = iis.read(); 537 if (soiMarker != JPEG.SOI) { 538 throw new IOException("skipImage : Invalid image doesn't " 539 + "start with SOI marker"); 540 } 541 } else { 542 throw new IOException("skipImage : Invalid image doesn't start " 543 + "with 0xff"); 544 } 545 boolean foundFF = false; 546 String IOOBE = "skipImage : Reached EOF before we got EOI marker"; 547 int markerLength = 2; 548 for (int byteval = iis.read(); 549 byteval != -1; 550 byteval = iis.read()) { 551 552 if (foundFF == true) { 553 switch (byteval) { 554 case JPEG.EOI: 555 if (debug) { 556 System.out.println("skipImage : Found EOI at " + 557 (iis.getStreamPosition() - markerLength)); 558 } 559 return; 560 case JPEG.SOI: 561 throw new IOException("skipImage : Found extra SOI" 562 + " marker before getting to EOI"); 563 case 0: 564 case 255: 565 // markers which doesn't contain length data 566 case JPEG.RST0: 567 case JPEG.RST1: 568 case JPEG.RST2: 569 case JPEG.RST3: 570 case JPEG.RST4: 571 case JPEG.RST5: 572 case JPEG.RST6: 573 case JPEG.RST7: 574 case JPEG.TEM: 575 break; 576 // markers which contains length data 577 case JPEG.SOF0: 578 case JPEG.SOF1: 579 case JPEG.SOF2: 580 case JPEG.SOF3: 581 case JPEG.DHT: 582 case JPEG.SOF5: 583 case JPEG.SOF6: 584 case JPEG.SOF7: 585 case JPEG.JPG: 586 case JPEG.SOF9: 587 case JPEG.SOF10: 588 case JPEG.SOF11: 589 case JPEG.DAC: 590 case JPEG.SOF13: 591 case JPEG.SOF14: 592 case JPEG.SOF15: 593 case JPEG.SOS: 594 case JPEG.DQT: 595 case JPEG.DNL: 596 case JPEG.DRI: 597 case JPEG.DHP: 598 case JPEG.EXP: 599 case JPEG.APP0: 600 case JPEG.APP1: 601 case JPEG.APP2: 602 case JPEG.APP3: 603 case JPEG.APP4: 604 case JPEG.APP5: 605 case JPEG.APP6: 606 case JPEG.APP7: 607 case JPEG.APP8: 608 case JPEG.APP9: 609 case JPEG.APP10: 610 case JPEG.APP11: 611 case JPEG.APP12: 612 case JPEG.APP13: 613 case JPEG.APP14: 614 case JPEG.APP15: 615 case JPEG.COM: 616 // read length of header from next 2 bytes 617 int lengthHigherBits, lengthLowerBits, length; 618 lengthHigherBits = iis.read(); 619 if (lengthHigherBits != (-1)) { 620 lengthLowerBits = iis.read(); 621 if (lengthLowerBits != (-1)) { 622 length = (lengthHigherBits << 8) | 623 lengthLowerBits; 624 // length contains already read 2 bytes 625 length -= 2; 626 } else { 627 throw new IndexOutOfBoundsException(IOOBE); 628 } 629 } else { 630 throw new IndexOutOfBoundsException(IOOBE); 631 } 632 // skip the length specified in marker 633 iis.skipBytes(length); 634 break; 635 case (-1): 636 throw new IndexOutOfBoundsException(IOOBE); 637 default: 638 throw new IOException("skipImage : Invalid marker " 639 + "starting with ff " 640 + Integer.toHexString(byteval)); 641 } 642 } 643 foundFF = (byteval == 0xff); 644 } 645 throw new IndexOutOfBoundsException(IOOBE); 646 } 647 648 /** 649 * Returns {@code true} if there is an image beyond 650 * the current stream position. Does not disturb the 651 * stream position. 652 */ 653 private boolean hasNextImage() throws IOException { 654 if (debug) { 655 System.out.print("hasNextImage called; returning "); 656 } 657 iis.mark(); 658 boolean foundFF = false; 659 for (int byteval = iis.read(); 660 byteval != -1; 661 byteval = iis.read()) { 662 663 if (foundFF == true) { 664 if (byteval == JPEG.SOI) { 665 iis.reset(); 666 if (debug) { 667 System.out.println("true"); 668 } 669 return true; 670 } 671 } 672 foundFF = (byteval == 0xff) ? true : false; 673 } 674 // We hit the end of the stream before we hit an SOI, so no image 675 iis.reset(); 676 if (debug) { 677 System.out.println("false"); 678 } 679 return false; 680 } 681 682 /** 683 * Push back the given number of bytes to the input stream. 684 * Called by the native code at the end of each image so 685 * that the next one can be identified from Java. 686 */ 687 private void pushBack(int num) throws IOException { 688 if (debug) { 689 System.out.println("pushing back " + num + " bytes"); 690 } 691 cbLock.lock(); 692 try { 693 iis.seek(iis.getStreamPosition()-num); 694 // The buffer is clear after this, so no need to set haveSeeked. 695 } finally { 696 cbLock.unlock(); 697 } 698 } 699 700 /** 701 * Reads header information for the given image, if possible. 702 */ 703 private void readHeader(int imageIndex, boolean reset) 704 throws IOException { 705 gotoImage(imageIndex); 706 readNativeHeader(reset); // Ignore return 707 currentImage = imageIndex; 708 } 709 710 private boolean readNativeHeader(boolean reset) throws IOException { 711 boolean retval = false; 712 retval = readImageHeader(structPointer, haveSeeked, reset); 713 haveSeeked = false; 714 return retval; 715 } 716 717 /** 718 * Read in the header information starting from the current 719 * stream position, returning {@code true} if the 720 * header was a tables-only image. After this call, the 721 * native IJG decompression struct will contain the image 722 * information required by most query calls below 723 * (e.g. getWidth, getHeight, etc.), if the header was not 724 * a tables-only image. 725 * If reset is {@code true}, the state of the IJG 726 * object is reset so that it can read a header again. 727 * This happens automatically if the header was a tables-only 728 * image. 729 */ 730 private native boolean readImageHeader(long structPointer, 731 boolean clearBuffer, 732 boolean reset) 733 throws IOException; 734 735 /* 736 * Called by the native code whenever an image header has been 737 * read. Whether we read metadata or not, we always need this 738 * information, so it is passed back independently of 739 * metadata, which may never be read. 740 */ 741 private void setImageData(int width, 742 int height, 743 int colorSpaceCode, 744 int outColorSpaceCode, 745 int numComponents, 746 byte [] iccData) { 747 this.width = width; 748 this.height = height; 749 this.colorSpaceCode = colorSpaceCode; 750 this.outColorSpaceCode = outColorSpaceCode; 751 this.numComponents = numComponents; 752 753 if (iccData == null) { 754 iccCS = null; 755 return; 756 } 757 758 ICC_Profile newProfile = null; 759 try { 760 newProfile = ICC_Profile.getInstance(iccData); 761 } catch (IllegalArgumentException e) { 762 /* 763 * Color profile data seems to be invalid. 764 * Ignore this profile. 765 */ 766 iccCS = null; 767 warningOccurred(WARNING_IGNORE_INVALID_ICC); 768 769 return; 770 } 771 byte[] newData = newProfile.getData(); 772 773 ICC_Profile oldProfile = null; 774 if (iccCS instanceof ICC_ColorSpace) { 775 oldProfile = ((ICC_ColorSpace)iccCS).getProfile(); 776 } 777 byte[] oldData = null; 778 if (oldProfile != null) { 779 oldData = oldProfile.getData(); 780 } 781 782 /* 783 * At the moment we can't rely on the ColorSpace.equals() 784 * and ICC_Profile.equals() because they do not detect 785 * the case when two profiles are created from same data. 786 * 787 * So, we have to do data comparison in order to avoid 788 * creation of different ColorSpace instances for the same 789 * embedded data. 790 */ 791 if (oldData == null || 792 !java.util.Arrays.equals(oldData, newData)) 793 { 794 iccCS = new ICC_ColorSpace(newProfile); 795 // verify new color space 796 try { 797 float[] colors = iccCS.fromRGB(new float[] {1f, 0f, 0f}); 798 } catch (CMMException e) { 799 /* 800 * Embedded profile seems to be corrupted. 801 * Ignore this profile. 802 */ 803 iccCS = null; 804 cbLock.lock(); 805 try { 806 warningOccurred(WARNING_IGNORE_INVALID_ICC); 807 } finally { 808 cbLock.unlock(); 809 } 810 } 811 } 812 } 813 814 public int getWidth(int imageIndex) throws IOException { 815 setThreadLock(); 816 try { 817 if (currentImage != imageIndex) { 818 cbLock.check(); 819 readHeader(imageIndex, true); 820 } 821 return width; 822 } finally { 823 clearThreadLock(); 824 } 825 } 826 827 public int getHeight(int imageIndex) throws IOException { 828 setThreadLock(); 829 try { 830 if (currentImage != imageIndex) { 831 cbLock.check(); 832 readHeader(imageIndex, true); 833 } 834 return height; 835 } finally { 836 clearThreadLock(); 837 } 838 } 839 840 /////////// Color Conversion and Image Types 841 842 /** 843 * Return an ImageTypeSpecifier corresponding to the given 844 * color space code, or null if the color space is unsupported. 845 */ 846 private ImageTypeProducer getImageType(int code) { 847 ImageTypeProducer ret = null; 848 849 if ((code > 0) && (code < JPEG.NUM_JCS_CODES)) { 850 ret = ImageTypeProducer.getTypeProducer(code); 851 } 852 return ret; 853 } 854 855 public ImageTypeSpecifier getRawImageType(int imageIndex) 856 throws IOException { 857 setThreadLock(); 858 try { 859 if (currentImage != imageIndex) { 860 cbLock.check(); 861 862 readHeader(imageIndex, true); 863 } 864 865 // Returns null if it can't be represented 866 return getImageType(colorSpaceCode).getType(); 867 } finally { 868 clearThreadLock(); 869 } 870 } 871 872 public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) 873 throws IOException { 874 setThreadLock(); 875 try { 876 return getImageTypesOnThread(imageIndex); 877 } finally { 878 clearThreadLock(); 879 } 880 } 881 882 private Iterator<ImageTypeSpecifier> getImageTypesOnThread(int imageIndex) 883 throws IOException { 884 if (currentImage != imageIndex) { 885 cbLock.check(); 886 readHeader(imageIndex, true); 887 } 888 889 // We return an iterator containing the default, any 890 // conversions that the library provides, and 891 // all the other default types with the same number 892 // of components, as we can do these as a post-process. 893 // As we convert Rasters rather than images, images 894 // with alpha cannot be converted in a post-process. 895 896 // If this image can't be interpreted, this method 897 // returns an empty Iterator. 898 899 // Get the raw ITS, if there is one. Note that this 900 // won't always be the same as the default. 901 ImageTypeProducer raw = getImageType(colorSpaceCode); 902 903 // Given the encoded colorspace, build a list of ITS's 904 // representing outputs you could handle starting 905 // with the default. 906 907 ArrayList<ImageTypeProducer> list = new ArrayList<ImageTypeProducer>(1); 908 909 switch (colorSpaceCode) { 910 case JPEG.JCS_GRAYSCALE: 911 list.add(raw); 912 list.add(getImageType(JPEG.JCS_RGB)); 913 break; 914 case JPEG.JCS_RGB: 915 list.add(raw); 916 list.add(getImageType(JPEG.JCS_GRAYSCALE)); 917 list.add(getImageType(JPEG.JCS_YCC)); 918 break; 919 case JPEG.JCS_RGBA: 920 list.add(raw); 921 break; 922 case JPEG.JCS_YCC: 923 if (raw != null) { // Might be null if PYCC.pf not installed 924 list.add(raw); 925 list.add(getImageType(JPEG.JCS_RGB)); 926 } 927 break; 928 case JPEG.JCS_YCCA: 929 if (raw != null) { // Might be null if PYCC.pf not installed 930 list.add(raw); 931 } 932 break; 933 case JPEG.JCS_YCbCr: 934 // As there is no YCbCr ColorSpace, we can't support 935 // the raw type. 936 937 // due to 4705399, use RGB as default in order to avoid 938 // slowing down of drawing operations with result image. 939 list.add(getImageType(JPEG.JCS_RGB)); 940 941 if (iccCS != null) { 942 list.add(new ImageTypeProducer() { 943 protected ImageTypeSpecifier produce() { 944 return ImageTypeSpecifier.createInterleaved 945 (iccCS, 946 JPEG.bOffsRGB, // Assume it's for RGB 947 DataBuffer.TYPE_BYTE, 948 false, 949 false); 950 } 951 }); 952 953 } 954 955 list.add(getImageType(JPEG.JCS_GRAYSCALE)); 956 list.add(getImageType(JPEG.JCS_YCC)); 957 break; 958 case JPEG.JCS_YCbCrA: // Default is to convert to RGBA 959 // As there is no YCbCr ColorSpace, we can't support 960 // the raw type. 961 list.add(getImageType(JPEG.JCS_RGBA)); 962 break; 963 } 964 965 return new ImageTypeIterator(list.iterator()); 966 } 967 968 /** 969 * Checks the implied color conversion between the stream and 970 * the target image, altering the IJG output color space if necessary. 971 * If a java color conversion is required, then this sets up 972 * {@code convert}. 973 * If bands are being rearranged at all (either source or destination 974 * bands are specified in the param), then the default color 975 * conversions are assumed to be correct. 976 * Throws an IIOException if there is no conversion available. 977 */ 978 private void checkColorConversion(BufferedImage image, 979 ImageReadParam param) 980 throws IIOException { 981 982 // If we are rearranging channels at all, the default 983 // conversions remain in place. If the user wants 984 // raw channels then he should do this while reading 985 // a Raster. 986 if (param != null) { 987 if ((param.getSourceBands() != null) || 988 (param.getDestinationBands() != null)) { 989 // Accept default conversions out of decoder, silently 990 return; 991 } 992 } 993 994 // XXX - We do not currently support any indexed color models, 995 // though we could, as IJG will quantize for us. 996 // This is a performance and memory-use issue, as 997 // users can read RGB and then convert to indexed in Java. 998 999 ColorModel cm = image.getColorModel(); 1000 1001 if (cm instanceof IndexColorModel) { 1002 throw new IIOException("IndexColorModel not supported"); 1003 } 1004 1005 // Now check the ColorSpace type against outColorSpaceCode 1006 // We may want to tweak the default 1007 ColorSpace cs = cm.getColorSpace(); 1008 int csType = cs.getType(); 1009 convert = null; 1010 switch (outColorSpaceCode) { 1011 case JPEG.JCS_GRAYSCALE: // Its gray in the file 1012 if (csType == ColorSpace.TYPE_RGB) { // We want RGB 1013 // IJG can do this for us more efficiently 1014 setOutColorSpace(structPointer, JPEG.JCS_RGB); 1015 // Update java state according to changes 1016 // in the native part of decoder. 1017 outColorSpaceCode = JPEG.JCS_RGB; 1018 numComponents = 3; 1019 } else if (csType != ColorSpace.TYPE_GRAY) { 1020 throw new IIOException("Incompatible color conversion"); 1021 } 1022 break; 1023 case JPEG.JCS_RGB: // IJG wants to go to RGB 1024 if (csType == ColorSpace.TYPE_GRAY) { // We want gray 1025 if (colorSpaceCode == JPEG.JCS_YCbCr) { 1026 // If the jpeg space is YCbCr, IJG can do it 1027 setOutColorSpace(structPointer, JPEG.JCS_GRAYSCALE); 1028 // Update java state according to changes 1029 // in the native part of decoder. 1030 outColorSpaceCode = JPEG.JCS_GRAYSCALE; 1031 numComponents = 1; 1032 } 1033 } else if ((iccCS != null) && 1034 (cm.getNumComponents() == numComponents) && 1035 (cs != iccCS)) { 1036 // We have an ICC profile but it isn't used in the dest 1037 // image. So convert from the profile cs to the target cs 1038 convert = new ColorConvertOp(iccCS, cs, null); 1039 // Leave IJG conversion in place; we still need it 1040 } else if ((iccCS == null) && 1041 (!cs.isCS_sRGB()) && 1042 (cm.getNumComponents() == numComponents)) { 1043 // Target isn't sRGB, so convert from sRGB to the target 1044 convert = new ColorConvertOp(JPEG.JCS.sRGB, cs, null); 1045 } else if (csType != ColorSpace.TYPE_RGB) { 1046 throw new IIOException("Incompatible color conversion"); 1047 } 1048 break; 1049 case JPEG.JCS_RGBA: 1050 // No conversions available; image must be RGBA 1051 if ((csType != ColorSpace.TYPE_RGB) || 1052 (cm.getNumComponents() != numComponents)) { 1053 throw new IIOException("Incompatible color conversion"); 1054 } 1055 break; 1056 case JPEG.JCS_YCC: 1057 { 1058 ColorSpace YCC = JPEG.JCS.getYCC(); 1059 if (YCC == null) { // We can't do YCC at all 1060 throw new IIOException("Incompatible color conversion"); 1061 } 1062 if ((cs != YCC) && 1063 (cm.getNumComponents() == numComponents)) { 1064 convert = new ColorConvertOp(YCC, cs, null); 1065 } 1066 } 1067 break; 1068 case JPEG.JCS_YCCA: 1069 { 1070 ColorSpace YCC = JPEG.JCS.getYCC(); 1071 // No conversions available; image must be YCCA 1072 if ((YCC == null) || // We can't do YCC at all 1073 (cs != YCC) || 1074 (cm.getNumComponents() != numComponents)) { 1075 throw new IIOException("Incompatible color conversion"); 1076 } 1077 } 1078 break; 1079 default: 1080 // Anything else we can't handle at all 1081 throw new IIOException("Incompatible color conversion"); 1082 } 1083 } 1084 1085 /** 1086 * Set the IJG output space to the given value. The library will 1087 * perform the appropriate colorspace conversions. 1088 */ 1089 private native void setOutColorSpace(long structPointer, int id); 1090 1091 /////// End of Color Conversion & Image Types 1092 1093 public ImageReadParam getDefaultReadParam() { 1094 return new JPEGImageReadParam(); 1095 } 1096 1097 public IIOMetadata getStreamMetadata() throws IOException { 1098 setThreadLock(); 1099 try { 1100 if (!tablesOnlyChecked) { 1101 cbLock.check(); 1102 checkTablesOnly(); 1103 } 1104 return streamMetadata; 1105 } finally { 1106 clearThreadLock(); 1107 } 1108 } 1109 1110 public IIOMetadata getImageMetadata(int imageIndex) 1111 throws IOException { 1112 setThreadLock(); 1113 try { 1114 // imageMetadataIndex will always be either a valid index or 1115 // -1, in which case imageMetadata will not be null. 1116 // So we can leave checking imageIndex for gotoImage. 1117 if ((imageMetadataIndex == imageIndex) 1118 && (imageMetadata != null)) { 1119 return imageMetadata; 1120 } 1121 1122 cbLock.check(); 1123 1124 gotoImage(imageIndex); 1125 1126 imageMetadata = new JPEGMetadata(false, false, iis, this); 1127 1128 imageMetadataIndex = imageIndex; 1129 1130 return imageMetadata; 1131 } finally { 1132 clearThreadLock(); 1133 } 1134 } 1135 1136 public BufferedImage read(int imageIndex, ImageReadParam param) 1137 throws IOException { 1138 setThreadLock(); 1139 try { 1140 cbLock.check(); 1141 try { 1142 readInternal(imageIndex, param, false); 1143 } catch (RuntimeException e) { 1144 resetLibraryState(structPointer); 1145 throw e; 1146 } catch (IOException e) { 1147 resetLibraryState(structPointer); 1148 throw e; 1149 } 1150 1151 BufferedImage ret = image; 1152 image = null; // don't keep a reference here 1153 return ret; 1154 } finally { 1155 clearThreadLock(); 1156 } 1157 } 1158 1159 private Raster readInternal(int imageIndex, 1160 ImageReadParam param, 1161 boolean wantRaster) throws IOException { 1162 readHeader(imageIndex, false); 1163 1164 WritableRaster imRas = null; 1165 int numImageBands = 0; 1166 1167 if (!wantRaster){ 1168 // Can we read this image? 1169 Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex); 1170 if (imageTypes.hasNext() == false) { 1171 throw new IIOException("Unsupported Image Type"); 1172 } 1173 1174 image = getDestination(param, imageTypes, width, height); 1175 imRas = image.getRaster(); 1176 1177 // The destination may still be incompatible. 1178 1179 numImageBands = image.getSampleModel().getNumBands(); 1180 1181 // Check whether we can handle any implied color conversion 1182 1183 // Throws IIOException if the stream and the image are 1184 // incompatible, and sets convert if a java conversion 1185 // is necessary 1186 checkColorConversion(image, param); 1187 1188 // Check the source and destination bands in the param 1189 checkReadParamBandSettings(param, numComponents, numImageBands); 1190 } else { 1191 // Set the output color space equal to the input colorspace 1192 // This disables all conversions 1193 setOutColorSpace(structPointer, colorSpaceCode); 1194 image = null; 1195 } 1196 1197 // Create an intermediate 1-line Raster that will hold the decoded, 1198 // subsampled, clipped, band-selected image data in a single 1199 // byte-interleaved buffer. The above transformations 1200 // will occur in C for performance. Every time this Raster 1201 // is filled we will call back to acceptPixels below to copy 1202 // this to whatever kind of buffer our image has. 1203 1204 int [] srcBands = JPEG.bandOffsets[numComponents-1]; 1205 int numRasterBands = (wantRaster ? numComponents : numImageBands); 1206 destinationBands = null; 1207 1208 Rectangle srcROI = new Rectangle(0, 0, 0, 0); 1209 destROI = new Rectangle(0, 0, 0, 0); 1210 computeRegions(param, width, height, image, srcROI, destROI); 1211 1212 int periodX = 1; 1213 int periodY = 1; 1214 1215 minProgressivePass = 0; 1216 maxProgressivePass = Integer.MAX_VALUE; 1217 1218 if (param != null) { 1219 periodX = param.getSourceXSubsampling(); 1220 periodY = param.getSourceYSubsampling(); 1221 1222 int[] sBands = param.getSourceBands(); 1223 if (sBands != null) { 1224 srcBands = sBands; 1225 numRasterBands = srcBands.length; 1226 } 1227 if (!wantRaster) { // ignore dest bands for Raster 1228 destinationBands = param.getDestinationBands(); 1229 } 1230 1231 minProgressivePass = param.getSourceMinProgressivePass(); 1232 maxProgressivePass = param.getSourceMaxProgressivePass(); 1233 1234 if (param instanceof JPEGImageReadParam) { 1235 JPEGImageReadParam jparam = (JPEGImageReadParam) param; 1236 if (jparam.areTablesSet()) { 1237 abbrevQTables = jparam.getQTables(); 1238 abbrevDCHuffmanTables = jparam.getDCHuffmanTables(); 1239 abbrevACHuffmanTables = jparam.getACHuffmanTables(); 1240 } 1241 } 1242 } 1243 1244 int lineSize = destROI.width*numRasterBands; 1245 1246 buffer = new DataBufferByte(lineSize); 1247 1248 int [] bandOffs = JPEG.bandOffsets[numRasterBands-1]; 1249 1250 raster = Raster.createInterleavedRaster(buffer, 1251 destROI.width, 1, 1252 lineSize, 1253 numRasterBands, 1254 bandOffs, 1255 null); 1256 1257 // Now that we have the Raster we'll decode to, get a view of the 1258 // target Raster that will permit a simple setRect for each scanline 1259 if (wantRaster) { 1260 target = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1261 destROI.width, 1262 destROI.height, 1263 lineSize, 1264 numRasterBands, 1265 bandOffs, 1266 null); 1267 } else { 1268 target = imRas; 1269 } 1270 int [] bandSizes = target.getSampleModel().getSampleSize(); 1271 for (int i = 0; i < bandSizes.length; i++) { 1272 if (bandSizes[i] <= 0 || bandSizes[i] > 8) { 1273 throw new IIOException("Illegal band size: should be 0 < size <= 8"); 1274 } 1275 } 1276 1277 /* 1278 * If the process is sequential, and we have restart markers, 1279 * we could skip to the correct restart marker, if the library 1280 * lets us. That's an optimization to investigate later. 1281 */ 1282 1283 // Check for update listeners (don't call back if none) 1284 boolean callbackUpdates = ((updateListeners != null) 1285 || (progressListeners != null)); 1286 1287 // Set up progression data 1288 initProgressData(); 1289 // if we have a metadata object, we can count the scans 1290 // and set knownPassCount 1291 if (imageIndex == imageMetadataIndex) { // We have metadata 1292 knownPassCount = 0; 1293 for (Iterator<MarkerSegment> iter = 1294 imageMetadata.markerSequence.iterator(); iter.hasNext();) { 1295 if (iter.next() instanceof SOSMarkerSegment) { 1296 knownPassCount++; 1297 } 1298 } 1299 } 1300 progInterval = Math.max((target.getHeight()-1) / 20, 1); 1301 if (knownPassCount > 0) { 1302 progInterval *= knownPassCount; 1303 } else if (maxProgressivePass != Integer.MAX_VALUE) { 1304 progInterval *= (maxProgressivePass - minProgressivePass + 1); 1305 } 1306 1307 if (debug) { 1308 System.out.println("**** Read Data *****"); 1309 System.out.println("numRasterBands is " + numRasterBands); 1310 System.out.print("srcBands:"); 1311 for (int i = 0; i<srcBands.length;i++) 1312 System.out.print(" " + srcBands[i]); 1313 System.out.println(); 1314 System.out.println("destination bands is " + destinationBands); 1315 if (destinationBands != null) { 1316 for (int i = 0; i < destinationBands.length; i++) { 1317 System.out.print(" " + destinationBands[i]); 1318 } 1319 System.out.println(); 1320 } 1321 System.out.println("sourceROI is " + srcROI); 1322 System.out.println("destROI is " + destROI); 1323 System.out.println("periodX is " + periodX); 1324 System.out.println("periodY is " + periodY); 1325 System.out.println("minProgressivePass is " + minProgressivePass); 1326 System.out.println("maxProgressivePass is " + maxProgressivePass); 1327 System.out.println("callbackUpdates is " + callbackUpdates); 1328 } 1329 1330 /* 1331 * All the Jpeg processing happens in native, we should clear 1332 * abortFlag of imageIODataStruct in imageioJPEG.c. And we need to 1333 * clear abortFlag because if in previous read() if we had called 1334 * reader.abort() that will continue to be valid for present call also. 1335 */ 1336 clearNativeReadAbortFlag(structPointer); 1337 processImageStarted(currentImage); 1338 /* 1339 * Note that getData disables acceleration on buffer, but it is 1340 * just a 1-line intermediate data transfer buffer that will not 1341 * affect the acceleration of the resulting image. 1342 */ 1343 boolean aborted = readImage(structPointer, 1344 buffer.getData(), 1345 numRasterBands, 1346 srcBands, 1347 bandSizes, 1348 srcROI.x, srcROI.y, 1349 srcROI.width, srcROI.height, 1350 periodX, periodY, 1351 abbrevQTables, 1352 abbrevDCHuffmanTables, 1353 abbrevACHuffmanTables, 1354 minProgressivePass, maxProgressivePass, 1355 callbackUpdates); 1356 1357 if (aborted) { 1358 processReadAborted(); 1359 } else { 1360 processImageComplete(); 1361 } 1362 1363 return target; 1364 1365 } 1366 1367 /** 1368 * This method is called back from C when the intermediate Raster 1369 * is full. The parameter indicates the scanline in the target 1370 * Raster to which the intermediate Raster should be copied. 1371 * After the copy, we notify update listeners. 1372 */ 1373 private void acceptPixels(int y, boolean progressive) { 1374 if (convert != null) { 1375 convert.filter(raster, raster); 1376 } 1377 target.setRect(destROI.x, destROI.y + y, raster); 1378 1379 cbLock.lock(); 1380 try { 1381 processImageUpdate(image, 1382 destROI.x, destROI.y+y, 1383 raster.getWidth(), 1, 1384 1, 1, 1385 destinationBands); 1386 if ((y > 0) && (y%progInterval == 0)) { 1387 int height = target.getHeight()-1; 1388 float percentOfPass = ((float)y)/height; 1389 if (progressive) { 1390 if (knownPassCount != UNKNOWN) { 1391 processImageProgress((pass + percentOfPass)*100.0F 1392 / knownPassCount); 1393 } else if (maxProgressivePass != Integer.MAX_VALUE) { 1394 // Use the range of allowed progressive passes 1395 processImageProgress((pass + percentOfPass)*100.0F 1396 / (maxProgressivePass - minProgressivePass + 1)); 1397 } else { 1398 // Assume there are a minimum of MIN_ESTIMATED_PASSES 1399 // and that there is always one more pass 1400 // Compute the percentage as the percentage at the end 1401 // of the previous pass, plus the percentage of this 1402 // pass scaled to be the percentage of the total remaining, 1403 // assuming a minimum of MIN_ESTIMATED_PASSES passes and 1404 // that there is always one more pass. This is monotonic 1405 // and asymptotic to 1.0, which is what we need. 1406 int remainingPasses = // including this one 1407 Math.max(2, MIN_ESTIMATED_PASSES-pass); 1408 int totalPasses = pass + remainingPasses-1; 1409 progInterval = Math.max(height/20*totalPasses, 1410 totalPasses); 1411 if (y%progInterval == 0) { 1412 percentToDate = previousPassPercentage + 1413 (1.0F - previousPassPercentage) 1414 * (percentOfPass)/remainingPasses; 1415 if (debug) { 1416 System.out.print("pass= " + pass); 1417 System.out.print(", y= " + y); 1418 System.out.print(", progInt= " + progInterval); 1419 System.out.print(", % of pass: " + percentOfPass); 1420 System.out.print(", rem. passes: " 1421 + remainingPasses); 1422 System.out.print(", prev%: " 1423 + previousPassPercentage); 1424 System.out.print(", %ToDate: " + percentToDate); 1425 System.out.print(" "); 1426 } 1427 processImageProgress(percentToDate*100.0F); 1428 } 1429 } 1430 } else { 1431 processImageProgress(percentOfPass * 100.0F); 1432 } 1433 } 1434 } finally { 1435 cbLock.unlock(); 1436 } 1437 } 1438 1439 private void initProgressData() { 1440 knownPassCount = UNKNOWN; 1441 pass = 0; 1442 percentToDate = 0.0F; 1443 previousPassPercentage = 0.0F; 1444 progInterval = 0; 1445 } 1446 1447 private void passStarted (int pass) { 1448 cbLock.lock(); 1449 try { 1450 this.pass = pass; 1451 previousPassPercentage = percentToDate; 1452 processPassStarted(image, 1453 pass, 1454 minProgressivePass, 1455 maxProgressivePass, 1456 0, 0, 1457 1,1, 1458 destinationBands); 1459 } finally { 1460 cbLock.unlock(); 1461 } 1462 } 1463 1464 private void passComplete () { 1465 cbLock.lock(); 1466 try { 1467 processPassComplete(image); 1468 } finally { 1469 cbLock.unlock(); 1470 } 1471 } 1472 1473 void thumbnailStarted(int thumbnailIndex) { 1474 cbLock.lock(); 1475 try { 1476 processThumbnailStarted(currentImage, thumbnailIndex); 1477 } finally { 1478 cbLock.unlock(); 1479 } 1480 } 1481 1482 // Provide access to protected superclass method 1483 void thumbnailProgress(float percentageDone) { 1484 cbLock.lock(); 1485 try { 1486 processThumbnailProgress(percentageDone); 1487 } finally { 1488 cbLock.unlock(); 1489 } 1490 } 1491 1492 // Provide access to protected superclass method 1493 void thumbnailComplete() { 1494 cbLock.lock(); 1495 try { 1496 processThumbnailComplete(); 1497 } finally { 1498 cbLock.unlock(); 1499 } 1500 } 1501 1502 /** 1503 * Returns {@code true} if the read was aborted. 1504 */ 1505 private native boolean readImage(long structPointer, 1506 byte [] buffer, 1507 int numRasterBands, 1508 int [] srcBands, 1509 int [] bandSizes, 1510 int sourceXOffset, int sourceYOffset, 1511 int sourceWidth, int sourceHeight, 1512 int periodX, int periodY, 1513 JPEGQTable [] abbrevQTables, 1514 JPEGHuffmanTable [] abbrevDCHuffmanTables, 1515 JPEGHuffmanTable [] abbrevACHuffmanTables, 1516 int minProgressivePass, 1517 int maxProgressivePass, 1518 boolean wantUpdates); 1519 1520 /* 1521 * We should call clearNativeReadAbortFlag() before we start reading 1522 * jpeg image as image processing happens at native side. 1523 */ 1524 private native void clearNativeReadAbortFlag(long structPointer); 1525 1526 public void abort() { 1527 setThreadLock(); 1528 try { 1529 /** 1530 * NB: we do not check the call back lock here, 1531 * we allow to abort the reader any time. 1532 */ 1533 1534 super.abort(); 1535 abortRead(structPointer); 1536 } finally { 1537 clearThreadLock(); 1538 } 1539 } 1540 1541 /** Set the C level abort flag. Keep it atomic for thread safety. */ 1542 private native void abortRead(long structPointer); 1543 1544 /** Resets library state when an exception occurred during a read. */ 1545 private native void resetLibraryState(long structPointer); 1546 1547 public boolean canReadRaster() { 1548 return true; 1549 } 1550 1551 public Raster readRaster(int imageIndex, ImageReadParam param) 1552 throws IOException { 1553 setThreadLock(); 1554 Raster retval = null; 1555 try { 1556 cbLock.check(); 1557 /* 1558 * This could be further optimized by not resetting the dest. 1559 * offset and creating a translated raster in readInternal() 1560 * (see bug 4994702 for more info). 1561 */ 1562 1563 // For Rasters, destination offset is logical, not physical, so 1564 // set it to 0 before calling computeRegions, so that the destination 1565 // region is not clipped. 1566 Point saveDestOffset = null; 1567 if (param != null) { 1568 saveDestOffset = param.getDestinationOffset(); 1569 param.setDestinationOffset(new Point(0, 0)); 1570 } 1571 retval = readInternal(imageIndex, param, true); 1572 // Apply the destination offset, if any, as a logical offset 1573 if (saveDestOffset != null) { 1574 target = target.createWritableTranslatedChild(saveDestOffset.x, 1575 saveDestOffset.y); 1576 } 1577 } catch (RuntimeException e) { 1578 resetLibraryState(structPointer); 1579 throw e; 1580 } catch (IOException e) { 1581 resetLibraryState(structPointer); 1582 throw e; 1583 } finally { 1584 clearThreadLock(); 1585 } 1586 return retval; 1587 } 1588 1589 public boolean readerSupportsThumbnails() { 1590 return true; 1591 } 1592 1593 public int getNumThumbnails(int imageIndex) throws IOException { 1594 setThreadLock(); 1595 try { 1596 cbLock.check(); 1597 1598 getImageMetadata(imageIndex); // checks iis state for us 1599 // Now check the jfif segments 1600 JFIFMarkerSegment jfif = 1601 (JFIFMarkerSegment) imageMetadata.findMarkerSegment 1602 (JFIFMarkerSegment.class, true); 1603 int retval = 0; 1604 if (jfif != null) { 1605 retval = (jfif.thumb == null) ? 0 : 1; 1606 retval += jfif.extSegments.size(); 1607 } 1608 return retval; 1609 } finally { 1610 clearThreadLock(); 1611 } 1612 } 1613 1614 public int getThumbnailWidth(int imageIndex, int thumbnailIndex) 1615 throws IOException { 1616 setThreadLock(); 1617 try { 1618 cbLock.check(); 1619 1620 if ((thumbnailIndex < 0) 1621 || (thumbnailIndex >= getNumThumbnails(imageIndex))) { 1622 throw new IndexOutOfBoundsException("No such thumbnail"); 1623 } 1624 // Now we know that there is a jfif segment 1625 JFIFMarkerSegment jfif = 1626 (JFIFMarkerSegment) imageMetadata.findMarkerSegment 1627 (JFIFMarkerSegment.class, true); 1628 return jfif.getThumbnailWidth(thumbnailIndex); 1629 } finally { 1630 clearThreadLock(); 1631 } 1632 } 1633 1634 public int getThumbnailHeight(int imageIndex, int thumbnailIndex) 1635 throws IOException { 1636 setThreadLock(); 1637 try { 1638 cbLock.check(); 1639 1640 if ((thumbnailIndex < 0) 1641 || (thumbnailIndex >= getNumThumbnails(imageIndex))) { 1642 throw new IndexOutOfBoundsException("No such thumbnail"); 1643 } 1644 // Now we know that there is a jfif segment 1645 JFIFMarkerSegment jfif = 1646 (JFIFMarkerSegment) imageMetadata.findMarkerSegment 1647 (JFIFMarkerSegment.class, true); 1648 return jfif.getThumbnailHeight(thumbnailIndex); 1649 } finally { 1650 clearThreadLock(); 1651 } 1652 } 1653 1654 public BufferedImage readThumbnail(int imageIndex, 1655 int thumbnailIndex) 1656 throws IOException { 1657 setThreadLock(); 1658 try { 1659 cbLock.check(); 1660 1661 if ((thumbnailIndex < 0) 1662 || (thumbnailIndex >= getNumThumbnails(imageIndex))) { 1663 throw new IndexOutOfBoundsException("No such thumbnail"); 1664 } 1665 // Now we know that there is a jfif segment and that iis is good 1666 JFIFMarkerSegment jfif = 1667 (JFIFMarkerSegment) imageMetadata.findMarkerSegment 1668 (JFIFMarkerSegment.class, true); 1669 return jfif.getThumbnail(iis, thumbnailIndex, this); 1670 } finally { 1671 clearThreadLock(); 1672 } 1673 } 1674 1675 private void resetInternalState() { 1676 // reset C structures 1677 resetReader(structPointer); 1678 1679 // reset local Java structures 1680 numImages = 0; 1681 imagePositions = new ArrayList<>(); 1682 currentImage = -1; 1683 image = null; 1684 raster = null; 1685 target = null; 1686 buffer = null; 1687 destROI = null; 1688 destinationBands = null; 1689 streamMetadata = null; 1690 imageMetadata = null; 1691 imageMetadataIndex = -1; 1692 haveSeeked = false; 1693 tablesOnlyChecked = false; 1694 iccCS = null; 1695 initProgressData(); 1696 } 1697 1698 public void reset() { 1699 setThreadLock(); 1700 try { 1701 cbLock.check(); 1702 super.reset(); 1703 } finally { 1704 clearThreadLock(); 1705 } 1706 } 1707 1708 private native void resetReader(long structPointer); 1709 1710 public void dispose() { 1711 setThreadLock(); 1712 try { 1713 cbLock.check(); 1714 1715 if (structPointer != 0) { 1716 disposerRecord.dispose(); 1717 structPointer = 0; 1718 } 1719 } finally { 1720 clearThreadLock(); 1721 } 1722 } 1723 1724 private static native void disposeReader(long structPointer); 1725 1726 private static class JPEGReaderDisposerRecord implements DisposerRecord { 1727 private long pData; 1728 1729 public JPEGReaderDisposerRecord(long pData) { 1730 this.pData = pData; 1731 } 1732 1733 public synchronized void dispose() { 1734 if (pData != 0) { 1735 disposeReader(pData); 1736 pData = 0; 1737 } 1738 } 1739 } 1740 1741 private Thread theThread = null; 1742 private int theLockCount = 0; 1743 1744 private synchronized void setThreadLock() { 1745 Thread currThread = Thread.currentThread(); 1746 if (theThread != null) { 1747 if (theThread != currThread) { 1748 // it looks like that this reader instance is used 1749 // by multiple threads. 1750 throw new IllegalStateException("Attempt to use instance of " + 1751 this + " locked on thread " + 1752 theThread + " from thread " + 1753 currThread); 1754 } else { 1755 theLockCount ++; 1756 } 1757 } else { 1758 theThread = currThread; 1759 theLockCount = 1; 1760 } 1761 } 1762 1763 private synchronized void clearThreadLock() { 1764 Thread currThread = Thread.currentThread(); 1765 if (theThread == null || theThread != currThread) { 1766 throw new IllegalStateException("Attempt to clear thread lock " + 1767 " form wrong thread." + 1768 " Locked thread: " + theThread + 1769 "; current thread: " + currThread); 1770 } 1771 theLockCount --; 1772 if (theLockCount == 0) { 1773 theThread = null; 1774 } 1775 } 1776 1777 private CallBackLock cbLock = new CallBackLock(); 1778 1779 private static class CallBackLock { 1780 1781 private State lockState; 1782 1783 CallBackLock() { 1784 lockState = State.Unlocked; 1785 } 1786 1787 void check() { 1788 if (lockState != State.Unlocked) { 1789 throw new IllegalStateException("Access to the reader is not allowed"); 1790 } 1791 } 1792 1793 private void lock() { 1794 lockState = State.Locked; 1795 } 1796 1797 private void unlock() { 1798 lockState = State.Unlocked; 1799 } 1800 1801 private static enum State { 1802 Unlocked, 1803 Locked 1804 } 1805 } 1806} 1807 1808/** 1809 * An internal helper class that wraps producer's iterator 1810 * and extracts specifier instances on demand. 1811 */ 1812class ImageTypeIterator implements Iterator<ImageTypeSpecifier> { 1813 private Iterator<ImageTypeProducer> producers; 1814 private ImageTypeSpecifier theNext = null; 1815 1816 public ImageTypeIterator(Iterator<ImageTypeProducer> producers) { 1817 this.producers = producers; 1818 } 1819 1820 public boolean hasNext() { 1821 if (theNext != null) { 1822 return true; 1823 } 1824 if (!producers.hasNext()) { 1825 return false; 1826 } 1827 do { 1828 theNext = producers.next().getType(); 1829 } while (theNext == null && producers.hasNext()); 1830 1831 return (theNext != null); 1832 } 1833 1834 public ImageTypeSpecifier next() { 1835 if (theNext != null || hasNext()) { 1836 ImageTypeSpecifier t = theNext; 1837 theNext = null; 1838 return t; 1839 } else { 1840 throw new NoSuchElementException(); 1841 } 1842 } 1843 1844 public void remove() { 1845 producers.remove(); 1846 } 1847} 1848 1849/** 1850 * An internal helper class that provides means for deferred creation 1851 * of ImageTypeSpecifier instance required to describe available 1852 * destination types. 1853 * 1854 * This implementation only supports standard 1855 * jpeg color spaces (defined by corresponding JCS color space code). 1856 * 1857 * To support other color spaces one can override produce() method to 1858 * return custom instance of ImageTypeSpecifier. 1859 */ 1860class ImageTypeProducer { 1861 1862 private ImageTypeSpecifier type = null; 1863 boolean failed = false; 1864 private int csCode; 1865 1866 public ImageTypeProducer(int csCode) { 1867 this.csCode = csCode; 1868 } 1869 1870 public ImageTypeProducer() { 1871 csCode = -1; // undefined 1872 } 1873 1874 public synchronized ImageTypeSpecifier getType() { 1875 if (!failed && type == null) { 1876 try { 1877 type = produce(); 1878 } catch (Throwable e) { 1879 failed = true; 1880 } 1881 } 1882 return type; 1883 } 1884 1885 private static final ImageTypeProducer [] defaultTypes = 1886 new ImageTypeProducer [JPEG.NUM_JCS_CODES]; 1887 1888 public static synchronized ImageTypeProducer getTypeProducer(int csCode) { 1889 if (csCode < 0 || csCode >= JPEG.NUM_JCS_CODES) { 1890 return null; 1891 } 1892 if (defaultTypes[csCode] == null) { 1893 defaultTypes[csCode] = new ImageTypeProducer(csCode); 1894 } 1895 return defaultTypes[csCode]; 1896 } 1897 1898 protected ImageTypeSpecifier produce() { 1899 switch (csCode) { 1900 case JPEG.JCS_GRAYSCALE: 1901 return ImageTypeSpecifier.createFromBufferedImageType 1902 (BufferedImage.TYPE_BYTE_GRAY); 1903 case JPEG.JCS_YCbCr: 1904 //there is no YCbCr raw type so by default we assume it as RGB 1905 case JPEG.JCS_RGB: 1906 return ImageTypeSpecifier.createInterleaved(JPEG.JCS.sRGB, 1907 JPEG.bOffsRGB, 1908 DataBuffer.TYPE_BYTE, 1909 false, 1910 false); 1911 case JPEG.JCS_RGBA: 1912 return ImageTypeSpecifier.createPacked(JPEG.JCS.sRGB, 1913 0xff000000, 1914 0x00ff0000, 1915 0x0000ff00, 1916 0x000000ff, 1917 DataBuffer.TYPE_INT, 1918 false); 1919 case JPEG.JCS_YCC: 1920 if (JPEG.JCS.getYCC() != null) { 1921 return ImageTypeSpecifier.createInterleaved( 1922 JPEG.JCS.getYCC(), 1923 JPEG.bandOffsets[2], 1924 DataBuffer.TYPE_BYTE, 1925 false, 1926 false); 1927 } else { 1928 return null; 1929 } 1930 case JPEG.JCS_YCCA: 1931 if (JPEG.JCS.getYCC() != null) { 1932 return ImageTypeSpecifier.createInterleaved( 1933 JPEG.JCS.getYCC(), 1934 JPEG.bandOffsets[3], 1935 DataBuffer.TYPE_BYTE, 1936 true, 1937 false); 1938 } else { 1939 return null; 1940 } 1941 default: 1942 return null; 1943 } 1944 } 1945} 1946