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